import { access, mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { pathToFileURL } from "node:url"; /** @param {string} name @param {string} fallback */ function argumentValue(name, fallback) { const index = process.argv.indexOf(name); return index >= 0 && process.argv[index + 1] ? process.argv[index + 1] : fallback; } const governancePath = argumentValue( "--governance", "config/contracts/registry-governance.json", ); const artifactPath = argumentValue( "--artifact", "artifacts/quality/registries.json", ); const governance = JSON.parse( await readFile(governancePath, "utf8"), ); const failures = []; const owners = new Map(); const snapshots = []; const rowsByRegistry = new Map(); const registryExtensions = [".js", ".jsx", ".mjs", ".ts", ".tsx", ".mts"]; /** @param {string} declaredPath */ async function resolveRegistrySource(declaredPath) { const extension = path.extname(declaredPath); const basePath = extension ? declaredPath.slice(0, -extension.length) : declaredPath; const candidates = []; for (const candidateExtension of registryExtensions) { const candidate = `${basePath}${candidateExtension}`; try { await access(candidate); candidates.push(candidate); } catch { // A migration may legitimately replace the declared extension. } } if (candidates.length > 1) { failures.push( `ambiguous registry source ${declaredPath}: ${candidates.join(", ")}`, ); return null; } return candidates[0] ?? null; } for (const specification of governance.registries) { if (owners.has(specification.registryId)) { failures.push(`duplicate owner for ${specification.registryId}`); } owners.set(specification.registryId, specification.owner); let rows = specification.declaredRows; const sourcePath = await resolveRegistrySource(specification.path); try { if (!sourcePath) throw new Error("missing registry source"); const module = await import( `${pathToFileURL(path.resolve(sourcePath)).href}?registry-check=${Date.now()}` ); rows = module[specification.exportName]; } catch { if (!rows) failures.push(`missing registry source ${specification.path}`); } if (!rows || typeof rows !== "object" || Array.isArray(rows)) { failures.push(`${specification.registryId} is not an object registry`); continue; } rowsByRegistry.set(specification.registryId, rows); for (const [rowName, row] of Object.entries(rows)) { if (!row || typeof row !== "object" || Array.isArray(row)) { failures.push(`${specification.registryId}.${rowName} is not an object`); continue; } for (const field of specification.requiredFields) { if (!(field in row)) { failures.push(`${specification.registryId}.${rowName} missing ${field}`); } } } for (const field of specification.uniqueFields ?? []) { const values = new Map(); for (const [rowName, row] of Object.entries(rows)) { if (!row || typeof row !== "object" || Array.isArray(row)) continue; const value = row[field]; if (value === undefined) continue; if (values.has(value)) { failures.push( `${specification.registryId}.${rowName} duplicates ${field}=${String(value)} from ${values.get(value)}`, ); } else { values.set(value, rowName); } } } snapshots.push({ registryId: specification.registryId, owner: specification.owner, source: sourcePath ?? specification.path, rowCount: Object.keys(rows).length, rows, }); } for (const specification of governance.registries) { const rows = rowsByRegistry.get(specification.registryId); if (!rows) continue; for (const reference of specification.references ?? []) { const targetRows = rowsByRegistry.get(reference.registryId); if (!targetRows) { failures.push( `${specification.registryId} references unknown registry ${reference.registryId}`, ); continue; } const targetValues = new Set( Object.values(targetRows) .filter((row) => row && typeof row === "object" && !Array.isArray(row)) .map((row) => row[reference.targetField]) .filter((value) => value !== undefined), ); for (const [rowName, row] of Object.entries(rows)) { if (!row || typeof row !== "object" || Array.isArray(row)) continue; const value = row[reference.field]; if (value !== undefined && !targetValues.has(value)) { failures.push( `${specification.registryId}.${rowName}.${reference.field} references unknown ${reference.registryId}.${reference.targetField}=${String(value)}`, ); } } } } const sourceFiles = governance.sourceDirectories ?? [ "src/application", "src/presentation", "src/domain", ]; const adHocPatterns = [ { name: "direct fetch", expression: /\bfetch\s*\(/ }, { name: "direct localStorage", expression: /\blocalStorage\.(?:get|set|remove)Item/ }, { name: "direct import.meta.env", expression: /\bimport\.meta\.env\./ }, { name: "raw API path", expression: /["']\/api\// }, ]; /** @param {string} directory */ async function scanDirectory(directory) { const entries = await import("node:fs/promises").then(({ readdir }) => readdir(directory, { withFileTypes: true }), ); for (const entry of entries) { const target = path.join(directory, entry.name); if (entry.isDirectory()) { await scanDirectory(target); continue; } if (!/\.(js|jsx|mjs|ts|tsx|mts)$/.test(entry.name)) continue; const content = await readFile(target, "utf8"); for (const pattern of adHocPatterns) { if (pattern.expression.test(content)) { failures.push(`ad-hoc ${pattern.name} in ${target}`); } } } } for (const sourceDirectory of sourceFiles) { await scanDirectory(sourceDirectory); } await mkdir("artifacts/quality", { recursive: true }); await writeFile( artifactPath, `${JSON.stringify( { schemaVersion: 1, generatedAt: new Date().toISOString(), compatibilityImpact: governance.compatibilityImpact.current, failures, registries: snapshots, }, null, 2, )}\n`, ); if (failures.length > 0) { process.stderr.write(`Registry governance failed:\n${failures.join("\n")}\n`); process.exit(1); } process.stdout.write(`Registry governance: ${snapshots.length} registries PASS\n`);