singularity-forge/scripts/check-versioned-json.mjs
2026-04-30 07:41:24 +02:00

89 lines
2.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Enforce schema/version markers on SF-owned JSON contracts.
*
* This intentionally does not scan ecosystem configuration files such as
* tsconfig.json, package.json, Biome config, or lockfiles. Those files are
* versioned by their owning tools. This check covers JSON that SF owns as
* runtime data, persisted contracts, or generated artifact templates.
*/
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";
const REQUIRED_PREFIXES = ["src/resources/extensions/sf/"];
const EXEMPT_SUFFIXES = ["/package.json"];
const VERSION_KEYS = ["schemaVersion", "version"];
function trackedJsonFiles() {
try {
const out = execFileSync("git", ["ls-files", "*.json"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
return out
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`failed to list tracked JSON files: ${message}`);
}
}
function shouldCheck(path) {
return (
REQUIRED_PREFIXES.some((prefix) => path.startsWith(prefix)) &&
!EXEMPT_SUFFIXES.some((suffix) => path.endsWith(suffix))
);
}
function hasOwn(object, key) {
return Object.prototype.hasOwnProperty.call(object, key);
}
function hasVersionMarker(parsed) {
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
if (VERSION_KEYS.some((key) => hasOwn(parsed, key))) return true;
const meta = parsed._meta;
return Boolean(
meta &&
typeof meta === "object" &&
!Array.isArray(meta) &&
VERSION_KEYS.some((key) => hasOwn(meta, key)),
);
}
const failures = [];
let checked = 0;
for (const path of trackedJsonFiles()) {
if (!shouldCheck(path)) continue;
checked++;
let parsed;
try {
parsed = JSON.parse(readFileSync(path, "utf8"));
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
failures.push(`${path}: invalid JSON (${message})`);
continue;
}
if (!hasVersionMarker(parsed)) {
failures.push(
`${path}: missing schemaVersion/version marker (top-level or _meta)`,
);
}
}
if (failures.length > 0) {
console.error("Versioned JSON check failed:");
for (const failure of failures) {
console.error(` - ${failure}`);
}
process.exit(1);
}
console.log(`Versioned JSON check passed (${checked} file${checked === 1 ? "" : "s"}).`);