From 7e4fb60155340477adc0847d2d6e61a7391f13b0 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Tue, 8 Sep 2026 14:27:24 -0700 Subject: [PATCH] Generate typed QX bindings and add source editing tools --- quixos-package-helpers.nix | 46 +++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/quixos-package-helpers.nix b/quixos-package-helpers.nix index 44e9c5b..6ff117a 100644 --- a/quixos-package-helpers.nix +++ b/quixos-package-helpers.nix @@ -510,6 +510,20 @@ EOF ''; }; + # Language-neutral, offline schema compilation. Snapshot directories must be + # fixed Nix inputs matching the resource lock's complete dependency closure. + mkQxBindingSchema = { pkgs, protocol, src, repository, commit, resources ? [ ] }: + let + snapshots = pkgs.writeText "qx-binding-snapshots.json" (builtins.toJSON { inherit resources; }); + in pkgs.runCommand "qx-binding-schema.json" { } '' + ${protocol}/bin/quixos-resource-compile \ + --root ${src} --kind package \ + --repository ${pkgs.lib.escapeShellArg repository} \ + --commit ${pkgs.lib.escapeShellArg commit} \ + --checkout-root "$TMPDIR/checkouts" \ + --snapshot-map ${snapshots} --snapshot-only true --schema-out "$out" > /dev/null + ''; + mkCaminoTsYarnNixifyFlake = { inputs, @@ -522,6 +536,9 @@ EOF nodejsAttr ? "nodejs_24", buildCommand ? "yarn build", buildEnv ? { }, + # An exact, compiler-produced BindingSchema JSON artifact and a backend. + # Other language helpers can consume the same schema with their own generator/runtime. + bindings ? null, nativeBuildInputs ? [ ], devShellPackages ? [ ], devShellHook ? "", @@ -560,6 +577,29 @@ EOF attrs ); + bindingConfig = if bindings == null then null else callOption bindings; + bindingSchema = if bindingConfig == null then null else bindingConfig.schema or (mkQxBindingSchema { + inherit pkgs; + protocol = bindingConfig.generator; + src = packageRoot; + inherit (bindingConfig) repository commit; + resources = bindingConfig.resources or [ ]; + }); + bindingOutput = if bindingConfig == null then "src/gen/qx.ts" else bindingConfig.output or "src/gen/qx.ts"; + bindingOptions = if bindingConfig == null then null else + pkgs.writeText "qx-typescript-options.json" (builtins.toJSON (bindingConfig.options or { })); + bindingCommand = if bindingConfig == null then "" else '' + mkdir -p ${lib.escapeShellArg (dirOf bindingOutput)} + ${bindingConfig.generator}/bin/quixos-codegen-ts \ + ${lib.escapeShellArg (toString bindingSchema)} \ + ${lib.escapeShellArg bindingConfig.packageRevisionId} \ + ${lib.escapeShellArg bindingOutput} ${bindingOptions} + ''; + generateBindings = pkgs.writeShellApplication { + name = "qx-generate-bindings"; + text = bindingCommand; + }; + bundleConfig = if bundle == null then { } else bundle; bundleOutfile = bundleConfig.outfile or "server.mjs"; bundlePlatform = bundleConfig.platform or "node"; @@ -634,6 +674,8 @@ EOF buildPhase = '' runHook preBuild ${exportsFor (callOption buildEnv)} + ${bindingCommand} + ${lib.optionalString (bindingConfig != null) "yarn exec tsc --noEmit"} ${buildCommand} ${bundleCommand} runHook postBuild @@ -671,7 +713,8 @@ EOF packages = [ nodejs project.yarn-freestanding - ] ++ callOption devShellPackages; + ] ++ lib.optional (bindingConfig != null) generateBindings ++ callOption devShellPackages; + # Explicit command keeps shell entry free of source mutations. shellHook = devShellHookBase + callOption devShellHook; }; } // maybeServerOutputs @@ -683,5 +726,6 @@ in mkTsPackageServer mkSchemaSupport mkCaminoSourcePackage + mkQxBindingSchema mkCaminoTsYarnNixifyFlake; }