#!/usr/bin/env node /** * Validate config/universal-resource-activation/manifest.json against * - universal-resource-activation.manifest.v1.schema.json * - universal-resource-activation.resource.v1.schema.json (per item in resources[]) * - universal-resource-activation.evidence-package.v1.schema.json (per item in evidencePackages[]) * * Usage: from repo root: node scripts/validate/validate-universal-resource-activation.mjs * Exit 0 on success, 1 on error. */ import { readFileSync, existsSync } from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import Ajv from 'ajv'; import addFormats from 'ajv-formats'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const projectRoot = path.join(__dirname, '../..'); const configDir = path.join(projectRoot, 'config', 'universal-resource-activation'); const manifestPath = path.join(configDir, 'manifest.json'); const manifestSchemaPath = path.join(projectRoot, 'config', 'universal-resource-activation.manifest.v1.schema.json'); const resourceSchemaPath = path.join(projectRoot, 'config', 'universal-resource-activation.resource.v1.schema.json'); const evidenceSchemaPath = path.join(projectRoot, 'config', 'universal-resource-activation.evidence-package.v1.schema.json'); function fail(msg) { console.error(`[validate-ura] ${msg}`); process.exit(1); } if (!existsSync(manifestPath)) { fail(`Missing ${manifestPath}`); } const ajv = new Ajv({ allErrors: true, strict: false, validateSchema: false, }); addFormats(ajv); const manifestSchema = JSON.parse(readFileSync(manifestSchemaPath, 'utf8')); const resourceSchema = JSON.parse(readFileSync(resourceSchemaPath, 'utf8')); const evidenceSchema = JSON.parse(readFileSync(evidenceSchemaPath, 'utf8')); const validateManifest = ajv.compile(manifestSchema); const validateResource = ajv.compile(resourceSchema); const validateEvidence = ajv.compile(evidenceSchema); const raw = readFileSync(manifestPath, 'utf8'); let data; try { data = JSON.parse(raw); } catch (e) { fail(`Invalid JSON: ${e.message}`); } if (!validateManifest(data)) { fail(`Manifest failed manifest schema: ${ajv.errorsText(validateManifest.errors, { separator: '\n' })}`); } if (!Array.isArray(data.resources)) { fail('resources must be an array'); } if (!Array.isArray(data.evidencePackages)) { fail('evidencePackages must be an array'); } data.resources.forEach((r, i) => { if (!validateResource(r)) { fail( `resources[${i}] (resourceId=${r?.resourceId}): ${ajv.errorsText(validateResource.errors, { separator: '\n' })}` ); } }); data.evidencePackages.forEach((p, i) => { if (!validateEvidence(p)) { fail( `evidencePackages[${i}] (id=${p?.evidencePackageId}): ${ajv.errorsText(validateEvidence.errors, { separator: '\n' })}` ); } }); // Cross-check: all resourceIds referenced in evidence exist const ids = new Set(data.resources.map((r) => r.resourceId).filter(Boolean)); data.evidencePackages.forEach((p, pi) => { (p.resourceIds || []).forEach((rid) => { if (!ids.has(rid)) { fail( `evidencePackages[${pi}] references unknown resourceId: ${rid}` ); } }); }); console.log( `[validate-ura] OK: ${data.resources.length} resource(s), ${data.evidencePackages.length} evidence package(s)` ); process.exit(0);