| 1 | // 🤖 Drift gate for the native seed catalogue: the JSON bundled into |
| 2 | // LChatCore (Seed/app-catalog.json, decoded by AppRegistry) must equal what |
| 3 | // tools/gen-app-catalog.mjs generates from the live app framework. When an |
| 4 | // app's manifest facts change (title, glyph, places, presentation, commands), |
| 5 | // regenerate with `node tools/gen-app-catalog.mjs --write` and commit both |
| 6 | // sides — the same shape as the schema drift gate (schema-engine.test.mjs). |
| 7 | import { readFileSync } from "node:fs"; |
| 8 | import { collect, seedPath } from "../tools/gen-app-catalog.mjs"; |
| 9 | |
| 10 | const generated = JSON.stringify(await collect(), null, 2) + "\n"; |
| 11 | const committed = readFileSync(seedPath, "utf8"); |
| 12 | |
| 13 | if (generated !== committed) { |
| 14 | console.error("FAIL: the bundled native seed catalogue has drifted from the app framework."); |
| 15 | console.error("Run: node tools/gen-app-catalog.mjs --write (then commit the seed)"); |
| 16 | const a = JSON.parse(generated), b = JSON.parse(committed); |
| 17 | const an = new Set(a.map((x) => x.name)), bn = new Set(b.map((x) => x.name)); |
| 18 | const missing = [...an].filter((n) => !bn.has(n)); |
| 19 | const stale = [...bn].filter((n) => !an.has(n)); |
| 20 | if (missing.length) console.error(" missing from seed:", missing.join(", ")); |
| 21 | if (stale.length) console.error(" stale in seed:", stale.join(", ")); |
| 22 | process.exit(1); |
| 23 | } |
| 24 | |
| 25 | // A couple of load-bearing facts native routing depends on — if these change |
| 26 | // on the web side, the native routers' behaviour changes with them; fail |
| 27 | // loudly so the change is a decision, not an accident. |
| 28 | const seed = JSON.parse(committed); |
| 29 | const byName = Object.fromEntries(seed.map((a) => [a.name, a])); |
| 30 | function assert(cond, msg) { |
| 31 | if (!cond) { console.error("FAIL:", msg); process.exit(1); } |
| 32 | } |
| 33 | assert(byName.admin?.companion === true && byName.admin?.defaultView === "interface", |
| 34 | "admin must be a companion opening on its interface (the Mac Chat|View / iOS routing fix)"); |
| 35 | assert(byName.board?.places.includes("chat"), |
| 36 | "board must be group-capable (the board-in-a-group fix)"); |
| 37 | assert(byName.todo?.companion === true && byName.remind?.companion === true, |
| 38 | "todo/remind stay companions (the native schema surfaces key off this)"); |
| 39 | |
| 40 | console.log(`appcatalog-seed: seed matches the framework (${seed.length} apps)`); |
| 41 | |