#!/usr/bin/env node /** * Enforce valid JSON everywhere and schemaVersion markers on SF-owned contracts. * * Ecosystem JSON such as package.json, tsconfig.json, lockfiles, and extension * manifests are parsed for validity but are not treated as SF data contracts. * Their `version` fields belong to their owning tools or component release * lifecycle. SF-owned runtime/data contracts use `schemaVersion` for shape * compatibility. */ import { execFileSync } from "node:child_process"; import { existsSync, readFileSync } from "node:fs"; const CONTRACT_EXACT_PATHS = new Set([ "src/resources/extensions/sf/workflow-templates/registry.json", ]); const CONTRACT_PREFIXES = ["src/resources/extensions/sf/learning/data/"]; 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((line) => line && existsSync(line)); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`failed to list tracked JSON files: ${message}`); } } export function isSfOwnedJsonContract(path) { return ( CONTRACT_EXACT_PATHS.has(path) || CONTRACT_PREFIXES.some((prefix) => path.startsWith(prefix)) ); } export function hasOwn(object, key) { return Object.hasOwn(object, key); } export function getSchemaVersion(parsed) { if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false; if (hasOwn(parsed, "schemaVersion")) return parsed.schemaVersion; const meta = parsed._meta; if ( meta && typeof meta === "object" && !Array.isArray(meta) && hasOwn(meta, "schemaVersion") ) { return meta.schemaVersion; } return undefined; } export function hasValidSchemaVersion(parsed) { const schemaVersion = getSchemaVersion(parsed); return ( typeof schemaVersion === "number" && Number.isInteger(schemaVersion) && schemaVersion >= 1 ); } export function checkJsonPolicy(paths, readText) { const failures = []; let contractsChecked = 0; let filesParsed = 0; for (const path of paths) { filesParsed++; let parsed; try { parsed = JSON.parse(readText(path)); } catch (error) { if ( error && typeof error === "object" && "code" in error && error.code === "ENOENT" ) { filesParsed--; continue; } const message = error instanceof Error ? error.message : String(error); failures.push(`${path}: invalid JSON (${message})`); continue; } if (!isSfOwnedJsonContract(path)) continue; contractsChecked++; if (!hasValidSchemaVersion(parsed)) { failures.push( `${path}: missing numeric schemaVersion marker (top-level or _meta)`, ); } } return { failures, filesParsed, contractsChecked }; } export function run() { const result = checkJsonPolicy(trackedJsonFiles(), (path) => readFileSync(path, "utf8"), ); if (result.failures.length > 0) { console.error("Versioned JSON check failed:"); for (const failure of result.failures) { console.error(` - ${failure}`); } process.exit(1); } console.log( `Versioned JSON check passed (${result.filesParsed} JSON file${result.filesParsed === 1 ? "" : "s"} parsed, ` + `${result.contractsChecked} SF contract${result.contractsChecked === 1 ? "" : "s"} checked).`, ); } if (import.meta.url === `file://${process.argv[1]}`) { run(); }