From 78e3917c23255a330f20d4badbc72e40bfae663e Mon Sep 17 00:00:00 2001 From: Peter Wielander Date: Wed, 26 Nov 2025 11:09:39 -0800 Subject: [PATCH 1/9] [world-local] Finish renaming embedded world to local world --- packages/cli/CHANGELOG.md | 2 +- packages/cli/src/lib/inspect/env.ts | 12 ++++++------ packages/cli/src/lib/inspect/flags.ts | 6 +++--- packages/cli/src/lib/inspect/setup.ts | 6 +++--- packages/cli/src/lib/inspect/web.ts | 2 +- packages/core/package.json | 2 +- packages/core/src/runtime/world.ts | 12 ++++++------ packages/core/src/workflow.ts | 4 ++-- packages/next/src/index.ts | 8 ++++---- .../components/display-utils/connection-status.tsx | 8 ++++---- packages/web/src/components/settings-sidebar.tsx | 12 ++++++------ packages/web/src/lib/config-world.ts | 4 ++-- packages/web/src/lib/config.ts | 4 ++-- packages/world-local/CHANGELOG.md | 6 +++--- packages/world-local/src/config.ts | 6 +++--- packages/world-local/src/index.ts | 4 ++-- packages/world-local/src/queue.ts | 8 ++++---- packages/world-local/src/storage.test.ts | 6 +++--- packages/world-local/src/storage.ts | 10 +++++----- packages/world-postgres/HOW_IT_WORKS.md | 3 +-- packages/world-postgres/src/queue.ts | 12 ++++++------ packages/world-testing/test/embedded.test.ts | 2 +- 22 files changed, 69 insertions(+), 70 deletions(-) diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 16a9073fc..b4f4d62d5 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -266,7 +266,7 @@ ### Patch Changes - 66332f2: Rename vercel-static builder to standalone -- dbf2207: Fix --backend flag not finding world when using embedded world package name explicitly +- dbf2207: Fix --backend flag not finding world when using local world package name explicitly - Updated dependencies [dbf2207] - Updated dependencies [eadf588] - @workflow/web@4.0.1-beta.4 diff --git a/packages/cli/src/lib/inspect/env.ts b/packages/cli/src/lib/inspect/env.ts index fccb83b90..89fb80c44 100644 --- a/packages/cli/src/lib/inspect/env.ts +++ b/packages/cli/src/lib/inspect/env.ts @@ -43,7 +43,7 @@ export const getEnvVars = (): Record => { WORKFLOW_VERCEL_PROXY_URL: env.WORKFLOW_VERCEL_PROXY_URL || '', WORKFLOW_LOCAL_UI: env.WORKFLOW_LOCAL_UI || '', PORT: env.PORT || '', - WORKFLOW_EMBEDDED_DATA_DIR: env.WORKFLOW_EMBEDDED_DATA_DIR || '', + WORKFLOW_LOCAL_DATA_DIR: env.WORKFLOW_LOCAL_DATA_DIR || '', }; }; @@ -75,10 +75,10 @@ async function findWorkflowDataDir(cwd: string) { } /** - * Overwrites process.env variables related to embedded world configuration, + * Overwrites process.env variables related to local world configuration, * if relevant environment variables aren't set already. */ -export const inferEmbeddedWorldEnvVars = async () => { +export const inferLocalWorldEnvVars = async () => { const envVars = getEnvVars(); if (!envVars.PORT) { logger.warn( @@ -89,12 +89,12 @@ export const inferEmbeddedWorldEnvVars = async () => { } // Paths to check, in order of preference - if (!envVars.WORKFLOW_EMBEDDED_DATA_DIR) { + if (!envVars.WORKFLOW_LOCAL_DATA_DIR) { const cwd = getWorkflowConfig().workingDir; const localPath = await findWorkflowDataDir(cwd); if (localPath) { - envVars.WORKFLOW_EMBEDDED_DATA_DIR = localPath; + envVars.WORKFLOW_LOCAL_DATA_DIR = localPath; writeEnvVars(envVars); return; } @@ -104,7 +104,7 @@ export const inferEmbeddedWorldEnvVars = async () => { if (repoRoot) { const repoPath = await findWorkflowDataDir(repoRoot); if (repoPath) { - envVars.WORKFLOW_EMBEDDED_DATA_DIR = repoPath; + envVars.WORKFLOW_LOCAL_DATA_DIR = repoPath; writeEnvVars(envVars); return; } diff --git a/packages/cli/src/lib/inspect/flags.ts b/packages/cli/src/lib/inspect/flags.ts index ec9289d44..0d84d28ac 100644 --- a/packages/cli/src/lib/inspect/flags.ts +++ b/packages/cli/src/lib/inspect/flags.ts @@ -30,12 +30,12 @@ export const cliFlags = { description: 'backend to inspect', required: false, char: 'b', - default: 'embedded', + default: 'local', env: 'WORKFLOW_TARGET_WORLD', helpGroup: 'Target', helpLabel: '-b, --backend', - helpValue: ['embedded', 'vercel', ''], - defaultHelp: 'embedded', + helpValue: ['local', 'vercel', ''], + defaultHelp: 'local', }), authToken: Flags.string({ description: diff --git a/packages/cli/src/lib/inspect/setup.ts b/packages/cli/src/lib/inspect/setup.ts index b88f6ca04..6db7cf044 100644 --- a/packages/cli/src/lib/inspect/setup.ts +++ b/packages/cli/src/lib/inspect/setup.ts @@ -3,7 +3,7 @@ import chalk from 'chalk'; import terminalLink from 'terminal-link'; import { logger, setJsonMode, setVerboseMode } from '../config/log.js'; import { - inferEmbeddedWorldEnvVars, + inferLocalWorldEnvVars, inferVercelEnvVars, writeEnvVars, } from './env.js'; @@ -52,10 +52,10 @@ export const setupCliWorld = async ( ) { await inferVercelEnvVars(); } else if ( - flags.backend === 'embedded' || + flags.backend === 'local' || flags.backend === '@workflow/world-local' ) { - await inferEmbeddedWorldEnvVars(); + await inferLocalWorldEnvVars(); } logger.debug('Initializing world'); diff --git a/packages/cli/src/lib/inspect/web.ts b/packages/cli/src/lib/inspect/web.ts index 2ffc6a515..037d86c05 100644 --- a/packages/cli/src/lib/inspect/web.ts +++ b/packages/cli/src/lib/inspect/web.ts @@ -369,7 +369,7 @@ function envToQueryParams( WORKFLOW_VERCEL_PROJECT: 'project', WORKFLOW_VERCEL_TEAM: 'team', PORT: 'port', - WORKFLOW_EMBEDDED_DATA_DIR: 'dataDir', + WORKFLOW_LOCAL_DATA_DIR: 'dataDir', }; for (const [envName, paramName] of Object.entries(envToQueryParamMappings)) { diff --git a/packages/core/package.json b/packages/core/package.json index d05d3217f..da00c9a8c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -41,7 +41,7 @@ "build": "tsc", "dev": "tsc --watch", "clean": "tsc --build --clean && rm -r dist ||:", - "test": "cross-env WORKFLOW_TARGET_WORLD=embedded vitest run src", + "test": "cross-env WORKFLOW_TARGET_WORLD=local vitest run src", "test:e2e": "vitest run e2e", "typecheck": "tsc --noEmit" }, diff --git a/packages/core/src/runtime/world.ts b/packages/core/src/runtime/world.ts index 732a1aa52..8ea9395d7 100644 --- a/packages/core/src/runtime/world.ts +++ b/packages/core/src/runtime/world.ts @@ -1,7 +1,7 @@ import { createRequire } from 'node:module'; import { join } from 'node:path'; import type { World } from '@workflow/world'; -import { createEmbeddedWorld } from '@workflow/world-local'; +import { createLocalWorld } from '@workflow/world-local'; import { createVercelWorld } from '@workflow/world-vercel'; const require = createRequire(join(process.cwd(), 'index.js')); @@ -14,12 +14,12 @@ const globalSymbols: typeof globalThis & { [StubbedWorldCache]?: World; } = globalThis; -function defaultWorld(): 'vercel' | 'embedded' { +function defaultWorld(): 'vercel' | 'local' { if (process.env.VERCEL_DEPLOYMENT_ID) { return 'vercel'; } - return 'embedded'; + return 'local'; } /** @@ -42,9 +42,9 @@ export const createWorld = (): World => { }); } - if (targetWorld === 'embedded') { - return createEmbeddedWorld({ - dataDir: process.env.WORKFLOW_EMBEDDED_DATA_DIR, + if (targetWorld === 'local') { + return createLocalWorld({ + dataDir: process.env.WORKFLOW_LOCAL_DATA_DIR, }); } diff --git a/packages/core/src/workflow.ts b/packages/core/src/workflow.ts index f27f4453f..542d97a9c 100644 --- a/packages/core/src/workflow.ts +++ b/packages/core/src/workflow.ts @@ -7,6 +7,7 @@ import * as nanoid from 'nanoid'; import { monotonicFactory } from 'ulid'; import { EventConsumerResult, EventsConsumer } from './events-consumer.js'; import { ENOTSUP } from './global.js'; +import { parseWorkflowName } from './parse-name.js'; import type { WorkflowOrchestratorContext } from './private.js'; import { dehydrateWorkflowReturnValue, @@ -28,7 +29,6 @@ import type { WorkflowMetadata } from './workflow/get-workflow-metadata.js'; import { WORKFLOW_CONTEXT_SYMBOL } from './workflow/get-workflow-metadata.js'; import { createCreateHook } from './workflow/hook.js'; import { createSleep } from './workflow/sleep.js'; -import { parseWorkflowName } from './parse-name.js'; export async function runWorkflow( workflowCode: string, @@ -104,7 +104,7 @@ export async function runWorkflow( getWorkflowRunStreamId(workflowRun.runId, namespace); // TODO: there should be a getUrl method on the world interface itself. This - // solution only works for vercel + embedded worlds. + // solution only works for vercel + local worlds. const url = process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : `http://localhost:${port ?? 3000}`; diff --git a/packages/next/src/index.ts b/packages/next/src/index.ts index ab29ddaa6..886b37973 100644 --- a/packages/next/src/index.ts +++ b/packages/next/src/index.ts @@ -13,7 +13,7 @@ export function withWorkflow( workflows, }: { workflows?: { - embedded?: { + local?: { port?: number; dataDir?: string; }; @@ -22,10 +22,10 @@ export function withWorkflow( ) { if (!process.env.VERCEL_DEPLOYMENT_ID) { if (!process.env.WORKFLOW_TARGET_WORLD) { - process.env.WORKFLOW_TARGET_WORLD = 'embedded'; - process.env.WORKFLOW_EMBEDDED_DATA_DIR = '.next/workflow-data'; + process.env.WORKFLOW_TARGET_WORLD = 'local'; + process.env.WORKFLOW_LOCAL_DATA_DIR = '.next/workflow-data'; } - const maybePort = workflows?.embedded?.port; + const maybePort = workflows?.local?.port; if (maybePort) { process.env.PORT = maybePort.toString(); } diff --git a/packages/web/src/components/display-utils/connection-status.tsx b/packages/web/src/components/display-utils/connection-status.tsx index 9fac111bf..868b3100a 100644 --- a/packages/web/src/components/display-utils/connection-status.tsx +++ b/packages/web/src/components/display-utils/connection-status.tsx @@ -25,22 +25,22 @@ const getConnectionInfo = ( return { provider: 'Vercel', parts }; } - if (backend === 'embedded') { - // Embedded backend + if (backend === 'local') { + // Local backend const parts: string[] = []; if (config.dataDir) { parts.push(`dir: ${config.dataDir}`); } if (config.port) parts.push(`port: ${config.port}`); - return { provider: 'Embedded', parts }; + return { provider: 'Local', parts }; } return { provider: config.backend || 'unknown', parts: [] }; }; export function ConnectionStatus({ config }: ConnectionStatusProps) { - const backend = config.backend || 'embedded'; + const backend = config.backend || 'local'; const { provider, parts } = getConnectionInfo(backend, config); return ( diff --git a/packages/web/src/components/settings-sidebar.tsx b/packages/web/src/components/settings-sidebar.tsx index f5affa3ba..69065db67 100644 --- a/packages/web/src/components/settings-sidebar.tsx +++ b/packages/web/src/components/settings-sidebar.tsx @@ -30,8 +30,8 @@ export function SettingsSidebar() { const [errors, setErrors] = useState([]); const [isValidating, setIsValidating] = useState(false); - const backend = localConfig.backend || 'embedded'; - const isEmbedded = backend === 'embedded'; + const backend = localConfig.backend || 'local'; + const isLocal = backend === 'local'; // Update local config when query params change useEffect(() => { @@ -112,7 +112,7 @@ export function SettingsSidebar() {
- {isEmbedded && ( + {isLocal && ( <>
@@ -175,7 +175,7 @@ export function SettingsSidebar() { )} - {!isEmbedded && ( + {!isLocal && ( <>
diff --git a/packages/web/src/lib/config-world.ts b/packages/web/src/lib/config-world.ts index 25a568c23..64a73917e 100644 --- a/packages/web/src/lib/config-world.ts +++ b/packages/web/src/lib/config-world.ts @@ -23,9 +23,9 @@ export async function validateWorldConfig( config: WorldConfig ): Promise { const errors: ValidationError[] = []; - const backend = config.backend || 'embedded'; + const backend = config.backend || 'local'; - if (backend === 'embedded') { + if (backend === 'local') { // Check if data directory exists if (config.dataDir) { const resolvedPath = resolve(config.dataDir); diff --git a/packages/web/src/lib/config.ts b/packages/web/src/lib/config.ts index 6d01d59a5..9aff0a962 100644 --- a/packages/web/src/lib/config.ts +++ b/packages/web/src/lib/config.ts @@ -7,7 +7,7 @@ import type { WorldConfig } from '@/lib/config-world'; // Default configuration const DEFAULT_CONFIG: WorldConfig = { - backend: 'embedded', + backend: 'local', dataDir: './.next/workflow-data', port: '3000', env: 'production', @@ -139,6 +139,6 @@ export const worldConfigToEnvMap = (config: WorldConfig): EnvMap => { WORKFLOW_VERCEL_PROJECT: config.project, WORKFLOW_VERCEL_TEAM: config.team, PORT: config.port, - WORKFLOW_EMBEDDED_DATA_DIR: config.dataDir, + WORKFLOW_LOCAL_DATA_DIR: config.dataDir, }; }; diff --git a/packages/world-local/CHANGELOG.md b/packages/world-local/CHANGELOG.md index 8b962c5af..a67041cbf 100644 --- a/packages/world-local/CHANGELOG.md +++ b/packages/world-local/CHANGELOG.md @@ -32,15 +32,15 @@ ### Major Changes -- aa015af: BREAKING: Change `createEmbeddedWorld` API signature from positional parameters to config object. Add baseUrl configuration support. +- aa015af: BREAKING: Change `createLocalWorld` API signature from positional parameters to config object. Add baseUrl configuration support. **Breaking change:** - - `createEmbeddedWorld(dataDir?, port?)` → `createEmbeddedWorld(args?: Partial)` + - `createLocalWorld(dataDir?, port?)` → `createLocalWorld(args?: Partial)` **New features:** - - Add `baseUrl` config option for HTTPS and custom hostnames (via config or `WORKFLOW_EMBEDDED_BASE_URL` env var) + - Add `baseUrl` config option for HTTPS and custom hostnames (via config or `WORKFLOW_LOCAL_BASE_URL` env var) - Support for port 0 (OS-assigned port) ### Patch Changes diff --git a/packages/world-local/src/config.ts b/packages/world-local/src/config.ts index 732843f1e..07190c40a 100644 --- a/packages/world-local/src/config.ts +++ b/packages/world-local/src/config.ts @@ -2,13 +2,13 @@ import { getPort } from '@workflow/utils/get-port'; import { once } from './util.js'; const getDataDirFromEnv = () => { - return process.env.WORKFLOW_EMBEDDED_DATA_DIR || '.workflow-data'; + return process.env.WORKFLOW_LOCAL_DATA_DIR || '.workflow-data'; }; export const DEFAULT_RESOLVE_DATA_OPTION = 'all'; const getBaseUrlFromEnv = () => { - return process.env.WORKFLOW_EMBEDDED_BASE_URL; + return process.env.WORKFLOW_LOCAL_BASE_URL; }; export type Config = { @@ -26,7 +26,7 @@ export const config = once(() => { /** * Resolves the base URL for queue requests following the priority order: - * 1. config.baseUrl (highest priority - full override from args or WORKFLOW_EMBEDDED_BASE_URL env var) + * 1. config.baseUrl (highest priority - full override from args or WORKFLOW_LOCAL_BASE_URL env var) * 2. config.port (explicit port override from args) * 3. PORT env var (explicit configuration) * 4. Auto-detected port via getPort (detect actual listening port) diff --git a/packages/world-local/src/index.ts b/packages/world-local/src/index.ts index 60564202d..37f4dcd5b 100644 --- a/packages/world-local/src/index.ts +++ b/packages/world-local/src/index.ts @@ -6,14 +6,14 @@ import { createStorage } from './storage.js'; import { createStreamer } from './streamer.js'; /** - * Creates an embedded world instance that combines queue, storage, and streamer functionalities. + * Creates a local world instance that combines queue, storage, and streamer functionalities. * * @param args - Optional configuration object * @param args.dataDir - Directory for storing workflow data (default: `.workflow-data/`) * @param args.port - Port override for queue transport (default: auto-detected) * @param args.baseUrl - Full base URL override for queue transport (default: `http://localhost:{port}`) */ -export function createEmbeddedWorld(args?: Partial): World { +export function createLocalWorld(args?: Partial): World { const definedArgs = args ? Object.fromEntries( Object.entries(args).filter(([, value]) => value !== undefined) diff --git a/packages/world-local/src/queue.ts b/packages/world-local/src/queue.ts index 37cccb47f..5d6a1f9de 100644 --- a/packages/world-local/src/queue.ts +++ b/packages/world-local/src/queue.ts @@ -95,7 +95,7 @@ export function createQueue(config: Partial): Queue { } catch {} } - console.error(`[embedded world] Failed to queue message`, { + console.error(`[local world] Failed to queue message`, { queueName, text, status: response.status, @@ -105,7 +105,7 @@ export function createQueue(config: Partial): Queue { } console.error( - `[embedded world] Reached max retries of embedded world queue implementation` + `[local world] Reached max retries of local world queue implementation` ); })() .catch((err) => { @@ -114,7 +114,7 @@ export function createQueue(config: Partial): Queue { const isAbortError = err?.name === 'AbortError' || err?.name === 'ResponseAborted'; if (!isAbortError) { - console.error('[embedded world] Queue operation failed:', err); + console.error('[local world] Queue operation failed:', err); } }) .finally(() => { @@ -179,7 +179,7 @@ export function createQueue(config: Partial): Queue { }; const getDeploymentId: Queue['getDeploymentId'] = async () => { - return 'dpl_embedded'; + return 'dpl_local'; }; return { queue, createQueueHandler, getDeploymentId }; diff --git a/packages/world-local/src/storage.test.ts b/packages/world-local/src/storage.test.ts index 64fd1f1c5..5a23803a6 100644 --- a/packages/world-local/src/storage.test.ts +++ b/packages/world-local/src/storage.test.ts @@ -1122,9 +1122,9 @@ describe('Storage', () => { expect(hook.runId).toBe(testRunId); expect(hook.hookId).toBe('hook_123'); expect(hook.token).toBe('my-hook-token'); - expect(hook.ownerId).toBe('embedded-owner'); - expect(hook.projectId).toBe('embedded-project'); - expect(hook.environment).toBe('embedded'); + expect(hook.ownerId).toBe('local-owner'); + expect(hook.projectId).toBe('local-project'); + expect(hook.environment).toBe('local'); expect(hook.createdAt).toBeInstanceOf(Date); // Verify file was created diff --git a/packages/world-local/src/storage.ts b/packages/world-local/src/storage.ts index 6d793f5f4..a4faa681f 100644 --- a/packages/world-local/src/storage.ts +++ b/packages/world-local/src/storage.ts @@ -121,7 +121,7 @@ function createHooksStorage(basedir: string): Storage['hooks'] { async function create(runId: string, data: CreateHookRequest): Promise { // Check if a hook with the same token already exists - // Token uniqueness is enforced globally per embedded environment + // Token uniqueness is enforced globally per local environment const existingHook = await findHookByToken(data.token); if (existingHook) { throw new Error( @@ -136,9 +136,9 @@ function createHooksStorage(basedir: string): Storage['hooks'] { hookId: data.hookId, token: data.token, metadata: data.metadata, - ownerId: 'embedded-owner', - projectId: 'embedded-project', - environment: 'embedded', + ownerId: 'local-owner', + projectId: 'local-project', + environment: 'local', createdAt: now, } as Hook; @@ -279,7 +279,7 @@ export function createStorage(basedir: string): Storage { * Note: This operation is not atomic. Concurrent updates from multiple * processes may result in lost updates (last writer wins). This is an * inherent limitation of filesystem-based storage without locking. - * For the local/embedded world, this is acceptable as it's typically + * For the local world, this is acceptable as it's typically * used in single-process scenarios. */ async update(id, data) { diff --git a/packages/world-postgres/HOW_IT_WORKS.md b/packages/world-postgres/HOW_IT_WORKS.md index a75c62dee..29b6b9e4a 100644 --- a/packages/world-postgres/HOW_IT_WORKS.md +++ b/packages/world-postgres/HOW_IT_WORKS.md @@ -33,7 +33,7 @@ Real-time data streaming via **PostgreSQL LISTEN/NOTIFY**: ## Setup -Call `world.start()` to initialize pg-boss workers. When `.start()` is called, workers begin listening to pg-boss queues. When a job arrives, workers make HTTP fetch calls to the embedded world endpoints (`.well-known/workflow/v1/flow` or `.well-known/workflow/v1/step`) to execute the actual workflow logic. +Call `world.start()` to initialize pg-boss workers. When `.start()` is called, workers begin listening to pg-boss queues. When a job arrives, workers make HTTP fetch calls to the local world endpoints (`.well-known/workflow/v1/flow` or `.well-known/workflow/v1/step`) to execute the actual workflow logic. In **Next.js**, the `world.setup()` function needs to be added to `instrumentation.ts|js` to ensure workers start before request handling: @@ -47,4 +47,3 @@ if (process.env.NEXT_RUNTIME !== "edge") { }); } ``` - diff --git a/packages/world-postgres/src/queue.ts b/packages/world-postgres/src/queue.ts index 5aded60f0..97879a47f 100644 --- a/packages/world-postgres/src/queue.ts +++ b/packages/world-postgres/src/queue.ts @@ -7,7 +7,7 @@ import { type QueuePrefix, type ValidQueueName, } from '@workflow/world'; -import { createEmbeddedWorld } from '@workflow/world-local'; +import { createLocalWorld } from '@workflow/world-local'; import type PgBoss from 'pg-boss'; import { monotonicFactory } from 'ulid'; import { MessageData } from './boss.js'; @@ -19,8 +19,8 @@ import type { PostgresWorldConfig } from './config.js'; * - `step` for step jobs * * When a message is queued, it is sent to pg-boss with the appropriate job type. - * When a job is processed, it is deserialized and then re-queued into the _embedded world_, showing that - * we can reuse the embedded world, mix and match worlds to build + * When a job is processed, it is deserialized and then re-queued into the _local world_, showing that + * we can reuse the local world, mix and match worlds to build * hybrid architectures, and even migrate between worlds. */ export function createQueue( @@ -28,7 +28,7 @@ export function createQueue( config: PostgresWorldConfig ): Queue & { start(): Promise } { const port = process.env.PORT ? Number(process.env.PORT) : undefined; - const embeddedWorld = createEmbeddedWorld({ dataDir: undefined, port }); + const localWorld = createLocalWorld({ dataDir: undefined, port }); const transport = new JsonTransport(); const generateMessageId = monotonicFactory(); @@ -39,7 +39,7 @@ export function createQueue( __wkf_step_: `${prefix}steps`, } as const satisfies Record; - const createQueueHandler = embeddedWorld.createQueueHandler; + const createQueueHandler = localWorld.createQueueHandler; const getDeploymentId: Queue['getDeploymentId'] = async () => { return 'postgres'; @@ -98,7 +98,7 @@ export function createQueue( ); const message = QueuePayloadSchema.parse(body); const queueName = `${queue}${messageData.id}` as const; - await embeddedWorld.queue(queueName, message, { + await localWorld.queue(queueName, message, { idempotencyKey: messageData.idempotencyKey, }); } diff --git a/packages/world-testing/test/embedded.test.ts b/packages/world-testing/test/embedded.test.ts index a7f5ab487..ad23d5a6f 100644 --- a/packages/world-testing/test/embedded.test.ts +++ b/packages/world-testing/test/embedded.test.ts @@ -1,3 +1,3 @@ import { createTestSuite } from '../src/index.mjs'; -createTestSuite('embedded'); +createTestSuite('local'); From b22518cf996f2f9edc8e5e719a38ee80b19e5ea1 Mon Sep 17 00:00:00 2001 From: Peter Wielander Date: Wed, 26 Nov 2025 11:13:18 -0800 Subject: [PATCH 2/9] Changeset --- .changeset/twenty-forks-grab.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changeset/twenty-forks-grab.md diff --git a/.changeset/twenty-forks-grab.md b/.changeset/twenty-forks-grab.md new file mode 100644 index 000000000..34bf90519 --- /dev/null +++ b/.changeset/twenty-forks-grab.md @@ -0,0 +1,11 @@ +--- +"@workflow/world-postgres": patch +"@workflow/world-testing": patch +"@workflow/world-local": patch +"@workflow/core": patch +"@workflow/next": patch +"@workflow/cli": patch +"@workflow/web": patch +--- + +Rename leftover references to "embedded world" to be "local world" From b643cf03de3a20458186788997241d3523c45868 Mon Sep 17 00:00:00 2001 From: Peter Wielander Date: Sat, 29 Nov 2025 16:17:27 -0800 Subject: [PATCH 3/9] rename --- packages/world-local/src/queue.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/world-local/src/queue.ts b/packages/world-local/src/queue.ts index a516a3c41..d063c4be4 100644 --- a/packages/world-local/src/queue.ts +++ b/packages/world-local/src/queue.ts @@ -1,11 +1,11 @@ import { setTimeout } from 'node:timers/promises'; import { JsonTransport } from '@vercel/queue'; import { MessageId, type Queue, ValidQueueName } from '@workflow/world'; +import { Sema } from 'async-sema'; import { monotonicFactory } from 'ulid'; import { Agent } from 'undici'; import z from 'zod'; import type { Config } from './config.js'; -import { Sema } from 'async-sema'; import { resolveBaseUrl } from './config.js'; // For local queue, there is no technical limit on the message visibility lifespan, @@ -112,7 +112,7 @@ export function createQueue(config: Partial): Queue { } catch {} } - console.error(`[embedded world] Failed to queue message`, { + console.error(`[local world] Failed to queue message`, { queueName, text, status: response.status, @@ -122,7 +122,7 @@ export function createQueue(config: Partial): Queue { } console.error( - `[embedded world] Reached max retries of embedded world queue implementation` + `[local world] Reached max retries of local world queue implementation` ); } finally { semaphore.release(); From f50fa391c6e928cd9961cc1669a99c96d4057f0c Mon Sep 17 00:00:00 2001 From: Peter Wielander Date: Sat, 29 Nov 2025 17:34:22 -0800 Subject: [PATCH 4/9] Fix observability change --- packages/core/src/observability.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/core/src/observability.ts b/packages/core/src/observability.ts index 3f4d2325d..e4acbfac0 100644 --- a/packages/core/src/observability.ts +++ b/packages/core/src/observability.ts @@ -30,12 +30,16 @@ const streamToStreamId = (value: any): string => { return `${STREAM_ID_PREFIX}null`; }; -const functionToFunctionId = (value: any): string => { - if ('name' in value) { - const name = String(value.name); - return ``; +const serializedStepFunctionToString = (value: unknown): string => { + if (!value) return 'null'; + if (typeof value !== 'object') return 'null'; + if ('stepId' in value) { + const stepId = value.stepId; + // TODO: Add closure vars to the string representation. + // value.closureVars + return ``; } - return ``; + return ''; }; /** @@ -49,7 +53,7 @@ const streamPrintRevivers: Record any> = { ReadableStream: streamToStreamId, WritableStream: streamToStreamId, TransformStream: streamToStreamId, - StepFunction: functionToFunctionId, + StepFunction: serializedStepFunctionToString, }; const hydrateStepIO = < From 22e0ec41ec05e880bf6227f859d305ab5a8a4f3d Mon Sep 17 00:00:00 2001 From: Peter Wielander Date: Sun, 30 Nov 2025 10:42:54 -0800 Subject: [PATCH 5/9] Fix --- packages/web/src/lib/known-worlds.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/web/src/lib/known-worlds.ts b/packages/web/src/lib/known-worlds.ts index d26a61ebd..712b78b69 100644 --- a/packages/web/src/lib/known-worlds.ts +++ b/packages/web/src/lib/known-worlds.ts @@ -27,10 +27,11 @@ export interface KnownWorld { */ export const KNOWN_WORLDS: KnownWorld[] = [ { - id: 'embedded', - displayName: 'Embedded', + id: 'local', + displayName: 'Local', packageName: null, - description: 'Local file-based storage, ideal for development', + description: + 'Local file-based storage with no dependencies, ideal for development', isBuiltIn: true, }, { From cc662e72c083446e584321601ecd7f497a54bdcb Mon Sep 17 00:00:00 2001 From: Peter Wielander Date: Sun, 30 Nov 2025 10:43:49 -0800 Subject: [PATCH 6/9] fix docs mentions --- docs/content/docs/deploying/world/local-world.mdx | 6 +++--- packages/web/src/lib/config-world.ts | 4 ++-- packages/web/src/lib/known-worlds.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/content/docs/deploying/world/local-world.mdx b/docs/content/docs/deploying/world/local-world.mdx index a2388c242..717736e13 100644 --- a/docs/content/docs/deploying/world/local-world.mdx +++ b/docs/content/docs/deploying/world/local-world.mdx @@ -75,7 +75,7 @@ const world = createEmbeddedWorld({ dataDir: './custom-workflow-data' }); ### Port -By default, the embedded world **automatically detects** which port your application is listening on using process introspection. This works seamlessly with frameworks like SvelteKit, Vite, and others that use non-standard ports. +By default, the local world **automatically detects** which port your application is listening on using process introspection. This works seamlessly with frameworks like SvelteKit, Vite, and others that use non-standard ports. **Auto-detection example** (recommended): @@ -103,6 +103,7 @@ const world = createEmbeddedWorld({ port: 3000 }); For advanced use cases like HTTPS or custom hostnames, you can override the entire base URL. When set, this takes precedence over all port detection and configuration. **Use cases:** + - HTTPS dev servers (e.g., `next dev --experimental-https`) - Custom hostnames (e.g., `local.example.com`) - Non-localhost development @@ -147,7 +148,7 @@ npm run dev You can explicitly set the local world through environment variables: ```bash -export WORKFLOW_TARGET_WORLD=embedded +export WORKFLOW_TARGET_WORLD=local npm run dev ``` @@ -258,4 +259,3 @@ const world = createEmbeddedWorld({ - [World Interface](/docs/deploying/world) - Understanding the World interface - [Vercel World](/docs/deploying/world/vercel-world) - For production deployments - [Observability](/docs/observability) - Monitoring and debugging tools - diff --git a/packages/web/src/lib/config-world.ts b/packages/web/src/lib/config-world.ts index f2989b276..0766ff721 100644 --- a/packages/web/src/lib/config-world.ts +++ b/packages/web/src/lib/config-world.ts @@ -1,7 +1,7 @@ 'use server'; -import { createRequire } from 'node:module'; import { existsSync } from 'node:fs'; +import { createRequire } from 'node:module'; import { join, resolve } from 'node:path'; import { KNOWN_WORLDS, type KnownWorld } from './known-worlds'; @@ -37,7 +37,7 @@ export interface WorldAvailability { /** * Check which world packages are installed. * - * Built-in worlds (embedded, vercel) are always available. + * Built-in worlds (local, vercel) are always available. * Third-party worlds are checked by attempting to resolve their package. */ export async function checkWorldsAvailability(): Promise { diff --git a/packages/web/src/lib/known-worlds.ts b/packages/web/src/lib/known-worlds.ts index 712b78b69..d81ababfb 100644 --- a/packages/web/src/lib/known-worlds.ts +++ b/packages/web/src/lib/known-worlds.ts @@ -22,7 +22,7 @@ export interface KnownWorld { /** * List of known worlds that can be configured in the UI. * - * Built-in worlds (embedded, vercel) are always available as they're bundled + * Built-in worlds (local, vercel) are always available as they're bundled * with @workflow/core. Third-party worlds need to be installed separately. */ export const KNOWN_WORLDS: KnownWorld[] = [ From 4340b363783a13041051aaf92e5fc4ccce0651aa Mon Sep 17 00:00:00 2001 From: Vercel Date: Sun, 30 Nov 2025 19:08:41 +0000 Subject: [PATCH 7/9] Fix: The `KNOWN_WORLDS` array still has `id: 'embedded'` and `displayName: 'Embedded'`\, but the codebase now expects the backend to be `'local'`\. This will cause UI feature detection and conditional logic to fail\. --- packages/web/src/lib/config-world.ts | 2 +- packages/web/src/lib/known-worlds.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web/src/lib/config-world.ts b/packages/web/src/lib/config-world.ts index f2989b276..635b413f5 100644 --- a/packages/web/src/lib/config-world.ts +++ b/packages/web/src/lib/config-world.ts @@ -37,7 +37,7 @@ export interface WorldAvailability { /** * Check which world packages are installed. * - * Built-in worlds (embedded, vercel) are always available. + * Built-in worlds (local, vercel) are always available. * Third-party worlds are checked by attempting to resolve their package. */ export async function checkWorldsAvailability(): Promise { diff --git a/packages/web/src/lib/known-worlds.ts b/packages/web/src/lib/known-worlds.ts index d26a61ebd..70af02f13 100644 --- a/packages/web/src/lib/known-worlds.ts +++ b/packages/web/src/lib/known-worlds.ts @@ -22,13 +22,13 @@ export interface KnownWorld { /** * List of known worlds that can be configured in the UI. * - * Built-in worlds (embedded, vercel) are always available as they're bundled + * Built-in worlds (local, vercel) are always available as they're bundled * with @workflow/core. Third-party worlds need to be installed separately. */ export const KNOWN_WORLDS: KnownWorld[] = [ { - id: 'embedded', - displayName: 'Embedded', + id: 'local', + displayName: 'Local', packageName: null, description: 'Local file-based storage, ideal for development', isBuiltIn: true, From ce5908b244b9348df8748d69c5cce62ae59cc856 Mon Sep 17 00:00:00 2001 From: Vercel Date: Sun, 30 Nov 2025 19:08:43 +0000 Subject: [PATCH 8/9] Fix: The documentation contains 19 outdated references to the old `createEmbeddedWorld` function name and `WORKFLOW_EMBEDDED_*` environment variables that should be `createLocalWorld` and `WORKFLOW_LOCAL_*` respectively\. --- .../docs/deploying/world/local-world.mdx | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/content/docs/deploying/world/local-world.mdx b/docs/content/docs/deploying/world/local-world.mdx index 717736e13..ddd7a57cb 100644 --- a/docs/content/docs/deploying/world/local-world.mdx +++ b/docs/content/docs/deploying/world/local-world.mdx @@ -62,15 +62,15 @@ By default, workflow data is stored in `.workflow-data/` in your project root. T **Environment variable:** ```bash -export WORKFLOW_EMBEDDED_DATA_DIR=./custom-workflow-data +export WORKFLOW_LOCAL_DATA_DIR=./custom-workflow-data ``` **Programmatically:** ```typescript -import { createEmbeddedWorld } from '@workflow/world-local'; +import { createLocalWorld } from '@workflow/world-local'; -const world = createEmbeddedWorld({ dataDir: './custom-workflow-data' }); +const world = createLocalWorld({ dataDir: './custom-workflow-data' }); ``` ### Port @@ -80,10 +80,10 @@ By default, the local world **automatically detects** which port your applicatio **Auto-detection example** (recommended): ```typescript -import { createEmbeddedWorld } from '@workflow/world-local'; +import { createLocalWorld } from '@workflow/world-local'; // Port is automatically detected - no configuration needed! -const world = createEmbeddedWorld(); +const world = createLocalWorld(); ``` If auto-detection fails, the world will fall back to the `PORT` environment variable, then to port `3000`. @@ -93,9 +93,9 @@ If auto-detection fails, the world will fall back to the `PORT` environment vari You can override the auto-detected port by specifying it explicitly: ```typescript -import { createEmbeddedWorld } from '@workflow/world-local'; +import { createLocalWorld } from '@workflow/world-local'; -const world = createEmbeddedWorld({ port: 3000 }); +const world = createLocalWorld({ port: 3000 }); ``` ### Base URL @@ -111,21 +111,21 @@ For advanced use cases like HTTPS or custom hostnames, you can override the enti **Environment variable:** ```bash -export WORKFLOW_EMBEDDED_BASE_URL=https://local.example.com:3000 +export WORKFLOW_LOCAL_BASE_URL=https://local.example.com:3000 ``` **Programmatically:** ```typescript -import { createEmbeddedWorld } from '@workflow/world-local'; +import { createLocalWorld } from '@workflow/world-local'; // HTTPS -const world = createEmbeddedWorld({ +const world = createLocalWorld({ baseUrl: 'https://localhost:3000' }); // Custom hostname -const world = createEmbeddedWorld({ +const world = createLocalWorld({ baseUrl: 'https://local.example.com:3000' }); ``` @@ -203,12 +203,12 @@ For production deployments, use the [Vercel World](/docs/deploying/world/vercel- ## API Reference -### `createEmbeddedWorld` +### `createLocalWorld` Creates a local world instance: ```typescript -function createEmbeddedWorld( +function createLocalWorld( args?: Partial<{ dataDir: string; port: number; @@ -220,9 +220,9 @@ function createEmbeddedWorld( **Parameters:** - `args` - Optional configuration object: - - `dataDir` - Directory for storing workflow data (default: `.workflow-data/` or `WORKFLOW_EMBEDDED_DATA_DIR` env var) + - `dataDir` - Directory for storing workflow data (default: `.workflow-data/` or `WORKFLOW_LOCAL_DATA_DIR` env var) - `port` - Port override for queue transport (default: auto-detected → `PORT` env var → `3000`) - - `baseUrl` - Full base URL override for queue transport (default: `http://localhost:{port}` or `WORKFLOW_EMBEDDED_BASE_URL` env var) + - `baseUrl` - Full base URL override for queue transport (default: `http://localhost:{port}` or `WORKFLOW_LOCAL_BASE_URL` env var) **Returns:** @@ -231,24 +231,24 @@ function createEmbeddedWorld( **Examples:** ```typescript -import { createEmbeddedWorld } from '@workflow/world-local'; +import { createLocalWorld } from '@workflow/world-local'; // Use all defaults (recommended - auto-detects port) -const world = createEmbeddedWorld(); +const world = createLocalWorld(); // Custom data directory -const world = createEmbeddedWorld({ dataDir: './my-data' }); +const world = createLocalWorld({ dataDir: './my-data' }); // Override port -const world = createEmbeddedWorld({ port: 3000 }); +const world = createLocalWorld({ port: 3000 }); // HTTPS with custom hostname -const world = createEmbeddedWorld({ +const world = createLocalWorld({ baseUrl: 'https://local.example.com:3000' }); // Multiple options -const world = createEmbeddedWorld({ +const world = createLocalWorld({ dataDir: './my-data', baseUrl: 'https://localhost:3000' }); From 9ba3c29860a070f83581a485a69dfae7a14f2d39 Mon Sep 17 00:00:00 2001 From: Peter Wielander Date: Sun, 30 Nov 2025 11:10:45 -0800 Subject: [PATCH 9/9] Missed one --- packages/world-postgres/HOW_IT_WORKS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/world-postgres/HOW_IT_WORKS.md b/packages/world-postgres/HOW_IT_WORKS.md index 29b6b9e4a..0ca48cd32 100644 --- a/packages/world-postgres/HOW_IT_WORKS.md +++ b/packages/world-postgres/HOW_IT_WORKS.md @@ -13,7 +13,7 @@ graph LR Client --> PG[pg-boss queue] PG --> Worker[Embedded Worker] Worker --> HTTP[HTTP fetch] - HTTP --> EW[Embedded World] + HTTP --> EW[Local World] PG -.-> F["${prefix}flows
(workflows)"] PG -.-> S["${prefix}steps
(steps)"]