From 88b9bb0af8792c401893b0ebe33d7c0a1e753b6a Mon Sep 17 00:00:00 2001 From: Aleksandar Grbic Date: Sat, 25 Jul 2026 13:56:55 +0200 Subject: [PATCH 1/2] fix(reachability): whole-identifier API-route match (kill substring false-green) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The API-registration check used a bare `apiRoutes.includes(`${camel}Routes`)`, so a feature named `count` matched an unrelated slice's `accountRoutes` (`account` ⊃ `count`) and was reported reachable even when its own `countRoutes` mount was genuinely absent — a false green in the very check meant to catch unregistered (hollow) features (#50 class). Guard the match with \p{ID_Continue} boundary lookarounds (same idiom as fieldIsMentioned), so only a whole-identifier mount counts, while every real idiom (`count: countRoutes`, `.use(countRoutes)`, `[countRoutes]`) still matches. Adds two regression tests. --- .../core/src/loop/boringstack/reachability.ts | 26 +++++++++++-- .../tests/boringstack-reachability.test.ts | 37 +++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/packages/core/src/loop/boringstack/reachability.ts b/packages/core/src/loop/boringstack/reachability.ts index bc756d65..03b49da8 100644 --- a/packages/core/src/loop/boringstack/reachability.ts +++ b/packages/core/src/loop/boringstack/reachability.ts @@ -56,10 +56,7 @@ export function checkFeatureReachable( } // 2. API registration: the resource must be mounted in the API route table. - if ( - inputs.apiRoutes !== null && - !inputs.apiRoutes.includes(`${camel}Routes`) - ) { + if (inputs.apiRoutes !== null && !registersRoutes(inputs.apiRoutes, camel)) { problems.push( `API route missing: ${camel}Routes is not registered in ` + `config/routes/routes.ts — the endpoints would be unreachable.` @@ -80,6 +77,27 @@ export function checkFeatureReachable( return { ok: problems.length === 0, problems }; } +/** + * True when `apiRoutes` mounts `Routes` as a WHOLE identifier, not merely as a + * substring of a longer name. A bare `.includes("countRoutes")` matches an unrelated + * slice's `accountRoutes` (`account` ⊃ `count`), so a `count` feature whose own + * `countRoutes` is genuinely absent would be reported reachable — a false green in the + * one check meant to catch unregistered (hollow) features. The `\p{ID_Continue}` + * lookarounds (the canonical Unicode "identifier continue" set — same idiom as + * `fieldIsMentioned`) reject a match glued to an adjacent identifier char on either + * side, while still matching every real mount idiom (`count: countRoutes`, + * `.use(countRoutes)`, `[countRoutes]`), whose delimiter before the name is not an + * identifier char. + */ +function registersRoutes(apiRoutes: string, camel: string): boolean { + const boundary = "\\p{ID_Continue}"; + const ident = `${camel}Routes`.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"); + + return new RegExp(`(?.title` + `.empty`. */ function localeHasFeatureKeys(jsonSrc: string, lower: string): boolean { let data: unknown; diff --git a/packages/core/tests/boringstack-reachability.test.ts b/packages/core/tests/boringstack-reachability.test.ts index b3479ff6..288a707a 100644 --- a/packages/core/tests/boringstack-reachability.test.ts +++ b/packages/core/tests/boringstack-reachability.test.ts @@ -46,6 +46,43 @@ describe("checkFeatureReachable", () => { expect(r.problems.join("\n")).toMatch(/API route missing/u); }); + test("a substring-only API match is NOT accepted (no false green off a sibling slice)", () => { + // `count`'s own `countRoutes` is absent, but a prior slice registered + // `accountRoutes` — which CONTAINS "countRoutes". A bare `.includes` would wrongly + // pass `count` as reachable; the whole-identifier check must still flag it. + const r = checkFeatureReachable("Count", { + uiRoutes: + 'const CountPage = lazy(() => import("@/features/count/components/CountPage/CountPage"));', + apiRoutes: "export const routes = {\n account: accountRoutes,\n};", + localeJsons: [ + JSON.stringify({ + features: { count: { title: "Counts", empty: "None." } }, + }), + ], + }); + + expect(r.ok).toBe(false); + expect(r.problems.join("\n")).toMatch(/API route missing/u); + }); + + test("a whole-identifier API match is accepted even next to a superstring sibling", () => { + // Both `count` and `account` are registered: `count` must be seen as reachable + // (its own whole-word `countRoutes` is present), not masked by `accountRoutes`. + const r = checkFeatureReachable("Count", { + uiRoutes: + 'const CountPage = lazy(() => import("@/features/count/components/CountPage/CountPage"));', + apiRoutes: + "export const routes = {\n account: accountRoutes,\n count: countRoutes,\n};", + localeJsons: [ + JSON.stringify({ + features: { count: { title: "Counts", empty: "None." } }, + }), + ], + }); + + expect(r.ok).toBe(true); + }); + test("flags missing i18n keys (page would show raw keys)", () => { const r = checkFeatureReachable("Bookmark", { ...wiredInputs(), From 5955c7d0b4625f5a749ceca4c4e1a8810f903441 Mon Sep 17 00:00:00 2001 From: Aleksandar Grbic Date: Sat, 25 Jul 2026 14:05:19 +0200 Subject: [PATCH 2/2] fix(reachability): include $ in the identifier boundary (JS IdentifierPart) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Panel advisory: \p{ID_Continue} omits $, a valid JS IdentifierPart, so $countRoutes (a distinct identifier) matched as a whole-word countRoutes. Add $ to the boundary class — the exact JS IdentifierPart set (ID_Continue already covers _, ZWJ, ZWNJ, verified at runtime). Adds a $-prefixed-sibling regression test. --- .../core/src/loop/boringstack/reachability.ts | 6 ++++-- .../tests/boringstack-reachability.test.ts | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/core/src/loop/boringstack/reachability.ts b/packages/core/src/loop/boringstack/reachability.ts index 03b49da8..bd8ef809 100644 --- a/packages/core/src/loop/boringstack/reachability.ts +++ b/packages/core/src/loop/boringstack/reachability.ts @@ -87,10 +87,12 @@ export function checkFeatureReachable( * `fieldIsMentioned`) reject a match glued to an adjacent identifier char on either * side, while still matching every real mount idiom (`count: countRoutes`, * `.use(countRoutes)`, `[countRoutes]`), whose delimiter before the name is not an - * identifier char. + * identifier char. `$` is added to the class because it is a JS IdentifierPart that + * `\p{ID_Continue}` omits — so `$countRoutes` is a distinct identifier, not a match. + * (`_`, ZWJ, and ZWNJ are already in `\p{ID_Continue}`, verified at runtime.) */ function registersRoutes(apiRoutes: string, camel: string): boolean { - const boundary = "\\p{ID_Continue}"; + const boundary = "[\\p{ID_Continue}$]"; const ident = `${camel}Routes`.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"); return new RegExp(`(? { expect(r.problems.join("\n")).toMatch(/API route missing/u); }); + test("a `$`-prefixed sibling identifier is NOT accepted (JS `$` is an identifier char)", () => { + // `$` is a valid JS IdentifierPart that `\p{ID_Continue}` omits, so `$countRoutes` + // is a DISTINCT identifier — it must not satisfy `count`'s reachability. + const r = checkFeatureReachable("Count", { + uiRoutes: + 'const CountPage = lazy(() => import("@/features/count/components/CountPage/CountPage"));', + apiRoutes: "export const routes = {\n meta: $countRoutes,\n};", + localeJsons: [ + JSON.stringify({ + features: { count: { title: "Counts", empty: "None." } }, + }), + ], + }); + + expect(r.ok).toBe(false); + expect(r.problems.join("\n")).toMatch(/API route missing/u); + }); + test("a whole-identifier API match is accepted even next to a superstring sibling", () => { // Both `count` and `account` are registered: `count` must be seen as reachable // (its own whole-word `countRoutes` is present), not masked by `accountRoutes`.