@ewhauser/world-celld 0.2.0 → 0.3.1
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/CHANGELOG.md +25 -0
- package/README.md +47 -19
- package/celld-worker/README.md +5 -3
- package/celld-worker/worker.ts +10 -1
- package/celld-worker/wrangler.jsonc +15 -4
- package/dist/index.d.ts +163 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +524 -197
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +47 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1874 -582
- package/dist/testing.js.map +1 -1
- package/dist/util-DmO0XkFw.js +545 -0
- package/dist/util-DmO0XkFw.js.map +1 -0
- package/dist/worker.d.ts +223 -111
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +1960 -551
- package/dist/worker.js.map +1 -1
- package/package.json +7 -5
- package/dist/retention-bLY1vcOu.js +0 -164
- package/dist/retention-bLY1vcOu.js.map +0 -1
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 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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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["RETRYABLE_STATUSES","DEFAULT_TIMEOUT_MS","delayMs","delay"],"sources":["../src/config.ts","../src/remote/errors.ts","../src/remote/rpc-client.ts","../src/remote/stream-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';\nimport { MAX_STREAM_LONG_POLL_MS } from './stream-protocol.js';\nimport type { WorkflowIndex } from './indexes.js';\n\n/** Public alias retained for custom in-process environments. */\nexport type IndexNamespace = WorkflowIndex;\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`.\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 /** Duration of one bounded idle stream read. Default: 20000 */\n streamLongPollMs?: number;\n /** Runtime stream batching delay. Default: 0 */\n streamFlushIntervalMs?: 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 streamLongPollMs: number;\n streamFlushIntervalMs: 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 const rpcTimeoutMs = config?.rpcTimeoutMs ?? 30_000;\n if (!Number.isSafeInteger(rpcTimeoutMs) || rpcTimeoutMs < 1) {\n throw new Error('world-celld: rpcTimeoutMs must be a positive safe integer');\n }\n const streamLongPollMs =\n config?.streamLongPollMs ??\n Math.min(MAX_STREAM_LONG_POLL_MS, Math.max(1, rpcTimeoutMs - 1_000));\n if (\n !Number.isSafeInteger(streamLongPollMs) ||\n streamLongPollMs < 1 ||\n streamLongPollMs > MAX_STREAM_LONG_POLL_MS\n ) {\n throw new Error(\n `world-celld: streamLongPollMs must be between 1 and ${MAX_STREAM_LONG_POLL_MS}`,\n );\n }\n if (streamLongPollMs >= rpcTimeoutMs) {\n throw new Error('world-celld: streamLongPollMs must be less than rpcTimeoutMs');\n }\n const streamFlushIntervalMs = config?.streamFlushIntervalMs ?? 0;\n if (!Number.isSafeInteger(streamFlushIntervalMs) || streamFlushIntervalMs < 0) {\n throw new Error('world-celld: streamFlushIntervalMs 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 streamLongPollMs,\n streamFlushIntervalMs,\n rpcTimeoutMs,\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 return await callFleetRoute(\n transport,\n `/v1/rpc/${binding}/${encodeURIComponent(name)}/${method}`,\n args,\n opts,\n );\n}\n\n/** Invoke a fixed authenticated worker route using the generic RPC codec. */\nexport async function callFleetRoute<T>(\n transport: RpcTransport,\n path: string,\n args: unknown[],\n opts?: { idempotent?: boolean },\n): Promise<T> {\n const doFetch = transport.fetchImpl ?? fetch;\n const url = `${transport.fleetUrl.replace(/\\/$/, '')}${path}`;\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","import {\n MAX_STREAM_BATCH_BYTES,\n MAX_STREAM_READ_BYTES,\n STREAM_BATCH_CONTENT_TYPE,\n decodeStreamReadResult,\n decodeStreamWriteResult,\n encodeStreamWriteBatch,\n type StreamReadRequest,\n type StreamReadResult,\n type StreamWriteResult,\n} from '../stream-protocol.js';\nimport { FleetTransportError, reconstructError, type WireError } from './errors.js';\nimport type { RpcTransport } from './rpc-client.js';\n\nconst RETRYABLE_STATUSES = new Set([502, 503, 504]);\nconst READ_ATTEMPTS = 3;\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_WRITE_RESPONSE_BYTES = 64;\nconst MAX_READ_RESPONSE_BYTES = MAX_STREAM_READ_BYTES + 64 * 1024;\nconst MAX_ERROR_RESPONSE_BYTES = 64 * 1024;\n\nfunction delayMs(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction requestSignal(timeoutMs: number, signal?: AbortSignal): AbortSignal {\n const timeout = AbortSignal.timeout(Math.max(1, timeoutMs));\n return signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\nfunction aborted(signal: AbortSignal): unknown {\n return signal.reason ?? new DOMException('The operation was aborted', 'AbortError');\n}\n\nasync function readBoundedResponse(response: Response, maxBytes: number): Promise<Uint8Array> {\n const contentLength = Number(response.headers.get('content-length') ?? '0');\n if (contentLength > maxBytes) {\n await response.body?.cancel('stream response exceeds configured limit').catch(() => undefined);\n throw new FleetTransportError(`world-celld: stream response exceeds ${maxBytes} bytes`);\n }\n if (!response.body) return new Uint8Array();\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel('stream response exceeds configured limit').catch(() => undefined);\n throw new FleetTransportError(`world-celld: stream response exceeds ${maxBytes} bytes`);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const body = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return body;\n}\n\nasync function errorFromResponse(response: Response): Promise<Error> {\n const text = new TextDecoder().decode(\n await readBoundedResponse(response, MAX_ERROR_RESPONSE_BYTES),\n );\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 return reconstructError(wire, response.status);\n}\n\nfunction streamUrl(transport: RpcTransport, name: string): string {\n return `${transport.fleetUrl.replace(/\\/$/, '')}/v1/streams/${encodeURIComponent(name)}/chunks`;\n}\n\nexport async function writeStreamChunks(\n transport: RpcTransport,\n name: string,\n runId: string,\n chunks: Uint8Array[],\n): Promise<StreamWriteResult> {\n const body = encodeStreamWriteBatch(chunks);\n if (body.byteLength > MAX_STREAM_BATCH_BYTES + 1024) {\n throw new Error('world-celld: encoded stream batch exceeds configured limit');\n }\n const url = new URL(streamUrl(transport, name));\n url.searchParams.set('runId', runId);\n const doFetch = transport.fetchImpl ?? fetch;\n\n let response: Response;\n try {\n response = await doFetch(url, {\n method: 'POST',\n headers: {\n 'content-type': STREAM_BATCH_CONTENT_TYPE,\n authorization: `Bearer ${transport.secret}`,\n },\n body,\n signal: requestSignal(transport.timeoutMs ?? DEFAULT_TIMEOUT_MS),\n });\n } catch (error) {\n throw new FleetTransportError(`world-celld: fleet unreachable at ${url.href}`, error);\n }\n\n if (!response.ok) throw await errorFromResponse(response);\n try {\n return decodeStreamWriteResult(await readBoundedResponse(response, MAX_WRITE_RESPONSE_BYTES));\n } catch (error) {\n if (error instanceof FleetTransportError) throw error;\n throw new FleetTransportError(`world-celld: malformed stream response from ${url.href}`, error);\n }\n}\n\nexport async function readStreamChunks(\n transport: RpcTransport,\n name: string,\n request: StreamReadRequest,\n signal?: AbortSignal,\n): Promise<StreamReadResult> {\n const url = new URL(streamUrl(transport, name));\n url.searchParams.set('runId', request.runId);\n url.searchParams.set('startIndex', String(request.startIndex));\n url.searchParams.set('maxChunks', String(request.maxChunks));\n url.searchParams.set('maxBytes', String(request.maxBytes));\n url.searchParams.set('waitMs', String(request.waitMs));\n const doFetch = transport.fetchImpl ?? fetch;\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= READ_ATTEMPTS; attempt++) {\n if (signal?.aborted) throw aborted(signal);\n if (attempt > 1) await delayMs(100 * attempt + Math.random() * 200);\n let response: Response;\n try {\n response = await doFetch(url, {\n method: 'GET',\n headers: { authorization: `Bearer ${transport.secret}` },\n signal: requestSignal(transport.timeoutMs ?? DEFAULT_TIMEOUT_MS, signal),\n });\n } catch (error) {\n if (signal?.aborted) throw aborted(signal);\n lastError = new FleetTransportError(`world-celld: fleet unreachable at ${url.href}`, error);\n continue;\n }\n\n if (response.ok) {\n try {\n return decodeStreamReadResult(await readBoundedResponse(response, MAX_READ_RESPONSE_BYTES));\n } catch (error) {\n lastError = new FleetTransportError(\n `world-celld: malformed stream response from ${url.href}`,\n error,\n );\n if (attempt < READ_ATTEMPTS) continue;\n throw lastError;\n }\n }\n\n const error = await errorFromResponse(response);\n if (RETRYABLE_STATUSES.has(response.status) && attempt < READ_ATTEMPTS) {\n lastError = error;\n continue;\n }\n throw error;\n }\n\n throw lastError instanceof Error\n ? lastError\n : new FleetTransportError('world-celld: stream read 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 } from '../config.js';\nimport {\n createWorkflowIndex,\n type HookIdShardStub,\n type HookTokenShardStub,\n type RunCatalogShardStub,\n type RunFenceStub,\n} from '../indexes.js';\nimport type { QueueCellStub } from '../queue.js';\nimport type { WorkflowRunDOStub } from '../storage.js';\nimport type { StreamDOStub } from '../streamer.js';\nimport { callDO, callFleetRoute, type RpcTransport } from './rpc-client.js';\nimport { readStreamChunks, writeStreamChunks } from './stream-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 'resolveHookTokenClaim',\n ],\n mutating: new Set(['applyEvent', 'scheduleCleanup', 'cleanupNow', 'rearmCleanup']),\n};\n\nconst STREAMS: MethodSpec = {\n methods: [\n 'closeStream',\n 'failStream',\n 'registerStream',\n 'listStreams',\n 'expireRegistry',\n 'finalizeRegistry',\n 'expireStream',\n ],\n mutating: new Set([\n 'closeStream',\n 'failStream',\n 'registerStream',\n 'expireRegistry',\n 'finalizeRegistry',\n 'expireStream',\n ]),\n};\n\nfunction makeStreamNamespace(transport: RpcTransport) {\n return {\n idFromName(name: string) {\n return { toString: () => name };\n },\n get(id: { toString(): string }): StreamDOStub {\n const name = id.toString();\n const control = makeStub<StreamDOStub>(transport, 'streams', name, STREAMS);\n control.writeChunks = (runId, chunks) => writeStreamChunks(transport, name, runId, chunks);\n control.readChunks = (request, signal) => readStreamChunks(transport, name, request, signal);\n return control;\n },\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\nconst RUN_CATALOG: MethodSpec = {\n methods: ['upsertRun', 'list', 'expireRun'],\n // Every method is idempotent; transport retries cannot duplicate effects.\n mutating: new Set(),\n};\n\nconst RUN_FENCES: MethodSpec = {\n methods: ['getStatus', 'fenceTerminal', 'fenceExpired'],\n mutating: new Set(),\n};\n\nconst HOOK_TOKENS: MethodSpec = {\n methods: ['get', 'reserve', 'finalize', 'releaseClaim', 'releaseBatch'],\n mutating: new Set(),\n};\n\nconst HOOK_IDS: MethodSpec = {\n methods: ['get', 'reserve', 'publish', 'releaseClaim', 'releaseBatch'],\n mutating: new Set(),\n};\n\nexport function createRemoteEnv(transport: RpcTransport): CelldWorldEnv {\n const runCatalog = makeNamespace<RunCatalogShardStub>(transport, 'run-catalog', RUN_CATALOG);\n const runFences = makeNamespace<RunFenceStub>(transport, 'run-fences', RUN_FENCES);\n const hookTokens = makeNamespace<HookTokenShardStub>(transport, 'hook-tokens', HOOK_TOKENS);\n const hookIds = makeNamespace<HookIdShardStub>(transport, 'hook-ids', HOOK_IDS);\n const workflowIndex = createWorkflowIndex({ runCatalog, runFences, hookTokens, hookIds });\n return {\n WORKFLOW_DB: makeNamespace<WorkflowRunDOStub>(transport, 'runs', RUNS),\n WORKFLOW_STREAMS: makeStreamNamespace(transport),\n WORKFLOW_INDEX: {\n ...workflowIndex,\n commitRun: (run, serializedMetadata) =>\n callFleetRoute(transport, '/v1/index/runs/commit', [run, serializedMetadata], {\n idempotent: true,\n }),\n listRuns: (options) =>\n callFleetRoute(transport, '/v1/index/runs/list', [options], { idempotent: true }),\n expireRun: (request) =>\n callFleetRoute(transport, '/v1/index/runs/expire', [request], { idempotent: true }),\n reserveHook: (token, owner) =>\n callFleetRoute(transport, '/v1/index/hooks/reserve', [token, owner], {\n idempotent: true,\n }),\n finalizeHookIndexes: (token, hookId, serializedHook, owner, reservation) =>\n callFleetRoute(\n transport,\n '/v1/index/hooks/finalize',\n [token, hookId, serializedHook, owner, reservation],\n { idempotent: true },\n ),\n releaseHookReservation: (token, owner, reservation) =>\n callFleetRoute(\n transport,\n '/v1/index/hooks/release-reservation',\n [token, owner, reservation],\n { idempotent: true },\n ),\n releaseHookIndexes: (request) =>\n callFleetRoute(transport, '/v1/index/hooks/release', [request], { idempotent: true }),\n },\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 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';\n\nconst QUEUE_PATHNAME: Pathname = 'flow';\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`. */\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 options?: { limit?: 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`; 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 an in-memory FIFO and HTTP-dispatches envelopes\n * to the user's server at `${baseUrl}/.well-known/workflow/v1/flow`.\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: [] };\n const wakers: Record<Pathname, Array<() => void>> = { flow: [] };\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 try {\n await response.body?.cancel();\n } catch {\n // The status already determines the queue outcome. Do not turn an ack\n // into a retry solely because releasing an unused body failed.\n }\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 },\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 parseQueueName(queueName);\n const runId =\n 'runId' in message && typeof message.runId === 'string' ? message.runId : 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_PATHNAME,\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_PATHNAME,\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 new Response(null, { status: 204 });\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 isTerminalWorkflowRunStatus,\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 type { HookReservation } from './indexes.js';\nimport { compact } from './util.js';\nimport type { RunReadOutcome } 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 resolveHookTokenClaim(request: {\n hookId: string;\n token: string;\n claimId: string;\n }): Promise<{ committed: boolean }>;\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 'WAIT_NOT_FOUND':\n throw new WorkflowWorldError(outcome.message, { status: 404 });\n case 'ENTITY_CONFLICT':\n throw new EntityConflictError(outcome.message);\n case 'HOOK_CLAIM_CANCELLED':\n throw new WorkflowWorldError(outcome.message, { status: 503 });\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 }\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\n/** Bound cross-run fanout so large list pages cannot create an RPC burst. */\nconst RUN_LIST_CONCURRENCY = 8;\n\ninterface RunIndexMetadata {\n runId: string;\n status: WorkflowRun['status'];\n}\n\n/**\n * Run statuses only move pending -> running -> terminal, and terminal statuses\n * are immutable. An older index value may therefore safely exclude a filter\n * only when it is already terminal, or when the caller asks for pending and\n * the index has advanced beyond pending. Earlier non-terminal metadata cannot\n * exclude a later status because a post-commit index write may need replay.\n */\nfunction indexStatusExcludes(\n indexed: WorkflowRun['status'],\n requested: WorkflowRun['status'] | undefined,\n): boolean {\n if (requested === undefined || indexed === requested) return false;\n return isTerminalWorkflowRunStatus(indexed) || requested === 'pending';\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.listRuns({\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 const candidates: Array<{ key: string; metadata: RunIndexMetadata }> = [];\n for (const key of kvList.keys) {\n const metadata = JSON.parse(key.value) as RunIndexMetadata;\n // Use monotonic status metadata as a conservative prefilter,\n // then still verify every candidate against the authoritative\n // RunDO below. Earlier metadata cannot exclude a later status.\n if (indexStatusExcludes(metadata.status, params?.status)) {\n continue;\n }\n candidates.push({ key: key.name, metadata });\n }\n\n // Fetch enough candidates to prove the requested page and hasMore,\n // retaining index order while limiting concurrent cross-run RPCs.\n for (let offset = 0; offset < candidates.length && matches.length <= limit;) {\n const needed = limit + 1 - matches.length;\n const batch = candidates.slice(offset, offset + Math.min(RUN_LIST_CONCURRENCY, needed));\n const resolved = await Promise.all(\n batch.map(async ({ key, metadata }) => {\n try {\n const run = await runsGet(metadata.runId, {\n resolveData: params?.resolveData,\n });\n return !params?.status || run.status === params.status ? { key, run } : null;\n } catch (error) {\n if (!WorkflowRunNotFoundError.is(error) && !RunExpiredError.is(error)) {\n throw error;\n }\n return null;\n }\n }),\n );\n for (const match of resolved) {\n if (match) matches.push(match);\n }\n offset += batch.length;\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 hookAdmission:\n | { owner: HookTokenOwner; reservation: HookReservation; token: string }\n | undefined;\n if (data.eventType === 'hook_created') {\n const owner = {\n runId: effectiveRunId,\n hookId: data.correlationId,\n };\n const admission = await env.WORKFLOW_INDEX.reserveHook(data.eventData.token, owner);\n if (admission.admitted) {\n tokenHolder = null;\n hookAdmission = {\n owner,\n reservation: admission.reservation,\n token: data.eventData.token,\n };\n } else {\n tokenHolder = admission.holder;\n }\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 // A thrown RPC is commit-ambiguous. Resolve it inside the authoritative\n // RunDO: that transaction either observes the committed hook or fences\n // this exact reservation before its token/ID claims are released.\n let outcome: ApplyEventOutcome;\n try {\n outcome = await stub.applyEvent({\n runId: effectiveRunId,\n data,\n params,\n tokenHolder,\n hookClaimId: hookAdmission?.reservation.claimId,\n cleanup,\n });\n } catch (error) {\n if (hookAdmission) {\n let resolution: { committed: boolean };\n try {\n resolution = await stub.resolveHookTokenClaim({\n hookId: hookAdmission.owner.hookId,\n token: hookAdmission.token,\n claimId: hookAdmission.reservation.claimId,\n });\n } catch {\n // Resolution is itself ambiguous, so fail closed and leave both\n // claims for an exact same-reservation retry.\n throw error;\n }\n if (!resolution.committed) {\n await env.WORKFLOW_INDEX.releaseHookReservation(\n hookAdmission.token,\n hookAdmission.owner,\n hookAdmission.reservation,\n );\n }\n }\n throw error;\n }\n\n if (!outcome.ok) {\n if (hookAdmission) {\n await env.WORKFLOW_INDEX.releaseHookReservation(\n hookAdmission.token,\n hookAdmission.owner,\n hookAdmission.reservation,\n );\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.commitRun(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 hookAdmission?.reservation,\n );\n } else if (hookAdmission) {\n await env.WORKFLOW_INDEX.releaseHookReservation(\n hookAdmission.token,\n hookAdmission.owner,\n hookAdmission.reservation,\n );\n }\n const terminal = Boolean(outcome.run && isTerminalWorkflowRunStatus(outcome.run.status));\n if (outcome.releasedHooks.length > 0) {\n await env.WORKFLOW_INDEX.releaseHookIndexes({\n runId: effectiveRunId,\n hooks: outcome.releasedHooks,\n terminal,\n });\n }\n\n const eventPage =\n outcome.events === undefined\n ? {}\n : {\n events: outcome.events,\n cursor: outcome.cursor ?? null,\n hasMore: outcome.hasMore ?? false,\n };\n return {\n event: outcome.event,\n run: outcome.run,\n step: outcome.step,\n hook: outcome.hook,\n wait: outcome.wait,\n stepCreated: outcome.stepCreated,\n maxEvents: outcome.maxEvents,\n ...eventPage,\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 stub = getRunDO(params.runId);\n const matches: Event[] = [];\n let scanCursor = params.pagination?.cursor;\n let exhausted = false;\n\n // Workflow 5 scopes correlation IDs to a run. Scan that run's dense\n // event log rather than the global derived index so identical step or\n // wait IDs in different runs can never leak into this result.\n while (matches.length <= limit && !exhausted) {\n const page = unwrapRead(\n await stub.listEvents({\n limit: Math.min(1000, Math.max(50, limit + 1)),\n cursor: scanCursor,\n sortOrder: params.pagination?.sortOrder ?? 'asc',\n }),\n );\n for (const event of page.data) {\n if (event.correlationId === params.correlationId) {\n matches.push(event);\n if (matches.length > limit) break;\n }\n }\n exhausted = !page.hasMore;\n scanCursor = page.cursor ?? page.data.at(-1)?.eventId;\n if (page.data.length === 0) exhausted = true;\n }\n\n const hasMore = matches.length > limit;\n const data = matches.slice(0, limit);\n return {\n data: data.map(parseEvent),\n cursor: hasMore ? (data.at(-1)?.eventId ?? null) : null,\n hasMore,\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.getHookById(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.getHookByToken(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 {\n ExpireRunStreamsResult,\n ExpireStreamResult,\n FinalizeRunStreamsResult,\n} from './retention.js';\nimport {\n MAX_STREAM_BATCH_BYTES,\n MAX_STREAM_CHUNK_BYTES,\n MAX_STREAM_LONG_POLL_MS,\n MAX_STREAM_READ_BYTES,\n MAX_STREAM_READ_CHUNKS,\n MAX_STREAM_WRITE_CHUNKS,\n normalizeStreamError,\n type StreamErrorData,\n type StreamReadRequest,\n type StreamReadResult,\n type StreamWriteResult,\n} from './stream-protocol.js';\n\nexport interface CelldStreamerConfig {\n env: {\n WORKFLOW_STREAMS: StreamDONamespace;\n };\n /** Duration of one bounded idle read. Default: 20000. */\n streamLongPollMs?: number;\n /** Runtime coalescing delay for streams.writeMulti(). Default: 0. */\n streamFlushIntervalMs?: number;\n}\n\nexport interface CelldStreamer extends Omit<Streamer, 'streams'> {\n streams: Streamer['streams'] & {\n fail(runId: string, name: string, error: StreamErrorData | string): Promise<void>;\n };\n writeToStream(\n name: string,\n runId: string | Promise<string>,\n chunk: string | Uint8Array,\n ): Promise<void>;\n writeChunksToStream(\n name: string,\n runId: string | Promise<string>,\n chunks: readonly (string | Uint8Array)[],\n ): Promise<void>;\n closeStream(name: string, runId: string | Promise<string>): Promise<void>;\n errorStream(\n name: string,\n runId: string | Promise<string>,\n error: StreamErrorData | string,\n ): Promise<void>;\n readFromStream(\n name: string,\n runId: string,\n startIndex?: number,\n ): Promise<ReadableStream<Uint8Array>>;\n listStreamsByRunId(runId: string): Promise<string[]>;\n getStreamChunks(\n name: string,\n runId: string,\n options?: GetChunksOptions,\n ): Promise<StreamChunksResponse>;\n getStreamInfo(name: string, runId: string): Promise<StreamInfoResponse>;\n}\n\n/** Direct StreamDO RPC surface. Remote stubs specialize read/write as binary HTTP. */\nexport interface StreamDOStub {\n writeChunks(runId: string, chunks: Uint8Array[]): Promise<StreamWriteResult>;\n readChunks(request: StreamReadRequest, signal?: AbortSignal): Promise<StreamReadResult>;\n closeStream(runId: string): Promise<void>;\n failStream(runId: string, error: StreamErrorData | string): Promise<void>;\n registerStream(runId: string, name: string): Promise<void>;\n listStreams(): Promise<string[]>;\n expireRegistry(\n runId: string,\n expiredAt: number,\n options?: { limit?: number },\n ): Promise<ExpireRunStreamsResult>;\n finalizeRegistry(runId: string, streams: string[]): Promise<FinalizeRunStreamsResult>;\n expireStream(\n runId: string,\n expiredAt: number,\n options?: { limit?: number; byteLimit?: number },\n ): 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\nconst DEFAULT_LONG_POLL_MS = MAX_STREAM_LONG_POLL_MS;\nconst DEFAULT_CHUNK_PAGE_SIZE = MAX_STREAM_READ_CHUNKS;\nconst MAX_CHUNK_PAGE_SIZE = MAX_STREAM_READ_CHUNKS;\nconst REGISTERED_STREAM_CACHE_SIZE = 4096;\n\nfunction toBytes(chunk: string | Uint8Array): Uint8Array {\n return typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk;\n}\n\nfunction throwTerminal(result: StreamReadResult): void {\n if (result.state === 'expired') throw new Error('Stream has expired');\n if (result.state === 'errored') {\n const error = new Error(result.error?.message ?? 'Stream failed');\n error.name = result.error?.name ?? 'Error';\n throw error;\n }\n}\n\nexport function createStreamer(config: CelldStreamerConfig): CelldStreamer {\n const { env } = config;\n const streamLongPollMs = config.streamLongPollMs ?? DEFAULT_LONG_POLL_MS;\n if (\n !Number.isSafeInteger(streamLongPollMs) ||\n streamLongPollMs < 1 ||\n streamLongPollMs > MAX_STREAM_LONG_POLL_MS\n ) {\n throw new Error(`streamLongPollMs must be between 1 and ${MAX_STREAM_LONG_POLL_MS}`);\n }\n const streamFlushIntervalMs = config.streamFlushIntervalMs ?? 0;\n if (!Number.isSafeInteger(streamFlushIntervalMs) || streamFlushIntervalMs < 0) {\n throw new Error('streamFlushIntervalMs must be a non-negative safe integer');\n }\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 /** Bounded per-isolate cache; eviction only repeats idempotent registration. */\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 if (registeredStreams.size >= REGISTERED_STREAM_CACHE_SIZE) {\n const oldest = registeredStreams.values().next().value;\n if (oldest !== undefined) registeredStreams.delete(oldest);\n }\n registeredStreams.add(cacheKey);\n }\n\n const writeChunksToStream = async (\n name: string,\n runId: string | Promise<string>,\n input: readonly (string | Uint8Array)[],\n ): Promise<void> => {\n if (input.length === 0) return;\n const resolvedRunId = await runId;\n const chunks = input.map(toBytes);\n for (const chunk of chunks) {\n if (chunk.byteLength > MAX_STREAM_CHUNK_BYTES) {\n throw new Error(`Stream chunk exceeds ${MAX_STREAM_CHUNK_BYTES} bytes`);\n }\n }\n await registerStreamForRun(name, resolvedRunId);\n\n let batch: Uint8Array[] = [];\n let batchBytes = 0;\n for (const chunk of chunks) {\n if (\n batch.length > 0 &&\n (batch.length === MAX_STREAM_WRITE_CHUNKS ||\n batchBytes + chunk.byteLength > MAX_STREAM_BATCH_BYTES)\n ) {\n await getStreamDO(name).writeChunks(resolvedRunId, batch);\n batch = [];\n batchBytes = 0;\n }\n batch.push(chunk);\n batchBytes += chunk.byteLength;\n }\n if (batch.length > 0) await getStreamDO(name).writeChunks(resolvedRunId, batch);\n };\n\n const writeToStream = (\n name: string,\n runId: string | Promise<string>,\n chunk: string | Uint8Array,\n ): Promise<void> => writeChunksToStream(name, runId, [chunk]);\n\n const closeStream = async (name: string, runId: string | Promise<string>): Promise<void> => {\n const resolvedRunId = await runId;\n await registerStreamForRun(name, resolvedRunId);\n await getStreamDO(name).closeStream(resolvedRunId);\n };\n\n const errorStream = async (\n name: string,\n runId: string | Promise<string>,\n error: StreamErrorData | string,\n ): Promise<void> => {\n const resolvedRunId = await runId;\n await registerStreamForRun(name, resolvedRunId);\n await getStreamDO(name).failStream(resolvedRunId, normalizeStreamError(error));\n };\n\n const readFromStream = async (\n name: string,\n runId: string,\n startIndex = 0,\n ): Promise<ReadableStream<Uint8Array>> => {\n const stub = getStreamDO(name);\n\n let nextIndex: number;\n if (startIndex < 0) {\n const info = await stub.readChunks({\n runId,\n startIndex: 0,\n maxChunks: 0,\n maxBytes: MAX_STREAM_READ_BYTES,\n waitMs: 0,\n });\n throwTerminal(info);\n nextIndex = Math.max(0, info.tailIndex + 1 + startIndex);\n } else {\n nextIndex = startIndex;\n }\n\n let cancelled = false;\n const abortController = new AbortController();\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n for (;;) {\n if (cancelled) return;\n let result: StreamReadResult;\n try {\n result = await stub.readChunks(\n {\n runId,\n startIndex: nextIndex,\n maxChunks: MAX_STREAM_READ_CHUNKS,\n maxBytes: MAX_STREAM_READ_BYTES,\n waitMs: streamLongPollMs,\n },\n abortController.signal,\n );\n } catch (error) {\n if (cancelled || abortController.signal.aborted) return;\n throw error;\n }\n\n if (result.chunks.length > 0) {\n for (const chunk of result.chunks) controller.enqueue(chunk);\n nextIndex += result.chunks.length;\n return;\n }\n throwTerminal(result);\n if (result.state === 'closed') {\n controller.close();\n return;\n }\n // A normal long-poll timeout returns no data; keep the pending\n // ReadableStream read attached through the next bounded request.\n }\n },\n\n cancel() {\n cancelled = true;\n abortController.abort(new DOMException('Stream reader cancelled', 'AbortError'));\n },\n });\n };\n\n const listStreamsByRunId = (runId: string): Promise<string[]> =>\n getRunRegistryDO(runId).listStreams();\n\n const getStreamChunks = async (\n name: string,\n runId: string,\n options?: GetChunksOptions,\n ): Promise<StreamChunksResponse> => {\n const requestedLimit = options?.limit ?? DEFAULT_CHUNK_PAGE_SIZE;\n if (!Number.isSafeInteger(requestedLimit) || requestedLimit < 1) {\n throw new Error('Stream chunk limit must be a positive safe integer');\n }\n const limit = Math.min(requestedLimit, 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 result = await getStreamDO(name).readChunks({\n runId,\n startIndex,\n maxChunks: limit,\n maxBytes: MAX_STREAM_READ_BYTES,\n waitMs: 0,\n });\n throwTerminal(result);\n const data: StreamChunk[] = result.chunks.map((chunk, offset) => ({\n index: startIndex + offset,\n data: chunk,\n }));\n const nextIndex = startIndex + data.length;\n const hasMore = nextIndex <= result.tailIndex;\n\n return {\n data,\n cursor: hasMore ? String(nextIndex) : null,\n hasMore,\n done: result.state === 'closed',\n };\n };\n\n const getStreamInfo = async (name: string, runId: string): Promise<StreamInfoResponse> => {\n const result = await getStreamDO(name).readChunks({\n runId,\n startIndex: 0,\n maxChunks: 0,\n maxBytes: MAX_STREAM_READ_BYTES,\n waitMs: 0,\n });\n throwTerminal(result);\n return { tailIndex: result.tailIndex, done: result.state === 'closed' };\n };\n\n return {\n streamFlushIntervalMs,\n streams: {\n write: (runId, name, chunk) => writeToStream(name, runId, chunk),\n writeMulti: (runId, name, chunks) => writeChunksToStream(name, runId, chunks),\n close: (runId, name) => closeStream(name, runId),\n fail: (runId, name, error) => errorStream(name, runId, error),\n get: (runId, name, startIndex) => readFromStream(name, runId, startIndex),\n list: listStreamsByRunId,\n getChunks: (runId, name, options) => getStreamChunks(name, runId, options),\n getInfo: (runId, name) => getStreamInfo(name, runId),\n },\n writeToStream,\n writeChunksToStream,\n closeStream,\n errorStream,\n readFromStream,\n listStreamsByRunId,\n getStreamChunks,\n getStreamInfo,\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, type CelldStreamer } 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 {\n MAX_STREAM_BATCH_BYTES,\n MAX_STREAM_CHUNK_BYTES,\n MAX_STREAM_ERROR_BYTES,\n MAX_STREAM_LONG_POLL_MS,\n MAX_STREAM_READ_BYTES,\n MAX_STREAM_READ_CHUNKS,\n MAX_STREAM_WRITE_CHUNKS,\n} from './stream-protocol.js';\nexport type {\n StreamErrorData,\n StreamReadRequest,\n StreamReadResult,\n StreamTerminalState,\n StreamWriteResult,\n} from './stream-protocol.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 & CelldStreamer & { 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 streamLongPollMs: resolved.streamLongPollMs,\n streamFlushIntervalMs: resolved.streamFlushIntervalMs,\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":";;;;;;AA4EA,SAAgB,cAAc,QAAgD;CAC5E,MAAM,eAAe,QAAQ,kBAAkB,QAAQ,IAAI,0BAA0B;CACrF,MAAM,iBACJ,OAAO,iBAAiB,WAAW,eAAe,OAAO,SAAS,cAAc,EAAE;CACpF,IAAI,CAAC,OAAO,cAAc,cAAc,KAAK,iBAAiB,GAC5D,MAAM,IAAI,MAAM,iEAAiE;CAGnF,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,IAAI,CAAC,OAAO,cAAc,YAAY,KAAK,eAAe,GACxD,MAAM,IAAI,MAAM,2DAA2D;CAE7E,MAAM,mBACJ,QAAQ,oBACR,KAAK,IAAA,KAA6B,KAAK,IAAI,GAAG,eAAe,GAAK,CAAC;CACrE,IACE,CAAC,OAAO,cAAc,gBAAgB,KACtC,mBAAmB,KACnB,mBAAA,KAEA,MAAM,IAAI,MACR,uDAAuD,yBACzD;CAEF,IAAI,oBAAoB,cACtB,MAAM,IAAI,MAAM,8DAA8D;CAEhF,MAAM,wBAAwB,QAAQ,yBAAyB;CAC/D,IAAI,CAAC,OAAO,cAAc,qBAAqB,KAAK,wBAAwB,GAC1E,MAAM,IAAI,MAAM,wEAAwE;CAG1F,OAAO;EACL,UAAU,QAAQ,YAAY,QAAQ,IAAI;EAC1C,QAAQ,QAAQ,UAAU,QAAQ,IAAI;EACtC,KAAK,QAAQ;EACb,cAAc,QAAQ,gBAAgB,QAAQ,IAAI,uBAAuB;EACzE,SAAS,QAAQ;EACjB,aAAa,QAAQ,eAAe;EACpC;EACA;EACA;EACA;CACF;AACF;;;;;;;;;AC1GA,SAAgB,iBAAiB,MAA6B,YAA2B;CACvF,MAAM,UAAU,MAAM,WAAW,oCAAoC;CACrE,MAAM,QAAQ,IAAI,mBAAmB,SAAS,EAC5C,QAAQ,MAAM,UAAU,WAC1B,CAAC;CACD,IAAI,MAAM,MACR,MAAM,OAAO,KAAK;CAEpB,OAAO;AACT;;AAGA,IAAa,sBAAb,cAAyC,MAAM;CAGlC;CAFX,YACE,SACA,OACA;EACA,MAAM,OAAO;EAFJ,KAAA,QAAA;EAGT,KAAK,OAAO;CACd;AACF;;;;ACrBA,MAAMA,uCAAqB,IAAI,IAAI;CAAC;CAAK;CAAK;AAAG,CAAC;AAElD,MAAMC,uBAAqB;AAE3B,SAASC,UAAQ,IAA2B;CAC1C,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;;;;;;;;;AAUA,eAAsB,OACpB,WACA,SACA,MACA,QACA,MACA,MACY;CACZ,OAAO,MAAM,eACX,WACA,WAAW,QAAQ,GAAG,mBAAmB,IAAI,EAAE,GAAG,UAClD,MACA,IACF;AACF;;AAGA,eAAsB,eACpB,WACA,MACA,MACA,MACY;CACZ,MAAM,UAAU,UAAU,aAAa;CACvC,MAAM,MAAM,GAAG,UAAU,SAAS,QAAQ,OAAO,EAAE,IAAI;CACvD,MAAM,WAAW,MAAM,aAAa,IAAmB;CAEvD,IAAI;CACJ,KAAK,IAAI,UAAU,GAAG,WAAW,UAAU,WAAW;EACpD,IAAI,UAAU,GACZ,MAAMA,UAAQ,MAAM,UAAU,KAAK,OAAO,IAAI,GAAG;EAEnD,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,QAAQ,KAAK;IAC5B,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,eAAe,UAAU,UAAU;IACrC;IACA,MAAM,aAAa,IAAI;IACvB,QAAQ,YAAY,QAAQ,KAAK,IAAI,GAAG,UAAU,aAAaD,oBAAkB,CAAC;GACpF,CAAC;EACH,SAAS,OAAO;GACd,YAAY,IAAI,oBAAoB,qCAAqC,OAAO,KAAK;GACrF;EACF;EAEA,IAAI,SAAS,IACX,IAAI;GACF,OAAO,SAAY,MAAM,SAAS,KAAK,CAAC;EAC1C,SAAS,OAAO;GACd,YAAY,IAAI,oBACd,mDAAmD,OACnD,KACF;GACA,IAAI,UAAU,UAAU;GACxB,MAAM;EACR;EAGF,MAAM,OAAO,MAAM,SAAS,KAAK;EACjC,IAAI;EACJ,IAAI;GACF,OAAQ,KAAK,MAAM,IAAI,CAAC,CAA2B;EACrD,QAAQ;GACN,OAAO,EAAE,SAAS,QAAQ,QAAQ,SAAS,SAAS;EACtD;EAEA,IAAID,qBAAmB,IAAI,SAAS,MAAM,KAAK,UAAU,UAAU;GACjE,YAAY,iBAAiB,MAAM,SAAS,MAAM;GAClD;EACF;EAEA,MAAM,iBAAiB,MAAM,SAAS,MAAM;CAC9C;CAEA,MAAM,qBAAqB,QACvB,YACA,IAAI,oBAAoB,2BAA2B,SAAS;AAClE;;;AC/FA,MAAM,qCAAqB,IAAI,IAAI;CAAC;CAAK;CAAK;AAAG,CAAC;AAClD,MAAM,gBAAgB;AACtB,MAAM,qBAAqB;AAC3B,MAAM,2BAA2B;AACjC,MAAM,0BAA0B,wBAAwB;AACxD,MAAM,2BAA2B;AAEjC,SAAS,QAAQ,IAA2B;CAC1C,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEA,SAAS,cAAc,WAAmB,QAAmC;CAC3E,MAAM,UAAU,YAAY,QAAQ,KAAK,IAAI,GAAG,SAAS,CAAC;CAC1D,OAAO,SAAS,YAAY,IAAI,CAAC,QAAQ,OAAO,CAAC,IAAI;AACvD;AAEA,SAAS,QAAQ,QAA8B;CAC7C,OAAO,OAAO,UAAU,IAAI,aAAa,6BAA6B,YAAY;AACpF;AAEA,eAAe,oBAAoB,UAAoB,UAAuC;CAE5F,IADsB,OAAO,SAAS,QAAQ,IAAI,gBAAgB,KAAK,GACvD,IAAI,UAAU;EAC5B,MAAM,SAAS,MAAM,OAAO,0CAA0C,CAAC,CAAC,YAAY,KAAA,CAAS;EAC7F,MAAM,IAAI,oBAAoB,wCAAwC,SAAS,OAAO;CACxF;CACA,IAAI,CAAC,SAAS,MAAM,uBAAO,IAAI,WAAW;CAE1C,MAAM,SAAS,SAAS,KAAK,UAAU;CACvC,MAAM,SAAuB,CAAC;CAC9B,IAAI,QAAQ;CACZ,IAAI;EACF,SAAS;GACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM;GACV,SAAS,MAAM;GACf,IAAI,QAAQ,UAAU;IACpB,MAAM,OAAO,OAAO,0CAA0C,CAAC,CAAC,YAAY,KAAA,CAAS;IACrF,MAAM,IAAI,oBAAoB,wCAAwC,SAAS,OAAO;GACxF;GACA,OAAO,KAAK,KAAK;EACnB;CACF,UAAU;EACR,OAAO,YAAY;CACrB;CAEA,MAAM,OAAO,IAAI,WAAW,KAAK;CACjC,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,QAAQ;EAC1B,KAAK,IAAI,OAAO,MAAM;EACtB,UAAU,MAAM;CAClB;CACA,OAAO;AACT;AAEA,eAAe,kBAAkB,UAAoC;CACnE,MAAM,OAAO,IAAI,YAAY,CAAC,CAAC,OAC7B,MAAM,oBAAoB,UAAU,wBAAwB,CAC9D;CACA,IAAI;CACJ,IAAI;EACF,OAAQ,KAAK,MAAM,IAAI,CAAC,CAA2B;CACrD,QAAQ;EACN,OAAO,EAAE,SAAS,QAAQ,QAAQ,SAAS,SAAS;CACtD;CACA,OAAO,iBAAiB,MAAM,SAAS,MAAM;AAC/C;AAEA,SAAS,UAAU,WAAyB,MAAsB;CAChE,OAAO,GAAG,UAAU,SAAS,QAAQ,OAAO,EAAE,EAAE,cAAc,mBAAmB,IAAI,EAAE;AACzF;AAEA,eAAsB,kBACpB,WACA,MACA,OACA,QAC4B;CAC5B,MAAM,OAAO,uBAAuB,MAAM;CAC1C,IAAI,KAAK,aAAA,SACP,MAAM,IAAI,MAAM,4DAA4D;CAE9E,MAAM,MAAM,IAAI,IAAI,UAAU,WAAW,IAAI,CAAC;CAC9C,IAAI,aAAa,IAAI,SAAS,KAAK;CACnC,MAAM,UAAU,UAAU,aAAa;CAEvC,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,QAAQ,KAAK;GAC5B,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,eAAe,UAAU,UAAU;GACrC;GACA;GACA,QAAQ,cAAc,UAAU,aAAa,kBAAkB;EACjE,CAAC;CACH,SAAS,OAAO;EACd,MAAM,IAAI,oBAAoB,qCAAqC,IAAI,QAAQ,KAAK;CACtF;CAEA,IAAI,CAAC,SAAS,IAAI,MAAM,MAAM,kBAAkB,QAAQ;CACxD,IAAI;EACF,OAAO,wBAAwB,MAAM,oBAAoB,UAAU,wBAAwB,CAAC;CAC9F,SAAS,OAAO;EACd,IAAI,iBAAiB,qBAAqB,MAAM;EAChD,MAAM,IAAI,oBAAoB,+CAA+C,IAAI,QAAQ,KAAK;CAChG;AACF;AAEA,eAAsB,iBACpB,WACA,MACA,SACA,QAC2B;CAC3B,MAAM,MAAM,IAAI,IAAI,UAAU,WAAW,IAAI,CAAC;CAC9C,IAAI,aAAa,IAAI,SAAS,QAAQ,KAAK;CAC3C,IAAI,aAAa,IAAI,cAAc,OAAO,QAAQ,UAAU,CAAC;CAC7D,IAAI,aAAa,IAAI,aAAa,OAAO,QAAQ,SAAS,CAAC;CAC3D,IAAI,aAAa,IAAI,YAAY,OAAO,QAAQ,QAAQ,CAAC;CACzD,IAAI,aAAa,IAAI,UAAU,OAAO,QAAQ,MAAM,CAAC;CACrD,MAAM,UAAU,UAAU,aAAa;CACvC,IAAI;CAEJ,KAAK,IAAI,UAAU,GAAG,WAAW,eAAe,WAAW;EACzD,IAAI,QAAQ,SAAS,MAAM,QAAQ,MAAM;EACzC,IAAI,UAAU,GAAG,MAAM,QAAQ,MAAM,UAAU,KAAK,OAAO,IAAI,GAAG;EAClE,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,QAAQ,KAAK;IAC5B,QAAQ;IACR,SAAS,EAAE,eAAe,UAAU,UAAU,SAAS;IACvD,QAAQ,cAAc,UAAU,aAAa,oBAAoB,MAAM;GACzE,CAAC;EACH,SAAS,OAAO;GACd,IAAI,QAAQ,SAAS,MAAM,QAAQ,MAAM;GACzC,YAAY,IAAI,oBAAoB,qCAAqC,IAAI,QAAQ,KAAK;GAC1F;EACF;EAEA,IAAI,SAAS,IACX,IAAI;GACF,OAAO,uBAAuB,MAAM,oBAAoB,UAAU,uBAAuB,CAAC;EAC5F,SAAS,OAAO;GACd,YAAY,IAAI,oBACd,+CAA+C,IAAI,QACnD,KACF;GACA,IAAI,UAAU,eAAe;GAC7B,MAAM;EACR;EAGF,MAAM,QAAQ,MAAM,kBAAkB,QAAQ;EAC9C,IAAI,mBAAmB,IAAI,SAAS,MAAM,KAAK,UAAU,eAAe;GACtE,YAAY;GACZ;EACF;EACA,MAAM;CACR;CAEA,MAAM,qBAAqB,QACvB,YACA,IAAI,oBAAoB,mCAAmC,SAAS;AAC1E;;;AC1JA,MAAM,OAAmB;CACvB,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,0BAAU,IAAI,IAAI;EAAC;EAAc;EAAmB;EAAc;CAAc,CAAC;AACnF;AAEA,MAAM,UAAsB;CAC1B,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,0BAAU,IAAI,IAAI;EAChB;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;AACH;AAEA,SAAS,oBAAoB,WAAyB;CACpD,OAAO;EACL,WAAW,MAAc;GACvB,OAAO,EAAE,gBAAgB,KAAK;EAChC;EACA,IAAI,IAA0C;GAC5C,MAAM,OAAO,GAAG,SAAS;GACzB,MAAM,UAAU,SAAuB,WAAW,WAAW,MAAM,OAAO;GAC1E,QAAQ,eAAe,OAAO,WAAW,kBAAkB,WAAW,MAAM,OAAO,MAAM;GACzF,QAAQ,cAAc,SAAS,WAAW,iBAAiB,WAAW,MAAM,SAAS,MAAM;GAC3F,OAAO;EACT;CACF;AACF;AAEA,MAAM,QAAoB;CACxB,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,0BAAU,IAAI,IAAI;EAChB;EACA;EACA;EACA;EACA;CACF,CAAC;AACH;AAEA,SAAS,SAAY,WAAyB,SAAiB,MAAc,MAAqB;CAChG,MAAM,OAAiE,CAAC;CACxE,KAAK,MAAM,UAAU,KAAK,SACxB,KAAK,WAAW,GAAG,SACjB,OAAO,WAAW,SAAS,MAAM,QAAQ,MAAM,EAC7C,YAAY,CAAC,KAAK,SAAS,IAAI,MAAM,EACvC,CAAC;CAEL,OAAO;AACT;AAEA,SAAS,cAAiB,WAAyB,SAAiB,MAAkB;CACpF,OAAO;EACL,WAAW,MAAc;GACvB,OAAO,EAAE,gBAAgB,KAAK;EAChC;EACA,IAAI,IAA+B;GACjC,OAAO,SAAY,WAAW,SAAS,GAAG,SAAS,GAAG,IAAI;EAC5D;CACF;AACF;AAEA,MAAM,cAA0B;CAC9B,SAAS;EAAC;EAAa;EAAQ;CAAW;CAE1C,0BAAU,IAAI,IAAI;AACpB;AAEA,MAAM,aAAyB;CAC7B,SAAS;EAAC;EAAa;EAAiB;CAAc;CACtD,0BAAU,IAAI,IAAI;AACpB;AAEA,MAAM,cAA0B;CAC9B,SAAS;EAAC;EAAO;EAAW;EAAY;EAAgB;CAAc;CACtE,0BAAU,IAAI,IAAI;AACpB;AAEA,MAAM,WAAuB;CAC3B,SAAS;EAAC;EAAO;EAAW;EAAW;EAAgB;CAAc;CACrE,0BAAU,IAAI,IAAI;AACpB;AAEA,SAAgB,gBAAgB,WAAwC;CACtE,MAAM,aAAa,cAAmC,WAAW,eAAe,WAAW;CAC3F,MAAM,YAAY,cAA4B,WAAW,cAAc,UAAU;CACjF,MAAM,aAAa,cAAkC,WAAW,eAAe,WAAW;CAC1F,MAAM,UAAU,cAA+B,WAAW,YAAY,QAAQ;CAC9E,MAAM,gBAAgB,oBAAoB;EAAE;EAAY;EAAW;EAAY;CAAQ,CAAC;CACxF,OAAO;EACL,aAAa,cAAiC,WAAW,QAAQ,IAAI;EACrE,kBAAkB,oBAAoB,SAAS;EAC/C,gBAAgB;GACd,GAAG;GACH,YAAY,KAAK,uBACf,eAAe,WAAW,yBAAyB,CAAC,KAAK,kBAAkB,GAAG,EAC5E,YAAY,KACd,CAAC;GACH,WAAW,YACT,eAAe,WAAW,uBAAuB,CAAC,OAAO,GAAG,EAAE,YAAY,KAAK,CAAC;GAClF,YAAY,YACV,eAAe,WAAW,yBAAyB,CAAC,OAAO,GAAG,EAAE,YAAY,KAAK,CAAC;GACpF,cAAc,OAAO,UACnB,eAAe,WAAW,2BAA2B,CAAC,OAAO,KAAK,GAAG,EACnE,YAAY,KACd,CAAC;GACH,sBAAsB,OAAO,QAAQ,gBAAgB,OAAO,gBAC1D,eACE,WACA,4BACA;IAAC;IAAO;IAAQ;IAAgB;IAAO;GAAW,GAClD,EAAE,YAAY,KAAK,CACrB;GACF,yBAAyB,OAAO,OAAO,gBACrC,eACE,WACA,uCACA;IAAC;IAAO;IAAO;GAAW,GAC1B,EAAE,YAAY,KAAK,CACrB;GACF,qBAAqB,YACnB,eAAe,WAAW,2BAA2B,CAAC,OAAO,GAAG,EAAE,YAAY,KAAK,CAAC;EACxF;EACA,gBAAgB,cAA6B,WAAW,SAAS,KAAK;CACxE;AACF;;;;;;;;;;;;;;;;ACzKA,SAAgB,kBAAkB,WAAmB;CACnD,QAAQ,GAAG,SAAoB;EAC7B,MAAM,QAAQ,OAAO,YAAY,cAAc,KAAA,IAAY,QAAQ,KAAK;EACxE,IAAI,CAAC,OAAO;EAQZ,IAAI,EALF,UAAU,OACV,UAAU,UACV,UAAU,OACV,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,OAAe,GAAG,KAAK,MAAM,SAAS,IAEjD;EAGd,MAAM,SAAS,qBADG,IAAI,KAAK,EAAA,CAAE,YACF,EAAE,KAAK,UAAU;EAC5C,MAAM,UAAU,KACb,KAAK,QAAS,OAAO,QAAQ,WAAW,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,OAAO,GAAG,CAAE,CAAC,CACpF,KAAK,GAAG;EAEX,IAAI,OAAO,QAAQ,QAAQ,UAAU,YACnC,QAAQ,OAAO,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG;OAE7C,QAAQ,MAAM,GAAG,OAAO,GAAG,SAAS;CAExC;AACF;;;ACnCA,MAAa,QAAQ,kBAAkB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACsCpD,MAAa,2CAA2B,IAAI,IAAI;CAAC;CAAK;CAAK;CAAK;AAAG,CAAC;AAEpE,SAAS,iBAAiB,KAAyC;CACjE,IAAI,eAAe,sBAAsB,IAAI,WAAW,KAAA,GACtD,OAAO,yBAAyB,IAAI,IAAI,MAAM;CAEhD,OAAO;AACT;;AAGA,SAAgB,eAAe,SAAyB;CACtD,OAAO,KAAK,IAAI,IAAI,KAAK,OAAO;AAClC;AAIA,MAAM,iBAA2B;AAEjC,SAAS,aAAsB;CAI7B,IAAI,QAAQ,IAAI,qBAAqB,SAAS,OAAO;CACrD,OAAO,QAAQ,IAAI,WAAW,UAAU,QAAQ,IAAI,aAAa;AACnE;AA+EA,SAAS,eAAe,QAAkC;CACxD,IAAI,OAAO,SAAS,OAAO,OAAO;CAClC,IAAI,QAAQ,IAAI,mBAAmB,OAAO,QAAQ,IAAI;CAEtD,OAAO,oBADM,QAAQ,IAAI,QAAQ;AAEnC;;;;;AAMA,SAAgB,SAAS,KAAa,QAAwB;CAC5D,IAAI,UAAU,GAAG,OAAO;CACxB,IAAI,OAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;EACnC,QAAQ,IAAI,WAAW,CAAC;EACxB,OAAO,KAAK,KAAK,MAAM,QAAU;CACnC;CACA,QAAQ,SAAS,KAAK;AACxB;;;;;;;;;;;;;AAcA,SAAS,eAAe,QAA0B;CAChD,MAAM,gBAAgB,OAAO,iBAAiB;CAC9C,MAAM,cAAc,OAAO,eAAe;CAC1C,MAAM,gBAAgB,OAAO,kBAAkB;CAE/C,MAAM,SAA2C,EAAE,MAAM,CAAC,EAAE;CAC5D,MAAM,SAA8C,EAAE,MAAM,CAAC,EAAE;;CAE/D,MAAM,mCAAmB,IAAI,IAAuB;CACpD,IAAI,UAAU;CAEd,SAAS,QAAQ,UAAwB;EACvC,IAAI,SAAS,gBACX,iBAAiB,OAAO,SAAS,cAAc;CAEnD;CAEA,SAAS,QAAQ,UAAoB,UAAwB;EAC3D,OAAO,SAAS,CAAC,KAAK,QAAQ;EAC9B,OAAO,SAAS,CAAC,MAAM,CAAC,GAAG;CAC7B;CAEA,eAAe,KAAK,UAAkD;EACpE,MAAM,WAAW,OAAO,SAAS,CAAC,MAAM;EACxC,IAAI,UAAU,OAAO;EACrB,OAAO,IAAI,SAAS,YAAY;GAC9B,OAAO,SAAS,CAAC,WAAW,QAAQ,OAAO,SAAS,CAAC,MAAM,KAAK,IAAI,CAAC;EACvE,CAAC;CACH;CAEA,eAAe,SAAS,UAAwB,UAAmC;EACjF,MAAM,MAAM,GAAG,eAAe,MAAM,EAAE,2BAA2B;EACjE,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,oBAAoB,SAAS;IAC7B,oBAAoB,SAAS;IAC7B,yBAAyB,OAAO,SAAS,OAAO;GAClD;GACA,MAAM,UAAU,SAAS,OAAO;GAChC,QAAQ,YAAY,QAAQ,aAAa;EAC3C,CAAC;EAED,IAAI,SAAS,IAAI;GACf,IAAI;IACF,MAAM,SAAS,MAAM,OAAO;GAC9B,QAAQ,CAGR;GACA,QAAQ,QAAQ;GAChB;EACF;EAEA,MAAM,OAAO,MAAM,SAAS,KAAK;EAEjC,IAAI,SAAS,WAAW,KAAK;GAC3B,IAAI;GACJ,IAAI;IACF,SAAS,KAAK,MAAM,IAAI;GAC1B,QAAQ;IACN,SAAS;GACX;GACA,MAAM,iBAAkB,QAA+C;GACvE,IAAI,OAAO,mBAAmB,UAAU;IAEtC,aAAW,iBAAiB,GAAI,CAAC,CAAC,WAAW,QAAQ,UAAU,QAAQ,CAAC;IACxE;GACF;EACF;EAEA,IAAI,yBAAyB,IAAI,SAAS,MAAM,GAAG;GACjD,QAAQ,QAAQ;GAChB,MACE,oCAAoC,SAAS,UAAU,mBAAmB,SAAS,OAAO,IAAI,MAChG;GACA;EACF;EAEA,IAAI,SAAS,UAAU,aAAa;GAClC,MAAM,OAAqB;IAAE,GAAG;IAAU,SAAS,SAAS,UAAU;GAAE;GACxE,MAAM,UAAU,gBAAgB,MAAM,KAAK,UAAU;GACrD,aAAW,OAAO,CAAC,CAAC,WAAW,QAAQ,UAAU,IAAI,CAAC;EACxD,OAAO;GACL,QAAQ,QAAQ;GAChB,QAAQ,MACN,oCAAoC,SAAS,UAAU,SAAS,SAAS,QAAQ,kBAAkB,SAAS,OAAO,IAAI,MACzH;EACF;CACF;CAEA,eAAe,KAAK,UAAoB;EACtC,OAAO,MAAM;GACX,IAAI,CAAC,SAAS;GACd,MAAM,WAAW,MAAM,KAAK,QAAQ;GACpC,IAAI,CAAC,UAAU;GACf,IAAI;IACF,MAAM,SAAS,UAAU,QAAQ;GACnC,SAAS,KAAK;IACZ,QAAQ,QAAQ;IAChB,QAAQ,MAAM,6CAA6C,SAAS,IAAI,GAAG;GAC7E;EACF;CACF;CAEA,OAAO;;EAEL,SAAS,gBAA2D;GAClE,OAAO,iBAAiB,iBAAiB,IAAI,cAAc,IAAI,KAAA;EACjE;EACA,KAAK,UAAoB,UAAwB,cAAuB;GACtE,IAAI,SAAS,gBACX,iBAAiB,IAAI,SAAS,gBAAgB,UAAU,MAAM,SAAS,SAAS,CAAC;GAEnF,IAAI,gBAAgB,eAAe,GACjC,aAAW,eAAe,GAAI,CAAC,CAAC,WAAW,QAAQ,UAAU,QAAQ,CAAC;QAEtE,QAAQ,UAAU,QAAQ;EAE9B;EACA,MAAM,QAAQ;GACZ,IAAI,SAAS;GACb,UAAU;GACV,KAAU,MAAM;EAClB;EACA,OAAO;GACL,UAAU;GACV,KAAK,MAAM,QAAQ,OAAO,OAAO,MAAM,GAAG,KAAK,MAAM,KAAK,MAAM,EAAE;EACpE;CACF;AACF;AAEA,SAAgB,YAAY,QAA8D;CACxF,MAAM,EAAE,KAAK,iBAAiB;CAC9B,MAAM,cAAc,OAAO,eAAe;CAE1C,MAAM,oBAAoB,iBAAiB;CAC3C,MAAM,WAAW,eAAe,MAAM;CAEtC,MAAM,gBAAgB,UAAiC;EACrD,MAAM,KAAK,IAAI,eAAe,WAAW,KAAK,OAAO;EACrD,OAAO,IAAI,eAAe,IAAI,EAAE;CAClC;CAEA,OAAO;EACL,MAAM,MAAM,WAAW,SAAS,MAAM;GACpC,eAAe,SAAS;GACxB,MAAM,QACJ,WAAW,WAAW,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ,KAAA;GAE5E,IAAI,WAAW,GAAG;IAIhB,MAAM,WAAW,SAAS,SAAS,MAAM,cAAc;IACvD,IAAI,UACF,OAAO,EAAE,WAAW,SAAS;IAE/B,MAAM,YAAY,UAAU,MAAM,OAAO,kBAAkB,GAAG;IAC9D,SAAS,KACP,gBACA;KACE;KACA;KACA,SAAS;KACT;KACA,gBAAgB,MAAM;IACxB,GACA,MAAM,YACR;IACA,OAAO,EAAE,UAAU;GACrB;GAIA,MAAM,YAAY,UAAU,MAAM,OAAO,kBAAkB,GAAG;GAC9D,MAAM,QAAQ,SAAS,MAAM,kBAAkB,WAAW,WAAW;GAErE,MAAM,UAAU,MAAM,aAAa,KAAK,CAAC,CAAC,QAAQ;IAChD;IACA;IACA,UAAU;IACV,MAAM,UAAU,OAAO;IACvB;IACA,gBAAgB,MAAM;IACtB,cAAc,MAAM;IACpB,QAAQ;KACN,eAAe,eAAe,MAAM;KACpC;IACF;GACF,CAAC;GAED,IAAI,CAAC,QAAQ,IAAI;IACf,IAAI,QAAQ,SAAS,eACnB,MAAM,IAAI,gBAAgB,QAAQ,OAAO;IAE3C,MAAM,IAAI,mBAAmB,QAAQ,SAAS,EAAE,QAAQ,IAAI,CAAC;GAC/D;GAEA,OAAO,EAAE,WAAW,UAAU,MAAM,QAAQ,SAAS,EAAE;EACzD;EAEA,mBAAmB,iBAAiB,SAAS;GAQ3C,OAAO,OAAO,QAAiB;IAC7B,MAAM,eAAe,IAAI,QAAQ,IAAI,kBAAkB;IACvD,MAAM,eAAe,IAAI,QAAQ,IAAI,kBAAkB;IACvD,MAAM,aAAa,IAAI,QAAQ,IAAI,uBAAuB;IAE1D,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,cAAc,CAAC,IAAI,MACxD,OAAO,SAAS,KAAK,EAAE,OAAO,mCAAmC,GAAG,EAAE,QAAQ,IAAI,CAAC;IAErF,IAAI,CAAC,aAAa,WAAW,eAAe,GAC1C,OAAO,SAAS,KAAK,EAAE,OAAO,kBAAkB,GAAG,EAAE,QAAQ,IAAI,CAAC;IAEpE,MAAM,UAAU,OAAO,SAAS,YAAY,EAAE;IAC9C,IAAI;KAGF,MAAM,SAAS,MAAM,QADR,MAAe,MAAM,IAAI,KAAK,CACX,GAAG;MACjC;MACA,WAAW;MACX,WAAW;KACb,CAAC;KACD,IAAI,UAAU,OAAO,OAAO,mBAAmB,UAC7C,OAAO,SAAS,KACd,EAAE,gBAAgB,OAAO,eAAe,GACxC;MAAE,QAAQ;MAAK,SAAS,EAAE,eAAe,OAAO,OAAO,cAAc,EAAE;KAAE,CAC3E;KAEF,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;IAC3C,SAAS,OAAO;KAGd,IAAI,iBAAiB,KAAK,GAAG;MAC3B,MAAM,gDAAgD;OACpD,QAAQ,MAAM;OACd,OAAO,OAAO,KAAK;MACrB,CAAC;MACD,OAAO,SAAS,KACd;OAAE,OAAO,OAAO,KAAK;OAAG,WAAW;MAAK,GACxC,EAAE,QAAQ,MAAM,OAAO,CACzB;KACF;KAEA,MAAM,iBAAiB,eAAe,OAAO;KAC7C,MAAM,4DAA4D;MAChE,OAAO,OAAO,KAAK;MACnB;KACF,CAAC;KACD,OAAO,SAAS,KACd;MAAE,OAAO,OAAO,KAAK;MAAG,YAAY;KAAe,GACnD;MAAE,QAAQ;MAAK,SAAS,EAAE,eAAe,OAAO,cAAc,EAAE;KAAE,CACpE;IACF;GACF;EACF;EAEA,MAAM,kBAAkB;GACtB,OAAO;EACT;EAEA,MAAM,QAAQ;GACZ,IAAI,WAAW,GACb,MAAM,SAAS,MAAM;EAGzB;CACF;AACF;;;AC3VA,SAAS,UAAU,MAAsD;CACvE,OAAO;EAAE,OAAO,KAAK;EAAO,QAAQ,KAAK;CAAO;AAClD;;;;;AAMA,SAAS,WACP,MACA,aACA,aACG;CACH,IAAI,gBAAgB,QAAQ;EAC1B,MAAM,UAAU,EAAE,GAAG,KAAK;EAC1B,KAAK,MAAM,OAAO,aAChB,IAAI,OAAO,SACT,OAAO,QAAQ;EAGnB,OAAO;CACT;CACA,OAAO;AACT;;;;AAKA,SAAS,eAAe,MAAY,aAAgC;CAClE,IAAI,gBAAgB,UAAU,cAAc,MAAM;EAChD,MAAM,EAAE,UAAU,GAAG,GAAG,SAAS;EACjC,OAAO;GAAE,UAAU,KAAA;GAAW,GAAG;EAAK;CACxC;CACA,OAAO;AACT;;;;;;;AAQA,SAAS,kBACP,SACA,OACA,MACO;CACP,QAAQ,QAAQ,MAAhB;EACE,KAAK,iBACH,MAAM,IAAI,yBAAyB,KAAK;EAC1C,KAAK,kBACH,MAAM,IAAI,mBAAmB,QAAQ,SAAS,EAAE,QAAQ,IAAI,CAAC;EAC/D,KAAK,kBACH,MAAM,IAAI,kBAAkB,KAAK,iBAAiB,KAAK;EACzD,KAAK,kBACH,MAAM,IAAI,mBAAmB,QAAQ,SAAS,EAAE,QAAQ,IAAI,CAAC;EAC/D,KAAK,mBACH,MAAM,IAAI,oBAAoB,QAAQ,OAAO;EAC/C,KAAK,wBACH,MAAM,IAAI,mBAAmB,QAAQ,SAAS,EAAE,QAAQ,IAAI,CAAC;EAC/D,KAAK,eACH,MAAM,IAAI,gBAAgB,QAAQ,OAAO;EAC3C,KAAK,aACH,MAAM,IAAI,cAAc,QAAQ,SAAS,EAAE,YAAY,QAAQ,kBAAkB,CAAC;EACpF,KAAK,qBACH,MAAM,IAAI,qBAAqB,QAAQ,kBAAkB,GAAG,oBAAoB;CACpF;AACF;AAEA,MAAM,YAAY,QAAkC,kBAAkB,MAAM,QAAQ,GAAG,CAAC;AACxF,MAAM,aAAa,SAAqB,WAAW,MAAM,QAAQ,IAAI,CAAC;AACtE,MAAM,aAAa,SAAqB,WAAW,MAAM,QAAQ,IAAI,CAAC;AACtE,MAAM,cAAc,UAAwB,YAAY,MAAM,QAAQ,KAAK,CAAC;AAE5E,SAAS,WAAc,SAA+B;CACpD,IAAI,CAAC,QAAQ,IACX,MAAM,IAAI,gBAAgB,QAAQ,OAAO;CAE3C,OAAO,QAAQ;AACjB;;AAGA,MAAM,uBAAuB;;;;;;;;AAc7B,SAAS,oBACP,SACA,WACS;CACT,IAAI,cAAc,KAAA,KAAa,YAAY,WAAW,OAAO;CAC7D,OAAO,4BAA4B,OAAO,KAAK,cAAc;AAC/D;AAEA,SAAgB,cAAc,QAA0C;CACtE,MAAM,EAAE,QAAQ;CAChB,MAAM,UAAU;EACd,aAAa,OAAO,kBAAkB;EACtC,aAAa,OAAO,eAAe;CACrC;CACA,MAAM,OAAO,iBAAiB;CAG9B,MAAM,YAAY,UAAqC;EACrD,MAAM,KAAK,IAAI,YAAY,WAAW,KAAK;EAC3C,OAAO,IAAI,YAAY,IAAI,EAAE;CAC/B;CAEA,MAAM,UAAU,OACd,OACA,WACkD;EAElD,MAAM,MAAM,WAAW,MADV,SAAS,KACU,CAAC,CAAC,OAAO,CAAC;EAE1C,IAAI,CAAC,KACH,MAAM,IAAI,yBAAyB,KAAK;EAG1C,OAAO,WAAW,SAAS,GAAG,GAAG,QAAQ,aAAa,CAAC,SAAS,QAAQ,CAAC;CAC3E;CAEA,OAAO;EACL,MAAM;GACJ,KAAK;GAEL,MAAM,KACJ,QACkE;IAClE,MAAM,QAAQ,QAAQ,YAAY,SAAS;IAC3C,MAAM,SAAS,QAAQ,eAAe,OAAO,OAAO,aAAa,KAAK;IACtE,MAAM,UAAU,QAAQ,YAAY,cAAc;IAClD,MAAM,UAGD,CAAC;IACN,IAAI,aAAa,QAAQ,YAAY;IACrC,IAAI,YAAY;IAKhB,OAAO,QAAQ,UAAU,SAAS,CAAC,WAAW;KAC5C,MAAM,SAAS,MAAM,IAAI,eAAe,SAAS;MAC/C;MACA,OAAO,KAAK,IAAI,KAAM,KAAK,IAAI,IAAI,KAAK,CAAC;MACzC,QAAQ;MACR;KACF,CAAC;KACD,IAAI,OAAO,KAAK,WAAW,GAAG;MAC5B,YAAY;MACZ;KACF;KAEA,MAAM,aAAiE,CAAC;KACxE,KAAK,MAAM,OAAO,OAAO,MAAM;MAC7B,MAAM,WAAW,KAAK,MAAM,IAAI,KAAK;MAIrC,IAAI,oBAAoB,SAAS,QAAQ,QAAQ,MAAM,GACrD;MAEF,WAAW,KAAK;OAAE,KAAK,IAAI;OAAM;MAAS,CAAC;KAC7C;KAIA,KAAK,IAAI,SAAS,GAAG,SAAS,WAAW,UAAU,QAAQ,UAAU,QAAQ;MAC3E,MAAM,SAAS,QAAQ,IAAI,QAAQ;MACnC,MAAM,QAAQ,WAAW,MAAM,QAAQ,SAAS,KAAK,IAAI,sBAAsB,MAAM,CAAC;MACtF,MAAM,WAAW,MAAM,QAAQ,IAC7B,MAAM,IAAI,OAAO,EAAE,KAAK,eAAe;OACrC,IAAI;QACF,MAAM,MAAM,MAAM,QAAQ,SAAS,OAAO,EACxC,aAAa,QAAQ,YACvB,CAAC;QACD,OAAO,CAAC,QAAQ,UAAU,IAAI,WAAW,OAAO,SAAS;SAAE;SAAK;QAAI,IAAI;OAC1E,SAAS,OAAO;QACd,IAAI,CAAC,yBAAyB,GAAG,KAAK,KAAK,CAAC,gBAAgB,GAAG,KAAK,GAClE,MAAM;QAER,OAAO;OACT;MACF,CAAC,CACH;MACA,KAAK,MAAM,SAAS,UAClB,IAAI,OAAO,QAAQ,KAAK,KAAK;MAE/B,UAAU,MAAM;KAClB;KAEA,YAAY,OAAO;KACnB,aAAa,OAAO,UAAU,OAAO,KAAK,GAAG,EAAE,CAAC,EAAE;IACpD;IAEA,MAAM,UAAU,QAAQ,SAAS;IACjC,MAAM,OAAO,QAAQ,MAAM,GAAG,KAAK;IAEnC,OAAO;KACL,MAAM,KAAK,KAAK,EAAE,UAAU,GAAG;KAC/B,QAAQ,UAAW,KAAK,GAAG,EAAE,CAAC,EAAE,OAAO,OAAQ;KAC/C;IACF;GACF;EACF;EAEA,QAAQ;GACN,MAAM,OACJ,OACA,MACA,QACsB;IAEtB,IAAI;IACJ,IAAI,KAAK,cAAc,kBAAkB,CAAC,SAAS,UAAU,KAC3D,iBAAiB,QAAQ,KAAK;SACzB,IAAI,CAAC,OACV,MAAM,IAAI,mBAAmB,gDAAgD,EAC3E,QAAQ,IACV,CAAC;SAED,iBAAiB;IAGnB,MAAM,OAAO,SAAS,cAAc;IAEpC,IAAI;IACJ,IAAI;IAGJ,IAAI,KAAK,cAAc,gBAAgB;KACrC,MAAM,QAAQ;MACZ,OAAO;MACP,QAAQ,KAAK;KACf;KACA,MAAM,YAAY,MAAM,IAAI,eAAe,YAAY,KAAK,UAAU,OAAO,KAAK;KAClF,IAAI,UAAU,UAAU;MACtB,cAAc;MACd,gBAAgB;OACd;OACA,aAAa,UAAU;OACvB,OAAO,KAAK,UAAU;MACxB;KACF,OACE,cAAc,UAAU;IAE5B;IAQA,IAAI;IACJ,IAAI;KACF,UAAU,MAAM,KAAK,WAAW;MAC9B,OAAO;MACP;MACA;MACA;MACA,aAAa,eAAe,YAAY;MACxC;KACF,CAAC;IACH,SAAS,OAAO;KACd,IAAI,eAAe;MACjB,IAAI;MACJ,IAAI;OACF,aAAa,MAAM,KAAK,sBAAsB;QAC5C,QAAQ,cAAc,MAAM;QAC5B,OAAO,cAAc;QACrB,SAAS,cAAc,YAAY;OACrC,CAAC;MACH,QAAQ;OAGN,MAAM;MACR;MACA,IAAI,CAAC,WAAW,WACd,MAAM,IAAI,eAAe,uBACvB,cAAc,OACd,cAAc,OACd,cAAc,WAChB;KAEJ;KACA,MAAM;IACR;IAEA,IAAI,CAAC,QAAQ,IAAI;KACf,IAAI,eACF,MAAM,IAAI,eAAe,uBACvB,cAAc,OACd,cAAc,OACd,cAAc,WAChB;KAEF,kBAAkB,SAAS,gBAAgB,IAAI;IACjD;IAIA,IAAI,QAAQ,KAAK;KACf,MAAM,OAAO,KAAK,UAAU;MAC1B,OAAO;MACP,WAAW,QAAQ,IAAI,UAAU,YAAY;MAC7C,QAAQ,QAAQ,IAAI;KACtB,CAAC;KACD,MAAM,IAAI,eAAe,UAAU,QAAQ,KAAK,IAAI;IACtD;IACA,IAAI,QAAQ,aAAa;KACvB,MAAM,aAAa,UAAU,QAAQ,WAAW;KAChD,MAAM,IAAI,eAAe,oBACvB,QAAQ,YAAY,OACpB,QAAQ,YAAY,QACpB,YACA,UAAU,QAAQ,WAAW,GAC7B,eAAe,WACjB;IACF,OAAO,IAAI,eACT,MAAM,IAAI,eAAe,uBACvB,cAAc,OACd,cAAc,OACd,cAAc,WAChB;IAEF,MAAM,WAAW,QAAQ,QAAQ,OAAO,4BAA4B,QAAQ,IAAI,MAAM,CAAC;IACvF,IAAI,QAAQ,cAAc,SAAS,GACjC,MAAM,IAAI,eAAe,mBAAmB;KAC1C,OAAO;KACP,OAAO,QAAQ;KACf;IACF,CAAC;IAGH,MAAM,YACJ,QAAQ,WAAW,KAAA,IACf,CAAC,IACD;KACE,QAAQ,QAAQ;KAChB,QAAQ,QAAQ,UAAU;KAC1B,SAAS,QAAQ,WAAW;IAC9B;IACN,OAAO;KACL,OAAO,QAAQ;KACf,KAAK,QAAQ;KACb,MAAM,QAAQ;KACd,MAAM,QAAQ;KACd,MAAM,QAAQ;KACd,aAAa,QAAQ;KACrB,WAAW,QAAQ;KACnB,GAAG;IACL;GACF;GAEA,MAAM,IAAI,OAAe,SAAiB,SAA0C;IAElF,MAAM,QAAQ,WAAW,MADZ,SAAS,KACY,CAAC,CAAC,SAAS,OAAO,CAAC;IAErD,IAAI,CAAC,OACH,MAAM,IAAI,mBAAmB,oBAAoB,WAAW,EAC1D,QAAQ,IACV,CAAC;IAGH,OAAO,WAAW,KAAK;GACzB;GAEA,MAAM,KAAK,QAA6D;IACtE,MAAM,EAAE,UAAU;IAClB,MAAM,QAAQ,QAAQ,YAAY,SAAS;IAG3C,MAAM,SAAS,WACb,MAFW,SAAS,KAEX,CAAC,CAAC,WAAW;KACpB;KACA,QAAQ,QAAQ,YAAY,UAAU,KAAA;KACtC,WAAW,OAAO,YAAY,aAAa;IAC7C,CAAC,CACH;IAEA,OAAO;KACL,MAAM,OAAO,KAAK,IAAI,UAAU;KAChC,QAAQ,OAAO;KACf,SAAS,OAAO;IAClB;GACF;GAEA,MAAM,oBAAoB,QAAQ;IAChC,MAAM,QAAQ,OAAO,YAAY,SAAS;IAC1C,MAAM,OAAO,SAAS,OAAO,KAAK;IAClC,MAAM,UAAmB,CAAC;IAC1B,IAAI,aAAa,OAAO,YAAY;IACpC,IAAI,YAAY;IAKhB,OAAO,QAAQ,UAAU,SAAS,CAAC,WAAW;KAC5C,MAAM,OAAO,WACX,MAAM,KAAK,WAAW;MACpB,OAAO,KAAK,IAAI,KAAM,KAAK,IAAI,IAAI,QAAQ,CAAC,CAAC;MAC7C,QAAQ;MACR,WAAW,OAAO,YAAY,aAAa;KAC7C,CAAC,CACH;KACA,KAAK,MAAM,SAAS,KAAK,MACvB,IAAI,MAAM,kBAAkB,OAAO,eAAe;MAChD,QAAQ,KAAK,KAAK;MAClB,IAAI,QAAQ,SAAS,OAAO;KAC9B;KAEF,YAAY,CAAC,KAAK;KAClB,aAAa,KAAK,UAAU,KAAK,KAAK,GAAG,EAAE,CAAC,EAAE;KAC9C,IAAI,KAAK,KAAK,WAAW,GAAG,YAAY;IAC1C;IAEA,MAAM,UAAU,QAAQ,SAAS;IACjC,MAAM,OAAO,QAAQ,MAAM,GAAG,KAAK;IACnC,OAAO;KACL,MAAM,KAAK,IAAI,UAAU;KACzB,QAAQ,UAAW,KAAK,GAAG,EAAE,CAAC,EAAE,WAAW,OAAQ;KACnD;IACF;GACF;EACF;EAEA,OAAO;GACL,MAAM,IAAI,OAA2B,QAAgB,QAAwB;IAC3E,IAAI,CAAC,OACH,MAAM,IAAI,mBAAmB,gDAAgD,EAC3E,QAAQ,IACV,CAAC;IAGH,MAAM,OAAO,WAAW,MADX,SAAS,KACW,CAAC,CAAC,QAAQ,MAAM,CAAC;IAElD,IAAI,CAAC,MACH,MAAM,IAAI,mBAAmB,mBAAmB,UAAU,EACxD,QAAQ,IACV,CAAC;IAGH,OAAO,WAAW,UAAU,IAAI,GAAG,QAAQ,aAAa,CAAC,SAAS,QAAQ,CAAC;GAC7E;GAEA,MAAM,KACJ,QACoD;IACpD,MAAM,EAAE,UAAU;IAClB,MAAM,QAAQ,QAAQ,YAAY,SAAS;IAG3C,MAAM,SAAS,WACb,MAFW,SAAS,KAEX,CAAC,CAAC,UAAU;KACnB;KACA,QAAQ,QAAQ,YAAY,UAAU,KAAA;KACtC,WAAW,QAAQ,YAAY,aAAa;IAC9C,CAAC,CACH;IAEA,OAAO;KACL,MAAM,OAAO,KAAK,KAAK,MACrB,WAAW,UAAU,CAAC,GAAG,QAAQ,aAAa,CAAC,SAAS,QAAQ,CAAC,CACnE;KACA,QAAQ,OAAO;KACf,SAAS,OAAO;IAClB;GACF;EACF;EAEA,OAAO;GACL,MAAM,IAAI,QAAgB,QAAwB;IAChD,MAAM,MAAM,MAAM,IAAI,eAAe,YAAY,MAAM;IAEvD,IAAI,CAAC,KACH,MAAM,IAAI,kBAAkB,MAAM;IAIpC,OAAO,eADM,UAAU,MAAY,GAAG,CACb,GAAG,QAAQ,eAAe,KAAK;GAC1D;GAEA,MAAM,WAAW,OAAe,QAAwB;IACtD,MAAM,MAAM,MAAM,IAAI,eAAe,eAAe,KAAK;IAEzD,IAAI,CAAC,KACH,MAAM,IAAI,kBAAkB,KAAK;IAInC,OAAO,eADM,UAAU,MAAY,GAAG,CACb,GAAG,QAAQ,eAAe,KAAK;GAC1D;GAEA,MAAM,KAAK,QAA2D;IACpE,IAAI,CAAC,OAAO,OACV,MAAM,IAAI,mBAAmB,uCAAuC,EAClE,QAAQ,IACV,CAAC;IAEH,MAAM,QAAQ,OAAO;IACrB,MAAM,QAAQ,QAAQ,YAAY,SAAS;IAG3C,MAAM,SAAS,WACb,MAFW,SAAS,KAEX,CAAC,CAAC,UAAU;KACnB;KACA,QAAQ,QAAQ,YAAY,UAAU,KAAA;KACtC,WAAW,QAAQ,YAAY,aAAa;IAC9C,CAAC,CACH;IAEA,OAAO;KACL,MAAM,OAAO,KAAK,KAAK,MAAM,eAAe,UAAU,CAAC,GAAG,QAAQ,eAAe,KAAK,CAAC;KACvF,QAAQ,OAAO;KACf,SAAS,OAAO;IAClB;GACF;EACF;CACF;AACF;;;ACzhBA,MAAM,uBAAuB;AAC7B,MAAM,0BAAA;AACN,MAAM,sBAAA;AACN,MAAM,+BAA+B;AAErC,SAAS,QAAQ,OAAwC;CACvD,OAAO,OAAO,UAAU,WAAW,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,IAAI;AACvE;AAEA,SAAS,cAAc,QAAgC;CACrD,IAAI,OAAO,UAAU,WAAW,MAAM,IAAI,MAAM,oBAAoB;CACpE,IAAI,OAAO,UAAU,WAAW;EAC9B,MAAM,QAAQ,IAAI,MAAM,OAAO,OAAO,WAAW,eAAe;EAChE,MAAM,OAAO,OAAO,OAAO,QAAQ;EACnC,MAAM;CACR;AACF;AAEA,SAAgB,eAAe,QAA4C;CACzE,MAAM,EAAE,QAAQ;CAChB,MAAM,mBAAmB,OAAO,oBAAoB;CACpD,IACE,CAAC,OAAO,cAAc,gBAAgB,KACtC,mBAAmB,KACnB,mBAAA,KAEA,MAAM,IAAI,MAAM,0CAA0C,yBAAyB;CAErF,MAAM,wBAAwB,OAAO,yBAAyB;CAC9D,IAAI,CAAC,OAAO,cAAc,qBAAqB,KAAK,wBAAwB,GAC1E,MAAM,IAAI,MAAM,2DAA2D;CAG7E,MAAM,eAAe,eAAqC;EACxD,MAAM,KAAK,IAAI,iBAAiB,WAAW,UAAU,YAAY;EACjE,OAAO,IAAI,iBAAiB,IAAI,EAAE;CACpC;CAEA,MAAM,oBAAoB,UAAgC;EACxD,MAAM,KAAK,IAAI,iBAAiB,WAAW,eAAe,OAAO;EACjE,OAAO,IAAI,iBAAiB,IAAI,EAAE;CACpC;;CAGA,MAAM,oCAAoB,IAAI,IAAY;CAE1C,eAAe,qBAAqB,MAAc,OAA8B;EAC9E,MAAM,WAAW,GAAG,MAAM,IAAI;EAC9B,IAAI,kBAAkB,IAAI,QAAQ,GAAG;EACrC,MAAM,iBAAiB,KAAK,CAAC,CAAC,eAAe,OAAO,IAAI;EACxD,IAAI,kBAAkB,QAAQ,8BAA8B;GAC1D,MAAM,SAAS,kBAAkB,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;GACjD,IAAI,WAAW,KAAA,GAAW,kBAAkB,OAAO,MAAM;EAC3D;EACA,kBAAkB,IAAI,QAAQ;CAChC;CAEA,MAAM,sBAAsB,OAC1B,MACA,OACA,UACkB;EAClB,IAAI,MAAM,WAAW,GAAG;EACxB,MAAM,gBAAgB,MAAM;EAC5B,MAAM,SAAS,MAAM,IAAI,OAAO;EAChC,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,aAAA,SACR,MAAM,IAAI,MAAM,wBAAwB,uBAAuB,OAAO;EAG1E,MAAM,qBAAqB,MAAM,aAAa;EAE9C,IAAI,QAAsB,CAAC;EAC3B,IAAI,aAAa;EACjB,KAAK,MAAM,SAAS,QAAQ;GAC1B,IACE,MAAM,SAAS,MACd,MAAM,WAAA,MACL,aAAa,MAAM,aAAA,UACrB;IACA,MAAM,YAAY,IAAI,CAAC,CAAC,YAAY,eAAe,KAAK;IACxD,QAAQ,CAAC;IACT,aAAa;GACf;GACA,MAAM,KAAK,KAAK;GAChB,cAAc,MAAM;EACtB;EACA,IAAI,MAAM,SAAS,GAAG,MAAM,YAAY,IAAI,CAAC,CAAC,YAAY,eAAe,KAAK;CAChF;CAEA,MAAM,iBACJ,MACA,OACA,UACkB,oBAAoB,MAAM,OAAO,CAAC,KAAK,CAAC;CAE5D,MAAM,cAAc,OAAO,MAAc,UAAmD;EAC1F,MAAM,gBAAgB,MAAM;EAC5B,MAAM,qBAAqB,MAAM,aAAa;EAC9C,MAAM,YAAY,IAAI,CAAC,CAAC,YAAY,aAAa;CACnD;CAEA,MAAM,cAAc,OAClB,MACA,OACA,UACkB;EAClB,MAAM,gBAAgB,MAAM;EAC5B,MAAM,qBAAqB,MAAM,aAAa;EAC9C,MAAM,YAAY,IAAI,CAAC,CAAC,WAAW,eAAe,qBAAqB,KAAK,CAAC;CAC/E;CAEA,MAAM,iBAAiB,OACrB,MACA,OACA,aAAa,MAC2B;EACxC,MAAM,OAAO,YAAY,IAAI;EAE7B,IAAI;EACJ,IAAI,aAAa,GAAG;GAClB,MAAM,OAAO,MAAM,KAAK,WAAW;IACjC;IACA,YAAY;IACZ,WAAW;IACX,UAAU;IACV,QAAQ;GACV,CAAC;GACD,cAAc,IAAI;GAClB,YAAY,KAAK,IAAI,GAAG,KAAK,YAAY,IAAI,UAAU;EACzD,OACE,YAAY;EAGd,IAAI,YAAY;EAChB,MAAM,kBAAkB,IAAI,gBAAgB;EAE5C,OAAO,IAAI,eAA2B;GACpC,MAAM,KAAK,YAAY;IACrB,SAAS;KACP,IAAI,WAAW;KACf,IAAI;KACJ,IAAI;MACF,SAAS,MAAM,KAAK,WAClB;OACE;OACA,YAAY;OACZ,WAAA;OACA,UAAU;OACV,QAAQ;MACV,GACA,gBAAgB,MAClB;KACF,SAAS,OAAO;MACd,IAAI,aAAa,gBAAgB,OAAO,SAAS;MACjD,MAAM;KACR;KAEA,IAAI,OAAO,OAAO,SAAS,GAAG;MAC5B,KAAK,MAAM,SAAS,OAAO,QAAQ,WAAW,QAAQ,KAAK;MAC3D,aAAa,OAAO,OAAO;MAC3B;KACF;KACA,cAAc,MAAM;KACpB,IAAI,OAAO,UAAU,UAAU;MAC7B,WAAW,MAAM;MACjB;KACF;IAGF;GACF;GAEA,SAAS;IACP,YAAY;IACZ,gBAAgB,MAAM,IAAI,aAAa,2BAA2B,YAAY,CAAC;GACjF;EACF,CAAC;CACH;CAEA,MAAM,sBAAsB,UAC1B,iBAAiB,KAAK,CAAC,CAAC,YAAY;CAEtC,MAAM,kBAAkB,OACtB,MACA,OACA,YACkC;EAClC,MAAM,iBAAiB,SAAS,SAAS;EACzC,IAAI,CAAC,OAAO,cAAc,cAAc,KAAK,iBAAiB,GAC5D,MAAM,IAAI,MAAM,oDAAoD;EAEtE,MAAM,QAAQ,KAAK,IAAI,gBAAgB,mBAAmB;EAC1D,MAAM,aAAa,SAAS,SAAS,OAAO,SAAS,QAAQ,QAAQ,EAAE,IAAI;EAC3E,IAAI,OAAO,MAAM,UAAU,KAAK,aAAa,GAC3C,MAAM,IAAI,MAAM,0BAA0B,SAAS,QAAQ;EAG7D,MAAM,SAAS,MAAM,YAAY,IAAI,CAAC,CAAC,WAAW;GAChD;GACA;GACA,WAAW;GACX,UAAU;GACV,QAAQ;EACV,CAAC;EACD,cAAc,MAAM;EACpB,MAAM,OAAsB,OAAO,OAAO,KAAK,OAAO,YAAY;GAChE,OAAO,aAAa;GACpB,MAAM;EACR,EAAE;EACF,MAAM,YAAY,aAAa,KAAK;EACpC,MAAM,UAAU,aAAa,OAAO;EAEpC,OAAO;GACL;GACA,QAAQ,UAAU,OAAO,SAAS,IAAI;GACtC;GACA,MAAM,OAAO,UAAU;EACzB;CACF;CAEA,MAAM,gBAAgB,OAAO,MAAc,UAA+C;EACxF,MAAM,SAAS,MAAM,YAAY,IAAI,CAAC,CAAC,WAAW;GAChD;GACA,YAAY;GACZ,WAAW;GACX,UAAU;GACV,QAAQ;EACV,CAAC;EACD,cAAc,MAAM;EACpB,OAAO;GAAE,WAAW,OAAO;GAAW,MAAM,OAAO,UAAU;EAAS;CACxE;CAEA,OAAO;EACL;EACA,SAAS;GACP,QAAQ,OAAO,MAAM,UAAU,cAAc,MAAM,OAAO,KAAK;GAC/D,aAAa,OAAO,MAAM,WAAW,oBAAoB,MAAM,OAAO,MAAM;GAC5E,QAAQ,OAAO,SAAS,YAAY,MAAM,KAAK;GAC/C,OAAO,OAAO,MAAM,UAAU,YAAY,MAAM,OAAO,KAAK;GAC5D,MAAM,OAAO,MAAM,eAAe,eAAe,MAAM,OAAO,UAAU;GACxE,MAAM;GACN,YAAY,OAAO,MAAM,YAAY,gBAAgB,MAAM,OAAO,OAAO;GACzE,UAAU,OAAO,SAAS,cAAc,MAAM,KAAK;EACrD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;;AChTA,SAAgB,iBAAiB,QAAuC;CACtE,MAAM,WAAW,cAAc,MAAM;CAGrC,IAAI,MAAM,SAAS;CACnB,IAAI,CAAC,KAAK;EACR,MAAM,YAAa,WAA6C;EAChE,IAAI,WACF,MAAM;CAEV;CAEA,IAAI,CAAC,KACC;MAAA,SAAS,UAAU;GACrB,IAAI,CAAC,SAAS,QACZ,MAAM,IAAI,MACR,8EACF;GAEF,MAAM,gBAAgB;IACpB,UAAU,SAAS;IACnB,QAAQ,SAAS;IACjB,WAAW,SAAS;GACtB,CAAC;EACH;;CAGF,IAAI,CAAC,KACH,MAAM,IAAI,MACR,4MAGF;CAGF,MAAM,UAAU,cAAc;EAC5B,KAAK;GACH,aAAa,IAAI;GACjB,gBAAgB,IAAI;EACtB;EACA,cAAc,SAAS;EACvB,gBAAgB,SAAS;EACzB,aAAa,SAAS;CACxB,CAAC;CAED,MAAM,QAAQ,YAAY;EACxB,KAAK,EACH,gBAAgB,IAAI,eACtB;EACA,cAAc,SAAS;EACvB,SAAS,SAAS;EAClB,aAAa,SAAS;CACxB,CAAC;CAED,MAAM,WAAW,eAAe;EAC9B,KAAK,EACH,kBAAkB,IAAI,iBACxB;EACA,kBAAkB,SAAS;EAC3B,uBAAuB,SAAS;CAClC,CAAC;CAED,MAAM,WAAW,UAAkB,IAAI,YAAY,IAAI,IAAI,YAAY,WAAW,KAAK,CAAC;CACxF,MAAM,YAAiC;EACrC,YAAY,UAAU,QAAQ,KAAK,CAAC,CAAC,iBAAiB;EACtD,WAAW,UAAU;GACnB,IAAI,SAAS,mBAAmB,GAC9B,MAAM,IAAI,MACR,2EACF;GAEF,OAAO,QAAQ,KAAK,CAAC,CAAC,gBAAgB;IACpC,aAAa,SAAS;IACtB,aAAa,SAAS;GACxB,CAAC;EACH;EACA,aAAa,UACX,QAAQ,KAAK,CAAC,CAAC,WAAW;GACxB,aAAa,KAAK,IAAI,GAAG,SAAS,cAAc;GAChD,aAAa,SAAS;EACxB,CAAC;EACH,QAAQ,UAAU,QAAQ,KAAK,CAAC,CAAC,aAAa;CAChD;CAEA,OAAO;EACL,GAAG;EACH,GAAG;EACH,GAAG;EACH;EAIA,aAAa;CACf;AACF"}
|
package/dist/testing.d.ts
CHANGED
|
@@ -18,6 +18,27 @@ interface FakeStorageMutation {
|
|
|
18
18
|
key: string;
|
|
19
19
|
value?: unknown;
|
|
20
20
|
}
|
|
21
|
+
interface FakeStorageOperationCounts {
|
|
22
|
+
get: number;
|
|
23
|
+
getMany: number;
|
|
24
|
+
list: number;
|
|
25
|
+
put: number;
|
|
26
|
+
putMany: number;
|
|
27
|
+
delete: number;
|
|
28
|
+
deleteMany: number;
|
|
29
|
+
transaction: number;
|
|
30
|
+
}
|
|
31
|
+
interface FakeStorageListCall {
|
|
32
|
+
options: FakeListOptions;
|
|
33
|
+
resultSize: number;
|
|
34
|
+
transactional: boolean;
|
|
35
|
+
}
|
|
36
|
+
interface FakeStorageOperationCall {
|
|
37
|
+
operation: 'get' | 'put' | 'delete';
|
|
38
|
+
keys: string[];
|
|
39
|
+
transactional: boolean;
|
|
40
|
+
synchronous: boolean;
|
|
41
|
+
}
|
|
21
42
|
/**
|
|
22
43
|
* Fake DurableObjectStorage over a Map. Transactions stage writes and commit
|
|
23
44
|
* on success (discard on throw), with reads seeing staged state — the
|
|
@@ -27,22 +48,41 @@ declare class FakeStorage {
|
|
|
27
48
|
private clock;
|
|
28
49
|
data: Map<string, unknown>;
|
|
29
50
|
alarmAt: number | null;
|
|
51
|
+
readonly getManyCalls: string[][];
|
|
52
|
+
readonly operationCounts: FakeStorageOperationCounts;
|
|
53
|
+
private transactionTail;
|
|
54
|
+
private transactionDelayMs;
|
|
55
|
+
/** Deterministic query-shape instrumentation for storage scaling tests. */
|
|
56
|
+
listCalls: FakeStorageListCall[];
|
|
57
|
+
operationCalls: FakeStorageOperationCall[];
|
|
30
58
|
private mutationFailure?;
|
|
31
59
|
constructor(clock?: () => number);
|
|
32
60
|
get<T>(key: string): Promise<T | undefined>;
|
|
61
|
+
get<T>(keys: string[]): Promise<Map<string, T>>;
|
|
33
62
|
put<T>(key: string, value: T): Promise<void>;
|
|
63
|
+
put<T>(entries: Record<string, T>): Promise<void>;
|
|
34
64
|
delete(key: string): Promise<boolean>;
|
|
65
|
+
delete(keys: string[]): Promise<number>;
|
|
35
66
|
deleteAll(): Promise<void>;
|
|
36
67
|
list<T>(options?: FakeListOptions): Promise<Map<string, T>>;
|
|
37
68
|
transaction<T>(cb: (txn: FakeTransaction) => Promise<T>): Promise<T>;
|
|
69
|
+
/** Add deterministic per-transaction work for contention benchmarks. */
|
|
70
|
+
setTransactionDelay(ms: number): void;
|
|
38
71
|
getAlarm(): Promise<number | null>;
|
|
39
72
|
setAlarm(at: number | Date): Promise<void>;
|
|
40
73
|
deleteAlarm(): Promise<void>;
|
|
41
74
|
now(): number;
|
|
75
|
+
resetOperationCounts(): void;
|
|
76
|
+
/** @internal Used by FakeTransaction to count the same platform operations. */
|
|
77
|
+
recordOperation(operation: keyof FakeStorageOperationCounts): void;
|
|
78
|
+
/** @internal Shared with FakeTransaction for deterministic key tracing. */
|
|
79
|
+
recordStorageCall(operation: FakeStorageOperationCall['operation'], keys: string[], transactional: boolean): void;
|
|
42
80
|
/** Inject one matching write failure, including writes inside transactions. */
|
|
43
81
|
failNextMutation(predicate: (mutation: FakeStorageMutation) => boolean, error?: Error): void;
|
|
44
82
|
/** @internal Shared with FakeTransaction so failure injection survives a transaction fix. */
|
|
45
83
|
maybeFailMutation(mutation: FakeStorageMutation): void;
|
|
84
|
+
/** @internal Shared with FakeTransaction for deterministic query tracing. */
|
|
85
|
+
recordList(options: FakeListOptions, resultSize: number, transactional: boolean): void;
|
|
46
86
|
}
|
|
47
87
|
declare class FakeTransaction {
|
|
48
88
|
private storage;
|
|
@@ -51,8 +91,12 @@ declare class FakeTransaction {
|
|
|
51
91
|
private stagedAlarm;
|
|
52
92
|
constructor(storage: FakeStorage);
|
|
53
93
|
get<T>(key: string): Promise<T | undefined>;
|
|
94
|
+
get<T>(keys: string[]): Promise<Map<string, T>>;
|
|
54
95
|
put<T>(key: string, value: T): Promise<void>;
|
|
96
|
+
put<T>(entries: Record<string, T>): Promise<void>;
|
|
55
97
|
delete(key: string): Promise<boolean>;
|
|
98
|
+
delete(keys: string[]): Promise<number>;
|
|
99
|
+
private deleteOne;
|
|
56
100
|
list<T>(options?: FakeListOptions): Promise<Map<string, T>>;
|
|
57
101
|
getAlarm(): Promise<number | null>;
|
|
58
102
|
setAlarm(at: number | Date): Promise<void>;
|
|
@@ -95,7 +139,10 @@ declare class FakeFleet {
|
|
|
95
139
|
}): Promise<void>;
|
|
96
140
|
};
|
|
97
141
|
};
|
|
142
|
+
private instantiate;
|
|
98
143
|
cell(bindingKey: string, name: string): CellSlot;
|
|
144
|
+
/** Recreate one in-memory DO instance while preserving its durable storage. */
|
|
145
|
+
restartCell(bindingKey: string, name: string): InstanceType<CellClass>;
|
|
99
146
|
advance(ms: number): void;
|
|
100
147
|
/**
|
|
101
148
|
* Dispatch every alarm due at the virtual current time, awaiting waitUntil
|
package/dist/testing.d.ts.map
CHANGED
|
@@ -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;
|
|
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;;UAGe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf,SAAS;EACT;EACA;;UAGe;EACf;EACA;EACA;EACA;;;;;;;cAmCW;UAwBS;EAvBpB,MAAI;EACJ;WACS;WACA,iBAAiB;UAUlB;UACA;;EAER,WAAW;EACX,gBAAgB;UACR;EAKR,YAAoB;EAEd,IAAI,GAAG,cAAc,QAAQ;EAC7B,IAAI,GAAG,iBAAiB,QAAQ,YAAY;EAe5C,IAAI,GAAG,aAAa,OAAO,IAAI;EAC/B,IAAI,GAAG,SAAS,eAAe,KAAK;EAmBpC,OAAO,cAAc;EACrB,OAAO,iBAAiB;EAgBxB,aAAa;EAIb,KAAK,GAAG,UAAS,kBAAuB,QAAQ,YAAY;EAO5D,YAAY,GAAG,KAAK,KAAK,oBAAoB,QAAQ,KAAK,QAAQ;;EAsBxE,oBAAoB;EAKd,YAAY;EAIZ,SAAS,aAAa,OAAO;EAI7B,eAAe;EAIrB;EAIA;;EASA,gBAAgB,iBAAiB;;EAKjC,kBACE,WAAW,uCACX,gBACA;;EAMF,iBACE,YAAY,UAAU,iCACtB,QAAK;;EAMP,kBAAkB,UAAU;;EAQ5B,WAAW,SAAS,iBAAiB,oBAAoB;;cAKrD;UAKgB;UAJZ;UACA;UACA;EAER,YAAoB,SAAS;EAEvB,IAAI,GAAG,cAAc,QAAQ;EAC7B,IAAI,GAAG,iBAAiB,QAAQ,YAAY;EAsB5C,IAAI,GAAG,aAAa,OAAO,IAAI;EAC/B,IAAI,GAAG,SAAS,eAAe,KAAK;EAqBpC,OAAO,cAAc;EACrB,OAAO,iBAAiB;UActB;EAQF,KAAK,GAAG,UAAS,kBAAuB,QAAQ,YAAY;EAU5D,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;;;UAyChC;EAqBR,KAAK,oBAAoB,eAAe;;EAmBxC,YAAY,oBAAoB,eAAe,aAAa;EAM5D,QAAQ;;;;;;;EAUF,iBAAiB;UAsBT;;EAQR,UAAU;;;;UC1dD;EACf;EACA,OAAO;EACP,SAAS;;UAGM;EACf;;EAEA;;EAEA,eAAe,oBAAoB,cAAc;;EAEjD,UAAU;;iBAGU,aAAa,UAAS,iBAAsB,QAAQ;;;UCnBzD,iBAAiB;EAChC,QAAQ;;iBAMY,cAAc,UAAS,iBAAsB,QAAQ"}
|