Symptom (2.0.0-beta.55)
A StaticSite whose main is an already-complete vite server bundle (React Router RSC app, effect-based request handling) deploys fine, but every request on the deployed Worker dies with:
TypeError: _(...).then is not a function
at MixedScheduler.<anonymous> (Schema-<hash>.js:14:4727)
The same code is healthy under vite dev and in the vite build itself — the failure is introduced by the Worker upload re-bundle. Setting build: { pure: false } fixes it deterministically.
Cause (corrected)
My first read of this was that the pure plugin was matching the effect/@effect/* packages on the pre-bundled input. That is not what happens, and it's worth correcting because it points at the real fix.
purePlugin matches by package name resolved from the module's path, not by anything in the code: resolvePackageInfo walks up to the nearest package.json and packageNameFromId reads the node_modules/<pkg> segment (PurePlugin.ts ~L120-121, L270-282, L294-366). In a vite/rolldown output (build/server/index.js + build/server/assets/*.js) the effect package boundaries no longer exist — walking up from any chunk lands on the app's own package.json, not effect. So the effect/@effect/* patterns never fire on this input.
What actually fires is autoDetectEntryPackage (default true, PurePlugin.ts L72/L91/L107-116): it detects the entry's owning package and adds it to the match set. The app package declares "sideEffects": false, so:
- every chunk under
build/server/ resolves to that one owning package and matches, and
- because
sideEffects is false, the plugin applies both /*#__PURE__*/ annotations and moduleSideEffects: false (PurePlugin.ts L133-145) across the whole pre-bundled output.
Marking an already-bundled vite artifact side-effect-free / pure lets rolldown drop top-level initializers the effect scheduler dereferences at request time — hence the per-request TypeError. build: { pure: false } disables the whole plugin, which is why it's a clean fix.
So the trigger is autoDetectEntryPackage + the app package's sideEffects: false, applied to a pre-bundled multi-chunk input — not effect-package matching.
Suggestions
- Behavior: when the bundle entry resolves into a build-output directory (chunk-shaped input / existing sourcemap),
autoDetectEntryPackage shouldn't auto-annotate it — the owning package.json's sideEffects: false describes the source tree, not a foreign bundler's already-emitted output. Defaulting auto-detect off for that input class (or for StaticSite/main-points-at-artifact specifically) would avoid the foot-gun.
- Docs: at minimum, document the failure mode on
BundleExtraOptions.pure / autoDetectEntryPackage — the current jsdoc describes the mechanism but not that feeding a pre-bundled input through it can drop runtime-needed initializers and 500 every request.
(For context: the idiomatic path for a vite app is Cloudflare.Vite, which bundles source itself and never re-bundles a foreign artifact — so this only bites when main is pointed at a pre-built bundle. The report stands as a sharp-edge in that configuration plus a docs gap.)
Happy to PR whichever direction you prefer (same fork as #586/#587).
Symptom (2.0.0-beta.55)
A
StaticSitewhosemainis an already-complete vite server bundle (React Router RSC app, effect-based request handling) deploys fine, but every request on the deployed Worker dies with:The same code is healthy under
vite devand in the vite build itself — the failure is introduced by the Worker upload re-bundle. Settingbuild: { pure: false }fixes it deterministically.Cause (corrected)
My first read of this was that the pure plugin was matching the
effect/@effect/*packages on the pre-bundled input. That is not what happens, and it's worth correcting because it points at the real fix.purePluginmatches by package name resolved from the module's path, not by anything in the code:resolvePackageInfowalks up to the nearestpackage.jsonandpackageNameFromIdreads thenode_modules/<pkg>segment (PurePlugin.ts~L120-121, L270-282, L294-366). In a vite/rolldown output (build/server/index.js+build/server/assets/*.js) the effect package boundaries no longer exist — walking up from any chunk lands on the app's ownpackage.json, noteffect. So theeffect/@effect/*patterns never fire on this input.What actually fires is
autoDetectEntryPackage(defaulttrue,PurePlugin.tsL72/L91/L107-116): it detects the entry's owning package and adds it to the match set. The app package declares"sideEffects": false, so:build/server/resolves to that one owning package and matches, andsideEffectsisfalse, the plugin applies both/*#__PURE__*/annotations andmoduleSideEffects: false(PurePlugin.tsL133-145) across the whole pre-bundled output.Marking an already-bundled vite artifact side-effect-free / pure lets rolldown drop top-level initializers the effect scheduler dereferences at request time — hence the per-request
TypeError.build: { pure: false }disables the whole plugin, which is why it's a clean fix.So the trigger is autoDetectEntryPackage + the app package's
sideEffects: false, applied to a pre-bundled multi-chunk input — not effect-package matching.Suggestions
autoDetectEntryPackageshouldn't auto-annotate it — the owningpackage.json'ssideEffects: falsedescribes the source tree, not a foreign bundler's already-emitted output. Defaulting auto-detect off for that input class (or forStaticSite/main-points-at-artifact specifically) would avoid the foot-gun.BundleExtraOptions.pure/autoDetectEntryPackage— the current jsdoc describes the mechanism but not that feeding a pre-bundled input through it can drop runtime-needed initializers and 500 every request.(For context: the idiomatic path for a vite app is
Cloudflare.Vite, which bundles source itself and never re-bundles a foreign artifact — so this only bites whenmainis pointed at a pre-built bundle. The report stands as a sharp-edge in that configuration plus a docs gap.)Happy to PR whichever direction you prefer (same fork as #586/#587).