|
| 1 | +/** |
| 2 | + * Server-only side-effect module that ensures `world.ts` is loaded so its |
| 3 | + * module-load side effect — `globalThis[GetWorldFnKey] ??= getWorld` — |
| 4 | + * fires in host bundles. |
| 5 | + * |
| 6 | + * # Why this exists |
| 7 | + * |
| 8 | + * `getWorldLazy()` in `./get-world-lazy.ts` checks the globalThis cache |
| 9 | + * (populated by `world.ts`'s module-load side effect) before falling back |
| 10 | + * to a runtime-built dynamic `import('./world.js')`. When a server route |
| 11 | + * only consumes a single helper that goes through `getWorldLazy` — most |
| 12 | + * commonly `start` from `workflow/api` — webpack/turbopack tree-shake the |
| 13 | + * named import `{ getWorld } from './runtime/world.js'` out of |
| 14 | + * `runtime.ts`, taking `world.ts`'s module evaluation with it. The |
| 15 | + * globalThis registration never fires. |
| 16 | + * |
| 17 | + * `getWorldLazy` then falls through to its dynamic-import fallback, which |
| 18 | + * itself fails: webpack inlines `get-world-lazy.js` into the bundled route |
| 19 | + * file, so the relative specifier `./world.js` resolves against |
| 20 | + * `/var/task/<app>/.next/server/app/<...>/route.js` — where no sibling |
| 21 | + * `world.js` exists — and Node throws `MODULE_NOT_FOUND`. The symptom is a |
| 22 | + * cold-start regression: the very first user request that goes through |
| 23 | + * `start()` fails until some other code path (typically the queue-driven |
| 24 | + * `/.well-known/workflow/v1/flow` route, which uses `getWorld` directly |
| 25 | + * via `workflowEntrypoint`) has loaded `world.ts` and populated the |
| 26 | + * cache. |
| 27 | + * |
| 28 | + * Importing this module for its side effect — exactly once, from the |
| 29 | + * host-side `workflow/api` (`packages/workflow/src/api.ts`) — guarantees |
| 30 | + * `world.ts` enters the bundle, the global is registered at module load, |
| 31 | + * and `getWorldLazy()` short-circuits to the registered function on the |
| 32 | + * first call. |
| 33 | + * |
| 34 | + * # Why a separate module instead of importing `./world.js` directly |
| 35 | + * |
| 36 | + * `world.ts` is internal to `@workflow/core` and not part of the public |
| 37 | + * exports surface. Adding a dedicated public init entry (this file) |
| 38 | + * keeps the side-effect intent obvious to anyone reading |
| 39 | + * `packages/workflow/src/api.ts`, and lets the `workflow` export |
| 40 | + * condition route to a stub for VM/step bundles (see below). |
| 41 | + * |
| 42 | + * # Why this doesn't break VM/step bundles |
| 43 | + * |
| 44 | + * The `@workflow/core/runtime/world-init` export resolves via the |
| 45 | + * `workflow` condition to `./dist/workflow/world-init-stub.js`, an empty |
| 46 | + * module. Esbuild runs the workflow VM and step bundlers with the |
| 47 | + * `workflow` condition active, so they pick up the stub and never reach |
| 48 | + * `world.ts`. Host bundlers (webpack, turbopack, Node.js) use the |
| 49 | + * `default` (or `node`) condition and pick up this file, loading |
| 50 | + * `world.ts` as intended. The split keeps `@workflow/world-vercel`, |
| 51 | + * `@workflow/world-local`, `cbor-x`, and other server-only deps out of |
| 52 | + * the workflow sandbox bundle. |
| 53 | + * |
| 54 | + * # Why we keep the `getWorldLazy` dynamic-import fallback |
| 55 | + * |
| 56 | + * The fallback remains in `./get-world-lazy.ts` as a defense-in-depth for |
| 57 | + * environments we haven't accounted for (CJS test runners, scripts that |
| 58 | + * import `start` without going through `workflow/api`, future bundlers |
| 59 | + * with stricter tree-shaking). With this init module in place, the |
| 60 | + * fallback should never fire in normal use — but if it does, the |
| 61 | + * dynamic-import branch still resolves correctly when `world.js` is |
| 62 | + * physically adjacent on disk (e.g., direct Node.js execution of |
| 63 | + * unbundled source). |
| 64 | + * |
| 65 | + * # Maintenance notes |
| 66 | + * |
| 67 | + * If you add another `getWorldLazy()` consumer that's reachable from a |
| 68 | + * host route without going through `workflow/api` (e.g., a new helper in |
| 69 | + * `workflow/runtime`), make sure that entry also imports this module — |
| 70 | + * or that it transitively reaches `world.ts` via a non-tree-shakeable |
| 71 | + * path. Adding a regression test in `world-init.test.ts` is preferred to |
| 72 | + * relying on careful manual tracing. |
| 73 | + */ |
| 74 | +import './world.js'; |
0 commit comments