Skip to content

fix(cloudflare/workers): enable strictExecutionOrder to prevent cross-chunk TDZ at startup - #940

Merged
john-royal merged 3 commits into
mainfrom
fix/worker-bundle-strict-execution-order
Jul 24, 2026
Merged

john-royal merged 3 commits into
mainfrom
fix/worker-bundle-strict-execution-order

Conversation

@john-royal

@john-royal john-royal commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Closes #749

Rolldown's chunking can split top-level initializer modules (e.g. Drizzle pgTable schemas) into a chunk that forms a cycle with the entry chunk (worker.js -> auth-*.js -> worker.js when drizzle-orm stays in the entry). ESM evaluation then runs the schema chunk before drizzle's class bindings initialize, and the script fails Cloudflare startup validation:

ScriptStartupError: Uncaught ReferenceError: Cannot access 'a' before initialization
  at auth-BFaahPAe.js:1:110

The fix enables rolldown's strictExecutionOrder on WorkerBundle's output options, which wraps cross-chunk modules so evaluation follows ESM semantics regardless of chunk boundaries:

 const outputOptions: rolldown.OutputOptions = {
   format: "esm",
   sourcemap: "hidden",
   minify: true,
   keepNames: true,
+  strictExecutionOrder: true,
   dir: `.alchemy/bundles/${options.id}`,
 };

Lambda's codeSplitting: false approach is not safe for Workers: Worker bundles rely on chunks to keep dynamically-imported Node/Bun-only modules from evaluating eagerly in workerd. strictExecutionOrder keeps the chunks and fixes only the evaluation order. This makes the user-side advancedChunks grouping workaround from the issue unnecessary.

WorkerProps.build now also forwards rolldown output overrides (merged over WorkerBundle's defaults) plus preserveEntrySignatures — the issue's other ask, chunking was previously uncontrollable per Worker:

Cloudflare.Worker("Api", {
  main,
  build: {
    preserveEntrySignatures: "allow-extension",
    output: {
      codeSplitting: {
        groups: [{ name: "auth", test: "packages/(schema|auth)/", includeDependenciesRecursively: false }],
      },
    },
  },
});

Group test patterns are strings (compiled to regexes by rolldown) — RegExp literals don't survive props serialization.

Repro / tests

test/Cloudflare/Workers/DrizzleSchemaChunks.test.ts + fixtures/drizzle-schema-chunks/ mirror the reporter's monorepo shape (db schema/* + auth package tables with top-level pgTable(...) cross-imports). The test deploys a stack whose Cloudflare.Worker forces the cyclic chunk layout via build options. Cloudflare's startup validation runs on exactly those chunks at upload, so the deploy is itself the regression assertion; the test also verifies the cyclic layout on disk (auth-*.js importing ./worker.js back) and fetches the worker.

The cycle is essential: an acyclic split (e.g. drizzle in its own chunk imported by the schema chunk) evaluates correctly under plain import order and never triggers the bug. Verified live that flipping strictExecutionOrder: false on the stack restores the exact ScriptStartupError above.

Passes live, plus WorkerBundle.test.ts (CJS-require conversion + deploy integration) and RandomEnvLocal.test.ts (local workerd path) are green with the new default.

…-chunk TDZ at startup

Rolldown's default chunking can split top-level initializer modules (e.g.
Drizzle pgTable schemas) away from the classes they read, so workerd
evaluates a reader before its imported binding is initialized and the
script fails Cloudflare startup validation with ScriptStartupError.
strictExecutionOrder wraps cross-chunk modules so evaluation follows ESM
semantics regardless of chunk boundaries.

Closes #749

Co-authored-by: Cursor <cursoragent@cursor.com>
@alchemy-version-bot

alchemy-version-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Install the packages built from this commit:

alchemy

bun add alchemy@https://pkg.ing/alchemy/40bf479

@alchemy.run/better-auth

bun add @alchemy.run/better-auth@https://pkg.ing/@alchemy.run/better-auth/40bf479

@alchemy.run/pr-package

bun add @alchemy.run/pr-package@https://pkg.ing/@alchemy.run/pr-package/40bf479

…build props

`build.output` merges over WorkerBundle's default output options so chunking
(`codeSplitting`) is controllable per Worker. The #749 regression test now
deploys a conventional stack that forces the bad schema/drizzle chunk split
through the real Worker build path and asserts it serves.

Co-authored-by: Cursor <cursoragent@cursor.com>
@john-royal
john-royal marked this pull request as ready for review July 24, 2026 16:57
…est actually regresses

The previous two-group split was acyclic (worker -> auth -> drizzle), which
plain ESM import order evaluates correctly — the deploy passed even with
strictExecutionOrder disabled. The bug needs the cyclic layout from the
issue (drizzle left in the entry chunk, worker.js <-> auth-*.js), which
requires includeDependenciesRecursively: false and therefore
preserveEntrySignatures, now exposed on Worker build props. With the cycle
in place, strictExecutionOrder: false restores the exact
ScriptStartupError from the issue.

Co-authored-by: Cursor <cursoragent@cursor.com>
@john-royal
john-royal merged commit 306d15e into main Jul 24, 2026
9 checks passed
@john-royal
john-royal deleted the fix/worker-bundle-strict-execution-order branch July 24, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cloudflare Worker ScriptStartupError after Alchemy/Rolldown bundling

1 participant