Files
quixos-protocol/src/capability-language/scaffold-recipes.ts
T

66 lines
3.2 KiB
TypeScript

import type { GitSource } from "../resource-lock/index.js";
import type { StructuralRequest } from "./structural-plan.js";
import { executionScaffold, isExecutionScaffold } from "./execution-scaffold.js";
import { scaffoldController } from "./controller-scaffold.js";
import { scaffoldComponent } from "./component-scaffold.js";
import { scaffoldQuery } from "./query-scaffold.js";
type Source = { repository: string; commit: string };
export type ScaffoldRecipe = {
template?: "typescript-execution" | "typescript-component";
source: Source;
directory?: string;
name?: string;
id?: string;
revision?: string;
declaration?: string;
queryDocument?: string;
initialFiles?: Record<string, string>;
controller?: { registry?: string; subject: string; snapshot: string; remote: string; input: "unit" | "value" };
tools?: { quixos: Source; protocol: Source; helpers: Source; sdk?: Source };
};
const source = (value: Source): GitSource => {
if (!value || typeof value.repository !== "string" || typeof value.commit !== "string")
throw Error("Scaffold requires an exact source identity; use a registered repository");
const url = new URL(value.repository);
if (
url.protocol !== "https:" ||
url.username ||
url.password ||
url.search ||
url.hash ||
!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(value.commit)
)
throw Error("Scaffolds require credential-free HTTPS sources and full exact commits");
return { resolver: "git", ...value };
};
const nixSource = (value: Source) =>
`git+${source(value).repository}?ref=refs/tags/quixos-reachability/${value.commit}&rev=${value.commit}`;
/** One-shot edits; structural planning retains interrupted work and authored files. */
export async function scaffoldRecipe(
root: string,
command: "package" | "function" | "component" | "controller" | "query" | "migration" | "refresh",
spec: ScaffoldRecipe,
): Promise<StructuralRequest> {
if (command === "refresh")
throw Error(
"Scaffold refresh is removed. Edit authored code and run check; add entries with scaffold function/component/query.",
);
if (command === "migration")
throw Error(
"Old-workspace migration scaffolding is retired. Current disposable workspaces do not require migration code.",
);
source(spec.source);
if (spec.directory && !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(spec.directory))
throw Error("Scaffold directory must be contained");
if (spec.template && !["typescript-execution", "typescript-component"].includes(spec.template))
throw Error("Use typescript-execution or typescript-component; old server/source-string presets are retired");
const prefix = spec.directory ? spec.directory + "/" : "";
if (command !== "package" && !(await isExecutionScaffold(root, prefix)))
throw Error("Scaffolding requires a checked execution package");
if (spec.tools) Object.values(spec.tools).forEach(source);
if (command === "controller") return scaffoldController(root, prefix, spec);
if (command === "component") return scaffoldComponent(root, prefix, spec);
if (command === "query") return scaffoldQuery(root, prefix, spec);
return executionScaffold(root, command, spec, prefix, nixSource);
}