chore: merge executor worktree (01-02 eslint-firewall)

This commit is contained in:
2026-05-08 23:48:11 -04:00
8 changed files with 1285 additions and 1 deletions
@@ -0,0 +1,268 @@
---
phase: 01-foundations-and-doctrine
plan: 02
subsystem: infra
tags: [eslint, eslint-plugin-boundaries, typescript-eslint, firewall, lint, vitest, architectural-firewall]
# Dependency graph
requires:
- phase: 01-foundations-and-doctrine/01
provides: "Phaser 4 + React 19 + Vite 8 + TS 6 scaffold with the seven src/ firewall directories pre-created (sim, render, ui, save, content, audio, store), ESLint 9.39.4 + eslint-plugin-boundaries 6.0.2 pre-installed, npm 'lint' script pre-declared with --max-warnings 0"
provides:
- "ESLint 9 flat config (eslint.config.js) declaring 9 element types — the seven Phase-1 firewall subsystems plus the template's app + game — and one rule (severity: error): src/sim/ MUST NOT import from src/render/ or src/ui/ (CORE-10)"
- "Deliberate-violation fixture (src/sim/__test_violation__/violator.ts) excluded from default lint glob via the eslint.config.js ignores block"
- "Vitest test (src/sim/__test_violation__/lint-firewall.test.ts) that runs ESLint programmatically against the violator and asserts boundaries/element-types fires with severity=error and message containing both 'sim' and 'render|ui'"
- "Render-side stub file (src/render/__firewall_target__.ts) — minimal export so the boundaries plugin can resolve the violator's import to a real path on disk. Without this, the plugin marks the import target as isUnknown and silently skips the rule (verified empirically; see Deviations)."
- "TypeScript-aware import resolution for ESLint via eslint-import-resolver-typescript (devDep), wired through eslint.config.js settings.import/resolver"
- "Build-glob exclusions in tsconfig.app.json for *.test.ts and src/sim/__test_violation__/** so 'tsc -b' does not try to typecheck Vitest test files (which use Node APIs) under the DOM-only project lib settings"
affects: [01-03-save-layer, 01-04-content-pipeline, 01-05-asset-provenance, 01-07-ci-workflow, 02-onwards]
# Tech tracking
tech-stack:
added:
- "typescript-eslint@^8.59.2 — parser only (provides @typescript-eslint/parser bundled). NO rule sets enabled. Required because ESLint's default Espree parser cannot parse .ts/.tsx syntax. Documented as a Plan 02 deviation (Rule 3 — Blocking) below."
- "eslint-import-resolver-typescript@^4 — required by eslint-plugin-boundaries to follow extension-less TS imports ('../../render/foo' -> src/render/foo.ts). Without it, the boundaries plugin marks all TS-import targets as isUnknown and the firewall rule silently skips (verified via the plugin's debug output). Documented as a Plan 02 deviation (Rule 1 — Bug fix)."
patterns:
- "Single ESLint flat config at repo root with element-types + ignores + parser-only typescript-eslint integration. No legacy .eslintrc.* file. Plan 02 owns ONE architectural rule; broader code-quality lint sets (js.configs.recommended, tseslint.configs.recommended) are deliberately omitted to keep Phase 1 scope tight."
- "Default posture is 'allow' — Phase 1 enforces ONE rule (CORE-10), not a closed-by-default architecture. Future phases may add cross-subsystem restrictions (e.g., render cannot import save) by adding entries to the rules array without changing the default."
- "Lint-rule-correctness-via-Vitest pattern: the firewall rule's end-to-end correctness is proven by a Vitest test that runs ESLint via the JS API against a deliberate-violation fixture, NOT by 'lint exits 0 on clean code' (which proves nothing about the rule). The fixture is excluded from the default lint glob so CI stays green; the test passes ignore:false to override the exclusion."
- "Test-violation fixtures live under __test_violation__/ subdirectories and are doubly-excluded — from eslint.config.js ignores AND from tsconfig.app.json's exclude block — so neither 'npm run lint' nor 'tsc -b' trip on them. Vitest's own include glob (src/**/*.test.ts) discovers the test inside that directory."
key-files:
created:
- eslint.config.js (ESLint 9 flat config — 9 element types, 1 rule, parser+resolver wiring, default-lint exclusions)
- src/sim/__test_violation__/violator.ts (deliberate sim → render import)
- src/sim/__test_violation__/lint-firewall.test.ts (Vitest test that asserts the rule fires)
- src/render/__firewall_target__.ts (minimal render-side export stub the violator targets)
modified:
- package.json (added typescript-eslint and eslint-import-resolver-typescript devDeps)
- package-lock.json (lockfile entries for the two new devDeps and their transitive closure: 18 + 14 packages)
- tsconfig.app.json (added exclude block for *.test.ts and src/sim/__test_violation__/**)
key-decisions:
- "Omitted js.configs.recommended and tseslint.configs.recommended rule sets. Plan 02 owns exactly one architectural rule (CORE-10); broader code-quality lint is out of Phase 1 scope. Future phases may layer more rules on top of this config without touching the firewall block. Plan 01's SUMMARY confirmed no template eslint baseline existed to preserve."
- "Created src/render/__firewall_target__.ts as a real TS module (not a non-existent path) for the violator to import. The plan's Step 1 said 'doesn't need to exist as a real module' but empirical testing showed the boundaries plugin classifies unresolvable imports as isUnknown and silently skips the rule — the import MUST resolve to a real file under src/render/ to be classified as type:render and trigger the disallow."
- "Wired eslint-import-resolver-typescript via the import/resolver setting (boundaries plugin reads this). Without it, ext-less TS imports cannot be followed and EVERY in-repo TS import is marked isUnknown — the firewall rule would silently no-op even when called against the right shape of code."
- "Used the legacy boundaries/element-types rule + array-form selectors ({ from: ['sim'], disallow: ['render', 'ui'] }) per the plan's Pattern 5 spec. The plugin emits stderr deprecation notices recommending boundaries/dependencies + object-form selectors (the v6 modern shape), but those notices are informational — they do NOT count as ESLint warnings (verified via -f json: 0 errors, 0 warnings) and do NOT trip --max-warnings 0. Migration to the v6 modern shape is deferred to a future phase if it ever becomes load-bearing."
- "Excluded src/sim/__test_violation__/** and *.test.ts from tsconfig.app.json's build glob (added an exclude block). Vitest discovers test files via its own include glob, completely independent of tsconfig — so this only narrows what 'tsc -b' compiles, not what 'npm test' runs. Required because the firewall test imports node:path / process which aren't in the DOM-only app lib config."
- "Suppressed the 'Multiple projects found' notice from eslint-import-resolver-typescript via noWarnOnMultipleProjects:true. The referenced-projects tsconfig layout (root tsconfig with references to tsconfig.app.json + tsconfig.node.json) is deliberate Plan 01 design — the resolver sees both as 'projects' and warns; we explicitly opt out."
patterns-established:
- "Lint-rule correctness via Vitest + ESLint Node API pattern: any architectural rule landed in this project should be paired with a Vitest test that imports the ESLint class, runs it programmatically against a deliberate-violation fixture (under __test_violation__/), and asserts the expected ruleId + severity fires. This satisfies the Nyquist Rule for static-analysis rules ('lint exits 0' proves nothing about whether a specific rule actually works)."
- "Double-exclusion pattern for test-violation fixtures: ignored by eslint.config.js (so npm run lint stays green) AND excluded from tsconfig.app.json (so tsc -b doesn't typecheck them). The Vitest test that consumes them passes ignore:false to ESLint to override the lint-side exclusion."
- "Real-target-required pattern for boundaries plugin tests: deliberate-violation fixtures must import from REAL files under the target element directory, not from non-existent paths. The boundaries plugin classifies import targets via element pattern after resolving the import to a file path; unresolvable imports are isUnknown and silently skip rule evaluation."
requirements-completed: [CORE-10]
# Metrics
duration: 22min
completed: 2026-05-09
---
# Phase 01 Plan 02: ESLint Firewall Summary
**ESLint 9 flat config + eslint-plugin-boundaries 6.0.2 enforcing CORE-10 (src/sim/ cannot import src/render/ or src/ui/) at error severity, with a Vitest test that runs ESLint programmatically against a deliberate-violation fixture and asserts the rule fires end-to-end.**
## Performance
- **Duration:** 22 min
- **Started:** 2026-05-09T03:12:34Z (worktree spawn — first action ran ~3 min after spawn due to dependency install)
- **Completed:** 2026-05-09T03:34:09Z
- **Tasks:** 2 (both completed atomically)
- **Files created:** 4 (eslint.config.js, violator.ts, lint-firewall.test.ts, __firewall_target__.ts)
- **Files modified:** 3 (package.json, package-lock.json, tsconfig.app.json)
## Accomplishments
- **CORE-10 firewall is structurally enforced.** `eslint.config.js` declares the seven Phase-1 subsystem element types (`sim`, `render`, `ui`, `save`, `content`, `audio`, `store`) plus the template's `app` (the React/Phaser bridge files) and `game` (`src/game/**`) types — 9 total. One rule, severity `error`: `{ from: ['sim'], disallow: ['render', 'ui'] }`. Default posture `allow` so Phase 1 enforces only this one architectural constraint, not a closed-by-default architecture.
- **The rule is provably correct end-to-end.** `src/sim/__test_violation__/lint-firewall.test.ts` instantiates the ESLint class, runs it against `src/sim/__test_violation__/violator.ts` (which imports from `src/render/__firewall_target__.ts`), and asserts the result includes a `boundaries/element-types` message at severity 2 (error) whose text contains both `sim` and `render|ui`. Test passes in ~1 second. This satisfies the Nyquist Rule — the rule's correctness is automated, not assumed from "lint exits 0 on clean code".
- **`npm run lint` exits 0 on the clean codebase.** Zero errors, zero warnings (verified via `-f json` formatter). The violator fixture is excluded by the `ignores` block in `eslint.config.js`, so it doesn't break CI; the test reaches it via `ignore: false` on the programmatic ESLint instance.
- **`npm run build` and `npm test` continue to pass.** TypeScript strict-mode build is green; the test suite is now 2/2 (sentinel from Plan 01 + this firewall test).
- **Wave 2 sibling plans are unblocked.** Plans 03/04/05/06 can now land their config and code without colliding on `eslint.config.js`. Plan 07's CI workflow can compose `npm run lint && npm run test` and rely on both being green for this rule.
## Task Commits
Each task was committed atomically on `worktree-agent-adaed29911349f3f4`:
1. **Task 1: ESLint flat config + boundaries plugin + CORE-10 firewall rule**`e9b742d` (chore)
2. **Task 2: CORE-10 firewall test + violator fixture + render target stub**`8c1d839` (test)
**Plan metadata:** _(this commit, by the orchestrator after merge)_`docs(01-02): complete eslint-firewall plan`
## Final shape of `eslint.config.js`
```javascript
import boundaries from 'eslint-plugin-boundaries';
import tseslint from 'typescript-eslint';
export default [
// 1. Default-lint exclusions (the violator fixture lives under
// src/sim/__test_violation__/ and must NOT trip CI).
{ ignores: ['src/sim/__test_violation__/**', 'dist/**', 'node_modules/**', 'coverage/**', '*.tsbuildinfo'] },
// 2. Phase-1 architectural firewall (CORE-10).
{
files: ['src/**/*.{ts,tsx,js,jsx,mjs,cjs}'],
plugins: { boundaries },
languageOptions: {
parser: tseslint.parser,
parserOptions: { ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true } },
},
settings: {
'boundaries/elements': [
{ type: 'sim', pattern: 'src/sim/**' },
{ type: 'render', pattern: 'src/render/**' },
{ type: 'ui', pattern: 'src/ui/**' },
{ type: 'save', pattern: 'src/save/**' },
{ type: 'content', pattern: 'src/content/**' },
{ type: 'audio', pattern: 'src/audio/**' },
{ type: 'store', pattern: 'src/store/**' },
{ type: 'app', pattern: 'src/{main,App,PhaserGame}.{ts,tsx}' },
{ type: 'game', pattern: 'src/game/**' },
],
'boundaries/include': ['src/**/*'],
'boundaries/ignore': ['src/vite-env.d.ts', 'src/__sentinel__.test.ts'],
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: ['./tsconfig.app.json', './tsconfig.node.json'],
noWarnOnMultipleProjects: true,
},
},
},
rules: {
'boundaries/element-types': ['error', {
default: 'allow',
rules: [
{ from: ['sim'], disallow: ['render', 'ui'] },
],
}],
},
},
];
```
## ESLint version landscape
Per Plan 01's drift report:
- **ESLint:** `^9.39.4` (installed by Plan 01, untouched here). Flat config only — no legacy `.eslintrc.*` file ever existed in the repo, so Task 1 was a creation, not a migration.
- **eslint-plugin-boundaries:** `^6.0.2` (installed by Plan 01, untouched here).
- **typescript-eslint:** `^8.59.2` — added by THIS plan (Task 1, devDep). Parser only (`tseslint.parser`); no rule sets are enabled.
- **eslint-import-resolver-typescript:** `^4.x` (latest installed) — added by THIS plan (Task 2, devDep). Required for the boundaries plugin's element classification to follow extension-less TS imports to disk.
## Verification snapshot
| Gate | Command | Result |
|------|---------|--------|
| Lint clean codebase | `npm run lint` | exit 0, 0 errors, 0 warnings |
| Firewall test passes | `npx vitest run src/sim/__test_violation__/lint-firewall.test.ts` | exit 0, 1/1 pass |
| Full test suite | `npm test` | exit 0, 2/2 pass (sentinel + firewall) |
| TS-strict build | `npm run build` | exit 0, dist/ produced |
| Rule fires when invoked | `npx eslint --no-ignore src/sim/__test_violation__/violator.ts` | exit 1, `boundaries/element-types` error mentioning sim/render |
## Decisions Made
See the `key-decisions` frontmatter block above. Brief rationale for each:
1. **Omitted broader rule sets** — Plan 02 owns ONE rule (CORE-10). Pulling in `js.configs.recommended` would expand scope to dozens of code-quality rules on a clean greenfield codebase that Plan 01's TS-strict + manual-curation discipline already covers. Future phases may add rule sets on top without disturbing the firewall block.
2. **Real render target file (not a non-existent path)** — empirical override of the plan's "doesn't need to exist as a real module" guidance. See Deviations below.
3. **TypeScript resolver wired in** — required by the boundaries plugin to classify import targets. See Deviations below.
4. **Kept `boundaries/element-types` (not `boundaries/dependencies`)** — followed the plan's Pattern 5 spec verbatim. The plugin's stderr deprecation notices are informational; they don't count as ESLint warnings and don't trip `--max-warnings 0`.
5. **Test-fixture dir excluded from `tsconfig.app.json`** — the firewall test uses `node:path` and `process`, which aren't in the DOM-only app lib config. Vitest discovers tests via its own glob.
6. **Suppressed multi-project resolver warning** — Plan 01 deliberately uses the referenced-projects tsconfig layout; the resolver's warning is asking us to undo Plan 01's design.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 — Blocking] Added `typescript-eslint` (parser only) so ESLint can parse .ts/.tsx**
- **Found during:** Task 1, Step 4 (running `npm run lint` after writing the initial config).
- **Issue:** ESLint's default parser (Espree) cannot parse TypeScript syntax or JSX. Initial `npm run lint` produced 5 "Parsing error: Unexpected token" errors against `src/main.tsx`, `src/App.tsx`, `src/PhaserGame.tsx`, `src/game/main.ts`, `src/game/scenes/Boot.ts`. Without a TS-aware parser, `npm run lint` cannot exit 0 on a TypeScript-strict scaffold, which violates Task 1's `<verify>` gate.
- **Fix:** Installed `typescript-eslint@^8.59.2` (the meta-package that bundles `@typescript-eslint/parser`). Wired ONLY the parser via `languageOptions.parser: tseslint.parser` in `eslint.config.js`. NO `tseslint.configs.*` rule sets are enabled — Plan 02's discipline of owning exactly one architectural rule is preserved.
- **Files modified:** `package.json`, `package-lock.json`, `eslint.config.js`.
- **Verification:** `npm run lint` exits 0 with 0 errors / 0 warnings (verified via `-f json` JSON formatter).
- **Committed in:** `e9b742d` (Task 1 commit).
**2. [Rule 1 — Bug] Created `src/render/__firewall_target__.ts` as a real import target for the violator**
- **Found during:** Task 2, Step 3 (running the Vitest test for the first time — it failed with `expected 0 to be greater than 0`, meaning the rule did not fire even though the violator was clearly a sim-importing-render shape).
- **Issue:** The plan's Step 1 said "ESLint's boundaries plugin lints the import path against element-type rules without resolving the module" — implying the violator could import a non-existent path like `'../../render/this-file-does-not-exist'`. This is empirically false. Running the boundaries plugin with `ESLINT_PLUGIN_BOUNDARIES_DEBUG=1` showed the import target classified as `{ type: null, isUnknown: true }`, and the rule then has nothing to disallow against and silently skips. The plugin REQUIRES the import target to resolve to a real file on disk so it can match the file path against element patterns.
- **Fix:** (a) Created `src/render/__firewall_target__.ts` exporting a single marker constant. (b) Updated the violator to import from this real file: `import { FIREWALL_TARGET_MARKER } from '../../render/__firewall_target__';`. Added documentation comments in both files explaining the role.
- **Files modified:** `src/sim/__test_violation__/violator.ts`, `src/render/__firewall_target__.ts` (new).
- **Verification:** `ESLINT_PLUGIN_BOUNDARIES_DEBUG=1 npx eslint ...` now shows the target classified as `{ type: 'render', isUnknown: false }`. After also fixing #3 below, the rule fires with: `Dependencies to elements of type "render" are not allowed in elements of type "sim" and captured "null". Denied by rule at index 0 boundaries/element-types`.
- **Committed in:** `8c1d839` (Task 2 commit).
**3. [Rule 1 — Bug] Added `eslint-import-resolver-typescript` so the boundaries plugin can resolve extension-less TS imports**
- **Found during:** Task 2, Step 3 (after fix #2, the target was STILL `isUnknown` because `'../../render/__firewall_target__'` has no `.ts` extension and Node-style resolution doesn't add one).
- **Issue:** `eslint-plugin-boundaries` uses `eslint-plugin-import`'s resolver mechanism to follow imports to disk. The default resolver is Node-style and refuses to add a `.ts` extension to extension-less imports. Without a TS-aware resolver, EVERY in-repo TS import is marked `isUnknown` and the firewall rule silently no-ops — even with a real target file present. This is a load-bearing wiring requirement the plan didn't anticipate (the plan focused on the rule-config shape; resolver wiring was implicit).
- **Fix:** Installed `eslint-import-resolver-typescript` (latest, ^4.x) as a devDep. Added `'import/resolver': { typescript: { alwaysTryTypes: true, project: ['./tsconfig.app.json', './tsconfig.node.json'], noWarnOnMultipleProjects: true } }` to the boundaries config block in `eslint.config.js`. The two-project array reflects Plan 01's referenced-projects tsconfig layout.
- **Files modified:** `package.json`, `package-lock.json`, `eslint.config.js`.
- **Verification:** Debug output now shows imports resolving to disk paths and classifying correctly; the rule fires against the violator; the Vitest test passes. `npm run lint` still exits 0 with 0 errors / 0 warnings.
- **Committed in:** `8c1d839` (Task 2 commit).
**4. [Rule 3 — Blocking] Excluded `*.test.ts` and `src/sim/__test_violation__/**` from `tsconfig.app.json` build glob**
- **Found during:** Task 2, Step 4 (running `npm run build` after Task 2 created the test file — `tsc -b` failed with 3 TS2591 errors on the test file's `node:path` and `process` references).
- **Issue:** The firewall test uses Node APIs (`node:path` for `resolve`, `process.cwd()`) but `tsconfig.app.json` has `lib: ["ES2022", "DOM", "DOM.Iterable"]` and `types: ["vite/client"]` — no Node types. The original `tsconfig.app.json` had `include: ["src"]` with no `exclude` block, so `tsc -b` tried to compile all of `src/` including test files. The test file was correct TypeScript for its target environment (Node, via Vitest), but wrong for the app's DOM-only project.
- **Fix:** Added an `exclude: ["src/**/*.test.ts", "src/**/*.test.tsx", "src/sim/__test_violation__/**"]` block to `tsconfig.app.json`. Vitest discovers tests via its own `include` glob in `vitest.config.ts`, completely independent of tsconfig — so this only narrows what `tsc -b` compiles, not what `npm test` runs.
- **Files modified:** `tsconfig.app.json`.
- **Verification:** `npm run build` now exits 0 (`tsc -b` clean, `vite build` clean); `npm test` still exits 0 with 2/2 passing.
- **Committed in:** `8c1d839` (Task 2 commit).
---
**Total deviations:** 4 auto-fixed (2 blocking, 2 bug fixes — all under the Rule 1/2/3 auto-fix umbrella; none required architectural changes per Rule 4).
**Impact on plan:** All four deviations are mechanical wiring requirements the plan's high-level spec didn't anticipate. The plan's intent (CORE-10 enforced + provably tested) is satisfied exactly. No scope creep — the only added dependencies are tooling (`typescript-eslint` parser, `eslint-import-resolver-typescript`); no rule sets, no broader lint coverage. Wave-2 sibling plans (0306) are unaffected.
## Issues Encountered
- **`node_modules/` was not present in the worktree at agent spawn.** Worktrees inherit `.git` but not the working tree's installed dependencies. Ran `npm ci --no-audit --no-fund` (10s, 209 packages) before any other work. Time cost: ~10s.
- **Boundaries plugin debug spelunking.** Three full debug-output cycles (`ESLINT_PLUGIN_BOUNDARIES_DEBUG=1`) were needed to diagnose deviations #2 and #3. The plugin's debug output is excellent — it shows the classification of both source and target files, which made the root causes visible immediately. Time cost: ~5 min.
## Authentication Gates
None — Phase 1 is build/dev tooling only; no external auth needed.
## Threat Flags
None — this plan is static-analysis tooling (no network, no auth, no file IO outside the build), and the boundary rule's mitigation effect is architectural integrity (preventing the simulation core from becoming non-deterministic or non-headless), not security. The threat-model section of the PLAN.md says "No security-relevant code in this plan; this is static-analysis tooling."
## Known Stubs
- **`src/render/__firewall_target__.ts`** is a one-line export-stub for the firewall test ONLY. It is NOT part of the runtime render layer. `src/render/` is otherwise empty in Phase 1 (only `.gitkeep`). Phase 2 will populate `src/render/` with real Phaser scenes. If the firewall test is ever rewritten to point at a real render module, this stub should be removed. Documented in the file's header comment.
- **`src/sim/__test_violation__/violator.ts`** is intentionally a deliberate-violation fixture, lint-tested only. It is excluded from both the lint glob and the TS build. Documented in the file's header comment.
These are intentional, plan-anticipated stubs. They exist because the test infrastructure for an architectural rule MUST stress-test the rule end-to-end, and that requires a real (not synthetic) sim → render edge in code.
## Next Plan Readiness
- **Plan 03 (save layer):** Unaffected. The `boundaries` rule does not restrict imports into `src/save/`; Plan 03 can populate `src/save/` freely. The TS resolver and the test-file exclusion in `tsconfig.app.json` will benefit Plan 03's IDB tests too — they should add their `src/save/**/*.test.ts` files and they'll be picked up by Vitest while excluded from `tsc -b`.
- **Plan 04 (content pipeline):** Unaffected. `src/content/` is a declared element type but has no `disallow` rule against it.
- **Plan 05 (asset provenance):** Unaffected — Plan 05 writes `scripts/validate-assets.mjs`, which lives outside `src/` and is therefore outside the boundaries rule's scope.
- **Plan 06 (doctrine docs):** Unaffected — pure markdown.
- **Plan 07 (CI workflow):** Ready. CI can compose `npm run lint && npm run test && npm run build` with high confidence — all three are green here, and the firewall rule has an automated test (not just "lint runs"), so a future regression that breaks the rule will be caught immediately.
## Self-Check
- [x] `eslint.config.js` exists at repo root — `test -f eslint.config.js` PASS.
- [x] `eslint.config.js` contains `boundaries/element-types` — PASS.
- [x] All 7 firewall element types declared (sim, render, ui, save, content, audio, store) — verified by individual `grep "type: '<name>'"` for each. PASS.
- [x] `disallow: ['render', 'ui']` from `sim` — PASS (line: `{ from: ['sim'], disallow: ['render', 'ui'] },`).
- [x] No legacy `.eslintrc.*` file remains — PASS (`ls .eslintrc.*` returns no matches).
- [x] `__test_violation__` is in the `ignores` block — PASS.
- [x] `npm run lint` exits 0 — PASS (0 errors, 0 warnings via JSON formatter).
- [x] `src/sim/__test_violation__/violator.ts` exists and imports from `'../../render/'` — PASS.
- [x] `src/sim/__test_violation__/lint-firewall.test.ts` exists, references `boundaries/element-types`, imports `ESLint` from `eslint`, and asserts both `sim` and `render|ui` — PASS (count of toMatch lines mentioning sim/render = 2).
- [x] `npx vitest run src/sim/__test_violation__/lint-firewall.test.ts` exits 0 — PASS.
- [x] `npx eslint --no-ignore src/sim/__test_violation__/violator.ts` exits non-zero — PASS (exit 1, expected error fires).
- [x] `npm run build` exits 0 — PASS.
- [x] `npm test` exits 0 with 2/2 passing — PASS.
- [x] Task 1 commit exists: `e9b742d` — verified in `git log`.
- [x] Task 2 commit exists: `8c1d839` — verified in `git log`.
**## Self-Check: PASSED**
---
*Phase: 01-foundations-and-doctrine*
*Plan: 02 of 7*
*Completed: 2026-05-09*
+128
View File
@@ -0,0 +1,128 @@
// eslint.config.js — ESLint 9 flat config
//
// Phase 1, Plan 02 (CORE-10): the architectural firewall.
//
// This file declares the seven src/ subsystem element types plus the
// template-provided `app` and `game` types, and one rule:
//
// `src/sim/` MUST NOT import from `src/render/` or `src/ui/`.
//
// The simulation core must remain rendering-agnostic and headless so the
// offline-catchup math in Phase 2 can run deterministically without React
// or Phaser. See CLAUDE.md "Architectural Firewall (load-bearing)" and
// .planning/phases/01-foundations-and-doctrine/01-CONTEXT.md (D-10).
//
// We intentionally do NOT pull in `js.configs.recommended` or the
// typescript-eslint *rule sets* here. Plan 02 owns exactly one
// architectural rule; broader code-quality lint is out of scope for
// Phase 1 (and would expand Wave-2 surface area on a clean greenfield
// codebase). Future phases may layer more rules on top of this config
// without touching the firewall block.
//
// We DO use `typescript-eslint`'s *parser* — it is the only way ESLint
// can parse `.ts` / `.tsx` files at all (Espree, ESLint's default
// parser, doesn't understand TypeScript syntax or JSX). This is a
// parser-only integration; no `tseslint.configs.*` rule sets are
// applied. This is documented as a Plan 02 deviation (Rule 3 — Blocking)
// in 01-02-SUMMARY.md.
import boundaries from 'eslint-plugin-boundaries';
import tseslint from 'typescript-eslint';
export default [
// ---------------------------------------------------------------------
// 1. Default-lint exclusions.
//
// The deliberate-violation fixture under src/sim/__test_violation__/
// exists ONLY to be lint-tested by Task 2's Vitest test (which runs
// ESLint programmatically with `ignore: false`). It must NOT trip
// `npm run lint` in CI — the rule is verified by the unit test, not
// by the default lint glob.
// ---------------------------------------------------------------------
{
ignores: [
'src/sim/__test_violation__/**',
'dist/**',
'node_modules/**',
'coverage/**',
'*.tsbuildinfo',
],
},
// ---------------------------------------------------------------------
// 2. Phase-1 architectural firewall (CORE-10).
//
// Seven src/ subsystem types matching CONTEXT D-10's directory layout,
// plus `app` (the React/Phaser bridge files at src/main.tsx, src/App.tsx,
// src/PhaserGame.tsx) and `game` (the Phaser scene tree at src/game/**).
//
// Default posture is `allow` — Phase 1 enforces ONE rule, not a
// closed-by-default architecture. Future phases may add cross-subsystem
// restrictions (e.g., `render` cannot import `save`) without changing
// the default.
// ---------------------------------------------------------------------
{
files: ['src/**/*.{ts,tsx,js,jsx,mjs,cjs}'],
plugins: { boundaries },
languageOptions: {
// Parser-only integration with typescript-eslint. Lets ESLint
// parse TS / TSX (incl. JSX) so the boundaries rule can inspect
// imports. No tseslint rule sets are enabled — that is out of
// Phase-1 scope (Plan 02 owns ONE rule: CORE-10).
parser: tseslint.parser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: { jsx: true },
},
},
settings: {
'boundaries/elements': [
{ type: 'sim', pattern: 'src/sim/**' },
{ type: 'render', pattern: 'src/render/**' },
{ type: 'ui', pattern: 'src/ui/**' },
{ type: 'save', pattern: 'src/save/**' },
{ type: 'content', pattern: 'src/content/**' },
{ type: 'audio', pattern: 'src/audio/**' },
{ type: 'store', pattern: 'src/store/**' },
{ type: 'app', pattern: 'src/{main,App,PhaserGame}.{ts,tsx}' },
{ type: 'game', pattern: 'src/game/**' },
],
'boundaries/include': ['src/**/*'],
// Quietly tolerate files that aren't classified (e.g., src/vite-env.d.ts,
// src/__sentinel__.test.ts). The firewall rule only fires on
// sim → {render, ui} edges; unclassified files don't trigger it.
'boundaries/ignore': ['src/vite-env.d.ts', 'src/__sentinel__.test.ts'],
// eslint-plugin-boundaries needs to RESOLVE import paths to disk
// files in order to classify the import target's element type.
// Without a TS-aware resolver, `import x from '../../render/foo'`
// (no extension) cannot be resolved to `src/render/foo.ts` and
// the target is marked `isUnknown`, silently skipping the rule.
// eslint-import-resolver-typescript reads tsconfig.json to follow
// bare-extension TS imports. Verified empirically during Plan 02
// execution; see 01-02-SUMMARY.md "Deviations" (Rule 1 — Bug fix).
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: ['./tsconfig.app.json', './tsconfig.node.json'],
// Suppress "Multiple projects found" noise — we deliberately
// use the referenced-projects tsconfig layout (root tsconfig
// with `references`) per Plan 01.
noWarnOnMultipleProjects: true,
},
},
},
rules: {
// CORE-10: the simulation core cannot reach into render or UI.
// Severity MUST be `error` — `npm run lint` runs with
// `--max-warnings 0` (per Plan 01), so a warning would also fail
// CI, but `error` makes intent unambiguous.
'boundaries/element-types': ['error', {
default: 'allow',
rules: [
{ from: ['sim'], disallow: ['render', 'ui'] },
],
}],
},
},
];
+794
View File
@@ -27,11 +27,13 @@
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "^6.0.1",
"@vitest/ui": "^4.1.5", "@vitest/ui": "^4.1.5",
"eslint": "^9.39.4", "eslint": "^9.39.4",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-boundaries": "^6.0.2", "eslint-plugin-boundaries": "^6.0.2",
"fake-indexeddb": "^6.2.5", "fake-indexeddb": "^6.2.5",
"happy-dom": "^20.9.0", "happy-dom": "^20.9.0",
"inklecate": "^1.8.1", "inklecate": "^1.8.1",
"typescript": "^6.0.3", "typescript": "^6.0.3",
"typescript-eslint": "^8.59.2",
"vite": "^8.0.11", "vite": "^8.0.11",
"vitest": "^4.1.5" "vitest": "^4.1.5"
} }
@@ -755,6 +757,594 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz",
"integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.59.2",
"@typescript-eslint/type-utils": "8.59.2",
"@typescript-eslint/utils": "8.59.2",
"@typescript-eslint/visitor-keys": "8.59.2",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.59.2",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz",
"integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.59.2",
"@typescript-eslint/types": "8.59.2",
"@typescript-eslint/typescript-estree": "8.59.2",
"@typescript-eslint/visitor-keys": "8.59.2",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz",
"integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.59.2",
"@typescript-eslint/types": "^8.59.2",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz",
"integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.2",
"@typescript-eslint/visitor-keys": "8.59.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz",
"integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz",
"integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.2",
"@typescript-eslint/typescript-estree": "8.59.2",
"@typescript-eslint/utils": "8.59.2",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz",
"integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz",
"integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.59.2",
"@typescript-eslint/tsconfig-utils": "8.59.2",
"@typescript-eslint/types": "8.59.2",
"@typescript-eslint/visitor-keys": "8.59.2",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.5"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz",
"integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.59.2",
"@typescript-eslint/types": "8.59.2",
"@typescript-eslint/typescript-estree": "8.59.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz",
"integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.2",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@unrs/resolver-binding-android-arm-eabi": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
"integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@unrs/resolver-binding-android-arm64": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz",
"integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@unrs/resolver-binding-darwin-arm64": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz",
"integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@unrs/resolver-binding-darwin-x64": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz",
"integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@unrs/resolver-binding-freebsd-x64": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz",
"integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz",
"integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz",
"integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz",
"integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-linux-arm64-musl": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz",
"integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz",
"integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==",
"cpu": [
"ppc64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz",
"integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==",
"cpu": [
"riscv64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz",
"integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==",
"cpu": [
"riscv64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz",
"integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==",
"cpu": [
"s390x"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-linux-x64-gnu": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz",
"integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-linux-x64-musl": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz",
"integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@unrs/resolver-binding-wasm32-wasi": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz",
"integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==",
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@napi-rs/wasm-runtime": "^0.2.11"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
"integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.4.3",
"@emnapi/runtime": "^1.4.3",
"@tybys/wasm-util": "^0.10.0"
}
},
"node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz",
"integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz",
"integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@unrs/resolver-binding-win32-x64-msvc": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz",
"integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@vitejs/plugin-react": { "node_modules/@vitejs/plugin-react": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
@@ -1272,6 +1862,31 @@
} }
} }
}, },
"node_modules/eslint-import-context": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz",
"integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==",
"dev": true,
"license": "MIT",
"dependencies": {
"get-tsconfig": "^4.10.1",
"stable-hash-x": "^0.2.0"
},
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint-import-context"
},
"peerDependencies": {
"unrs-resolver": "^1.0.0"
},
"peerDependenciesMeta": {
"unrs-resolver": {
"optional": true
}
}
},
"node_modules/eslint-import-resolver-node": { "node_modules/eslint-import-resolver-node": {
"version": "0.3.9", "version": "0.3.9",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
@@ -1294,6 +1909,41 @@
"ms": "^2.1.1" "ms": "^2.1.1"
} }
}, },
"node_modules/eslint-import-resolver-typescript": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz",
"integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==",
"dev": true,
"license": "ISC",
"dependencies": {
"debug": "^4.4.1",
"eslint-import-context": "^0.1.8",
"get-tsconfig": "^4.10.1",
"is-bun-module": "^2.0.0",
"stable-hash-x": "^0.2.0",
"tinyglobby": "^0.2.14",
"unrs-resolver": "^1.7.11"
},
"engines": {
"node": "^16.17.0 || >=18.6.0"
},
"funding": {
"url": "https://opencollective.com/eslint-import-resolver-typescript"
},
"peerDependencies": {
"eslint": "*",
"eslint-plugin-import": "*",
"eslint-plugin-import-x": "*"
},
"peerDependenciesMeta": {
"eslint-plugin-import": {
"optional": true
},
"eslint-plugin-import-x": {
"optional": true
}
}
},
"node_modules/eslint-module-utils": { "node_modules/eslint-module-utils": {
"version": "2.12.1", "version": "2.12.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
@@ -1630,6 +2280,19 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/get-tsconfig": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz",
"integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
"dev": true,
"license": "MIT",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/glob-parent": { "node_modules/glob-parent": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -1809,6 +2472,16 @@
"inklecate": "cli.js" "inklecate": "cli.js"
} }
}, },
"node_modules/is-bun-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
"integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.7.1"
}
},
"node_modules/is-core-module": { "node_modules/is-core-module": {
"version": "2.16.1", "version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@@ -2339,6 +3012,22 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
} }
}, },
"node_modules/napi-postinstall": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
"integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
"dev": true,
"license": "MIT",
"bin": {
"napi-postinstall": "lib/cli.js"
},
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/napi-postinstall"
}
},
"node_modules/natural-compare": { "node_modules/natural-compare": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -2624,6 +3313,16 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/rolldown": { "node_modules/rolldown": {
"version": "1.0.0-rc.18", "version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz",
@@ -2684,6 +3383,19 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/semver": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
"integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/shebang-command": { "node_modules/shebang-command": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -2755,6 +3467,16 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/stable-hash-x": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz",
"integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/stackback": { "node_modules/stackback": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -2915,6 +3637,19 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/ts-api-utils": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
"integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18.12"
},
"peerDependencies": {
"typescript": ">=4.8.4"
}
},
"node_modules/ts-assertions": { "node_modules/ts-assertions": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/ts-assertions/-/ts-assertions-2.0.6.tgz", "resolved": "https://registry.npmjs.org/ts-assertions/-/ts-assertions-2.0.6.tgz",
@@ -2957,6 +3692,30 @@
"node": ">=14.17" "node": ">=14.17"
} }
}, },
"node_modules/typescript-eslint": {
"version": "8.59.2",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz",
"integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.59.2",
"@typescript-eslint/parser": "8.59.2",
"@typescript-eslint/typescript-estree": "8.59.2",
"@typescript-eslint/utils": "8.59.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/uglify-js": { "node_modules/uglify-js": {
"version": "3.19.3", "version": "3.19.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
@@ -2988,6 +3747,41 @@
"node": ">= 4.0.0" "node": ">= 4.0.0"
} }
}, },
"node_modules/unrs-resolver": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
"integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"napi-postinstall": "^0.3.0"
},
"funding": {
"url": "https://opencollective.com/unrs-resolver"
},
"optionalDependencies": {
"@unrs/resolver-binding-android-arm-eabi": "1.11.1",
"@unrs/resolver-binding-android-arm64": "1.11.1",
"@unrs/resolver-binding-darwin-arm64": "1.11.1",
"@unrs/resolver-binding-darwin-x64": "1.11.1",
"@unrs/resolver-binding-freebsd-x64": "1.11.1",
"@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1",
"@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1",
"@unrs/resolver-binding-linux-arm64-gnu": "1.11.1",
"@unrs/resolver-binding-linux-arm64-musl": "1.11.1",
"@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1",
"@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1",
"@unrs/resolver-binding-linux-riscv64-musl": "1.11.1",
"@unrs/resolver-binding-linux-s390x-gnu": "1.11.1",
"@unrs/resolver-binding-linux-x64-gnu": "1.11.1",
"@unrs/resolver-binding-linux-x64-musl": "1.11.1",
"@unrs/resolver-binding-wasm32-wasi": "1.11.1",
"@unrs/resolver-binding-win32-arm64-msvc": "1.11.1",
"@unrs/resolver-binding-win32-ia32-msvc": "1.11.1",
"@unrs/resolver-binding-win32-x64-msvc": "1.11.1"
}
},
"node_modules/uri-js": { "node_modules/uri-js": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+2
View File
@@ -35,11 +35,13 @@
"@vitejs/plugin-react": "^6.0.1", "@vitejs/plugin-react": "^6.0.1",
"@vitest/ui": "^4.1.5", "@vitest/ui": "^4.1.5",
"eslint": "^9.39.4", "eslint": "^9.39.4",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-boundaries": "^6.0.2", "eslint-plugin-boundaries": "^6.0.2",
"fake-indexeddb": "^6.2.5", "fake-indexeddb": "^6.2.5",
"happy-dom": "^20.9.0", "happy-dom": "^20.9.0",
"inklecate": "^1.8.1", "inklecate": "^1.8.1",
"typescript": "^6.0.3", "typescript": "^6.0.3",
"typescript-eslint": "^8.59.2",
"vite": "^8.0.11", "vite": "^8.0.11",
"vitest": "^4.1.5" "vitest": "^4.1.5"
} }
+19
View File
@@ -0,0 +1,19 @@
// Target stub for the CORE-10 firewall test fixture.
//
// The deliberate-violation fixture at
// src/sim/__test_violation__/violator.ts imports from this file so the
// boundaries plugin can resolve the import to a real path under
// src/render/ and classify it as the `render` element type.
//
// Without a real file to resolve to, eslint-plugin-boundaries marks the
// target as `isUnknown: true` and the boundaries/element-types rule
// silently skips the check (verified empirically via the plugin's
// debug output during Plan 02 execution).
//
// This file is otherwise unused. It is NOT part of the runtime render
// layer; src/render/ is intentionally empty in Phase 1 (only .gitkeep
// existed before this file). Phase 2 will populate src/render/ with
// real Phaser scenes and remove this stub if the firewall test is
// rewritten to point at a real render module.
export const FIREWALL_TARGET_MARKER = 'render-target-for-firewall-test';
@@ -0,0 +1,49 @@
// CORE-10 firewall test: programmatically run ESLint against the
// deliberate-violation fixture and assert that
// `eslint-plugin-boundaries` flags the sim → render import.
//
// Per the Nyquist Rule, the rule needs an automated end-to-end check —
// not just "lint exits 0 on clean code, trust me". This test invokes
// the rule machinery via the ESLint Node API and inspects the messages
// directly.
//
// The fixture (./violator.ts) is excluded from `npm run lint` via the
// `ignores` block in eslint.config.js so it doesn't break CI. We pass
// `ignore: false` to the programmatic ESLint instance below to override
// that exclusion for this single test.
import { describe, it, expect } from 'vitest';
import { ESLint } from 'eslint';
import { resolve } from 'node:path';
describe('CORE-10: src/sim/ cannot import from src/render/ or src/ui/', () => {
it('eslint-plugin-boundaries flags a sim → render import as an error', async () => {
const eslint = new ESLint({
overrideConfigFile: resolve(process.cwd(), 'eslint.config.js'),
ignore: false,
});
const fixturePath = resolve(
process.cwd(),
'src/sim/__test_violation__/violator.ts',
);
const results = await eslint.lintFiles([fixturePath]);
expect(results).toHaveLength(1);
const messages = results[0].messages;
const boundaryErrors = messages.filter(
(m) => m.ruleId === 'boundaries/element-types' && m.severity === 2,
);
expect(boundaryErrors.length).toBeGreaterThan(0);
// The error message should mention 'sim' (the offending element)
// and either 'render' or 'ui' (the disallowed targets). Both terms
// are checked separately so a regression that drops either side is
// caught.
const combined = boundaryErrors.map((m) => m.message).join(' | ');
expect(combined).toMatch(/sim/i);
expect(combined).toMatch(/render|ui/i);
});
});
+19
View File
@@ -0,0 +1,19 @@
// DELIBERATE VIOLATION OF CORE-10 — DO NOT USE OUTSIDE THE FIREWALL TEST.
//
// This file lives under src/sim/__test_violation__/ and is excluded from
// `npm run lint` via the `ignores` block in eslint.config.js. Its sole
// purpose is to be lint-tested by lint-firewall.test.ts to prove the
// boundaries/element-types rule actually fires (CORE-10).
//
// The import below targets a real file under src/render/ —
// __firewall_target__.ts — because eslint-plugin-boundaries needs to
// resolve the import to a real path on disk to classify the target's
// element type. If the import path does not resolve, the plugin marks
// the target as `isUnknown` and silently skips the check (verified
// empirically against eslint-plugin-boundaries 6.0.2 during Plan 02
// execution; see 01-02-SUMMARY.md "Deviations").
import { FIREWALL_TARGET_MARKER } from '../../render/__firewall_target__';
export const VIOLATION_MARKER = 'sim-imports-render';
export const _ref = FIREWALL_TARGET_MARKER;
+6 -1
View File
@@ -24,5 +24,10 @@
"types": ["vite/client"] "types": ["vite/client"]
}, },
"include": ["src"] "include": ["src"],
"exclude": [
"src/**/*.test.ts",
"src/**/*.test.tsx",
"src/sim/__test_violation__/**"
]
} }