Skip to content

chore(deps): modernize toolchain (Vite 8, TS 6, ESLint 10, Hono) + cut 14 direct deps (-139 pkgs) - #786

Merged
FelixTJDietrich merged 1 commit into
mainfrom
chore/dependency-hygiene
Jul 1, 2026
Merged

chore(deps): modernize toolchain (Vite 8, TS 6, ESLint 10, Hono) + cut 14 direct deps (-139 pkgs)#786
FelixTJDietrich merged 1 commit into
mainfrom
chore/dependency-hygiene

Conversation

@FelixTJDietrich

@FelixTJDietrich FelixTJDietrich commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

A ruthless dependency-modernization pass over the whole monorepo: push the build toolchain to the latest working majors, migrate the server's runtime majors (including Express 5 → Hono 4), and cut dead/redundant packages — leaving the repo measurably leaner, more current, and behavior-identical.

Net −139 resolved packages (2667 → 2528), 14 direct dependencies removed vs 4 added, every runtime/published dependency at npm latest, and zero new vulnerabilities introduced (the one security-relevant bump, ws, actually fixes an advisory in the server runtime).

Stacked on top of #785 (single-entry @tumaet/apollon), now merged to main; this PR rebases cleanly onto it with no conflicts.


Headline outcomes

Toolchain Vite 6→8 (Rolldown/Oxc) · @vitejs/plugin-react 4→6 · vite-plugin-dts 4→5 (+@microsoft/api-extractor) · TypeScript 5.7→6.0 · ESLint 9→10 (→ @eslint-react)
Server runtime zod 3→4 · redis 5→6 (RESP2-pinned) · pino 9→10 · dotenv 16→17 · ulid 2→3 · Express 5 → Hono 4
Footprint −139 packages net · 14 direct deps cut · webapp initial bundle ≈ −47% (the @ionic/react drop)
Security 0 introduced; ws 8.18→8.21 remediates GHSA ws-DoS in the WS relay; all residual crit/high are inherited dev/build/test transitives
Currency Every runtime + published dependency at npm latest; the only lag is dev tooling and items deliberately held by the repo's minimumReleaseAge

1. Build toolchain & shared dev tooling

