2026-09-21 16:59:35 -07:00
|
|
|
import type { GitSource } from "../resource-lock/index.js";
|
2026-09-15 15:23:24 -07:00
|
|
|
import type { StructuralRequest } from "./structural-plan.js";
|
2026-09-21 00:44:47 -07:00
|
|
|
import { executionScaffold, isExecutionScaffold } from "./execution-scaffold.js";
|
2026-09-21 16:59:35 -07:00
|
|
|
import { scaffoldController } from "./controller-scaffold.js";
|
|
|
|
|
import { scaffoldComponent } from "./component-scaffold.js";
|
2026-09-21 01:25:00 -07:00
|
|
|
import { scaffoldQuery } from "./query-scaffold.js";
|
2026-09-15 15:23:24 -07:00
|
|
|
type Source = { repository: string; commit: string };
|
2026-09-10 18:27:41 -07:00
|
|
|
export type ScaffoldRecipe = {
|
2026-09-21 16:59:35 -07:00
|
|
|
template?: "typescript-execution" | "typescript-component";
|
2026-09-15 15:23:24 -07:00
|
|
|
source: Source;
|
|
|
|
|
directory?: string;
|
|
|
|
|
name?: string;
|
|
|
|
|
id?: string;
|
|
|
|
|
revision?: string;
|
2026-09-10 18:27:41 -07:00
|
|
|
declaration?: string;
|
2026-09-21 01:25:00 -07:00
|
|
|
queryDocument?: string;
|
2026-09-15 13:45:42 -07:00
|
|
|
initialFiles?: Record<string, string>;
|
2026-09-21 16:59:35 -07:00
|
|
|
controller?: { subject: string; snapshot: string; remote: string; input: "unit" | "value" };
|
|
|
|
|
tools?: { quixos: Source; protocol: Source; helpers: Source; sdk?: Source };
|
2026-09-10 18:27:41 -07:00
|
|
|
};
|
|
|
|
|
const source = (value: Source): GitSource => {
|
2026-09-15 15:23:24 -07:00
|
|
|
if (!value || typeof value.repository !== "string" || typeof value.commit !== "string")
|
2026-09-21 16:59:35 -07:00
|
|
|
throw Error("Scaffold requires an exact source identity; use a registered repository");
|
2026-09-10 18:27:41 -07:00
|
|
|
const url = new URL(value.repository);
|
2026-09-15 15:23:24 -07:00
|
|
|
if (
|
|
|
|
|
url.protocol !== "https:" ||
|
|
|
|
|
url.username ||
|
|
|
|
|
url.password ||
|
|
|
|
|
url.search ||
|
|
|
|
|
url.hash ||
|
|
|
|
|
!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(value.commit)
|
|
|
|
|
)
|
2026-09-21 16:59:35 -07:00
|
|
|
throw Error("Scaffolds require credential-free HTTPS sources and full exact commits");
|
2026-09-15 15:23:24 -07:00
|
|
|
return { resolver: "git", ...value };
|
2026-09-10 18:27:41 -07:00
|
|
|
};
|
2026-09-15 15:23:24 -07:00
|
|
|
const nixSource = (value: Source) =>
|
|
|
|
|
`git+${source(value).repository}?ref=refs/tags/quixos-reachability/${value.commit}&rev=${value.commit}`;
|
2026-09-21 16:59:35 -07:00
|
|
|
/** One-shot edits; structural planning retains interrupted work and authored files. */
|
|
|
|
|
export async function scaffoldRecipe(
|
2026-09-15 15:23:24 -07:00
|
|
|
root: string,
|
2026-09-21 16:59:35 -07:00
|
|
|
command: "package" | "function" | "component" | "controller" | "query" | "migration" | "refresh",
|
2026-09-15 15:23:24 -07:00
|
|
|
spec: ScaffoldRecipe,
|
2026-09-21 16:59:35 -07:00
|
|
|
): Promise<StructuralRequest> {
|
2026-09-15 15:23:24 -07:00
|
|
|
if (command === "refresh")
|
2026-09-21 16:59:35 -07:00
|
|
|
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.",
|
2026-09-15 15:23:24 -07:00
|
|
|
);
|
2026-09-10 18:27:41 -07:00
|
|
|
source(spec.source);
|
2026-09-15 15:23:24 -07:00
|
|
|
if (spec.directory && !/^[A-Za-z0-9_-][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$/.test(spec.directory))
|
2026-09-21 16:59:35 -07:00
|
|
|
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);
|
2026-09-21 01:25:00 -07:00
|
|
|
if (command === "query") return scaffoldQuery(root, prefix, spec);
|
2026-09-21 16:59:35 -07:00
|
|
|
return executionScaffold(root, command, spec, prefix, nixSource);
|
|
|
|
|
}
|