Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/calm-world-singleton.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'workflow': patch
'@workflow/core': patch
---

Reuse the runtime World singleton when the workflow entrypoint initializes its queue handler, avoiding duplicate resources for stateful async World factories.
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ type WorldHandlers = Pick<World, "createQueueHandler" | "specVersion">;
```

<Callout type="warn">
This is SDK infrastructure used by framework adapters and the workflow entrypoint. Application code should use [`getWorld()`](/docs/api-reference/workflow-runtime/get-world) instead.
This is SDK infrastructure used by framework adapters at build time. Runtime routes and application code should use [`getWorld()`](/docs/api-reference/workflow-runtime/get-world) instead.
</Callout>

## Related Functions

- [`getWorld()`](/docs/api-reference/workflow-runtime/get-world) - Resolve the full World instance at runtime.
- [`workflowEntrypoint()`](/docs/api-reference/workflow-runtime/workflow-entrypoint) - The route handler factory built on these handlers.
- [`workflowEntrypoint()`](/docs/api-reference/workflow-runtime/workflow-entrypoint) - Create the runtime route handler that shares the full World instance.
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ Returns a fetch-style request handler: `(req: Request) => Promise<Response>`.

## Related Functions

- [`getWorldHandlers()`](/docs/api-reference/workflow-runtime/get-world-handlers) - The build-time World access this handler is built on.
- [`getWorld()`](/docs/api-reference/workflow-runtime/get-world) - Resolve the runtime World instance this handler shares with workflow execution.
- [`getWorldHandlers()`](/docs/api-reference/workflow-runtime/get-world-handlers) - Access build-time-safe World handlers for framework tooling.
- [`healthCheck()`](/docs/api-reference/workflow-runtime/health-check) - Verify the entrypoint processes queue messages end-to-end.
40 changes: 40 additions & 0 deletions packages/core/src/runtime-world-singleton.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { SPEC_VERSION_CURRENT, type World } from '@workflow/world';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { getWorld, setWorld } from './runtime/world.js';
import { workflowEntrypoint } from './runtime.js';

const createLocalWorld = vi.hoisted(() => vi.fn());

vi.mock('@workflow/world-local', () => ({
createWorld: createLocalWorld,
}));

describe('workflowEntrypoint world initialization', () => {
const world = {
specVersion: SPEC_VERSION_CURRENT,
createQueueHandler: vi.fn(
() => async () => new Response(null, { status: 204 })
),
} as unknown as World;

beforeEach(() => {
setWorld(undefined);
createLocalWorld.mockReset();
createLocalWorld.mockResolvedValue(world);
});

afterEach(() => {
setWorld(undefined);
vi.clearAllMocks();
});

it('reuses the runtime World after initializing the route handler', async () => {
const handler = workflowEntrypoint('');

const response = await handler(new Request('https://example.test'));

expect(response.status).toBe(204);
await expect(getWorld()).resolves.toBe(world);
expect(createLocalWorld).toHaveBeenCalledTimes(1);
});
});
8 changes: 2 additions & 6 deletions packages/core/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,7 @@ import { runStepSingleFlight } from './runtime/step-single-flight.js';
import { handleSuspension } from './runtime/suspension-handler.js';
import { useQuickJSVm } from './runtime/vm-mode.js';
import { getWaitContinuationDispatch } from './runtime/wait-continuation.js';
import {
getWorld,
getWorldHandlers,
type WorldHandlers,
} from './runtime/world.js';
import { getWorld, type WorldHandlers } from './runtime/world.js';
import { dehydrateRunError } from './serialization.js';
import { remapErrorStack } from './source-map.js';
import * as Attribute from './telemetry/semantic-conventions.js';
Expand Down Expand Up @@ -4504,7 +4500,7 @@ export function workflowEntrypoint(
cachedHandler = await trace('workflow.route.init', async () => {
const worldHandlers = await trace(
'workflow.route.get_world_handlers',
async () => getWorldHandlers()
async () => getWorld()
);
return handler(worldHandlers);
});
Expand Down