@ewhauser/world-celld 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["delay"],"sources":["../src/config.ts","../src/remote/errors.ts","../src/remote/rpc-client.ts","../src/remote/namespaces.ts","../src/vendor/shared/debug.ts","../src/util.ts","../src/queue.ts","../src/storage.ts","../src/streamer.ts","../src/index.ts"],"sourcesContent":["import type { WorkflowRunDONamespace } from './storage.js';\nimport type { StreamDONamespace } from './streamer.js';\nimport type { QueueCellNamespace } from './queue.js';\n\n/** Storage-layer KV interface (satisfied by IndexDO over HTTP, or a mock). */\nexport interface IndexNamespace {\n get(key: string): Promise<string | null>;\n put(key: string, value: string): Promise<void>;\n delete(key: string): Promise<void>;\n list(options?: { prefix?: string; cursor?: string; limit?: number; reverse?: boolean }): Promise<{\n keys: Array<{ name: string }>;\n list_complete: boolean;\n cursor?: string;\n }>;\n reserveHookToken(\n token: string,\n owner: HookTokenOwner,\n ): Promise<{ claimed: boolean; holder?: HookTokenOwner }>;\n finalizeHookIndexes(\n token: string,\n hookId: string,\n serializedHook: string,\n owner: HookTokenOwner,\n ): Promise<void>;\n releaseHookToken(token: string, owner: HookTokenOwner): Promise<void>;\n deleteHookIndexes(token: string, hookId: string, owner: HookTokenOwner): Promise<void>;\n}\n\nexport interface HookTokenOwner {\n runId: string;\n hookId: string;\n}\n\nexport interface CelldWorldEnv {\n WORKFLOW_DB: WorkflowRunDONamespace;\n WORKFLOW_INDEX: IndexNamespace;\n WORKFLOW_QUEUE: QueueCellNamespace;\n WORKFLOW_STREAMS: StreamDONamespace;\n}\n\nexport interface CelldWorldConfig {\n /**\n * Base URL of any celld fleet node's public listener (e.g.\n * `http://fleet.internal:8080`). Requests are routed to the deployed\n * world-celld worker, which forwards them to the owning cells.\n * Default: process.env.CELLD_FLEET_URL\n */\n fleetUrl?: string;\n /**\n * Bearer secret shared with the worker (its WORLD_SECRET var).\n * Default: process.env.CELLD_WORLD_SECRET\n */\n secret?: string;\n /**\n * Pre-built namespaces (used by tests and the in-process harness). When\n * provided, fleetUrl/secret are not used.\n */\n env?: CelldWorldEnv;\n /** Default: process.env.CELLD_DEPLOYMENT_ID || 'celld-default' */\n deploymentId?: string;\n /**\n * Base URL the app's workflow endpoints are mounted on; QueueDO cells\n * deliver to `${baseUrl}/.well-known/workflow/v1/{flow|step}`.\n * Default: process.env.WORKFLOW_BASE_URL || `http://localhost:${PORT ?? 3000}`\n */\n baseUrl?: string;\n /** Number of queue cells to spread enqueues over. Default: 1 */\n queueShards?: number;\n /** Poll interval (ms) while waiting for new chunks on a live stream. Default: 250 */\n readPollMs?: number;\n /** Per-attempt fleet RPC deadline in milliseconds. Default: 30000 */\n rpcTimeoutMs?: number;\n}\n\nexport interface ResolvedCelldConfig {\n fleetUrl?: string;\n secret?: string;\n env?: CelldWorldEnv;\n deploymentId: string;\n baseUrl?: string;\n queueShards: number;\n readPollMs: number;\n rpcTimeoutMs: number;\n}\n\nexport function resolveConfig(config?: CelldWorldConfig): ResolvedCelldConfig {\n return {\n fleetUrl: config?.fleetUrl ?? process.env.CELLD_FLEET_URL,\n secret: config?.secret ?? process.env.CELLD_WORLD_SECRET,\n env: config?.env,\n deploymentId: config?.deploymentId ?? process.env.CELLD_DEPLOYMENT_ID ?? 'celld-default',\n baseUrl: config?.baseUrl,\n queueShards: config?.queueShards ?? 1,\n readPollMs: config?.readPollMs ?? 250,\n rpcTimeoutMs: config?.rpcTimeoutMs ?? 30_000,\n };\n}\n","import { WorkflowWorldError } from '@workflow/errors';\n\nexport interface WireError {\n name?: string;\n message?: string;\n status?: number;\n}\n\n/**\n * Reconstruct a typed error from a router error payload. `@workflow/errors`\n * matches with name-based `.is()` checks, so a WorkflowWorldError with the\n * original name (and status) restored behaves identically to the source\n * error for every consumer in the workflow runtime.\n */\nexport function reconstructError(wire: WireError | undefined, httpStatus: number): Error {\n const message = wire?.message ?? `world-celld rpc failed with HTTP ${httpStatus}`;\n const error = new WorkflowWorldError(message, {\n status: wire?.status ?? httpStatus,\n });\n if (wire?.name) {\n error.name = wire.name;\n }\n return error;\n}\n\n/** Transport-level failure (fleet unreachable, malformed response). */\nexport class FleetTransportError extends Error {\n constructor(\n message: string,\n readonly cause?: unknown,\n ) {\n super(message);\n this.name = 'FleetTransportError';\n }\n}\n","import { rpcParse, rpcStringify } from '../codec.js';\nimport { FleetTransportError, reconstructError, type WireError } from './errors.js';\n\nexport interface RpcTransport {\n fleetUrl: string;\n secret: string;\n /** Per-attempt request deadline. Default: 30 seconds. */\n timeoutMs?: number;\n /** Injectable for tests. Default: globalThis.fetch */\n fetchImpl?: typeof fetch;\n}\n\n/** Statuses that mean \"the fleet couldn't take the request\" (safe to retry reads). */\nconst RETRYABLE_STATUSES = new Set([502, 503, 504]);\nconst READ_RETRIES = 2;\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nfunction delayMs(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Invoke one DO method through the worker router.\n *\n * Mutating calls run exactly once from the client's perspective: a blind\n * retry of e.g. applyEvent would turn commit-then-network-fail into a\n * spurious ENTITY_CONFLICT. The workflow runtime already gets at-least-once\n * via queue redelivery. Read-only calls retry on transport failures.\n */\nexport async function callDO<T>(\n transport: RpcTransport,\n binding: string,\n name: string,\n method: string,\n args: unknown[],\n opts?: { idempotent?: boolean },\n): Promise<T> {\n const doFetch = transport.fetchImpl ?? fetch;\n const url = `${transport.fleetUrl.replace(/\\/$/, '')}/v1/rpc/${binding}/${encodeURIComponent(\n name,\n )}/${method}`;\n const attempts = opts?.idempotent ? 1 + READ_RETRIES : 1;\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (attempt > 1) {\n await delayMs(100 * attempt + Math.random() * 200);\n }\n let response: Response;\n try {\n response = await doFetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${transport.secret}`,\n },\n body: rpcStringify(args),\n signal: AbortSignal.timeout(Math.max(1, transport.timeoutMs ?? DEFAULT_TIMEOUT_MS)),\n });\n } catch (error) {\n lastError = new FleetTransportError(`world-celld: fleet unreachable at ${url}`, error);\n continue;\n }\n\n if (response.ok) {\n try {\n return rpcParse<T>(await response.text());\n } catch (error) {\n lastError = new FleetTransportError(\n `world-celld: malformed successful response from ${url}`,\n error,\n );\n if (attempt < attempts) continue;\n throw lastError;\n }\n }\n\n const text = await response.text();\n let wire: WireError | undefined;\n try {\n wire = (JSON.parse(text) as { error?: WireError }).error;\n } catch {\n wire = { message: text || `HTTP ${response.status}` };\n }\n\n if (RETRYABLE_STATUSES.has(response.status) && attempt < attempts) {\n lastError = reconstructError(wire, response.status);\n continue;\n }\n\n throw reconstructError(wire, response.status);\n }\n\n throw lastError instanceof Error\n ? lastError\n : new FleetTransportError('world-celld: rpc failed', lastError);\n}\n","/**\n * HTTP-backed implementations of the structural namespace interfaces the\n * vendored storage/streamer/queue layers consume. `get(id)` returns a stub\n * whose method calls become `POST /v1/rpc/{binding}/{name}/{method}` against\n * the fleet — the celld stand-in for Cloudflare's `env` bindings.\n */\nimport type { CelldWorldEnv, HookTokenOwner, IndexNamespace } from '../config.js';\nimport type { QueueCellStub } from '../queue.js';\nimport type { WorkflowRunDOStub } from '../storage.js';\nimport type { StreamDOStub } from '../streamer.js';\nimport { callDO, type RpcTransport } from './rpc-client.js';\n\ninterface MethodSpec {\n methods: readonly string[];\n mutating: ReadonlySet<string>;\n}\n\nconst RUNS: MethodSpec = {\n methods: ['applyEvent', 'getRun', 'getStep', 'getEvent', 'listEvents', 'listSteps', 'listHooks'],\n mutating: new Set(['applyEvent']),\n};\n\nconst STREAMS: MethodSpec = {\n methods: ['writeChunk', 'closeStream', 'getChunks', 'getInfo', 'registerStream', 'listStreams'],\n mutating: new Set(['writeChunk', 'closeStream', 'registerStream']),\n};\n\nconst QUEUE: MethodSpec = {\n methods: [\n 'enqueue',\n 'stats',\n 'listDeadLetters',\n 'redriveDeadLetter',\n 'purgeDeadLetters',\n 'rearmAlarm',\n ],\n mutating: new Set(['enqueue', 'redriveDeadLetter', 'purgeDeadLetters', 'rearmAlarm']),\n};\n\nfunction makeStub<T>(transport: RpcTransport, binding: string, name: string, spec: MethodSpec): T {\n const stub: Record<string, (...args: unknown[]) => Promise<unknown>> = {};\n for (const method of spec.methods) {\n stub[method] = (...args: unknown[]) =>\n callDO(transport, binding, name, method, args, {\n idempotent: !spec.mutating.has(method),\n });\n }\n return stub as T;\n}\n\nfunction makeNamespace<T>(transport: RpcTransport, binding: string, spec: MethodSpec) {\n return {\n idFromName(name: string) {\n return { toString: () => name };\n },\n get(id: { toString(): string }): T {\n return makeStub<T>(transport, binding, id.toString(), spec);\n },\n };\n}\n\n/** The IndexDO is a single well-known cell. */\nconst INDEX_CELL_NAME = 'index';\n\nfunction makeIndexNamespace(transport: RpcTransport): IndexNamespace {\n const call = <T>(method: string, args: unknown[], idempotent: boolean) =>\n callDO<T>(transport, 'index', INDEX_CELL_NAME, method, args, { idempotent });\n return {\n get: (key) => call<string | null>('get', [key], true),\n put: (key, value) => call<void>('put', [key, value], false),\n delete: (key) => call<void>('delete', [key], false),\n list: (options) => call('list', [options], true),\n reserveHookToken: (token: string, owner: HookTokenOwner) =>\n call('reserveHookToken', [token, owner], false),\n finalizeHookIndexes: (token, hookId, serializedHook, owner) =>\n call<void>('finalizeHookIndexes', [token, hookId, serializedHook, owner], false),\n releaseHookToken: (token, owner) => call<void>('releaseHookToken', [token, owner], false),\n deleteHookIndexes: (token, hookId, owner) =>\n call<void>('deleteHookIndexes', [token, hookId, owner], false),\n };\n}\n\nexport function createRemoteEnv(transport: RpcTransport): CelldWorldEnv {\n return {\n WORKFLOW_DB: makeNamespace<WorkflowRunDOStub>(transport, 'runs', RUNS),\n WORKFLOW_STREAMS: makeNamespace<StreamDOStub>(transport, 'streams', STREAMS),\n WORKFLOW_INDEX: makeIndexNamespace(transport),\n WORKFLOW_QUEUE: makeNamespace<QueueCellStub>(transport, 'queue', QUEUE),\n };\n}\n","/**\n * Creates a namespaced debug logger.\n * Logs to stderr to avoid corrupting CLI JSON output on stdout.\n *\n * Control via WORKFLOW_DEBUG environment variable:\n * WORKFLOW_DEBUG=1 // Enable all\n * WORKFLOW_DEBUG=mysql-world // Specific namespace\n * WORKFLOW_DEBUG=mysql-world,redis-world // Multiple namespaces\n *\n * Vendored from vinnymac/worlds packages/shared, modified: guarded for\n * non-Node runtimes (celld/workerd), where `process` may be absent or\n * stubbed — falls back to console.error.\n */\nexport function createDebugLogger(namespace: string) {\n return (...args: unknown[]) => {\n const debug = typeof process === 'undefined' ? undefined : process.env?.WORKFLOW_DEBUG;\n if (!debug) return;\n\n const enabled =\n debug === '1' ||\n debug === 'true' ||\n debug === '*' ||\n debug.split(',').some((ns: string) => ns.trim() === namespace);\n\n if (!enabled) return;\n\n const timestamp = new Date().toISOString();\n const prefix = `[${timestamp}] [${namespace}]`;\n const message = args\n .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)))\n .join(' ');\n\n if (typeof process.stderr?.write === 'function') {\n process.stderr.write(`${prefix} ${message}\\n`);\n } else {\n console.error(`${prefix} ${message}`);\n }\n };\n}\n","import { createDebugLogger } from './vendor/shared/index.js';\n\nexport { compact } from './vendor/shared/index.js';\nexport const debug = createDebugLogger('celld-world');\n","/**\n * Queue implementation for the celld world.\n *\n * Vendored from vinnymac/worlds packages/world-cloudflare/src/queue.ts\n * (Apache-2.0, see NOTICE), modified for celld:\n *\n * - The Cloudflare Queues producer is replaced by an `enqueue` RPC to a\n * QueueDO cell (`q:<shard>`), which owns the message lifecycle: scheduling\n * via durable alarms, delivery via outbound fetch, retries with capped\n * backoff, and a dead-letter table.\n * - The consumer-side claim-DO idempotency mechanism is gone: a queue cell is\n * a single serialized writer, so idempotencyKey dedup happens inside the\n * cell (at enqueue and across the delivery state machine).\n * - `createQueueHandler` has ONE dialect — the x-vqs wire format the test\n * pump has always used — extended so permanent errors surface as their\n * HTTP statuses. QueueDO deliveries and the in-process test pump hit the\n * exact same handler path.\n */\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { WorkflowWorldError } from '@workflow/errors';\nimport {\n MessageId,\n parseQueueName,\n type Queue,\n type QueueKind,\n type QueuePayload,\n type ValidQueueName,\n} from '@workflow/world';\nimport { parse, stringify } from './vendor/shared/index.js';\nimport { monotonicFactory } from 'ulid';\nimport { debug } from './util.js';\n\n/**\n * HTTP status codes that indicate permanent (non-retryable) failures.\n * Deliveries with these statuses are dropped immediately instead of retried.\n *\n * - 404: Resource not found (e.g., run was deleted)\n * - 409: Conflict (e.g., duplicate event that can't be replayed)\n * - 410: Gone (e.g., run was already terminal)\n * - 422: Unprocessable entity (e.g., invalid payload structure)\n */\nexport const PERMANENT_ERROR_STATUSES = new Set([404, 409, 410, 422]);\n\nfunction isPermanentError(err: unknown): err is WorkflowWorldError {\n if (err instanceof WorkflowWorldError && err.status !== undefined) {\n return PERMANENT_ERROR_STATUSES.has(err.status);\n }\n return false;\n}\n\n/** Caps at 60 seconds. */\nexport function computeBackoff(attempt: number): number {\n return Math.min(60, 2 ** attempt);\n}\n\ntype Pathname = 'flow' | 'step';\n\nconst QUEUE_PATHNAMES = {\n workflow: 'flow',\n step: 'step',\n} as const satisfies Record<QueueKind, Pathname>;\n\nfunction isTestMode(): boolean {\n // Explicit override: force the production QueueDO path even under a test\n // runner (used by the conformance suite to exercise live queue cells —\n // the world-testing server inherits VITEST from the vitest parent).\n if (process.env.CELLD_QUEUE_MODE === 'cells') return false;\n return process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';\n}\n\n/**\n * Enqueue request accepted by a QueueDO cell. The message payload travels\n * pre-encoded with the shared tagged-JSON codec (`body`), so the cell can\n * forward it byte-identical to the app's queue handler without re-encoding.\n */\nexport interface EnqueueRequest {\n messageId: string;\n queueName: ValidQueueName;\n pathname: Pathname;\n /** `stringify(message)` — the exact bytes POSTed to the app on delivery. */\n body: string;\n idempotencyKey?: string;\n delaySeconds?: number;\n /**\n * Delivery configuration. The cell pins this from its first enqueue (the\n * eve-ambient pattern); a later mismatch is rejected as CONFIG_MISMATCH.\n */\n config: QueueCellConfig;\n}\n\nexport interface QueueCellConfig {\n /** Base URL of the app; deliveries POST to `${baseUrl}/.well-known/workflow/v1/{flow|step}`. */\n targetBaseUrl: string;\n queueShards: number;\n}\n\nexport type EnqueueOutcome =\n | { ok: true; messageId: string; deduped: boolean }\n | { ok: false; code: 'CONFIG_MISMATCH'; message: string };\n\n/** RPC surface of QueueDO used by the queue producer. */\nexport interface QueueCellStub {\n enqueue(request: EnqueueRequest): Promise<EnqueueOutcome>;\n}\n\nexport interface QueueCellNamespace {\n idFromName(name: string): { toString(): string };\n get(id: { toString(): string }): QueueCellStub;\n}\n\nexport interface CelldQueueConfig {\n env: {\n WORKFLOW_QUEUE: QueueCellNamespace;\n };\n deploymentId: string;\n /**\n * Base URL the app's workflow endpoints are mounted on. QueueDO cells\n * deliver to `${baseUrl}/.well-known/workflow/v1/{flow|step}`; the test\n * pump uses the same value.\n * Default: process.env.WORKFLOW_BASE_URL || `http://localhost:${process.env.PORT ?? 3000}`\n */\n baseUrl?: string;\n /** Number of `q:<shard>` cells to spread enqueues over. Default: 1 */\n queueShards?: number;\n /** Per-job HTTP request timeout (ms) for the test pump. Default: 300_000 */\n httpTimeoutMs?: number;\n /** Maximum retry attempts in the test pump before dropping a job. Default: 5 */\n maxAttempts?: number;\n /** Base backoff delay (ms) for test pump retries. Default: 1000 */\n backoffDelayMs?: number;\n}\n\ninterface PumpEnvelope {\n messageId: string;\n queueName: ValidQueueName;\n attempt: number;\n message: QueuePayload;\n idempotencyKey?: string;\n}\n\nfunction resolveBaseUrl(config: CelldQueueConfig): string {\n if (config.baseUrl) return config.baseUrl;\n if (process.env.WORKFLOW_BASE_URL) return process.env.WORKFLOW_BASE_URL;\n const port = process.env.PORT ?? '3000';\n return `http://localhost:${port}`;\n}\n\n/**\n * FNV-1a hash for shard selection. All messages sharing an idempotencyKey\n * land on the same cell so dedup stays strictly consistent.\n */\nexport function shardFor(key: string, shards: number): number {\n if (shards <= 1) return 0;\n let hash = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n hash ^= key.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0) % shards;\n}\n\n/**\n * In-process test pump. Holds two in-memory FIFOs and HTTP-dispatches\n * envelopes to the user's server at\n * `${baseUrl}/.well-known/workflow/v1/{flow|step}`.\n *\n * Mirrors world-local's idempotency semantics: messages are deduplicated on\n * `idempotencyKey` while a message with the same key is in flight, and the\n * key is released only when the message is fully handled (success or drop).\n *\n * Modified vs upstream: permanent-error statuses (404/409/410/422) from the\n * handler drop the message immediately instead of burning retry budget —\n * matching QueueDO's production semantics.\n */\nfunction createTestPump(config: CelldQueueConfig) {\n const httpTimeoutMs = config.httpTimeoutMs ?? 300_000;\n const maxAttempts = config.maxAttempts ?? 5;\n const baseBackoffMs = config.backoffDelayMs ?? 1000;\n\n const queues: Record<Pathname, PumpEnvelope[]> = { flow: [], step: [] };\n const wakers: Record<Pathname, Array<() => void>> = { flow: [], step: [] };\n /** Inflight messageIds by idempotencyKey (world-local queue.js semantics). */\n const inflightMessages = new Map<string, MessageId>();\n let running = false;\n\n function release(envelope: PumpEnvelope) {\n if (envelope.idempotencyKey) {\n inflightMessages.delete(envelope.idempotencyKey);\n }\n }\n\n function enqueue(pathname: Pathname, envelope: PumpEnvelope) {\n queues[pathname].push(envelope);\n wakers[pathname].shift()?.();\n }\n\n async function take(pathname: Pathname): Promise<PumpEnvelope | null> {\n const existing = queues[pathname].shift();\n if (existing) return existing;\n return new Promise((resolve) => {\n wakers[pathname].push(() => resolve(queues[pathname].shift() ?? null));\n });\n }\n\n async function dispatch(envelope: PumpEnvelope, pathname: Pathname): Promise<void> {\n const url = `${resolveBaseUrl(config)}/.well-known/workflow/v1/${pathname}`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-vqs-queue-name': envelope.queueName,\n 'x-vqs-message-id': envelope.messageId,\n 'x-vqs-message-attempt': String(envelope.attempt),\n },\n body: stringify(envelope.message),\n signal: AbortSignal.timeout(httpTimeoutMs),\n });\n\n if (response.ok) {\n release(envelope);\n return;\n }\n\n const text = await response.text();\n\n if (response.status === 503) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = null;\n }\n const timeoutSeconds = (parsed as { timeoutSeconds?: number } | null)?.timeoutSeconds;\n if (typeof timeoutSeconds === 'number') {\n // Same message re-delivered later: the idempotency key stays claimed.\n void delay(timeoutSeconds * 1000).then(() => enqueue(pathname, envelope));\n return;\n }\n }\n\n if (PERMANENT_ERROR_STATUSES.has(response.status)) {\n release(envelope);\n debug(\n `[world-celld test pump] dropping ${envelope.messageId}: permanent HTTP ${response.status}: ${text}`,\n );\n return;\n }\n\n if (envelope.attempt < maxAttempts) {\n const next: PumpEnvelope = { ...envelope, attempt: envelope.attempt + 1 };\n const backoff = baseBackoffMs * 2 ** (next.attempt - 1);\n void delay(backoff).then(() => enqueue(pathname, next));\n } else {\n release(envelope);\n console.error(\n `[world-celld test pump] dropping ${envelope.messageId} after ${envelope.attempt} attempts: HTTP ${response.status}: ${text}`,\n );\n }\n }\n\n async function loop(pathname: Pathname) {\n while (true) {\n if (!running) return;\n const envelope = await take(pathname);\n if (!envelope) continue;\n try {\n await dispatch(envelope, pathname);\n } catch (err) {\n release(envelope);\n console.error(`[world-celld test pump] dispatch error on ${pathname}:`, err);\n }\n }\n }\n\n return {\n /** Returns the inflight messageId when the key is already claimed. */\n inflight(idempotencyKey: string | undefined): MessageId | undefined {\n return idempotencyKey ? inflightMessages.get(idempotencyKey) : undefined;\n },\n push(pathname: Pathname, envelope: PumpEnvelope, delaySeconds?: number) {\n if (envelope.idempotencyKey) {\n inflightMessages.set(envelope.idempotencyKey, MessageId.parse(envelope.messageId));\n }\n if (delaySeconds && delaySeconds > 0) {\n void delay(delaySeconds * 1000).then(() => enqueue(pathname, envelope));\n } else {\n enqueue(pathname, envelope);\n }\n },\n async start() {\n if (running) return;\n running = true;\n void loop('flow');\n void loop('step');\n },\n stop() {\n running = false;\n for (const list of Object.values(wakers)) for (const w of list) w();\n },\n };\n}\n\nexport function createQueue(config: CelldQueueConfig): Queue & { start(): Promise<void> } {\n const { env, deploymentId } = config;\n const queueShards = config.queueShards ?? 1;\n\n const generateMessageId = monotonicFactory();\n const testPump = createTestPump(config);\n\n const getQueueStub = (shard: number): QueueCellStub => {\n const id = env.WORKFLOW_QUEUE.idFromName(`q:${shard}`);\n return env.WORKFLOW_QUEUE.get(id);\n };\n\n return {\n async queue(queueName, message, opts) {\n const { kind } = parseQueueName(queueName);\n\n if (isTestMode()) {\n // Dedup on idempotencyKey while a message with the same key is in\n // flight — core re-enqueues every still-pending step on every replay\n // with idempotencyKey = stepId and relies on queue-level dedup.\n const existing = testPump.inflight(opts?.idempotencyKey);\n if (existing) {\n return { messageId: existing };\n }\n const messageId = MessageId.parse(`msg_${generateMessageId()}`);\n testPump.push(\n QUEUE_PATHNAMES[kind],\n {\n messageId,\n queueName,\n attempt: 1,\n message,\n idempotencyKey: opts?.idempotencyKey,\n },\n opts?.delaySeconds,\n );\n return { messageId };\n }\n\n // Production: enqueue into a QueueDO cell. The cell owns scheduling,\n // delivery, retries, dedup, and the dead-letter table.\n const messageId = MessageId.parse(`msg_${generateMessageId()}`);\n const shard = shardFor(opts?.idempotencyKey ?? messageId, queueShards);\n\n const outcome = await getQueueStub(shard).enqueue({\n messageId,\n queueName,\n pathname: QUEUE_PATHNAMES[kind],\n body: stringify(message),\n idempotencyKey: opts?.idempotencyKey,\n delaySeconds: opts?.delaySeconds,\n config: {\n targetBaseUrl: resolveBaseUrl(config),\n queueShards,\n },\n });\n\n if (!outcome.ok) {\n throw new WorkflowWorldError(outcome.message, { status: 409 });\n }\n\n return { messageId: MessageId.parse(outcome.messageId) };\n },\n\n createQueueHandler(queueNamePrefix, handler) {\n // ONE dialect for the pump, QueueDO deliveries, and\n // @workflow/world-testing's mounted routes: x-vqs headers + tagged-JSON\n // body. The response taxonomy drives the sender's retry state machine:\n // - 2xx -> ack\n // - 503 + {timeoutSeconds} -> redeliver after N seconds (key stays claimed)\n // - 404/409/410/422 -> permanent, drop without retrying\n // - other non-2xx -> retry with capped backoff\n return async (req: Request) => {\n const reqQueueName = req.headers.get('x-vqs-queue-name');\n const reqMessageId = req.headers.get('x-vqs-message-id') as MessageId | null;\n const attemptStr = req.headers.get('x-vqs-message-attempt');\n\n if (!reqQueueName || !reqMessageId || !attemptStr || !req.body) {\n return Response.json({ error: 'Missing required headers or body' }, { status: 400 });\n }\n if (!reqQueueName.startsWith(queueNamePrefix)) {\n return Response.json({ error: 'Unhandled queue' }, { status: 400 });\n }\n const attempt = Number.parseInt(attemptStr, 10);\n try {\n // Tagged-JSON codec: revives Uint8Array payloads (runInput.input).\n const body = parse<unknown>(await req.text());\n const result = await handler(body, {\n attempt,\n queueName: reqQueueName,\n messageId: reqMessageId,\n });\n if (result && typeof result.timeoutSeconds === 'number') {\n return Response.json(\n { timeoutSeconds: result.timeoutSeconds },\n { status: 503, headers: { 'Retry-After': String(result.timeoutSeconds) } },\n );\n }\n return Response.json({ ok: true });\n } catch (error) {\n // Permanent vs transient distinction: permanent errors surface as\n // their own status so the sender drops instead of retrying.\n if (isPermanentError(error)) {\n debug('[world-celld queue handler] permanent error:', {\n status: error.status,\n error: String(error),\n });\n return Response.json(\n { error: String(error), permanent: true },\n { status: error.status },\n );\n }\n\n const backoffSeconds = computeBackoff(attempt);\n debug('[world-celld queue handler] transient error, will retry:', {\n error: String(error),\n backoffSeconds,\n });\n return Response.json(\n { error: String(error), retryAfter: backoffSeconds },\n { status: 500, headers: { 'Retry-After': String(backoffSeconds) } },\n );\n }\n };\n },\n\n async getDeploymentId() {\n return deploymentId;\n },\n\n async start() {\n if (isTestMode()) {\n await testPump.start();\n }\n // Production: QueueDO cells are push-based, nothing to start.\n },\n };\n}\n","import {\n EntityConflictError,\n HookNotFoundError,\n RunExpiredError,\n RunNotSupportedError,\n TooEarlyError,\n WorkflowRunNotFoundError,\n WorkflowWorldError,\n} from '@workflow/errors';\nimport type {\n CreateEventParams,\n CreateEventRequest,\n Event,\n EventResult,\n GetEventParams,\n GetHookParams,\n GetStepParams,\n GetWorkflowRunParams,\n Hook,\n ListEventsParams,\n ListHooksParams,\n ListWorkflowRunStepsParams,\n ListWorkflowRunsParams,\n PaginatedResponse,\n ResolveData,\n RunCreatedEventRequest,\n Step,\n StepWithoutData,\n Storage,\n WorkflowRun,\n WorkflowRunWithoutData,\n} from '@workflow/world';\nimport {\n EventSchema,\n HookSchema,\n SPEC_VERSION_CURRENT,\n StepSchema,\n WorkflowRunSchema,\n} from '@workflow/world';\nimport { parse, stringify } from './vendor/shared/index.js';\nimport { monotonicFactory } from 'ulid';\nimport type { ApplyEventFailure, ApplyEventOutcome, ApplyEventRequest } from './apply-event.js';\nimport type { HookTokenOwner, IndexNamespace } from './config.js';\nimport { compact } from './util.js';\n\n/**\n * RPC surface of WorkflowRunDO used by the storage layer. Declared\n * structurally to avoid a circular type reference on the DO class.\n */\nexport interface WorkflowRunDOStub {\n applyEvent(request: ApplyEventRequest): Promise<ApplyEventOutcome>;\n getRun(): Promise<WorkflowRun | null>;\n getStep(stepId: string): Promise<Step | null>;\n getEvent(eventId: string): Promise<Event | null>;\n listEvents(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: 'asc' | 'desc';\n }): Promise<{ data: Event[]; cursor: string | null; hasMore: boolean }>;\n listSteps(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: 'asc' | 'desc';\n }): Promise<{ data: Step[]; cursor: string | null; hasMore: boolean }>;\n listHooks(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: 'asc' | 'desc';\n }): Promise<{ data: Hook[]; cursor: string | null; hasMore: boolean }>;\n}\n\nexport interface WorkflowRunDONamespace {\n idFromName(name: string): DurableObjectIdLike;\n get(id: DurableObjectIdLike): WorkflowRunDOStub;\n}\n\nexport interface DurableObjectIdLike {\n toString(): string;\n}\n\nexport interface CloudflareStorageConfig {\n env: {\n WORKFLOW_DB: WorkflowRunDONamespace;\n WORKFLOW_INDEX: IndexNamespace;\n };\n deploymentId: string;\n}\n\nfunction sortableTimestamp(date: Date): string {\n return date.getTime().toString().padStart(13, '0');\n}\n\nfunction workflowRunIndexKey(run: WorkflowRun): string {\n return `run:${run.workflowName}:${sortableTimestamp(run.createdAt)}:${run.runId}`;\n}\n\nfunction globalRunIndexKey(run: WorkflowRun): string {\n return `runall:${sortableTimestamp(run.createdAt)}:${run.runId}`;\n}\n\nfunction hookOwner(hook: Pick<Hook, 'runId' | 'hookId'>): HookTokenOwner {\n return { runId: hook.runId, hookId: hook.hookId };\n}\n\n/**\n * Filter data based on ResolveData parameter.\n * When resolveData is 'none', strips specified keys to reduce data transfer.\n */\nfunction filterData<T extends object>(\n data: T,\n resolveData: ResolveData | undefined,\n keysToStrip: (keyof T)[],\n): T {\n if (resolveData === 'none') {\n const newData = { ...data };\n for (const key of keysToStrip) {\n if (key in newData) {\n delete newData[key];\n }\n }\n return newData;\n }\n return data;\n}\n\n/**\n * Filter hook data based on resolveData parameter\n */\nfunction filterHookData(hook: Hook, resolveData: ResolveData): Hook {\n if (resolveData === 'none' && 'metadata' in hook) {\n const { metadata: _, ...rest } = hook;\n return { metadata: undefined, ...rest };\n }\n return hook;\n}\n\n/**\n * Convert a structured applyEvent failure back into the typed error the\n * runtime matches on (via error-name based `.is()` checks). Typed errors\n * cannot cross the DO RPC boundary intact, which is why the DO returns\n * outcome objects instead of throwing.\n */\nfunction throwOutcomeError(\n outcome: ApplyEventFailure,\n runId: string,\n data: CreateEventRequest | RunCreatedEventRequest,\n): never {\n switch (outcome.code) {\n case 'RUN_NOT_FOUND':\n throw new WorkflowRunNotFoundError(runId);\n case 'STEP_NOT_FOUND':\n throw new WorkflowWorldError(outcome.message, { status: 404 });\n case 'HOOK_NOT_FOUND':\n throw new HookNotFoundError(data.correlationId ?? runId);\n case 'ENTITY_CONFLICT':\n throw new EntityConflictError(outcome.message);\n case 'RUN_EXPIRED':\n throw new RunExpiredError(outcome.message);\n case 'TOO_EARLY':\n throw new TooEarlyError(outcome.message, { retryAfter: outcome.retryAfterSeconds });\n case 'RUN_NOT_SUPPORTED':\n throw new RunNotSupportedError(outcome.runSpecVersion ?? 0, SPEC_VERSION_CURRENT);\n case 'LEGACY_RUN_NOT_SUPPORTED':\n throw new WorkflowWorldError(outcome.message, { status: 422 });\n }\n}\n\nconst parseRun = (run: WorkflowRun): WorkflowRun => WorkflowRunSchema.parse(compact(run));\nconst parseStep = (step: Step): Step => StepSchema.parse(compact(step));\nconst parseHook = (hook: Hook): Hook => HookSchema.parse(compact(hook));\nconst parseEvent = (event: Event): Event => EventSchema.parse(compact(event));\n\nexport function createStorage(config: CloudflareStorageConfig): Storage {\n const { env } = config;\n const ulid = monotonicFactory();\n\n // Helper to get or create a DO for a run\n const getRunDO = (runId: string): WorkflowRunDOStub => {\n const id = env.WORKFLOW_DB.idFromName(runId);\n return env.WORKFLOW_DB.get(id);\n };\n\n const runsGet = async (\n runId: string,\n params?: GetWorkflowRunParams,\n ): Promise<WorkflowRun | WorkflowRunWithoutData> => {\n const stub = getRunDO(runId);\n const run = await stub.getRun();\n\n if (!run) {\n throw new WorkflowRunNotFoundError(runId);\n }\n\n return filterData(parseRun(run), params?.resolveData, ['input', 'output']);\n };\n\n return {\n runs: {\n get: runsGet,\n\n async list(\n params?: ListWorkflowRunsParams,\n ): Promise<PaginatedResponse<WorkflowRun | WorkflowRunWithoutData>> {\n const limit = params?.pagination?.limit ?? 20;\n const prefix = params?.workflowName ? `run:${params.workflowName}:` : 'runall:';\n const reverse = params?.pagination?.sortOrder === 'desc';\n const matches: Array<{\n key: string;\n run: WorkflowRun | WorkflowRunWithoutData;\n }> = [];\n let scanCursor = params?.pagination?.cursor;\n let exhausted = false;\n\n // Keep scanning index pages until the requested page is full. This\n // prevents status filters and stale derived entries from producing\n // short pages or cursors that skip matching runs.\n while (matches.length <= limit && !exhausted) {\n const kvList = await env.WORKFLOW_INDEX.list({\n prefix,\n limit: Math.min(1000, Math.max(50, limit)),\n cursor: scanCursor,\n reverse,\n });\n if (kvList.keys.length === 0) {\n exhausted = true;\n break;\n }\n\n for (const key of kvList.keys) {\n const meta = await env.WORKFLOW_INDEX.get(key.name);\n if (!meta) continue;\n const { runId } = JSON.parse(meta) as { runId: string };\n try {\n const run = await runsGet(runId, { resolveData: params?.resolveData });\n if (!params?.status || run.status === params.status) {\n matches.push({ key: key.name, run });\n if (matches.length > limit) break;\n }\n } catch (error) {\n if (!WorkflowRunNotFoundError.is(error)) throw error;\n }\n }\n\n exhausted = kvList.list_complete;\n scanCursor = kvList.cursor ?? kvList.keys.at(-1)?.name;\n }\n\n const hasMore = matches.length > limit;\n const page = matches.slice(0, limit);\n\n return {\n data: page.map(({ run }) => run),\n cursor: hasMore ? (page.at(-1)?.key ?? null) : null,\n hasMore,\n };\n },\n } as Storage['runs'],\n\n events: {\n async create(\n runId: string | null,\n data: RunCreatedEventRequest | CreateEventRequest,\n _params?: CreateEventParams,\n ): Promise<EventResult> {\n // For run_created events, generate a runId server-side if absent.\n let effectiveRunId: string;\n if (data.eventType === 'run_created' && (!runId || runId === '')) {\n effectiveRunId = `wrun_${ulid()}`;\n } else if (!runId) {\n throw new WorkflowWorldError('runId is required for non-run_created events', {\n status: 400,\n });\n } else {\n effectiveRunId = runId;\n }\n\n const stub = getRunDO(effectiveRunId);\n\n let tokenHolder: ApplyEventRequest['tokenHolder'];\n let hookReservation: HookTokenOwner | undefined;\n if (data.eventType === 'hook_created') {\n hookReservation = {\n runId: effectiveRunId,\n hookId: data.correlationId,\n };\n const reservation = await env.WORKFLOW_INDEX.reserveHookToken(\n data.eventData.token,\n hookReservation,\n );\n tokenHolder = reservation.claimed ? null : reservation.holder;\n }\n\n // Guards, event append, and entity mutation run in ONE DO storage\n // transaction (see apply-event.ts). The event is schema-validated\n // before anything is persisted.\n let outcome: ApplyEventOutcome;\n try {\n outcome = await stub.applyEvent({ runId: effectiveRunId, data, tokenHolder });\n } catch (error) {\n if (hookReservation && data.eventType === 'hook_created') {\n await env.WORKFLOW_INDEX.releaseHookToken(data.eventData.token, hookReservation);\n }\n throw error;\n }\n\n if (!outcome.ok) {\n if (hookReservation && data.eventType === 'hook_created') {\n await env.WORKFLOW_INDEX.releaseHookToken(data.eventData.token, hookReservation);\n }\n throwOutcomeError(outcome, effectiveRunId, data);\n }\n\n // Derived indexes are deliberately rewritten on idempotent replay so\n // a committed run or hook can repair an interrupted index update.\n if (outcome.run) {\n const meta = JSON.stringify({\n runId: effectiveRunId,\n createdAt: outcome.run.createdAt.toISOString(),\n status: outcome.run.status,\n });\n await env.WORKFLOW_INDEX.put(workflowRunIndexKey(outcome.run), meta);\n await env.WORKFLOW_INDEX.put(globalRunIndexKey(outcome.run), meta);\n }\n if (outcome.hookToIndex) {\n const serialized = stringify(outcome.hookToIndex);\n await env.WORKFLOW_INDEX.finalizeHookIndexes(\n outcome.hookToIndex.token,\n outcome.hookToIndex.hookId,\n serialized,\n hookOwner(outcome.hookToIndex),\n );\n } else if (hookReservation && data.eventType === 'hook_created') {\n await env.WORKFLOW_INDEX.releaseHookToken(data.eventData.token, hookReservation);\n }\n for (const released of outcome.releasedHooks) {\n await env.WORKFLOW_INDEX.deleteHookIndexes(released.token, released.hookId, {\n runId: effectiveRunId,\n hookId: released.hookId,\n });\n }\n if (outcome.event?.correlationId) {\n const correlationKey = `correlation:${encodeURIComponent(\n outcome.event.correlationId,\n )}:${sortableTimestamp(outcome.event.createdAt)}:${outcome.event.eventId}:${effectiveRunId}`;\n await env.WORKFLOW_INDEX.put(\n correlationKey,\n JSON.stringify({ runId: effectiveRunId, eventId: outcome.event.eventId }),\n );\n }\n\n return {\n event: outcome.event,\n run: outcome.run,\n step: outcome.step,\n hook: outcome.hook,\n events: outcome.events,\n };\n },\n\n async get(runId: string, eventId: string, _params?: GetEventParams): Promise<Event> {\n const stub = getRunDO(runId);\n const event = await stub.getEvent(eventId);\n\n if (!event) {\n throw new WorkflowWorldError(`Event not found: ${eventId}`, {\n status: 404,\n });\n }\n\n return parseEvent(event);\n },\n\n async list(params: ListEventsParams): Promise<PaginatedResponse<Event>> {\n const { runId } = params;\n const limit = params?.pagination?.limit ?? 100;\n\n const stub = getRunDO(runId);\n const result = await stub.listEvents({\n limit,\n cursor: params?.pagination?.cursor || undefined,\n sortOrder: params.pagination?.sortOrder || 'asc',\n });\n\n return {\n data: result.data.map(parseEvent),\n cursor: result.cursor,\n hasMore: result.hasMore,\n };\n },\n\n async listByCorrelationId(params) {\n const limit = params.pagination?.limit ?? 100;\n const prefix = `correlation:${encodeURIComponent(params.correlationId)}:`;\n const listed = await env.WORKFLOW_INDEX.list({\n prefix,\n limit,\n cursor: params.pagination?.cursor,\n reverse: params.pagination?.sortOrder === 'desc',\n });\n const events = await Promise.all(\n listed.keys.map(async ({ name }) => {\n const raw = await env.WORKFLOW_INDEX.get(name);\n if (!raw) return null;\n const { runId, eventId } = JSON.parse(raw) as { runId: string; eventId: string };\n return getRunDO(runId).getEvent(eventId);\n }),\n );\n return {\n data: events.filter((event): event is Event => event !== null).map(parseEvent),\n cursor: listed.list_complete ? null : (listed.cursor ?? null),\n hasMore: !listed.list_complete,\n };\n },\n },\n\n steps: {\n async get(runId: string | undefined, stepId: string, params?: GetStepParams) {\n if (!runId) {\n throw new WorkflowWorldError('runId is required for Cloudflare step lookup', {\n status: 400,\n });\n }\n const stub = getRunDO(runId);\n const step = await stub.getStep(stepId);\n\n if (!step) {\n throw new WorkflowWorldError(`Step not found: ${stepId}`, {\n status: 404,\n });\n }\n\n return filterData(parseStep(step), params?.resolveData, ['input', 'output']);\n },\n\n async list(\n params: ListWorkflowRunStepsParams,\n ): Promise<PaginatedResponse<Step | StepWithoutData>> {\n const { runId } = params;\n const limit = params?.pagination?.limit ?? 20;\n\n const stub = getRunDO(runId);\n const result = await stub.listSteps({\n limit,\n cursor: params?.pagination?.cursor || undefined,\n sortOrder: params?.pagination?.sortOrder ?? 'asc',\n });\n\n return {\n data: result.data.map((s) =>\n filterData(parseStep(s), params?.resolveData, ['input', 'output']),\n ),\n cursor: result.cursor,\n hasMore: result.hasMore,\n };\n },\n } as Storage['steps'],\n\n hooks: {\n async get(hookId: string, params?: GetHookParams) {\n const raw = await env.WORKFLOW_INDEX.get(`hookid:${hookId}`);\n\n if (!raw) {\n throw new HookNotFoundError(hookId);\n }\n\n const hook = parseHook(parse<Hook>(raw));\n return filterHookData(hook, params?.resolveData ?? 'all');\n },\n\n async getByToken(token: string, params?: GetHookParams) {\n const raw = await env.WORKFLOW_INDEX.get(`hook:${token}`);\n\n if (!raw) {\n throw new HookNotFoundError(token);\n }\n\n const hook = parseHook(parse<Hook>(raw));\n return filterHookData(hook, params?.resolveData ?? 'all');\n },\n\n async list(params: ListHooksParams): Promise<PaginatedResponse<Hook>> {\n if (!params.runId) {\n throw new WorkflowWorldError('runId is required for listing hooks', {\n status: 400,\n });\n }\n const runId = params.runId;\n const limit = params?.pagination?.limit ?? 100;\n\n const stub = getRunDO(runId);\n const result = await stub.listHooks({\n limit,\n cursor: params?.pagination?.cursor || undefined,\n sortOrder: params?.pagination?.sortOrder ?? 'asc',\n });\n\n return {\n data: result.data.map((h) => filterHookData(parseHook(h), params?.resolveData ?? 'all')),\n cursor: result.cursor,\n hasMore: result.hasMore,\n };\n },\n },\n };\n}\n","import type {\n GetChunksOptions,\n StreamChunk,\n StreamChunksResponse,\n StreamInfoResponse,\n Streamer,\n} from '@workflow/world';\n\nexport interface CelldStreamerConfig {\n env: {\n WORKFLOW_STREAMS: StreamDONamespace;\n };\n /**\n * Poll interval (ms) while waiting for new chunks on a live stream.\n * Modified vs upstream: configurable because each poll is a fleet HTTP\n * round-trip here, not an in-process DO call. Default: 100.\n */\n readPollMs?: number;\n}\n\n/**\n * RPC surface of StreamDO (see durable-objects/StreamDO.ts). The streamer\n * talks to the DO exclusively via these methods — there is no fetch()\n * protocol.\n */\nexport interface StreamDOStub {\n writeChunk(data: Uint8Array): Promise<number>;\n closeStream(): Promise<void>;\n getChunks(params: {\n startIndex: number;\n limit: number;\n }): Promise<{ chunks: Uint8Array[]; done: boolean; tailIndex: number }>;\n getInfo(): Promise<{ tailIndex: number; done: boolean }>;\n registerStream(name: string): Promise<void>;\n listStreams(): Promise<string[]>;\n}\n\nexport interface StreamDONamespace {\n idFromName(name: string): StreamDOId;\n get(id: StreamDOId): StreamDOStub;\n}\n\nexport interface StreamDOId {\n toString(): string;\n}\n\n/** Default poll interval while waiting for new chunks on a live stream. */\nconst READ_POLL_MS = 100;\n/** Chunks fetched per DO round-trip while reading. */\nconst READ_BATCH_SIZE = 32;\n/** Default / maximum page sizes for getStreamChunks. */\nconst DEFAULT_CHUNK_PAGE_SIZE = 32;\nconst MAX_CHUNK_PAGE_SIZE = 32;\n\nfunction toBytes(chunk: string | Uint8Array): Uint8Array {\n return typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk;\n}\n\nexport function createStreamer(config: CelldStreamerConfig): Streamer {\n const { env } = config;\n const readPollMs = config.readPollMs ?? READ_POLL_MS;\n\n const getStreamDO = (streamName: string): StreamDOStub => {\n const id = env.WORKFLOW_STREAMS.idFromName(`stream:${streamName}`);\n return env.WORKFLOW_STREAMS.get(id);\n };\n\n const getRunRegistryDO = (runId: string): StreamDOStub => {\n const id = env.WORKFLOW_STREAMS.idFromName(`run-streams:${runId}`);\n return env.WORKFLOW_STREAMS.get(id);\n };\n\n /** Per-isolate cache so each (runId, stream) pair registers only once. */\n const registeredStreams = new Set<string>();\n\n async function registerStreamForRun(name: string, runId: string): Promise<void> {\n const cacheKey = `${runId}\\0${name}`;\n if (registeredStreams.has(cacheKey)) return;\n await getRunRegistryDO(runId).registerStream(name);\n registeredStreams.add(cacheKey);\n }\n\n return {\n async writeToStream(name: string, runId: string | Promise<string>, chunk: string | Uint8Array) {\n const resolvedRunId = await runId;\n await registerStreamForRun(name, resolvedRunId);\n await getStreamDO(name).writeChunk(toBytes(chunk));\n },\n\n async closeStream(name: string, runId: string | Promise<string>) {\n const resolvedRunId = await runId;\n await registerStreamForRun(name, resolvedRunId);\n await getStreamDO(name).closeStream();\n },\n\n async readFromStream(name: string, startIndex = 0) {\n const stub = getStreamDO(name);\n\n // Negative startIndex counts back from the current end, clamped to 0.\n let nextIndex: number;\n if (startIndex < 0) {\n const info = await stub.getInfo();\n nextIndex = Math.max(0, info.tailIndex + 1 + startIndex);\n } else {\n nextIndex = startIndex;\n }\n\n let cancelled = false;\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n // Loop until we can enqueue data, close, or the reader cancels.\n // Errors from the DO propagate and error the stream — readers must\n // never see a silently-truncated stream.\n while (true) {\n if (cancelled) return;\n const { chunks, done } = await stub.getChunks({\n startIndex: nextIndex,\n limit: READ_BATCH_SIZE,\n });\n if (chunks.length > 0) {\n for (const chunk of chunks) {\n controller.enqueue(chunk);\n }\n nextIndex += chunks.length;\n return;\n }\n if (done) {\n controller.close();\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, readPollMs));\n }\n },\n\n cancel() {\n cancelled = true;\n },\n });\n },\n\n async listStreamsByRunId(runId: string): Promise<string[]> {\n return getRunRegistryDO(runId).listStreams();\n },\n\n async getStreamChunks(\n name: string,\n _runId: string,\n options?: GetChunksOptions,\n ): Promise<StreamChunksResponse> {\n const limit = Math.min(options?.limit ?? DEFAULT_CHUNK_PAGE_SIZE, MAX_CHUNK_PAGE_SIZE);\n const startIndex = options?.cursor ? Number.parseInt(options.cursor, 10) : 0;\n if (Number.isNaN(startIndex) || startIndex < 0) {\n throw new Error(`Invalid stream cursor: ${options?.cursor}`);\n }\n\n const stub = getStreamDO(name);\n const result = await stub.getChunks({ startIndex, limit: limit + 1 });\n const hasMore = result.chunks.length > limit;\n const data: StreamChunk[] = result.chunks\n .slice(0, limit)\n .map((chunk, offset) => ({ index: startIndex + offset, data: chunk }));\n\n return {\n data,\n cursor: hasMore ? String(startIndex + limit) : null,\n hasMore,\n done: result.done,\n };\n },\n\n async getStreamInfo(name: string, _runId: string): Promise<StreamInfoResponse> {\n return getStreamDO(name).getInfo();\n },\n };\n}\n","/**\n * @ewhauser/world-celld — celld World implementation for the Vercel Workflow\n * DevKit. Modeled on vinnymac/worlds packages/world-cloudflare/src/index.ts\n * (Apache-2.0, see NOTICE).\n */\nimport { SPEC_VERSION_CURRENT, type World } from '@workflow/world';\nimport { type CelldWorldConfig, type CelldWorldEnv, resolveConfig } from './config.js';\nimport { createRemoteEnv } from './remote/namespaces.js';\nimport { createQueue } from './queue.js';\nimport { createStorage } from './storage.js';\nimport { createStreamer } from './streamer.js';\n\nexport type { CelldWorldConfig, CelldWorldEnv, IndexNamespace } from './config.js';\nexport type {\n EnqueueOutcome,\n EnqueueRequest,\n QueueCellConfig,\n QueueCellNamespace,\n QueueCellStub,\n} from './queue.js';\nexport type { WorkflowRunDONamespace, WorkflowRunDOStub } from './storage.js';\nexport type { StreamDONamespace, StreamDOStub } from './streamer.js';\n\nexport function createCelldWorld(config?: CelldWorldConfig): World {\n const resolved = resolveConfig(config);\n\n // Check for global test environment first (for @workflow/world-testing)\n let env = resolved.env;\n if (!env) {\n const globalEnv = (globalThis as { CELLD_ENV?: CelldWorldEnv }).CELLD_ENV;\n if (globalEnv) {\n env = globalEnv;\n }\n }\n\n if (!env) {\n if (resolved.fleetUrl) {\n if (!resolved.secret) {\n throw new Error(\n 'world-celld: config.secret (or CELLD_WORLD_SECRET) is required with fleetUrl',\n );\n }\n env = createRemoteEnv({\n fleetUrl: resolved.fleetUrl,\n secret: resolved.secret,\n timeoutMs: resolved.rpcTimeoutMs,\n });\n }\n }\n\n if (!env) {\n throw new Error(\n 'celld environment not configured. Provide config.fleetUrl + config.secret ' +\n '(or CELLD_FLEET_URL / CELLD_WORLD_SECRET), or pass config.env with ' +\n 'WORKFLOW_DB, WORKFLOW_INDEX, WORKFLOW_QUEUE, WORKFLOW_STREAMS',\n );\n }\n\n const storage = createStorage({\n env: {\n WORKFLOW_DB: env.WORKFLOW_DB,\n WORKFLOW_INDEX: env.WORKFLOW_INDEX,\n },\n deploymentId: resolved.deploymentId,\n });\n\n const queue = createQueue({\n env: {\n WORKFLOW_QUEUE: env.WORKFLOW_QUEUE,\n },\n deploymentId: resolved.deploymentId,\n baseUrl: resolved.baseUrl,\n queueShards: resolved.queueShards,\n });\n\n const streamer = createStreamer({\n env: {\n WORKFLOW_STREAMS: env.WORKFLOW_STREAMS,\n },\n readPollMs: resolved.readPollMs,\n });\n\n return {\n ...storage,\n ...queue,\n ...streamer,\n // Enables resilient start: runs are created at the current spec version,\n // so the queue message carries runInput and run_started can bootstrap the\n // run when run_created has not landed yet.\n specVersion: SPEC_VERSION_CURRENT,\n };\n}\n\n// Export createWorld as an alias for compatibility with @workflow/world\nexport { createCelldWorld as createWorld };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["delay"],"sources":["../src/config.ts","../src/remote/errors.ts","../src/remote/rpc-client.ts","../src/remote/namespaces.ts","../src/vendor/shared/debug.ts","../src/util.ts","../src/queue.ts","../src/storage.ts","../src/streamer.ts","../src/index.ts"],"sourcesContent":["import type { WorkflowRunDONamespace } from './storage.js';\nimport type { StreamDONamespace } from './streamer.js';\nimport type { QueueCellNamespace } from './queue.js';\n\n/** Storage-layer KV interface (satisfied by IndexDO over HTTP, or a mock). */\nexport interface IndexNamespace {\n get(key: string): Promise<string | null>;\n put(key: string, value: string): Promise<void>;\n delete(key: string): Promise<void>;\n putOwned(runId: string, key: string, value: string): Promise<{ stored: boolean }>;\n expireRun(\n request: import('./retention.js').ExpireRunIndexesRequest,\n ): Promise<import('./retention.js').ExpireRunIndexesResult>;\n list(options?: { prefix?: string; cursor?: string; limit?: number; reverse?: boolean }): Promise<{\n keys: Array<{ name: string }>;\n list_complete: boolean;\n cursor?: string;\n }>;\n reserveHookToken(\n token: string,\n owner: HookTokenOwner,\n ): Promise<{ claimed: boolean; holder?: HookTokenOwner }>;\n finalizeHookIndexes(\n token: string,\n hookId: string,\n serializedHook: string,\n owner: HookTokenOwner,\n ): Promise<void>;\n releaseHookToken(token: string, owner: HookTokenOwner): Promise<void>;\n deleteHookIndexes(token: string, hookId: string, owner: HookTokenOwner): Promise<void>;\n}\n\nexport interface HookTokenOwner {\n runId: string;\n hookId: string;\n}\n\nexport interface CelldWorldEnv {\n WORKFLOW_DB: WorkflowRunDONamespace;\n WORKFLOW_INDEX: IndexNamespace;\n WORKFLOW_QUEUE: QueueCellNamespace;\n WORKFLOW_STREAMS: StreamDONamespace;\n}\n\nexport interface CelldWorldConfig {\n /**\n * Base URL of any celld fleet node's public listener (e.g.\n * `http://fleet.internal:8080`). Requests are routed to the deployed\n * world-celld worker, which forwards them to the owning cells.\n * Default: process.env.CELLD_FLEET_URL\n */\n fleetUrl?: string;\n /**\n * Bearer secret shared with the worker (its WORLD_SECRET var).\n * Default: process.env.CELLD_WORLD_SECRET\n */\n secret?: string;\n /**\n * Pre-built namespaces (used by tests and the in-process harness). When\n * provided, fleetUrl/secret are not used.\n */\n env?: CelldWorldEnv;\n /** Default: process.env.CELLD_DEPLOYMENT_ID || 'celld-default' */\n deploymentId?: string;\n /**\n * Base URL the app's workflow endpoints are mounted on; QueueDO cells\n * deliver to `${baseUrl}/.well-known/workflow/v1/{flow|step}`.\n * Default: process.env.WORKFLOW_BASE_URL || `http://localhost:${PORT ?? 3000}`\n */\n baseUrl?: string;\n /** Number of queue cells to spread enqueues over. Default: 1 */\n queueShards?: number;\n /**\n * Keep terminal run payloads for this many milliseconds before replacing\n * them with metadata-only tombstones. Zero disables automatic cleanup.\n * Default: process.env.CELLD_RUN_RETENTION_MS || 0\n */\n runRetentionMs?: number;\n /** Poll interval (ms) while waiting for new chunks on a live stream. Default: 250 */\n readPollMs?: number;\n /** Per-attempt fleet RPC deadline in milliseconds. Default: 30000 */\n rpcTimeoutMs?: number;\n}\n\nexport interface ResolvedCelldConfig {\n fleetUrl?: string;\n secret?: string;\n env?: CelldWorldEnv;\n deploymentId: string;\n baseUrl?: string;\n queueShards: number;\n runRetentionMs: number;\n readPollMs: number;\n rpcTimeoutMs: number;\n}\n\nexport function resolveConfig(config?: CelldWorldConfig): ResolvedCelldConfig {\n const retentionRaw = config?.runRetentionMs ?? process.env.CELLD_RUN_RETENTION_MS ?? 0;\n const runRetentionMs =\n typeof retentionRaw === 'number' ? retentionRaw : Number.parseInt(retentionRaw, 10);\n if (!Number.isSafeInteger(runRetentionMs) || runRetentionMs < 0) {\n throw new Error('world-celld: runRetentionMs must be a non-negative safe integer');\n }\n\n return {\n fleetUrl: config?.fleetUrl ?? process.env.CELLD_FLEET_URL,\n secret: config?.secret ?? process.env.CELLD_WORLD_SECRET,\n env: config?.env,\n deploymentId: config?.deploymentId ?? process.env.CELLD_DEPLOYMENT_ID ?? 'celld-default',\n baseUrl: config?.baseUrl,\n queueShards: config?.queueShards ?? 1,\n runRetentionMs,\n readPollMs: config?.readPollMs ?? 250,\n rpcTimeoutMs: config?.rpcTimeoutMs ?? 30_000,\n };\n}\n","import { WorkflowWorldError } from '@workflow/errors';\n\nexport interface WireError {\n name?: string;\n message?: string;\n status?: number;\n}\n\n/**\n * Reconstruct a typed error from a router error payload. `@workflow/errors`\n * matches with name-based `.is()` checks, so a WorkflowWorldError with the\n * original name (and status) restored behaves identically to the source\n * error for every consumer in the workflow runtime.\n */\nexport function reconstructError(wire: WireError | undefined, httpStatus: number): Error {\n const message = wire?.message ?? `world-celld rpc failed with HTTP ${httpStatus}`;\n const error = new WorkflowWorldError(message, {\n status: wire?.status ?? httpStatus,\n });\n if (wire?.name) {\n error.name = wire.name;\n }\n return error;\n}\n\n/** Transport-level failure (fleet unreachable, malformed response). */\nexport class FleetTransportError extends Error {\n constructor(\n message: string,\n readonly cause?: unknown,\n ) {\n super(message);\n this.name = 'FleetTransportError';\n }\n}\n","import { rpcParse, rpcStringify } from '../codec.js';\nimport { FleetTransportError, reconstructError, type WireError } from './errors.js';\n\nexport interface RpcTransport {\n fleetUrl: string;\n secret: string;\n /** Per-attempt request deadline. Default: 30 seconds. */\n timeoutMs?: number;\n /** Injectable for tests. Default: globalThis.fetch */\n fetchImpl?: typeof fetch;\n}\n\n/** Statuses that mean \"the fleet couldn't take the request\" (safe to retry reads). */\nconst RETRYABLE_STATUSES = new Set([502, 503, 504]);\nconst READ_RETRIES = 2;\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nfunction delayMs(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Invoke one DO method through the worker router.\n *\n * Mutating calls run exactly once from the client's perspective: a blind\n * retry of e.g. applyEvent would turn commit-then-network-fail into a\n * spurious ENTITY_CONFLICT. The workflow runtime already gets at-least-once\n * via queue redelivery. Read-only calls retry on transport failures.\n */\nexport async function callDO<T>(\n transport: RpcTransport,\n binding: string,\n name: string,\n method: string,\n args: unknown[],\n opts?: { idempotent?: boolean },\n): Promise<T> {\n const doFetch = transport.fetchImpl ?? fetch;\n const url = `${transport.fleetUrl.replace(/\\/$/, '')}/v1/rpc/${binding}/${encodeURIComponent(\n name,\n )}/${method}`;\n const attempts = opts?.idempotent ? 1 + READ_RETRIES : 1;\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (attempt > 1) {\n await delayMs(100 * attempt + Math.random() * 200);\n }\n let response: Response;\n try {\n response = await doFetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n authorization: `Bearer ${transport.secret}`,\n },\n body: rpcStringify(args),\n signal: AbortSignal.timeout(Math.max(1, transport.timeoutMs ?? DEFAULT_TIMEOUT_MS)),\n });\n } catch (error) {\n lastError = new FleetTransportError(`world-celld: fleet unreachable at ${url}`, error);\n continue;\n }\n\n if (response.ok) {\n try {\n return rpcParse<T>(await response.text());\n } catch (error) {\n lastError = new FleetTransportError(\n `world-celld: malformed successful response from ${url}`,\n error,\n );\n if (attempt < attempts) continue;\n throw lastError;\n }\n }\n\n const text = await response.text();\n let wire: WireError | undefined;\n try {\n wire = (JSON.parse(text) as { error?: WireError }).error;\n } catch {\n wire = { message: text || `HTTP ${response.status}` };\n }\n\n if (RETRYABLE_STATUSES.has(response.status) && attempt < attempts) {\n lastError = reconstructError(wire, response.status);\n continue;\n }\n\n throw reconstructError(wire, response.status);\n }\n\n throw lastError instanceof Error\n ? lastError\n : new FleetTransportError('world-celld: rpc failed', lastError);\n}\n","/**\n * HTTP-backed implementations of the structural namespace interfaces the\n * vendored storage/streamer/queue layers consume. `get(id)` returns a stub\n * whose method calls become `POST /v1/rpc/{binding}/{name}/{method}` against\n * the fleet — the celld stand-in for Cloudflare's `env` bindings.\n */\nimport type { CelldWorldEnv, HookTokenOwner, IndexNamespace } from '../config.js';\nimport type { QueueCellStub } from '../queue.js';\nimport type { WorkflowRunDOStub } from '../storage.js';\nimport type { StreamDOStub } from '../streamer.js';\nimport { callDO, type RpcTransport } from './rpc-client.js';\n\ninterface MethodSpec {\n methods: readonly string[];\n mutating: ReadonlySet<string>;\n}\n\nconst RUNS: MethodSpec = {\n methods: [\n 'applyEvent',\n 'getRun',\n 'getStep',\n 'getEvent',\n 'listEvents',\n 'listSteps',\n 'listHooks',\n 'getCleanupStatus',\n 'scheduleCleanup',\n 'cleanupNow',\n 'rearmCleanup',\n ],\n mutating: new Set(['applyEvent', 'scheduleCleanup', 'cleanupNow', 'rearmCleanup']),\n};\n\nconst STREAMS: MethodSpec = {\n methods: [\n 'writeChunk',\n 'closeStream',\n 'getChunks',\n 'getInfo',\n 'registerStream',\n 'listStreams',\n 'expireRegistry',\n 'finalizeRegistry',\n 'expireStream',\n ],\n mutating: new Set([\n 'writeChunk',\n 'closeStream',\n 'registerStream',\n 'expireRegistry',\n 'finalizeRegistry',\n 'expireStream',\n ]),\n};\n\nconst QUEUE: MethodSpec = {\n methods: [\n 'enqueue',\n 'stats',\n 'listDeadLetters',\n 'redriveDeadLetter',\n 'purgeDeadLetters',\n 'rearmAlarm',\n 'expireRun',\n ],\n mutating: new Set([\n 'enqueue',\n 'redriveDeadLetter',\n 'purgeDeadLetters',\n 'rearmAlarm',\n 'expireRun',\n ]),\n};\n\nfunction makeStub<T>(transport: RpcTransport, binding: string, name: string, spec: MethodSpec): T {\n const stub: Record<string, (...args: unknown[]) => Promise<unknown>> = {};\n for (const method of spec.methods) {\n stub[method] = (...args: unknown[]) =>\n callDO(transport, binding, name, method, args, {\n idempotent: !spec.mutating.has(method),\n });\n }\n return stub as T;\n}\n\nfunction makeNamespace<T>(transport: RpcTransport, binding: string, spec: MethodSpec) {\n return {\n idFromName(name: string) {\n return { toString: () => name };\n },\n get(id: { toString(): string }): T {\n return makeStub<T>(transport, binding, id.toString(), spec);\n },\n };\n}\n\n/** The IndexDO is a single well-known cell. */\nconst INDEX_CELL_NAME = 'index';\n\nfunction makeIndexNamespace(transport: RpcTransport): IndexNamespace {\n const call = <T>(method: string, args: unknown[], idempotent: boolean) =>\n callDO<T>(transport, 'index', INDEX_CELL_NAME, method, args, { idempotent });\n return {\n get: (key) => call<string | null>('get', [key], true),\n put: (key, value) => call<void>('put', [key, value], false),\n delete: (key) => call<void>('delete', [key], false),\n putOwned: (runId, key, value) =>\n call<{ stored: boolean }>('putOwned', [runId, key, value], false),\n expireRun: (request) => call('expireRun', [request], false),\n list: (options) => call('list', [options], true),\n reserveHookToken: (token: string, owner: HookTokenOwner) =>\n call('reserveHookToken', [token, owner], false),\n finalizeHookIndexes: (token, hookId, serializedHook, owner) =>\n call<void>('finalizeHookIndexes', [token, hookId, serializedHook, owner], false),\n releaseHookToken: (token, owner) => call<void>('releaseHookToken', [token, owner], false),\n deleteHookIndexes: (token, hookId, owner) =>\n call<void>('deleteHookIndexes', [token, hookId, owner], false),\n };\n}\n\nexport function createRemoteEnv(transport: RpcTransport): CelldWorldEnv {\n return {\n WORKFLOW_DB: makeNamespace<WorkflowRunDOStub>(transport, 'runs', RUNS),\n WORKFLOW_STREAMS: makeNamespace<StreamDOStub>(transport, 'streams', STREAMS),\n WORKFLOW_INDEX: makeIndexNamespace(transport),\n WORKFLOW_QUEUE: makeNamespace<QueueCellStub>(transport, 'queue', QUEUE),\n };\n}\n","/**\n * Creates a namespaced debug logger.\n * Logs to stderr to avoid corrupting CLI JSON output on stdout.\n *\n * Control via WORKFLOW_DEBUG environment variable:\n * WORKFLOW_DEBUG=1 // Enable all\n * WORKFLOW_DEBUG=mysql-world // Specific namespace\n * WORKFLOW_DEBUG=mysql-world,redis-world // Multiple namespaces\n *\n * Vendored from vinnymac/worlds packages/shared, modified: guarded for\n * non-Node runtimes (celld/workerd), where `process` may be absent or\n * stubbed — falls back to console.error.\n */\nexport function createDebugLogger(namespace: string) {\n return (...args: unknown[]) => {\n const debug = typeof process === 'undefined' ? undefined : process.env?.WORKFLOW_DEBUG;\n if (!debug) return;\n\n const enabled =\n debug === '1' ||\n debug === 'true' ||\n debug === '*' ||\n debug.split(',').some((ns: string) => ns.trim() === namespace);\n\n if (!enabled) return;\n\n const timestamp = new Date().toISOString();\n const prefix = `[${timestamp}] [${namespace}]`;\n const message = args\n .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)))\n .join(' ');\n\n if (typeof process.stderr?.write === 'function') {\n process.stderr.write(`${prefix} ${message}\\n`);\n } else {\n console.error(`${prefix} ${message}`);\n }\n };\n}\n","import { createDebugLogger } from './vendor/shared/index.js';\n\nexport { compact } from './vendor/shared/index.js';\nexport const debug = createDebugLogger('celld-world');\n","/**\n * Queue implementation for the celld world.\n *\n * Vendored from vinnymac/worlds packages/world-cloudflare/src/queue.ts\n * (Apache-2.0, see NOTICE), modified for celld:\n *\n * - The Cloudflare Queues producer is replaced by an `enqueue` RPC to a\n * QueueDO cell (`q:<shard>`), which owns the message lifecycle: scheduling\n * via durable alarms, delivery via outbound fetch, retries with capped\n * backoff, and a dead-letter table.\n * - The consumer-side claim-DO idempotency mechanism is gone: a queue cell is\n * a single serialized writer, so idempotencyKey dedup happens inside the\n * cell (at enqueue and across the delivery state machine).\n * - `createQueueHandler` has ONE dialect — the x-vqs wire format the test\n * pump has always used — extended so permanent errors surface as their\n * HTTP statuses. QueueDO deliveries and the in-process test pump hit the\n * exact same handler path.\n */\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { WorkflowWorldError } from '@workflow/errors';\nimport { RunExpiredError } from '@workflow/errors';\nimport {\n MessageId,\n parseQueueName,\n type Queue,\n type QueueKind,\n type QueuePayload,\n type ValidQueueName,\n} from '@workflow/world';\nimport { parse, stringify } from './vendor/shared/index.js';\nimport { monotonicFactory } from 'ulid';\nimport { debug } from './util.js';\n\n/**\n * HTTP status codes that indicate permanent (non-retryable) failures.\n * Deliveries with these statuses are dropped immediately instead of retried.\n *\n * - 404: Resource not found (e.g., run was deleted)\n * - 409: Conflict (e.g., duplicate event that can't be replayed)\n * - 410: Gone (e.g., run was already terminal)\n * - 422: Unprocessable entity (e.g., invalid payload structure)\n */\nexport const PERMANENT_ERROR_STATUSES = new Set([404, 409, 410, 422]);\n\nfunction isPermanentError(err: unknown): err is WorkflowWorldError {\n if (err instanceof WorkflowWorldError && err.status !== undefined) {\n return PERMANENT_ERROR_STATUSES.has(err.status);\n }\n return false;\n}\n\n/** Caps at 60 seconds. */\nexport function computeBackoff(attempt: number): number {\n return Math.min(60, 2 ** attempt);\n}\n\ntype Pathname = 'flow' | 'step';\n\nconst QUEUE_PATHNAMES = {\n workflow: 'flow',\n step: 'step',\n} as const satisfies Record<QueueKind, Pathname>;\n\nfunction isTestMode(): boolean {\n // Explicit override: force the production QueueDO path even under a test\n // runner (used by the conformance suite to exercise live queue cells —\n // the world-testing server inherits VITEST from the vitest parent).\n if (process.env.CELLD_QUEUE_MODE === 'cells') return false;\n return process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';\n}\n\n/**\n * Enqueue request accepted by a QueueDO cell. The message payload travels\n * pre-encoded with the shared tagged-JSON codec (`body`), so the cell can\n * forward it byte-identical to the app's queue handler without re-encoding.\n */\nexport interface EnqueueRequest {\n messageId: string;\n queueName: ValidQueueName;\n pathname: Pathname;\n /** `stringify(message)` — the exact bytes POSTed to the app on delivery. */\n body: string;\n /** Owning workflow run; absent only for health-check messages. */\n runId?: string;\n idempotencyKey?: string;\n delaySeconds?: number;\n /**\n * Delivery configuration. The cell pins this from its first enqueue (the\n * eve-ambient pattern); a later mismatch is rejected as CONFIG_MISMATCH.\n */\n config: QueueCellConfig;\n}\n\nexport interface QueueCellConfig {\n /** Base URL of the app; deliveries POST to `${baseUrl}/.well-known/workflow/v1/{flow|step}`. */\n targetBaseUrl: string;\n queueShards: number;\n}\n\nexport type EnqueueOutcome =\n | { ok: true; messageId: string; deduped: boolean }\n | { ok: false; code: 'CONFIG_MISMATCH' | 'RUN_EXPIRED'; message: string };\n\n/** RPC surface of QueueDO used by the queue producer. */\nexport interface QueueCellStub {\n enqueue(request: EnqueueRequest): Promise<EnqueueOutcome>;\n expireRun(\n runId: string,\n expiredAt: number,\n ): Promise<import('./retention.js').ExpireQueueRunResult>;\n}\n\nexport interface QueueCellNamespace {\n idFromName(name: string): { toString(): string };\n get(id: { toString(): string }): QueueCellStub;\n}\n\nexport interface CelldQueueConfig {\n env: {\n WORKFLOW_QUEUE: QueueCellNamespace;\n };\n deploymentId: string;\n /**\n * Base URL the app's workflow endpoints are mounted on. QueueDO cells\n * deliver to `${baseUrl}/.well-known/workflow/v1/{flow|step}`; the test\n * pump uses the same value.\n * Default: process.env.WORKFLOW_BASE_URL || `http://localhost:${process.env.PORT ?? 3000}`\n */\n baseUrl?: string;\n /** Number of `q:<shard>` cells to spread enqueues over. Default: 1 */\n queueShards?: number;\n /** Per-job HTTP request timeout (ms) for the test pump. Default: 300_000 */\n httpTimeoutMs?: number;\n /** Maximum retry attempts in the test pump before dropping a job. Default: 5 */\n maxAttempts?: number;\n /** Base backoff delay (ms) for test pump retries. Default: 1000 */\n backoffDelayMs?: number;\n}\n\ninterface PumpEnvelope {\n messageId: string;\n queueName: ValidQueueName;\n attempt: number;\n message: QueuePayload;\n idempotencyKey?: string;\n}\n\nfunction resolveBaseUrl(config: CelldQueueConfig): string {\n if (config.baseUrl) return config.baseUrl;\n if (process.env.WORKFLOW_BASE_URL) return process.env.WORKFLOW_BASE_URL;\n const port = process.env.PORT ?? '3000';\n return `http://localhost:${port}`;\n}\n\n/**\n * FNV-1a hash for shard selection. All messages sharing an idempotencyKey\n * land on the same cell so dedup stays strictly consistent.\n */\nexport function shardFor(key: string, shards: number): number {\n if (shards <= 1) return 0;\n let hash = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n hash ^= key.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0) % shards;\n}\n\n/**\n * In-process test pump. Holds two in-memory FIFOs and HTTP-dispatches\n * envelopes to the user's server at\n * `${baseUrl}/.well-known/workflow/v1/{flow|step}`.\n *\n * Mirrors world-local's idempotency semantics: messages are deduplicated on\n * `idempotencyKey` while a message with the same key is in flight, and the\n * key is released only when the message is fully handled (success or drop).\n *\n * Modified vs upstream: permanent-error statuses (404/409/410/422) from the\n * handler drop the message immediately instead of burning retry budget —\n * matching QueueDO's production semantics.\n */\nfunction createTestPump(config: CelldQueueConfig) {\n const httpTimeoutMs = config.httpTimeoutMs ?? 300_000;\n const maxAttempts = config.maxAttempts ?? 5;\n const baseBackoffMs = config.backoffDelayMs ?? 1000;\n\n const queues: Record<Pathname, PumpEnvelope[]> = { flow: [], step: [] };\n const wakers: Record<Pathname, Array<() => void>> = { flow: [], step: [] };\n /** Inflight messageIds by idempotencyKey (world-local queue.js semantics). */\n const inflightMessages = new Map<string, MessageId>();\n let running = false;\n\n function release(envelope: PumpEnvelope) {\n if (envelope.idempotencyKey) {\n inflightMessages.delete(envelope.idempotencyKey);\n }\n }\n\n function enqueue(pathname: Pathname, envelope: PumpEnvelope) {\n queues[pathname].push(envelope);\n wakers[pathname].shift()?.();\n }\n\n async function take(pathname: Pathname): Promise<PumpEnvelope | null> {\n const existing = queues[pathname].shift();\n if (existing) return existing;\n return new Promise((resolve) => {\n wakers[pathname].push(() => resolve(queues[pathname].shift() ?? null));\n });\n }\n\n async function dispatch(envelope: PumpEnvelope, pathname: Pathname): Promise<void> {\n const url = `${resolveBaseUrl(config)}/.well-known/workflow/v1/${pathname}`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-vqs-queue-name': envelope.queueName,\n 'x-vqs-message-id': envelope.messageId,\n 'x-vqs-message-attempt': String(envelope.attempt),\n },\n body: stringify(envelope.message),\n signal: AbortSignal.timeout(httpTimeoutMs),\n });\n\n if (response.ok) {\n release(envelope);\n return;\n }\n\n const text = await response.text();\n\n if (response.status === 503) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = null;\n }\n const timeoutSeconds = (parsed as { timeoutSeconds?: number } | null)?.timeoutSeconds;\n if (typeof timeoutSeconds === 'number') {\n // Same message re-delivered later: the idempotency key stays claimed.\n void delay(timeoutSeconds * 1000).then(() => enqueue(pathname, envelope));\n return;\n }\n }\n\n if (PERMANENT_ERROR_STATUSES.has(response.status)) {\n release(envelope);\n debug(\n `[world-celld test pump] dropping ${envelope.messageId}: permanent HTTP ${response.status}: ${text}`,\n );\n return;\n }\n\n if (envelope.attempt < maxAttempts) {\n const next: PumpEnvelope = { ...envelope, attempt: envelope.attempt + 1 };\n const backoff = baseBackoffMs * 2 ** (next.attempt - 1);\n void delay(backoff).then(() => enqueue(pathname, next));\n } else {\n release(envelope);\n console.error(\n `[world-celld test pump] dropping ${envelope.messageId} after ${envelope.attempt} attempts: HTTP ${response.status}: ${text}`,\n );\n }\n }\n\n async function loop(pathname: Pathname) {\n while (true) {\n if (!running) return;\n const envelope = await take(pathname);\n if (!envelope) continue;\n try {\n await dispatch(envelope, pathname);\n } catch (err) {\n release(envelope);\n console.error(`[world-celld test pump] dispatch error on ${pathname}:`, err);\n }\n }\n }\n\n return {\n /** Returns the inflight messageId when the key is already claimed. */\n inflight(idempotencyKey: string | undefined): MessageId | undefined {\n return idempotencyKey ? inflightMessages.get(idempotencyKey) : undefined;\n },\n push(pathname: Pathname, envelope: PumpEnvelope, delaySeconds?: number) {\n if (envelope.idempotencyKey) {\n inflightMessages.set(envelope.idempotencyKey, MessageId.parse(envelope.messageId));\n }\n if (delaySeconds && delaySeconds > 0) {\n void delay(delaySeconds * 1000).then(() => enqueue(pathname, envelope));\n } else {\n enqueue(pathname, envelope);\n }\n },\n async start() {\n if (running) return;\n running = true;\n void loop('flow');\n void loop('step');\n },\n stop() {\n running = false;\n for (const list of Object.values(wakers)) for (const w of list) w();\n },\n };\n}\n\nexport function createQueue(config: CelldQueueConfig): Queue & { start(): Promise<void> } {\n const { env, deploymentId } = config;\n const queueShards = config.queueShards ?? 1;\n\n const generateMessageId = monotonicFactory();\n const testPump = createTestPump(config);\n\n const getQueueStub = (shard: number): QueueCellStub => {\n const id = env.WORKFLOW_QUEUE.idFromName(`q:${shard}`);\n return env.WORKFLOW_QUEUE.get(id);\n };\n\n return {\n async queue(queueName, message, opts) {\n const { kind } = parseQueueName(queueName);\n const runId =\n 'runId' in message\n ? message.runId\n : 'workflowRunId' in message\n ? message.workflowRunId\n : undefined;\n\n if (isTestMode()) {\n // Dedup on idempotencyKey while a message with the same key is in\n // flight — core re-enqueues every still-pending step on every replay\n // with idempotencyKey = stepId and relies on queue-level dedup.\n const existing = testPump.inflight(opts?.idempotencyKey);\n if (existing) {\n return { messageId: existing };\n }\n const messageId = MessageId.parse(`msg_${generateMessageId()}`);\n testPump.push(\n QUEUE_PATHNAMES[kind],\n {\n messageId,\n queueName,\n attempt: 1,\n message,\n idempotencyKey: opts?.idempotencyKey,\n },\n opts?.delaySeconds,\n );\n return { messageId };\n }\n\n // Production: enqueue into a QueueDO cell. The cell owns scheduling,\n // delivery, retries, dedup, and the dead-letter table.\n const messageId = MessageId.parse(`msg_${generateMessageId()}`);\n const shard = shardFor(opts?.idempotencyKey ?? messageId, queueShards);\n\n const outcome = await getQueueStub(shard).enqueue({\n messageId,\n queueName,\n pathname: QUEUE_PATHNAMES[kind],\n body: stringify(message),\n runId,\n idempotencyKey: opts?.idempotencyKey,\n delaySeconds: opts?.delaySeconds,\n config: {\n targetBaseUrl: resolveBaseUrl(config),\n queueShards,\n },\n });\n\n if (!outcome.ok) {\n if (outcome.code === 'RUN_EXPIRED') {\n throw new RunExpiredError(outcome.message);\n }\n throw new WorkflowWorldError(outcome.message, { status: 409 });\n }\n\n return { messageId: MessageId.parse(outcome.messageId) };\n },\n\n createQueueHandler(queueNamePrefix, handler) {\n // ONE dialect for the pump, QueueDO deliveries, and\n // @workflow/world-testing's mounted routes: x-vqs headers + tagged-JSON\n // body. The response taxonomy drives the sender's retry state machine:\n // - 2xx -> ack\n // - 503 + {timeoutSeconds} -> redeliver after N seconds (key stays claimed)\n // - 404/409/410/422 -> permanent, drop without retrying\n // - other non-2xx -> retry with capped backoff\n return async (req: Request) => {\n const reqQueueName = req.headers.get('x-vqs-queue-name');\n const reqMessageId = req.headers.get('x-vqs-message-id') as MessageId | null;\n const attemptStr = req.headers.get('x-vqs-message-attempt');\n\n if (!reqQueueName || !reqMessageId || !attemptStr || !req.body) {\n return Response.json({ error: 'Missing required headers or body' }, { status: 400 });\n }\n if (!reqQueueName.startsWith(queueNamePrefix)) {\n return Response.json({ error: 'Unhandled queue' }, { status: 400 });\n }\n const attempt = Number.parseInt(attemptStr, 10);\n try {\n // Tagged-JSON codec: revives Uint8Array payloads (runInput.input).\n const body = parse<unknown>(await req.text());\n const result = await handler(body, {\n attempt,\n queueName: reqQueueName,\n messageId: reqMessageId,\n });\n if (result && typeof result.timeoutSeconds === 'number') {\n return Response.json(\n { timeoutSeconds: result.timeoutSeconds },\n { status: 503, headers: { 'Retry-After': String(result.timeoutSeconds) } },\n );\n }\n return Response.json({ ok: true });\n } catch (error) {\n // Permanent vs transient distinction: permanent errors surface as\n // their own status so the sender drops instead of retrying.\n if (isPermanentError(error)) {\n debug('[world-celld queue handler] permanent error:', {\n status: error.status,\n error: String(error),\n });\n return Response.json(\n { error: String(error), permanent: true },\n { status: error.status },\n );\n }\n\n const backoffSeconds = computeBackoff(attempt);\n debug('[world-celld queue handler] transient error, will retry:', {\n error: String(error),\n backoffSeconds,\n });\n return Response.json(\n { error: String(error), retryAfter: backoffSeconds },\n { status: 500, headers: { 'Retry-After': String(backoffSeconds) } },\n );\n }\n };\n },\n\n async getDeploymentId() {\n return deploymentId;\n },\n\n async start() {\n if (isTestMode()) {\n await testPump.start();\n }\n // Production: QueueDO cells are push-based, nothing to start.\n },\n };\n}\n","import {\n EntityConflictError,\n HookNotFoundError,\n RunExpiredError,\n RunNotSupportedError,\n TooEarlyError,\n WorkflowRunNotFoundError,\n WorkflowWorldError,\n} from '@workflow/errors';\nimport type {\n CreateEventParams,\n CreateEventRequest,\n Event,\n EventResult,\n GetEventParams,\n GetHookParams,\n GetStepParams,\n GetWorkflowRunParams,\n Hook,\n ListEventsParams,\n ListHooksParams,\n ListWorkflowRunStepsParams,\n ListWorkflowRunsParams,\n PaginatedResponse,\n ResolveData,\n RunCreatedEventRequest,\n Step,\n StepWithoutData,\n Storage,\n WorkflowRun,\n WorkflowRunWithoutData,\n} from '@workflow/world';\nimport {\n EventSchema,\n HookSchema,\n SPEC_VERSION_CURRENT,\n StepSchema,\n WorkflowRunSchema,\n} from '@workflow/world';\nimport { parse, stringify } from './vendor/shared/index.js';\nimport { monotonicFactory } from 'ulid';\nimport type { ApplyEventFailure, ApplyEventOutcome, ApplyEventRequest } from './apply-event.js';\nimport type { HookTokenOwner, IndexNamespace } from './config.js';\nimport { compact } from './util.js';\nimport {\n correlationIndexKey,\n globalRunIndexKey,\n type RunReadOutcome,\n workflowRunIndexKey,\n} from './retention.js';\n\n/**\n * RPC surface of WorkflowRunDO used by the storage layer. Declared\n * structurally to avoid a circular type reference on the DO class.\n */\nexport interface WorkflowRunDOStub {\n applyEvent(request: ApplyEventRequest): Promise<ApplyEventOutcome>;\n getRun(): Promise<RunReadOutcome<WorkflowRun | null>>;\n getStep(stepId: string): Promise<RunReadOutcome<Step | null>>;\n getEvent(eventId: string): Promise<RunReadOutcome<Event | null>>;\n listEvents(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: 'asc' | 'desc';\n }): Promise<RunReadOutcome<{ data: Event[]; cursor: string | null; hasMore: boolean }>>;\n listSteps(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: 'asc' | 'desc';\n }): Promise<RunReadOutcome<{ data: Step[]; cursor: string | null; hasMore: boolean }>>;\n listHooks(params?: {\n limit?: number;\n cursor?: string;\n sortOrder?: 'asc' | 'desc';\n }): Promise<RunReadOutcome<{ data: Hook[]; cursor: string | null; hasMore: boolean }>>;\n getCleanupStatus(): Promise<import('./retention.js').CleanupRecord | null>;\n scheduleCleanup(\n request: import('./retention.js').ScheduleCleanupRequest,\n ): Promise<import('./retention.js').CleanupRecord | null>;\n cleanupNow(\n request: import('./retention.js').ScheduleCleanupRequest,\n ): Promise<import('./retention.js').CleanupRecord | null>;\n rearmCleanup(): Promise<import('./retention.js').CleanupRecord | null>;\n}\n\nexport interface WorkflowRunDONamespace {\n idFromName(name: string): DurableObjectIdLike;\n get(id: DurableObjectIdLike): WorkflowRunDOStub;\n}\n\nexport interface DurableObjectIdLike {\n toString(): string;\n}\n\nexport interface CloudflareStorageConfig {\n env: {\n WORKFLOW_DB: WorkflowRunDONamespace;\n WORKFLOW_INDEX: IndexNamespace;\n };\n deploymentId: string;\n runRetentionMs?: number;\n queueShards?: number;\n}\n\nfunction hookOwner(hook: Pick<Hook, 'runId' | 'hookId'>): HookTokenOwner {\n return { runId: hook.runId, hookId: hook.hookId };\n}\n\n/**\n * Filter data based on ResolveData parameter.\n * When resolveData is 'none', strips specified keys to reduce data transfer.\n */\nfunction filterData<T extends object>(\n data: T,\n resolveData: ResolveData | undefined,\n keysToStrip: (keyof T)[],\n): T {\n if (resolveData === 'none') {\n const newData = { ...data };\n for (const key of keysToStrip) {\n if (key in newData) {\n delete newData[key];\n }\n }\n return newData;\n }\n return data;\n}\n\n/**\n * Filter hook data based on resolveData parameter\n */\nfunction filterHookData(hook: Hook, resolveData: ResolveData): Hook {\n if (resolveData === 'none' && 'metadata' in hook) {\n const { metadata: _, ...rest } = hook;\n return { metadata: undefined, ...rest };\n }\n return hook;\n}\n\n/**\n * Convert a structured applyEvent failure back into the typed error the\n * runtime matches on (via error-name based `.is()` checks). Typed errors\n * cannot cross the DO RPC boundary intact, which is why the DO returns\n * outcome objects instead of throwing.\n */\nfunction throwOutcomeError(\n outcome: ApplyEventFailure,\n runId: string,\n data: CreateEventRequest | RunCreatedEventRequest,\n): never {\n switch (outcome.code) {\n case 'RUN_NOT_FOUND':\n throw new WorkflowRunNotFoundError(runId);\n case 'STEP_NOT_FOUND':\n throw new WorkflowWorldError(outcome.message, { status: 404 });\n case 'HOOK_NOT_FOUND':\n throw new HookNotFoundError(data.correlationId ?? runId);\n case 'ENTITY_CONFLICT':\n throw new EntityConflictError(outcome.message);\n case 'RUN_EXPIRED':\n throw new RunExpiredError(outcome.message);\n case 'TOO_EARLY':\n throw new TooEarlyError(outcome.message, { retryAfter: outcome.retryAfterSeconds });\n case 'RUN_NOT_SUPPORTED':\n throw new RunNotSupportedError(outcome.runSpecVersion ?? 0, SPEC_VERSION_CURRENT);\n case 'LEGACY_RUN_NOT_SUPPORTED':\n throw new WorkflowWorldError(outcome.message, { status: 422 });\n }\n}\n\nconst parseRun = (run: WorkflowRun): WorkflowRun => WorkflowRunSchema.parse(compact(run));\nconst parseStep = (step: Step): Step => StepSchema.parse(compact(step));\nconst parseHook = (hook: Hook): Hook => HookSchema.parse(compact(hook));\nconst parseEvent = (event: Event): Event => EventSchema.parse(compact(event));\n\nfunction unwrapRead<T>(outcome: RunReadOutcome<T>): T {\n if (!outcome.ok) {\n throw new RunExpiredError(outcome.message);\n }\n return outcome.value;\n}\n\nexport function createStorage(config: CloudflareStorageConfig): Storage {\n const { env } = config;\n const cleanup = {\n retentionMs: config.runRetentionMs ?? 0,\n queueShards: config.queueShards ?? 1,\n };\n const ulid = monotonicFactory();\n\n // Helper to get or create a DO for a run\n const getRunDO = (runId: string): WorkflowRunDOStub => {\n const id = env.WORKFLOW_DB.idFromName(runId);\n return env.WORKFLOW_DB.get(id);\n };\n\n const runsGet = async (\n runId: string,\n params?: GetWorkflowRunParams,\n ): Promise<WorkflowRun | WorkflowRunWithoutData> => {\n const stub = getRunDO(runId);\n const run = unwrapRead(await stub.getRun());\n\n if (!run) {\n throw new WorkflowRunNotFoundError(runId);\n }\n\n return filterData(parseRun(run), params?.resolveData, ['input', 'output']);\n };\n\n return {\n runs: {\n get: runsGet,\n\n async list(\n params?: ListWorkflowRunsParams,\n ): Promise<PaginatedResponse<WorkflowRun | WorkflowRunWithoutData>> {\n const limit = params?.pagination?.limit ?? 20;\n const prefix = params?.workflowName ? `run:${params.workflowName}:` : 'runall:';\n const reverse = params?.pagination?.sortOrder === 'desc';\n const matches: Array<{\n key: string;\n run: WorkflowRun | WorkflowRunWithoutData;\n }> = [];\n let scanCursor = params?.pagination?.cursor;\n let exhausted = false;\n\n // Keep scanning index pages until the requested page is full. This\n // prevents status filters and stale derived entries from producing\n // short pages or cursors that skip matching runs.\n while (matches.length <= limit && !exhausted) {\n const kvList = await env.WORKFLOW_INDEX.list({\n prefix,\n limit: Math.min(1000, Math.max(50, limit)),\n cursor: scanCursor,\n reverse,\n });\n if (kvList.keys.length === 0) {\n exhausted = true;\n break;\n }\n\n for (const key of kvList.keys) {\n const meta = await env.WORKFLOW_INDEX.get(key.name);\n if (!meta) continue;\n const { runId } = JSON.parse(meta) as { runId: string };\n try {\n const run = await runsGet(runId, { resolveData: params?.resolveData });\n if (!params?.status || run.status === params.status) {\n matches.push({ key: key.name, run });\n if (matches.length > limit) break;\n }\n } catch (error) {\n if (!WorkflowRunNotFoundError.is(error) && !RunExpiredError.is(error)) throw error;\n }\n }\n\n exhausted = kvList.list_complete;\n scanCursor = kvList.cursor ?? kvList.keys.at(-1)?.name;\n }\n\n const hasMore = matches.length > limit;\n const page = matches.slice(0, limit);\n\n return {\n data: page.map(({ run }) => run),\n cursor: hasMore ? (page.at(-1)?.key ?? null) : null,\n hasMore,\n };\n },\n } as Storage['runs'],\n\n events: {\n async create(\n runId: string | null,\n data: RunCreatedEventRequest | CreateEventRequest,\n _params?: CreateEventParams,\n ): Promise<EventResult> {\n // For run_created events, generate a runId server-side if absent.\n let effectiveRunId: string;\n if (data.eventType === 'run_created' && (!runId || runId === '')) {\n effectiveRunId = `wrun_${ulid()}`;\n } else if (!runId) {\n throw new WorkflowWorldError('runId is required for non-run_created events', {\n status: 400,\n });\n } else {\n effectiveRunId = runId;\n }\n\n const stub = getRunDO(effectiveRunId);\n\n let tokenHolder: ApplyEventRequest['tokenHolder'];\n let hookReservation: HookTokenOwner | undefined;\n if (data.eventType === 'hook_created') {\n hookReservation = {\n runId: effectiveRunId,\n hookId: data.correlationId,\n };\n const reservation = await env.WORKFLOW_INDEX.reserveHookToken(\n data.eventData.token,\n hookReservation,\n );\n tokenHolder = reservation.claimed ? null : reservation.holder;\n }\n\n // Guards, event append, and entity mutation run in ONE DO storage\n // transaction (see apply-event.ts). The event is schema-validated\n // before anything is persisted.\n let outcome: ApplyEventOutcome;\n try {\n outcome = await stub.applyEvent({ runId: effectiveRunId, data, tokenHolder, cleanup });\n } catch (error) {\n if (hookReservation && data.eventType === 'hook_created') {\n await env.WORKFLOW_INDEX.releaseHookToken(data.eventData.token, hookReservation);\n }\n throw error;\n }\n\n if (!outcome.ok) {\n if (hookReservation && data.eventType === 'hook_created') {\n await env.WORKFLOW_INDEX.releaseHookToken(data.eventData.token, hookReservation);\n }\n throwOutcomeError(outcome, effectiveRunId, data);\n }\n\n // Derived indexes are deliberately rewritten on idempotent replay so\n // a committed run or hook can repair an interrupted index update.\n if (outcome.run) {\n const meta = JSON.stringify({\n runId: effectiveRunId,\n createdAt: outcome.run.createdAt.toISOString(),\n status: outcome.run.status,\n });\n await env.WORKFLOW_INDEX.putOwned(effectiveRunId, workflowRunIndexKey(outcome.run), meta);\n await env.WORKFLOW_INDEX.putOwned(effectiveRunId, globalRunIndexKey(outcome.run), meta);\n }\n if (outcome.hookToIndex) {\n const serialized = stringify(outcome.hookToIndex);\n await env.WORKFLOW_INDEX.finalizeHookIndexes(\n outcome.hookToIndex.token,\n outcome.hookToIndex.hookId,\n serialized,\n hookOwner(outcome.hookToIndex),\n );\n } else if (hookReservation && data.eventType === 'hook_created') {\n await env.WORKFLOW_INDEX.releaseHookToken(data.eventData.token, hookReservation);\n }\n for (const released of outcome.releasedHooks) {\n await env.WORKFLOW_INDEX.deleteHookIndexes(released.token, released.hookId, {\n runId: effectiveRunId,\n hookId: released.hookId,\n });\n }\n if (outcome.event?.correlationId) {\n const correlationKey = correlationIndexKey(\n outcome.event.correlationId,\n outcome.event.createdAt,\n outcome.event.eventId,\n effectiveRunId,\n );\n await env.WORKFLOW_INDEX.putOwned(\n effectiveRunId,\n correlationKey,\n JSON.stringify({ runId: effectiveRunId, eventId: outcome.event.eventId }),\n );\n }\n\n return {\n event: outcome.event,\n run: outcome.run,\n step: outcome.step,\n hook: outcome.hook,\n events: outcome.events,\n };\n },\n\n async get(runId: string, eventId: string, _params?: GetEventParams): Promise<Event> {\n const stub = getRunDO(runId);\n const event = unwrapRead(await stub.getEvent(eventId));\n\n if (!event) {\n throw new WorkflowWorldError(`Event not found: ${eventId}`, {\n status: 404,\n });\n }\n\n return parseEvent(event);\n },\n\n async list(params: ListEventsParams): Promise<PaginatedResponse<Event>> {\n const { runId } = params;\n const limit = params?.pagination?.limit ?? 100;\n\n const stub = getRunDO(runId);\n const result = unwrapRead(\n await stub.listEvents({\n limit,\n cursor: params?.pagination?.cursor || undefined,\n sortOrder: params.pagination?.sortOrder || 'asc',\n }),\n );\n\n return {\n data: result.data.map(parseEvent),\n cursor: result.cursor,\n hasMore: result.hasMore,\n };\n },\n\n async listByCorrelationId(params) {\n const limit = params.pagination?.limit ?? 100;\n const prefix = `correlation:${encodeURIComponent(params.correlationId)}:`;\n const listed = await env.WORKFLOW_INDEX.list({\n prefix,\n limit,\n cursor: params.pagination?.cursor,\n reverse: params.pagination?.sortOrder === 'desc',\n });\n const events = await Promise.all(\n listed.keys.map(async ({ name }) => {\n const raw = await env.WORKFLOW_INDEX.get(name);\n if (!raw) return null;\n const { runId, eventId } = JSON.parse(raw) as { runId: string; eventId: string };\n const outcome = await getRunDO(runId).getEvent(eventId);\n return outcome.ok ? outcome.value : null;\n }),\n );\n return {\n data: events.filter((event): event is Event => event !== null).map(parseEvent),\n cursor: listed.list_complete ? null : (listed.cursor ?? null),\n hasMore: !listed.list_complete,\n };\n },\n },\n\n steps: {\n async get(runId: string | undefined, stepId: string, params?: GetStepParams) {\n if (!runId) {\n throw new WorkflowWorldError('runId is required for Cloudflare step lookup', {\n status: 400,\n });\n }\n const stub = getRunDO(runId);\n const step = unwrapRead(await stub.getStep(stepId));\n\n if (!step) {\n throw new WorkflowWorldError(`Step not found: ${stepId}`, {\n status: 404,\n });\n }\n\n return filterData(parseStep(step), params?.resolveData, ['input', 'output']);\n },\n\n async list(\n params: ListWorkflowRunStepsParams,\n ): Promise<PaginatedResponse<Step | StepWithoutData>> {\n const { runId } = params;\n const limit = params?.pagination?.limit ?? 20;\n\n const stub = getRunDO(runId);\n const result = unwrapRead(\n await stub.listSteps({\n limit,\n cursor: params?.pagination?.cursor || undefined,\n sortOrder: params?.pagination?.sortOrder ?? 'asc',\n }),\n );\n\n return {\n data: result.data.map((s) =>\n filterData(parseStep(s), params?.resolveData, ['input', 'output']),\n ),\n cursor: result.cursor,\n hasMore: result.hasMore,\n };\n },\n } as Storage['steps'],\n\n hooks: {\n async get(hookId: string, params?: GetHookParams) {\n const raw = await env.WORKFLOW_INDEX.get(`hookid:${hookId}`);\n\n if (!raw) {\n throw new HookNotFoundError(hookId);\n }\n\n const hook = parseHook(parse<Hook>(raw));\n return filterHookData(hook, params?.resolveData ?? 'all');\n },\n\n async getByToken(token: string, params?: GetHookParams) {\n const raw = await env.WORKFLOW_INDEX.get(`hook:${token}`);\n\n if (!raw) {\n throw new HookNotFoundError(token);\n }\n\n const hook = parseHook(parse<Hook>(raw));\n return filterHookData(hook, params?.resolveData ?? 'all');\n },\n\n async list(params: ListHooksParams): Promise<PaginatedResponse<Hook>> {\n if (!params.runId) {\n throw new WorkflowWorldError('runId is required for listing hooks', {\n status: 400,\n });\n }\n const runId = params.runId;\n const limit = params?.pagination?.limit ?? 100;\n\n const stub = getRunDO(runId);\n const result = unwrapRead(\n await stub.listHooks({\n limit,\n cursor: params?.pagination?.cursor || undefined,\n sortOrder: params?.pagination?.sortOrder ?? 'asc',\n }),\n );\n\n return {\n data: result.data.map((h) => filterHookData(parseHook(h), params?.resolveData ?? 'all')),\n cursor: result.cursor,\n hasMore: result.hasMore,\n };\n },\n },\n };\n}\n","import type {\n GetChunksOptions,\n StreamChunk,\n StreamChunksResponse,\n StreamInfoResponse,\n Streamer,\n} from '@workflow/world';\nimport type { ExpireRunStreamsResult, ExpireStreamResult } from './retention.js';\n\nexport interface CelldStreamerConfig {\n env: {\n WORKFLOW_STREAMS: StreamDONamespace;\n };\n /**\n * Poll interval (ms) while waiting for new chunks on a live stream.\n * Modified vs upstream: configurable because each poll is a fleet HTTP\n * round-trip here, not an in-process DO call. Default: 100.\n */\n readPollMs?: number;\n}\n\n/**\n * RPC surface of StreamDO (see durable-objects/StreamDO.ts). The streamer\n * talks to the DO exclusively via these methods — there is no fetch()\n * protocol.\n */\nexport interface StreamDOStub {\n writeChunk(runId: string, data: Uint8Array): Promise<number>;\n closeStream(runId: string): Promise<void>;\n getChunks(params: {\n startIndex: number;\n limit: number;\n }): Promise<{ chunks: Uint8Array[]; done: boolean; tailIndex: number }>;\n getInfo(): Promise<{ tailIndex: number; done: boolean }>;\n registerStream(runId: string, name: string): Promise<void>;\n listStreams(): Promise<string[]>;\n expireRegistry(runId: string, expiredAt: number): Promise<ExpireRunStreamsResult>;\n finalizeRegistry(runId: string): Promise<void>;\n expireStream(runId: string, expiredAt: number): Promise<ExpireStreamResult>;\n}\n\nexport interface StreamDONamespace {\n idFromName(name: string): StreamDOId;\n get(id: StreamDOId): StreamDOStub;\n}\n\nexport interface StreamDOId {\n toString(): string;\n}\n\n/** Default poll interval while waiting for new chunks on a live stream. */\nconst READ_POLL_MS = 100;\n/** Chunks fetched per DO round-trip while reading. */\nconst READ_BATCH_SIZE = 32;\n/** Default / maximum page sizes for getStreamChunks. */\nconst DEFAULT_CHUNK_PAGE_SIZE = 32;\nconst MAX_CHUNK_PAGE_SIZE = 32;\n\nfunction toBytes(chunk: string | Uint8Array): Uint8Array {\n return typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk;\n}\n\nexport function createStreamer(config: CelldStreamerConfig): Streamer {\n const { env } = config;\n const readPollMs = config.readPollMs ?? READ_POLL_MS;\n\n const getStreamDO = (streamName: string): StreamDOStub => {\n const id = env.WORKFLOW_STREAMS.idFromName(`stream:${streamName}`);\n return env.WORKFLOW_STREAMS.get(id);\n };\n\n const getRunRegistryDO = (runId: string): StreamDOStub => {\n const id = env.WORKFLOW_STREAMS.idFromName(`run-streams:${runId}`);\n return env.WORKFLOW_STREAMS.get(id);\n };\n\n /** Per-isolate cache so each (runId, stream) pair registers only once. */\n const registeredStreams = new Set<string>();\n\n async function registerStreamForRun(name: string, runId: string): Promise<void> {\n const cacheKey = `${runId}\\0${name}`;\n if (registeredStreams.has(cacheKey)) return;\n await getRunRegistryDO(runId).registerStream(runId, name);\n registeredStreams.add(cacheKey);\n }\n\n return {\n async writeToStream(name: string, runId: string | Promise<string>, chunk: string | Uint8Array) {\n const resolvedRunId = await runId;\n await registerStreamForRun(name, resolvedRunId);\n await getStreamDO(name).writeChunk(resolvedRunId, toBytes(chunk));\n },\n\n async closeStream(name: string, runId: string | Promise<string>) {\n const resolvedRunId = await runId;\n await registerStreamForRun(name, resolvedRunId);\n await getStreamDO(name).closeStream(resolvedRunId);\n },\n\n async readFromStream(name: string, startIndex = 0) {\n const stub = getStreamDO(name);\n\n // Negative startIndex counts back from the current end, clamped to 0.\n let nextIndex: number;\n if (startIndex < 0) {\n const info = await stub.getInfo();\n nextIndex = Math.max(0, info.tailIndex + 1 + startIndex);\n } else {\n nextIndex = startIndex;\n }\n\n let cancelled = false;\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n // Loop until we can enqueue data, close, or the reader cancels.\n // Errors from the DO propagate and error the stream — readers must\n // never see a silently-truncated stream.\n while (true) {\n if (cancelled) return;\n const { chunks, done } = await stub.getChunks({\n startIndex: nextIndex,\n limit: READ_BATCH_SIZE,\n });\n if (chunks.length > 0) {\n for (const chunk of chunks) {\n controller.enqueue(chunk);\n }\n nextIndex += chunks.length;\n return;\n }\n if (done) {\n controller.close();\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, readPollMs));\n }\n },\n\n cancel() {\n cancelled = true;\n },\n });\n },\n\n async listStreamsByRunId(runId: string): Promise<string[]> {\n return getRunRegistryDO(runId).listStreams();\n },\n\n async getStreamChunks(\n name: string,\n _runId: string,\n options?: GetChunksOptions,\n ): Promise<StreamChunksResponse> {\n const limit = Math.min(options?.limit ?? DEFAULT_CHUNK_PAGE_SIZE, MAX_CHUNK_PAGE_SIZE);\n const startIndex = options?.cursor ? Number.parseInt(options.cursor, 10) : 0;\n if (Number.isNaN(startIndex) || startIndex < 0) {\n throw new Error(`Invalid stream cursor: ${options?.cursor}`);\n }\n\n const stub = getStreamDO(name);\n const result = await stub.getChunks({ startIndex, limit: limit + 1 });\n const hasMore = result.chunks.length > limit;\n const data: StreamChunk[] = result.chunks\n .slice(0, limit)\n .map((chunk, offset) => ({ index: startIndex + offset, data: chunk }));\n\n return {\n data,\n cursor: hasMore ? String(startIndex + limit) : null,\n hasMore,\n done: result.done,\n };\n },\n\n async getStreamInfo(name: string, _runId: string): Promise<StreamInfoResponse> {\n return getStreamDO(name).getInfo();\n },\n };\n}\n","/**\n * @ewhauser/world-celld — celld World implementation for the Vercel Workflow\n * DevKit. Modeled on vinnymac/worlds packages/world-cloudflare/src/index.ts\n * (Apache-2.0, see NOTICE).\n */\nimport { SPEC_VERSION_CURRENT, type World } from '@workflow/world';\nimport { type CelldWorldConfig, type CelldWorldEnv, resolveConfig } from './config.js';\nimport { createRemoteEnv } from './remote/namespaces.js';\nimport { createQueue } from './queue.js';\nimport { createStorage } from './storage.js';\nimport { createStreamer } from './streamer.js';\nimport type { CleanupRecord } from './retention.js';\n\nexport type { CelldWorldConfig, CelldWorldEnv, IndexNamespace } from './config.js';\nexport type {\n EnqueueOutcome,\n EnqueueRequest,\n QueueCellConfig,\n QueueCellNamespace,\n QueueCellStub,\n} from './queue.js';\nexport type { WorkflowRunDONamespace, WorkflowRunDOStub } from './storage.js';\nexport type { StreamDONamespace, StreamDOStub } from './streamer.js';\nexport type { CleanupPhase, CleanupRecord, RunTombstone } from './retention.js';\n\nexport interface CelldRetentionAdmin {\n getStatus(runId: string): Promise<CleanupRecord | null>;\n schedule(runId: string): Promise<CleanupRecord | null>;\n cleanupNow(runId: string): Promise<CleanupRecord | null>;\n rearm(runId: string): Promise<CleanupRecord | null>;\n}\n\nexport type CelldWorld = World & { retention: CelldRetentionAdmin };\n\nexport function createCelldWorld(config?: CelldWorldConfig): CelldWorld {\n const resolved = resolveConfig(config);\n\n // Check for global test environment first (for @workflow/world-testing)\n let env = resolved.env;\n if (!env) {\n const globalEnv = (globalThis as { CELLD_ENV?: CelldWorldEnv }).CELLD_ENV;\n if (globalEnv) {\n env = globalEnv;\n }\n }\n\n if (!env) {\n if (resolved.fleetUrl) {\n if (!resolved.secret) {\n throw new Error(\n 'world-celld: config.secret (or CELLD_WORLD_SECRET) is required with fleetUrl',\n );\n }\n env = createRemoteEnv({\n fleetUrl: resolved.fleetUrl,\n secret: resolved.secret,\n timeoutMs: resolved.rpcTimeoutMs,\n });\n }\n }\n\n if (!env) {\n throw new Error(\n 'celld environment not configured. Provide config.fleetUrl + config.secret ' +\n '(or CELLD_FLEET_URL / CELLD_WORLD_SECRET), or pass config.env with ' +\n 'WORKFLOW_DB, WORKFLOW_INDEX, WORKFLOW_QUEUE, WORKFLOW_STREAMS',\n );\n }\n\n const storage = createStorage({\n env: {\n WORKFLOW_DB: env.WORKFLOW_DB,\n WORKFLOW_INDEX: env.WORKFLOW_INDEX,\n },\n deploymentId: resolved.deploymentId,\n runRetentionMs: resolved.runRetentionMs,\n queueShards: resolved.queueShards,\n });\n\n const queue = createQueue({\n env: {\n WORKFLOW_QUEUE: env.WORKFLOW_QUEUE,\n },\n deploymentId: resolved.deploymentId,\n baseUrl: resolved.baseUrl,\n queueShards: resolved.queueShards,\n });\n\n const streamer = createStreamer({\n env: {\n WORKFLOW_STREAMS: env.WORKFLOW_STREAMS,\n },\n readPollMs: resolved.readPollMs,\n });\n\n const runStub = (runId: string) => env.WORKFLOW_DB.get(env.WORKFLOW_DB.idFromName(runId));\n const retention: CelldRetentionAdmin = {\n getStatus: (runId) => runStub(runId).getCleanupStatus(),\n schedule: (runId) => {\n if (resolved.runRetentionMs === 0) {\n throw new Error(\n 'world-celld: runRetentionMs must be greater than zero to schedule cleanup',\n );\n }\n return runStub(runId).scheduleCleanup({\n retentionMs: resolved.runRetentionMs,\n queueShards: resolved.queueShards,\n });\n },\n cleanupNow: (runId) =>\n runStub(runId).cleanupNow({\n retentionMs: Math.max(1, resolved.runRetentionMs),\n queueShards: resolved.queueShards,\n }),\n rearm: (runId) => runStub(runId).rearmCleanup(),\n };\n\n return {\n ...storage,\n ...queue,\n ...streamer,\n retention,\n // Enables resilient start: runs are created at the current spec version,\n // so the queue message carries runInput and run_started can bootstrap the\n // run when run_created has not landed yet.\n specVersion: SPEC_VERSION_CURRENT,\n };\n}\n\n// Export createWorld as an alias for compatibility with @workflow/world\nexport { createCelldWorld as createWorld };\n"],"mappings":"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"}
@@ -117,6 +117,48 @@ function compact(obj) {
117
117
  return value;
118
118
  }
119
119
  //#endregion
120
- export { stringify as a, parse as i, rpcParse as n, rpcStringify as r, compact as t };
120
+ //#region src/retention.ts
121
+ const CLEANUP_RECORD_KEY = "retention:cleanup";
122
+ const TOMBSTONE_KEY = "retention:tombstone";
123
+ const INDEX_MARKER_PREFIX = "retention:index:";
124
+ const HOOK_MARKER_PREFIX = "retention:hook:";
125
+ function sortableTimestamp(date) {
126
+ return date.getTime().toString().padStart(13, "0");
127
+ }
128
+ function workflowRunIndexKey(run) {
129
+ return `run:${run.workflowName}:${sortableTimestamp(run.createdAt)}:${run.runId}`;
130
+ }
131
+ function globalRunIndexKey(run) {
132
+ return `runall:${sortableTimestamp(run.createdAt)}:${run.runId}`;
133
+ }
134
+ function correlationIndexKey(correlationId, createdAt, eventId, runId) {
135
+ return `correlation:${encodeURIComponent(correlationId)}:${sortableTimestamp(createdAt)}:${eventId}:${runId}`;
136
+ }
137
+ function indexMarkerKey(indexKey) {
138
+ return `${INDEX_MARKER_PREFIX}${encodeURIComponent(indexKey)}`;
139
+ }
140
+ function hookMarkerKey(reference) {
141
+ return `${HOOK_MARKER_PREFIX}${encodeURIComponent(reference.hookId)}:${encodeURIComponent(reference.token)}`;
142
+ }
143
+ function cleanupTombstone(record, now) {
144
+ return {
145
+ version: 1,
146
+ runId: record.runId,
147
+ terminalStatus: record.terminalStatus,
148
+ completedAt: record.completedAt,
149
+ expiredAt: record.dueAt,
150
+ tombstonedAt: now
151
+ };
152
+ }
153
+ function expiredRead(tombstone) {
154
+ return {
155
+ ok: false,
156
+ code: "RUN_EXPIRED",
157
+ message: `Workflow run "${tombstone.runId}" expired at ${tombstone.expiredAt.toISOString()}`,
158
+ tombstone
159
+ };
160
+ }
161
+ //#endregion
162
+ export { cleanupTombstone as a, globalRunIndexKey as c, workflowRunIndexKey as d, compact as f, stringify as g, parse as h, TOMBSTONE_KEY as i, hookMarkerKey as l, rpcStringify as m, HOOK_MARKER_PREFIX as n, correlationIndexKey as o, rpcParse as p, INDEX_MARKER_PREFIX as r, expiredRead as s, CLEANUP_RECORD_KEY as t, indexMarkerKey as u };
121
163
 
122
- //# sourceMappingURL=util-BvWlkqgV.js.map
164
+ //# sourceMappingURL=retention-bLY1vcOu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-bLY1vcOu.js","names":[],"sources":["../src/vendor/shared/serialization.ts","../src/codec.ts","../src/vendor/shared/util.ts","../src/retention.ts"],"sourcesContent":["/** Standard Date fields used across all worlds */\nexport const DATE_FIELDS = new Set([\n 'createdAt',\n 'updatedAt',\n 'startedAt',\n 'completedAt',\n 'retryAfter',\n] as const);\n\n/**\n * JSON reviver that converts ISO date strings to Date objects.\n * Use with JSON.parse(json, dateReviver).\n */\nexport function dateReviver(key: string, value: unknown): unknown {\n if (\n DATE_FIELDS.has(key as typeof DATE_FIELDS extends Set<infer T> ? T : never) &&\n typeof value === 'string'\n ) {\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? value : date;\n }\n return value;\n}\n\n/**\n * Base64 helpers. Modified vs upstream: implemented over atob/btoa instead of\n * Buffer so this module is safe inside the celld worker bundle (workerd has\n * no Buffer global); atob/btoa are global in both workerd and Node >= 16.\n */\nexport function b64encode(bytes: Uint8Array): string {\n let bin = '';\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode(...bytes.subarray(i, i + CHUNK));\n }\n return btoa(bin);\n}\n\nexport function b64decode(text: string): Uint8Array {\n const bin = atob(text);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) {\n out[i] = bin.charCodeAt(i);\n }\n return out;\n}\n\n/**\n * JSON replacer that encodes Uint8Array as a tagged object with base64 data.\n */\nexport function uint8ArrayReplacer(_key: string, value: unknown): unknown {\n if (value instanceof Uint8Array) {\n return {\n __type: 'Uint8Array',\n data: b64encode(value),\n };\n }\n return value;\n}\n\n/**\n * JSON reviver that decodes tagged Uint8Array objects and ISO date strings.\n * Supports both:\n * - New format: { __type: 'Uint8Array', data: '<base64>' }\n * - Legacy format: { __uint8array: true, data: [1, 2, 3] }\n */\nexport function uint8ArrayReviver(key: string, value: unknown): unknown {\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n // New format: base64-encoded\n if (obj.__type === 'Uint8Array' && typeof obj.data === 'string') {\n return b64decode(obj.data);\n }\n // Legacy format: number array (backwards compat with NATS JetStream data)\n if (obj.__uint8array === true && Array.isArray(obj.data)) {\n return new Uint8Array(obj.data as number[]);\n }\n }\n return dateReviver(key, value);\n}\n\n/** Stringify with Uint8Array support. */\nexport function stringify(value: unknown): string {\n return JSON.stringify(value, uint8ArrayReplacer);\n}\n\n/** Parse with Uint8Array and Date support. */\nexport function parse<T>(text: string): T {\n return JSON.parse(text, uint8ArrayReviver) as T;\n}\n\n/** Deep clone using structuredClone */\nexport function deepClone<T>(value: T): T {\n return structuredClone(value);\n}\n","/**\n * RPC wire codec for the Node client <-> worker router HTTP boundary.\n *\n * Extends the shared tagged-JSON format (`{__type:'Uint8Array', data}` +\n * DATE_FIELDS revival) with explicitly tagged Dates: on Cloudflare the DO RPC\n * boundary is structured clone, which preserves EVERY Date — including ones\n * outside the well-known DATE_FIELDS (e.g. inside applyEvent outcomes and\n * user payloads). The HTTP hop must preserve them too, so Dates travel as\n * `{__type:'Date', iso}`.\n *\n * Runs on both sides of the wire, including inside the celld worker — so no\n * Node built-ins (Buffer): base64 goes through atob/btoa.\n */\nimport { b64decode, b64encode, dateReviver } from './vendor/shared/serialization.js';\n\nfunction rpcReplacer(this: unknown, key: string, value: unknown): unknown {\n // JSON.stringify invokes Date.toJSON before the replacer sees the value;\n // recover the original from the holder object.\n const original =\n this && typeof this === 'object' ? (this as Record<string, unknown>)[key] : undefined;\n if (original instanceof Date) {\n return { __type: 'Date', iso: original.toISOString() };\n }\n if (value instanceof Uint8Array) {\n return { __type: 'Uint8Array', data: b64encode(value) };\n }\n return value;\n}\n\nfunction rpcReviver(key: string, value: unknown): unknown {\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n if (obj.__type === 'Date' && typeof obj.iso === 'string') {\n return new Date(obj.iso);\n }\n if (obj.__type === 'Uint8Array' && typeof obj.data === 'string') {\n return b64decode(obj.data);\n }\n }\n // Fallback shared-codec semantics for untagged ISO strings in DATE_FIELDS.\n return dateReviver(key, value);\n}\n\nexport function rpcStringify(value: unknown): string {\n return JSON.stringify(value, rpcReplacer);\n}\n\nexport function rpcParse<T>(text: string): T {\n return JSON.parse(text, rpcReviver) as T;\n}\n","export class Mutex {\n promise: Promise<unknown> = Promise.resolve();\n andThen<T>(fn: () => Promise<T> | T): Promise<T> {\n this.promise = this.promise.then(\n () => fn(),\n () => fn(),\n );\n return this.promise as Promise<T>;\n }\n}\n\nexport function compact<T extends object>(obj: T) {\n const value = {} as {\n [key in keyof T]: null extends T[key] ? undefined | NonNullable<T[key]> : T[key];\n };\n for (const key in obj) {\n if (obj[key] !== null) {\n value[key] = obj[key] as any;\n } else {\n value[key] = undefined as any;\n }\n }\n return value;\n}\n\n/**\n * Reference counting helper for managing resource lifecycle\n */\nexport class Rc {\n private count = 0;\n private disposeFn?: () => void | Promise<void>;\n\n constructor(disposeFn?: () => void | Promise<void>) {\n this.disposeFn = disposeFn;\n }\n\n inc(): void {\n this.count++;\n }\n\n async dec(): Promise<void> {\n this.count--;\n if (this.count === 0 && this.disposeFn) {\n await this.disposeFn();\n }\n }\n\n getCount(): number {\n return this.count;\n }\n}\n","import type { TerminalWorkflowRunStatus, WorkflowRun } from '@workflow/world';\n\nexport const CLEANUP_RECORD_KEY = 'retention:cleanup';\nexport const TOMBSTONE_KEY = 'retention:tombstone';\nexport const INDEX_MARKER_PREFIX = 'retention:index:';\nexport const HOOK_MARKER_PREFIX = 'retention:hook:';\n\nexport type CleanupPhase = 'retained' | 'index' | 'streams' | 'queues' | 'payload' | 'tombstoned';\n\nexport interface CleanupRecord {\n version: 1;\n runId: string;\n workflowName: string;\n createdAt: Date;\n completedAt: Date;\n terminalStatus: TerminalWorkflowRunStatus;\n dueAt: Date;\n queueShards: number;\n phase: CleanupPhase;\n attempts: number;\n lastError?: string;\n lastAttemptAt?: Date;\n tombstonedAt?: Date;\n deletedPayloadKeys: number;\n deletedStreams: number;\n deletedQueueMessages: number;\n}\n\nexport interface RunTombstone {\n version: 1;\n runId: string;\n terminalStatus: TerminalWorkflowRunStatus;\n completedAt: Date;\n expiredAt: Date;\n tombstonedAt: Date;\n}\n\nexport type RunReadOutcome<T> =\n | { ok: true; value: T }\n | { ok: false; code: 'RUN_EXPIRED'; message: string; tombstone: RunTombstone };\n\nexport interface HookIndexReference {\n hookId: string;\n token: string;\n}\n\nexport interface ExpireRunIndexesRequest {\n runId: string;\n keys: string[];\n hooks: HookIndexReference[];\n expiredAt: number;\n}\n\nexport interface ExpireRunIndexesResult {\n deleted: number;\n}\n\nexport interface ExpireRunStreamsResult {\n streams: string[];\n}\n\nexport interface ExpireStreamResult {\n deleted: boolean;\n chunks: number;\n}\n\nexport interface ExpireQueueRunResult {\n deleted: number;\n}\n\nexport interface ScheduleCleanupRequest {\n retentionMs: number;\n queueShards: number;\n}\n\nexport function sortableTimestamp(date: Date): string {\n return date.getTime().toString().padStart(13, '0');\n}\n\nexport function workflowRunIndexKey(\n run: Pick<WorkflowRun, 'workflowName' | 'createdAt' | 'runId'>,\n) {\n return `run:${run.workflowName}:${sortableTimestamp(run.createdAt)}:${run.runId}`;\n}\n\nexport function globalRunIndexKey(run: Pick<WorkflowRun, 'createdAt' | 'runId'>) {\n return `runall:${sortableTimestamp(run.createdAt)}:${run.runId}`;\n}\n\nexport function correlationIndexKey(\n correlationId: string,\n createdAt: Date,\n eventId: string,\n runId: string,\n): string {\n return `correlation:${encodeURIComponent(correlationId)}:${sortableTimestamp(createdAt)}:${eventId}:${runId}`;\n}\n\nexport function indexMarkerKey(indexKey: string): string {\n return `${INDEX_MARKER_PREFIX}${encodeURIComponent(indexKey)}`;\n}\n\nexport function hookMarkerKey(reference: HookIndexReference): string {\n return `${HOOK_MARKER_PREFIX}${encodeURIComponent(reference.hookId)}:${encodeURIComponent(reference.token)}`;\n}\n\nexport function cleanupTombstone(record: CleanupRecord, now: Date): RunTombstone {\n return {\n version: 1,\n runId: record.runId,\n terminalStatus: record.terminalStatus,\n completedAt: record.completedAt,\n expiredAt: record.dueAt,\n tombstonedAt: now,\n };\n}\n\nexport function expiredRead<T>(tombstone: RunTombstone): RunReadOutcome<T> {\n return {\n ok: false,\n code: 'RUN_EXPIRED',\n message: `Workflow run \"${tombstone.runId}\" expired at ${tombstone.expiredAt.toISOString()}`,\n tombstone,\n };\n}\n"],"mappings":"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"}
package/dist/testing.d.ts CHANGED
@@ -117,6 +117,8 @@ interface Harness {
117
117
  }
118
118
  interface HarnessOptions {
119
119
  secret?: string;
120
+ /** Use FakeFleet's manually advanced clock instead of wall-clock time. */
121
+ virtualClock?: boolean;
120
122
  /** Additional cell classes by binding key (e.g. { queue: QueueDO }). */
121
123
  extraClasses?: Record<string, new (ctx: unknown, env: unknown) => object>;
122
124
  /** env passed to cell constructors (celld `vars`). */
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing/fake-cell.ts","../src/testing/http-harness.ts","../src/testing/index.ts"],"mappings":";;;;;;;UAOiB;EACf;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;;;;;;;cAmCW;UAQS;EAPpB,MAAI;EACJ;UACQ;EAKY,YAAA;EAEd,IAAI,GAAG,cAAc,QAAQ;EAI7B,IAAI,GAAG,aAAa,OAAO,IAAI;EAO/B,OAAO,cAAc;EAKrB,aAAa;EAIb,KAAK,GAAG,UAAS,kBAAuB,QAAQ,YAAY;EAK5D,YAAY,GAAG,KAAK,KAAK,oBAAoB,QAAQ,KAAK,QAAQ;EAOlE,YAAY;EAIZ,SAAS,aAAa,OAAO;EAI7B,eAAe;EAIrB;;EAKA,iBACE,YAAY,UAAU,iCACtB,QAAK;;EAMP,kBAAkB,UAAU;;cAQxB;UAKgB;UAJZ;UACA;UACA;EAEY,YAAA,SAAS;EAEvB,IAAI,GAAG,cAAc,QAAQ;EAM7B,IAAI,GAAG,aAAa,OAAO,IAAI;EAM/B,OAAO,cAAc;EAQrB,KAAK,GAAG,UAAS,kBAAuB,QAAQ,YAAY;EAQ5D,YAAY;EAIZ,SAAS,aAAa,OAAO;EAI7B,eAAe;EAIrB;;KAOG,iBACH,cACA;EAEA,OAAO;IAAS;MAAwB;;UAGhC;EACR,UAAU,aAAa;EACvB,SAAS;EACT,cAAc;EACd;EACA;;;;;;;;cAYW;UAKD;UACA;UALF;EACR;EAGU,YAAA,SAAS,eAAe,YACxB,UAAS,yBACjB;EAKF,UAAU;IAEW,WAAA;;;IAGP,MAAA;MAAE;;MArCF,OAAA;QAAE;UAAwB;;;EAyCxC,KAAK,oBAAoB,eAAe;EA8BxC,QAAQ;;;;;;;EAUF,iBAAiB;UAsBT;;EAQR,UAAU;;;;UC7RD;EACf;EACA,OAAO;EACP,SAAS;;UAGM;EACf;;EAEA,eAAe,oBAAoB,cAAc;;EAEjD,UAAU;;iBAGU,aAAa,UAAS,iBAAsB,QAAQ;;;UCdzD,iBAAiB;EAChC,QAAQ;;iBAMY,cAAc,UAAS,iBAAsB,QAAQ"}
1
+ {"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing/fake-cell.ts","../src/testing/http-harness.ts","../src/testing/index.ts"],"mappings":";;;;;;;UAOiB;EACf;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;;;;;;;cAmCW;UAQS;EAPpB,MAAI;EACJ;UACQ;EAKR,YAAoB;EAEd,IAAI,GAAG,cAAc,QAAQ;EAI7B,IAAI,GAAG,aAAa,OAAO,IAAI;EAO/B,OAAO,cAAc;EAKrB,aAAa;EAIb,KAAK,GAAG,UAAS,kBAAuB,QAAQ,YAAY;EAK5D,YAAY,GAAG,KAAK,KAAK,oBAAoB,QAAQ,KAAK,QAAQ;EAOlE,YAAY;EAIZ,SAAS,aAAa,OAAO;EAI7B,eAAe;EAIrB;;EAKA,iBACE,YAAY,UAAU,iCACtB,QAAK;;EAMP,kBAAkB,UAAU;;cAQxB;UAKgB;UAJZ;UACA;UACA;EAER,YAAoB,SAAS;EAEvB,IAAI,GAAG,cAAc,QAAQ;EAM7B,IAAI,GAAG,aAAa,OAAO,IAAI;EAM/B,OAAO,cAAc;EAQrB,KAAK,GAAG,UAAS,kBAAuB,QAAQ,YAAY;EAQ5D,YAAY;EAIZ,SAAS,aAAa,OAAO;EAI7B,eAAe;EAIrB;;KAOG,iBACH,cACA;EAEA,OAAO;IAAS;MAAwB;;UAGhC;EACR,UAAU,aAAa;EACvB,SAAS;EACT,cAAc;EACd;EACA;;;;;;;;cAYW;UAKD;UACA;UALF;EACR;EAEA,YACU,SAAS,eAAe,YACxB,UAAS,yBACjB;EAKF,UAAU;IAEN,WAAU;MACC;;IAEX,MAAG;MAAS;;MArCF,OAAA;QAAE;UAAwB;;;EAyCxC,KAAK,oBAAoB,eAAe;EA8BxC,QAAQ;;;;;;;EAUF,iBAAiB;UAsBT;;EAQR,UAAU;;;;UC7RD;EACf;EACA,OAAO;EACP,SAAS;;UAGM;EACf;;EAEA;;EAEA,eAAe,oBAAoB,cAAc;;EAEjD,UAAU;;iBAGU,aAAa,UAAS,iBAAsB,QAAQ;;;UChBzD,iBAAiB;EAChC,QAAQ;;iBAMY,cAAc,UAAS,iBAAsB,QAAQ"}