import { readFileSync, readdirSync } from 'node:fs' import path from 'node:path' import { describe, expect, it } from 'vitest' const repositoryRoot = process.cwd() function sourceFiles(relativeRoot: string): string[] { const absoluteRoot = path.join(repositoryRoot, relativeRoot) return readdirSync(absoluteRoot, { recursive: true, withFileTypes: true }) .filter( (entry) => entry.isFile() && /\.(?:ts|tsx)$/u.test(entry.name) && !entry.name.endsWith('.test.ts'), ) .map((entry) => path.join(entry.parentPath, entry.name)) } function violations(files: readonly string[], forbidden: RegExp) { return files .filter((file) => forbidden.test(readFileSync(file, 'utf8'))) .map((file) => path.relative(repositoryRoot, file).replaceAll('\\', '/')) } describe('architectural dependency boundaries', () => { it('keeps persistence adapters out of Next.js route handlers and UI', () => { const routes = sourceFiles('apps/web/src/app').filter((file) => file.endsWith(`${path.sep}route.ts`), ) const ui = sourceFiles('packages/ui/src') const forbidden = /(?:@devrunbook\/db|drizzle-orm)/u expect(violations(routes, forbidden)).toEqual([]) expect(violations(ui, forbidden)).toEqual([]) }) it('keeps domain and application packages independent of framework and persistence code', () => { expect( violations( sourceFiles('packages/domain/src'), /(?:@devrunbook\/(?:application|db)|drizzle-orm|from ['"]next(?:\/|['"]))/u, ), ).toEqual([]) expect( violations( sourceFiles('packages/application/src'), /(?:@devrunbook\/db|drizzle-orm|from ['"]next(?:\/|['"]))/u, ), ).toEqual([]) }) it('prevents content and composer code from executing imported instructions', () => { const files = [ ...sourceFiles('packages/content/src'), ...sourceFiles('packages/composer/src'), ] expect( violations( files, /(?:node:child_process|child_process|Bun\.spawn|Deno\.Command)/u, ), ).toEqual([]) }) it('keeps operator commands attached to application use cases', () => { const commands = sourceFiles('apps/worker/src/operator') for (const command of commands) { const source = readFileSync(command, 'utf8') expect(source, path.relative(repositoryRoot, command)).toContain( '@devrunbook/application', ) expect(source, path.relative(repositoryRoot, command)).not.toMatch( /(?:getSqlClient|\.execute\(|sql`)/u, ) } }) it('keeps worker orchestration downstream and job handlers adapter-free', () => { const upstream = [ ...sourceFiles('packages/domain/src'), ...sourceFiles('packages/application/src'), ...sourceFiles('packages/db/src'), ...sourceFiles('apps/web/src'), ] expect( violations(upstream, /(?:@devrunbook\/worker|apps\/worker)/u), ).toEqual([]) const handlers = sourceFiles('apps/worker/src/jobs').filter((file) => file.endsWith(`${path.sep}handlers.ts`), ) expect( violations( handlers, /(?:@devrunbook\/db|drizzle-orm|getSqlClient|node:child_process|sql`)/u, ), ).toEqual([]) }) })