chore(web): jsdom 29.1.1 → 30.0.1 — the only landable npm major (eslint 10 + TypeScript 7 are peer-blocked upstream) [schedule: Daily agent update check] #255

Closed
opened 2026-08-01 04:10:05 +02:00 by dominik.polakovics · 0 comments

Goal

Bump jsdom 29.1.1 → 30.0.1 in web/package.json + web/package-lock.json. It is the only npm major in this repo's SPA that can actually land right now — the other two available majors (ESLint 10, TypeScript 7) are blocked by peer-dependency walls that are documented at the bottom of this issue so nobody re-derives them.

This is a small, bounded change. The reason it gets its own issue rather than a drive-by is that jsdom 30's single breaking change is a Node.js floor bump, which has to clear two independent gates (native CI and the hermetic nix gate) — both of which have been checked below, so you do not have to.

Everything here was verified on 2026-08-01 against the npm registry, jsdom's GitHub releases, and nixpkgs at this repo's pinned flake.lock rev.

Evidence — what is behind

web/package.json:29 declares "jsdom": "^29.1.1"; web/package-lock.json resolves it to 29.1.1. Latest is 30.0.1 (published 2026-07-29).

Every other direct dependency in web/package.json is already at its latest published release — commit 4cc17b6 refreshed the in-range pins on 2026-07-31. Verified against registry.npmjs.org on 2026-08-01:

