Stage 1: project foundation (strict TS, lint boundaries B-1..B-7, directory structure, CI, boundary tests)
- 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
This commit is contained in:
commit
c0bfd413ff
100 changed files with 9863 additions and 0 deletions
70
tests/unit/boundaries.test.ts
Normal file
70
tests/unit/boundaries.test.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { readdirSync, existsSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
function walkFiles(dir: string, exts = [".ts", ".js"]): string[] {
|
||||
const out: string[] = [];
|
||||
function walk(d: string) {
|
||||
if (!existsSync(d)) return;
|
||||
for (const e of readdirSync(d, { withFileTypes: true })) {
|
||||
const p = join(d, e.name);
|
||||
if (e.isDirectory()) walk(p);
|
||||
else if (exts.some((ext) => p.endsWith(ext))) out.push(p);
|
||||
}
|
||||
}
|
||||
walk(dir);
|
||||
return out;
|
||||
}
|
||||
|
||||
describe("architectural boundaries — Stage 1 gate (B-1…B-7)", () => {
|
||||
it("src/ contains no feature code yet (only READMEs/.gitkeep)", () => {
|
||||
const tsFiles = walkFiles("src", [".ts"]);
|
||||
// Stage 1 must have zero feature .ts files — directories exist but are empty
|
||||
expect(tsFiles).toEqual([]);
|
||||
});
|
||||
|
||||
it("directory structure matches IMPLEMENTATION-CONTRACT.md §4", () => {
|
||||
const requiredDirs = [
|
||||
"src/platform/idb",
|
||||
"src/platform/cache",
|
||||
"src/platform/sw",
|
||||
"src/storage",
|
||||
"src/data",
|
||||
"src/sync/transport",
|
||||
"src/sync/verifier",
|
||||
"src/domain/emergency",
|
||||
"src/domain/schedule",
|
||||
"src/domain/map",
|
||||
"src/domain/festival",
|
||||
"src/domain/readiness",
|
||||
"src/domain/clock",
|
||||
"src/domain/favorites",
|
||||
"src/ui/components",
|
||||
"src/ui/views",
|
||||
"src/ui/router",
|
||||
"src/ui/render",
|
||||
"src/app",
|
||||
"src/emergency-baseline",
|
||||
"public",
|
||||
"content",
|
||||
"pipeline",
|
||||
"tests/unit",
|
||||
"tests/integration",
|
||||
"tests/device-matrix",
|
||||
];
|
||||
for (const d of requiredDirs) expect(existsSync(d), `missing dir ${d}`).toBe(true);
|
||||
});
|
||||
|
||||
it("forbidden v1 scope is not present (§2 — mesh/accounts/auth)", () => {
|
||||
const allFiles = [...walkFiles("src"), ...walkFiles("tests")].join("\n");
|
||||
expect(allFiles).not.toMatch(/mesh/i);
|
||||
expect(allFiles).not.toMatch(/auth/i);
|
||||
});
|
||||
|
||||
it("eslint boundaries config exists and lists B-1…B-7 elements", async () => {
|
||||
// eslint check is via lint job; dynamic import of .js config has no types
|
||||
// @ts-expect-error — no types for flat config
|
||||
const cfg: unknown = await import("../../eslint.config.js");
|
||||
expect(cfg).toBeDefined();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue