Mirrors `content.js` normalizeId() in a self-contained
`fixtures/run-node.mjs`. Loads `query-extraction.json` and
`shared-normalization.json` and asserts each case the same way the
Python runner does.
content.js can't be imported directly — it lives inside an injected
IIFE in the extension — so the runner duplicates the regexes
(ID_RE_DASHED, ID_RE_UNDASHED, BUILTIN_ID_NORMALIZERS). Inline
comment + README update flag that they must be kept in sync.
Why this matters: `shared-normalization.json` now actually catches
cross-side drift. A case that passes one side but fails the other is
the canary — without a Node runner, the contract was aspirational.
Verified:
$ node fixtures/run-node.mjs
query-extraction.json -> normalizeId (10 cases): 10 passed
shared-normalization.json -> normalizeId (5 cases): 5 passed
OK: all 15 cases passed
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>