Files
WRNexusJS/packages/cli/src/contracts-command.ts
T
Clintchiz 586a6db8ff
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-02 23:18:51 +05:30

81 lines
3.2 KiB
TypeScript

import { mkdir, readFile, writeFile } from "node:fs/promises";
import { existsSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import {
ContractRegistry,
checkContractCompatibility,
type ContractSnapshot,
} from "@wrnexus/validation";
const CURRENT_FILE = "wrnexus.contracts.json";
const BASELINE_FILE = join(".wrnexus", "contracts.json");
function validateSnapshot(value: unknown, source: string): ContractSnapshot {
const snapshot = value as Partial<ContractSnapshot>;
if (snapshot?.format !== 1 || !Array.isArray(snapshot.contracts)) {
throw new Error(`WRN-CONTRACT-FORMAT: ${source} is not a version 1 contract snapshot.`);
}
return snapshot as ContractSnapshot;
}
async function currentSnapshot(appRoot: string): Promise<ContractSnapshot> {
const modulePath = join(appRoot, "app", "contracts.ts");
if (existsSync(modulePath)) {
const imported = (await import(`${modulePath}?t=${Date.now()}`)) as {
default?: ContractRegistry | ContractSnapshot;
contracts?: ContractRegistry | ContractSnapshot;
};
const value = imported.contracts ?? imported.default;
if (value instanceof ContractRegistry) return value.snapshot();
return validateSnapshot(value, modulePath);
}
const jsonPath = join(appRoot, CURRENT_FILE);
if (!existsSync(jsonPath)) {
throw new Error(
`WRN-CONTRACT-SOURCE: create app/contracts.ts exporting a ContractRegistry, or ${CURRENT_FILE}.`,
);
}
return validateSnapshot(JSON.parse(await readFile(jsonPath, "utf8")), jsonPath);
}
export interface ContractCommandResult {
ok: boolean;
issueCount: number;
baseline: string;
}
export async function runContractsCommand(
root: string,
command = "check",
args: string[] = [],
): Promise<ContractCommandResult> {
const appRoot = resolve(root);
const baseline = join(appRoot, BASELINE_FILE);
const current = await currentSnapshot(appRoot);
if (command === "snapshot") {
await mkdir(dirname(baseline), { recursive: true });
await writeFile(baseline, `${JSON.stringify(current, null, 2)}\n`, "utf8");
if (args.includes("--json")) console.log(JSON.stringify({ ok: true, baseline }));
else console.log(`✓ Contract baseline written: ${baseline}`);
return { ok: true, issueCount: 0, baseline };
}
if (command !== "check") throw new Error(`WRN-CONTRACT-COMMAND: unknown command '${command}'.`);
if (!existsSync(baseline)) {
throw new Error(`WRN-CONTRACT-BASELINE: no baseline found; run 'wrnexus contracts snapshot'.`);
}
const previous = validateSnapshot(JSON.parse(await readFile(baseline, "utf8")), baseline);
const issues = checkContractCompatibility(previous, current);
if (args.includes("--json")) {
console.log(JSON.stringify({ ok: issues.length === 0, issues, baseline }, null, 2));
} else if (issues.length === 0) {
console.log(`✓ ${current.contracts.length} contracts are backward compatible.`);
} else {
console.error(`Breaking contract changes detected (${issues.length}):`);
for (const issue of issues) {
console.error(` ${issue.code} ${issue.contract}: ${issue.message}`);
if (issue.consumers.length) console.error(` Consumers: ${issue.consumers.join(", ")}`);
}
}
return { ok: issues.length === 0, issueCount: issues.length, baseline };
}