Files
quixos-protocol/src/capability-language/checked-resources.ts
T

41 lines
2.0 KiB
TypeScript
Raw Normal View History

import { readFile } from "node:fs/promises";
import type { CheckedResourceResolver } from "./assembly.js";
import type { CapabilityResource } from "./parser.js";
/** Internal compiler inputs produced by checked-candidate.nix, not authoring
* receipts. The Nix composition owns their derivations and exact toolchain.
* Ordinary compiler callers omit this map and run all checks directly. */
export async function loadCheckedResources(filename?: string): Promise<CheckedResourceResolver | undefined> {
if (!filename) return undefined;
const entries = JSON.parse(await readFile(filename, "utf8")) as Array<{
kind: string;
repository: string;
commit: string;
artifact: string;
}>;
const resources = new Map<string, string>();
for (const entry of entries) {
const key = `${entry.kind}\0${entry.repository}\0${entry.commit}`;
if (resources.has(key)) throw new Error("Repeated checked resource input");
if (!/^\/nix\/store\/[^/]+$/.test(entry.artifact))
throw new Error("Checked resources must be immutable Nix outputs");
resources.set(key, entry.artifact);
}
return async (kind, source, lock, dependencies) => {
const artifact = resources.get(`${kind}\0${source.repository}\0${source.commit}`);
if (!artifact) return undefined;
const graph = JSON.parse(await readFile(`${artifact}/graph.json`, "utf8"));
const expected = [...dependencies].map(([binding, node]) => ({ binding, resourceKey: node.key }));
if (
graph.quixos.commit !== lock.quixos.commit ||
graph.quixos.repository !== lock.quixos.repository ||
graph.root.kind !== kind ||
graph.root.source.repository !== source.repository ||
graph.root.source.commit !== source.commit ||
JSON.stringify(graph.root.dependencies) !== JSON.stringify(expected)
)
throw new Error("Checked resource input does not match its locked dependency graph");
return JSON.parse(await readFile(`${artifact}/candidate.json`, "utf8")) as CapabilityResource;
};
}