Package lockfile npm latest
jsdom 29.1.1 30.0.1 this issue
@solidjs/router 1.0.0 1.0.0 current
solid-js 1.9.14 1.9.14 current
lucide-solid 1.28.0 1.28.0 current
vite 8.2.0 8.2.0 current
vitest 4.1.10 4.1.10 current
vite-plugin-solid 2.11.14 2.11.14 current
prettier 3.9.6 3.9.6 current
@playwright/test 1.62.1 1.62.1 current
@typescript-eslint/*, typescript-eslint 8.65.0 8.65.0 current
eslint-plugin-solid 0.14.5 0.14.5 current (upstream is dormant — see below)
eslint, @eslint/js 9.39.5 9.39.5 on the maintenance tag blocked on 10.x, see below
typescript 6.0.3 6.0.3 is the newest 6.x blocked on 7.x, see below

So jsdom is the whole of the actionable work.

Security

No npm advisory reaches any version in this repo's dependency tree, and jsdom 30 does not close one. Checked every direct dependency against the GitHub Advisory Database (ecosystem=npm) on 2026-08-01; the only advisories that matched a package we use are all patched well below our pins:

GHSA-fx2h-pf6j-xcff  high      vite     vulnerable >= 8.0.0, <= 8.0.15  patched 8.0.16   (we are on 8.2.0)
GHSA-4w7w-66w2-5vf9  medium    vite     vulnerable >= 8.0.0, <= 8.0.4   patched 8.0.5    (we are on 8.2.0)
GHSA-v2wj-q39q-566r  high      vite     vulnerable >= 8.0.0, <= 8.0.4   patched 8.0.5    (we are on 8.2.0)
GHSA-5xrq-8626-4rwp  critical  vitest   vulnerable >= 4.0.0, <  4.1.0   patched 4.1.0    (we are on 4.1.10)
GHSA-3qxh-p7jc-5xh6  high      solid-js vulnerable <  1.9.4             patched 1.9.4    (we are on 1.9.14)

jsdom's only advisory (GHSA-f4c9-cqv8-9v98) is a withdrawn 2022 entry. This bump is hygiene, not a security fix — do not describe it as one in the PR.

What actually changed in jsdom 29.1.1 → 30.0.1

From jsdom's GitHub releases. The delta is two releases and it is small.

v30.0.0 (2026-07-27)

Breaking changes:

  • Node.js minimum version raised to ^22.22.2 || ^24.15.0 || >=26.0.0.

That is the entire breaking-change list — one item. Other changes, all additive or bug fixes:

  • Added CSS.escape() and CSS.supports() functions.
  • Added 'background-position-x' and 'background-position-y' CSS properties.
  • Fixed getComputedStyle() to convert length values into pixels.
  • Fixed CSS function serialization, e.g., in the return value of getPropertyValue().
  • Fixed the type of error thrown by document.evaluate().

v30.0.1 (2026-07-29)

  • Fixed getComputedStyle() with calc() and other functions throwing an exception, which regressed in v30.0.0.
  • Sped up range operations on large documents.

Take 30.0.1, not 30.0.0 — 30.0.0 shipped a getComputedStyle() regression that 30.0.1 fixes.

Does any of it break how this repo uses jsdom?

jsdom is a devDependency only. Its sole use is as the vitest test environment — web/vite.config.ts:29:

test: {
  environment: 'jsdom',
  exclude: [...configDefaults.exclude, 'e2e/**'],
},

It is never imported by web/src/** production code, and the Playwright e2e specs under web/e2e/ run in real browsers and are excluded from vitest. So the blast radius is the vitest suite only.

The one thing to watch is getComputedStyle(). Both 30.0.0 and 30.0.1 change its behavior, and this repo has two production call sites plus a test that asserts against jsdom's implementation of it:

  • web/src/components/ToolPanel.tsx:213const overflowY = getComputedStyle(n).overflowY; (scroll-container walk)
  • web/src/components/AppShell.tsx:189const overflowX = getComputedStyle(n).overflowX; (same idiom, horizontal)
  • web/src/components/ToolPanel.test.tsx:599 — comment reads "jsdom's getComputedStyle sees it; scroll metrics stubbed (jsdom is 0)."

Both call sites read overflow-x / overflow-y, which are keyword properties (visible, auto, scroll, hidden). The 30.0.0 changes are about length values converted to pixels and CSS function serialization (calc(), var(), etc.) — neither touches keyword serialization. So this is expected to pass unchanged. Run the suite and confirm; if ToolPanel.test.tsx fails, treat it as a real finding and report it on this issue rather than loosening the assertion.

Nothing in this repo calls CSS.escape, CSS.supports, document.evaluate, or reads background-position-*, so the additive changes are inert.

The Node.js floor — both gates already checked, both clear

jsdom 30 declares engines: { node: "^22.22.2 || ^24.15.0 || >=26.0.0" }. Two places build the SPA:

  1. Native CI.forgejo/workflows/ci.yml:51 pins node-version: "24" via actions/setup-node@v4, which resolves to the newest 24.x. Currently ≥ 24.16.0. Clear.
  2. Hermetic nix gatenix/package.nix builds the web derivation with nixpkgs' default nodejs. At this repo's pinned flake.lock rev (d407951447dc, locked 2026-07-05), nixpkgs sets nodejs = nodejs_24 (pkgs/top-level/all-packages.nix:2428) and nodejs/v24.nix:26 reads version = "24.16.0". 24.16.0 ≥ 24.15.0 — clear.

No flake.lock bump is needed for this, and none should be included. If npm install errors with EBADENGINE, that contradicts the check above — stop and report it here rather than adding --force or an engines override.

Scope

Path Change
web/package.json:29 "jsdom": "^29.1.1""^30.0.1"
web/package-lock.json regenerated by npm install, committed in the same commit

That is all. No nix hash to regenerate — unlike the Go side's vendorHash, the SPA derivation uses importNpmLock (nix/package.nix:74-75), and the comment at nix/package.nix:67-68 states it outright: "importNpmLock derives per-package fetches from web/package-lock.json directly — no npmDepsHash to keep in sync." Do not go looking for one.

Both package.json and package-lock.json must land together — CI runs npm ci (.forgejo/workflows/ci.yml:57), which fails if they are out of sync.

No production code change is expected. If you need one, that is a finding — report it here.

How to verify

cd web
npm install                 # regenerates package-lock.json
npm ci                      # proves package.json and the lock agree (what CI runs)
npm run lint
npm run format:check
npm test                    # vitest under the new jsdom — the real gate
npm run build               # tsc --noEmit && vite build

Then the hermetic gate from the repo root, which re-runs lint + format:check + test inside the nix derivation against nixpkgs' node:

nix flake check

Done means

  • web/package.json declares "jsdom": "^30.0.1" and web/package-lock.json resolves jsdom to 30.0.1, both in the same commit.
  • npm ci succeeds (package.json and lockfile in sync) with no EBADENGINE warning.
  • npm test passes with no test edited — in particular web/src/components/ToolPanel.test.tsx passes unchanged.
  • npm run lint, npm run format:check, and npm run build all green.
  • nix flake check passes.
  • No flake.lock change, no nix/package.nix change, no web/src/** change in the diff.
  • PR body says Closes #<this issue>.

Out of scope — the two blocked majors, with the evidence

Do not attempt either of these in this PR. Both were investigated on 2026-08-01 and both are blocked by hard peer-dependency ranges. They are recorded here so the next update sweep does not re-derive them.

ESLint 9.39.5 → 10.8.0 — blocked by eslint-plugin-solid

eslint-plugin-solid@0.14.5 (web/package.json:28, used at web/eslint.config.js:3) declares:

"peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "typescript": ">=4.8.4" }

No ^10.0.0. Upstream is effectively dormant — 0.14.5 was published 2024-12-11 and the last commit to solidjs-community/eslint-plugin-solid was 2025-11-18. ESLint 10 support is tracked in open issue #203 (2026-02-19) and open, unmerged PR #207 "Add eslint v10 support" (last updated 2026-07-30).

typescript-eslint@8.65.0 already accepts eslint: ^8.57.0 || ^9.0.0 || ^10.0.0, so it is only the solid plugin holding this back. Note also that ESLint 10's eslint:recommended newly enables no-unassigned-vars, no-useless-assignment, and preserve-caught-error, and that "JSX references are now tracked" in scope analysis — so even once the peer range opens, the bump will produce new lint findings and needs its own issue.

We are already on the newest 9.x (9.39.5, npm's maintenance dist-tag), so there is nothing to do on ESLint today.

TypeScript 6.0.3 → 7.0.2 — blocked by typescript-eslint

typescript-eslint@8.65.0 and both @typescript-eslint/* packages declare:

"peerDependencies": { "typescript": ">=4.8.4 <6.1.0" }

TypeScript 7.0.2 is far outside that range — so is 6.1. 6.0.3 (published 2026-04-16) is the newest TypeScript release inside the supported range, and it is what we are pinned to. Nothing to do until typescript-eslint ships a release that widens the range.

Not npm, checked and deliberately skipped

  • Go modules — covered by #250, do not touch go.mod here.
  • codex agent-tools pin — covered by #249.
  • Claude Code agent-tools pin — covered by #235; 2.1.220 is still npm's latest for @anthropic-ai/claude-code as of 2026-08-01, so there is nothing newer to chase.
  • alpine:3.22 base in containers/agent-tools/Containerfile.claude:31 / Containerfile.codex:27 — investigated and deliberately not filed. Alpine 3.24 exists, but alpine:3.22 is a floating minor tag on a branch supported until 2027-05-01 that already receives musl security backports: Alpine's secdb shows CVE-2026-6042 fixed at musl 1.2.5-r11 and CVE-2026-40200 at musl 1.2.5-r12, which is what alpine:3.22 currently ships. There is no open musl advisory against the pinned base, and the loader it contributes to the claude image (Containerfile.claude:69,77) is the sole libc every containerized Claude Code instance consults — so bumping it to musl 1.2.6 without a security driver is risk with no payoff.

Filed by the Daily agent update check schedule, 2026-08-01. Versions from registry.npmjs.org; advisories from the GitHub Advisory Database; jsdom changelog from its GitHub releases; nixpkgs node version read from the rev pinned in this repo's flake.lock.

## Goal Bump **`jsdom` 29.1.1 → 30.0.1** in `web/package.json` + `web/package-lock.json`. It is the only npm major in this repo's SPA that can actually land right now — the other two available majors (ESLint 10, TypeScript 7) are blocked by peer-dependency walls that are documented at the bottom of this issue so nobody re-derives them. This is a small, bounded change. The reason it gets its own issue rather than a drive-by is that jsdom 30's single breaking change is a **Node.js floor bump**, which has to clear two independent gates (native CI and the hermetic nix gate) — both of which have been checked below, so you do not have to. Everything here was verified on 2026-08-01 against the npm registry, jsdom's GitHub releases, and nixpkgs at this repo's pinned `flake.lock` rev. ## Evidence — what is behind `web/package.json:29` declares `"jsdom": "^29.1.1"`; `web/package-lock.json` resolves it to **29.1.1**. Latest is **30.0.1** (published 2026-07-29). Every other direct dependency in `web/package.json` is already at its latest published release — commit `4cc17b6` refreshed the in-range pins on 2026-07-31. Verified against `registry.npmjs.org` on 2026-08-01: | Package | lockfile | npm `latest` | | |---|---|---|---| | `jsdom` | 29.1.1 | **30.0.1** | **this issue** | | `@solidjs/router` | 1.0.0 | 1.0.0 | current | | `solid-js` | 1.9.14 | 1.9.14 | current | | `lucide-solid` | 1.28.0 | 1.28.0 | current | | `vite` | 8.2.0 | 8.2.0 | current | | `vitest` | 4.1.10 | 4.1.10 | current | | `vite-plugin-solid` | 2.11.14 | 2.11.14 | current | | `prettier` | 3.9.6 | 3.9.6 | current | | `@playwright/test` | 1.62.1 | 1.62.1 | current | | `@typescript-eslint/*`, `typescript-eslint` | 8.65.0 | 8.65.0 | current | | `eslint-plugin-solid` | 0.14.5 | 0.14.5 | current (upstream is dormant — see below) | | `eslint`, `@eslint/js` | 9.39.5 | 9.39.5 on the `maintenance` tag | **blocked** on 10.x, see below | | `typescript` | 6.0.3 | 6.0.3 is the newest 6.x | **blocked** on 7.x, see below | So `jsdom` is the whole of the actionable work. ## Security **No npm advisory reaches any version in this repo's dependency tree, and jsdom 30 does not close one.** Checked every direct dependency against the GitHub Advisory Database (`ecosystem=npm`) on 2026-08-01; the only advisories that matched a package we use are all patched well below our pins: ``` GHSA-fx2h-pf6j-xcff high vite vulnerable >= 8.0.0, <= 8.0.15 patched 8.0.16 (we are on 8.2.0) GHSA-4w7w-66w2-5vf9 medium vite vulnerable >= 8.0.0, <= 8.0.4 patched 8.0.5 (we are on 8.2.0) GHSA-v2wj-q39q-566r high vite vulnerable >= 8.0.0, <= 8.0.4 patched 8.0.5 (we are on 8.2.0) GHSA-5xrq-8626-4rwp critical vitest vulnerable >= 4.0.0, < 4.1.0 patched 4.1.0 (we are on 4.1.10) GHSA-3qxh-p7jc-5xh6 high solid-js vulnerable < 1.9.4 patched 1.9.4 (we are on 1.9.14) ``` jsdom's only advisory (`GHSA-f4c9-cqv8-9v98`) is a **withdrawn** 2022 entry. **This bump is hygiene, not a security fix** — do not describe it as one in the PR. ## What actually changed in jsdom 29.1.1 → 30.0.1 From jsdom's GitHub releases. The delta is two releases and it is small. ### v30.0.0 (2026-07-27) > **Breaking changes:** > - Node.js minimum version raised to `^22.22.2 || ^24.15.0 || >=26.0.0`. That is the **entire** breaking-change list — one item. Other changes, all additive or bug fixes: > * Added `CSS.escape()` and `CSS.supports()` functions. > * Added `'background-position-x'` and `'background-position-y'` CSS properties. > * Fixed `getComputedStyle()` to convert length values into pixels. > * Fixed CSS function serialization, e.g., in the return value of `getPropertyValue()`. > * Fixed the type of error thrown by `document.evaluate()`. ### v30.0.1 (2026-07-29) > * Fixed `getComputedStyle()` with `calc()` and other functions throwing an exception, which regressed in v30.0.0. > * Sped up range operations on large documents. **Take 30.0.1, not 30.0.0** — 30.0.0 shipped a `getComputedStyle()` regression that 30.0.1 fixes. ## Does any of it break how this repo uses jsdom? jsdom is a **devDependency only**. Its sole use is as the vitest test environment — `web/vite.config.ts:29`: ```ts test: { environment: 'jsdom', exclude: [...configDefaults.exclude, 'e2e/**'], }, ``` It is never imported by `web/src/**` production code, and the Playwright e2e specs under `web/e2e/` run in real browsers and are excluded from vitest. So the blast radius is the vitest suite only. **The one thing to watch is `getComputedStyle()`.** Both 30.0.0 and 30.0.1 change its behavior, and this repo has two production call sites plus a test that asserts against jsdom's implementation of it: - `web/src/components/ToolPanel.tsx:213` — `const overflowY = getComputedStyle(n).overflowY;` (scroll-container walk) - `web/src/components/AppShell.tsx:189` — `const overflowX = getComputedStyle(n).overflowX;` (same idiom, horizontal) - `web/src/components/ToolPanel.test.tsx:599` — comment reads *"jsdom's getComputedStyle sees it; scroll metrics stubbed (jsdom is 0)."* Both call sites read `overflow-x` / `overflow-y`, which are **keyword** properties (`visible`, `auto`, `scroll`, `hidden`). The 30.0.0 changes are about **length values converted to pixels** and **CSS function serialization** (`calc()`, `var()`, etc.) — neither touches keyword serialization. **So this is expected to pass unchanged.** Run the suite and confirm; if `ToolPanel.test.tsx` fails, treat it as a real finding and report it on this issue rather than loosening the assertion. Nothing in this repo calls `CSS.escape`, `CSS.supports`, `document.evaluate`, or reads `background-position-*`, so the additive changes are inert. ## The Node.js floor — both gates already checked, both clear jsdom 30 declares `engines: { node: "^22.22.2 || ^24.15.0 || >=26.0.0" }`. Two places build the SPA: 1. **Native CI** — `.forgejo/workflows/ci.yml:51` pins `node-version: "24"` via `actions/setup-node@v4`, which resolves to the newest 24.x. Currently ≥ 24.16.0. **Clear.** 2. **Hermetic nix gate** — `nix/package.nix` builds the `web` derivation with nixpkgs' default `nodejs`. At this repo's pinned `flake.lock` rev (`d407951447dc`, locked 2026-07-05), nixpkgs sets `nodejs = nodejs_24` (`pkgs/top-level/all-packages.nix:2428`) and `nodejs/v24.nix:26` reads `version = "24.16.0"`. **24.16.0 ≥ 24.15.0 — clear.** **No `flake.lock` bump is needed for this, and none should be included.** If `npm install` errors with `EBADENGINE`, that contradicts the check above — stop and report it here rather than adding `--force` or an `engines` override. ## Scope | Path | Change | |---|---| | `web/package.json:29` | `"jsdom": "^29.1.1"` → `"^30.0.1"` | | `web/package-lock.json` | regenerated by `npm install`, committed in the same commit | That is all. **No nix hash to regenerate** — unlike the Go side's `vendorHash`, the SPA derivation uses `importNpmLock` (`nix/package.nix:74-75`), and the comment at `nix/package.nix:67-68` states it outright: *"importNpmLock derives per-package fetches from web/package-lock.json directly — no npmDepsHash to keep in sync."* Do not go looking for one. Both `package.json` and `package-lock.json` must land together — CI runs `npm ci` (`.forgejo/workflows/ci.yml:57`), which fails if they are out of sync. No production code change is expected. If you need one, that is a finding — report it here. ## How to verify ```sh cd web npm install # regenerates package-lock.json npm ci # proves package.json and the lock agree (what CI runs) npm run lint npm run format:check npm test # vitest under the new jsdom — the real gate npm run build # tsc --noEmit && vite build ``` Then the hermetic gate from the repo root, which re-runs lint + format:check + test inside the nix derivation against nixpkgs' node: ```sh nix flake check ``` ## Done means - [ ] `web/package.json` declares `"jsdom": "^30.0.1"` and `web/package-lock.json` resolves jsdom to `30.0.1`, both in the same commit. - [ ] `npm ci` succeeds (package.json and lockfile in sync) with no `EBADENGINE` warning. - [ ] `npm test` passes with **no test edited** — in particular `web/src/components/ToolPanel.test.tsx` passes unchanged. - [ ] `npm run lint`, `npm run format:check`, and `npm run build` all green. - [ ] `nix flake check` passes. - [ ] No `flake.lock` change, no `nix/package.nix` change, no `web/src/**` change in the diff. - [ ] PR body says `Closes #<this issue>`. ## Out of scope — the two blocked majors, with the evidence **Do not attempt either of these in this PR.** Both were investigated on 2026-08-01 and both are blocked by hard peer-dependency ranges. They are recorded here so the next update sweep does not re-derive them. ### ESLint 9.39.5 → 10.8.0 — blocked by `eslint-plugin-solid` `eslint-plugin-solid@0.14.5` (`web/package.json:28`, used at `web/eslint.config.js:3`) declares: ```json "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "typescript": ">=4.8.4" } ``` No `^10.0.0`. Upstream is effectively dormant — 0.14.5 was published **2024-12-11** and the last commit to `solidjs-community/eslint-plugin-solid` was **2025-11-18**. ESLint 10 support is tracked in **open** issue [#203](https://github.com/solidjs-community/eslint-plugin-solid/issues/203) (2026-02-19) and **open, unmerged** PR [#207](https://github.com/solidjs-community/eslint-plugin-solid/pull/207) "Add eslint v10 support" (last updated 2026-07-30). `typescript-eslint@8.65.0` already accepts `eslint: ^8.57.0 || ^9.0.0 || ^10.0.0`, so it is *only* the solid plugin holding this back. Note also that ESLint 10's `eslint:recommended` newly enables `no-unassigned-vars`, `no-useless-assignment`, and `preserve-caught-error`, and that "JSX references are now tracked" in scope analysis — so even once the peer range opens, the bump will produce new lint findings and needs its own issue. **We are already on the newest 9.x** (`9.39.5`, npm's `maintenance` dist-tag), so there is nothing to do on ESLint today. ### TypeScript 6.0.3 → 7.0.2 — blocked by `typescript-eslint` `typescript-eslint@8.65.0` and both `@typescript-eslint/*` packages declare: ```json "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } ``` TypeScript 7.0.2 is far outside that range — so is 6.1. **`6.0.3` (published 2026-04-16) is the newest TypeScript release inside the supported range**, and it is what we are pinned to. Nothing to do until typescript-eslint ships a release that widens the range. ### Not npm, checked and deliberately skipped - **Go modules** — covered by #250, do not touch `go.mod` here. - **codex agent-tools pin** — covered by #249. - **Claude Code agent-tools pin** — covered by #235; `2.1.220` is still npm's `latest` for `@anthropic-ai/claude-code` as of 2026-08-01, so there is nothing newer to chase. - **`alpine:3.22` base in `containers/agent-tools/Containerfile.claude:31` / `Containerfile.codex:27`** — investigated and deliberately **not** filed. Alpine 3.24 exists, but `alpine:3.22` is a floating minor tag on a branch supported until **2027-05-01** that already receives musl security backports: Alpine's secdb shows `CVE-2026-6042` fixed at `musl 1.2.5-r11` and `CVE-2026-40200` at `musl 1.2.5-r12`, which is what `alpine:3.22` currently ships. There is **no open musl advisory** against the pinned base, and the loader it contributes to the claude image (`Containerfile.claude:69,77`) is the sole libc every containerized Claude Code instance consults — so bumping it to musl 1.2.6 without a security driver is risk with no payoff. --- *Filed by the Daily agent update check schedule, 2026-08-01. Versions from `registry.npmjs.org`; advisories from the GitHub Advisory Database; jsdom changelog from its GitHub releases; nixpkgs node version read from the rev pinned in this repo's `flake.lock`.*
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab#255
No description provided.