Add imperative feature bundles, intrinsic component sizing, and authoring timing logs
Generate state-backed atoms, interfaces, relationships and React/CSS packages in a resumable command that prints the full source diff. Keep scaffold output ordinary editable code. Shorten installed authoring guides while preserving contracts and clarify historical design context. Test generated bundles through immutable Nix verification and cover browser isolation, nested sizing, interruption recovery, and command help.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import { readFile, writeFile, mkdtemp, rm, realpath } from "node:fs/promises";
|
||||
import { parseQx, formatQx, lintQx } from "./source.js";
|
||||
import { scaffoldAtom } from "./scaffold.js";
|
||||
import {loadQxSources} from "./source-loader.js";
|
||||
import { createGitCapabilityResolver } from "./git-resolver.js";
|
||||
import { planEvolution } from "../capability-model/index.js";
|
||||
import { snapshotRepository } from "./candidate-check.js";
|
||||
@@ -41,6 +42,39 @@ const planSummary = (plan: Awaited<ReturnType<typeof planStructure>>) => ({
|
||||
|
||||
const main = async () => {
|
||||
const [command, ...args] = process.argv.slice(2);
|
||||
if (command === "scaffold-validate-qx") {
|
||||
if (args.length !== 1) throw new Error("scaffold-validate-qx SOURCES_JSON");
|
||||
const sources = JSON.parse(args[0]);
|
||||
if (!Array.isArray(sources) || sources.length > 100) throw new Error("Expected at most 100 scaffold sources");
|
||||
for (const entry of sources) {
|
||||
if (!entry || typeof entry.file !== "string" || typeof entry.source !== "string" || entry.source.length > 1024*1024) throw new Error("Invalid scaffold source");
|
||||
const diagnostics = parseQx(entry.source, entry.file).diagnostics;
|
||||
if (diagnostics.length) throw new Error(diagnostics.map(d => `${entry.file}:${d.line}:${d.column + 1}: ${d.message}`).join("\n"));
|
||||
}
|
||||
process.stdout.write('{"syntaxValid":true,"verificationEvidence":false}\n');
|
||||
return;
|
||||
}
|
||||
if (command === "scaffold-placement-binding") {
|
||||
if (args.length !== 1) throw new Error("scaffold-placement-binding ROOT");
|
||||
const {source} = await loadQxSources(args[0]);
|
||||
const syntax = parseQx(source);
|
||||
const text = (n: {start: number; end: number}) => source.slice(n.start, n.end);
|
||||
const matches: string[] = [];
|
||||
for (const edge of walkSyntax(syntax.root)) {
|
||||
if (edge.kind !== "edgeDecl") continue;
|
||||
for (const endpoint of edge.children.filter(n => n.kind === "edgeEndpoint")) {
|
||||
const target = endpoint.children.find(n => n.kind === "targetConstraint");
|
||||
if (!target || !syntax.tokens.some(t => t.start >= target.start && t.end <= target.end && t.kind === "INTERFACE") ||
|
||||
!target.children.some(n => n.kind === "identifier" && text(n) === "WebStudioPlaceable")) continue;
|
||||
const edgeName = text(edge.children.find(n => n.kind === "identifier")!);
|
||||
const projection = text(endpoint.children.find(n => n.kind === "identifier")!);
|
||||
matches.push(`bind placements.resolve to edge ${edgeName}.${projection}.resolve;`);
|
||||
}
|
||||
}
|
||||
if (matches.length !== 1) throw new Error(`Expected one canvas placement edge for WebStudioPlaceable; found ${matches.length}. Configure the workspace canvas before adding a bundle.`);
|
||||
process.stdout.write(JSON.stringify({binding: matches[0]}) + "\n");
|
||||
return;
|
||||
}
|
||||
if (command === "package-identity") {
|
||||
if (args.length !== 1) throw new Error("usage: quixos-qx package-identity PACKAGE_QX");
|
||||
const authored = await readFile(args[0], "utf8");
|
||||
@@ -91,12 +125,14 @@ const main = async () => {
|
||||
if (!args.length || args.length > 2) throw new Error("usage: quixos-qx converge WORKBENCH [REGISTERED_DIRECTORY] (join package writers first)");
|
||||
const context = await authoringContext(args[0]);
|
||||
if (command === "converge") {
|
||||
console.error(`[${new Date().toISOString()}] Capture: waiting for coordinator lock (120s limit)`);
|
||||
const result = spawnSync("flock", ["--exclusive", "--timeout", "120", "--conflict-exit-code", "75", path.join(context.workbench, ".quixos/converge.lock"),
|
||||
process.execPath, process.argv[1], "_converge", context.workbench, ...(args[1] ? [args[1]] : [])], {stdio: "inherit"});
|
||||
if (result.error) throw result.error;
|
||||
if (result.status === 75) process.stderr.write("Timed out after 120 seconds waiting for source capture; inspect the active coordinator. No build lock is held.\n");
|
||||
process.exitCode = result.status ?? 1; return;
|
||||
}
|
||||
console.error(`[${new Date().toISOString()}] Capture: coordinator lock acquired`);
|
||||
const result = await convergeAuthoring(context.workbench, args[1]);
|
||||
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
||||
if (!result.converged) process.exitCode = 1;
|
||||
@@ -173,7 +209,7 @@ const main = async () => {
|
||||
const spec = await readSpec(specFile) as ScaffoldRecipe;
|
||||
if (!spec.name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(spec.name) || !spec.id || !spec.revision || !spec.tools?.quixos) throw new Error("Interface scaffold requires name, id, revision and Quixos toolchain source");
|
||||
const request: StructuralRequest = {kind: "interface", source: spec.source, files: [
|
||||
{file: "interface.qx", create: `interface ${spec.name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n}\n`},
|
||||
{file: "interface.qx", create: spec.declaration ?? `interface ${spec.name} id ${JSON.stringify(spec.id)} revision ${JSON.stringify(spec.revision)} {\n}\n`},
|
||||
{file: "quixos.lock", create: formatQuixosLock({formatVersion: 1, quixos: {resolver: "git", ...spec.tools.quixos}, resources: []})},
|
||||
{file: ".gitignore", create: ".quixos/\n"},
|
||||
]};
|
||||
|
||||
Reference in New Issue
Block a user