/** * Import-boundary law (Bottom-Up §06 dependency law; Critics §12). * * Each package declares exactly which other @insignia/iios-* packages it MAY * import. Anything else is a violation. This is an allowlist, not a ladder — * it correctly forbids sibling imports (e.g. the SDK must never import the * backend service, and vice-versa). Runnable as `pnpm boundary` (CI) + a test. */ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, join, resolve } from 'node:path'; const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..'); /** package -> the @insignia/iios-* packages it is allowed to import. */ const ALLOWED = { '@insignia/iios-contracts': [], '@insignia/iios-testkit': ['@insignia/iios-contracts'], '@insignia/iios-kernel-client': ['@insignia/iios-contracts'], '@insignia/iios-adapter-sdk': ['@insignia/iios-contracts'], '@insignia/iios-service': ['@insignia/iios-contracts', '@insignia/iios-testkit', '@insignia/iios-adapter-sdk'], '@insignia/iios-message-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'], '@insignia/iios-inbox-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'], '@insignia/iios-community-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'], '@insignia/iios-ai-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'], '@insignia/iios-support-web': [ '@insignia/iios-contracts', '@insignia/iios-kernel-client', '@insignia/iios-message-web', '@insignia/iios-inbox-web', ], }; const KNOWN = Object.keys(ALLOWED); function listTsFiles(dir) { if (!existsSync(dir)) return []; const out = []; for (const entry of readdirSync(dir)) { if (entry === 'node_modules' || entry === 'dist') continue; const full = join(dir, entry); if (statSync(full).isDirectory()) out.push(...listTsFiles(full)); else if (full.endsWith('.ts') || full.endsWith('.tsx')) out.push(full); } return out; } function packageOf(importPath) { return KNOWN.find((k) => importPath === k || importPath.startsWith(k + '/')); } export function findBoundaryViolations() { const pkgsDir = join(ROOT, 'packages'); const violations = []; if (!existsSync(pkgsDir)) return violations; for (const pkg of readdirSync(pkgsDir)) { const pkgJsonPath = join(pkgsDir, pkg, 'package.json'); if (!existsSync(pkgJsonPath)) continue; const name = JSON.parse(readFileSync(pkgJsonPath, 'utf8')).name; const allowed = ALLOWED[name]; if (!allowed) continue; // not a governed package const patterns = [ /from\s*['"](@insignia\/iios-[^'"]+)['"]/g, /import\s*['"](@insignia\/iios-[^'"]+)['"]/g, /import\s*\(\s*['"](@insignia\/iios-[^'"]+)['"]/g, /require\(\s*['"](@insignia\/iios-[^'"]+)['"]/g, ]; for (const file of listTsFiles(join(pkgsDir, pkg, 'src'))) { const content = readFileSync(file, 'utf8'); for (const re of patterns) { let m; while ((m = re.exec(content))) { const target = packageOf(m[1]); if (target && target !== name && !allowed.includes(target)) { violations.push({ file: file.replace(ROOT + '/', ''), from: name, imports: target }); } } } } } return violations; } if (import.meta.url === `file://${process.argv[1]}`) { const violations = findBoundaryViolations(); if (violations.length > 0) { console.error('✗ import-boundary violations (a package imports a forbidden dependency):'); for (const v of violations) console.error(` ${v.file}: ${v.from} → ${v.imports}`); process.exit(1); } console.log('✓ import-boundary: OK'); }