fix(build): keep browser-reachable modules off node-only imports so next build --turbopack succeeds again - #13704
Conversation
…ervice diegosouzapw#13283 made src/lib/combos/controlCenter.ts — imported by the "use client" ComboControlCenterClient page — import resolveProviderAlias() from open-sse/services/model.ts. That module lazily imports the DB layer (@/lib/db/readCache, @/lib/db/models, …); Turbopack follows those edges into the client bundle, drags the executors (down to the Playwright-backed zai-web browser automation) along, and `next build --turbopack` fails with 168 "Module not found: Can't resolve 'child_process' / 'async_hooks'" errors. Every Docker image build since the 2026-09-11 batch fails the same way. Move ALIAS_TO_PROVIDER_ID and resolveProviderAlias() into a pure open-sse/services/providerAlias.ts that depends only on the static provider registry, re-export both from model.ts so the existing server-side importers are untouched, and point controlCenter.ts at the pure module. Verified with an import-graph walk over src/ + open-sse/: at the last commit that published an image no "use client"/edge entry reached a playwright importer, at the branch tip exactly one did (this chain), and none do after this change.
…nts module diegosouzapw#13264 made open-sse/config/providers/registry/codebuddy-cn/index.ts import CODEBUDDY_CN_USER_AGENT from src/lib/oauth/constants/oauth.ts. The provider registry is browser-reachable (dashboard model pickers → src/shared/constants/models.ts → open-sse/config/providerModels.ts → providerRegistry → every registry entry), and oauth.ts imports open-sse/utils/cursorAgentCliVersion.ts, which imports node:fs / node:os / node:path. With the Playwright edge from diegosouzapw#13283 fixed, `next build --turbopack` then fails one step later on every dashboard page: TurbopackInternalError: Failed to write app endpoint /(dashboard)/dashboard/page Caused by: the chunking context (unknown) does not support external modules (request: node:fs) Define the constant in a dependency-free open-sse/config/providers/registry/codebuddy-cn/userAgent.ts, import it there from the registry entry, and have oauth.ts import + re-export it so the OAuth config, chat headers and usage/quota client keep sharing the one string that diegosouzapw#12702 requires. Verified with an import-graph walk (client/edge entries → files importing a Node built-in): the set of reachable chains is now identical to the last release-branch commit that published a Docker image.
|
Thanks for this — confirmed the fix: your There are a few other open PRs attacking the same two import chains (#13436, #13604, #13635, |
…nerically The guard test from diegosouzapw#10692 only knew a hand-written list of server-only modules and ignored dynamic `import()`, so neither of the two regressions fixed in this PR (diegosouzapw#13264: registry entry → oauth constants → node:fs; diegosouzapw#13283: combo control center → model.ts → lazy DB import → child_process) could fail PR CI — they only surfaced in the next Docker build. - Treat any first-party module that imports a non-polyfillable Node builtin (fs, net, tls, child_process, async_hooks, worker_threads, …) as server-only, in addition to the explicit list. Polyfilled builtins (path, crypto, buffer, …) are deliberately not listed. - Follow dynamic `import("…")` edges: the bundler still has to build the lazy chunk, so a builtin behind one fails the build exactly like a static import. - Report each offending edge once with an example chain and a count instead of one chain per client entry point. On release/v3.8.51 without the two fixes the test now fails with exactly the two chains above; with them it passes.
|
Thanks — picked up the follow-up in The guard from #10692 could not have caught either chain: it only knew a hand-written
Verified on With the two fixes on this branch it passes (1/1), and eslint/prettier are clean on the file. |
|
Thanks @seanford for tracking down both server-to-client import chains that broke The same fix is already on |
Summary
next build --turbopack— and therefore every Docker image build — has been failing onrelease/v3.8.51since the 2026-09-11 owner batch. Docker Hub confirms it: the lastnext/next-webimages were published 2026-09-11 18:16 UTC, and nothing has been published from any of the ~150 commits since. Two commits in that batch each introduced a server → client import edge, and Turbopack fails on the first one it meets, which is why the second was hidden until the first was fixed:src/lib/combos/controlCenter.ts— imported by the"use client"ComboControlCenterClientpage — importresolveProviderAlias()fromopen-sse/services/model.ts. That module lazily imports the DB layer (@/lib/db/readCache,@/lib/db/models, …); Turbopack follows thoseawait import()edges into the browser bundle, drags the executors (down to the Playwright-backedzai-webbrowser automation) along, and the build dies with 168 ×Module not found: Can't resolve 'child_process'/'async_hooks'fromplaywright-coreanddetect-libc.open-sse/config/providers/registry/codebuddy-cn/index.tsimportCODEBUDDY_CN_USER_AGENTfromsrc/lib/oauth/constants/oauth.ts, which importsopen-sse/utils/cursorAgentCliVersion.ts(node:fs/node:os/node:path). With (1) fixed, the build fails one step later on every dashboard page:TurbopackInternalError: Failed to write app endpoint /(dashboard)/dashboard/page — the chunking context (unknown) does not support external modules (request: node:fs).Changes
open-sse/services/providerAlias.ts(new, pure):ALIAS_TO_PROVIDER_ID+resolveProviderAlias(), depending only on the static provider registry.model.tsimports and re-exports both, so its 8 existing server-side importers are untouched;controlCenter.tsimports the pure module.open-sse/config/providers/registry/codebuddy-cn/userAgent.ts(new, dependency-free):CODEBUDDY_CN_USER_AGENT. The registry entry imports it directly;src/lib/oauth/constants/oauth.tsimports and re-exports it, so OAuth, chat headers and the usage/quota client keep sharing the single string that [BUG] deepseek harness + codebuddy cn #12702 requires.changelog.d/fixes/fragments (placeholder0000— I'll rename to this PR's number once assigned).No behaviour change at runtime; this is purely which module the constants live in.
Validation
src/+open-sse/(client/edge entries → files importingplaywrightor a Node built-in): at the last commit that published an image (af49d4972) no"use client"/edge entry reached either; at the branch tip exactly these two chains did; after this PR the reachable set is identical toaf49d4972again.Dockerfiletargetrunner-web(the same builddocker-publish.ymlruns) on a clean host: fails on the tip with the errors above, succeeds with this PR (Image … Built).npm run typecheck:coreclean;eslint --suppressions-location config/quality/eslint-suppressions.jsonandprettier --checkclean on all touched files.node --import tsx/esm --teston the 19 unit files that exerciseresolveProviderAlias,combos/controlCenter,services/model,codebuddy-cnandconstants/oauth: 191 tests, all passing.release/v3.8.51@c0f92ec98.Reviewer notes
release/v3.8.51, including my three follow-up PRs on/v1/rerankprovider nodes.🤖 Generated with Claude Code
https://claude.ai/code/session_01KHaWGXax8LT4TxyGopEVxX