Package Before After Rationale
vite 6.4.2 8.1.0 Rolldown/Oxc bundler+minifier. Oxc drops debugger on minify, so the explicit esbuild: { drop } is gone.
@vitejs/plugin-react 4.3.4 6.0.3 Requires Vite 8's vite/internal; paired with the Vite bump.
vite-plugin-dts 4.3.0 5.0.3 v5 renamed rollupTypesbundleTypes; emits one rolled, self-contained .d.ts per entry via api-extractor.
@microsoft/api-extractor 7.58.9 (new) Now an explicit peer of vite-plugin-dts 5; analyzes the TS-6-emitted declarations and rolls @tumaet/ui in.
typescript 5.7.2 6.0.0 Dropped deprecated baseUrl (TS5101) and made paths ./-relative across all tsconfigs.
typescript-eslint 8.18.1 8.62.0 TS 6 support. (Configs are not type-aware, so no unsupported-version warning fires.)
eslint 9.17.0 10.6.0 Latest major.
@eslint/js 9.17.0 10.0.1 Latest published @eslint/js (it versions independently of eslint — 10.0.1 is the head, not 10.6.0).
eslint-plugin-react 7.37.2 removed@eslint-react/eslint-plugin 5.9.3 eslint-plugin-react has no ESLint-10 release (calls the removed context.getFilename()). @eslint-react is flat-config + v10-native; recommended-typescript preserves coverage and disables the prop-types rules TS already enforces.
eslint-plugin-react-hooks 7.1.1 7.1.1 Retained as the hooks / React-Compiler authority; @eslint-react's overlapping hook rules are turned off so each concern is reported once.
globals 15.13.0 17.7.0 Latest.
jsdom 28.1.0 29.1.1 Latest (test env + server DOM).
size-limit / @size-limit/* 11.2.0 12.1.0 Latest major.
vitest / @vitest/* 4.1.2 4.1.9 Latest patch.
tailwindcss / @tailwindcss/* 4.0.17 4.3.1 Latest (Vite 8 peer).
@types/node 24.12.4 26.0.1 Latest.
@playwright/test / playwright 1.59.1 1.61.1 Latest; kept on one version (single playwright-core + browser set). CI image → v1.61.1-noble.
@commitlint/* 19.6.0 21.1.0 Latest major.
concurrently 9.1.0 10.0.3 Latest major.
knip 6.18.0 6.20.0 Bump (newly surfaced two findings — addressed below).
lint-staged 15.5.2 17.0.8 Latest major.
prettier 3.4.2 3.8.4 Latest minor.

2. Server runtime majors + Express → Hono

Package Before After Rationale / breaking-change handling
express + cors + cookie + pino-http 5.1.0 / 2.8.5 / 1.0.2 / 10.4.0 removed Replaced by Hono. Dropped these 4 + ~18 transitives (body-parser, raw-body, finalhandler, qs, send, accepts, …).
hono 4.12.27 (new) Zero runtime dependencies. Routes, status codes, bodies, headers, owner-cookie HMAC, CORS policy, zod error envelopes and the request-id/access-log middleware preserved.
@hono/node-server 2.0.6 (new) Zero runtime dependencies. buildApp still returns an unstarted http.Server via createAdaptorServer, so server.ts + all supertest tests are unchanged. The yjs WS relay (standalone ws on WS_PORT) is intentionally untouched (moving it would change the client URL contract).
zod 3.24.2 4.4.3 Migrated per v4 guidance; error envelopes preserved.
redis 5.11.0 6.0.1 v6 defaults to RESP3, which changes raw sendCommand reply shapes this module parses; pinned the documented { RESP: 2 } escape hatch to preserve wire shapes; Redis type derived from the factory.
pino 9.6.0 10.3.1 Latest major (access logging reimplemented as Hono middleware reusing the existing logger).
ulid 2.3.0 3.0.2 Latest major (API-compatible).
ws 8.18.1 8.21.0 Security: >=8.0.0 <8.21.0 is vulnerable to a memory-exhaustion DoS (GHSA-3h5v-q93c-6h6q). This bump patches the server's WS-relay runtime.
pdfmake 0.3.7 0.3.11 Latest patch.
global-jsdom 28.0.0 29.0.0 Tracks the jsdom 29 bump.
canvas (@napi-rs/canvas) 1.0.0 1.0.1 Latest patch.
supertest / @types/supertest 7.0.0 / 6.0.2 7.2.2 / 7.2.0 Latest (test only).
testcontainers 10.16.0 12.0.3 Latest major (integration-test only).
tsx 4.19.2 4.22.4 Latest (dev runner).
@types/express* / @types/cors (present) removed Express type packages no longer needed.

3. Cuts (dependency removals)

Removed Where Replacement / why it's safe
uuid + @types/uuid library, webapp Library: a 9-line embed-safe crypto.getRandomValues RFC-4122 v4 (works in any context, unlike crypto.randomUUID which needs a secure context an embed can't assume); webapp uses native crypto.randomUUID. New test asserts format/variant over 10k samples + uniqueness + CSPRNG.
cmdk packages/ui The command component had zero importers; removed it and the dep.
@ionic/react webapp Imported only for isPlatform() UA checks but dragged the entire Stencil runtime into the initial bundle and double-bundled the editor. Replaced by a 45-line MIT-attributed platform.ts (UA detection reproduced byte-for-byte; isNativePlatform = Capacitor.isNativePlatform()). Initial bundle ≈ −47%.
express, cors, cookie, pino-http server → Hono (see §2).
dotenv server → Node's built-in util.parseEnv (stable since Node 20.12; the server targets Node ≥24.15). The side-effect env.ts reads .env and merges it with the same semantics dotenv had — silent, no-override of pre-set vars, no-op when the file is absent. Parity unit-checked; one fewer runtime dependency.
@vitest/coverage-v8 packages/ui Dead there — no coverage config and no test:coverage script (only vitest run). library/server keep it (they do collect coverage).
babel-plugin-react-compiler vscode-extension Kept / restored. It is used — webview-vite.config.ts (a root-workspace file re-exported by editor/menu) references it as a Babel plugin. An earlier pass wrongly cut it; restoring it keeps the dependency graph honest (no phantom dep resolved only via hoisting).

4. Notable client bumps

Package Before After
@base-ui/react 1.5.0 1.6.0
@xyflow/react 12.9.3 12.11.1 (latest major — there is no v13)
zustand 5.0.12 5.0.14
lucide-react 1.18.0 1.22.0
react-toastify 11.0.5 11.1.0
@tanstack/react-router 1.170.15 1.170.16
@capacitor/* 8.3.0 8.4.1
@chenglou/pretext 0.0.6 0.0.8
@easyops-cn/docusaurus-search-local · @mermaid-js/layout-elk 0.55.1 · 0.2.1 0.55.2 · 0.2.2

yjs (13.6.31) + y-protocols (1.0.7) were promoted to the catalog: the library externalizes them as a CRDT singleton, so every client workspace must pin exactly one version.


5. Security audit (pnpm audit)

No vulnerability is introduced by this PR. Every critical/high advisory is on a transitive package that is already present in main and lives in a dev / build / test toolchain — none in the published @tumaet/apollon runtime or the server's request-handling runtime. The one security-relevant change is a remediation:

Advisory Severity Path Status
ws memory-exhaustion DoS (GHSA-3h5v-q93c-6h6q) high server WS relay (direct ws) Fixed by this PR (ws 8.18.1 → 8.21.0)
shell-quote newline escape critical docs > webpack-dev-server > launch-editor Inherited; Docusaurus dev-server build chain only
serialize-javascript, ws, lodash-es, linkify-it high docs > @docusaurus/* (webpack / mermaid / markdownlint) Inherited; docs build only
undici (TLS-bypass / WS-DoS / routing) high library > vitest > jsdom > undici, docs > cheerio > undici Inherited; test + docs only. The server's jsdom uses no outbound undici TLS path, so the advisory doesn't apply to that usage.
tmp path traversal high webapp > @capacitor/assets > @trapezedev/project Inherited; native-asset generation tool only
form-data CRLF high server > supertest > superagent Inherited; test only

Recommendation (gap, not done here): the remaining inherited transitives are backward-compatible patch bumps and could be force-floated via pnpm.overrides (e.g. undici@>=7.28.0, form-data@>=4.0.6, tmp@>=0.2.6). They're left out of this PR because they're dev/build-only and belong with their respective Docusaurus/Capacitor/Vitest major updates rather than this toolchain pass — happy to add an overrides block if preferred.


6. Compatibility ceilings (held with evidence, not guessed)

Wanted Reality Resolution
ESLint 10 eslint-plugin-react has no v10 release (peer caps ^9.7; calls removed context.getFilename()) Migrate to @eslint-react (v10-native, flat-config)
Vite 8 + correct .d.ts older api-extractor choked on React 19's JSX.Element Install @microsoft/api-extractor 7.58.9 + bundleTypes
@vitejs/plugin-react 6 needs Vite 8's vite/internal paired with Vite 8
api-extractor on TS 6 api-extractor 7.58.9 (the latest) still bundles TS 5.9.3 → emits a "newer than bundled engine" warning Accepted: build is green and the rolled .d.ts is type-checked against real built types by check:doc-snippets in CI. No newer api-extractor exists yet.

7. Version-currency & intentional holds

Every runtime and published dependency is at npm latest. The only items behind latest are dev tooling and release-age holds:

Package Pinned Latest Why held
vite 8.1.0 8.1.2 Published <24h ago — held by the repo's minimumReleaseAge
typescript-eslint 8.62.0 8.62.1 Published <24h ago — release-age hold
@eslint-react/eslint-plugin 5.9.3 5.10.0 New minor (new rules) — deferred to avoid re-triaging lint in this PR
tailwindcss 4.3.1 4.3.2 Patch; low value, deferred
knip · prettier · @commitlint/cli · testcontainers 6.20.0 · 3.8.4 · 21.1.0 · 12.0.3 6.23.0 · 3.9.4 · 21.2.0 · 12.0.4 Dev-only lag; each needs a re-format/re-lint pass. Trivial follow-up.

8. Verification

All commands run green on this branch (rebased on main):

Check Result
pnpm install --frozen-lockfile
pnpm lint (all workspaces) ✅ 0 errors
pnpm knip fully clean — 0 findings, 0 config hints
Builds: library (single-entry incl. rolled .d.ts), webapp (tsc -b), server, vscode build:all, @tumaet/ui types
@tumaet/ui typecheck (incl. 28 stories)
size-limit ✅ library 45.4/85 kB, export 1.4/700 kB; webapp initial 34.65/35 kB, total 1.11/1.3 MB
Tests ✅ library 1222 · webapp 256 · server 150 (Redis/WS Docker integration) · ui 37

9. Gaps & open follow-ups (honest)

  • Inherited transitive vulns (§5): not patched here; recommend a follow-up pnpm.overrides block (or the corresponding Docusaurus/Capacitor/Vitest major updates).
  • Dev-tooling version lag (§7): knip / prettier / commitlint / testcontainers / @eslint-react / tailwindcss have newer releases; each is a trivial, separable bump.
  • api-extractor ↔ TS 6 (§6): the bundled-TS warning persists until a TS-6-aware api-extractor ships; mitigated by the CI doc-snippet type-check against the built .d.ts.
  • Server-behavior polish (separate from dependency hygiene): the Hono migration is behavior-preserving and fully tested, but a few code-quality items remain for a follow-up — log c.req.url (with query) rather than c.req.path, take the last X-Forwarded-Proto segment to match the old trust proxy=1, replace two as/as never escapes, and add explicit CORS-parity + body-limit-drain tests.
  • Visual baselines: regenerate for Playwright 1.59→1.61 via the update-visual-baselines workflow (image already bumped to v1.61.1-noble).

How to test

pnpm install --frozen-lockfile && pnpm -r build && pnpm lint && pnpm knip
pnpm --filter @tumaet/ui run typecheck         # now covers 28 stories
pnpm --filter @tumaet/apollon test             # 1222
pnpm --filter @tumaet/webapp test && pnpm --filter @tumaet/webapp run size
pnpm --filter @tumaet/server test              # 150, needs Docker (testcontainers)

Confirm library/dist emits only index/internals/export.d.ts (no react/external), packages/ui/dist has zero *.stories.d.ts, and @tumaet/apollon resolves as a single entry from the webapp.

🤖 Generated with Claude Code

@FelixTJDietrich FelixTJDietrich changed the title chore(deps): modernize toolchain (Vite 8, TS 6, ESLint 10, Hono) + cut 40+ dependencies chore(deps): modernize toolchain (Vite 8, TS 6, ESLint 10, Hono) + cut 13 direct deps (−138 packages) Jun 30, 2026
@FelixTJDietrich FelixTJDietrich changed the title chore(deps): modernize toolchain (Vite 8, TS 6, ESLint 10, Hono) + cut 13 direct deps (−138 packages) chore(deps): modernize toolchain (Vite 8, TS 6, ESLint 10, Hono) + cut 14 direct deps (−139 packages) Jun 30, 2026
@FelixTJDietrich
FelixTJDietrich force-pushed the chore/dependency-hygiene branch 6 times, most recently from a115c36 to 60ae054 Compare July 1, 2026 06:56
@FelixTJDietrich
FelixTJDietrich marked this pull request as ready for review July 1, 2026 07:16
@FelixTJDietrich

Copy link
Copy Markdown
Contributor Author

CI note: e2e-tests is failing on an environmental flake, not this PR's changes

All deterministic checks are green (lint, format, docs typecheck, build, size-limit, knip, and every unit/integration suite — library 1222 / server 150 / ui 37 / webapp 256). The server was also validated live end-to-end (all routes + SVG/PNG/PDF export, soft-owner cookie, CORS, graceful shutdown).

The only red is e2e-tests, and the evidence shows it's CI-runner slowness, not a regression:

  • visual-regression-tests and perf-firefox-tests pass on the same runs — both render the editor canvas and pixel-screenshot it, so the canvas renders correctly with every bumped dependency.
  • The e2e failures are waitForCanvasReady 15 s timeouts (helpers/canvas.ts:13, .react-flow slow to become visible), hitting unrelated tests (navbar, templates, mobile) — up to 8 flaky in a single run.
  • An earlier run of this exact branch passed all e2e (0 failed); it had the same class of canvas-timing flake, just milder. The webapp initial bundle is ~47 % smaller here (dropped @ionic/react), so load is faster, not slower.

No code fix belongs in a dependency PR for this — the remaining levers are the shared waitForCanvasReady timeout (a #778/#761 helper) or CI-runner capacity. Recommend re-running e2e (it passes on a healthy runner) and/or a separate test-stability change. Happy to open that separately.

@FelixTJDietrich FelixTJDietrich changed the title chore(deps): modernize toolchain (Vite 8, TS 6, ESLint 10, Hono) + cut 14 direct deps (−139 packages) chore(deps): modernize toolchain (Vite 8, TS 6, ESLint 10, Hono) + cut 14 direct deps (-139 pkgs) Jul 1, 2026
@FelixTJDietrich
FelixTJDietrich force-pushed the chore/dependency-hygiene branch from 60ae054 to a064942 Compare July 1, 2026 07:44

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FelixTJDietrich The dependency cleanup and Hono migration look solid, and CI is green. I left one small docs cleanup inline; approving.

Comment thread .changeset/library-dependency-modernization.md
@FelixTJDietrich
FelixTJDietrich force-pushed the chore/dependency-hygiene branch 7 times, most recently from c9efc2b to 24778da Compare July 1, 2026 09:39

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FelixTJDietrich The dependency and Hono migration code generally looks solid, but the current e2e-tests check is failing on the changed Playwright readiness path. I left that as the blocking item inline, plus two small stale-doc cleanup notes.

Comment thread standalone/webapp/tests/helpers/canvas.ts Outdated
Comment thread .changeset/standalone-runtime-modernization.md
Comment thread .github/workflows/update-visual-baselines.yml
@github-project-automation github-project-automation Bot moved this from Backlog to In progress in Apollon Development Jul 1, 2026
@FelixTJDietrich
FelixTJDietrich force-pushed the chore/dependency-hygiene branch 6 times, most recently from fe8fbab to 8fc6563 Compare July 1, 2026 11:12
@FelixTJDietrich
FelixTJDietrich force-pushed the chore/dependency-hygiene branch 7 times, most recently from 194e204 to f8a9905 Compare July 1, 2026 12:33
…t 14 direct deps (-139 pkgs)

Toolchain to the latest working majors:
- Vite 6→8 (Rolldown/Oxc), @vitejs/plugin-react 4→6, vite-plugin-dts 4→5
  (rollupTypes→bundleTypes via @microsoft/api-extractor), TypeScript 5.7→6.0.
- ESLint 9→10. eslint-plugin-react has no v10 release (it calls the removed
  context.getFilename()), so React linting moves to @eslint-react/eslint-plugin
  (flat-config, v10-native); eslint-plugin-react-hooks stays the hooks authority
  and @eslint-react's overlapping hook rules are disabled so each is reported once.

Server runtime majors:
- zod 3→4, redis 5→6 (pinned RESP2 to preserve the raw sendCommand reply shapes
  this code parses), pino 9→10, dotenv 16→17, ulid 2→3.
- Express 5 → Hono 4 + @hono/node-server (both zero-runtime-dep), dropping
  express/cors/cookie/pino-http and ~18 transitives. Routes, status codes, bodies,
  headers, owner-cookie HMAC, CORS policy, zod error envelopes and the yjs WS relay
  are preserved; buildApp still returns an unstarted http.Server.

Cuts:
- uuid → an embed-safe crypto.getRandomValues RFC-4122 v4 (works in any context,
  unlike crypto.randomUUID which needs a secure context an embed can't assume).
- cmdk + the unused command component; @ionic/react → a 45-line vendored
  platform.ts (its Stencil runtime was in the initial bundle for UA checks only) —
  webapp initial bundle nearly halved.
- packages/ui: drop dead @vitest/coverage-v8 (no coverage config/script there).

~50 deps bumped to latest (modulo the repo's >24h minimumReleaseAge).

Verified: pnpm install --frozen-lockfile; pnpm lint 0 errors; pnpm knip clean;
builds (library single-entry incl. rolled .d.ts, webapp, server, vscode, ui types
+ stories typecheck); size-limit within budget (webapp initial 34.65/35 kB);
tests library 1222 / webapp 256 / server 150 (Docker) / ui 37.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

test(e2e): run CI against a bundled build with test seams enabled (root-cause fix
for the flaky `.react-flow` canvas timeouts). The suite ran against the Vite dev
server because it needs the `import.meta.env.DEV`-gated test seams
(`window.apollonEditor`, `__apollonSafeArea`, `__apollonPerf`); the dev server
serves the app as hundreds of unbundled native-ESM modules and compiles the heavy
editor route on demand, so every test's fresh browser context refetched them — a
slow, variable request waterfall that intermittently blew the canvas-ready
budget, regardless of parallelism or timeout. Fix: gate the three seams on
`DEV || VITE_E2E` (still statically stripped from real prod builds — verified),
and in CI serve a `VITE_E2E=true` production build via `vite preview` — a handful
of static files, so loads are fast and deterministic and the suite keeps
`workers: "50%"` (short runs) without flake. Local runs keep the dev server.
Verified locally: the e2e build ships the seams, a plain prod build strips all
three, and preview serves the built app.

docs: resolve review comments — README tech-stack table and the server
Dockerfile image description now say Hono (not Express); the visual-tests
contributor doc uses the v1.61.1-noble Playwright image to match CI.

test(e2e): correct the landscape editor-chrome test to match the deliberate
design. `editor.spec.ts` "keeps the phone layout in landscape" asserted the
mobile compact pill (navbar ≤36px) at `PHONE_LANDSCAPE` (844px), but
`constants/responsive.ts` deliberately keeps the FULL desktop action set in
landscape (844px > NARROW_VIEW_QUERY's 767.95px — "wide enough … just shorter").
The assertion contradicted the app's documented intent and only ever "passed"
because the dev-server canvas flake killed the test before it reached the
assertion; with e2e now on a bundled build the test runs deterministically and
exposed the mismatch. Corrected the navbar bound to the unified header height
(≤52, same as the portrait sibling) and the palette overflow to the sub-pixel
tolerance (≤4); the safe-area-inset assertions (logo/actions/controls/minimap
clear the notch) are unchanged — that's the real regression guard. Verified
locally against the e2e build: the full 139-test suite passes (workers=2).

test(e2e): keep the library perf probe live in the e2e build so perf-firefox
works against the bundled app. The perf suite reads `window.__apollonPerf`, which
proxies the library's `ApollonEditor.__perf()` / perf-counters — both gated on
`import.meta.env.DEV`, so they returned undefined in the prod-mode e2e build. The
webapp consumes the library from source, so gating them on `DEV || VITE_E2E`
(shared `PERF_PROBE_ENABLED`) compiles them live under the `VITE_E2E=true` e2e
build while the standalone `build:lib` (no VITE_E2E) still strips them — verified:
`check-no-perf-hooks` stays green, the probe is present in the e2e build and
absent from a plain prod build, and the perf test passes locally.
@FelixTJDietrich
FelixTJDietrich force-pushed the chore/dependency-hygiene branch from f8a9905 to 0d49d51 Compare July 1, 2026 12:46
@FelixTJDietrich
FelixTJDietrich merged commit 16e90a7 into main Jul 1, 2026
21 checks passed
@FelixTJDietrich
FelixTJDietrich deleted the chore/dependency-hygiene branch July 1, 2026 12:51
@github-project-automation github-project-automation Bot moved this from In progress to Done in Apollon Development Jul 1, 2026
@github-actions github-actions Bot mentioned this pull request Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants