#!/usr/bin/env node // Validate the scroll registry and manifests with the same checks the page runs. // node tools/validate_scroll.mjs all scrolls in scrolls/index.json // node tools/validate_scroll.mjs path/to/manifest.json [--local] // --local applies the limits used for "Open your own" (as a visitor's browser would). // Exit code 1 if anything is wrong. Git LFS pointer files count as present. import { readFileSync, statSync, existsSync } from 'node:fs'; import { dirname, resolve, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { validateManifest, validateRegistry, LOCAL_LIMITS, REGISTRY_LIMITS, peelOrder } from '../lib/manifest.js'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const args = process.argv.slice(2); const local = args.includes('--local'); const paths = args.filter((a) => !a.startsWith('--')); let bad = 0; function lfsSize(p) { // an LFS pointer stands for the real file: report the size it records const st = statSync(p); if (st.size < 400) { const t = readFileSync(p, 'utf8'); const m = /^version https:\/\/git-lfs.*\nsize (\d+)/m.exec(t); if (m) return +m[1]; } return st.size; } function check(manifestPath, trusted) { let m; try { m = JSON.parse(readFileSync(manifestPath, 'utf8')); } catch (e) { console.log(`FAIL ${manifestPath}: ${e.message}`); bad++; return; } const base = resolve(dirname(manifestPath), m.base || ''); const v = validateManifest(m, { trusted, limits: trusted ? REGISTRY_LIMITS : LOCAL_LIMITS, fileSize: (p) => { const f = join(base, p); return existsSync(f) ? lfsSize(f) : null; }, }); const n = v.ok ? peelOrder(m).length : 0; console.log(`${v.ok ? 'ok ' : 'FAIL'} ${manifestPath}${v.ok ? `: ${m.id}, ${n} windings, ${m.layers.length} layers, ${v.files.length} files` : ''}`); for (const e of v.errors) console.log(` error: ${e}`); for (const w of v.warnings) console.log(` note: ${w}`); if (!v.ok) bad++; } if (paths.length) for (const p of paths) check(resolve(p), !local); else { const reg = JSON.parse(readFileSync(join(root, 'scrolls/index.json'), 'utf8')); const r = validateRegistry(reg); console.log(`${r.ok ? 'ok ' : 'FAIL'} scrolls/index.json: ${reg.scrolls?.length ?? 0} scrolls`); for (const e of r.errors) console.log(` error: ${e}`); if (!r.ok) bad++; for (const s of reg.scrolls || []) check(join(root, s.manifest), true); } process.exit(bad ? 1 : 0);