52803dda05
Add kinded parameters, capability bounds, Self, aliases and closed application identities. Check generic implementations universally and build candidate-specific codecs and descriptors from immutable schemas. Preserve lexical aliases and exact dispatch identities in package and host bindings. Add an imperative CRUD+index domain scaffold with explicit soft-deletion semantics, source/codegen regression coverage, installed CLI tests and an authoring guide. Existing Web Studio opaque props and class-level create-menu migration are separate from the implemented language core.
137 lines
5.1 KiB
JavaScript
137 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { readFile, writeFile } from "node:fs/promises";
|
|
import process from "node:process";
|
|
import { compileWorkspaceRepository } from "./assembly.js";
|
|
import { createGitCapabilityResolver } from "./git-resolver.js";
|
|
import { bindingSchema, specializeBindingSchema } from "../bindings/index.js";
|
|
import {
|
|
planEvolution,
|
|
runtimeContracts,
|
|
type EvolutionReview,
|
|
type WorkspaceRevision,
|
|
} from "../capability-model/index.js";
|
|
|
|
const usage = `usage: quixos-workspace-compile --root DIRECTORY --checkout-root DIRECTORY
|
|
[--snapshot-map PATH] [--graph-out PATH] [--workspace-id ID] [--workspace-revision-id ID]
|
|
[--source-root-commit GIT_REV] [--baseline PLAN_JSON] [--evolution-out PATH]
|
|
[--reviews REVIEW_JSON] [--schemas-out PATH]
|
|
|
|
Resolves a workspace's recursive resource-lock graph, clones every exact
|
|
resource revision, validates standalone interface/package manifests, and emits
|
|
the checked workspace plan as JSON.`;
|
|
|
|
const parseArgs = (args: string[]) => {
|
|
const values = new Map<string, string>();
|
|
for (let index = 0; index < args.length; index += 2) {
|
|
const key = args[index];
|
|
const value = args[index + 1];
|
|
if (!key?.startsWith("--") || !value) throw new Error(usage);
|
|
values.set(key, value);
|
|
}
|
|
const rootDirectory = values.get("--root");
|
|
const checkoutRoot = values.get("--checkout-root");
|
|
if (!rootDirectory || !checkoutRoot) throw new Error(usage);
|
|
return {
|
|
rootDirectory,
|
|
checkoutRoot,
|
|
graphOut: values.get("--graph-out"),
|
|
schemasOut: values.get("--schemas-out"),
|
|
snapshotMap: values.get("--snapshot-map"),
|
|
workspaceId: values.get("--workspace-id"),
|
|
workspaceRevisionId: values.get("--workspace-revision-id"),
|
|
sourceRootCommit: values.get("--source-root-commit"),
|
|
baseline: values.get("--baseline"),
|
|
evolutionOut: values.get("--evolution-out"),
|
|
reviews: values.get("--reviews"),
|
|
};
|
|
};
|
|
|
|
const main = async () => {
|
|
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
process.stdout.write(`${usage}\n`);
|
|
return;
|
|
}
|
|
const options = parseArgs(process.argv.slice(2));
|
|
const resolveResource = await createGitCapabilityResolver({
|
|
checkoutRoot: options.checkoutRoot,
|
|
snapshotMap: options.snapshotMap,
|
|
});
|
|
const assembled = await compileWorkspaceRepository({
|
|
rootDirectory: options.rootDirectory,
|
|
workspaceId: options.workspaceId,
|
|
workspaceRevisionId: options.workspaceRevisionId,
|
|
sourceRootCommit: options.sourceRootCommit,
|
|
resolveResource,
|
|
});
|
|
|
|
if (options.graphOut) {
|
|
await writeFile(
|
|
options.graphOut,
|
|
`${JSON.stringify(
|
|
{
|
|
formatVersion: 1,
|
|
quixos: assembled.lock.quixos,
|
|
directResources: [...assembled.directResources.entries()].map(([bindingKey, node]) => {
|
|
const [kind, binding] = bindingKey.split("\0");
|
|
return { kind, binding, resourceKey: node.key, directory: node.directory };
|
|
}),
|
|
resources: assembled.resources.map((node) => ({
|
|
key: node.key,
|
|
kind: node.kind,
|
|
source: node.source,
|
|
directory: node.directory,
|
|
resourceId:
|
|
node.resource.kind === "interface"
|
|
? node.resource.revision.interfaceId
|
|
: node.resource.revision.packageId,
|
|
revisionId: node.resource.revision.revisionId,
|
|
dependencies: [...node.dependencies.entries()].map(([binding, dependency]) => ({
|
|
binding,
|
|
resourceKey: dependency.key,
|
|
})),
|
|
})),
|
|
},
|
|
null,
|
|
2,
|
|
)}\n`,
|
|
);
|
|
}
|
|
const candidate = { ...assembled.workspace, executionContracts: runtimeContracts(assembled.workspace) };
|
|
if (options.schemasOut) {
|
|
const schemas: Record<string, unknown> = {};
|
|
for (const node of assembled.resources.filter((entry) => entry.kind === "package")) {
|
|
const closure = new Map<string, typeof node>();
|
|
const visit = (entry: typeof node) => {
|
|
if (closure.has(entry.key)) return;
|
|
closure.set(entry.key, entry);
|
|
entry.dependencies.forEach(visit);
|
|
};
|
|
visit(node);
|
|
schemas[node.resource.revision.revisionId] = specializeBindingSchema(
|
|
bindingSchema({ resources: [...closure.values()] }),
|
|
candidate,
|
|
node.resource.revision.revisionId,
|
|
);
|
|
}
|
|
await writeFile(options.schemasOut, JSON.stringify(schemas));
|
|
}
|
|
if (options.evolutionOut) {
|
|
const baseline = options.baseline
|
|
? (JSON.parse(await readFile(options.baseline, "utf8")) as WorkspaceRevision)
|
|
: null;
|
|
const reviews = options.reviews ? (JSON.parse(await readFile(options.reviews, "utf8")) as EvolutionReview[]) : [];
|
|
if (!Array.isArray(reviews)) throw new Error("Review file must contain an array");
|
|
await writeFile(
|
|
options.evolutionOut,
|
|
`${JSON.stringify(planEvolution(baseline, candidate, { reviews }), null, 2)}\n`,
|
|
);
|
|
}
|
|
process.stdout.write(`${JSON.stringify(candidate, null, 2)}\n`);
|
|
};
|
|
|
|
main().catch((error: unknown) => {
|
|
process.stderr.write(`${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`);
|
|
process.exitCode = 1;
|
|
});
|