fix(test): pin miner-ui jsdom localStorage over Node 26's broken global#7597
Conversation
Node 26 predefines an experimental globalThis.localStorage accessor (nodejs/node#60303) that returns undefined without --localstorage-file. It exists on globalThis before jsdom's environment is installed, so Vitest's populateGlobal skips copying jsdom's working Storage over it and every bare localStorage call (theme-toggle.test.tsx, mirroring theme-toggle.tsx) throws under Node 26 while passing on Node 22/24. Point globalThis.localStorage at jsdom's real Storage from the raw JSDOM window unconditionally: a no-op where the global already is that object, the fix on Node 26+. A ??= guard would keep the broken accessor since it already counts as present. Closes JSONbored#7592
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-21 00:35:20 UTC
Review summary Nits — 5 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Visual preview
Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy. Scroll preview
A short scroll-through clip (desktop) — click either thumbnail to open the full animation. Evidence for scroll-linked behavior a single screenshot can't show. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
… broken global (#7616) * fix(test): pin loopover-ui + ui-kit jsdom localStorage over Node 26's broken global (#7612) Node 26 predefines an experimental globalThis.localStorage accessor (nodejs/node#60303) that returns undefined without --localstorage-file. It exists on globalThis before jsdom's environment is installed, so Vitest's populateGlobal skips copying jsdom's working Storage over it, and every bare localStorage call throws under Node 26 while passing on Node 22/24 -- the identical gap #7597 fixed in apps/loopover-miner-ui, which never got mirrored into apps/loopover-ui or packages/loopover-ui-kit. Confirmed via a full npm run test:ci on Node 26: 5 files / 26 tests failed in apps/loopover-ui with "Cannot read properties of undefined (reading 'clear')" on window.localStorage.clear() -- routes/index, api/try-it, app-panels/onboarding-preview-card, lib/analytics-window, lib/use-local-storage. packages/loopover-ui-kit has no test that currently touches localStorage, so this guard is preventive there. Point globalThis.localStorage at jsdom's real Storage from the raw JSDOM window unconditionally: a no-op where the global already is that object, the fix on Node 26+. All 5 previously-failing files now pass on Node 26 (426/426 in apps/loopover-ui, 19/19 in ui-kit), with no change on Node 22/24. Closes #7612 * fix(test): satisfy prettier's line-wrap in the loopover-ui localStorage guard npm run ui:lint flagged the previous wrap of the jsdom.window?.localStorage chain; break before ?.window instead of before ?.localStorage to match apps/loopover-ui's printWidth-100 prettier config. No behavior change.
Every engines.node in this repo was open-ended (root and 4 of 5 engines-declaring workspaces at ">=22.0.0", loopover-miner at ">=22.13.0" -- both with no upper bound), and there was no root .npmrc, so npm never warned or blocked a Node 23+ install locally. That silent gap is exactly how the Node 26 jsdom/localStorage bug (#7592/#7597, #7612) went undetected until someone happened to run tests locally on a newer Node -- twice. - Tighten engines.node to exclude Node 23+ on root + every workspace that already declares one (discovery-index, loopover-engine, loopover-ui-kit, loopover-miner, loopover-mcp), preserving each workspace's own floor (loopover-miner keeps >=22.13.0, aligned with unflagged node:sqlite support per its own test). - Add a root .npmrc with engine-strict=true so an out-of-range Node becomes a hard install/run error instead of a silently-ignored warning. CI is unaffected: .github/actions/setup-workspace already strips any repo .npmrc before installing (a guard against a malicious fork PR's .npmrc), and pins its own Node via node-version-file: .nvmrc regardless. - Add scripts/check-engines-nvmrc-sync.mjs (+ .d.mts + unit tests), asserting .nvmrc and every declared engines.node stay in lockstep, so a future .nvmrc bump that forgets to widen engines.node is caught mechanically. Wired into both the local test:ci aggregate and a real ci.yml step (gated like Branding drift check; .nvmrc added to the backend path filter so a .nvmrc-only change still triggers it). - Update the 3 pre-existing tests that hard-asserted the old un-bounded engines.node strings (loopover-engine-scaffold, miner-package-skeleton, miner-run-state) to the new values. - Document the new check in reference.md's CI table. Closes #7613
…rd (#7620) (#7624) #7612's fix mirrored #7597's Node 26 jsdom/localStorage guard verbatim into apps/loopover-ui and packages/loopover-ui-kit, on top of the existing copy already in apps/loopover-miner-ui -- three copies of the same ~15-line guard that can drift, and a fourth future jsdom workspace wouldn't automatically inherit the fix. Extract into test/helpers/vitest-jsdom-node26-localstorage.ts, exporting one patchJsdomLocalStorageForNode26() function. All three vitest.setup.ts files now import and call it instead of inlining the logic. No behavior change -- same guard, same no-op-on-Node-22/24 / fix-on-Node-26+ semantics. Verified on Node 26: all three consuming workspaces' full test suites pass post-extraction (loopover-ui 79/79 files, ui-kit 3/3, ui-miner 28/28) -- the regression proof for a helper this thin, matching this repo's existing test/helpers/*.ts convention of no dedicated test file. Closes #7620

fix(test): pin miner-ui jsdom localStorage over Node 26's broken global
Node 26 predefines an experimental globalThis.localStorage accessor
(nodejs/node#60303) that returns undefined without --localstorage-file.
It exists on globalThis before jsdom's environment is installed, so
Vitest's populateGlobal skips copying jsdom's working Storage over it and
every bare localStorage call (theme-toggle.test.tsx, mirroring
theme-toggle.tsx) throws under Node 26 while passing on Node 22/24.
Point globalThis.localStorage at jsdom's real Storage from the raw JSDOM
window unconditionally: a no-op where the global already is that object,
the fix on Node 26+. A ??= guard would keep the broken accessor since it
already counts as present.
Closes #7592