import { expect, test } from "bun:test" import { existsSync, readdirSync, readFileSync, statSync } from "node:fs" import path from "node:path" // The engine is the reusable, content-agnostic layer: it must import nothing from // game (content) or app (browser glue). game -> engine and app -> game -> engine are // fine; the reverse is the violation. This freezes the Stage-3 seam so a stray import // can't quietly re-couple the layers. function tsFiles(dir: string): string[] { const out: string[] = [] for (const name of readdirSync(dir)) { const p = path.join(dir, name) if (statSync(p).isDirectory()) { out.push(...tsFiles(p)) } else if (p.endsWith(".ts")) { out.push(p) } } return out } function repositoryRoot(): string { const parent = new URL("..", import.meta.url).pathname return existsSync(path.join(parent, "app/renderer.ts")) ? parent : path.join(parent, "..") } function importsLayer(source: string, layers: string): boolean { return new RegExp(`(?:from\\s+|import\\s*(?:\\(\\s*)?)["'][^"']*/(?:${layers})/`).test(source) } test("engine imports nothing from game or app", () => { const engineDir = path.join(repositoryRoot(), "engine") const offenders = tsFiles(engineDir).filter((file) => importsLayer(readFileSync(file, "utf8"), "game|app")) expect(offenders).toEqual([]) }) test("game imports nothing from app", () => { const gameDir = path.join(repositoryRoot(), "game") const offenders = tsFiles(gameDir).filter((file) => importsLayer(readFileSync(file, "utf8"), "app")) expect(offenders).toEqual([]) }) test("render driver and worker know no game content", () => { const root = repositoryRoot() const files = [path.join(root, "app/renderer.ts"), path.join(root, "app/render-worker.ts")] const offenders = files.filter((file) => importsLayer(readFileSync(file, "utf8"), "game")) expect(offenders).toEqual([]) }) test("game exports no closed content-kind registries", () => { const gameDir = path.join(repositoryRoot(), "game") const offenders = tsFiles(gameDir).filter((file) => { const source = readFileSync(file, "utf8") return ( /\b(?:export\s+)?(?:type|enum)\s+\w+Kind\b/.test(source) || /\b(?:const|let|var)\s+\w*_(?:KINDS|REGISTRY)\b/.test(source) ) }) expect(offenders).toEqual([]) })