| 1 | // The apps-directory contract: web/static/apps/ holds apps and nothing else. |
| 2 | // Every .js file there calls defineApp — one app per file — so listing the |
| 3 | // directory lists the apps. Shared machinery an app leans on (the schema |
| 4 | // engine + renderer, the flow engine, Eleven's scripted-conversation feel, the |
| 5 | // Vault's lock crypto) is a sibling module in web/static/, imported as |
| 6 | // ../<name>.js. Helpers drifted in here once and made the directory a poor |
| 7 | // index of what an app IS; this test keeps it honest. |
| 8 | // node test/apps-dir.test.mjs |
| 9 | import assert from "node:assert/strict"; |
| 10 | import { readFileSync, readdirSync } from "node:fs"; |
| 11 | |
| 12 | const dir = new URL("../web/static/apps/", import.meta.url); |
| 13 | const files = readdirSync(dir).filter((f) => f.endsWith(".js")).sort(); |
| 14 | |
| 15 | // A defineApp CALL, not a mention: schema-render.js documented the framework in |
| 16 | // prose and would have passed a bare substring match. |
| 17 | const CALLS_DEFINE_APP = /\bdefineApp\s*\(/; |
| 18 | |
| 19 | const notApps = files.filter((f) => !CALLS_DEFINE_APP.test(readFileSync(new URL(f, dir), "utf8"))); |
| 20 | assert.deepStrictEqual( |
| 21 | notApps, |
| 22 | [], |
| 23 | `web/static/apps/ must contain only apps, but these call no defineApp: ${notApps.join(", ")}.\n` + |
| 24 | `Move shared machinery to web/static/ and import it as ../<name>.js.`, |
| 25 | ); |
| 26 | |
| 27 | // Guard the guard: a directory that somehow scanned empty would pass vacuously. |
| 28 | assert.ok(files.length > 10, `expected the apps directory to hold apps, found ${files.length} .js files`); |
| 29 | |
| 30 | console.log(`apps-dir tests passed (${files.length} apps, all calling defineApp)`); |
| 31 | |