Repository navigation
fix: migrate four client packages to TypeScript 7 (Clients workflow) - #1583
Conversation
The npm-major bump in 688ce58 (PR #1555) moved TypeScript 5.9 -> 7 across the client packages and left six Clients jobs red. #1567 already fixed Portal-next. This fixes four of the remaining five; the two react-native jobs are a separate migration (see below). TS 7 no longer auto-includes every package under node_modules/@types the way 5.x did, so @types/node has to be named explicitly: - @meshweaver/react 11x TS2591 - the tests that read the server catalogs off disk (i18n/localize, render/parity, ciTrigger) lost node:fs, node:path and process. - @meshweaver/client-web same TS2591 in the wire/rest contract guards, plus the TS7006 implicit-any that follows from an untyped readFileSync. Both get "types": ["node"] - the fix the compiler's own diagnostic prescribes. Nothing is silenced: the files stay fully checked, and naming one package is stricter than 5.x's include-everything. - Portal example TS5102: baseUrl was removed in TS 7. Dropped it; the "paths" entries are already relative to the tsconfig, which is exactly what baseUrl "." resolved them against (same one-liner portal-next took in #1567). - @meshweaver/client a real API migration, not a config fix. TS 7 removed ts.transpileModule: the package's "." export is now lib/version.cjs (a version string) and the API moved to typescript/unstable/*, which is Project/Program/ Checker shaped with no single-string transform anywhere in it. The node kernel's type-stripper now uses amaro - the SWC-based stripper Node itself bundles for --experimental-strip-types. mode: "transform", not "strip": strip-only REJECTS the TS constructs that carry runtime semantics (enum, namespace, parameter properties) that transpileModule used to compile, and Node's built-in module.stripTypeScriptTypes is strip-only. Verified through executeCode that all of them still run. typescript drops to a devDependency (it is only tsc now), so the worker no longer ships the compiler. amaro declares engines node>=22, hence the engines field and that job moving to node 22 - Node 20 went end-of-life 2026-04-30. That job also gains `npm test`: it only ran typecheck, so the js/ts execution core this commit changes had no CI coverage at all. Strictly more checking. Verified locally with each job's own commands (npm ci from the committed lock, then typecheck/test/build): react 261 tests + lib build, client-web 52 tests, client 5 tests from a clean npm ci, portal tsc + vite build. NOT fixed here - RN connector and RN web need a scope call. Dependabot bumped expo 52->57 and react 18->19 but pinned react-native at 0.76.5, whose peers are react ^18.2.0 / @types/react ^18.2.6. That set is genuinely unresolvable, which is what the npm ci ERESOLVE reports; forcing it with --legacy-peer-deps or an overrides entry would install a combination upstream does not support. Pinning @types/react back to 18 does not work either: RN typechecks ../react/src, which resolves React 19 types from clients/react/node_modules, so the two React type identities collide (TS2786 across every JSX call site). The coherent direction is forward - react-native 0.82 peers are exactly react ^19.1.1 - i.e. the Expo SDK 52->57 upgrade, which also has to deal with expo-av. Tracked separately. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes multiple Clients workflow failures after the TypeScript 7 upgrade by updating client tsconfigs for explicit Node typings, adjusting a portal tsconfig for removed options, and migrating the Node worker’s TypeScript transform off the removed ts.transpileModule.
Changes:
- Migrate the Node worker TypeScript-to-JS transform from
typescript.transpileModuletoamaro, and update CI to use Node 22 plus add worker tests. - Add explicit
"types": ["node"]to affected client tsconfigs to restore Node globals/modules under TS 7. - Remove the
baseUrloption from the portal example tsconfig to align with TS 7 behavior.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| clients/typescript/src/worker.ts | Replaces TypeScript runtime transpilation with amaro transform-based compilation for Code node execution. |
| clients/typescript/package.json | Adds Node >=22 engines, moves typescript to devDependency, and adds amaro runtime dependency. |
| clients/typescript/package-lock.json | Updates lockfile to reflect amaro dependency and typescript as dev-only, plus engines metadata. |
| clients/react/tsconfig.json | Explicitly includes Node types to fix missing node:*/process typing under TS 7. |
| clients/grpc-web/tsconfig.json | Explicitly includes Node types for contract tests reading files under TS 7. |
| clients/portal/tsconfig.json | Drops baseUrl while keeping path aliases working under TS 7. |
| .github/workflows/clients.yml | Bumps the Node worker job to Node 22 and adds a test step to cover the execution core. |
Files not reviewed (1)
- clients/typescript/package-lock.json: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…O tests on node 20 The react job's `npm test` had NEVER RUN on main: the TS 7 typecheck failure in the step before it short-circuited the job, so this second breakage from the same dependabot commit (688ce58, jsdom 29 -> 30) sat latent behind it. Fixing the typecheck un-masked it. jsdom@30.0.1 declares engines ^22.22.2 || ^24.15.0 || >=26.0.0; jsdom 29 still allowed node 20. On node 20 its undici dependency throws TypeError: webidl.util.markAsUncloneable is not a function at new CacheStorage (node_modules/undici/lib/web/cache/cachestorage.js:20) at node_modules/jsdom/lib/api.js:12 because markAsUncloneable landed in node 22. Every jsdom worker then fails to start, so vitest collects nothing: Test Files no tests Tests no tests -> exit 1 That is the dangerous shape: "no tests" is a failure that reads like an absence of work rather than a break, which is exactly why this must be the node version and not a swallowed error. portal-next has been on node 22 all along, which is why it is green on the same jsdom. Verified by running the job's own commands on both runtimes against the same committed lock: node 20.20.2 -> npm ci ok, typecheck ok, 0 tests collected, exit 1 node 22.23.2 -> npm ci ok, typecheck ok, 35 files / 261 tests PASSED, build ok 22.23.2 satisfies jsdom's ^22.22.2. Node 20 also went end-of-life 2026-04-30. grpc-web and portal stay on 20 deliberately: neither uses jsdom (portal has no test script at all), so neither is sitting on this trap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8c3b062 to
986a213
Compare
|
Arrived at the same four fixes independently in #1585 before spotting this — closing mine in favour of this + #1587. Two findings from that pass that are not in either PR: 1. 🚨 The
|
|
Both points are correct, and I'm taking one and deliberately not taking the other. Recording why rather than resolving silently. The duplicated compile is real. The stale job name is a genuine defect and it gets fixed — a job labelled So the sequence is: this merges as-is, #1588 lands the rename it already carries. If #1588 stalls for any reason, the rename comes back here as a one-liner. Worth noting the two PRs independently added |
|
Runtime proof for the |
…kflow can go green The new 'Clients workflow verdict' gate resolves the Clients conclusion for the head commit. On this branch that conclusion was still 'failure' because the four packages #1583 migrates were red here - this branch predates it. Merging main brings the RN fix and the TS 7 migration together, which is the first tree where all eight Clients jobs can pass.
The npm-major bump in
688ce589b(PR #1555) moved TypeScript 5.9 → 7 across the client packages and left sixClientsjobs red. #1567 already fixedPortal-next. This fixes four of the remaining five. The two react-native jobs need a scope call — see the bottom.Verified locally by running each job's own commands (
npm cifrom the committed lock, then typecheck / test / build).The TS 7 change behind three of them
TypeScript 7 no longer auto-includes every package under
node_modules/@typesthe way 5.x did. Reproduced in a pristine project — same tsconfig, same@types/node@26.2.0, only the compiler version varying:import { readFileSync } from "node:fs"+process.cwd()TS2591onnode:fs,node:path,process@meshweaver/reactTS2591— the tests that read the server catalogs off disk (i18n/localize,render/parity,ciTrigger) lostnode:fs/node:path/process"types": ["node"]@meshweaver/client-webTS2591in the wire/rest contract guards, plusTS7006implicit-any that follows from an untypedreadFileSync"types": ["node"]Portal exampleTS5102: Option 'baseUrl' has been removedbaseUrl; thepathsentries are already relative to the tsconfig, which is exactly whatbaseUrl "."resolved them against — the same one-liner portal-next took in #1567"types": ["node"]is the fix the compiler's own diagnostic prescribes. Nothing is silenced — the files stay fully checked, and naming one package is stricter than 5.x's include-everything.@meshweaver/client— a real API migrationTS 7 removed
ts.transpileModule. The package's"."export is nowlib/version.cjs(a version string); the API moved totypescript/unstable/*, which isProject/Program/Checkershaped with no single-string transform anywhere in it (grep -r transpileover the package: zero hits). So this is not a config problem and there is no in-package replacement.The node kernel's type-stripper now uses
amaro— the SWC-based stripper Node itself bundles for--experimental-strip-types.mode: "transform", not"strip", is deliberate: strip-only rejects the TS constructs that carry runtime semantics, whichtranspileModuleused to compile. Node's ownmodule.stripTypeScriptTypesis strip-only, which is why it is not enough. Verified through the realexecuteCode:const answer: number = 6*7enum Color { Red, Green } Color.Greennamespace N { export const v = 42 } N.vclass C { constructor(private readonly x: number){} … }typescriptdrops to a devDependency (it is onlytscnow), so the worker no longer ships the whole compiler.amarodeclaresengines: node>=22, hence theenginesfield and that job moving to node 22 — Node 20 went end-of-life 2026-04-30.That job also gains
npm test: it previously ran typecheck only, so the js/ts execution core this PR changes had no CI coverage at all.Nothing was suppressed
No
ignoreBuildErrors, no newskipLibCheck, no@ts-nocheck/@ts-ignore/@ts-expect-error, no--legacy-peer-deps/--force, no narrowedinclude, no deleted or skipped test. Net change to how much is checked: more (one added test step).Local verification
@meshweaver/react@meshweaver/client-web@meshweaver/clientnpm ci→ typecheck clean · 5/5 testsPortal examplenpm ci→tsc --noEmitclean · vite build cleanThe i18n catalog drift guard (15 tests) and the
ciTriggerworkflow guard (4 tests) both pass — the latter re-run after theclients.ymledit.NOT fixed here — RN connector / RN web need a scope call
Dependabot bumped expo 52→57 and react 18→19 but pinned
react-nativeat 0.76.5, whose peers arereact: ^18.2.0/@types/react: ^18.2.6. That set is genuinely unresolvable — thenpm ciERESOLVEis the drift guard working correctly, not a packaging nit.Three candidate fixes, all rejected on evidence:
--legacy-peer-deps/ anoverridesentry — installs a combination upstream does not support (RN 0.76.5 predates React 19), and the RN web job actually renders through Playwright. Cannot be proven, so it cannot be forced.@types/reactback to 18 for RN — tested: RN typechecks../react/src, which resolves React 19 types fromclients/react/node_modules, so two React type identities collide →TS2786at every JSX call site.clients/reactto React 18 as well — would re-breakPortal-next, which is green on React 19.The coherent direction is forward:
react-native@0.82peers are exactlyreact: ^19.1.1/@types/react: ^19.1.1— i.e. the Expo SDK 52→57 upgrade, six RN minors, which also has to deal withexpo-av. The repo already flagged this inclients/react-native/package.json: "the real fix is the Expo SDK upgrade … which is its own migration." Tracked separately rather than half-done here.These two jobs do not gate image publication:
main-cd.ymltriggers on MeshWeaver Build and Test, and its image legs buildgrpc-web+portal-nextonly — neverreact-native. (The #1568 stranding was the portal-next leg, fixed in #1567.)No What's New entry: CI/build plumbing, and no workflow publishes these npm packages.
🤖 Generated with Claude Code