test(01-02): add CORE-10 firewall test + violator fixture

- src/sim/__test_violation__/violator.ts deliberately imports from
  src/render/__firewall_target__.ts to trigger the firewall rule.
- src/sim/__test_violation__/lint-firewall.test.ts runs ESLint
  programmatically (with ignore: false) against the violator and
  asserts boundaries/element-types fires with severity=error and the
  message mentions both 'sim' and 'render'.
- src/render/__firewall_target__.ts is a minimal export so the
  boundaries plugin can resolve the import to a real path on disk.
  Without a real target, the plugin marks the import as isUnknown
  and silently skips the rule (verified empirically; see SUMMARY).
- eslint.config.js gains an import/resolver: typescript block so the
  TS-aware resolver follows extension-less imports
  ('../../render/foo' -> src/render/foo.ts). Required by the
  boundaries plugin's element classification of import targets.
- tsconfig.app.json excludes *.test.ts and src/sim/__test_violation__/
  so 'tsc -b' does not try to typecheck Node-API-using test code with
  the DOM-only project's lib settings; vitest still discovers them
  via its own include glob.
- Added eslint-import-resolver-typescript as devDep.

Verifies green:
  npm run lint        -> 0 errors, 0 warnings (violator excluded)
  npm test            -> 2/2 pass (sentinel + firewall)
  npm run build       -> tsc -b clean, vite build clean
  npx eslint --no-ignore src/sim/__test_violation__/violator.ts
                      -> exits 1 with the expected
                         boundaries/element-types error

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 23:34:01 -04:00
parent e9b742da79
commit 8c1d839adf
7 changed files with 573 additions and 1 deletions
+18
View File
@@ -93,6 +93,24 @@ export default [
// 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.
+461
View File
@@ -27,6 +27,7 @@
"@vitejs/plugin-react": "^6.0.1",
"@vitest/ui": "^4.1.5",
"eslint": "^9.39.4",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-boundaries": "^6.0.2",
"fake-indexeddb": "^6.2.5",
"happy-dom": "^20.9.0",
@@ -1038,6 +1039,312 @@
"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": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
@@ -1555,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": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
@@ -1577,6 +1909,41 @@
"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": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
@@ -1913,6 +2280,19 @@
"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": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -2092,6 +2472,16 @@
"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": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@@ -2622,6 +3012,22 @@
"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": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -2907,6 +3313,16 @@
"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": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz",
@@ -3051,6 +3467,16 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"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": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -3321,6 +3747,41 @@
"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": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+1
View File
@@ -35,6 +35,7 @@
"@vitejs/plugin-react": "^6.0.1",
"@vitest/ui": "^4.1.5",
"eslint": "^9.39.4",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-boundaries": "^6.0.2",
"fake-indexeddb": "^6.2.5",
"happy-dom": "^20.9.0",
+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"]
},
"include": ["src"]
"include": ["src"],
"exclude": [
"src/**/*.test.ts",
"src/**/*.test.tsx",
"src/sim/__test_violation__/**"
]
}