#!/usr/bin/env node /** * Generate the site's read-only service catalog from the API's single source. * * Source : api/src/service-catalog.ts (COMPLIANCE_SERVICES -- the authority) * Output : site/src/lib/service-catalog.generated.ts (SERVICE_META for display) * * Run automatically before the site build (site `prebuild` script). Never edit * the generated file by hand. To change a price or name, edit the API catalog. * * We import the API module directly (it is plain TS with a literal object), via * a tiny transpile-free parse: the catalog is a static object literal, so we * evaluate it in a sandboxed module context. */ import { readFileSync, writeFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, resolve } from "node:path"; const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT = resolve(__dirname, ".."); const SRC = resolve(ROOT, "api/src/service-catalog.ts"); const OUT = resolve(ROOT, "site/src/lib/service-catalog.generated.ts"); const tsSource = readFileSync(SRC, "utf8"); // Extract the object literal assigned to COMPLIANCE_SERVICES. const m = tsSource.match(/export const COMPLIANCE_SERVICES[^=]*=\s*(\{[\s\S]*?\n\});/); if (!m) { console.error("[gen-service-catalog] could not locate COMPLIANCE_SERVICES object literal in", SRC); process.exit(1); } // The object literal is valid JS (string keys, number/bool/string values, trailing commas). // Evaluate it safely in an isolated function scope (no access to anything). let catalog; try { catalog = Function(`"use strict"; return (${m[1]});`)(); } catch (e) { console.error("[gen-service-catalog] failed to evaluate catalog literal:", e); process.exit(1); } const slugs = Object.keys(catalog).sort(); const lines = slugs.map((slug) => { const s = catalog[slug]; const parts = [`name: ${JSON.stringify(s.name)}`, `price_cents: ${s.price_cents}`]; if (s.gov_fee_label) parts.push(`gov_fee_label: ${JSON.stringify(s.gov_fee_label)}`); return ` ${JSON.stringify(slug)}: { ${parts.join(", ")} },`; }); const out = `/** * AUTO-GENERATED -- DO NOT EDIT. * * Source of truth: api/src/service-catalog.ts (COMPLIANCE_SERVICES). * Regenerate with: node scripts/gen-service-catalog.mjs (runs on site prebuild). * * Display-only subset (name, price_cents, gov_fee_label) of the API catalog, * so the public site can never drift from what the API actually charges. */ export interface ServiceMeta { name: string; price_cents: number; gov_fee_label?: string; } export const SERVICE_META: Record = { ${lines.join("\n")} }; `; writeFileSync(OUT, out); console.log(`[gen-service-catalog] wrote ${slugs.length} services -> ${OUT}`);