Build and activate checked component packages through normal source authoring (WI-12)

This commit is contained in:
Timothy J. Aveni
2026-09-21 17:38:40 -07:00
parent e1ad15c620
commit 1e08ccd937
11 changed files with 336 additions and 571 deletions
+23 -3
View File
@@ -26,7 +26,7 @@ export async function executionScaffold(
): Promise<StructuralRequest> {
if (!spec.name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(spec.name) || !spec.id)
throw new Error("Execution scaffold requires a simple name and an ID");
if (spec.initialFiles) throw new Error("Edit execution source after scaffolding; initialFiles is not supported");
if (spec.initialFiles && command !== "package") throw new Error("Initial files are only for new packages");
const files: StructuralRequest["files"] = [];
const create = (file: string, content: string) => files.push({ file: prefix + file, create: content });
if (command === "package") {
@@ -37,6 +37,7 @@ export async function executionScaffold(
formatQuixosLock({ formatVersion: 1, quixos: { resolver: "git", ...spec.tools.quixos }, resources: [] }),
);
create("quixos.check.json", json({ backend: "execution" }));
create("quixos.components.json", "{}\n");
create("package.json", json({ name: `@quixos/${spec.name.toLowerCase()}`, private: true, type: "module" }));
create(".gitignore", "node_modules/\ndist/\nsrc/gen/\n.quixos/\nresult\n");
for (const lane of ["browser", "server", "remote"])
@@ -55,11 +56,15 @@ export async function executionScaffold(
flake-utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs { inherit system; };
in {
quixosPackages.checkedArtifacts = { plan, generator, core, sharedRuntime, typescript, packageRevisionId }:
quixosPackages.checkedArtifacts = { plan, generator, core, sharedRuntime, typescript, packageRevisionId, react ? null }:
(import (helpers + "/artifacts.nix")) {
inherit pkgs plan core sharedRuntime typescript packageRevisionId;
protocol = generator;
src = ./.;
components = let entries = builtins.fromJSON (builtins.readFile ./quixos.components.json); in
if entries == {} then null else
if react == null then throw "Component entries require the workspace-selected React runtime" else
{ inherit (react) sdk runtime; inherit entries; };
portable = {
entry = "src/server.ts";
entries.browser = "src/browser.ts";
@@ -73,7 +78,7 @@ export async function executionScaffold(
);
create(
"README.md",
`# ${spec.name}\n\nThis execution package exports checked browser/server modules and a process service.\nUse scaffold function with an explicit execution contract to add a typed handler.\nSnapshot/transaction targets choose browser.ts and/or server.ts; remote handlers\nuse remote.ts. Shared helpers are ordinary imports. Generated bindings are build\noutputs, not authored source. The coordinator supplies the checked workspace plan\nand runtime dependencies to checkedArtifacts.\n\nThe execution scaffold currently requires the execution artifact build path;\nlegacy checkedServer verification and activation do not install these artifacts.\nA source-only structural check is not runtime qualification.\n`,
`# ${spec.name}\n\nThis execution package exports checked browser/server modules and a process service.\nUse scaffold function with an explicit execution contract to add a typed handler.\nSnapshot/transaction targets choose browser.ts and/or server.ts; remote handlers\nuse remote.ts. Shared helpers are ordinary imports. Generated bindings are build\noutputs, not authored source. The coordinator supplies the checked workspace plan\nand runtime dependencies to checkedArtifacts.\n\nRun qx-workspace check from the workspace root to compile exact source and build\nchecked artifacts. Activate the resulting report separately through the workspace\nhost. A source-only structural check is not runtime qualification.\n`,
);
} else if (command === "function") {
if (!spec.declaration)
@@ -154,5 +159,20 @@ export async function executionScaffold(
}
} else
throw new Error("Execution packages use ordinary factory transactions; migration scaffolding is not supported");
if (spec.initialFiles) {
for (const [file, content] of Object.entries(spec.initialFiles)) {
if (
typeof content !== "string" ||
!/^(?:package\.qx|quixos\.components\.json|quixos\.controllers\.json|README\.md|(?:src|queries)\/(?:[A-Za-z0-9_-]+\/)*[A-Za-z0-9_.-]+)$/.test(
file,
) ||
file.split("/").some((part) => part === ".." || part === "." || part === "gen")
)
throw new Error("Not an initial authored execution file: " + file);
const previous = files.findIndex((entry) => entry.file === prefix + file);
if (previous >= 0) files.splice(previous, 1);
create(file, content);
}
}
return { kind: "package", source: spec.source, resourceRoot: spec.directory, validation: "syntax", files };
}