Repository navigation
fix: let browser bundlers leave undici out - #149
Merged
Merged
Conversation
The SDK could not be bundled for a browser. The default transport loads undici with a dynamic import() so it never runs outside Node, but bundlers still follow the import and pull in undici, whose node: built-ins fail a browser build: rspack stops with 'Reading from "node:assert" is not handled by plugins', esbuild with 'Could not resolve "node:assert"'. package.json#browser now maps undici to false, which bundlers read as an empty module in a browser build. In a browser the SDK already uses the global fetch, so nothing calls it. A test bundles src/index.ts for the browser with esbuild, added as a dev dependency, and fails on the build's errors or if undici's files reach the bundle.
doistbot
reviewed
Nov 14, 2026
doistbot
left a comment
Member
There was a problem hiding this comment.
The PR lets browser bundlers leave undici out by mapping it to an empty module in package.json#browser, with a new esbuild-based regression test proving undici's files stay out of a browser bundle, plus a README note on browser usage. The approach is clean and well-evidenced — the test exercises the same specifier-level mapping consumers' bundlers use and fails without the fix.
Few things worth tightening:
- The new
esbuilddev dependency ships an install script that runs during normal developer and CI installs, but it's only needed for this one test. Consider a bundler without a lifecycle install hook to stay within the repo's dependency-security requirements.
I also left one optional follow-up note in the details below.
Optional follow-up note (1)
src/browser-bundle.test.ts:28:
esbuild.build()rejects on build errors (the PR's ownBuild failed with 121 errorsfailure is that rejection), so onceresultis assigned,result.errorsis always[]and this assertion can never fail. Drop it; the rejection already fails the test on build errors, and theundiciInputscheck below is the assertion that actually guards the fix.
esbuild runs an install script on every npm install, which the repository's dependency rules don't allow for a new dependency, and it was only here for the browser-bundle test. rolldown, already installed for Vite, has none, so the test uses it instead, pinned to the version the lockfile already resolves. rolldown doesn't fail a build on an import it can't resolve for a browser; it logs UNRESOLVED_IMPORT. The test now collects those and asserts there are none, beside the check that no undici file is bundled. That replaces the assertion on esbuild's errors, which could never fail, since esbuild throws on build errors.
doist-release-bot Bot
added a commit
that referenced
this pull request
Nov 14, 2026
## [5.1.1](v5.1.0...v5.1.1) (2026-11-14) ### Bug Fixes * let browser bundlers leave undici out ([#149](#149)) ([1cbf34f](1cbf34f))
Contributor
|
🎉 This PR is included in version 5.1.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
@todoist/comms-sdkcan't be bundled for a browser. Bundling 5.1.0 with rspack (target: 'web') fails with 25 errors likeReading from "node:assert" is not handled by plugins (Unhandled scheme), one for eachnode:module undici imports.The default transport loads undici with a dynamic
import('undici')(src/transport/http-dispatcher.ts) so that it only runs on Node. In a browser it never runs:getDefaultTransport()returnsundefinedand requests use the globalfetch. But bundlers still follow a dynamic import, so they pull undici and its Node built-ins into the build.What was changed
package.json#browsermapsundicitofalse. Bundlers building for a browser replace it with an empty module; Node ignores the field, so nothing changes there.src/browser-bundle.test.tsbundlessrc/index.tsfor the browser with rolldown, reading the package's ownpackage.jsonas a consumer's bundler does. It fails if any import can't be resolved for a browser (rolldown'sUNRESOLVED_IMPORT), or if undici's files end up in the bundle. Rolldown was already installed for Vite and has no install script; it's now a direct dev dependency, pinned to the version the lockfile already resolved.Proof
browserfield removed it fails listing 121UNRESOLVED_IMPORTwarnings, such asCould not resolve 'node:assert' in node_modules/undici/lib/dispatcher/client.js. Rolldown bundled 113 of undici's files in that run. With the field restored it passes.target: 'web'). It failed with theUnhandled schemeerrors. The packed tarball from this branch compiles with no aliases. The output's only mention of undici is the SDK's own list of retryableUND_ERR_*codes infetch-with-retry.ts; none of undici's code is there.vite buildof a page importing the packed tarball succeeds, with undici replaced by Vite's empty browser stand-in.api.workspaces.getWorkspaces()withbaseUrlset to a local stand-in for the API on the same origin. It resolved to[]. The server receivedGET /api/v1.0/workspaces/getwithAuthorization: Bearer test-token.npm run verifypasses.Not verified:
browserfield, but I only ran rspack, Vite, rolldown and esbuild.getWorkspaces()ran there.