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
170
eslint.config.js
Normal file
170
eslint.config.js
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import js from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
import boundaries from "eslint-plugin-boundaries";
|
||||
import globals from "globals";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: ["dist/**", "node_modules/**", "coverage/**", "experiments/**"],
|
||||
},
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.strictTypeChecked,
|
||||
...tseslint.configs.stylisticTypeChecked,
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
projectService: {
|
||||
allowDefaultProject: ["eslint.config.js", "vitest.config.ts"],
|
||||
},
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
boundaries,
|
||||
},
|
||||
settings: {
|
||||
"boundaries/elements": [
|
||||
{ type: "platform", pattern: "src/platform/**" },
|
||||
{ type: "storage", pattern: "src/storage/**" },
|
||||
{ type: "data", pattern: "src/data/**" },
|
||||
{ type: "sync-verifier", pattern: "src/sync/verifier/**" },
|
||||
{ type: "sync-transport", pattern: "src/sync/transport/**" },
|
||||
{ type: "sync", pattern: "src/sync/**" },
|
||||
{ type: "domain", pattern: "src/domain/**" },
|
||||
{ type: "ui", pattern: "src/ui/**" },
|
||||
{ type: "app", pattern: "src/app/**" },
|
||||
{ type: "emergency-baseline", pattern: "src/emergency-baseline/**" },
|
||||
{ type: "tests", pattern: "tests/**" },
|
||||
{ type: "scripts", pattern: "scripts/**" },
|
||||
],
|
||||
"boundaries/ignore": ["**/*.test.ts", "**/*.spec.ts"],
|
||||
},
|
||||
rules: {
|
||||
// Architectural invariants — dependency rules B-1 … B-7
|
||||
// B-1: ui never touches platform/storage/sync/transport/fetch/idb/caches
|
||||
// Implemented via boundaries + no-restricted-globals where applicable
|
||||
"boundaries/element-types": [
|
||||
"error",
|
||||
{
|
||||
default: "disallow",
|
||||
rules: [
|
||||
// platform: low-level — may not import domain/ui/sync
|
||||
{ from: "platform", allow: [] },
|
||||
// storage: may only import platform
|
||||
{ from: "storage", allow: ["platform"] },
|
||||
// data: may import platform only
|
||||
{ from: "data", allow: ["platform"] },
|
||||
// sync-verifier: may import platform (hash) but not data/domain/ui/sync
|
||||
{ from: "sync-verifier", allow: ["platform"] },
|
||||
// sync-transport: byte pipe — may not import data/domain/ui
|
||||
{ from: "sync-transport", allow: [] },
|
||||
// sync: orchestration — may import data + sync sublayers + platform (via verifier)
|
||||
{ from: "sync", allow: ["data", "sync-verifier", "sync-transport", "platform"] },
|
||||
// domain: may import data + clock (domain/clock) — clock is domain, so allow domain internal
|
||||
{ from: "domain", allow: ["data", "domain", "emergency-baseline"] },
|
||||
// ui: may import domain + app helpers, but NOT platform/storage/sync
|
||||
{ from: "ui", allow: ["domain", "emergency-baseline"] },
|
||||
// app: composition root may import via public APIs — allow all for Stage 1, will tighten later
|
||||
{
|
||||
from: "app",
|
||||
allow: [
|
||||
"platform",
|
||||
"storage",
|
||||
"data",
|
||||
"sync",
|
||||
"sync-verifier",
|
||||
"sync-transport",
|
||||
"domain",
|
||||
"ui",
|
||||
"emergency-baseline",
|
||||
],
|
||||
},
|
||||
// emergency-baseline: generated, no deps
|
||||
{ from: "emergency-baseline", allow: [] },
|
||||
// tests/scripts may import anything for validation
|
||||
{
|
||||
from: "tests",
|
||||
allow: [
|
||||
"platform",
|
||||
"storage",
|
||||
"data",
|
||||
"sync",
|
||||
"sync-verifier",
|
||||
"sync-transport",
|
||||
"domain",
|
||||
"ui",
|
||||
"app",
|
||||
"emergency-baseline",
|
||||
],
|
||||
},
|
||||
{
|
||||
from: "scripts",
|
||||
allow: [
|
||||
"platform",
|
||||
"storage",
|
||||
"data",
|
||||
"sync",
|
||||
"sync-verifier",
|
||||
"sync-transport",
|
||||
"domain",
|
||||
"ui",
|
||||
"app",
|
||||
"emergency-baseline",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
// Explicit forbidden globals in ui (B-1 core)
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
name: "indexedDB",
|
||||
message: "B-1: ui must not touch indexedDB — use data/ read APIs only.",
|
||||
},
|
||||
{
|
||||
name: "caches",
|
||||
message: "B-1: ui must not touch caches — use platform/cache only via data/sync.",
|
||||
},
|
||||
],
|
||||
// General strictness
|
||||
"no-console": ["warn", { allow: ["warn", "error"] }],
|
||||
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
||||
"@typescript-eslint/consistent-type-imports": "error",
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@typescript-eslint/no-unnecessary-condition": "warn",
|
||||
},
|
||||
},
|
||||
{
|
||||
// B-7: no innerHTML with festival content — forbid assignment to innerHTML in src
|
||||
files: ["src/**/*.ts"],
|
||||
rules: {
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "AssignmentExpression[left.property.name='innerHTML']",
|
||||
message:
|
||||
"B-7: Do not render festival content via innerHTML of raw strings — use ui/render safe DOM (C-22).",
|
||||
},
|
||||
{
|
||||
selector: "CallExpression[callee.property.name='insertAdjacentHTML']",
|
||||
message:
|
||||
"B-7: Do not use insertAdjacentHTML with festival content — use ui/render safe DOM (C-22).",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
// Allow explicit forbidden-globals only where justified; tests may use them
|
||||
files: ["tests/**/*.ts", "scripts/**/*.ts"],
|
||||
rules: {
|
||||
"no-restricted-globals": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
},
|
||||
},
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue