- dedicated git repo at /home/avi/Projects/Lumen (main)
- TypeScript strict (target ES2022, bundler, exactOptionalPropertyTypes, noUncheckedIndexedAccess)
- ESLint 9 + typescript-eslint strictTypeChecked + eslint-plugin-boundaries for B-1..B-7, no-restricted-globals/syntax for B-1/B-7
- Prettier 3.5
- Structure per IMPLEMENTATION-CONTRACT.md §4 (src/platform/idb|cache|sw, storage, data, sync/{transport,verifier}, domain/{emergency,schedule,map,festival,readiness,clock,favorites}, ui/{components,views,router,render}, app, emergency-baseline, assets, public, content, pipeline, tests, scripts)
- CI: .github/workflows/ci.yml (typecheck + lint + format + test)
- Boundary tests: tests/unit/boundaries.test.ts (4 tests) + scripts/check-boundaries.ts
- No feature code, no PWA/IDB/sync/mesh/accounts per contract Stage 1
98 lines
3.3 KiB
TypeScript
98 lines
3.3 KiB
TypeScript
/**
|
|
* Stage 1 — architectural boundary gate.
|
|
* Minimal check that no source file violates B-1 … B-7 by string scanning
|
|
* (full boundary enforcement is via eslint-plugin-boundaries in `npm run lint`).
|
|
* This script is a second cheap gate for CI and for `tests/unit/boundaries.test.ts`.
|
|
*
|
|
* Rules checked (subset where string grep is sufficient for Stage 1):
|
|
* B-1 ui must not import indexedDB/caches/fetch/localStorage
|
|
* B-7 no innerHTML/insertAdjacentHTML in src
|
|
*/
|
|
import { readdirSync, readFileSync, existsSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
|
|
const SRC = "src";
|
|
|
|
const violations: string[] = [];
|
|
|
|
function walk(dir: string, cb: (file: string) => void): void {
|
|
if (!existsSync(dir)) return;
|
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
const p = join(dir, entry.name);
|
|
if (entry.isDirectory()) walk(p, cb);
|
|
else if (entry.isFile() && (p.endsWith(".ts") || p.endsWith(".js"))) cb(p);
|
|
}
|
|
}
|
|
|
|
const B1_FORBIDDEN_IN_UI = [
|
|
/from\s+["'].*platform\//,
|
|
/from\s+["'].*storage\//,
|
|
/from\s+["'].*sync\//,
|
|
/indexedDB/,
|
|
/\bcaches\b/,
|
|
/\blocalStorage\b/,
|
|
];
|
|
|
|
walk(SRC, (file) => {
|
|
const content = readFileSync(file, "utf8");
|
|
const isUI = file.startsWith("src/ui/");
|
|
const isDomain = file.startsWith("src/domain/");
|
|
const isSyncTransport = file.startsWith("src/sync/transport/");
|
|
const isPlatform = file.startsWith("src/platform/");
|
|
|
|
if (isUI) {
|
|
for (const rx of B1_FORBIDDEN_IN_UI) {
|
|
if (rx.test(content)) {
|
|
// allow comments that mention the pattern; require actual import usage
|
|
// simple heuristic: check import line
|
|
const lines = content.split("\n");
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line: string = lines[i] ?? "";
|
|
if (rx.test(line) && /import|require/.test(line)) {
|
|
violations.push(
|
|
`${file}:${String(i + 1)}: B-1 violation — ui must not touch platform/storage/sync or raw indexedDB/caches/localStorage — line: ${line.trim()}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// B-7 innerHTML
|
|
if (file.includes("src/")) {
|
|
const lines = content.split("\n");
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line: string = lines[i] ?? "";
|
|
if (/\.innerHTML\s*=/.test(line) || line.includes("insertAdjacentHTML")) {
|
|
violations.push(
|
|
`${file}:${String(i + 1)}: B-7 violation — do not use innerHTML/insertAdjacentHTML with festival content (C-22).`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// B-2 domain must not import platform/sync
|
|
if (isDomain && /from\s+["'].*platform\//.test(content)) {
|
|
violations.push(`${file}: B-2 violation — domain must not import platform.`);
|
|
}
|
|
|
|
// sync-transport must not import data/domain
|
|
if (isSyncTransport && /from\s+["'].*(data|domain)\//.test(content)) {
|
|
violations.push(`${file}: B-3 violation — transport must not import data/domain.`);
|
|
}
|
|
|
|
// platform must not import domain/ui/sync
|
|
if (isPlatform && /from\s+["'].*(domain|ui|sync)\//.test(content)) {
|
|
violations.push(`${file}: platform must not import domain/ui/sync.`);
|
|
}
|
|
});
|
|
|
|
if (violations.length) {
|
|
console.error("Boundary violations found:");
|
|
for (const v of violations) console.error(" " + v);
|
|
process.exit(1);
|
|
} else {
|
|
console.warn(
|
|
"Boundary check OK — no B-1…B-7 string violations in src/ (full check via eslint boundaries).",
|
|
);
|
|
}
|