Compare commits
4 Commits
6657af7ca0
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8dde1f6b82 | |||
| e0874093f2 | |||
| 0b159fcb81 | |||
| f33eee054f |
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos",
|
||||
"sourceCommit": "8484d99fcf78a49341a17597bc62211e5f1807dd",
|
||||
"sourcePath": "quixos-instance/quixos-nix-helpers",
|
||||
"exportName": "quixos-nix-helpers",
|
||||
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import {createRequire} from "node:module";
|
||||
import path from "node:path";
|
||||
|
||||
// Resolve build dependencies from the package's locked node_modules, not from
|
||||
// the helper's own Nix-store location. This program runs only during the build.
|
||||
const require = createRequire(path.join(process.cwd(), "package.json"));
|
||||
const {build} = require("esbuild");
|
||||
const options = JSON.parse(process.argv[2]);
|
||||
const platform = new Map([
|
||||
["react", "/__quixos/platform/react/v18.mjs"],
|
||||
["react/jsx-runtime", "/__quixos/platform/react-jsx-runtime/v18.mjs"],
|
||||
["react/jsx-dev-runtime", "/__quixos/platform/react-jsx-dev-runtime/v18.mjs"],
|
||||
["@quixos/web-studio-react-runtime", "/__quixos/platform/web-studio-react-runtime/v1.mjs"],
|
||||
]);
|
||||
await build({...options, plugins: [{name: "quixos-browser-source", setup(api) {
|
||||
api.onResolve({filter: /\?browser-source$/}, async ({path: specifier, resolveDir}) => {
|
||||
if (!specifier.startsWith("./") && !specifier.startsWith("../")) throw new Error("Browser source imports must name a relative compiled module");
|
||||
const resolved = await api.resolve(specifier.slice(0, -"?browser-source".length), {resolveDir, kind: "import-statement"});
|
||||
if (resolved.errors.length) return {errors: resolved.errors};
|
||||
return {path: resolved.path, namespace: "browser-source"};
|
||||
});
|
||||
api.onLoad({filter: /.*/, namespace: "browser-source"}, async ({path: entry}) => {
|
||||
const browser = await build({entryPoints: [entry], bundle: true, write: false, outfile: "component.mjs",
|
||||
format: "esm", platform: "browser", target: "es2022", metafile: true,
|
||||
plugins: [{name: "quixos-platform", setup(browserApi) {
|
||||
browserApi.onResolve({filter: /.*/}, ({path: name}) => platform.has(name) ? {path: platform.get(name), external: true} : undefined);
|
||||
}}]});
|
||||
const js = browser.outputFiles.find(file => file.path.endsWith(".mjs"));
|
||||
if (!js) throw new Error("Browser source build produced no JavaScript");
|
||||
const css = browser.outputFiles.find(file => file.path.endsWith(".css"));
|
||||
if (css && Object.values(browser.metafile.outputs).some(output => output.exports?.includes("styles"))) throw new Error("Use either imported CSS or an explicit styles export, not both");
|
||||
if (browser.outputFiles.some(file => !file.path.endsWith(".mjs") && !file.path.endsWith(".css"))) throw new Error("Browser assets must be embedded, not emitted as unserved files");
|
||||
const source = js.text + (css ? `\nexport const styles = ${JSON.stringify(css.text)};\n` : "");
|
||||
return {contents: `export default ${JSON.stringify(source)};`, loader: "js", watchFiles: Object.keys(browser.metafile.inputs)};
|
||||
});
|
||||
}}]});
|
||||
@@ -1,17 +0,0 @@
|
||||
import fs from "node:fs";
|
||||
import crypto from "node:crypto";
|
||||
const [schemaPath, packageRevisionId, bindingOutput, generatorPath, output] = process.argv.slice(2);
|
||||
if (!schemaPath || !packageRevisionId || !bindingOutput || !generatorPath || !output) throw new Error("Missing candidate check receipt inputs");
|
||||
const canonical = (value) => Array.isArray(value) ? value.map(canonical) : value && typeof value === "object"
|
||||
? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => [key, canonical(entry)])) : value;
|
||||
const hash = (value) => `sha256:${crypto.createHash("sha256").update(JSON.stringify(canonical(value))).digest("hex")}`;
|
||||
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf8"));
|
||||
if (!schema.packages.some((entry) => entry.revisionId === packageRevisionId)) throw new Error("Checked binding schema lacks the package");
|
||||
const generated = fs.readFileSync(bindingOutput, "utf8");
|
||||
if (generated !== fs.readFileSync(".qx-checked-bindings", "utf8")) throw new Error("Build replaced candidate-generated bindings; its check is not evidence for this candidate");
|
||||
const receipt = {
|
||||
schemaVersion: 1, packageRevisionId, success: true, bindingSchema: schema,
|
||||
bindingSchemaDigest: hash(schema), generatedDigest: hash(generated),
|
||||
checkerDigest: hash({ generatorPath, compiler: JSON.parse(fs.readFileSync("node_modules/typescript/package.json", "utf8")), lock: fs.readFileSync("yarn.lock", "utf8") }),
|
||||
};
|
||||
fs.writeFileSync(output, `${JSON.stringify(receipt, null, 2)}\n`, { flag: "wx" });
|
||||
+15
-115
@@ -510,20 +510,6 @@ 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,
|
||||
@@ -535,13 +521,8 @@ EOF
|
||||
promptName ? null,
|
||||
nodejsAttr ? "nodejs_24",
|
||||
buildCommand ? "yarn build",
|
||||
sourcePortals ? { },
|
||||
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,
|
||||
# A dedicated entrypoint calling SDK serveMigration; never start the
|
||||
# normal package server in the isolated migration execution boundary.
|
||||
migrationEntrypoint ? null,
|
||||
nativeBuildInputs ? [ ],
|
||||
devShellPackages ? [ ],
|
||||
devShellHook ? "",
|
||||
@@ -550,8 +531,6 @@ EOF
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
outputsFor = candidateBindings:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
lib = pkgs.lib;
|
||||
@@ -582,34 +561,12 @@ EOF
|
||||
attrs
|
||||
);
|
||||
|
||||
bindingConfig = if candidateBindings != null then candidateBindings
|
||||
else 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";
|
||||
authoringCheck = builtins.fromJSON (builtins.readFile "${packageRoot}/quixos.check.json");
|
||||
bindingOptionsFile = if bindingConfig == null then null else
|
||||
pkgs.writeText "qx-typescript-options.json" (builtins.toJSON (authoringCheck.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} ${bindingOptionsFile}
|
||||
${lib.optionalString (installServer != null && descriptorPath != null) ''
|
||||
${bindingConfig.generator}/bin/quixos-qx package-descriptor ${lib.escapeShellArg (toString bindingSchema)} \
|
||||
${lib.escapeShellArg bindingConfig.packageRevisionId} > ${lib.escapeShellArg descriptorPath}
|
||||
''}
|
||||
'';
|
||||
generateBindings = pkgs.writeShellApplication {
|
||||
name = "qx-generate-bindings";
|
||||
text = bindingCommand;
|
||||
};
|
||||
portalLinksFor = attrs:
|
||||
lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList
|
||||
(target: source: "ln -sfn ${source} ${lib.escapeShellArg target}")
|
||||
attrs
|
||||
);
|
||||
|
||||
bundleConfig = if bundle == null then { } else bundle;
|
||||
bundleOutfile = bundleConfig.outfile or "server.mjs";
|
||||
@@ -617,32 +574,16 @@ EOF
|
||||
bundleTarget = bundleConfig.target or "node24";
|
||||
bundleFormat = bundleConfig.format or "esm";
|
||||
bundleBanner = bundleConfig.banner or nodeRequireBanner;
|
||||
bundleAliases = bundleConfig.aliases or {
|
||||
"@automerge/automerge" = "./node_modules/@automerge/automerge/dist/mjs/entrypoints/fullfat_base64.js";
|
||||
};
|
||||
bundleAliasArgs = lib.concatStringsSep " " (
|
||||
lib.mapAttrsToList
|
||||
(from: to: "--alias:${from}=${lib.escapeShellArg to}")
|
||||
bundleAliases
|
||||
);
|
||||
nodeRequireBanner = "import { createRequire } from 'module';const require = createRequire(import.meta.url);";
|
||||
bundleCommand =
|
||||
if bundle == null
|
||||
then ""
|
||||
else if bundleConfig.browserSources or false then ''
|
||||
${nodejs}/bin/node ${./browser-source.mjs} ${lib.escapeShellArg (builtins.toJSON {
|
||||
entryPoints = [ bundleConfig.entry ]; bundle = true; platform = bundlePlatform;
|
||||
target = bundleTarget; format = bundleFormat; alias = bundleAliases;
|
||||
preserveSymlinks = bundleConfig.preserveSymlinks or true;
|
||||
banner.js = bundleBanner; outfile = bundleOutfile;
|
||||
})}
|
||||
'' else ''
|
||||
else ''
|
||||
esbuild ${lib.escapeShellArg bundleConfig.entry} \
|
||||
--bundle \
|
||||
--platform=${bundlePlatform} \
|
||||
--target=${bundleTarget} \
|
||||
--format=${bundleFormat} \
|
||||
${bundleAliasArgs} \
|
||||
${lib.optionalString (bundleConfig.preserveSymlinks or true) "--preserve-symlinks \\"}
|
||||
--banner:js=${lib.escapeShellArg bundleBanner} \
|
||||
--outfile=${lib.escapeShellArg bundleOutfile}
|
||||
@@ -654,22 +595,12 @@ EOF
|
||||
installConfig.libexecName or (lib.strings.sanitizeDerivationName packageNameFinal);
|
||||
serverFile = installConfig.serverFile or bundleOutfile;
|
||||
descriptorPath = installConfig.descriptorPath or "descriptor.quixos-package.txtpb";
|
||||
extraFiles = installConfig.extraFiles or [ ];
|
||||
installExtraFile = file:
|
||||
let
|
||||
source = toString file.source;
|
||||
target = file.target or (baseNameOf source);
|
||||
mode = file.mode or "0644";
|
||||
in ''
|
||||
install -Dm${toString mode} ${lib.escapeShellArg source} "$out/libexec/${serverLibexecName}/${target}"
|
||||
'';
|
||||
installServerPhase =
|
||||
if installServer == null
|
||||
then null
|
||||
else ''
|
||||
runHook preInstall
|
||||
install -Dm755 ${lib.escapeShellArg serverFile} "$out/libexec/${serverLibexecName}/${serverFile}"
|
||||
${lib.concatMapStringsSep "\n" installExtraFile extraFiles}
|
||||
mkdir -p "$out/bin"
|
||||
cat > "$out/bin/${serverBin}" <<EOF
|
||||
#!${pkgs.runtimeShell}
|
||||
@@ -679,17 +610,6 @@ EOF
|
||||
${lib.optionalString (installConfig ? descriptorPath && descriptorPath != null) ''
|
||||
cp ${lib.escapeShellArg descriptorPath} "$out/${descriptorPath}"
|
||||
''}
|
||||
${lib.optionalString (bindingConfig != null) ''
|
||||
install -m 0444 quixos-check.json "$out/quixos-check.json"
|
||||
''}
|
||||
${lib.optionalString (migrationEntrypoint != null) ''
|
||||
install -Dm444 migration.mjs "$out/libexec/${serverLibexecName}/migration.mjs"
|
||||
cat > "$out/bin/migrate" <<EOF
|
||||
#!${pkgs.runtimeShell}
|
||||
exec ${nodejs}/bin/node --max-old-space-size=256 "$out/libexec/${serverLibexecName}/migration.mjs" "\$@"
|
||||
EOF
|
||||
chmod +x "$out/bin/migrate"
|
||||
''}
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
@@ -698,27 +618,16 @@ EOF
|
||||
overrideAttrs = old: {
|
||||
nativeBuildInputs =
|
||||
(old.nativeBuildInputs or [ ])
|
||||
++ lib.optional (bundle != null || migrationEntrypoint != null) pkgs.esbuild
|
||||
++ lib.optional (bundle != null) pkgs.esbuild
|
||||
++ callOption nativeBuildInputs;
|
||||
preConfigure = (old.preConfigure or "") + ''
|
||||
${portalLinksFor (callOption sourcePortals)}
|
||||
'';
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
${exportsFor (callOption buildEnv)}
|
||||
${bindingCommand}
|
||||
${lib.optionalString (bindingConfig != null && bundle != null) "${bindingConfig.generator}/bin/quixos-qx bundle-policy src"}
|
||||
${lib.optionalString (bindingConfig != null) "yarn exec tsc --noEmit"}
|
||||
${lib.optionalString (bindingConfig != null) "cp ${lib.escapeShellArg bindingOutput} .qx-checked-bindings"}
|
||||
${buildCommand}
|
||||
${lib.optionalString (bindingConfig != null) ''
|
||||
yarn exec tsc --noEmit
|
||||
node ${./check-receipt.mjs} ${lib.escapeShellArg (toString bindingSchema)} \
|
||||
${lib.escapeShellArg bindingConfig.packageRevisionId} ${lib.escapeShellArg bindingOutput} \
|
||||
${lib.escapeShellArg (toString bindingConfig.generator)} quixos-check.json
|
||||
''}
|
||||
${bundleCommand}
|
||||
${lib.optionalString (migrationEntrypoint != null) ''
|
||||
esbuild ${lib.escapeShellArg migrationEntrypoint} --bundle --platform=node --target=node24 --format=esm \
|
||||
${bundleAliasArgs} --preserve-symlinks --banner:js=${lib.escapeShellArg nodeRequireBanner} --outfile=migration.mjs
|
||||
''}
|
||||
runHook postBuild
|
||||
'';
|
||||
} // lib.optionalAttrs (installServerPhase != null) {
|
||||
@@ -753,19 +662,11 @@ EOF
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = [
|
||||
nodejs
|
||||
project.yarn-freestanding
|
||||
] ++ lib.optional (bindingConfig != null) generateBindings ++ callOption devShellPackages;
|
||||
# Explicit command keeps shell entry free of source mutations.
|
||||
pkgs.yarn-berry_4
|
||||
] ++ callOption devShellPackages;
|
||||
shellHook = devShellHookBase + callOption devShellHook;
|
||||
};
|
||||
} // maybeServerOutputs;
|
||||
in (outputsFor null) // {
|
||||
# The workspace supplies a compiler-produced schema for the exact
|
||||
# candidate graph. Standalone builds may use checked-in authoring types,
|
||||
# but only this build path regenerates and witnesses candidate contracts.
|
||||
quixosPackages.checkedServer = { schema, generator, packageRevisionId }:
|
||||
(outputsFor { inherit schema generator packageRevisionId; }).packages.server;
|
||||
}
|
||||
} // maybeServerOutputs
|
||||
);
|
||||
in
|
||||
{
|
||||
@@ -774,6 +675,5 @@ in
|
||||
mkTsPackageServer
|
||||
mkSchemaSupport
|
||||
mkCaminoSourcePackage
|
||||
mkQxBindingSchema
|
||||
mkCaminoTsYarnNixifyFlake;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user