import { readdir, readFile } from 'node:fs/promises' import { join } from 'node:path' const sourceRoot = new URL('../src/', import.meta.url) const forbidden = [ /from\s+["']three(?:\/|["'])/, /from\s+["']@types\/three/, /from\s+["']sqlite3?/, /from\s+["']@sqlite/, /from\s+["']opfs/, /SharedArrayBuffer/, /\bpostMessage\s*\(/, ] const allowDirectRuntime = new Set(['facade/threeViewport.ts', 'facade/threeViewport.tsx', 'facade/persistenceWorker.ts', 'facade/projectStore.ts', 'facade/projectMigrationSeedWorker.ts', 'facade/geometryWorker.ts', 'facade/nativeHistoryWorkerClient.ts', 'facade/nativeHistoryWorkerEntry.ts', 'facade/planegcsWorkerClient.ts', 'facade/planegcsWorkerEntry.ts']) const allowWorkerMessaging = new Set(['assemblySolverWorker.ts', 'meshLodWorker.ts', 'chromeAssemblyHarness.ts', 'chromeMeshHarness.ts', 'facade/camPipeline.ts']) async function walk(relative = '') { const directory = new URL(relative, sourceRoot) const entries = await readdir(directory, { withFileTypes: true }) const files = [] for (const entry of entries) { const next = join(relative, entry.name) if (entry.isDirectory()) files.push(...await walk(next)) else if (/\.(ts|tsx)$/.test(entry.name)) files.push(next) } return files } const violations = [] for (const file of await walk()) { if (allowDirectRuntime.has(file)) continue const content = await readFile(new URL(file, sourceRoot), 'utf8') for (const pattern of forbidden) { if (pattern.source === '\\bpostMessage\\s*\\(' && allowWorkerMessaging.has(file)) continue if (pattern.test(content)) violations.push(`${file}: ${pattern}`) } } if (violations.length) { console.error('Facade-only boundary failed:') violations.forEach((violation) => console.error(`- ${violation}`)) process.exit(1) } console.log(`Facade-only boundary passed (${(await walk()).length} TypeScript source files checked).`)