@coji/durably 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/durably.ts","../src/events.ts","../src/job.ts","../src/migrations.ts","../src/storage.ts","../src/worker.ts","../src/errors.ts","../src/context.ts","../src/define-job.ts","../src/http.ts","../src/sse.ts","../src/server.ts"],"sourcesContent":["import type { Dialect } from 'kysely'\nimport { Kysely } from 'kysely'\nimport type { z } from 'zod'\nimport type { JobDefinition } from './define-job'\nimport {\n type AnyEventInput,\n type DurablyEvent,\n type ErrorHandler,\n type EventEmitter,\n type EventListener,\n type EventType,\n type Unsubscribe,\n createEventEmitter,\n} from './events'\nimport {\n type JobHandle,\n type JobRegistry,\n createJobHandle,\n createJobRegistry,\n} from './job'\nimport { runMigrations } from './migrations'\nimport type { Database } from './schema'\nimport {\n type Run,\n type RunFilter,\n type Storage,\n createKyselyStorage,\n} from './storage'\nimport { type Worker, createWorker } from './worker'\n\n/**\n * Options for creating a Durably instance\n */\nexport interface DurablyOptions<\n TLabels extends Record<string, string> = Record<string, string>,\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n TJobs extends Record<string, JobDefinition<string, any, any>> = Record<\n string,\n never\n >,\n> {\n dialect: Dialect\n pollingInterval?: number\n heartbeatInterval?: number\n staleThreshold?: number\n /**\n * Zod schema for labels. When provided:\n * - Labels are type-checked at compile time\n * - Labels are validated at runtime on trigger()\n */\n labels?: z.ZodType<TLabels>\n /**\n * Job definitions to register. Shorthand for calling .register() after creation.\n * @example\n * ```ts\n * const durably = createDurably({\n * dialect,\n * jobs: { importCsv: importCsvJob, syncUsers: syncUsersJob },\n * })\n * ```\n */\n jobs?: TJobs\n}\n\n/**\n * Default configuration values\n */\nconst DEFAULTS = {\n pollingInterval: 1000,\n heartbeatInterval: 5000,\n staleThreshold: 30000,\n} as const\n\n/**\n * Plugin interface for extending Durably\n */\nexport interface DurablyPlugin {\n name: string\n // biome-ignore lint/suspicious/noExplicitAny: plugin needs to accept any Durably instance\n install(durably: Durably<any, any>): void\n}\n\n/**\n * Helper type to transform JobDefinition record to JobHandle record\n */\ntype TransformToHandles<\n TJobs extends Record<string, JobDefinition<string, unknown, unknown>>,\n TLabels extends Record<string, string> = Record<string, string>,\n> = {\n [K in keyof TJobs]: TJobs[K] extends JobDefinition<\n infer TName,\n infer TInput,\n infer TOutput\n >\n ? JobHandle<TName & string, TInput, TOutput, TLabels>\n : never\n}\n\n/**\n * Durably instance with type-safe jobs\n */\nexport interface Durably<\n TJobs extends Record<\n string,\n JobHandle<string, unknown, unknown, Record<string, string>>\n > = Record<string, never>,\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n /**\n * Registered job handles (type-safe)\n */\n readonly jobs: TJobs\n\n /**\n * Initialize Durably: run migrations and start the worker\n * This is the recommended way to start Durably.\n * Equivalent to calling migrate() then start().\n * @example\n * ```ts\n * const durably = createDurably({ dialect }).register({ ... })\n * await durably.init()\n * ```\n */\n init(): Promise<void>\n\n /**\n * Run database migrations\n * This is idempotent and safe to call multiple times\n */\n migrate(): Promise<void>\n\n /**\n * Get the underlying Kysely database instance\n * Useful for testing and advanced use cases\n */\n readonly db: Kysely<Database>\n\n /**\n * Storage layer for database operations\n */\n readonly storage: Storage\n\n /**\n * Register an event listener\n * @returns Unsubscribe function\n */\n on<T extends EventType>(type: T, listener: EventListener<T>): Unsubscribe\n\n /**\n * Emit an event (auto-assigns timestamp and sequence)\n */\n emit(event: AnyEventInput): void\n\n /**\n * Register an error handler for listener exceptions\n */\n onError(handler: ErrorHandler): void\n\n /**\n * Register job definitions and return a new Durably instance with type-safe jobs\n * @example\n * ```ts\n * const durably = createDurably({ dialect })\n * .register({\n * importCsv: importCsvJob,\n * syncUsers: syncUsersJob,\n * })\n * await durably.migrate()\n * // Usage: durably.jobs.importCsv.trigger({ rows: [...] })\n * ```\n */\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n register<TNewJobs extends Record<string, JobDefinition<string, any, any>>>(\n jobDefs: TNewJobs,\n ): Durably<TJobs & TransformToHandles<TNewJobs, TLabels>, TLabels>\n\n /**\n * Start the worker polling loop\n */\n start(): void\n\n /**\n * Stop the worker after current run completes\n */\n stop(): Promise<void>\n\n /**\n * Retry a failed run by resetting it to pending\n * @throws Error if run is not in failed status\n */\n retry(runId: string): Promise<void>\n\n /**\n * Cancel a pending or running run\n * @throws Error if run is already completed, failed, or cancelled\n */\n cancel(runId: string): Promise<void>\n\n /**\n * Delete a completed, failed, or cancelled run and its associated steps and logs\n * @throws Error if run is pending or running, or does not exist\n */\n deleteRun(runId: string): Promise<void>\n\n /**\n * Get a run by ID\n * @example\n * ```ts\n * // Untyped (returns Run)\n * const run = await durably.getRun(runId)\n *\n * // Typed (returns custom type)\n * type MyRun = Run & { input: { userId: string }; output: { count: number } | null }\n * const typedRun = await durably.getRun<MyRun>(runId)\n * ```\n */\n getRun<T extends Run<TLabels> = Run<TLabels>>(\n runId: string,\n ): Promise<T | null>\n\n /**\n * Get runs with optional filtering\n * @example\n * ```ts\n * // Untyped (returns Run[])\n * const runs = await durably.getRuns({ status: 'completed' })\n *\n * // Typed (returns custom type[])\n * type MyRun = Run & { input: { userId: string }; output: { count: number } | null }\n * const typedRuns = await durably.getRuns<MyRun>({ jobName: 'my-job' })\n * ```\n */\n getRuns<T extends Run<TLabels> = Run<TLabels>>(\n filter?: RunFilter<TLabels>,\n ): Promise<T[]>\n\n /**\n * Register a plugin\n */\n use(plugin: DurablyPlugin): void\n\n /**\n * Get a registered job handle by name\n * Returns undefined if job is not registered\n */\n getJob<TName extends string = string>(\n name: TName,\n ): JobHandle<TName, Record<string, unknown>, unknown, TLabels> | undefined\n\n /**\n * Subscribe to events for a specific run\n * Returns a ReadableStream that can be used for SSE\n */\n subscribe(runId: string): ReadableStream<DurablyEvent>\n}\n\n/**\n * Internal state shared across Durably instances\n */\ninterface DurablyState {\n db: Kysely<Database>\n storage: Storage\n eventEmitter: EventEmitter\n jobRegistry: JobRegistry\n worker: Worker\n labelsSchema: z.ZodType | undefined\n migrating: Promise<void> | null\n migrated: boolean\n}\n\n/**\n * Create a Durably instance implementation\n */\nfunction createDurablyInstance<\n TJobs extends Record<\n string,\n JobHandle<string, unknown, unknown, Record<string, string>>\n >,\n TLabels extends Record<string, string> = Record<string, string>,\n>(state: DurablyState, jobs: TJobs): Durably<TJobs, TLabels> {\n const { db, storage, eventEmitter, jobRegistry, worker } = state\n\n const durably: Durably<TJobs, TLabels> = {\n db,\n storage,\n jobs,\n on: eventEmitter.on,\n emit: eventEmitter.emit,\n onError: eventEmitter.onError,\n start: worker.start,\n stop: worker.stop,\n\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n register<TNewJobs extends Record<string, JobDefinition<string, any, any>>>(\n jobDefs: TNewJobs,\n ): Durably<TJobs & TransformToHandles<TNewJobs, TLabels>, TLabels> {\n const newHandles = {} as TransformToHandles<TNewJobs, TLabels>\n\n for (const key of Object.keys(jobDefs) as (keyof TNewJobs)[]) {\n const jobDef = jobDefs[key]\n const handle = createJobHandle(\n jobDef,\n storage,\n eventEmitter,\n jobRegistry,\n state.labelsSchema as z.ZodType<TLabels> | undefined,\n )\n newHandles[key] = handle as TransformToHandles<\n TNewJobs,\n TLabels\n >[typeof key]\n }\n\n // Create new instance with merged jobs\n const mergedJobs = { ...jobs, ...newHandles } as TJobs &\n TransformToHandles<TNewJobs, TLabels>\n return createDurablyInstance<typeof mergedJobs, TLabels>(\n state,\n mergedJobs,\n )\n },\n\n getRun: storage.getRun,\n getRuns: storage.getRuns,\n\n use(plugin: DurablyPlugin): void {\n plugin.install(durably)\n },\n\n getJob<TName extends string = string>(\n name: TName,\n ): JobHandle<TName, Record<string, unknown>, unknown, TLabels> | undefined {\n const registeredJob = jobRegistry.get(name)\n if (!registeredJob) {\n return undefined\n }\n return registeredJob.handle as JobHandle<\n TName,\n Record<string, unknown>,\n unknown,\n TLabels\n >\n },\n\n subscribe(runId: string): ReadableStream<DurablyEvent> {\n // Track closed state and cleanup function in outer scope for cancel handler\n let closed = false\n let cleanup: (() => void) | null = null\n\n // Events that close the stream after enqueuing\n const closeEvents = new Set<EventType>(['run:complete', 'run:delete'])\n // All event types to subscribe to for a run\n const subscribedEvents: EventType[] = [\n 'run:start',\n 'run:complete',\n 'run:fail',\n 'run:cancel',\n 'run:delete',\n 'run:retry',\n 'run:progress',\n 'step:start',\n 'step:complete',\n 'step:fail',\n 'log:write',\n ]\n\n return new ReadableStream<DurablyEvent>({\n start: (controller) => {\n const unsubscribes = subscribedEvents.map((type) =>\n eventEmitter.on(type, (event) => {\n if (closed || event.runId !== runId) return\n controller.enqueue(event)\n if (closeEvents.has(type)) {\n closed = true\n cleanup?.()\n controller.close()\n }\n }),\n )\n\n cleanup = () => {\n for (const unsub of unsubscribes) unsub()\n }\n },\n cancel: () => {\n // Clean up event listeners when stream is cancelled by consumer\n if (!closed) {\n closed = true\n cleanup?.()\n }\n },\n })\n },\n\n async retry(runId: string): Promise<void> {\n const run = await storage.getRun(runId)\n if (!run) {\n throw new Error(`Run not found: ${runId}`)\n }\n if (run.status === 'completed') {\n throw new Error(`Cannot retry completed run: ${runId}`)\n }\n if (run.status === 'pending') {\n throw new Error(`Cannot retry pending run: ${runId}`)\n }\n if (run.status === 'running') {\n throw new Error(`Cannot retry running run: ${runId}`)\n }\n await storage.updateRun(runId, {\n status: 'pending',\n error: null,\n })\n\n // Emit run:retry event\n eventEmitter.emit({\n type: 'run:retry',\n runId,\n jobName: run.jobName,\n labels: run.labels,\n })\n },\n\n async cancel(runId: string): Promise<void> {\n const run = await storage.getRun(runId)\n if (!run) {\n throw new Error(`Run not found: ${runId}`)\n }\n if (run.status === 'completed') {\n throw new Error(`Cannot cancel completed run: ${runId}`)\n }\n if (run.status === 'failed') {\n throw new Error(`Cannot cancel failed run: ${runId}`)\n }\n if (run.status === 'cancelled') {\n throw new Error(`Cannot cancel already cancelled run: ${runId}`)\n }\n await storage.updateRun(runId, {\n status: 'cancelled',\n })\n\n // Emit run:cancel event\n eventEmitter.emit({\n type: 'run:cancel',\n runId,\n jobName: run.jobName,\n labels: run.labels,\n })\n },\n\n async deleteRun(runId: string): Promise<void> {\n const run = await storage.getRun(runId)\n if (!run) {\n throw new Error(`Run not found: ${runId}`)\n }\n if (run.status === 'pending') {\n throw new Error(`Cannot delete pending run: ${runId}`)\n }\n if (run.status === 'running') {\n throw new Error(`Cannot delete running run: ${runId}`)\n }\n await storage.deleteRun(runId)\n\n // Emit run:delete event\n eventEmitter.emit({\n type: 'run:delete',\n runId,\n jobName: run.jobName,\n labels: run.labels,\n })\n },\n\n async migrate(): Promise<void> {\n if (state.migrated) {\n return\n }\n\n if (state.migrating) {\n return state.migrating\n }\n\n state.migrating = runMigrations(db)\n .then(() => {\n state.migrated = true\n })\n .finally(() => {\n state.migrating = null\n })\n\n return state.migrating\n },\n\n async init(): Promise<void> {\n await this.migrate()\n this.start()\n },\n }\n\n return durably\n}\n\n/**\n * Create a Durably instance\n */\n// Overload: with jobs\nexport function createDurably<\n TLabels extends Record<string, string> = Record<string, string>,\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n TJobs extends Record<string, JobDefinition<string, any, any>> = Record<\n string,\n never\n >,\n>(\n options: DurablyOptions<TLabels, TJobs> & { jobs: TJobs },\n): Durably<TransformToHandles<TJobs, TLabels>, TLabels>\n\n// Overload: without jobs\nexport function createDurably<\n TLabels extends Record<string, string> = Record<string, string>,\n>(options: DurablyOptions<TLabels>): Durably<Record<string, never>, TLabels>\n\n// Implementation\nexport function createDurably<\n TLabels extends Record<string, string> = Record<string, string>,\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n TJobs extends Record<string, JobDefinition<string, any, any>> = Record<\n string,\n never\n >,\n>(\n options: DurablyOptions<TLabels, TJobs>,\n):\n | Durably<TransformToHandles<TJobs, TLabels>, TLabels>\n | Durably<Record<string, never>, TLabels> {\n const config = {\n pollingInterval: options.pollingInterval ?? DEFAULTS.pollingInterval,\n heartbeatInterval: options.heartbeatInterval ?? DEFAULTS.heartbeatInterval,\n staleThreshold: options.staleThreshold ?? DEFAULTS.staleThreshold,\n }\n\n const db = new Kysely<Database>({ dialect: options.dialect })\n const storage = createKyselyStorage(db)\n const eventEmitter = createEventEmitter()\n const jobRegistry = createJobRegistry()\n const worker = createWorker(config, storage, eventEmitter, jobRegistry)\n\n const state: DurablyState = {\n db,\n storage,\n eventEmitter,\n jobRegistry,\n worker,\n labelsSchema: options.labels,\n migrating: null,\n migrated: false,\n }\n\n const instance = createDurablyInstance<Record<string, never>, TLabels>(\n state,\n {},\n )\n\n if (options.jobs) {\n return instance.register(options.jobs)\n }\n\n return instance\n}\n","/**\n * Base event interface\n */\nexport interface BaseEvent {\n type: string\n timestamp: string\n sequence: number\n}\n\n/**\n * Run trigger event (emitted when a job is triggered, before worker picks it up)\n */\nexport interface RunTriggerEvent extends BaseEvent {\n type: 'run:trigger'\n runId: string\n jobName: string\n input: unknown\n labels: Record<string, string>\n}\n\n/**\n * Run start event\n */\nexport interface RunStartEvent extends BaseEvent {\n type: 'run:start'\n runId: string\n jobName: string\n input: unknown\n labels: Record<string, string>\n}\n\n/**\n * Run complete event\n */\nexport interface RunCompleteEvent extends BaseEvent {\n type: 'run:complete'\n runId: string\n jobName: string\n output: unknown\n duration: number\n labels: Record<string, string>\n}\n\n/**\n * Run fail event\n */\nexport interface RunFailEvent extends BaseEvent {\n type: 'run:fail'\n runId: string\n jobName: string\n error: string\n failedStepName: string\n labels: Record<string, string>\n}\n\n/**\n * Run cancel event\n */\nexport interface RunCancelEvent extends BaseEvent {\n type: 'run:cancel'\n runId: string\n jobName: string\n labels: Record<string, string>\n}\n\n/**\n * Run delete event (emitted when a run is deleted)\n */\nexport interface RunDeleteEvent extends BaseEvent {\n type: 'run:delete'\n runId: string\n jobName: string\n labels: Record<string, string>\n}\n\n/**\n * Run retry event (emitted when a failed run is retried)\n */\nexport interface RunRetryEvent extends BaseEvent {\n type: 'run:retry'\n runId: string\n jobName: string\n labels: Record<string, string>\n}\n\n/**\n * Progress data reported by step.progress()\n */\nexport interface ProgressData {\n current: number\n total?: number\n message?: string\n}\n\n/**\n * Run progress event\n */\nexport interface RunProgressEvent extends BaseEvent {\n type: 'run:progress'\n runId: string\n jobName: string\n progress: ProgressData\n labels: Record<string, string>\n}\n\n/**\n * Step start event\n */\nexport interface StepStartEvent extends BaseEvent {\n type: 'step:start'\n runId: string\n jobName: string\n stepName: string\n stepIndex: number\n labels: Record<string, string>\n}\n\n/**\n * Step complete event\n */\nexport interface StepCompleteEvent extends BaseEvent {\n type: 'step:complete'\n runId: string\n jobName: string\n stepName: string\n stepIndex: number\n output: unknown\n duration: number\n labels: Record<string, string>\n}\n\n/**\n * Step fail event\n */\nexport interface StepFailEvent extends BaseEvent {\n type: 'step:fail'\n runId: string\n jobName: string\n stepName: string\n stepIndex: number\n error: string\n labels: Record<string, string>\n}\n\nexport interface StepCancelEvent extends BaseEvent {\n type: 'step:cancel'\n runId: string\n jobName: string\n stepName: string\n stepIndex: number\n labels: Record<string, string>\n}\n\n/**\n * Log data reported by step.log\n */\nexport interface LogData {\n level: 'info' | 'warn' | 'error'\n message: string\n data?: unknown\n stepName?: string | null\n}\n\n/**\n * Log write event\n */\nexport interface LogWriteEvent extends BaseEvent, LogData {\n type: 'log:write'\n runId: string\n jobName: string\n labels: Record<string, string>\n stepName: string | null\n data: unknown\n}\n\n/**\n * Worker error event (internal errors like heartbeat failures)\n */\nexport interface WorkerErrorEvent extends BaseEvent {\n type: 'worker:error'\n error: string\n context: string\n runId?: string\n}\n\n/**\n * All event types as discriminated union\n */\nexport type DurablyEvent =\n | RunTriggerEvent\n | RunStartEvent\n | RunCompleteEvent\n | RunFailEvent\n | RunCancelEvent\n | RunDeleteEvent\n | RunRetryEvent\n | RunProgressEvent\n | StepStartEvent\n | StepCompleteEvent\n | StepFailEvent\n | StepCancelEvent\n | LogWriteEvent\n | WorkerErrorEvent\n\n/**\n * Event types for type-safe event names\n */\nexport type EventType = DurablyEvent['type']\n\n/**\n * Extract event by type\n */\nexport type EventByType<T extends EventType> = Extract<\n DurablyEvent,\n { type: T }\n>\n\n/**\n * Event input (without auto-generated fields)\n */\nexport type EventInput<T extends EventType> = Omit<\n EventByType<T>,\n 'timestamp' | 'sequence'\n>\n\n/**\n * All possible event inputs as a union (properly distributed)\n */\nexport type AnyEventInput =\n | EventInput<'run:trigger'>\n | EventInput<'run:start'>\n | EventInput<'run:complete'>\n | EventInput<'run:fail'>\n | EventInput<'run:cancel'>\n | EventInput<'run:delete'>\n | EventInput<'run:retry'>\n | EventInput<'run:progress'>\n | EventInput<'step:start'>\n | EventInput<'step:complete'>\n | EventInput<'step:fail'>\n | EventInput<'step:cancel'>\n | EventInput<'log:write'>\n | EventInput<'worker:error'>\n\n/**\n * Event listener function\n */\nexport type EventListener<T extends EventType> = (event: EventByType<T>) => void\n\n/**\n * Unsubscribe function returned by on()\n */\nexport type Unsubscribe = () => void\n\n/**\n * Error handler function for listener exceptions\n */\nexport type ErrorHandler = (error: Error, event: DurablyEvent) => void\n\n/**\n * Event emitter interface\n */\nexport interface EventEmitter {\n /**\n * Register an event listener\n * @returns Unsubscribe function\n */\n on<T extends EventType>(type: T, listener: EventListener<T>): Unsubscribe\n\n /**\n * Register an error handler for listener exceptions\n */\n onError(handler: ErrorHandler): void\n\n /**\n * Emit an event (auto-assigns timestamp and sequence)\n */\n emit(event: AnyEventInput): void\n}\n\n/**\n * Create an event emitter\n */\nexport function createEventEmitter(): EventEmitter {\n const listeners = new Map<EventType, Set<EventListener<EventType>>>()\n let sequence = 0\n let errorHandler: ErrorHandler | null = null\n\n return {\n on<T extends EventType>(type: T, listener: EventListener<T>): Unsubscribe {\n if (!listeners.has(type)) {\n listeners.set(type, new Set())\n }\n\n const typeListeners = listeners.get(type)\n typeListeners?.add(listener as unknown as EventListener<EventType>)\n\n return () => {\n typeListeners?.delete(listener as unknown as EventListener<EventType>)\n }\n },\n\n onError(handler: ErrorHandler): void {\n errorHandler = handler\n },\n\n emit(event: AnyEventInput): void {\n sequence++\n const fullEvent = {\n ...event,\n timestamp: new Date().toISOString(),\n sequence,\n } as DurablyEvent\n\n const typeListeners = listeners.get(event.type)\n if (!typeListeners) {\n return\n }\n\n for (const listener of typeListeners) {\n try {\n listener(fullEvent)\n } catch (error) {\n if (errorHandler) {\n errorHandler(\n error instanceof Error ? error : new Error(String(error)),\n fullEvent,\n )\n }\n // Continue to next listener regardless of error\n }\n }\n },\n }\n}\n","import { type z, prettifyError } from 'zod'\nimport type { JobDefinition } from './define-job'\nimport type { EventEmitter, LogData, ProgressData } from './events'\nimport type { Run, RunFilter, Storage } from './storage'\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst noop = () => {}\n\n/**\n * Validate job input and throw on failure\n */\nfunction validateJobInputOrThrow<T>(\n schema: z.ZodType<T>,\n input: unknown,\n context?: string,\n): T {\n const result = schema.safeParse(input)\n if (!result.success) {\n const prefix = context ? `${context}: ` : ''\n throw new Error(`${prefix}Invalid input: ${prettifyError(result.error)}`)\n }\n return result.data\n}\n\n/**\n * Step context passed to the job function\n */\nexport interface StepContext {\n /**\n * The ID of the current run\n */\n readonly runId: string\n\n /**\n * Execute a step with automatic persistence and replay\n */\n run<T>(name: string, fn: (signal: AbortSignal) => T | Promise<T>): Promise<T>\n\n /**\n * Report progress for the current run\n */\n progress(current: number, total?: number, message?: string): void\n\n /**\n * Log a message\n */\n log: {\n info(message: string, data?: unknown): void\n warn(message: string, data?: unknown): void\n error(message: string, data?: unknown): void\n }\n}\n\n/**\n * Job function type\n */\nexport type JobFunction<TInput, TOutput> = (\n step: StepContext,\n input: TInput,\n) => Promise<TOutput>\n\n/**\n * Trigger options for trigger() and batchTrigger()\n */\nexport interface TriggerOptions<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n idempotencyKey?: string\n concurrencyKey?: string\n labels?: TLabels\n}\n\n/**\n * Options for triggerAndWait() (extends TriggerOptions with wait-specific options)\n */\nexport interface TriggerAndWaitOptions<\n TLabels extends Record<string, string> = Record<string, string>,\n> extends TriggerOptions<TLabels> {\n /** Timeout in milliseconds */\n timeout?: number\n /** Called when step.progress() is invoked during execution */\n onProgress?: (progress: ProgressData) => void | Promise<void>\n /** Called when step.log is invoked during execution */\n onLog?: (log: LogData) => void | Promise<void>\n}\n\n/**\n * Typed run with output type\n */\nexport interface TypedRun<\n TOutput,\n TLabels extends Record<string, string> = Record<string, string>,\n> extends Omit<Run<TLabels>, 'output'> {\n output: TOutput | null\n}\n\n/**\n * Batch trigger input - either just the input or input with options\n */\nexport type BatchTriggerInput<\n TInput,\n TLabels extends Record<string, string> = Record<string, string>,\n> = TInput | { input: TInput; options?: TriggerOptions<TLabels> }\n\n/**\n * Result of triggerAndWait\n */\nexport interface TriggerAndWaitResult<TOutput> {\n id: string\n output: TOutput\n}\n\n/**\n * Job handle returned by defineJob\n */\nexport interface JobHandle<\n TName extends string,\n TInput,\n TOutput,\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n readonly name: TName\n\n /**\n * Trigger a new run\n */\n trigger(\n input: TInput,\n options?: TriggerOptions<TLabels>,\n ): Promise<TypedRun<TOutput, TLabels>>\n\n /**\n * Trigger a new run and wait for completion\n * Returns the output directly, throws if the run fails\n */\n triggerAndWait(\n input: TInput,\n options?: TriggerAndWaitOptions<TLabels>,\n ): Promise<TriggerAndWaitResult<TOutput>>\n\n /**\n * Trigger multiple runs in a batch\n * All inputs are validated before any runs are created\n */\n batchTrigger(\n inputs: BatchTriggerInput<TInput, TLabels>[],\n ): Promise<TypedRun<TOutput, TLabels>[]>\n\n /**\n * Get a run by ID\n */\n getRun(id: string): Promise<TypedRun<TOutput, TLabels> | null>\n\n /**\n * Get runs with optional filter\n */\n getRuns(\n filter?: Omit<RunFilter<TLabels>, 'jobName'>,\n ): Promise<TypedRun<TOutput, TLabels>[]>\n}\n\n/**\n * Internal job registration\n */\nexport interface RegisteredJob<TInput, TOutput> {\n name: string\n inputSchema: z.ZodType\n outputSchema: z.ZodType | undefined\n labelsSchema: z.ZodType | undefined\n fn: JobFunction<TInput, TOutput>\n jobDef: JobDefinition<string, TInput, TOutput>\n // biome-ignore lint/suspicious/noExplicitAny: handle may have any labels type\n handle: JobHandle<string, TInput, TOutput, any>\n}\n\n/**\n * Job registry for managing registered jobs\n */\nexport interface JobRegistry {\n /**\n * Register a job (called internally by createJobHandle)\n */\n set<TInput, TOutput>(job: RegisteredJob<TInput, TOutput>): void\n\n /**\n * Get a registered job by name\n */\n get(name: string): RegisteredJob<unknown, unknown> | undefined\n\n /**\n * Check if a job is registered\n */\n has(name: string): boolean\n}\n\n/**\n * Create a job registry\n */\nexport function createJobRegistry(): JobRegistry {\n const jobs = new Map<string, RegisteredJob<unknown, unknown>>()\n\n return {\n set<TInput, TOutput>(job: RegisteredJob<TInput, TOutput>): void {\n jobs.set(job.name, job as RegisteredJob<unknown, unknown>)\n },\n\n get(name: string): RegisteredJob<unknown, unknown> | undefined {\n return jobs.get(name)\n },\n\n has(name: string): boolean {\n return jobs.has(name)\n },\n }\n}\n\n/**\n * Create a job handle from a JobDefinition\n */\nexport function createJobHandle<\n TName extends string,\n TInput,\n TOutput,\n TLabels extends Record<string, string> = Record<string, string>,\n>(\n jobDef: JobDefinition<TName, TInput, TOutput>,\n storage: Storage,\n eventEmitter: EventEmitter,\n registry: JobRegistry,\n labelsSchema?: z.ZodType<TLabels>,\n): JobHandle<TName, TInput, TOutput, TLabels> {\n // Check if same JobDefinition is already registered (idempotent)\n const existingJob = registry.get(jobDef.name)\n if (existingJob) {\n // If same JobDefinition (same reference), return existing handle\n if (existingJob.jobDef === jobDef) {\n return existingJob.handle as JobHandle<TName, TInput, TOutput, TLabels>\n }\n // Different JobDefinition with same name - error\n throw new Error(\n `Job \"${jobDef.name}\" is already registered with a different definition`,\n )\n }\n\n const inputSchema = jobDef.input as z.ZodType<TInput>\n const outputSchema = jobDef.output as z.ZodType<TOutput> | undefined\n\n const handle: JobHandle<TName, TInput, TOutput, TLabels> = {\n name: jobDef.name,\n\n async trigger(\n input: TInput,\n options?: TriggerOptions<TLabels>,\n ): Promise<TypedRun<TOutput, TLabels>> {\n // Validate input\n const validatedInput = validateJobInputOrThrow(inputSchema, input)\n\n // Validate labels if schema provided\n if (labelsSchema && options?.labels) {\n validateJobInputOrThrow(labelsSchema, options.labels, 'labels')\n }\n\n // Create the run\n const run = await storage.createRun({\n jobName: jobDef.name,\n input: validatedInput,\n idempotencyKey: options?.idempotencyKey,\n concurrencyKey: options?.concurrencyKey,\n labels: options?.labels,\n })\n\n // Emit run:trigger event\n eventEmitter.emit({\n type: 'run:trigger',\n runId: run.id,\n jobName: jobDef.name,\n input: validatedInput,\n labels: run.labels,\n })\n\n return run as TypedRun<TOutput, TLabels>\n },\n\n async triggerAndWait(\n input: TInput,\n options?: TriggerAndWaitOptions<TLabels>,\n ): Promise<TriggerAndWaitResult<TOutput>> {\n // Trigger the run\n const run = await this.trigger(input, options)\n\n // Wait for completion via event subscription\n return new Promise((resolve, reject) => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n let resolved = false\n\n const unsubscribes: (() => void)[] = []\n\n const cleanup = () => {\n if (resolved) return\n resolved = true\n for (const unsub of unsubscribes) unsub()\n if (timeoutId) {\n clearTimeout(timeoutId)\n }\n }\n\n unsubscribes.push(\n eventEmitter.on('run:complete', (event) => {\n if (event.runId === run.id && !resolved) {\n cleanup()\n resolve({\n id: run.id,\n output: event.output as TOutput,\n })\n }\n }),\n )\n\n unsubscribes.push(\n eventEmitter.on('run:fail', (event) => {\n if (event.runId === run.id && !resolved) {\n cleanup()\n reject(new Error(event.error))\n }\n }),\n )\n\n if (options?.onProgress) {\n const onProgress = options.onProgress\n unsubscribes.push(\n eventEmitter.on('run:progress', (event) => {\n if (event.runId === run.id && !resolved) {\n void Promise.resolve(onProgress(event.progress)).catch(noop)\n }\n }),\n )\n }\n\n if (options?.onLog) {\n const onLog = options.onLog\n unsubscribes.push(\n eventEmitter.on('log:write', (event) => {\n if (event.runId === run.id && !resolved) {\n const { level, message, data, stepName } = event\n void Promise.resolve(\n onLog({ level, message, data, stepName }),\n ).catch(noop)\n }\n }),\n )\n }\n\n // Check current status after subscribing (race condition mitigation)\n // If the run completed before we subscribed, we need to handle it\n storage\n .getRun(run.id)\n .then((currentRun) => {\n if (resolved || !currentRun) return\n if (currentRun.status === 'completed') {\n cleanup()\n resolve({\n id: run.id,\n output: currentRun.output as TOutput,\n })\n } else if (currentRun.status === 'failed') {\n cleanup()\n reject(new Error(currentRun.error || 'Run failed'))\n }\n })\n .catch((error) => {\n if (resolved) return\n cleanup()\n reject(error instanceof Error ? error : new Error(String(error)))\n })\n\n // Set timeout if specified\n if (options?.timeout !== undefined) {\n timeoutId = setTimeout(() => {\n if (!resolved) {\n cleanup()\n reject(\n new Error(`triggerAndWait timeout after ${options.timeout}ms`),\n )\n }\n }, options.timeout)\n }\n })\n },\n\n async batchTrigger(\n inputs: (TInput | { input: TInput; options?: TriggerOptions<TLabels> })[],\n ): Promise<TypedRun<TOutput, TLabels>[]> {\n if (inputs.length === 0) {\n return []\n }\n\n // Normalize inputs to { input, options } format\n const normalized = inputs.map((item) => {\n if (item && typeof item === 'object' && 'input' in item) {\n return item as { input: TInput; options?: TriggerOptions<TLabels> }\n }\n return { input: item as TInput, options: undefined }\n })\n\n // Validate all inputs and labels first (before creating any runs)\n const validated: {\n input: unknown\n options?: TriggerOptions<TLabels>\n }[] = []\n for (let i = 0; i < normalized.length; i++) {\n const validatedInput = validateJobInputOrThrow(\n inputSchema,\n normalized[i].input,\n `at index ${i}`,\n )\n if (labelsSchema && normalized[i].options?.labels) {\n validateJobInputOrThrow(\n labelsSchema,\n normalized[i].options?.labels,\n `labels at index ${i}`,\n )\n }\n validated.push({\n input: validatedInput,\n options: normalized[i].options,\n })\n }\n\n // Create all runs\n const runs = await storage.batchCreateRuns(\n validated.map((v) => ({\n jobName: jobDef.name,\n input: v.input,\n idempotencyKey: v.options?.idempotencyKey,\n concurrencyKey: v.options?.concurrencyKey,\n labels: v.options?.labels,\n })),\n )\n\n // Emit run:trigger events for all created runs\n for (let i = 0; i < runs.length; i++) {\n eventEmitter.emit({\n type: 'run:trigger',\n runId: runs[i].id,\n jobName: jobDef.name,\n input: validated[i].input,\n labels: runs[i].labels,\n })\n }\n\n return runs as TypedRun<TOutput, TLabels>[]\n },\n\n async getRun(id: string): Promise<TypedRun<TOutput, TLabels> | null> {\n const run = await storage.getRun(id)\n if (!run || run.jobName !== jobDef.name) {\n return null\n }\n return run as TypedRun<TOutput, TLabels>\n },\n\n async getRuns(\n filter?: Omit<RunFilter<TLabels>, 'jobName'>,\n ): Promise<TypedRun<TOutput, TLabels>[]> {\n const runs = await storage.getRuns({\n ...filter,\n jobName: jobDef.name,\n })\n return runs as TypedRun<TOutput, TLabels>[]\n },\n }\n\n // Register the job with the handle\n registry.set({\n name: jobDef.name,\n inputSchema,\n outputSchema,\n labelsSchema,\n fn: jobDef.run as JobFunction<unknown, unknown>,\n jobDef: jobDef as JobDefinition<string, TInput, TOutput>,\n handle,\n })\n\n return handle\n}\n","import type { Kysely } from 'kysely'\nimport type { Database } from './schema'\n\n/**\n * Migration definitions\n */\ninterface Migration {\n version: number\n up: (db: Kysely<Database>) => Promise<void>\n}\n\nexport const LATEST_SCHEMA_VERSION = 1\n\nconst migrations: Migration[] = [\n {\n version: 1,\n up: async (db) => {\n // Create runs table\n await db.schema\n .createTable('durably_runs')\n .ifNotExists()\n .addColumn('id', 'text', (col) => col.primaryKey())\n .addColumn('job_name', 'text', (col) => col.notNull())\n .addColumn('input', 'text', (col) => col.notNull())\n .addColumn('status', 'text', (col) => col.notNull())\n .addColumn('idempotency_key', 'text')\n .addColumn('concurrency_key', 'text')\n .addColumn('labels', 'text', (col) => col.notNull().defaultTo('{}'))\n .addColumn('current_step_index', 'integer', (col) =>\n col.notNull().defaultTo(0),\n )\n .addColumn('progress', 'text')\n .addColumn('output', 'text')\n .addColumn('error', 'text')\n .addColumn('heartbeat_at', 'text', (col) => col.notNull())\n .addColumn('started_at', 'text')\n .addColumn('completed_at', 'text')\n .addColumn('created_at', 'text', (col) => col.notNull())\n .addColumn('updated_at', 'text', (col) => col.notNull())\n .execute()\n\n // Create runs indexes\n await db.schema\n .createIndex('idx_durably_runs_job_idempotency')\n .ifNotExists()\n .on('durably_runs')\n .columns(['job_name', 'idempotency_key'])\n .unique()\n .execute()\n\n await db.schema\n .createIndex('idx_durably_runs_status_concurrency')\n .ifNotExists()\n .on('durably_runs')\n .columns(['status', 'concurrency_key'])\n .execute()\n\n await db.schema\n .createIndex('idx_durably_runs_status_created')\n .ifNotExists()\n .on('durably_runs')\n .columns(['status', 'created_at'])\n .execute()\n\n // Create steps table\n await db.schema\n .createTable('durably_steps')\n .ifNotExists()\n .addColumn('id', 'text', (col) => col.primaryKey())\n .addColumn('run_id', 'text', (col) => col.notNull())\n .addColumn('name', 'text', (col) => col.notNull())\n .addColumn('index', 'integer', (col) => col.notNull())\n .addColumn('status', 'text', (col) => col.notNull())\n .addColumn('output', 'text')\n .addColumn('error', 'text')\n .addColumn('started_at', 'text', (col) => col.notNull())\n .addColumn('completed_at', 'text')\n .execute()\n\n // Create steps index\n await db.schema\n .createIndex('idx_durably_steps_run_index')\n .ifNotExists()\n .on('durably_steps')\n .columns(['run_id', 'index'])\n .execute()\n\n // Create logs table\n await db.schema\n .createTable('durably_logs')\n .ifNotExists()\n .addColumn('id', 'text', (col) => col.primaryKey())\n .addColumn('run_id', 'text', (col) => col.notNull())\n .addColumn('step_name', 'text')\n .addColumn('level', 'text', (col) => col.notNull())\n .addColumn('message', 'text', (col) => col.notNull())\n .addColumn('data', 'text')\n .addColumn('created_at', 'text', (col) => col.notNull())\n .execute()\n\n // Create logs index\n await db.schema\n .createIndex('idx_durably_logs_run_created')\n .ifNotExists()\n .on('durably_logs')\n .columns(['run_id', 'created_at'])\n .execute()\n\n // Create schema_versions table\n await db.schema\n .createTable('durably_schema_versions')\n .ifNotExists()\n .addColumn('version', 'integer', (col) => col.primaryKey())\n .addColumn('applied_at', 'text', (col) => col.notNull())\n .execute()\n },\n },\n]\n\n/**\n * Get the current schema version from the database\n */\nasync function getCurrentVersion(db: Kysely<Database>): Promise<number> {\n try {\n const result = await db\n .selectFrom('durably_schema_versions')\n .select('version')\n .orderBy('version', 'desc')\n .limit(1)\n .executeTakeFirst()\n\n return result?.version ?? 0\n } catch {\n // Table doesn't exist yet\n return 0\n }\n}\n\n/**\n * Run pending migrations\n */\nexport async function runMigrations(db: Kysely<Database>): Promise<void> {\n const currentVersion = await getCurrentVersion(db)\n\n for (const migration of migrations) {\n if (migration.version > currentVersion) {\n await db.transaction().execute(async (trx) => {\n await migration.up(trx)\n\n await trx\n .insertInto('durably_schema_versions')\n .values({\n version: migration.version,\n applied_at: new Date().toISOString(),\n })\n .execute()\n })\n }\n }\n}\n","import { type Kysely, sql } from 'kysely'\nimport { monotonicFactory } from 'ulidx'\nimport type { Database } from './schema'\n\nconst ulid = monotonicFactory()\n\n/**\n * Run data for creating a new run\n */\nexport interface CreateRunInput<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n jobName: string\n input: unknown\n idempotencyKey?: string\n concurrencyKey?: string\n labels?: TLabels\n}\n\n/**\n * Run data returned from storage\n */\nexport interface Run<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n id: string\n jobName: string\n input: unknown\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n idempotencyKey: string | null\n concurrencyKey: string | null\n currentStepIndex: number\n stepCount: number\n progress: { current: number; total?: number; message?: string } | null\n output: unknown | null\n error: string | null\n labels: TLabels\n heartbeatAt: string\n startedAt: string | null\n completedAt: string | null\n createdAt: string\n updatedAt: string\n}\n\n/**\n * Run update data\n */\nexport interface UpdateRunInput {\n status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n currentStepIndex?: number\n progress?: { current: number; total?: number; message?: string } | null\n output?: unknown\n error?: string | null\n heartbeatAt?: string\n startedAt?: string\n completedAt?: string\n}\n\n/**\n * Run filter options\n */\nexport interface RunFilter<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n /** Filter by job name(s). Pass a string for one, or an array for multiple (OR). */\n jobName?: string | string[]\n /** Filter by labels (all specified labels must match) */\n labels?: { [K in keyof TLabels]?: TLabels[K] }\n /** Maximum number of runs to return */\n limit?: number\n /** Number of runs to skip (for pagination) */\n offset?: number\n}\n\n/**\n * Step data for creating a new step\n */\nexport interface CreateStepInput {\n runId: string\n name: string\n index: number\n status: 'completed' | 'failed' | 'cancelled'\n output?: unknown\n error?: string\n startedAt: string // ISO8601 timestamp when step execution started\n}\n\n/**\n * Step data returned from storage\n */\nexport interface Step {\n id: string\n runId: string\n name: string\n index: number\n status: 'completed' | 'failed' | 'cancelled'\n output: unknown | null\n error: string | null\n startedAt: string\n completedAt: string | null\n}\n\n/**\n * Log data for creating a new log\n */\nexport interface CreateLogInput {\n runId: string\n stepName: string | null\n level: 'info' | 'warn' | 'error'\n message: string\n data?: unknown\n}\n\n/**\n * Log data returned from storage\n */\nexport interface Log {\n id: string\n runId: string\n stepName: string | null\n level: 'info' | 'warn' | 'error'\n message: string\n data: unknown | null\n createdAt: string\n}\n\n/**\n * A client-safe subset of Run, excluding internal fields like\n * heartbeatAt, idempotencyKey, concurrencyKey, and updatedAt.\n */\nexport type ClientRun<\n TLabels extends Record<string, string> = Record<string, string>,\n> = Omit<\n Run<TLabels>,\n 'idempotencyKey' | 'concurrencyKey' | 'heartbeatAt' | 'updatedAt'\n>\n\n/**\n * Project a full Run to a ClientRun by stripping internal fields.\n */\nexport function toClientRun<\n TLabels extends Record<string, string> = Record<string, string>,\n>(run: Run<TLabels>): ClientRun<TLabels> {\n const {\n idempotencyKey,\n concurrencyKey,\n heartbeatAt,\n updatedAt,\n ...clientRun\n } = run\n return clientRun\n}\n\n/**\n * Storage interface for database operations\n */\nexport interface Storage {\n // Run operations\n createRun(input: CreateRunInput): Promise<Run>\n batchCreateRuns(inputs: CreateRunInput[]): Promise<Run[]>\n updateRun(runId: string, data: UpdateRunInput): Promise<void>\n deleteRun(runId: string): Promise<void>\n getRun<T extends Run = Run>(runId: string): Promise<T | null>\n getRuns<T extends Run = Run>(filter?: RunFilter): Promise<T[]>\n claimNextPendingRun(excludeConcurrencyKeys: string[]): Promise<Run | null>\n\n // Step operations\n createStep(input: CreateStepInput): Promise<Step>\n getSteps(runId: string): Promise<Step[]>\n getCompletedStep(runId: string, name: string): Promise<Step | null>\n\n // Log operations\n createLog(input: CreateLogInput): Promise<Log>\n getLogs(runId: string): Promise<Log[]>\n}\n\n/**\n * Convert database row to Run object\n */\n/**\n * Validate label keys: alphanumeric, dash, underscore, dot, slash only\n */\nconst LABEL_KEY_PATTERN = /^[a-zA-Z0-9\\-_./]+$/\n\nfunction validateLabels(labels: Record<string, string> | undefined): void {\n if (!labels) return\n for (const key of Object.keys(labels)) {\n if (!LABEL_KEY_PATTERN.test(key)) {\n throw new Error(\n `Invalid label key \"${key}\": must contain only alphanumeric characters, dashes, underscores, dots, and slashes`,\n )\n }\n }\n}\n\nfunction rowToRun(\n row: Database['durably_runs'] & { step_count?: number | bigint | null },\n): Run {\n return {\n id: row.id,\n jobName: row.job_name,\n input: JSON.parse(row.input),\n status: row.status,\n idempotencyKey: row.idempotency_key,\n concurrencyKey: row.concurrency_key,\n currentStepIndex: row.current_step_index,\n stepCount: Number(row.step_count ?? 0),\n progress: row.progress ? JSON.parse(row.progress) : null,\n output: row.output ? JSON.parse(row.output) : null,\n error: row.error,\n labels: JSON.parse(row.labels),\n heartbeatAt: row.heartbeat_at,\n startedAt: row.started_at,\n completedAt: row.completed_at,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n\n/**\n * Convert database row to Step object\n */\nfunction rowToStep(row: Database['durably_steps']): Step {\n return {\n id: row.id,\n runId: row.run_id,\n name: row.name,\n index: row.index,\n status: row.status,\n output: row.output ? JSON.parse(row.output) : null,\n error: row.error,\n startedAt: row.started_at,\n completedAt: row.completed_at,\n }\n}\n\n/**\n * Convert database row to Log object\n */\nfunction rowToLog(row: Database['durably_logs']): Log {\n return {\n id: row.id,\n runId: row.run_id,\n stepName: row.step_name,\n level: row.level,\n message: row.message,\n data: row.data ? JSON.parse(row.data) : null,\n createdAt: row.created_at,\n }\n}\n\n/**\n * Create a Kysely-based Storage implementation\n */\nexport function createKyselyStorage(db: Kysely<Database>): Storage {\n return {\n async createRun(input: CreateRunInput): Promise<Run> {\n const now = new Date().toISOString()\n\n // Check for existing run with same idempotency key\n if (input.idempotencyKey) {\n const existing = await db\n .selectFrom('durably_runs')\n .selectAll()\n .where('job_name', '=', input.jobName)\n .where('idempotency_key', '=', input.idempotencyKey)\n .executeTakeFirst()\n\n if (existing) {\n return rowToRun(existing)\n }\n }\n\n validateLabels(input.labels)\n\n const id = ulid()\n const run: Database['durably_runs'] = {\n id,\n job_name: input.jobName,\n input: JSON.stringify(input.input),\n status: 'pending',\n idempotency_key: input.idempotencyKey ?? null,\n concurrency_key: input.concurrencyKey ?? null,\n current_step_index: 0,\n progress: null,\n output: null,\n error: null,\n labels: JSON.stringify(input.labels ?? {}),\n heartbeat_at: now,\n started_at: null,\n completed_at: null,\n created_at: now,\n updated_at: now,\n }\n\n await db.insertInto('durably_runs').values(run).execute()\n\n return rowToRun(run)\n },\n\n async batchCreateRuns(inputs: CreateRunInput[]): Promise<Run[]> {\n if (inputs.length === 0) {\n return []\n }\n\n // Use transaction to ensure atomicity of idempotency checks and inserts\n return await db.transaction().execute(async (trx) => {\n const now = new Date().toISOString()\n const runs: Database['durably_runs'][] = []\n\n // Validate all labels upfront\n for (const input of inputs) {\n validateLabels(input.labels)\n }\n\n // Process inputs - check idempotency keys and create run objects\n for (const input of inputs) {\n // Check for existing run with same idempotency key\n if (input.idempotencyKey) {\n const existing = await trx\n .selectFrom('durably_runs')\n .selectAll()\n .where('job_name', '=', input.jobName)\n .where('idempotency_key', '=', input.idempotencyKey)\n .executeTakeFirst()\n\n if (existing) {\n runs.push(existing)\n continue\n }\n }\n\n const id = ulid()\n runs.push({\n id,\n job_name: input.jobName,\n input: JSON.stringify(input.input),\n status: 'pending',\n idempotency_key: input.idempotencyKey ?? null,\n concurrency_key: input.concurrencyKey ?? null,\n current_step_index: 0,\n progress: null,\n output: null,\n error: null,\n labels: JSON.stringify(input.labels ?? {}),\n heartbeat_at: now,\n started_at: null,\n completed_at: null,\n created_at: now,\n updated_at: now,\n })\n }\n\n // Insert all new runs in a single batch\n const newRuns = runs.filter((r) => r.created_at === now)\n if (newRuns.length > 0) {\n await trx.insertInto('durably_runs').values(newRuns).execute()\n }\n\n return runs.map(rowToRun)\n })\n },\n\n async updateRun(runId: string, data: UpdateRunInput): Promise<void> {\n const now = new Date().toISOString()\n const updates: Partial<Database['durably_runs']> = {\n updated_at: now,\n }\n\n if (data.status !== undefined) updates.status = data.status\n if (data.currentStepIndex !== undefined)\n updates.current_step_index = data.currentStepIndex\n if (data.progress !== undefined)\n updates.progress = data.progress ? JSON.stringify(data.progress) : null\n if (data.output !== undefined)\n updates.output = JSON.stringify(data.output)\n if (data.error !== undefined) updates.error = data.error\n if (data.heartbeatAt !== undefined)\n updates.heartbeat_at = data.heartbeatAt\n if (data.startedAt !== undefined) updates.started_at = data.startedAt\n if (data.completedAt !== undefined)\n updates.completed_at = data.completedAt\n\n await db\n .updateTable('durably_runs')\n .set(updates)\n .where('id', '=', runId)\n .execute()\n },\n\n async deleteRun(runId: string): Promise<void> {\n // Delete in order: logs -> steps -> run (due to foreign key constraints)\n await db.deleteFrom('durably_logs').where('run_id', '=', runId).execute()\n await db.deleteFrom('durably_steps').where('run_id', '=', runId).execute()\n await db.deleteFrom('durably_runs').where('id', '=', runId).execute()\n },\n\n async getRun<T extends Run = Run>(runId: string): Promise<T | null> {\n const row = await db\n .selectFrom('durably_runs')\n .leftJoin('durably_steps', 'durably_runs.id', 'durably_steps.run_id')\n .selectAll('durably_runs')\n .select((eb) =>\n eb.fn.count<number>('durably_steps.id').as('step_count'),\n )\n .where('durably_runs.id', '=', runId)\n .groupBy('durably_runs.id')\n .executeTakeFirst()\n\n return row ? (rowToRun(row) as T) : null\n },\n\n async getRuns<T extends Run = Run>(filter?: RunFilter): Promise<T[]> {\n let query = db\n .selectFrom('durably_runs')\n .leftJoin('durably_steps', 'durably_runs.id', 'durably_steps.run_id')\n .selectAll('durably_runs')\n .select((eb) =>\n eb.fn.count<number>('durably_steps.id').as('step_count'),\n )\n .groupBy('durably_runs.id')\n\n if (filter?.status) {\n query = query.where('durably_runs.status', '=', filter.status)\n }\n if (filter?.jobName) {\n if (Array.isArray(filter.jobName)) {\n if (filter.jobName.length > 0) {\n query = query.where('durably_runs.job_name', 'in', filter.jobName)\n }\n } else {\n query = query.where('durably_runs.job_name', '=', filter.jobName)\n }\n }\n if (filter?.labels) {\n const labels = filter.labels as Record<string, string>\n validateLabels(labels)\n for (const [key, value] of Object.entries(labels)) {\n if (value === undefined) continue\n query = query.where(\n sql`json_extract(durably_runs.labels, ${`$.\"${key}\"`})`,\n '=',\n value,\n )\n }\n }\n\n query = query.orderBy('durably_runs.created_at', 'desc')\n\n if (filter?.limit !== undefined) {\n query = query.limit(filter.limit)\n }\n if (filter?.offset !== undefined) {\n // SQLite requires LIMIT when using OFFSET\n if (filter.limit === undefined) {\n query = query.limit(-1) // -1 means unlimited in SQLite\n }\n query = query.offset(filter.offset)\n }\n\n const rows = await query.execute()\n return rows.map(rowToRun) as T[]\n },\n\n async claimNextPendingRun(\n excludeConcurrencyKeys: string[],\n ): Promise<Run | null> {\n const now = new Date().toISOString()\n\n let subquery = db\n .selectFrom('durably_runs')\n .select('id')\n .where('status', '=', 'pending')\n .orderBy('created_at', 'asc')\n .orderBy('id', 'asc')\n .limit(1)\n\n if (excludeConcurrencyKeys.length > 0) {\n subquery = subquery.where((eb) =>\n eb.or([\n eb('concurrency_key', 'is', null),\n eb('concurrency_key', 'not in', excludeConcurrencyKeys),\n ]),\n )\n }\n\n const row = await db\n .updateTable('durably_runs')\n .set({\n status: 'running',\n heartbeat_at: now,\n started_at: sql`COALESCE(started_at, ${now})`,\n updated_at: now,\n })\n .where('id', '=', (eb) =>\n eb.selectFrom(subquery.as('sub')).select('id'),\n )\n .returningAll()\n .executeTakeFirst()\n\n if (!row) return null\n return rowToRun({ ...row, step_count: 0 })\n },\n\n async createStep(input: CreateStepInput): Promise<Step> {\n const completedAt = new Date().toISOString()\n const id = ulid()\n\n const step: Database['durably_steps'] = {\n id,\n run_id: input.runId,\n name: input.name,\n index: input.index,\n status: input.status,\n output:\n input.output !== undefined ? JSON.stringify(input.output) : null,\n error: input.error ?? null,\n started_at: input.startedAt,\n completed_at: completedAt,\n }\n\n await db.insertInto('durably_steps').values(step).execute()\n\n return rowToStep(step)\n },\n\n async getSteps(runId: string): Promise<Step[]> {\n const rows = await db\n .selectFrom('durably_steps')\n .selectAll()\n .where('run_id', '=', runId)\n .orderBy('index', 'asc')\n .execute()\n\n return rows.map(rowToStep)\n },\n\n async getCompletedStep(runId: string, name: string): Promise<Step | null> {\n const row = await db\n .selectFrom('durably_steps')\n .selectAll()\n .where('run_id', '=', runId)\n .where('name', '=', name)\n .where('status', '=', 'completed')\n .executeTakeFirst()\n\n return row ? rowToStep(row) : null\n },\n\n async createLog(input: CreateLogInput): Promise<Log> {\n const now = new Date().toISOString()\n const id = ulid()\n\n const log: Database['durably_logs'] = {\n id,\n run_id: input.runId,\n step_name: input.stepName,\n level: input.level,\n message: input.message,\n data: input.data !== undefined ? JSON.stringify(input.data) : null,\n created_at: now,\n }\n\n await db.insertInto('durably_logs').values(log).execute()\n\n return rowToLog(log)\n },\n\n async getLogs(runId: string): Promise<Log[]> {\n const rows = await db\n .selectFrom('durably_logs')\n .selectAll()\n .where('run_id', '=', runId)\n .orderBy('created_at', 'asc')\n .execute()\n\n return rows.map(rowToLog)\n },\n }\n}\n","import { prettifyError } from 'zod'\nimport { createStepContext } from './context'\nimport { CancelledError, getErrorMessage } from './errors'\nimport type { EventEmitter } from './events'\nimport type { JobRegistry } from './job'\nimport type { Storage } from './storage'\n\n/**\n * Worker configuration\n */\nexport interface WorkerConfig {\n pollingInterval: number\n heartbeatInterval: number\n staleThreshold: number\n}\n\n/**\n * Worker state\n */\nexport interface Worker {\n /**\n * Start the worker polling loop\n */\n start(): void\n\n /**\n * Stop the worker after current run completes\n */\n stop(): Promise<void>\n\n /**\n * Check if worker is running\n */\n readonly isRunning: boolean\n}\n\n/**\n * Create a worker instance\n */\nexport function createWorker(\n config: WorkerConfig,\n storage: Storage,\n eventEmitter: EventEmitter,\n jobRegistry: JobRegistry,\n): Worker {\n let running = false\n let currentRunPromise: Promise<void> | null = null\n let pollingTimeout: ReturnType<typeof setTimeout> | null = null\n let stopResolver: (() => void) | null = null\n let heartbeatInterval: ReturnType<typeof setInterval> | null = null\n let currentRunId: string | null = null\n\n /**\n * Recover stale runs by resetting them to pending\n */\n async function recoverStaleRuns(): Promise<void> {\n const staleThreshold = new Date(\n Date.now() - config.staleThreshold,\n ).toISOString()\n const runningRuns = await storage.getRuns({ status: 'running' })\n\n for (const run of runningRuns) {\n if (run.heartbeatAt < staleThreshold) {\n // This run is stale - reset to pending\n await storage.updateRun(run.id, {\n status: 'pending',\n })\n }\n }\n }\n\n /**\n * Update heartbeat for current run\n */\n async function updateHeartbeat(): Promise<void> {\n if (currentRunId) {\n await storage.updateRun(currentRunId, {\n heartbeatAt: new Date().toISOString(),\n })\n }\n }\n\n /**\n * Handle successful run completion\n */\n async function handleRunSuccess(\n runId: string,\n jobName: string,\n output: unknown,\n startTime: number,\n ): Promise<void> {\n // Check if run was cancelled during execution - don't overwrite cancelled status\n const currentRun = await storage.getRun(runId)\n if (!currentRun || currentRun.status === 'cancelled') {\n return\n }\n\n await storage.updateRun(runId, {\n status: 'completed',\n output,\n completedAt: new Date().toISOString(),\n })\n\n eventEmitter.emit({\n type: 'run:complete',\n runId,\n jobName,\n output,\n duration: Date.now() - startTime,\n labels: currentRun.labels,\n })\n }\n\n /**\n * Handle failed run\n */\n async function handleRunFailure(\n runId: string,\n jobName: string,\n error: unknown,\n ): Promise<void> {\n // If the error is CancelledError, don't treat it as a failure\n // The run status is already 'cancelled'\n if (error instanceof CancelledError) {\n return\n }\n\n // Check if run was cancelled during execution - don't overwrite cancelled status\n const currentRun = await storage.getRun(runId)\n if (!currentRun || currentRun.status === 'cancelled') {\n return\n }\n\n const errorMessage = getErrorMessage(error)\n\n // Get the failed step name if available\n const steps = await storage.getSteps(runId)\n const failedStep = steps.find((s) => s.status === 'failed')\n\n await storage.updateRun(runId, {\n status: 'failed',\n error: errorMessage,\n completedAt: new Date().toISOString(),\n })\n\n eventEmitter.emit({\n type: 'run:fail',\n runId,\n jobName,\n error: errorMessage,\n failedStepName: failedStep?.name ?? 'unknown',\n labels: currentRun.labels,\n })\n }\n\n /**\n * Execute a run with heartbeat management\n */\n async function executeRun(\n run: Awaited<ReturnType<typeof storage.getRun>> & { id: string },\n job: NonNullable<ReturnType<typeof jobRegistry.get>>,\n ): Promise<void> {\n // Track current run for heartbeat updates\n currentRunId = run.id\n\n // Start heartbeat interval\n // Errors are emitted as events but don't stop execution\n heartbeatInterval = setInterval(() => {\n updateHeartbeat().catch((error) => {\n eventEmitter.emit({\n type: 'worker:error',\n error: getErrorMessage(error),\n context: 'heartbeat',\n runId: run.id,\n })\n })\n }, config.heartbeatInterval)\n\n // Emit run:start event\n eventEmitter.emit({\n type: 'run:start',\n runId: run.id,\n jobName: run.jobName,\n input: run.input,\n labels: run.labels,\n })\n\n const startTime = Date.now()\n\n const { step, dispose } = createStepContext(\n run,\n run.jobName,\n storage,\n eventEmitter,\n )\n\n try {\n // Execute job with step context\n const output = await job.fn(step, run.input)\n\n // Validate output if schema exists\n if (job.outputSchema) {\n const parseResult = job.outputSchema.safeParse(output)\n if (!parseResult.success) {\n throw new Error(`Invalid output: ${prettifyError(parseResult.error)}`)\n }\n }\n\n await handleRunSuccess(run.id, run.jobName, output, startTime)\n } catch (error) {\n await handleRunFailure(run.id, run.jobName, error)\n } finally {\n dispose()\n // Stop heartbeat interval\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval)\n heartbeatInterval = null\n }\n currentRunId = null\n }\n }\n\n async function processNextRun(): Promise<boolean> {\n // Get running runs to exclude their concurrency keys\n const runningRuns = await storage.getRuns({ status: 'running' })\n const excludeConcurrencyKeys = runningRuns\n .filter(\n (r): r is typeof r & { concurrencyKey: string } =>\n r.concurrencyKey !== null,\n )\n .map((r) => r.concurrencyKey)\n\n // Atomically claim next pending run (SELECT + UPDATE in one statement)\n const run = await storage.claimNextPendingRun(excludeConcurrencyKeys)\n if (!run) {\n return false\n }\n\n // Get the job definition\n const job = jobRegistry.get(run.jobName)\n if (!job) {\n // Unknown job - mark as failed\n await storage.updateRun(run.id, {\n status: 'failed',\n error: `Unknown job: ${run.jobName}`,\n })\n return true\n }\n\n await executeRun(run, job)\n\n return true\n }\n\n async function poll(): Promise<void> {\n if (!running) {\n return\n }\n\n const doWork = async () => {\n // Recover stale runs before processing\n await recoverStaleRuns()\n await processNextRun()\n }\n\n try {\n currentRunPromise = doWork()\n await currentRunPromise\n } finally {\n currentRunPromise = null\n }\n\n if (running) {\n pollingTimeout = setTimeout(() => poll(), config.pollingInterval)\n } else if (stopResolver) {\n stopResolver()\n stopResolver = null\n }\n }\n\n return {\n get isRunning(): boolean {\n return running\n },\n\n start(): void {\n if (running) {\n return\n }\n running = true\n poll()\n },\n\n async stop(): Promise<void> {\n if (!running) {\n return\n }\n\n running = false\n\n if (pollingTimeout) {\n clearTimeout(pollingTimeout)\n pollingTimeout = null\n }\n\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval)\n heartbeatInterval = null\n }\n\n if (currentRunPromise) {\n // Wait for current run to complete\n return new Promise<void>((resolve) => {\n stopResolver = resolve\n })\n }\n },\n }\n}\n","/**\n * Error thrown when a run is cancelled during execution.\n * The worker catches this error and treats it specially - it does not\n * mark the run as failed, as the run status is already 'cancelled'.\n */\nexport class CancelledError extends Error {\n constructor(runId: string) {\n super(`Run was cancelled: ${runId}`)\n this.name = 'CancelledError'\n }\n}\n\n/**\n * Extract error message from unknown error\n */\nexport function getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","import { CancelledError } from './errors'\nimport type { EventEmitter } from './events'\nimport type { StepContext } from './job'\nimport type { Run, Storage } from './storage'\n\n/**\n * Create a step context for executing a run\n */\nexport function createStepContext(\n run: Run,\n jobName: string,\n storage: Storage,\n eventEmitter: EventEmitter,\n): { step: StepContext; dispose: () => void } {\n let stepIndex = run.currentStepIndex\n let currentStepName: string | null = null\n\n const controller = new AbortController()\n\n const unsubscribe = eventEmitter.on('run:cancel', (event) => {\n if (event.runId === run.id) {\n controller.abort()\n }\n })\n\n const step: StepContext = {\n get runId(): string {\n return run.id\n },\n\n async run<T>(\n name: string,\n fn: (signal: AbortSignal) => T | Promise<T>,\n ): Promise<T> {\n // Fast path: check in-memory signal first (set by run:cancel event)\n if (controller.signal.aborted) {\n throw new CancelledError(run.id)\n }\n\n // Slow path: DB check for cases where event wasn't received\n // (e.g., run cancelled while worker was down, then resumed)\n const currentRun = await storage.getRun(run.id)\n if (currentRun?.status === 'cancelled') {\n controller.abort()\n throw new CancelledError(run.id)\n }\n\n // Check cancellation before replaying cached steps\n if (controller.signal.aborted) {\n throw new CancelledError(run.id)\n }\n\n // Check if step was already completed\n const existingStep = await storage.getCompletedStep(run.id, name)\n if (existingStep) {\n stepIndex++\n return existingStep.output as T\n }\n\n // Track current step for log attribution\n currentStepName = name\n\n // Record step start time\n const startedAt = new Date().toISOString()\n const startTime = Date.now()\n\n // Emit step:start event\n eventEmitter.emit({\n type: 'step:start',\n runId: run.id,\n jobName,\n stepName: name,\n stepIndex,\n labels: run.labels,\n })\n\n try {\n // Execute the step with the abort signal\n const result = await fn(controller.signal)\n\n // Save step result\n await storage.createStep({\n runId: run.id,\n name,\n index: stepIndex,\n status: 'completed',\n output: result,\n startedAt,\n })\n\n // Update run's current step index\n stepIndex++\n await storage.updateRun(run.id, { currentStepIndex: stepIndex })\n\n // Emit step:complete event\n eventEmitter.emit({\n type: 'step:complete',\n runId: run.id,\n jobName,\n stepName: name,\n stepIndex: stepIndex - 1,\n output: result,\n duration: Date.now() - startTime,\n labels: run.labels,\n })\n\n return result\n } catch (error) {\n const isCancelled = controller.signal.aborted\n const errorMessage =\n error instanceof Error ? error.message : String(error)\n\n await storage.createStep({\n runId: run.id,\n name,\n index: stepIndex,\n status: isCancelled ? 'cancelled' : 'failed',\n error: errorMessage,\n startedAt,\n })\n\n eventEmitter.emit({\n ...(isCancelled\n ? { type: 'step:cancel' as const }\n : { type: 'step:fail' as const, error: errorMessage }),\n runId: run.id,\n jobName,\n stepName: name,\n stepIndex,\n labels: run.labels,\n })\n\n if (isCancelled) {\n throw new CancelledError(run.id)\n }\n throw error\n } finally {\n // Clear current step after execution\n currentStepName = null\n }\n },\n\n progress(current: number, total?: number, message?: string): void {\n const progressData = { current, total, message }\n // Fire and forget - don't await\n storage.updateRun(run.id, { progress: progressData })\n // Emit progress event\n eventEmitter.emit({\n type: 'run:progress',\n runId: run.id,\n jobName,\n progress: progressData,\n labels: run.labels,\n })\n },\n\n log: {\n info(message: string, data?: unknown): void {\n eventEmitter.emit({\n type: 'log:write',\n runId: run.id,\n jobName,\n labels: run.labels,\n stepName: currentStepName,\n level: 'info',\n message,\n data,\n })\n },\n\n warn(message: string, data?: unknown): void {\n eventEmitter.emit({\n type: 'log:write',\n runId: run.id,\n jobName,\n labels: run.labels,\n stepName: currentStepName,\n level: 'warn',\n message,\n data,\n })\n },\n\n error(message: string, data?: unknown): void {\n eventEmitter.emit({\n type: 'log:write',\n runId: run.id,\n jobName,\n labels: run.labels,\n stepName: currentStepName,\n level: 'error',\n message,\n data,\n })\n },\n },\n }\n\n return { step, dispose: unsubscribe }\n}\n","import type { z } from 'zod'\nimport type { StepContext } from './job'\n\n/**\n * Job run function type\n */\nexport type JobRunFunction<TInput, TOutput> = (\n step: StepContext,\n input: TInput,\n) => Promise<TOutput>\n\n/**\n * Job definition - a standalone description of a job\n * This is the result of calling defineJob() and can be passed to durably.register()\n */\nexport interface JobDefinition<TName extends string, TInput, TOutput> {\n readonly name: TName\n readonly input: z.ZodType<TInput>\n readonly output: z.ZodType<TOutput> | undefined\n readonly run: JobRunFunction<TInput, TOutput>\n}\n\n/**\n * Extract input type from a JobDefinition\n * @example\n * ```ts\n * type Input = JobInput<typeof myJob> // { userId: string }\n * ```\n */\nexport type JobInput<T> =\n T extends JobDefinition<string, infer TInput, unknown> ? TInput : never\n\n/**\n * Extract output type from a JobDefinition\n * @example\n * ```ts\n * type Output = JobOutput<typeof myJob> // { count: number }\n * ```\n */\nexport type JobOutput<T> =\n T extends JobDefinition<string, unknown, infer TOutput> ? TOutput : never\n\n/**\n * Configuration for defining a job\n */\nexport interface DefineJobConfig<\n TName extends string,\n TInputSchema extends z.ZodType,\n TOutputSchema extends z.ZodType | undefined,\n> {\n name: TName\n input: TInputSchema\n output?: TOutputSchema\n run: JobRunFunction<\n z.infer<TInputSchema>,\n TOutputSchema extends z.ZodType ? z.infer<TOutputSchema> : void\n >\n}\n\n/**\n * Define a job - creates a JobDefinition that can be registered with durably.register()\n *\n * @example\n * ```ts\n * import { defineJob } from '@coji/durably'\n * import { z } from 'zod'\n *\n * export const syncUsers = defineJob({\n * name: 'sync-users',\n * input: z.object({ orgId: z.string() }),\n * output: z.object({ syncedCount: z.number() }),\n * run: async (step, input) => {\n * const users = await step.run('fetch-users', () => fetchUsers(input.orgId))\n * return { syncedCount: users.length }\n * },\n * })\n * ```\n */\nexport function defineJob<\n TName extends string,\n TInputSchema extends z.ZodType,\n TOutputSchema extends z.ZodType | undefined = undefined,\n>(\n config: DefineJobConfig<TName, TInputSchema, TOutputSchema>,\n): JobDefinition<\n TName,\n z.infer<TInputSchema>,\n TOutputSchema extends z.ZodType ? z.infer<TOutputSchema> : void\n> {\n return {\n name: config.name,\n input: config.input,\n output: config.output,\n run: config.run,\n } as JobDefinition<\n TName,\n z.infer<TInputSchema>,\n TOutputSchema extends z.ZodType ? z.infer<TOutputSchema> : void\n >\n}\n","/**\n * HTTP response utilities for the Durably HTTP handler.\n * Extracted to eliminate duplication in server.ts handlers.\n */\n\nimport { getErrorMessage } from './errors'\n\nexport { getErrorMessage }\n\n/**\n * JSON response headers\n */\nconst JSON_HEADERS = {\n 'Content-Type': 'application/json',\n} as const\n\n/**\n * Create a JSON response\n */\nexport function jsonResponse(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: JSON_HEADERS,\n })\n}\n\n/**\n * Create an error response with consistent format\n */\nexport function errorResponse(\n message: string,\n status: 400 | 404 | 500 = 500,\n): Response {\n return jsonResponse({ error: message }, status)\n}\n\n/**\n * Create a success response with { success: true }\n */\nexport function successResponse(): Response {\n return jsonResponse({ success: true })\n}\n\n/**\n * Get required query parameter or return error response\n */\nexport function getRequiredQueryParam(\n url: URL,\n paramName: string,\n): string | Response {\n const value = url.searchParams.get(paramName)\n if (!value) {\n return errorResponse(`${paramName} query parameter is required`, 400)\n }\n return value\n}\n","/**\n * SSE (Server-Sent Events) utilities for streaming events to clients.\n * Extracted to eliminate duplication between subscribe and runsSubscribe handlers.\n */\n\nimport type { Unsubscribe } from './events'\n\n/**\n * SSE response headers\n */\nconst SSE_HEADERS = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n} as const\n\n/**\n * Encode data as SSE format: `data: ${json}\\n\\n`\n */\nfunction formatSSE(data: unknown): string {\n return `data: ${JSON.stringify(data)}\\n\\n`\n}\n\n/**\n * Create a TextEncoder for SSE streams\n */\nfunction createSSEEncoder(): TextEncoder {\n return new TextEncoder()\n}\n\n/**\n * Encode and format data for SSE streaming\n */\nfunction encodeSSE(encoder: TextEncoder, data: unknown): Uint8Array {\n return encoder.encode(formatSSE(data))\n}\n\n/**\n * Create an SSE Response from a ReadableStream\n */\nexport function createSSEResponse(stream: ReadableStream): Response {\n return new Response(stream, {\n status: 200,\n headers: SSE_HEADERS,\n })\n}\n\n/**\n * Transform a ReadableStream of events into an SSE-formatted stream\n */\nexport function createSSEStreamFromReader<T>(\n reader: ReadableStreamDefaultReader<T>,\n): ReadableStream<Uint8Array> {\n const encoder = createSSEEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n controller.close()\n break\n }\n\n controller.enqueue(encodeSSE(encoder, value))\n }\n } catch (error) {\n controller.error(error)\n }\n },\n cancel() {\n reader.releaseLock()\n },\n })\n}\n\n/**\n * Transform a ReadableStream of events into an SSE-formatted stream with\n * throttling for `run:progress` events.\n */\nexport function createThrottledSSEStreamFromReader<T>(\n reader: ReadableStreamDefaultReader<T>,\n throttleMs: number,\n): ReadableStream<Uint8Array> {\n if (throttleMs <= 0) {\n return createSSEStreamFromReader(reader)\n }\n\n const encoder = createSSEEncoder()\n let closed = false\n let throttle: {\n controller: SSEStreamController\n dispose: () => void\n } | null = null\n\n return new ReadableStream({\n async start(controller) {\n const innerCtrl: SSEStreamController = {\n enqueue: (data: unknown) =>\n controller.enqueue(encodeSSE(encoder, data)),\n close: () => {\n closed = true\n controller.close()\n },\n get closed() {\n return closed\n },\n }\n throttle = createThrottledSSEController(innerCtrl, throttleMs)\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n throttle.controller.close()\n break\n }\n throttle.controller.enqueue(value)\n }\n } catch (error) {\n throttle.dispose()\n reader.releaseLock()\n controller.error(error)\n }\n },\n cancel() {\n closed = true\n throttle?.dispose()\n reader.releaseLock()\n },\n })\n}\n\n/**\n * SSE stream controller with cleanup support\n */\nexport interface SSEStreamController {\n enqueue: (data: unknown) => void\n close: () => void\n readonly closed: boolean\n}\n\n/**\n * Create an SSE stream from event subscriptions.\n * Handles the common pattern of subscribing to multiple events and streaming them.\n *\n * @param setup - Function to set up event subscriptions, returns cleanup functions\n * @returns SSE Response\n */\nexport function createSSEStreamFromSubscriptions(\n setup: (controller: SSEStreamController) => Unsubscribe[],\n): ReadableStream<Uint8Array> {\n const encoder = createSSEEncoder()\n let closed = false\n let unsubscribes: Unsubscribe[] = []\n\n return new ReadableStream({\n start(controller) {\n const sseController: SSEStreamController = {\n enqueue: (data: unknown) => {\n if (closed) return\n controller.enqueue(encodeSSE(encoder, data))\n },\n close: () => {\n if (closed) return\n closed = true\n controller.close()\n },\n get closed() {\n return closed\n },\n }\n\n unsubscribes = setup(sseController)\n },\n cancel() {\n closed = true\n for (const unsubscribe of unsubscribes) {\n unsubscribe()\n }\n },\n })\n}\n\nconst TERMINAL_EVENT_TYPES = new Set([\n 'run:complete',\n 'run:fail',\n 'run:cancel',\n 'run:delete',\n])\n\n/**\n * Create an SSE stream controller that throttles `run:progress` events.\n *\n * - First progress event per run is delivered immediately\n * - Subsequent events within the throttle window are coalesced (latest wins)\n * - A trailing flush ensures the last progress is always delivered\n * - Non-progress events pass through immediately\n */\nexport function createThrottledSSEController(\n inner: SSEStreamController,\n throttleMs: number,\n): { controller: SSEStreamController; dispose: () => void } {\n if (throttleMs <= 0) {\n return { controller: inner, dispose: () => {} }\n }\n\n // Per-run throttle state\n const pending = new Map<\n string,\n { data: unknown; timer: ReturnType<typeof setTimeout> }\n >()\n\n // Track last send time per run for leading-edge delivery\n const lastSent = new Map<string, number>()\n\n const controller: SSEStreamController = {\n enqueue(data: unknown) {\n if (inner.closed) return\n\n const event =\n typeof data === 'object' && data !== null\n ? (data as { type?: string; runId?: string })\n : null\n\n // Flush and clean up throttle state for terminal run events\n if (event?.runId && TERMINAL_EVENT_TYPES.has(event.type ?? '')) {\n lastSent.delete(event.runId)\n const entry = pending.get(event.runId)\n if (entry) {\n clearTimeout(entry.timer)\n if (!inner.closed) inner.enqueue(entry.data)\n pending.delete(event.runId)\n }\n }\n\n if (event?.type !== 'run:progress' || !event?.runId) {\n inner.enqueue(data)\n return\n }\n\n const runId = event.runId\n const now = Date.now()\n const last = lastSent.get(runId) ?? 0\n\n // Leading edge: send immediately if enough time has passed\n if (now - last >= throttleMs) {\n lastSent.set(runId, now)\n // Clear any pending flush for this run\n const entry = pending.get(runId)\n if (entry) {\n clearTimeout(entry.timer)\n pending.delete(runId)\n }\n inner.enqueue(data)\n return\n }\n\n // Trailing edge: buffer latest and schedule flush\n const existing = pending.get(runId)\n if (existing) {\n clearTimeout(existing.timer)\n }\n\n const delay = Math.max(0, throttleMs - (now - last))\n const timer = setTimeout(() => {\n const current = pending.get(runId)\n if (!current || current.timer !== timer) return\n\n pending.delete(runId)\n if (!inner.closed) {\n lastSent.set(runId, Date.now())\n inner.enqueue(current.data)\n }\n }, delay)\n\n pending.set(runId, { data, timer })\n },\n close() {\n // Flush all pending progress events before closing\n for (const [, entry] of pending) {\n clearTimeout(entry.timer)\n if (!inner.closed) {\n inner.enqueue(entry.data)\n }\n }\n pending.clear()\n lastSent.clear()\n inner.close()\n },\n get closed() {\n return inner.closed\n },\n }\n\n const dispose = () => {\n for (const [, entry] of pending) {\n clearTimeout(entry.timer)\n }\n pending.clear()\n lastSent.clear()\n }\n\n return { controller, dispose }\n}\n","import type { Durably } from './durably'\nimport type { AnyEventInput } from './events'\nimport {\n errorResponse,\n getErrorMessage,\n getRequiredQueryParam,\n jsonResponse,\n successResponse,\n} from './http'\nimport {\n createSSEResponse,\n createSSEStreamFromSubscriptions,\n createThrottledSSEController,\n createThrottledSSEStreamFromReader,\n type SSEStreamController,\n} from './sse'\nimport type { Run, RunFilter } from './storage'\nimport { toClientRun } from './storage'\n\n/**\n * Run operation types for onRunAccess\n */\nexport type RunOperation =\n | 'read'\n | 'subscribe'\n | 'steps'\n | 'retry'\n | 'cancel'\n | 'delete'\n\n/**\n * Subscription filter — only fields that SSE subscriptions actually support.\n */\nexport type RunsSubscribeFilter<\n TLabels extends Record<string, string> = Record<string, string>,\n> = Pick<RunFilter<TLabels>, 'jobName' | 'labels'>\n\n/**\n * Request body for triggering a job\n */\nexport interface TriggerRequest<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n jobName: string\n input: unknown\n idempotencyKey?: string\n concurrencyKey?: string\n labels?: TLabels\n}\n\n/**\n * Response for trigger endpoint\n */\nexport interface TriggerResponse {\n runId: string\n}\n\n/**\n * Auth middleware configuration.\n * When `auth` is set, `authenticate` is required.\n * TContext is inferred from authenticate's return type.\n * TLabels is inferred from the Durably instance.\n */\nexport interface AuthConfig<\n TContext,\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n /** Authenticate every request. Return context or throw Response to reject. */\n authenticate: (request: Request) => Promise<TContext> | TContext\n\n /** Guard before trigger. Called after body validation and job resolution. */\n onTrigger?: (\n ctx: TContext,\n trigger: TriggerRequest<TLabels>,\n ) => Promise<void> | void\n\n /** Guard before run-level operations. Run is pre-fetched. */\n onRunAccess?: (\n ctx: TContext,\n run: Run<TLabels>,\n info: { operation: RunOperation },\n ) => Promise<void> | void\n\n /** Scope runs list queries (GET /runs). */\n scopeRuns?: (\n ctx: TContext,\n filter: RunFilter<TLabels>,\n ) => RunFilter<TLabels> | Promise<RunFilter<TLabels>>\n\n /** Scope runs subscribe stream (GET /runs/subscribe). Falls back to scopeRuns if not set. */\n scopeRunsSubscribe?: (\n ctx: TContext,\n filter: RunsSubscribeFilter<TLabels>,\n ) => RunsSubscribeFilter<TLabels> | Promise<RunsSubscribeFilter<TLabels>>\n}\n\n/**\n * Handler interface for HTTP endpoints\n */\nexport interface DurablyHandler {\n /**\n * Handle all Durably HTTP requests with automatic routing + auth\n *\n * Routes:\n * - GET {basePath}/subscribe?runId=xxx - SSE stream\n * - GET {basePath}/runs - List runs\n * - GET {basePath}/runs/subscribe - SSE stream of run updates\n * - GET {basePath}/run?runId=xxx - Get single run\n * - GET {basePath}/steps?runId=xxx - Get steps\n * - POST {basePath}/trigger - Trigger a job\n * - POST {basePath}/retry?runId=xxx - Retry a failed run\n * - POST {basePath}/cancel?runId=xxx - Cancel a run\n * - DELETE {basePath}/run?runId=xxx - Delete a run\n */\n handle(request: Request, basePath: string): Promise<Response>\n}\n\n/**\n * Options for createDurablyHandler\n */\nexport interface CreateDurablyHandlerOptions<\n TContext = undefined,\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n /**\n * Called before handling each request (after authentication).\n * Use this to initialize Durably (migrate, start worker, etc.)\n */\n onRequest?: () => Promise<void> | void\n\n /**\n * Throttle interval in milliseconds for SSE progress events.\n * @default 100\n */\n sseThrottleMs?: number\n\n /**\n * Auth middleware. When set, authenticate is required and auth applies to ALL endpoints.\n */\n auth?: AuthConfig<TContext, TLabels>\n}\n\n/**\n * Valid status values for runs\n */\nconst VALID_STATUSES = [\n 'pending',\n 'running',\n 'completed',\n 'failed',\n 'cancelled',\n] as const satisfies readonly RunFilter['status'][]\n\nconst VALID_STATUSES_SET: ReadonlySet<string> = new Set(VALID_STATUSES)\n\n/**\n * Parse label.* query params into a Record<string, string>\n */\nfunction parseLabelsFromParams(\n searchParams: URLSearchParams,\n): Record<string, string> | undefined {\n const labels: Record<string, string> = {}\n for (const [key, value] of searchParams.entries()) {\n if (key.startsWith('label.')) {\n labels[key.slice(6)] = value\n }\n }\n return Object.keys(labels).length > 0 ? labels : undefined\n}\n\n/**\n * Parse and validate RunFilter from query params.\n * Returns the filter or an error Response.\n */\nfunction parseRunFilter(url: URL): RunFilter | Response {\n const jobNames = url.searchParams.getAll('jobName')\n const statusParam = url.searchParams.get('status')\n const limitParam = url.searchParams.get('limit')\n const offsetParam = url.searchParams.get('offset')\n const labels = parseLabelsFromParams(url.searchParams)\n\n // Validate status\n if (statusParam && !VALID_STATUSES_SET.has(statusParam)) {\n return errorResponse(\n `Invalid status: ${statusParam}. Must be one of: ${VALID_STATUSES.join(', ')}`,\n 400,\n )\n }\n\n // Validate limit\n let limit: number | undefined\n if (limitParam) {\n limit = Number.parseInt(limitParam, 10)\n if (Number.isNaN(limit) || limit < 0) {\n return errorResponse('Invalid limit: must be a non-negative integer', 400)\n }\n }\n\n // Validate offset\n let offset: number | undefined\n if (offsetParam) {\n offset = Number.parseInt(offsetParam, 10)\n if (Number.isNaN(offset) || offset < 0) {\n return errorResponse(\n 'Invalid offset: must be a non-negative integer',\n 400,\n )\n }\n }\n\n return {\n jobName: jobNames.length > 0 ? jobNames : undefined,\n status: statusParam as RunFilter['status'],\n labels,\n limit,\n offset,\n }\n}\n\n/**\n * Parse RunsSubscribeFilter from query params.\n */\nfunction parseRunsSubscribeFilter(url: URL): RunsSubscribeFilter {\n const jobNames = url.searchParams.getAll('jobName')\n const labels = parseLabelsFromParams(url.searchParams)\n\n return {\n jobName: jobNames.length > 0 ? jobNames : undefined,\n labels,\n }\n}\n\n/**\n * Check if event labels match filter labels (all filter labels must match)\n */\nfunction matchesLabels(\n eventLabels: Record<string, string>,\n filterLabels: Record<string, string>,\n): boolean {\n for (const [key, value] of Object.entries(filterLabels)) {\n if (eventLabels[key] !== value) return false\n }\n return true\n}\n\n/**\n * Create HTTP handlers for Durably\n * Uses Web Standard Request/Response for framework-agnostic usage\n */\n// biome-ignore lint/suspicious/noExplicitAny: TLabels must be inferred from Durably instance\nexport function createDurablyHandler<\n TContext = undefined,\n TLabels extends Record<string, string> = Record<string, string>,\n>(\n durably: Durably<any, TLabels>,\n options?: CreateDurablyHandlerOptions<TContext, TLabels>,\n): DurablyHandler {\n const throttleMs = options?.sseThrottleMs ?? 100\n const auth = options?.auth\n\n // Validate: auth requires authenticate\n if (auth && !auth.authenticate) {\n throw new Error(\n 'createDurablyHandler: auth.authenticate is required when auth is provided',\n )\n }\n\n // --- Shared helpers ---\n\n /** Wrap handler with try/catch that re-throws Response and catches everything else as 500 */\n async function withErrorHandling(\n fn: () => Promise<Response>,\n ): Promise<Response> {\n try {\n return await fn()\n } catch (error) {\n if (error instanceof Response) throw error\n return errorResponse(getErrorMessage(error), 500)\n }\n }\n\n /** Fetch run, check auth, return run or error Response */\n async function requireRunAccess(\n url: URL,\n ctx: TContext | undefined,\n operation: RunOperation,\n ): Promise<{ run: Run<TLabels>; runId: string } | Response> {\n const runId = getRequiredQueryParam(url, 'runId')\n if (runId instanceof Response) return runId\n\n const run = await durably.getRun(runId)\n if (!run) return errorResponse('Run not found', 404)\n\n if (auth?.onRunAccess && ctx !== undefined) {\n await auth.onRunAccess(ctx as TContext, run as Run<TLabels>, {\n operation,\n })\n }\n\n return { run: run as Run<TLabels>, runId }\n }\n\n // --- Private endpoint handlers (closure-scoped, not exposed on returned object) ---\n\n async function handleTrigger(\n request: Request,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const body = (await request.json()) as TriggerRequest<TLabels>\n\n if (!body.jobName) {\n return errorResponse('jobName is required', 400)\n }\n\n const job = durably.getJob(body.jobName)\n if (!job) {\n return errorResponse(`Job not found: ${body.jobName}`, 404)\n }\n\n // Auth hook: onTrigger (after validation)\n if (auth?.onTrigger && ctx !== undefined) {\n await auth.onTrigger(ctx as TContext, body)\n }\n\n const run = await job.trigger(\n (body.input ?? {}) as Record<string, unknown>,\n {\n idempotencyKey: body.idempotencyKey,\n concurrencyKey: body.concurrencyKey,\n labels: body.labels,\n },\n )\n\n const response: TriggerResponse = { runId: run.id }\n return jsonResponse(response)\n })\n }\n\n async function handleSubscribe(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n const result = await requireRunAccess(url, ctx, 'subscribe')\n if (result instanceof Response) return result\n\n const stream = durably.subscribe(result.runId)\n const sseStream = createThrottledSSEStreamFromReader(\n stream.getReader() as ReadableStreamDefaultReader<AnyEventInput>,\n throttleMs,\n )\n return createSSEResponse(sseStream)\n }\n\n async function handleRuns(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const filterOrError = parseRunFilter(url)\n if (filterOrError instanceof Response) return filterOrError\n\n let filter: RunFilter<TLabels> = filterOrError as RunFilter<TLabels>\n\n // Auth hook: scopeRuns\n if (auth?.scopeRuns && ctx !== undefined) {\n filter = await auth.scopeRuns(ctx as TContext, filter)\n }\n\n const runs = await durably.getRuns(filter)\n return jsonResponse(runs.map(toClientRun))\n })\n }\n\n async function handleRun(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'read')\n if (result instanceof Response) return result\n\n return jsonResponse(toClientRun(result.run))\n })\n }\n\n async function handleSteps(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'steps')\n if (result instanceof Response) return result\n\n const steps = await durably.storage.getSteps(result.runId)\n return jsonResponse(steps)\n })\n }\n\n async function handleRetry(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'retry')\n if (result instanceof Response) return result\n\n await durably.retry(result.runId)\n return successResponse()\n })\n }\n\n async function handleCancel(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'cancel')\n if (result instanceof Response) return result\n\n await durably.cancel(result.runId)\n return successResponse()\n })\n }\n\n async function handleDelete(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'delete')\n if (result instanceof Response) return result\n\n await durably.deleteRun(result.runId)\n return successResponse()\n })\n }\n\n async function handleRunsSubscribe(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n let filter: RunsSubscribeFilter<TLabels>\n\n if (ctx !== undefined && auth?.scopeRunsSubscribe) {\n const parsed = parseRunsSubscribeFilter(\n url,\n ) as RunsSubscribeFilter<TLabels>\n filter = await auth.scopeRunsSubscribe(ctx as TContext, parsed)\n } else if (ctx !== undefined && auth?.scopeRuns) {\n // Fallback: use scopeRuns with subscribe-compatible filter\n const parsed = parseRunsSubscribeFilter(\n url,\n ) as RunsSubscribeFilter<TLabels>\n const scoped = await auth.scopeRuns(\n ctx as TContext,\n {\n ...parsed,\n } as RunFilter<TLabels>,\n )\n filter = { jobName: scoped.jobName, labels: scoped.labels }\n } else {\n filter = parseRunsSubscribeFilter(url) as RunsSubscribeFilter<TLabels>\n }\n\n return createRunsSSEStream(filter)\n }\n\n function createRunsSSEStream(filter: RunsSubscribeFilter): Response {\n const jobNameFilter = Array.isArray(filter.jobName)\n ? filter.jobName\n : filter.jobName\n ? [filter.jobName]\n : []\n const labelsFilter = filter.labels\n\n const matchesFilter = (\n jobName: string,\n labels?: Record<string, string>,\n ) => {\n if (jobNameFilter.length > 0 && !jobNameFilter.includes(jobName))\n return false\n if (\n labelsFilter &&\n (!labels ||\n !matchesLabels(labels, labelsFilter as Record<string, string>))\n )\n return false\n return true\n }\n\n const sseStream = createSSEStreamFromSubscriptions(\n (innerCtrl: SSEStreamController) => {\n const { controller: ctrl, dispose } = createThrottledSSEController(\n innerCtrl,\n throttleMs,\n )\n\n const unsubscribes = [\n durably.on('run:trigger', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:trigger',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:start', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:start',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:complete', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:complete',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:fail', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:fail',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:cancel', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:cancel',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:delete', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:delete',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:retry', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:retry',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:progress', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:progress',\n runId: event.runId,\n jobName: event.jobName,\n progress: event.progress,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('step:start', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'step:start',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('step:complete', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'step:complete',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('step:fail', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'step:fail',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n error: event.error,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('step:cancel', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'step:cancel',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('log:write', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'log:write',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n stepName: event.stepName,\n level: event.level,\n message: event.message,\n data: event.data,\n })\n }\n }),\n ]\n\n return [...unsubscribes, dispose]\n },\n )\n\n return createSSEResponse(sseStream)\n }\n\n // --- Public API: only handle() ---\n\n return {\n async handle(request: Request, basePath: string): Promise<Response> {\n try {\n // 1. Authenticate (fail fast before anything else)\n let ctx: TContext | undefined\n if (auth?.authenticate) {\n ctx = await auth.authenticate(request)\n }\n\n // 2. Run onRequest hook (lazy init: migrations, worker start)\n if (options?.onRequest) {\n await options.onRequest()\n }\n\n // 3. Route by path + method\n const url = new URL(request.url)\n const path = url.pathname.replace(basePath, '')\n const method = request.method\n\n // GET routes\n if (method === 'GET') {\n if (path === '/subscribe') return await handleSubscribe(url, ctx)\n if (path === '/runs') return await handleRuns(url, ctx)\n if (path === '/run') return await handleRun(url, ctx)\n if (path === '/steps') return await handleSteps(url, ctx)\n if (path === '/runs/subscribe')\n return await handleRunsSubscribe(url, ctx)\n }\n\n // POST routes\n if (method === 'POST') {\n if (path === '/trigger') return await handleTrigger(request, ctx)\n if (path === '/retry') return await handleRetry(url, ctx)\n if (path === '/cancel') return await handleCancel(url, ctx)\n }\n\n // DELETE routes\n if (method === 'DELETE') {\n if (path === '/run') return await handleDelete(url, ctx)\n }\n\n return new Response('Not Found', { status: 404 })\n } catch (error) {\n // Auth hooks throw Response to reject — return as-is\n if (error instanceof Response) return error\n return errorResponse(getErrorMessage(error), 500)\n }\n },\n }\n}\n"],"mappings":"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","names":["prettifyError","prettifyError"]}
1
+ {"version":3,"sources":["../src/durably.ts","../src/errors.ts","../src/context.ts","../src/events.ts","../src/job.ts","../src/migrations.ts","../src/storage.ts","../src/worker.ts","../src/define-job.ts","../src/http.ts","../src/sse.ts","../src/server.ts"],"sourcesContent":["import type { Dialect } from 'kysely'\nimport { Kysely } from 'kysely'\nimport { monotonicFactory } from 'ulidx'\nimport type { z } from 'zod'\nimport { createStepContext } from './context'\nimport type { JobDefinition } from './define-job'\nimport { CancelledError, getErrorMessage, LeaseLostError } from './errors'\nimport {\n type AnyEventInput,\n type DurablyEvent,\n type ErrorHandler,\n type EventEmitter,\n type EventListener,\n type EventType,\n type Unsubscribe,\n createEventEmitter,\n} from './events'\nimport {\n type JobHandle,\n type JobRegistry,\n createJobHandle,\n createJobRegistry,\n validateJobInputOrThrow,\n} from './job'\nimport { runMigrations } from './migrations'\nimport type { Database } from './schema'\nimport {\n type DatabaseBackend,\n type Run,\n type RunFilter,\n type Store,\n createKyselyStore,\n} from './storage'\nimport { type Worker, createWorker } from './worker'\n\n/**\n * Options for creating a Durably instance\n */\nexport interface DurablyOptions<\n TLabels extends Record<string, string> = Record<string, string>,\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n TJobs extends Record<string, JobDefinition<string, any, any>> = Record<\n string,\n never\n >,\n> {\n dialect: Dialect\n /**\n * Browser-local singleton key used to detect multiple runtimes against the same local database in one tab.\n * When omitted, Durably will use browser-local dialect metadata if available.\n */\n singletonKey?: string\n pollingIntervalMs?: number\n leaseRenewIntervalMs?: number\n leaseMs?: number\n preserveSteps?: boolean\n /**\n * Zod schema for labels. When provided:\n * - Labels are type-checked at compile time\n * - Labels are validated at runtime on trigger()\n */\n labels?: z.ZodType<TLabels>\n /**\n * Job definitions to register. Shorthand for calling .register() after creation.\n * @example\n * ```ts\n * const durably = createDurably({\n * dialect,\n * jobs: { importCsv: importCsvJob, syncUsers: syncUsersJob },\n * })\n * ```\n */\n jobs?: TJobs\n /**\n * Auto-delete terminal runs older than the specified duration.\n * Only runs in terminal states (completed, failed, cancelled) are purged.\n * @example '30d' (30 days), '24h' (24 hours), '60m' (60 minutes)\n */\n retainRuns?: string\n}\n\n/**\n * Default configuration values\n */\nconst DEFAULTS = {\n pollingIntervalMs: 1000,\n leaseRenewIntervalMs: 5000,\n leaseMs: 30000,\n preserveSteps: false,\n} as const\n\nfunction parseDuration(value: string): number {\n const match = value.match(/^(\\d+)(d|h|m)$/)\n if (!match) {\n throw new Error(\n `Invalid duration format: \"${value}\". Use e.g. '30d', '24h', '60m'`,\n )\n }\n const num = Number.parseInt(match[1], 10)\n const unit = match[2]\n const multipliers: Record<string, number> = {\n d: 86400000,\n h: 3600000,\n m: 60000,\n }\n return num * multipliers[unit]\n}\n\nconst PURGE_INTERVAL_MS = 60_000\n\nconst ulid = monotonicFactory()\nconst BROWSER_SINGLETON_REGISTRY_KEY = '__durablyBrowserSingletonRegistry'\nconst BROWSER_LOCAL_DIALECT_KEY = '__durablyBrowserLocalKey'\n\nfunction defaultWorkerId(): string {\n return `worker_${ulid()}`\n}\n\nfunction detectBackend(dialect: Dialect): DatabaseBackend {\n return dialect.constructor.name === 'PostgresDialect' ? 'postgres' : 'generic'\n}\n\nfunction isBrowserLikeEnvironment(): boolean {\n return (\n typeof globalThis.window !== 'undefined' ||\n typeof globalThis.document !== 'undefined'\n )\n}\n\nfunction getBrowserSingletonKey(\n dialect: Dialect,\n explicitKey?: string,\n): string | null {\n if (!isBrowserLikeEnvironment()) {\n return null\n }\n\n if (explicitKey) {\n return explicitKey\n }\n\n const taggedDialect = dialect as Dialect & {\n [BROWSER_LOCAL_DIALECT_KEY]?: unknown\n }\n const taggedKey = taggedDialect[BROWSER_LOCAL_DIALECT_KEY]\n return typeof taggedKey === 'string' ? taggedKey : null\n}\n\nfunction registerBrowserSingletonWarning(singletonKey: string): () => void {\n type Registry = Map<string, Set<string>>\n const globalRegistry = globalThis as typeof globalThis & {\n [BROWSER_SINGLETON_REGISTRY_KEY]?: Registry\n }\n const registry =\n globalRegistry[BROWSER_SINGLETON_REGISTRY_KEY] ??\n new Map<string, Set<string>>()\n globalRegistry[BROWSER_SINGLETON_REGISTRY_KEY] = registry\n\n const instanceId = ulid()\n const instances = registry.get(singletonKey) ?? new Set<string>()\n const hadExistingInstance = instances.size > 0\n instances.add(instanceId)\n registry.set(singletonKey, instances)\n\n if (\n hadExistingInstance &&\n (typeof process === 'undefined' || process.env.NODE_ENV !== 'production')\n ) {\n console.warn(\n `[durably] Multiple runtimes were created for browser-local store \"${singletonKey}\" in one tab. Prefer a single shared instance per tab.`,\n )\n }\n\n let released = false\n return () => {\n if (released) {\n return\n }\n released = true\n const activeInstances = registry.get(singletonKey)\n if (!activeInstances) {\n return\n }\n activeInstances.delete(instanceId)\n if (activeInstances.size === 0) {\n registry.delete(singletonKey)\n }\n }\n}\n\n/**\n * Plugin interface for extending Durably\n */\nexport interface DurablyPlugin {\n name: string\n // biome-ignore lint/suspicious/noExplicitAny: plugin needs to accept any Durably instance\n install(durably: Durably<any, any>): void\n}\n\n/**\n * Helper type to transform JobDefinition record to JobHandle record\n */\ntype TransformToHandles<\n TJobs extends Record<string, JobDefinition<string, unknown, unknown>>,\n TLabels extends Record<string, string> = Record<string, string>,\n> = {\n [K in keyof TJobs]: TJobs[K] extends JobDefinition<\n infer TName,\n infer TInput,\n infer TOutput\n >\n ? JobHandle<TName & string, TInput, TOutput, TLabels>\n : never\n}\n\n/**\n * Durably instance with type-safe jobs\n */\nexport interface Durably<\n TJobs extends Record<\n string,\n JobHandle<string, unknown, unknown, Record<string, string>>\n > = Record<string, never>,\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n /**\n * Registered job handles (type-safe)\n */\n readonly jobs: TJobs\n\n /**\n * Initialize Durably: run migrations and start the worker\n * This is the recommended way to start Durably.\n * Equivalent to calling migrate() then start().\n * @example\n * ```ts\n * const durably = createDurably({ dialect }).register({ ... })\n * await durably.init()\n * ```\n */\n init(): Promise<void>\n\n /**\n * Run database migrations\n * This is idempotent and safe to call multiple times\n */\n migrate(): Promise<void>\n\n /**\n * Get the underlying Kysely database instance\n * Useful for testing and advanced use cases\n */\n readonly db: Kysely<Database>\n\n /**\n * Storage layer for database operations\n */\n readonly storage: Store<TLabels>\n\n /**\n * Register an event listener\n * @returns Unsubscribe function\n */\n on<T extends EventType>(type: T, listener: EventListener<T>): Unsubscribe\n\n /**\n * Emit an event (auto-assigns timestamp and sequence)\n */\n emit(event: AnyEventInput): void\n\n /**\n * Register an error handler for listener exceptions\n */\n onError(handler: ErrorHandler): void\n\n /**\n * Register job definitions and return a new Durably instance with type-safe jobs\n * @example\n * ```ts\n * const durably = createDurably({ dialect })\n * .register({\n * importCsv: importCsvJob,\n * syncUsers: syncUsersJob,\n * })\n * await durably.migrate()\n * // Usage: durably.jobs.importCsv.trigger({ rows: [...] })\n * ```\n */\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n register<TNewJobs extends Record<string, JobDefinition<string, any, any>>>(\n jobDefs: TNewJobs,\n ): Durably<TJobs & TransformToHandles<TNewJobs, TLabels>, TLabels>\n\n /**\n * Process a single claimable run.\n */\n processOne(options?: { workerId?: string }): Promise<boolean>\n\n /**\n * Process runs until the queue appears idle.\n */\n processUntilIdle(options?: {\n workerId?: string\n maxRuns?: number\n }): Promise<number>\n\n /**\n * Start the worker polling loop\n */\n start(options?: { workerId?: string }): void\n\n /**\n * Stop the worker after current run completes\n */\n stop(): Promise<void>\n\n /**\n * Create a fresh run from a completed, failed, or cancelled run\n * @throws Error if run is pending, running, or does not exist\n */\n retrigger(runId: string): Promise<Run<TLabels>>\n\n /**\n * Cancel a pending or running run\n * @throws Error if run is already completed, failed, or cancelled\n */\n cancel(runId: string): Promise<void>\n\n /**\n * Delete a completed, failed, or cancelled run and its associated steps and logs\n * @throws Error if run is pending or running, or does not exist\n */\n deleteRun(runId: string): Promise<void>\n\n /**\n * Delete terminal runs older than the specified cutoff.\n * Only runs in terminal states (completed, failed, cancelled) are purged.\n * @returns Number of deleted runs\n */\n purgeRuns(options: { olderThan: Date; limit?: number }): Promise<number>\n\n /**\n * Get a run by ID\n * @example\n * ```ts\n * // Untyped (returns Run)\n * const run = await durably.getRun(runId)\n *\n * // Typed (returns custom type)\n * type MyRun = Run & { input: { userId: string }; output: { count: number } | null }\n * const typedRun = await durably.getRun<MyRun>(runId)\n * ```\n */\n getRun<T extends Run<TLabels> = Run<TLabels>>(\n runId: string,\n ): Promise<T | null>\n\n /**\n * Get runs with optional filtering\n * @example\n * ```ts\n * // Untyped (returns Run[])\n * const runs = await durably.getRuns({ status: 'completed' })\n *\n * // Typed (returns custom type[])\n * type MyRun = Run & { input: { userId: string }; output: { count: number } | null }\n * const typedRuns = await durably.getRuns<MyRun>({ jobName: 'my-job' })\n * ```\n */\n getRuns<T extends Run<TLabels> = Run<TLabels>>(\n filter?: RunFilter<TLabels>,\n ): Promise<T[]>\n\n /**\n * Register a plugin\n */\n use(plugin: DurablyPlugin): void\n\n /**\n * Get a registered job handle by name\n * Returns undefined if job is not registered\n */\n getJob<TName extends string = string>(\n name: TName,\n ): JobHandle<TName, Record<string, unknown>, unknown, TLabels> | undefined\n\n /**\n * Subscribe to events for a specific run\n * Returns a ReadableStream that can be used for SSE\n */\n subscribe(runId: string): ReadableStream<DurablyEvent>\n}\n\n/**\n * Internal state shared across Durably instances\n */\ninterface DurablyState<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n db: Kysely<Database>\n storage: Store<TLabels>\n eventEmitter: EventEmitter\n jobRegistry: JobRegistry\n worker: Worker\n labelsSchema: z.ZodType | undefined\n preserveSteps: boolean\n migrating: Promise<void> | null\n migrated: boolean\n leaseMs: number\n leaseRenewIntervalMs: number\n retainRunsMs: number | null\n lastPurgeAt: number\n releaseBrowserSingleton: () => void\n}\n\n/**\n * Create a Durably instance implementation\n */\nfunction createDurablyInstance<\n TJobs extends Record<\n string,\n JobHandle<string, unknown, unknown, Record<string, string>>\n >,\n TLabels extends Record<string, string> = Record<string, string>,\n>(state: DurablyState<TLabels>, jobs: TJobs): Durably<TJobs, TLabels> {\n const {\n db,\n storage,\n eventEmitter,\n jobRegistry,\n worker,\n releaseBrowserSingleton,\n } = state\n\n async function getRunOrThrow(runId: string): Promise<Run<TLabels>> {\n const run = await storage.getRun(runId)\n if (!run) {\n throw new Error(`Run not found: ${runId}`)\n }\n return run as Run<TLabels>\n }\n\n async function executeRun(\n run: Run<TLabels>,\n workerId: string,\n ): Promise<void> {\n const job = jobRegistry.get(run.jobName)\n if (!job) {\n await storage.failRun(\n run.id,\n run.leaseGeneration,\n `Unknown job: ${run.jobName}`,\n new Date().toISOString(),\n )\n return\n }\n\n const { step, abortLeaseOwnership, dispose } = createStepContext(\n run,\n run.jobName,\n run.leaseGeneration,\n storage,\n eventEmitter,\n )\n let leaseDeadlineTimer: ReturnType<typeof setTimeout> | null = null\n\n const scheduleLeaseDeadline = (leaseExpiresAt: string | null) => {\n if (leaseDeadlineTimer) {\n clearTimeout(leaseDeadlineTimer)\n leaseDeadlineTimer = null\n }\n\n if (!leaseExpiresAt) {\n return\n }\n\n const delay = Math.max(0, Date.parse(leaseExpiresAt) - Date.now())\n leaseDeadlineTimer = setTimeout(() => {\n abortLeaseOwnership()\n }, delay)\n }\n\n scheduleLeaseDeadline(run.leaseExpiresAt)\n\n const leaseTimer = setInterval(() => {\n const now = new Date().toISOString()\n storage\n .renewLease(run.id, run.leaseGeneration, now, state.leaseMs)\n .then((renewed) => {\n if (!renewed) {\n abortLeaseOwnership()\n eventEmitter.emit({\n type: 'worker:error',\n error: `Lease renewal lost ownership for run ${run.id}`,\n context: 'lease-renewal',\n runId: run.id,\n })\n return\n }\n\n const renewedLeaseExpiresAt = new Date(\n Date.parse(now) + state.leaseMs,\n ).toISOString()\n\n scheduleLeaseDeadline(renewedLeaseExpiresAt)\n\n eventEmitter.emit({\n type: 'run:lease-renewed',\n runId: run.id,\n jobName: run.jobName,\n leaseOwner: workerId,\n leaseExpiresAt: renewedLeaseExpiresAt,\n labels: run.labels,\n })\n })\n .catch((error) => {\n eventEmitter.emit({\n type: 'worker:error',\n error: getErrorMessage(error),\n context: 'lease-renewal',\n runId: run.id,\n })\n })\n }, state.leaseRenewIntervalMs)\n\n const started = Date.now()\n let reachedTerminalState = false\n\n try {\n eventEmitter.emit({\n type: 'run:leased',\n runId: run.id,\n jobName: run.jobName,\n input: run.input,\n leaseOwner: workerId,\n leaseExpiresAt: run.leaseExpiresAt ?? new Date().toISOString(),\n labels: run.labels,\n })\n const output = await job.fn(step, run.input)\n\n if (job.outputSchema) {\n const parseResult = job.outputSchema.safeParse(output)\n if (!parseResult.success) {\n throw new Error(`Invalid output: ${parseResult.error.message}`)\n }\n }\n\n const completedAt = new Date().toISOString()\n const completed = await storage.completeRun(\n run.id,\n run.leaseGeneration,\n output,\n completedAt,\n )\n\n if (completed) {\n reachedTerminalState = true\n eventEmitter.emit({\n type: 'run:complete',\n runId: run.id,\n jobName: run.jobName,\n output,\n duration: Date.now() - started,\n labels: run.labels,\n })\n } else {\n eventEmitter.emit({\n type: 'worker:error',\n error: `Lease lost before completing run ${run.id}`,\n context: 'run-completion',\n })\n }\n } catch (error) {\n if (error instanceof LeaseLostError || error instanceof CancelledError) {\n return\n }\n\n const errorMessage = getErrorMessage(error)\n const completedAt = new Date().toISOString()\n const failed = await storage.failRun(\n run.id,\n run.leaseGeneration,\n errorMessage,\n completedAt,\n )\n\n if (failed) {\n reachedTerminalState = true\n const steps = await storage.getSteps(run.id)\n const failedStep = steps.find((entry) => entry.status === 'failed')\n eventEmitter.emit({\n type: 'run:fail',\n runId: run.id,\n jobName: run.jobName,\n error: errorMessage,\n failedStepName: failedStep?.name ?? 'unknown',\n labels: run.labels,\n })\n } else {\n eventEmitter.emit({\n type: 'worker:error',\n error: `Lease lost before recording failure for run ${run.id}`,\n context: 'run-failure',\n })\n }\n } finally {\n clearInterval(leaseTimer)\n if (leaseDeadlineTimer) {\n clearTimeout(leaseDeadlineTimer)\n }\n dispose()\n if (!state.preserveSteps && reachedTerminalState) {\n await storage.deleteSteps(run.id)\n }\n }\n }\n\n const durably: Durably<TJobs, TLabels> = {\n db,\n storage,\n jobs,\n on: eventEmitter.on,\n emit: eventEmitter.emit,\n onError: eventEmitter.onError,\n start: worker.start,\n async stop(): Promise<void> {\n releaseBrowserSingleton()\n await worker.stop()\n },\n\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n register<TNewJobs extends Record<string, JobDefinition<string, any, any>>>(\n jobDefs: TNewJobs,\n ): Durably<TJobs & TransformToHandles<TNewJobs, TLabels>, TLabels> {\n const newHandles = {} as TransformToHandles<TNewJobs, TLabels>\n\n for (const key of Object.keys(jobDefs) as (keyof TNewJobs)[]) {\n const jobDef = jobDefs[key]\n const handle = createJobHandle(\n jobDef,\n storage,\n eventEmitter,\n jobRegistry,\n state.labelsSchema as z.ZodType<TLabels> | undefined,\n )\n newHandles[key] = handle as TransformToHandles<\n TNewJobs,\n TLabels\n >[typeof key]\n }\n\n // Create new instance with merged jobs\n const mergedJobs = { ...jobs, ...newHandles } as TJobs &\n TransformToHandles<TNewJobs, TLabels>\n return createDurablyInstance<typeof mergedJobs, TLabels>(\n state,\n mergedJobs,\n )\n },\n\n getRun: storage.getRun.bind(storage),\n getRuns: storage.getRuns.bind(storage),\n\n use(plugin: DurablyPlugin): void {\n plugin.install(durably)\n },\n\n getJob<TName extends string = string>(\n name: TName,\n ): JobHandle<TName, Record<string, unknown>, unknown, TLabels> | undefined {\n const registeredJob = jobRegistry.get(name)\n if (!registeredJob) {\n return undefined\n }\n return registeredJob.handle as JobHandle<\n TName,\n Record<string, unknown>,\n unknown,\n TLabels\n >\n },\n\n subscribe(runId: string): ReadableStream<DurablyEvent> {\n // Track closed state and cleanup function in outer scope for cancel handler\n let closed = false\n let cleanup: (() => void) | null = null\n\n // Events that close the stream after enqueuing\n const closeEvents = new Set<EventType>([\n 'run:complete',\n 'run:fail',\n 'run:cancel',\n 'run:delete',\n ])\n // All event types to subscribe to for a run\n const subscribedEvents: EventType[] = [\n 'run:leased',\n 'run:complete',\n 'run:fail',\n 'run:cancel',\n 'run:delete',\n 'run:progress',\n 'step:start',\n 'step:complete',\n 'step:fail',\n 'log:write',\n ]\n\n return new ReadableStream<DurablyEvent>({\n start: (controller) => {\n // Subscribe to future events first (before DB read) to avoid race conditions\n const unsubscribes = subscribedEvents.map((type) =>\n eventEmitter.on(type, (event) => {\n if (closed || event.runId !== runId) return\n controller.enqueue(event)\n if (closeEvents.has(type)) {\n closed = true\n cleanup?.()\n controller.close()\n }\n }),\n )\n\n cleanup = () => {\n for (const unsub of unsubscribes) unsub()\n }\n\n const closeStream = () => {\n closed = true\n cleanup?.()\n controller.close()\n }\n\n // Send current run state as initial events so clients that connect\n // after events were emitted still get the correct state (#106)\n storage\n .getRun(runId)\n .then((run) => {\n if (closed || !run) return\n\n // Synthetic replay events use sequence=0 and approximate fields\n // (e.g. duration=0) since exact values aren't persisted in the run record.\n const base = {\n runId,\n jobName: run.jobName,\n labels: run.labels as Record<string, string>,\n timestamp: new Date().toISOString(),\n sequence: 0,\n }\n\n if (run.status === 'leased') {\n controller.enqueue({\n ...base,\n type: 'run:leased',\n input: run.input,\n leaseOwner: run.leaseOwner ?? '',\n leaseExpiresAt: run.leaseExpiresAt ?? '',\n })\n if (run.progress != null) {\n controller.enqueue({\n ...base,\n type: 'run:progress',\n progress: run.progress,\n })\n }\n } else if (run.status === 'completed') {\n controller.enqueue({\n ...base,\n type: 'run:complete',\n output: run.output,\n duration: 0,\n })\n closeStream()\n } else if (run.status === 'failed') {\n controller.enqueue({\n ...base,\n type: 'run:fail',\n error: run.error ?? 'Unknown error',\n failedStepName: '',\n })\n closeStream()\n } else if (run.status === 'cancelled') {\n controller.enqueue({\n ...base,\n type: 'run:cancel',\n })\n closeStream()\n }\n // pending: no initial event needed, useJobRun already defaults to pending\n })\n .catch((error) => {\n if (closed) return\n closed = true\n cleanup?.()\n controller.error(error)\n })\n },\n cancel: () => {\n // Clean up event listeners when stream is cancelled by consumer\n if (!closed) {\n closed = true\n cleanup?.()\n }\n },\n })\n },\n\n async retrigger(runId: string): Promise<Run<TLabels>> {\n const run = await getRunOrThrow(runId)\n if (run.status === 'pending') {\n throw new Error(`Cannot retrigger pending run: ${runId}`)\n }\n if (run.status === 'leased') {\n throw new Error(`Cannot retrigger leased run: ${runId}`)\n }\n const job = jobRegistry.get(run.jobName)\n if (!job) {\n throw new Error(`Unknown job: ${run.jobName}`)\n }\n\n // Validate original input against current schema\n const validatedInput = validateJobInputOrThrow(\n job.inputSchema,\n run.input,\n `Cannot retrigger run ${runId}`,\n )\n\n const nextRun = await storage.enqueue({\n jobName: run.jobName,\n input: validatedInput,\n concurrencyKey: run.concurrencyKey ?? undefined,\n labels: run.labels,\n })\n\n eventEmitter.emit({\n type: 'run:trigger',\n runId: nextRun.id,\n jobName: run.jobName,\n input: validatedInput,\n labels: run.labels,\n })\n\n return nextRun as Run<TLabels>\n },\n\n async cancel(runId: string): Promise<void> {\n const run = await getRunOrThrow(runId)\n if (run.status === 'completed') {\n throw new Error(`Cannot cancel completed run: ${runId}`)\n }\n if (run.status === 'failed') {\n throw new Error(`Cannot cancel failed run: ${runId}`)\n }\n if (run.status === 'cancelled') {\n throw new Error(`Cannot cancel already cancelled run: ${runId}`)\n }\n const wasPending = run.status === 'pending'\n const cancelled = await storage.cancelRun(runId, new Date().toISOString())\n\n if (!cancelled) {\n // Run transitioned to a terminal state between the check and the update\n const current = await getRunOrThrow(runId)\n throw new Error(\n `Cannot cancel run ${runId}: status changed to ${current.status}`,\n )\n }\n\n // For pending runs, no worker will clean up steps, so do it here\n if (wasPending && !state.preserveSteps) {\n await storage.deleteSteps(runId)\n }\n\n // Emit run:cancel event\n eventEmitter.emit({\n type: 'run:cancel',\n runId,\n jobName: run.jobName,\n labels: run.labels,\n })\n },\n\n async deleteRun(runId: string): Promise<void> {\n const run = await getRunOrThrow(runId)\n if (run.status === 'pending') {\n throw new Error(`Cannot delete pending run: ${runId}`)\n }\n if (run.status === 'leased') {\n throw new Error(`Cannot delete leased run: ${runId}`)\n }\n await storage.deleteRun(runId)\n\n // Emit run:delete event\n eventEmitter.emit({\n type: 'run:delete',\n runId,\n jobName: run.jobName,\n labels: run.labels,\n })\n },\n\n async purgeRuns(options: {\n olderThan: Date\n limit?: number\n }): Promise<number> {\n return storage.purgeRuns({\n olderThan: options.olderThan.toISOString(),\n limit: options.limit,\n })\n },\n\n async processOne(options?: { workerId?: string }): Promise<boolean> {\n const workerId = options?.workerId ?? defaultWorkerId()\n const now = new Date().toISOString()\n\n await storage.releaseExpiredLeases(now)\n\n const run = await storage.claimNext(workerId, now, state.leaseMs)\n if (!run) {\n // Auto-purge old terminal runs if retainRuns is configured.\n // Runs after claimNext so purge never serializes with job claiming.\n // lastPurgeAt starts at 0, so the first idle cycle purges immediately.\n if (\n state.retainRunsMs !== null &&\n Date.now() - state.lastPurgeAt >= PURGE_INTERVAL_MS\n ) {\n const purgeNow = Date.now()\n state.lastPurgeAt = purgeNow\n const cutoff = new Date(purgeNow - state.retainRunsMs).toISOString()\n storage\n .purgeRuns({ olderThan: cutoff, limit: 100 })\n .catch((error) => {\n eventEmitter.emit({\n type: 'worker:error',\n error: getErrorMessage(error),\n context: 'auto-purge',\n })\n })\n }\n return false\n }\n\n await executeRun(run, workerId)\n return true\n },\n\n async processUntilIdle(options?: {\n workerId?: string\n maxRuns?: number\n }): Promise<number> {\n const workerId = options?.workerId ?? defaultWorkerId()\n const maxRuns = options?.maxRuns ?? Number.POSITIVE_INFINITY\n let processed = 0\n\n while (processed < maxRuns) {\n const didProcess = await this.processOne({ workerId })\n if (!didProcess) {\n break\n }\n processed++\n }\n\n return processed\n },\n\n async migrate(): Promise<void> {\n if (state.migrated) {\n return\n }\n\n if (state.migrating) {\n return state.migrating\n }\n\n state.migrating = runMigrations(db)\n .then(() => {\n state.migrated = true\n })\n .finally(() => {\n state.migrating = null\n })\n\n return state.migrating\n },\n\n async init(): Promise<void> {\n await this.migrate()\n this.start()\n },\n }\n\n return durably\n}\n\n/**\n * Create a Durably instance\n */\n// Overload: with jobs\nexport function createDurably<\n TLabels extends Record<string, string> = Record<string, string>,\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n TJobs extends Record<string, JobDefinition<string, any, any>> = Record<\n string,\n never\n >,\n>(\n options: DurablyOptions<TLabels, TJobs> & { jobs: TJobs },\n): Durably<TransformToHandles<TJobs, TLabels>, TLabels>\n\n// Overload: without jobs\nexport function createDurably<\n TLabels extends Record<string, string> = Record<string, string>,\n>(options: DurablyOptions<TLabels>): Durably<Record<string, never>, TLabels>\n\n// Implementation\nexport function createDurably<\n TLabels extends Record<string, string> = Record<string, string>,\n // biome-ignore lint/suspicious/noExplicitAny: flexible type constraint for job definitions\n TJobs extends Record<string, JobDefinition<string, any, any>> = Record<\n string,\n never\n >,\n>(\n options: DurablyOptions<TLabels, TJobs>,\n):\n | Durably<TransformToHandles<TJobs, TLabels>, TLabels>\n | Durably<Record<string, never>, TLabels> {\n const config = {\n pollingIntervalMs: options.pollingIntervalMs ?? DEFAULTS.pollingIntervalMs,\n leaseRenewIntervalMs:\n options.leaseRenewIntervalMs ?? DEFAULTS.leaseRenewIntervalMs,\n leaseMs: options.leaseMs ?? DEFAULTS.leaseMs,\n preserveSteps: options.preserveSteps ?? DEFAULTS.preserveSteps,\n retainRunsMs: options.retainRuns ? parseDuration(options.retainRuns) : null,\n }\n\n const db = new Kysely<Database>({ dialect: options.dialect })\n const singletonKey = getBrowserSingletonKey(\n options.dialect,\n options.singletonKey,\n )\n const releaseBrowserSingleton =\n singletonKey !== null\n ? registerBrowserSingletonWarning(singletonKey)\n : () => {}\n const backend = detectBackend(options.dialect)\n const storage = createKyselyStore(db, backend) as Store<TLabels>\n const originalDestroy = db.destroy.bind(db)\n db.destroy = (async () => {\n releaseBrowserSingleton()\n return originalDestroy()\n }) as typeof db.destroy\n const eventEmitter = createEventEmitter()\n const jobRegistry = createJobRegistry()\n let processOneImpl:\n | ((options?: { workerId?: string }) => Promise<boolean>)\n | null = null\n const worker = createWorker(\n { pollingIntervalMs: config.pollingIntervalMs },\n (runtimeOptions) => {\n if (!processOneImpl) {\n throw new Error('Durably runtime is not initialized')\n }\n return processOneImpl(runtimeOptions)\n },\n )\n\n const state: DurablyState<TLabels> = {\n db,\n storage,\n eventEmitter,\n jobRegistry,\n worker,\n labelsSchema: options.labels,\n preserveSteps: config.preserveSteps,\n migrating: null,\n migrated: false,\n leaseMs: config.leaseMs,\n leaseRenewIntervalMs: config.leaseRenewIntervalMs,\n retainRunsMs: config.retainRunsMs,\n lastPurgeAt: 0,\n releaseBrowserSingleton,\n }\n\n const instance = createDurablyInstance<Record<string, never>, TLabels>(\n state,\n {},\n )\n processOneImpl = instance.processOne\n\n if (options.jobs) {\n return instance.register(options.jobs)\n }\n\n return instance\n}\n","/**\n * Error thrown when a run is cancelled during execution.\n * The worker catches this error and treats it specially - it does not\n * mark the run as failed, as the run status is already 'cancelled'.\n */\nexport class CancelledError extends Error {\n constructor(runId: string) {\n super(`Run was cancelled: ${runId}`)\n this.name = 'CancelledError'\n }\n}\n\n/**\n * Error thrown when a worker loses lease ownership during execution.\n */\nexport class LeaseLostError extends Error {\n constructor(runId: string) {\n super(`Lease ownership was lost: ${runId}`)\n this.name = 'LeaseLostError'\n }\n}\n\n/**\n * Extract error message from unknown error\n */\nexport function getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","import { CancelledError, getErrorMessage, LeaseLostError } from './errors'\nimport type { EventEmitter } from './events'\nimport type { StepContext } from './job'\nimport type { Run, Store } from './storage'\n\nconst LEASE_LOST = 'lease-lost'\n\n/**\n * Create a step context for executing a run\n */\nexport function createStepContext(\n run: Run,\n jobName: string,\n leaseGeneration: number,\n storage: Store,\n eventEmitter: EventEmitter,\n): {\n step: StepContext\n abortLeaseOwnership(): void\n dispose: () => void\n} {\n let stepIndex = run.currentStepIndex\n let currentStepName: string | null = null\n\n const controller = new AbortController()\n\n function abortForLeaseLoss() {\n if (!controller.signal.aborted) {\n controller.abort(LEASE_LOST)\n }\n }\n\n function throwIfAborted(): void {\n if (!controller.signal.aborted) {\n return\n }\n\n if (controller.signal.reason === LEASE_LOST) {\n throw new LeaseLostError(run.id)\n }\n\n throw new CancelledError(run.id)\n }\n\n const unsubscribe = eventEmitter.on('run:cancel', (event) => {\n if (event.runId === run.id) {\n controller.abort()\n }\n })\n\n const step: StepContext = {\n get runId(): string {\n return run.id\n },\n\n get signal(): AbortSignal {\n return controller.signal\n },\n\n isAborted(): boolean {\n return controller.signal.aborted\n },\n\n throwIfAborted(): void {\n throwIfAborted()\n },\n\n async run<T>(\n name: string,\n fn: (signal: AbortSignal) => T | Promise<T>,\n ): Promise<T> {\n // Fast path: check in-memory signal first (set by run:cancel event)\n throwIfAborted()\n\n // Slow path: DB check for cases where event wasn't received\n // (e.g., run cancelled while worker was down, then resumed)\n const currentRun = await storage.getRun(run.id)\n if (currentRun?.status === 'cancelled') {\n controller.abort()\n throwIfAborted()\n }\n\n if (\n currentRun &&\n ((currentRun.status === 'leased' &&\n currentRun.leaseGeneration !== leaseGeneration) ||\n currentRun.status === 'completed' ||\n currentRun.status === 'failed')\n ) {\n abortForLeaseLoss()\n throwIfAborted()\n }\n\n // Check cancellation before replaying cached steps\n throwIfAborted()\n\n // Check if step was already completed\n const existingStep = await storage.getCompletedStep(run.id, name)\n if (existingStep) {\n stepIndex++\n return existingStep.output as T\n }\n\n // Track current step for log attribution\n currentStepName = name\n\n // Record step start time\n const startedAt = new Date().toISOString()\n const startTime = Date.now()\n\n // Emit step:start event\n eventEmitter.emit({\n type: 'step:start',\n runId: run.id,\n jobName,\n stepName: name,\n stepIndex,\n labels: run.labels,\n })\n\n try {\n // Execute the step with the abort signal\n const result = await fn(controller.signal)\n throwIfAborted()\n\n // Persist step result atomically with lease generation guard.\n // If the lease was reclaimed by another worker, this returns null.\n const savedStep = await storage.persistStep(run.id, leaseGeneration, {\n name,\n index: stepIndex,\n status: 'completed',\n output: result,\n startedAt,\n })\n\n if (!savedStep) {\n abortForLeaseLoss()\n throwIfAborted()\n }\n\n stepIndex++\n\n // Emit step:complete event\n eventEmitter.emit({\n type: 'step:complete',\n runId: run.id,\n jobName,\n stepName: name,\n stepIndex: stepIndex - 1,\n output: result,\n duration: Date.now() - startTime,\n labels: run.labels,\n })\n\n return result\n } catch (error) {\n // If lease was already lost, don't attempt to write step data —\n // we no longer own this run and must not pollute the new owner's state.\n if (error instanceof LeaseLostError) {\n throw error\n }\n\n // Check if signal was aborted due to lease loss (not cancellation).\n // fn() may have thrown a different error while the lease was lost.\n const isLeaseLost =\n controller.signal.aborted && controller.signal.reason === LEASE_LOST\n if (isLeaseLost) {\n throw new LeaseLostError(run.id)\n }\n\n const isCancelled = controller.signal.aborted\n const errorMessage = getErrorMessage(error)\n\n // Persist failed/cancelled step record with generation guard.\n // For cancellation: the generation still matches (cancelRun doesn't\n // change it), so the guard passes. For normal errors: the guard\n // prevents stale writes if the lease was reclaimed.\n const savedStep = await storage.persistStep(run.id, leaseGeneration, {\n name,\n index: stepIndex,\n status: isCancelled ? 'cancelled' : 'failed',\n error: errorMessage,\n startedAt,\n })\n\n if (!savedStep) {\n // Lease was lost during this window\n abortForLeaseLoss()\n throw new LeaseLostError(run.id)\n }\n\n eventEmitter.emit({\n ...(isCancelled\n ? { type: 'step:cancel' as const }\n : { type: 'step:fail' as const, error: errorMessage }),\n runId: run.id,\n jobName,\n stepName: name,\n stepIndex,\n labels: run.labels,\n })\n\n if (isCancelled) {\n throwIfAborted()\n }\n throw error\n } finally {\n // Clear current step after execution\n currentStepName = null\n }\n },\n\n progress(current: number, total?: number, message?: string): void {\n const progressData = { current, total, message }\n // Fire and forget - don't await\n storage.updateProgress(run.id, leaseGeneration, progressData)\n // Emit progress event\n eventEmitter.emit({\n type: 'run:progress',\n runId: run.id,\n jobName,\n progress: progressData,\n labels: run.labels,\n })\n },\n\n log: {\n info(message: string, data?: unknown): void {\n eventEmitter.emit({\n type: 'log:write',\n runId: run.id,\n jobName,\n labels: run.labels,\n stepName: currentStepName,\n level: 'info',\n message,\n data,\n })\n },\n\n warn(message: string, data?: unknown): void {\n eventEmitter.emit({\n type: 'log:write',\n runId: run.id,\n jobName,\n labels: run.labels,\n stepName: currentStepName,\n level: 'warn',\n message,\n data,\n })\n },\n\n error(message: string, data?: unknown): void {\n eventEmitter.emit({\n type: 'log:write',\n runId: run.id,\n jobName,\n labels: run.labels,\n stepName: currentStepName,\n level: 'error',\n message,\n data,\n })\n },\n },\n }\n\n return {\n step,\n abortLeaseOwnership: abortForLeaseLoss,\n dispose: unsubscribe,\n }\n}\n","/**\n * Base event interface\n */\nexport interface BaseEvent {\n type: string\n timestamp: string\n sequence: number\n}\n\n/**\n * Run trigger event (emitted when a job is triggered, before worker picks it up)\n */\nexport interface RunTriggerEvent extends BaseEvent {\n type: 'run:trigger'\n runId: string\n jobName: string\n input: unknown\n labels: Record<string, string>\n}\n\n/**\n * Run leased event\n */\nexport interface RunLeasedEvent extends BaseEvent {\n type: 'run:leased'\n runId: string\n jobName: string\n input: unknown\n leaseOwner: string\n leaseExpiresAt: string\n labels: Record<string, string>\n}\n\nexport interface RunLeaseRenewedEvent extends BaseEvent {\n type: 'run:lease-renewed'\n runId: string\n jobName: string\n leaseOwner: string\n leaseExpiresAt: string\n labels: Record<string, string>\n}\n\n/**\n * Run complete event\n */\nexport interface RunCompleteEvent extends BaseEvent {\n type: 'run:complete'\n runId: string\n jobName: string\n output: unknown\n duration: number\n labels: Record<string, string>\n}\n\n/**\n * Run fail event\n */\nexport interface RunFailEvent extends BaseEvent {\n type: 'run:fail'\n runId: string\n jobName: string\n error: string\n failedStepName: string\n labels: Record<string, string>\n}\n\n/**\n * Run cancel event\n */\nexport interface RunCancelEvent extends BaseEvent {\n type: 'run:cancel'\n runId: string\n jobName: string\n labels: Record<string, string>\n}\n\n/**\n * Run delete event (emitted when a run is deleted)\n */\nexport interface RunDeleteEvent extends BaseEvent {\n type: 'run:delete'\n runId: string\n jobName: string\n labels: Record<string, string>\n}\n\n/**\n * Progress data reported by step.progress()\n */\nexport interface ProgressData {\n current: number\n total?: number\n message?: string\n}\n\n/**\n * Run progress event\n */\nexport interface RunProgressEvent extends BaseEvent {\n type: 'run:progress'\n runId: string\n jobName: string\n progress: ProgressData\n labels: Record<string, string>\n}\n\n/**\n * Step start event\n */\nexport interface StepStartEvent extends BaseEvent {\n type: 'step:start'\n runId: string\n jobName: string\n stepName: string\n stepIndex: number\n labels: Record<string, string>\n}\n\n/**\n * Step complete event\n */\nexport interface StepCompleteEvent extends BaseEvent {\n type: 'step:complete'\n runId: string\n jobName: string\n stepName: string\n stepIndex: number\n output: unknown\n duration: number\n labels: Record<string, string>\n}\n\n/**\n * Step fail event\n */\nexport interface StepFailEvent extends BaseEvent {\n type: 'step:fail'\n runId: string\n jobName: string\n stepName: string\n stepIndex: number\n error: string\n labels: Record<string, string>\n}\n\nexport interface StepCancelEvent extends BaseEvent {\n type: 'step:cancel'\n runId: string\n jobName: string\n stepName: string\n stepIndex: number\n labels: Record<string, string>\n}\n\n/**\n * Log data reported by step.log\n */\nexport interface LogData {\n level: 'info' | 'warn' | 'error'\n message: string\n data?: unknown\n stepName?: string | null\n}\n\n/**\n * Log write event\n */\nexport interface LogWriteEvent extends BaseEvent, LogData {\n type: 'log:write'\n runId: string\n jobName: string\n labels: Record<string, string>\n stepName: string | null\n data: unknown\n}\n\n/**\n * Worker error event (internal errors like heartbeat failures)\n */\nexport interface WorkerErrorEvent extends BaseEvent {\n type: 'worker:error'\n error: string\n context: string\n runId?: string\n}\n\n/**\n * All event types as discriminated union\n */\nexport type DurablyEvent =\n | RunTriggerEvent\n | RunLeasedEvent\n | RunLeaseRenewedEvent\n | RunCompleteEvent\n | RunFailEvent\n | RunCancelEvent\n | RunDeleteEvent\n | RunProgressEvent\n | StepStartEvent\n | StepCompleteEvent\n | StepFailEvent\n | StepCancelEvent\n | LogWriteEvent\n | WorkerErrorEvent\n\n/**\n * Event types for type-safe event names\n */\nexport type EventType = DurablyEvent['type']\n\n/**\n * Extract event by type\n */\nexport type EventByType<T extends EventType> = Extract<\n DurablyEvent,\n { type: T }\n>\n\n/**\n * Event input (without auto-generated fields)\n */\nexport type EventInput<T extends EventType> = Omit<\n EventByType<T>,\n 'timestamp' | 'sequence'\n>\n\n/**\n * All possible event inputs as a union (properly distributed)\n */\nexport type AnyEventInput =\n | EventInput<'run:trigger'>\n | EventInput<'run:leased'>\n | EventInput<'run:lease-renewed'>\n | EventInput<'run:complete'>\n | EventInput<'run:fail'>\n | EventInput<'run:cancel'>\n | EventInput<'run:delete'>\n | EventInput<'run:progress'>\n | EventInput<'step:start'>\n | EventInput<'step:complete'>\n | EventInput<'step:fail'>\n | EventInput<'step:cancel'>\n | EventInput<'log:write'>\n | EventInput<'worker:error'>\n\n/**\n * Event listener function\n */\nexport type EventListener<T extends EventType> = (event: EventByType<T>) => void\n\n/**\n * Unsubscribe function returned by on()\n */\nexport type Unsubscribe = () => void\n\n/**\n * Error handler function for listener exceptions\n */\nexport type ErrorHandler = (error: Error, event: DurablyEvent) => void\n\n/**\n * Event emitter interface\n */\nexport interface EventEmitter {\n /**\n * Register an event listener\n * @returns Unsubscribe function\n */\n on<T extends EventType>(type: T, listener: EventListener<T>): Unsubscribe\n\n /**\n * Register an error handler for listener exceptions\n */\n onError(handler: ErrorHandler): void\n\n /**\n * Emit an event (auto-assigns timestamp and sequence)\n */\n emit(event: AnyEventInput): void\n}\n\n/**\n * Create an event emitter\n */\nexport function createEventEmitter(): EventEmitter {\n const listeners = new Map<EventType, Set<EventListener<EventType>>>()\n let sequence = 0\n let errorHandler: ErrorHandler | null = null\n\n return {\n on<T extends EventType>(type: T, listener: EventListener<T>): Unsubscribe {\n if (!listeners.has(type)) {\n listeners.set(type, new Set())\n }\n\n const typeListeners = listeners.get(type)\n typeListeners?.add(listener as unknown as EventListener<EventType>)\n\n return () => {\n typeListeners?.delete(listener as unknown as EventListener<EventType>)\n }\n },\n\n onError(handler: ErrorHandler): void {\n errorHandler = handler\n },\n\n emit(event: AnyEventInput): void {\n sequence++\n const fullEvent = {\n ...event,\n timestamp: new Date().toISOString(),\n sequence,\n } as DurablyEvent\n\n const typeListeners = listeners.get(event.type)\n if (!typeListeners) {\n return\n }\n\n for (const listener of typeListeners) {\n try {\n listener(fullEvent)\n } catch (error) {\n if (errorHandler) {\n errorHandler(\n error instanceof Error ? error : new Error(String(error)),\n fullEvent,\n )\n }\n // Continue to next listener regardless of error\n }\n }\n },\n }\n}\n","import { type z, prettifyError } from 'zod'\nimport type { JobDefinition } from './define-job'\nimport type { EventEmitter, LogData, ProgressData } from './events'\nimport type { Run, RunFilter, Store } from './storage'\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst noop = () => {}\n\n/**\n * Validate job input and throw on failure\n */\nexport function validateJobInputOrThrow<T>(\n schema: z.ZodType<T>,\n input: unknown,\n context?: string,\n): T {\n const result = schema.safeParse(input)\n if (!result.success) {\n const prefix = context ? `${context}: ` : ''\n throw new Error(`${prefix}Invalid input: ${prettifyError(result.error)}`)\n }\n return result.data\n}\n\n/**\n * Step context passed to the job function\n */\nexport interface StepContext {\n /**\n * The ID of the current run\n */\n readonly runId: string\n\n /**\n * AbortSignal for cooperative cancellation or lease-loss handling.\n */\n readonly signal: AbortSignal\n\n /**\n * Whether this execution should stop cooperatively.\n */\n isAborted(): boolean\n\n /**\n * Throw if execution has been cancelled or lease ownership was lost.\n */\n throwIfAborted(): void\n\n /**\n * Execute a step with automatic persistence and replay\n */\n run<T>(name: string, fn: (signal: AbortSignal) => T | Promise<T>): Promise<T>\n\n /**\n * Report progress for the current run\n */\n progress(current: number, total?: number, message?: string): void\n\n /**\n * Log a message\n */\n log: {\n info(message: string, data?: unknown): void\n warn(message: string, data?: unknown): void\n error(message: string, data?: unknown): void\n }\n}\n\n/**\n * Job function type\n */\nexport type JobFunction<TInput, TOutput> = (\n step: StepContext,\n input: TInput,\n) => Promise<TOutput>\n\n/**\n * Trigger options for trigger() and batchTrigger()\n */\nexport interface TriggerOptions<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n idempotencyKey?: string\n concurrencyKey?: string\n labels?: TLabels\n}\n\n/**\n * Options for triggerAndWait() (extends TriggerOptions with wait-specific options)\n */\nexport interface TriggerAndWaitOptions<\n TLabels extends Record<string, string> = Record<string, string>,\n> extends TriggerOptions<TLabels> {\n /** Timeout in milliseconds */\n timeout?: number\n /** Called when step.progress() is invoked during execution */\n onProgress?: (progress: ProgressData) => void | Promise<void>\n /** Called when step.log is invoked during execution */\n onLog?: (log: LogData) => void | Promise<void>\n}\n\n/**\n * Typed run with output type\n */\nexport interface TypedRun<\n TOutput,\n TLabels extends Record<string, string> = Record<string, string>,\n> extends Omit<Run<TLabels>, 'output'> {\n output: TOutput | null\n}\n\n/**\n * Batch trigger input - either just the input or input with options\n */\nexport type BatchTriggerInput<\n TInput,\n TLabels extends Record<string, string> = Record<string, string>,\n> = TInput | { input: TInput; options?: TriggerOptions<TLabels> }\n\n/**\n * Result of triggerAndWait\n */\nexport interface TriggerAndWaitResult<TOutput> {\n id: string\n output: TOutput\n}\n\n/**\n * Job handle returned by defineJob\n */\nexport interface JobHandle<\n TName extends string,\n TInput,\n TOutput,\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n readonly name: TName\n\n /**\n * Trigger a new run\n */\n trigger(\n input: TInput,\n options?: TriggerOptions<TLabels>,\n ): Promise<TypedRun<TOutput, TLabels>>\n\n /**\n * Trigger a new run and wait for completion\n * Returns the output directly, throws if the run fails\n */\n triggerAndWait(\n input: TInput,\n options?: TriggerAndWaitOptions<TLabels>,\n ): Promise<TriggerAndWaitResult<TOutput>>\n\n /**\n * Trigger multiple runs in a batch\n * All inputs are validated before any runs are created\n */\n batchTrigger(\n inputs: BatchTriggerInput<TInput, TLabels>[],\n ): Promise<TypedRun<TOutput, TLabels>[]>\n\n /**\n * Get a run by ID\n */\n getRun(id: string): Promise<TypedRun<TOutput, TLabels> | null>\n\n /**\n * Get runs with optional filter\n */\n getRuns(\n filter?: Omit<RunFilter<TLabels>, 'jobName'>,\n ): Promise<TypedRun<TOutput, TLabels>[]>\n}\n\n/**\n * Internal job registration\n */\nexport interface RegisteredJob<TInput, TOutput> {\n name: string\n inputSchema: z.ZodType\n outputSchema: z.ZodType | undefined\n labelsSchema: z.ZodType | undefined\n fn: JobFunction<TInput, TOutput>\n jobDef: JobDefinition<string, TInput, TOutput>\n // biome-ignore lint/suspicious/noExplicitAny: handle may have any labels type\n handle: JobHandle<string, TInput, TOutput, any>\n}\n\n/**\n * Job registry for managing registered jobs\n */\nexport interface JobRegistry {\n /**\n * Register a job (called internally by createJobHandle)\n */\n set<TInput, TOutput>(job: RegisteredJob<TInput, TOutput>): void\n\n /**\n * Get a registered job by name\n */\n get(name: string): RegisteredJob<unknown, unknown> | undefined\n\n /**\n * Check if a job is registered\n */\n has(name: string): boolean\n}\n\n/**\n * Create a job registry\n */\nexport function createJobRegistry(): JobRegistry {\n const jobs = new Map<string, RegisteredJob<unknown, unknown>>()\n\n return {\n set<TInput, TOutput>(job: RegisteredJob<TInput, TOutput>): void {\n jobs.set(job.name, job as RegisteredJob<unknown, unknown>)\n },\n\n get(name: string): RegisteredJob<unknown, unknown> | undefined {\n return jobs.get(name)\n },\n\n has(name: string): boolean {\n return jobs.has(name)\n },\n }\n}\n\n/**\n * Create a job handle from a JobDefinition\n */\nexport function createJobHandle<\n TName extends string,\n TInput,\n TOutput,\n TLabels extends Record<string, string> = Record<string, string>,\n>(\n jobDef: JobDefinition<TName, TInput, TOutput>,\n storage: Store,\n eventEmitter: EventEmitter,\n registry: JobRegistry,\n labelsSchema?: z.ZodType<TLabels>,\n): JobHandle<TName, TInput, TOutput, TLabels> {\n // Check if same JobDefinition is already registered (idempotent)\n const existingJob = registry.get(jobDef.name)\n if (existingJob) {\n // If same JobDefinition (same reference), return existing handle\n if (existingJob.jobDef === jobDef) {\n return existingJob.handle as JobHandle<TName, TInput, TOutput, TLabels>\n }\n // Different JobDefinition with same name - error\n throw new Error(\n `Job \"${jobDef.name}\" is already registered with a different definition`,\n )\n }\n\n const inputSchema = jobDef.input as z.ZodType<TInput>\n const outputSchema = jobDef.output as z.ZodType<TOutput> | undefined\n\n const handle: JobHandle<TName, TInput, TOutput, TLabels> = {\n name: jobDef.name,\n\n async trigger(\n input: TInput,\n options?: TriggerOptions<TLabels>,\n ): Promise<TypedRun<TOutput, TLabels>> {\n // Validate input\n const validatedInput = validateJobInputOrThrow(inputSchema, input)\n\n // Validate labels if schema provided\n if (labelsSchema && options?.labels) {\n validateJobInputOrThrow(labelsSchema, options.labels, 'labels')\n }\n\n // Create the run\n const run = await storage.enqueue({\n jobName: jobDef.name,\n input: validatedInput,\n idempotencyKey: options?.idempotencyKey,\n concurrencyKey: options?.concurrencyKey,\n labels: options?.labels,\n })\n\n // Emit run:trigger event\n eventEmitter.emit({\n type: 'run:trigger',\n runId: run.id,\n jobName: jobDef.name,\n input: validatedInput,\n labels: run.labels,\n })\n\n return run as TypedRun<TOutput, TLabels>\n },\n\n async triggerAndWait(\n input: TInput,\n options?: TriggerAndWaitOptions<TLabels>,\n ): Promise<TriggerAndWaitResult<TOutput>> {\n // Trigger the run\n const run = await this.trigger(input, options)\n\n // Wait for completion via event subscription\n return new Promise((resolve, reject) => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n let resolved = false\n\n const unsubscribes: (() => void)[] = []\n\n const cleanup = () => {\n if (resolved) return\n resolved = true\n for (const unsub of unsubscribes) unsub()\n if (timeoutId) {\n clearTimeout(timeoutId)\n }\n }\n\n unsubscribes.push(\n eventEmitter.on('run:complete', (event) => {\n if (event.runId === run.id && !resolved) {\n cleanup()\n resolve({\n id: run.id,\n output: event.output as TOutput,\n })\n }\n }),\n )\n\n unsubscribes.push(\n eventEmitter.on('run:fail', (event) => {\n if (event.runId === run.id && !resolved) {\n cleanup()\n reject(new Error(event.error))\n }\n }),\n )\n\n if (options?.onProgress) {\n const onProgress = options.onProgress\n unsubscribes.push(\n eventEmitter.on('run:progress', (event) => {\n if (event.runId === run.id && !resolved) {\n void Promise.resolve(onProgress(event.progress)).catch(noop)\n }\n }),\n )\n }\n\n if (options?.onLog) {\n const onLog = options.onLog\n unsubscribes.push(\n eventEmitter.on('log:write', (event) => {\n if (event.runId === run.id && !resolved) {\n const { level, message, data, stepName } = event\n void Promise.resolve(\n onLog({ level, message, data, stepName }),\n ).catch(noop)\n }\n }),\n )\n }\n\n // Check current status after subscribing (race condition mitigation)\n // If the run completed before we subscribed, we need to handle it\n storage\n .getRun(run.id)\n .then((currentRun) => {\n if (resolved || !currentRun) return\n if (currentRun.status === 'completed') {\n cleanup()\n resolve({\n id: run.id,\n output: currentRun.output as TOutput,\n })\n } else if (currentRun.status === 'failed') {\n cleanup()\n reject(new Error(currentRun.error || 'Run failed'))\n }\n })\n .catch((error) => {\n if (resolved) return\n cleanup()\n reject(error instanceof Error ? error : new Error(String(error)))\n })\n\n // Set timeout if specified\n if (options?.timeout !== undefined) {\n timeoutId = setTimeout(() => {\n if (!resolved) {\n cleanup()\n reject(\n new Error(`triggerAndWait timeout after ${options.timeout}ms`),\n )\n }\n }, options.timeout)\n }\n })\n },\n\n async batchTrigger(\n inputs: (TInput | { input: TInput; options?: TriggerOptions<TLabels> })[],\n ): Promise<TypedRun<TOutput, TLabels>[]> {\n if (inputs.length === 0) {\n return []\n }\n\n // Normalize inputs to { input, options } format\n const normalized = inputs.map((item) => {\n if (item && typeof item === 'object' && 'input' in item) {\n return item as { input: TInput; options?: TriggerOptions<TLabels> }\n }\n return { input: item as TInput, options: undefined }\n })\n\n // Validate all inputs and labels first (before creating any runs)\n const validated: {\n input: unknown\n options?: TriggerOptions<TLabels>\n }[] = []\n for (let i = 0; i < normalized.length; i++) {\n const validatedInput = validateJobInputOrThrow(\n inputSchema,\n normalized[i].input,\n `at index ${i}`,\n )\n if (labelsSchema && normalized[i].options?.labels) {\n validateJobInputOrThrow(\n labelsSchema,\n normalized[i].options?.labels,\n `labels at index ${i}`,\n )\n }\n validated.push({\n input: validatedInput,\n options: normalized[i].options,\n })\n }\n\n // Create all runs\n const runs = await storage.enqueueMany(\n validated.map((v) => ({\n jobName: jobDef.name,\n input: v.input,\n idempotencyKey: v.options?.idempotencyKey,\n concurrencyKey: v.options?.concurrencyKey,\n labels: v.options?.labels,\n })),\n )\n\n // Emit run:trigger events for all created runs\n for (let i = 0; i < runs.length; i++) {\n eventEmitter.emit({\n type: 'run:trigger',\n runId: runs[i].id,\n jobName: jobDef.name,\n input: validated[i].input,\n labels: runs[i].labels,\n })\n }\n\n return runs as TypedRun<TOutput, TLabels>[]\n },\n\n async getRun(id: string): Promise<TypedRun<TOutput, TLabels> | null> {\n const run = await storage.getRun(id)\n if (!run || run.jobName !== jobDef.name) {\n return null\n }\n return run as TypedRun<TOutput, TLabels>\n },\n\n async getRuns(\n filter?: Omit<RunFilter<TLabels>, 'jobName'>,\n ): Promise<TypedRun<TOutput, TLabels>[]> {\n const runs = await storage.getRuns({\n ...filter,\n jobName: jobDef.name,\n })\n return runs as TypedRun<TOutput, TLabels>[]\n },\n }\n\n // Register the job with the handle\n registry.set({\n name: jobDef.name,\n inputSchema,\n outputSchema,\n labelsSchema,\n fn: jobDef.run as JobFunction<unknown, unknown>,\n jobDef: jobDef as JobDefinition<string, TInput, TOutput>,\n handle,\n })\n\n return handle\n}\n","import { type Kysely, sql } from 'kysely'\nimport type { Database } from './schema'\n\n/**\n * Migration definitions\n */\ninterface Migration {\n version: number\n up: (db: Kysely<Database>) => Promise<void>\n}\n\nexport const LATEST_SCHEMA_VERSION = 1\n\nconst migrations: Migration[] = [\n {\n version: 1,\n up: async (db) => {\n // Create runs table\n await db.schema\n .createTable('durably_runs')\n .ifNotExists()\n .addColumn('id', 'text', (col) => col.primaryKey())\n .addColumn('job_name', 'text', (col) => col.notNull())\n .addColumn('input', 'text', (col) => col.notNull())\n .addColumn('status', 'text', (col) => col.notNull())\n .addColumn('idempotency_key', 'text')\n .addColumn('concurrency_key', 'text')\n .addColumn('labels', 'text', (col) => col.notNull().defaultTo('{}'))\n .addColumn('current_step_index', 'integer', (col) =>\n col.notNull().defaultTo(0),\n )\n .addColumn('progress', 'text')\n .addColumn('output', 'text')\n .addColumn('error', 'text')\n .addColumn('lease_owner', 'text')\n .addColumn('lease_expires_at', 'text')\n .addColumn('lease_generation', 'integer', (col) =>\n col.notNull().defaultTo(0),\n )\n .addColumn('started_at', 'text')\n .addColumn('completed_at', 'text')\n .addColumn('created_at', 'text', (col) => col.notNull())\n .addColumn('updated_at', 'text', (col) => col.notNull())\n .execute()\n\n // Create runs indexes\n await db.schema\n .createIndex('idx_durably_runs_job_idempotency')\n .ifNotExists()\n .on('durably_runs')\n .columns(['job_name', 'idempotency_key'])\n .unique()\n .execute()\n\n await db.schema\n .createIndex('idx_durably_runs_status_concurrency')\n .ifNotExists()\n .on('durably_runs')\n .columns(['status', 'concurrency_key'])\n .execute()\n\n await db.schema\n .createIndex('idx_durably_runs_status_created')\n .ifNotExists()\n .on('durably_runs')\n .columns(['status', 'created_at'])\n .execute()\n\n await db.schema\n .createIndex('idx_durably_runs_status_lease_expires')\n .ifNotExists()\n .on('durably_runs')\n .columns(['status', 'lease_expires_at'])\n .execute()\n\n await db.schema\n .createIndex('idx_durably_runs_job_created')\n .ifNotExists()\n .on('durably_runs')\n .columns(['job_name', 'created_at'])\n .execute()\n\n await db.schema\n .createIndex('idx_durably_runs_status_completed')\n .ifNotExists()\n .on('durably_runs')\n .columns(['status', 'completed_at'])\n .execute()\n\n // Create normalized labels table for indexed label filtering\n await db.schema\n .createTable('durably_run_labels')\n .ifNotExists()\n .addColumn('run_id', 'text', (col) => col.notNull())\n .addColumn('key', 'text', (col) => col.notNull())\n .addColumn('value', 'text', (col) => col.notNull())\n .execute()\n\n await db.schema\n .createIndex('idx_durably_run_labels_pk')\n .ifNotExists()\n .on('durably_run_labels')\n .columns(['run_id', 'key'])\n .unique()\n .execute()\n\n await db.schema\n .createIndex('idx_durably_run_labels_key_value')\n .ifNotExists()\n .on('durably_run_labels')\n .columns(['key', 'value'])\n .execute()\n\n // Create steps table\n await db.schema\n .createTable('durably_steps')\n .ifNotExists()\n .addColumn('id', 'text', (col) => col.primaryKey())\n .addColumn('run_id', 'text', (col) => col.notNull())\n .addColumn('name', 'text', (col) => col.notNull())\n .addColumn('index', 'integer', (col) => col.notNull())\n .addColumn('status', 'text', (col) => col.notNull())\n .addColumn('output', 'text')\n .addColumn('error', 'text')\n .addColumn('started_at', 'text', (col) => col.notNull())\n .addColumn('completed_at', 'text')\n .execute()\n\n // Create steps indexes\n await db.schema\n .createIndex('idx_durably_steps_run_index')\n .ifNotExists()\n .on('durably_steps')\n .columns(['run_id', 'index'])\n .execute()\n\n // Partial unique index: completed steps must be unique per (run_id, name).\n // This guarantees deterministic replay — getCompletedStep(runId, name)\n // returns at most one row. Failed/cancelled steps are not constrained\n // so retries within the same run can re-execute a previously failed step.\n await sql`\n CREATE UNIQUE INDEX IF NOT EXISTS idx_durably_steps_completed_unique\n ON durably_steps(run_id, name) WHERE status = 'completed'\n `.execute(db)\n\n // Create logs table\n await db.schema\n .createTable('durably_logs')\n .ifNotExists()\n .addColumn('id', 'text', (col) => col.primaryKey())\n .addColumn('run_id', 'text', (col) => col.notNull())\n .addColumn('step_name', 'text')\n .addColumn('level', 'text', (col) => col.notNull())\n .addColumn('message', 'text', (col) => col.notNull())\n .addColumn('data', 'text')\n .addColumn('created_at', 'text', (col) => col.notNull())\n .execute()\n\n // Create logs index\n await db.schema\n .createIndex('idx_durably_logs_run_created')\n .ifNotExists()\n .on('durably_logs')\n .columns(['run_id', 'created_at'])\n .execute()\n\n // Create schema_versions table\n await db.schema\n .createTable('durably_schema_versions')\n .ifNotExists()\n .addColumn('version', 'integer', (col) => col.primaryKey())\n .addColumn('applied_at', 'text', (col) => col.notNull())\n .execute()\n },\n },\n]\n\n/**\n * Get the current schema version from the database\n */\nasync function getCurrentVersion(db: Kysely<Database>): Promise<number> {\n try {\n const result = await db\n .selectFrom('durably_schema_versions')\n .select('version')\n .orderBy('version', 'desc')\n .limit(1)\n .executeTakeFirst()\n\n return result?.version ?? 0\n } catch {\n // Table doesn't exist yet\n return 0\n }\n}\n\n/**\n * Run pending migrations\n */\nexport async function runMigrations(db: Kysely<Database>): Promise<void> {\n const currentVersion = await getCurrentVersion(db)\n\n for (const migration of migrations) {\n if (migration.version > currentVersion) {\n await db.transaction().execute(async (trx) => {\n await migration.up(trx)\n\n await trx\n .insertInto('durably_schema_versions')\n .values({\n version: migration.version,\n applied_at: new Date().toISOString(),\n })\n .execute()\n })\n }\n }\n}\n","import { type Kysely, type SqlBool, sql } from 'kysely'\nimport { monotonicFactory } from 'ulidx'\nimport type { Database } from './schema'\n\nconst ulid = monotonicFactory()\n\nexport type RunStatus =\n | 'pending'\n | 'leased'\n | 'completed'\n | 'failed'\n | 'cancelled'\n\n/** Run statuses that represent terminal (non-active) states */\nconst TERMINAL_STATUSES: RunStatus[] = ['completed', 'failed', 'cancelled']\n\n/**\n * Run data for creating a new run\n */\nexport interface CreateRunInput<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n jobName: string\n input: unknown\n idempotencyKey?: string\n concurrencyKey?: string\n labels?: TLabels\n}\n\n/**\n * Run data returned from storage\n */\nexport interface Run<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n id: string\n jobName: string\n input: unknown\n status: RunStatus\n idempotencyKey: string | null\n concurrencyKey: string | null\n currentStepIndex: number\n stepCount: number\n progress: { current: number; total?: number; message?: string } | null\n output: unknown | null\n error: string | null\n labels: TLabels\n leaseOwner: string | null\n leaseExpiresAt: string | null\n leaseGeneration: number\n startedAt: string | null\n completedAt: string | null\n createdAt: string\n updatedAt: string\n}\n\n/**\n * Run filter options\n */\nexport interface RunFilter<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n status?: RunStatus\n /** Filter by job name(s). Pass a string for one, or an array for multiple (OR). */\n jobName?: string | string[]\n /** Filter by labels (all specified labels must match) */\n labels?: { [K in keyof TLabels]?: TLabels[K] }\n /** Maximum number of runs to return */\n limit?: number\n /** Number of runs to skip (for pagination) */\n offset?: number\n}\n\n/**\n * Step data for persisting a step checkpoint\n */\nexport interface CreateStepInput {\n name: string\n index: number\n status: 'completed' | 'failed' | 'cancelled'\n output?: unknown\n error?: string\n startedAt: string // ISO8601 timestamp when step execution started\n}\n\n/**\n * Step data returned from storage\n */\nexport interface Step {\n id: string\n runId: string\n name: string\n index: number\n status: 'completed' | 'failed' | 'cancelled'\n output: unknown | null\n error: string | null\n startedAt: string\n completedAt: string | null\n}\n\n/**\n * Log data for creating a new log\n */\nexport interface CreateLogInput {\n runId: string\n stepName: string | null\n level: 'info' | 'warn' | 'error'\n message: string\n data?: unknown\n}\n\n/**\n * Log data returned from storage\n */\nexport interface Log {\n id: string\n runId: string\n stepName: string | null\n level: 'info' | 'warn' | 'error'\n message: string\n data: unknown | null\n createdAt: string\n}\n\nexport interface ProgressData {\n current: number\n total?: number\n message?: string\n}\n\nexport type DatabaseBackend = 'generic' | 'postgres'\n\n/**\n * Data for updating a run\n */\nexport interface UpdateRunData {\n status?: RunStatus\n currentStepIndex?: number\n progress?: ProgressData | null\n output?: unknown\n error?: string | null\n leaseOwner?: string | null\n leaseExpiresAt?: string | null\n startedAt?: string\n completedAt?: string\n}\n\n/**\n * Unified storage interface used by the runtime.\n */\nexport interface Store<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n // Run lifecycle\n enqueue(input: CreateRunInput<TLabels>): Promise<Run<TLabels>>\n enqueueMany(inputs: CreateRunInput<TLabels>[]): Promise<Run<TLabels>[]>\n getRun<T extends Run<TLabels> = Run<TLabels>>(\n runId: string,\n ): Promise<T | null>\n getRuns<T extends Run<TLabels> = Run<TLabels>>(\n filter?: RunFilter<TLabels>,\n ): Promise<T[]>\n updateRun(runId: string, data: UpdateRunData): Promise<void>\n deleteRun(runId: string): Promise<void>\n\n // Lease management (all lease-holder writes guarded by leaseGeneration)\n claimNext(\n workerId: string,\n now: string,\n leaseMs: number,\n ): Promise<Run<TLabels> | null>\n renewLease(\n runId: string,\n leaseGeneration: number,\n now: string,\n leaseMs: number,\n ): Promise<boolean>\n releaseExpiredLeases(now: string): Promise<number>\n completeRun(\n runId: string,\n leaseGeneration: number,\n output: unknown,\n completedAt: string,\n ): Promise<boolean>\n failRun(\n runId: string,\n leaseGeneration: number,\n error: string,\n completedAt: string,\n ): Promise<boolean>\n cancelRun(runId: string, now: string): Promise<boolean>\n\n // Steps (checkpoints)\n /**\n * Atomically persist a step checkpoint, guarded by lease generation.\n * Inserts the step record and advances currentStepIndex (for completed\n * steps only) in a single transaction. Returns null if the generation\n * does not match (lease was lost).\n */\n persistStep(\n runId: string,\n leaseGeneration: number,\n input: CreateStepInput,\n ): Promise<Step | null>\n getSteps(runId: string): Promise<Step[]>\n getCompletedStep(runId: string, name: string): Promise<Step | null>\n deleteSteps(runId: string): Promise<void>\n\n // Progress\n updateProgress(\n runId: string,\n leaseGeneration: number,\n progress: ProgressData | null,\n ): Promise<void>\n\n // Purge\n purgeRuns(options: { olderThan: string; limit?: number }): Promise<number>\n\n // Logs\n createLog(input: CreateLogInput): Promise<Log>\n getLogs(runId: string): Promise<Log[]>\n}\n\n/**\n * A client-safe subset of Run, excluding internal fields like\n * leaseOwner, leaseExpiresAt, idempotencyKey, concurrencyKey, and updatedAt.\n */\nexport type ClientRun<\n TLabels extends Record<string, string> = Record<string, string>,\n> = Omit<\n Run<TLabels>,\n | 'idempotencyKey'\n | 'concurrencyKey'\n | 'leaseOwner'\n | 'leaseExpiresAt'\n | 'leaseGeneration'\n | 'updatedAt'\n>\n\n/**\n * Project a full Run to a ClientRun by stripping internal fields.\n */\nexport function toClientRun<\n TLabels extends Record<string, string> = Record<string, string>,\n>(run: Run<TLabels>): ClientRun<TLabels> {\n const {\n idempotencyKey,\n concurrencyKey,\n leaseOwner,\n leaseExpiresAt,\n leaseGeneration,\n updatedAt,\n ...clientRun\n } = run\n return clientRun\n}\n\n/**\n * Validate label keys: alphanumeric, dash, underscore, dot, slash only\n */\nconst LABEL_KEY_PATTERN = /^[a-zA-Z0-9\\-_./]+$/\n\nfunction validateLabels(labels: Record<string, string> | undefined): void {\n if (!labels) return\n for (const key of Object.keys(labels)) {\n if (!LABEL_KEY_PATTERN.test(key)) {\n throw new Error(\n `Invalid label key \"${key}\": must contain only alphanumeric characters, dashes, underscores, dots, and slashes`,\n )\n }\n }\n}\n\nfunction rowToRun(\n row: Database['durably_runs'] & { step_count?: number | bigint | null },\n): Run {\n return {\n id: row.id,\n jobName: row.job_name,\n input: JSON.parse(row.input),\n status: row.status,\n idempotencyKey: row.idempotency_key,\n concurrencyKey: row.concurrency_key,\n currentStepIndex: row.current_step_index,\n stepCount: Number(row.step_count ?? 0),\n progress: row.progress ? JSON.parse(row.progress) : null,\n output: row.output ? JSON.parse(row.output) : null,\n error: row.error,\n labels: JSON.parse(row.labels),\n leaseOwner: row.lease_owner,\n leaseExpiresAt: row.lease_expires_at,\n leaseGeneration: row.lease_generation,\n startedAt: row.started_at,\n completedAt: row.completed_at,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n\n/**\n * Convert database row to Step object\n */\nfunction rowToStep(row: Database['durably_steps']): Step {\n return {\n id: row.id,\n runId: row.run_id,\n name: row.name,\n index: row.index,\n status: row.status,\n output: row.output ? JSON.parse(row.output) : null,\n error: row.error,\n startedAt: row.started_at,\n completedAt: row.completed_at,\n }\n}\n\n/**\n * Convert database row to Log object\n */\nfunction rowToLog(row: Database['durably_logs']): Log {\n return {\n id: row.id,\n runId: row.run_id,\n stepName: row.step_name,\n level: row.level,\n message: row.message,\n data: row.data ? JSON.parse(row.data) : null,\n createdAt: row.created_at,\n }\n}\n\n/**\n * Simple async mutex for serializing write operations.\n * Prevents SQLITE_BUSY errors with libsql, which opens separate\n * connections for transactions causing write/write conflicts.\n */\nfunction createWriteMutex() {\n let queue: Promise<void> = Promise.resolve()\n\n return async function withWriteLock<T>(fn: () => Promise<T>): Promise<T> {\n let release: () => void\n const next = new Promise<void>((resolve) => {\n release = resolve\n })\n const prev = queue\n queue = next\n await prev\n try {\n return await fn()\n } finally {\n release!()\n }\n }\n}\n\n/**\n * Create a Kysely-based Store implementation\n */\nexport function createKyselyStore(\n db: Kysely<Database>,\n backend: DatabaseBackend = 'generic',\n): Store<Record<string, string>> {\n const withWriteLock = createWriteMutex()\n\n /** Delete runs and all associated data (steps, logs, labels) in dependency order */\n async function cascadeDeleteRuns(\n trx: Kysely<Database>,\n ids: string[],\n ): Promise<void> {\n if (ids.length === 0) return\n await trx.deleteFrom('durably_steps').where('run_id', 'in', ids).execute()\n await trx.deleteFrom('durably_logs').where('run_id', 'in', ids).execute()\n await trx\n .deleteFrom('durably_run_labels')\n .where('run_id', 'in', ids)\n .execute()\n await trx.deleteFrom('durably_runs').where('id', 'in', ids).execute()\n }\n\n async function insertLabelRows(\n executor: Kysely<Database>,\n runId: string,\n labels: Record<string, string> | undefined,\n ): Promise<void> {\n const entries = Object.entries(labels ?? {})\n if (entries.length > 0) {\n await executor\n .insertInto('durably_run_labels')\n .values(entries.map(([key, value]) => ({ run_id: runId, key, value })))\n .execute()\n }\n }\n\n async function terminateRun(\n runId: string,\n leaseGeneration: number,\n completedAt: string,\n fields: {\n status: 'completed' | 'failed'\n output?: string\n error?: string | null\n },\n ): Promise<boolean> {\n const result = await db\n .updateTable('durably_runs')\n .set({\n ...fields,\n lease_owner: null,\n lease_expires_at: null,\n completed_at: completedAt,\n updated_at: completedAt,\n })\n .where('id', '=', runId)\n .where('status', '=', 'leased')\n .where('lease_generation', '=', leaseGeneration)\n .executeTakeFirst()\n\n return Number(result.numUpdatedRows) > 0\n }\n\n const store: Store<Record<string, string>> = {\n async enqueue(input: CreateRunInput): Promise<Run> {\n const now = new Date().toISOString()\n\n // Check for existing run with same idempotency key\n if (input.idempotencyKey) {\n const existing = await db\n .selectFrom('durably_runs')\n .selectAll()\n .where('job_name', '=', input.jobName)\n .where('idempotency_key', '=', input.idempotencyKey)\n .executeTakeFirst()\n\n if (existing) {\n return rowToRun(existing)\n }\n }\n\n validateLabels(input.labels)\n\n const id = ulid()\n const run: Database['durably_runs'] = {\n id,\n job_name: input.jobName,\n input: JSON.stringify(input.input),\n status: 'pending',\n idempotency_key: input.idempotencyKey ?? null,\n concurrency_key: input.concurrencyKey ?? null,\n current_step_index: 0,\n progress: null,\n output: null,\n error: null,\n labels: JSON.stringify(input.labels ?? {}),\n lease_owner: null,\n lease_expires_at: null,\n lease_generation: 0,\n started_at: null,\n completed_at: null,\n created_at: now,\n updated_at: now,\n }\n\n // Use transaction to ensure run + label rows are atomic\n await db.transaction().execute(async (trx) => {\n await trx.insertInto('durably_runs').values(run).execute()\n await insertLabelRows(trx, id, input.labels)\n })\n\n return rowToRun(run)\n },\n\n async enqueueMany(inputs: CreateRunInput[]): Promise<Run[]> {\n if (inputs.length === 0) {\n return []\n }\n // Use transaction to ensure atomicity of idempotency checks and inserts\n return await db.transaction().execute(async (trx) => {\n const now = new Date().toISOString()\n const runs: Database['durably_runs'][] = []\n\n // Validate all labels upfront\n for (const input of inputs) {\n validateLabels(input.labels)\n }\n\n // Process inputs - check idempotency keys and create run objects\n const allLabelRows: Array<{\n run_id: string\n key: string\n value: string\n }> = []\n for (const input of inputs) {\n // Check for existing run with same idempotency key\n if (input.idempotencyKey) {\n const existing = await trx\n .selectFrom('durably_runs')\n .selectAll()\n .where('job_name', '=', input.jobName)\n .where('idempotency_key', '=', input.idempotencyKey)\n .executeTakeFirst()\n\n if (existing) {\n runs.push(existing)\n continue\n }\n }\n\n const id = ulid()\n if (input.labels) {\n for (const [key, value] of Object.entries(input.labels)) {\n allLabelRows.push({ run_id: id, key, value })\n }\n }\n runs.push({\n id,\n job_name: input.jobName,\n input: JSON.stringify(input.input),\n status: 'pending',\n idempotency_key: input.idempotencyKey ?? null,\n concurrency_key: input.concurrencyKey ?? null,\n current_step_index: 0,\n progress: null,\n output: null,\n error: null,\n labels: JSON.stringify(input.labels ?? {}),\n lease_owner: null,\n lease_expires_at: null,\n lease_generation: 0,\n started_at: null,\n completed_at: null,\n created_at: now,\n updated_at: now,\n })\n }\n\n // Insert all new runs in a single batch\n const newRuns = runs.filter((r) => r.created_at === now)\n if (newRuns.length > 0) {\n await trx.insertInto('durably_runs').values(newRuns).execute()\n\n // Insert normalized labels for indexed filtering (single batch)\n if (allLabelRows.length > 0) {\n await trx\n .insertInto('durably_run_labels')\n .values(allLabelRows)\n .execute()\n }\n }\n\n return runs.map(rowToRun)\n })\n },\n\n async getRun<T extends Run = Run>(runId: string): Promise<T | null> {\n const row = await db\n .selectFrom('durably_runs')\n .leftJoin('durably_steps', 'durably_runs.id', 'durably_steps.run_id')\n .selectAll('durably_runs')\n .select((eb) =>\n eb.fn.count<number>('durably_steps.id').as('step_count'),\n )\n .where('durably_runs.id', '=', runId)\n .groupBy('durably_runs.id')\n .executeTakeFirst()\n\n return row ? (rowToRun(row) as T) : null\n },\n\n async getRuns<T extends Run = Run>(filter?: RunFilter): Promise<T[]> {\n let query = db\n .selectFrom('durably_runs')\n .leftJoin('durably_steps', 'durably_runs.id', 'durably_steps.run_id')\n .selectAll('durably_runs')\n .select((eb) =>\n eb.fn.count<number>('durably_steps.id').as('step_count'),\n )\n .groupBy('durably_runs.id')\n\n if (filter?.status) {\n query = query.where('durably_runs.status', '=', filter.status)\n }\n if (filter?.jobName) {\n if (Array.isArray(filter.jobName)) {\n if (filter.jobName.length > 0) {\n query = query.where('durably_runs.job_name', 'in', filter.jobName)\n }\n } else {\n query = query.where('durably_runs.job_name', '=', filter.jobName)\n }\n }\n if (filter?.labels) {\n const labels = filter.labels as Record<string, string>\n validateLabels(labels)\n for (const [key, value] of Object.entries(labels)) {\n if (value === undefined) continue\n // Use indexed label table with JSON fallback for atomicity safety:\n // if label rows haven't been written yet, fall back to JSON column\n const jsonFallback =\n backend === 'postgres'\n ? sql<SqlBool>`durably_runs.labels ->> ${key} = ${value}`\n : sql<SqlBool>`json_extract(durably_runs.labels, ${`$.${key}`}) = ${value}`\n query = query.where((eb) =>\n eb.or([\n eb.exists(\n eb\n .selectFrom('durably_run_labels')\n .select(sql.lit(1).as('one'))\n .whereRef('durably_run_labels.run_id', '=', 'durably_runs.id')\n .where('durably_run_labels.key', '=', key)\n .where('durably_run_labels.value', '=', value),\n ),\n jsonFallback,\n ]),\n )\n }\n }\n\n query = query.orderBy('durably_runs.created_at', 'desc')\n\n if (filter?.limit !== undefined) {\n query = query.limit(filter.limit)\n }\n if (filter?.offset !== undefined) {\n // SQLite requires LIMIT when using OFFSET\n if (filter.limit === undefined) {\n query = query.limit(-1) // -1 means unlimited in SQLite\n }\n query = query.offset(filter.offset)\n }\n\n const rows = await query.execute()\n return rows.map(rowToRun) as T[]\n },\n\n async updateRun(runId, data) {\n const now = new Date().toISOString()\n const status = data.status\n\n await db\n .updateTable('durably_runs')\n .set({\n status,\n current_step_index: data.currentStepIndex,\n progress:\n data.progress !== undefined\n ? data.progress\n ? JSON.stringify(data.progress)\n : null\n : undefined,\n output:\n data.output !== undefined ? JSON.stringify(data.output) : undefined,\n error: data.error,\n lease_owner:\n data.leaseOwner !== undefined ? data.leaseOwner : undefined,\n lease_expires_at:\n data.leaseExpiresAt !== undefined ? data.leaseExpiresAt : undefined,\n started_at: data.startedAt,\n completed_at: data.completedAt,\n updated_at: now,\n })\n .where('id', '=', runId)\n .execute()\n },\n\n async deleteRun(runId: string) {\n await db.transaction().execute(async (trx) => {\n await cascadeDeleteRuns(trx, [runId])\n })\n },\n\n async purgeRuns(options: {\n olderThan: string\n limit?: number\n }): Promise<number> {\n const limit = options.limit ?? 500\n\n return await db.transaction().execute(async (trx) => {\n const rows = await trx\n .selectFrom('durably_runs')\n .select('id')\n .where('status', 'in', TERMINAL_STATUSES)\n .where('completed_at', '<', options.olderThan)\n .orderBy('completed_at', 'asc')\n .limit(limit)\n .execute()\n\n if (rows.length === 0) return 0\n\n const ids = rows.map((r) => r.id)\n await cascadeDeleteRuns(trx, ids)\n return ids.length\n })\n },\n\n async claimNext(\n workerId: string,\n now: string,\n leaseMs: number,\n ): Promise<Run | null> {\n const leaseExpiresAt = new Date(Date.parse(now) + leaseMs).toISOString()\n const activeLeaseGuard = sql<boolean>`\n (\n concurrency_key IS NULL\n OR NOT EXISTS (\n SELECT 1\n FROM durably_runs AS active\n WHERE active.concurrency_key = durably_runs.concurrency_key\n AND active.id <> durably_runs.id\n AND active.status = 'leased'\n AND active.lease_expires_at IS NOT NULL\n AND active.lease_expires_at > ${now}\n )\n )\n `\n\n if (backend === 'postgres') {\n return await db.transaction().execute(async (trx) => {\n const skipKeys: string[] = []\n\n // Loop: on concurrency-key conflict, exclude that key and retry\n // to find the next eligible candidate in the same transaction.\n for (;;) {\n const concurrencyCondition =\n skipKeys.length > 0\n ? sql`\n AND (\n concurrency_key IS NULL\n OR concurrency_key NOT IN (${sql.join(skipKeys)})\n )\n `\n : sql``\n\n // Step 1: Find and lock a candidate row\n const candidateResult = await sql<{\n id: string\n concurrency_key: string | null\n }>`\n SELECT id, concurrency_key\n FROM durably_runs\n WHERE\n (\n status = 'pending'\n OR (status = 'leased' AND lease_expires_at IS NOT NULL AND lease_expires_at <= ${now})\n )\n AND ${activeLeaseGuard}\n ${concurrencyCondition}\n ORDER BY created_at ASC, id ASC\n FOR UPDATE SKIP LOCKED\n LIMIT 1\n `.execute(trx)\n\n const candidate = candidateResult.rows[0]\n if (!candidate) return null\n\n // Step 2: If the candidate has a concurrency key, serialize via\n // advisory lock and re-verify with a fresh snapshot (READ COMMITTED\n // gives each statement its own snapshot).\n if (candidate.concurrency_key) {\n await sql`SELECT pg_advisory_xact_lock(hashtext(${candidate.concurrency_key}))`.execute(\n trx,\n )\n\n const conflict = await sql`\n SELECT 1 FROM durably_runs\n WHERE concurrency_key = ${candidate.concurrency_key}\n AND id <> ${candidate.id}\n AND status = 'leased'\n AND lease_expires_at IS NOT NULL\n AND lease_expires_at > ${now}\n LIMIT 1\n `.execute(trx)\n\n if (conflict.rows.length > 0) {\n // Key is occupied — exclude it and try the next candidate\n skipKeys.push(candidate.concurrency_key)\n continue\n }\n }\n\n // Step 3: Claim the candidate (increment lease_generation)\n const result = await sql<Database['durably_runs']>`\n UPDATE durably_runs\n SET\n status = 'leased',\n lease_owner = ${workerId},\n lease_expires_at = ${leaseExpiresAt},\n lease_generation = lease_generation + 1,\n started_at = COALESCE(started_at, ${now}),\n updated_at = ${now}\n WHERE id = ${candidate.id}\n RETURNING *\n `.execute(trx)\n\n const row = result.rows[0]\n if (!row) return null\n return rowToRun({ ...row, step_count: 0 })\n }\n })\n }\n\n let subquery = db\n .selectFrom('durably_runs')\n .select('durably_runs.id')\n .where((eb) =>\n eb.or([\n eb('status', '=', 'pending'),\n eb.and([\n eb('status', '=', 'leased'),\n eb('lease_expires_at', 'is not', null),\n eb('lease_expires_at', '<=', now),\n ]),\n ]),\n )\n .where(activeLeaseGuard)\n .orderBy('created_at', 'asc')\n .orderBy('id', 'asc')\n .limit(1)\n\n const row = await db\n .updateTable('durably_runs')\n .set({\n status: 'leased',\n lease_owner: workerId,\n lease_expires_at: leaseExpiresAt,\n lease_generation: sql`lease_generation + 1`,\n started_at: sql`COALESCE(started_at, ${now})`,\n updated_at: now,\n })\n .where('id', '=', (eb) =>\n eb.selectFrom(subquery.as('sub')).select('id'),\n )\n .returningAll()\n .executeTakeFirst()\n\n if (!row) return null\n return rowToRun({ ...row, step_count: 0 })\n },\n\n async renewLease(\n runId: string,\n leaseGeneration: number,\n now: string,\n leaseMs: number,\n ): Promise<boolean> {\n const leaseExpiresAt = new Date(Date.parse(now) + leaseMs).toISOString()\n const result = await db\n .updateTable('durably_runs')\n .set({\n lease_expires_at: leaseExpiresAt,\n updated_at: now,\n })\n .where('id', '=', runId)\n .where('status', '=', 'leased')\n .where('lease_generation', '=', leaseGeneration)\n .where('lease_expires_at', '>', now)\n .executeTakeFirst()\n\n return Number(result.numUpdatedRows) > 0\n },\n\n async releaseExpiredLeases(now: string): Promise<number> {\n const result = await db\n .updateTable('durably_runs')\n .set({\n status: 'pending',\n lease_owner: null,\n lease_expires_at: null,\n updated_at: now,\n })\n .where('status', '=', 'leased')\n .where('lease_expires_at', 'is not', null)\n .where('lease_expires_at', '<=', now)\n .executeTakeFirst()\n\n return Number(result.numUpdatedRows)\n },\n\n async completeRun(\n runId: string,\n leaseGeneration: number,\n output: unknown,\n completedAt: string,\n ): Promise<boolean> {\n return terminateRun(runId, leaseGeneration, completedAt, {\n status: 'completed',\n output: JSON.stringify(output),\n error: null,\n })\n },\n\n async failRun(\n runId: string,\n leaseGeneration: number,\n error: string,\n completedAt: string,\n ): Promise<boolean> {\n return terminateRun(runId, leaseGeneration, completedAt, {\n status: 'failed',\n error,\n })\n },\n\n async cancelRun(runId: string, now: string): Promise<boolean> {\n const result = await db\n .updateTable('durably_runs')\n .set({\n status: 'cancelled',\n lease_owner: null,\n lease_expires_at: null,\n completed_at: now,\n updated_at: now,\n })\n .where('id', '=', runId)\n .where('status', 'in', ['pending', 'leased'])\n .executeTakeFirst()\n\n return Number(result.numUpdatedRows) > 0\n },\n\n async persistStep(\n runId: string,\n leaseGeneration: number,\n input: CreateStepInput,\n ): Promise<Step | null> {\n const completedAt = new Date().toISOString()\n const id = ulid()\n const outputJson =\n input.output !== undefined ? JSON.stringify(input.output) : null\n const errorValue = input.error ?? null\n\n return await db.transaction().execute(async (trx) => {\n // Atomic INSERT...SELECT: the step is only inserted if the\n // lease generation matches. Single statement, no TOCTOU.\n const insertResult = await sql`\n INSERT INTO durably_steps (id, run_id, name, \"index\", status, output, error, started_at, completed_at)\n SELECT ${id}, ${runId}, ${input.name}, ${input.index}, ${input.status},\n ${outputJson}, ${errorValue}, ${input.startedAt}, ${completedAt}\n FROM durably_runs\n WHERE id = ${runId} AND lease_generation = ${leaseGeneration}\n `.execute(trx)\n\n if (Number(insertResult.numAffectedRows) === 0) return null\n\n // Advance step index for completed steps only\n if (input.status === 'completed') {\n await trx\n .updateTable('durably_runs')\n .set({\n current_step_index: input.index + 1,\n updated_at: completedAt,\n })\n .where('id', '=', runId)\n .where('lease_generation', '=', leaseGeneration)\n .execute()\n }\n\n return {\n id,\n runId,\n name: input.name,\n index: input.index,\n status: input.status,\n output: input.output !== undefined ? input.output : null,\n error: errorValue,\n startedAt: input.startedAt,\n completedAt,\n } as Step\n })\n },\n\n async deleteSteps(runId: string): Promise<void> {\n await db.deleteFrom('durably_steps').where('run_id', '=', runId).execute()\n await db.deleteFrom('durably_logs').where('run_id', '=', runId).execute()\n },\n\n async getSteps(runId: string): Promise<Step[]> {\n const rows = await db\n .selectFrom('durably_steps')\n .selectAll()\n .where('run_id', '=', runId)\n .orderBy('index', 'asc')\n .execute()\n\n return rows.map(rowToStep)\n },\n\n async getCompletedStep(runId: string, name: string): Promise<Step | null> {\n const row = await db\n .selectFrom('durably_steps')\n .selectAll()\n .where('run_id', '=', runId)\n .where('name', '=', name)\n .where('status', '=', 'completed')\n .executeTakeFirst()\n\n return row ? rowToStep(row) : null\n },\n\n async updateProgress(\n runId: string,\n leaseGeneration: number,\n progress: ProgressData | null,\n ): Promise<void> {\n await db\n .updateTable('durably_runs')\n .set({\n progress: progress ? JSON.stringify(progress) : null,\n updated_at: new Date().toISOString(),\n })\n .where('id', '=', runId)\n .where('lease_generation', '=', leaseGeneration)\n .execute()\n },\n\n async createLog(input: CreateLogInput): Promise<Log> {\n const now = new Date().toISOString()\n const id = ulid()\n\n const log: Database['durably_logs'] = {\n id,\n run_id: input.runId,\n step_name: input.stepName,\n level: input.level,\n message: input.message,\n data: input.data !== undefined ? JSON.stringify(input.data) : null,\n created_at: now,\n }\n\n await db.insertInto('durably_logs').values(log).execute()\n\n return rowToLog(log)\n },\n\n async getLogs(runId: string): Promise<Log[]> {\n const rows = await db\n .selectFrom('durably_logs')\n .selectAll()\n .where('run_id', '=', runId)\n .orderBy('created_at', 'asc')\n .execute()\n\n return rows.map(rowToLog)\n },\n }\n\n // Wrap all mutating methods with write lock to prevent SQLITE_BUSY.\n // libsql opens separate connections for transactions, so concurrent\n // writes from the same Kysely instance can conflict. The mutex\n // serializes writes within a single process. Reads are not locked.\n const mutatingKeys = [\n 'enqueue',\n 'enqueueMany',\n 'updateRun',\n 'deleteRun',\n 'purgeRuns',\n 'claimNext',\n 'renewLease',\n 'releaseExpiredLeases',\n 'completeRun',\n 'failRun',\n 'cancelRun',\n 'persistStep',\n 'deleteSteps',\n 'updateProgress',\n 'createLog',\n ] as const\n\n for (const key of mutatingKeys) {\n const original = store[key] as (...args: unknown[]) => Promise<unknown>\n ;(store as unknown as Record<string, unknown>)[key] = (\n ...args: unknown[]\n ): Promise<unknown> => withWriteLock(() => original.apply(store, args))\n }\n\n return store\n}\n","/**\n * Worker configuration\n */\nexport interface WorkerConfig {\n pollingIntervalMs: number\n}\n\n/**\n * Worker state\n */\nexport interface Worker {\n start(options?: { workerId?: string }): void\n stop(): Promise<void>\n readonly isRunning: boolean\n}\n\n/**\n * Create a thin worker loop around processOne().\n */\nexport function createWorker(\n config: WorkerConfig,\n processOne: (options?: { workerId?: string }) => Promise<boolean>,\n): Worker {\n let running = false\n let pollingTimeout: ReturnType<typeof setTimeout> | null = null\n let inFlight: Promise<void> | null = null\n let stopResolver: (() => void) | null = null\n let activeWorkerId: string | undefined\n\n async function poll(): Promise<void> {\n if (!running) {\n return\n }\n\n try {\n inFlight = processOne({ workerId: activeWorkerId }).then(() => undefined)\n await inFlight\n } finally {\n inFlight = null\n }\n\n if (running) {\n pollingTimeout = setTimeout(() => {\n void poll()\n }, config.pollingIntervalMs)\n return\n }\n\n if (stopResolver) {\n stopResolver()\n stopResolver = null\n }\n }\n\n return {\n get isRunning(): boolean {\n return running\n },\n\n start(options?: { workerId?: string }): void {\n if (running) {\n return\n }\n\n activeWorkerId = options?.workerId\n running = true\n void poll()\n },\n\n async stop(): Promise<void> {\n if (!running) {\n return\n }\n\n running = false\n\n if (pollingTimeout) {\n clearTimeout(pollingTimeout)\n pollingTimeout = null\n }\n\n if (inFlight) {\n return new Promise<void>((resolve) => {\n stopResolver = resolve\n })\n }\n },\n }\n}\n","import type { z } from 'zod'\nimport type { StepContext } from './job'\n\n/**\n * Job run function type\n */\nexport type JobRunFunction<TInput, TOutput> = (\n step: StepContext,\n input: TInput,\n) => Promise<TOutput>\n\n/**\n * Job definition - a standalone description of a job\n * This is the result of calling defineJob() and can be passed to durably.register()\n */\nexport interface JobDefinition<TName extends string, TInput, TOutput> {\n readonly name: TName\n readonly input: z.ZodType<TInput>\n readonly output: z.ZodType<TOutput> | undefined\n readonly run: JobRunFunction<TInput, TOutput>\n}\n\n/**\n * Extract input type from a JobDefinition\n * @example\n * ```ts\n * type Input = JobInput<typeof myJob> // { userId: string }\n * ```\n */\nexport type JobInput<T> =\n T extends JobDefinition<string, infer TInput, unknown> ? TInput : never\n\n/**\n * Extract output type from a JobDefinition\n * @example\n * ```ts\n * type Output = JobOutput<typeof myJob> // { count: number }\n * ```\n */\nexport type JobOutput<T> =\n T extends JobDefinition<string, unknown, infer TOutput> ? TOutput : never\n\n/**\n * Configuration for defining a job\n */\nexport interface DefineJobConfig<\n TName extends string,\n TInputSchema extends z.ZodType,\n TOutputSchema extends z.ZodType | undefined,\n> {\n name: TName\n input: TInputSchema\n output?: TOutputSchema\n run: JobRunFunction<\n z.infer<TInputSchema>,\n TOutputSchema extends z.ZodType ? z.infer<TOutputSchema> : void\n >\n}\n\n/**\n * Define a job - creates a JobDefinition that can be registered with durably.register()\n *\n * @example\n * ```ts\n * import { defineJob } from '@coji/durably'\n * import { z } from 'zod'\n *\n * export const syncUsers = defineJob({\n * name: 'sync-users',\n * input: z.object({ orgId: z.string() }),\n * output: z.object({ syncedCount: z.number() }),\n * run: async (step, input) => {\n * const users = await step.run('fetch-users', () => fetchUsers(input.orgId))\n * return { syncedCount: users.length }\n * },\n * })\n * ```\n */\nexport function defineJob<\n TName extends string,\n TInputSchema extends z.ZodType,\n TOutputSchema extends z.ZodType | undefined = undefined,\n>(\n config: DefineJobConfig<TName, TInputSchema, TOutputSchema>,\n): JobDefinition<\n TName,\n z.infer<TInputSchema>,\n TOutputSchema extends z.ZodType ? z.infer<TOutputSchema> : void\n> {\n return {\n name: config.name,\n input: config.input,\n output: config.output,\n run: config.run,\n } as JobDefinition<\n TName,\n z.infer<TInputSchema>,\n TOutputSchema extends z.ZodType ? z.infer<TOutputSchema> : void\n >\n}\n","/**\n * HTTP response utilities for the Durably HTTP handler.\n * Extracted to eliminate duplication in server.ts handlers.\n */\n\nimport { getErrorMessage } from './errors'\n\nexport { getErrorMessage }\n\n/**\n * JSON response headers\n */\nconst JSON_HEADERS = {\n 'Content-Type': 'application/json',\n} as const\n\n/**\n * Create a JSON response\n */\nexport function jsonResponse(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: JSON_HEADERS,\n })\n}\n\n/**\n * Create an error response with consistent format\n */\nexport function errorResponse(\n message: string,\n status: 400 | 404 | 500 = 500,\n): Response {\n return jsonResponse({ error: message }, status)\n}\n\n/**\n * Create a success response with { success: true }\n */\nexport function successResponse(): Response {\n return jsonResponse({ success: true })\n}\n\n/**\n * Get required query parameter or return error response\n */\nexport function getRequiredQueryParam(\n url: URL,\n paramName: string,\n): string | Response {\n const value = url.searchParams.get(paramName)\n if (!value) {\n return errorResponse(`${paramName} query parameter is required`, 400)\n }\n return value\n}\n","/**\n * SSE (Server-Sent Events) utilities for streaming events to clients.\n * Extracted to eliminate duplication between subscribe and runsSubscribe handlers.\n */\n\nimport type { Unsubscribe } from './events'\n\n/**\n * SSE response headers\n */\nconst SSE_HEADERS = {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n} as const\n\n/**\n * Encode data as SSE format: `data: ${json}\\n\\n`\n */\nfunction formatSSE(data: unknown): string {\n return `data: ${JSON.stringify(data)}\\n\\n`\n}\n\n/**\n * Create a TextEncoder for SSE streams\n */\nfunction createSSEEncoder(): TextEncoder {\n return new TextEncoder()\n}\n\n/**\n * Encode and format data for SSE streaming\n */\nfunction encodeSSE(encoder: TextEncoder, data: unknown): Uint8Array {\n return encoder.encode(formatSSE(data))\n}\n\n/**\n * Create an SSE Response from a ReadableStream\n */\nexport function createSSEResponse(stream: ReadableStream): Response {\n return new Response(stream, {\n status: 200,\n headers: SSE_HEADERS,\n })\n}\n\n/**\n * Transform a ReadableStream of events into an SSE-formatted stream\n */\nexport function createSSEStreamFromReader<T>(\n reader: ReadableStreamDefaultReader<T>,\n): ReadableStream<Uint8Array> {\n const encoder = createSSEEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n controller.close()\n break\n }\n\n controller.enqueue(encodeSSE(encoder, value))\n }\n } catch (error) {\n controller.error(error)\n }\n },\n cancel() {\n reader.releaseLock()\n },\n })\n}\n\n/**\n * Transform a ReadableStream of events into an SSE-formatted stream with\n * throttling for `run:progress` events.\n */\nexport function createThrottledSSEStreamFromReader<T>(\n reader: ReadableStreamDefaultReader<T>,\n throttleMs: number,\n): ReadableStream<Uint8Array> {\n if (throttleMs <= 0) {\n return createSSEStreamFromReader(reader)\n }\n\n const encoder = createSSEEncoder()\n let closed = false\n let throttle: {\n controller: SSEStreamController\n dispose: () => void\n } | null = null\n\n return new ReadableStream({\n async start(controller) {\n const innerCtrl: SSEStreamController = {\n enqueue: (data: unknown) =>\n controller.enqueue(encodeSSE(encoder, data)),\n close: () => {\n closed = true\n controller.close()\n },\n get closed() {\n return closed\n },\n }\n throttle = createThrottledSSEController(innerCtrl, throttleMs)\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n throttle.controller.close()\n break\n }\n throttle.controller.enqueue(value)\n }\n } catch (error) {\n throttle.dispose()\n reader.releaseLock()\n controller.error(error)\n }\n },\n cancel() {\n closed = true\n throttle?.dispose()\n reader.releaseLock()\n },\n })\n}\n\n/**\n * SSE stream controller with cleanup support\n */\nexport interface SSEStreamController {\n enqueue: (data: unknown) => void\n close: () => void\n readonly closed: boolean\n}\n\n/**\n * Create an SSE stream from event subscriptions.\n * Handles the common pattern of subscribing to multiple events and streaming them.\n *\n * @param setup - Function to set up event subscriptions, returns cleanup functions\n * @returns SSE Response\n */\nexport function createSSEStreamFromSubscriptions(\n setup: (controller: SSEStreamController) => Unsubscribe[],\n): ReadableStream<Uint8Array> {\n const encoder = createSSEEncoder()\n let closed = false\n let unsubscribes: Unsubscribe[] = []\n\n return new ReadableStream({\n start(controller) {\n const sseController: SSEStreamController = {\n enqueue: (data: unknown) => {\n if (closed) return\n controller.enqueue(encodeSSE(encoder, data))\n },\n close: () => {\n if (closed) return\n closed = true\n controller.close()\n },\n get closed() {\n return closed\n },\n }\n\n unsubscribes = setup(sseController)\n },\n cancel() {\n closed = true\n for (const unsubscribe of unsubscribes) {\n unsubscribe()\n }\n },\n })\n}\n\nconst TERMINAL_EVENT_TYPES = new Set([\n 'run:complete',\n 'run:fail',\n 'run:cancel',\n 'run:delete',\n])\n\n/**\n * Create an SSE stream controller that throttles `run:progress` events.\n *\n * - First progress event per run is delivered immediately\n * - Subsequent events within the throttle window are coalesced (latest wins)\n * - A trailing flush ensures the last progress is always delivered\n * - Non-progress events pass through immediately\n */\nexport function createThrottledSSEController(\n inner: SSEStreamController,\n throttleMs: number,\n): { controller: SSEStreamController; dispose: () => void } {\n if (throttleMs <= 0) {\n return { controller: inner, dispose: () => {} }\n }\n\n // Per-run throttle state\n const pending = new Map<\n string,\n { data: unknown; timer: ReturnType<typeof setTimeout> }\n >()\n\n // Track last send time per run for leading-edge delivery\n const lastSent = new Map<string, number>()\n\n const controller: SSEStreamController = {\n enqueue(data: unknown) {\n if (inner.closed) return\n\n const event =\n typeof data === 'object' && data !== null\n ? (data as { type?: string; runId?: string })\n : null\n\n // Flush and clean up throttle state for terminal run events\n if (event?.runId && TERMINAL_EVENT_TYPES.has(event.type ?? '')) {\n lastSent.delete(event.runId)\n const entry = pending.get(event.runId)\n if (entry) {\n clearTimeout(entry.timer)\n if (!inner.closed) inner.enqueue(entry.data)\n pending.delete(event.runId)\n }\n }\n\n if (event?.type !== 'run:progress' || !event?.runId) {\n inner.enqueue(data)\n return\n }\n\n const runId = event.runId\n const now = Date.now()\n const last = lastSent.get(runId) ?? 0\n\n // Leading edge: send immediately if enough time has passed\n if (now - last >= throttleMs) {\n lastSent.set(runId, now)\n // Clear any pending flush for this run\n const entry = pending.get(runId)\n if (entry) {\n clearTimeout(entry.timer)\n pending.delete(runId)\n }\n inner.enqueue(data)\n return\n }\n\n // Trailing edge: buffer latest and schedule flush\n const existing = pending.get(runId)\n if (existing) {\n clearTimeout(existing.timer)\n }\n\n const delay = Math.max(0, throttleMs - (now - last))\n const timer = setTimeout(() => {\n const current = pending.get(runId)\n if (!current || current.timer !== timer) return\n\n pending.delete(runId)\n if (!inner.closed) {\n lastSent.set(runId, Date.now())\n inner.enqueue(current.data)\n }\n }, delay)\n\n pending.set(runId, { data, timer })\n },\n close() {\n // Flush all pending progress events before closing\n for (const [, entry] of pending) {\n clearTimeout(entry.timer)\n if (!inner.closed) {\n inner.enqueue(entry.data)\n }\n }\n pending.clear()\n lastSent.clear()\n inner.close()\n },\n get closed() {\n return inner.closed\n },\n }\n\n const dispose = () => {\n for (const [, entry] of pending) {\n clearTimeout(entry.timer)\n }\n pending.clear()\n lastSent.clear()\n }\n\n return { controller, dispose }\n}\n","import type { Durably } from './durably'\nimport type { AnyEventInput } from './events'\nimport {\n errorResponse,\n getErrorMessage,\n getRequiredQueryParam,\n jsonResponse,\n successResponse,\n} from './http'\nimport {\n createSSEResponse,\n createSSEStreamFromSubscriptions,\n createThrottledSSEController,\n createThrottledSSEStreamFromReader,\n type SSEStreamController,\n} from './sse'\nimport type { Run, RunFilter } from './storage'\nimport { toClientRun } from './storage'\n\n/**\n * Run operation types for onRunAccess\n */\nexport type RunOperation =\n | 'read'\n | 'subscribe'\n | 'steps'\n | 'retrigger'\n | 'cancel'\n | 'delete'\n\n/**\n * Subscription filter — only fields that SSE subscriptions actually support.\n */\nexport type RunsSubscribeFilter<\n TLabels extends Record<string, string> = Record<string, string>,\n> = Pick<RunFilter<TLabels>, 'jobName' | 'labels'>\n\n/**\n * Request body for triggering a job\n */\nexport interface TriggerRequest<\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n jobName: string\n input: unknown\n idempotencyKey?: string\n concurrencyKey?: string\n labels?: TLabels\n}\n\n/**\n * Response for trigger endpoint\n */\nexport interface TriggerResponse {\n runId: string\n}\n\n/**\n * Auth middleware configuration.\n * When `auth` is set, `authenticate` is required.\n * TContext is inferred from authenticate's return type.\n * TLabels is inferred from the Durably instance.\n */\nexport interface AuthConfig<\n TContext,\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n /** Authenticate every request. Return context or throw Response to reject. */\n authenticate: (request: Request) => Promise<TContext> | TContext\n\n /** Guard before trigger. Called after body validation and job resolution. */\n onTrigger?: (\n ctx: TContext,\n trigger: TriggerRequest<TLabels>,\n ) => Promise<void> | void\n\n /** Guard before run-level operations. Run is pre-fetched. */\n onRunAccess?: (\n ctx: TContext,\n run: Run<TLabels>,\n info: { operation: RunOperation },\n ) => Promise<void> | void\n\n /** Scope runs list queries (GET /runs). */\n scopeRuns?: (\n ctx: TContext,\n filter: RunFilter<TLabels>,\n ) => RunFilter<TLabels> | Promise<RunFilter<TLabels>>\n\n /** Scope runs subscribe stream (GET /runs/subscribe). Falls back to scopeRuns if not set. */\n scopeRunsSubscribe?: (\n ctx: TContext,\n filter: RunsSubscribeFilter<TLabels>,\n ) => RunsSubscribeFilter<TLabels> | Promise<RunsSubscribeFilter<TLabels>>\n}\n\n/**\n * Handler interface for HTTP endpoints\n */\nexport interface DurablyHandler {\n /**\n * Handle all Durably HTTP requests with automatic routing + auth\n *\n * Routes:\n * - GET {basePath}/subscribe?runId=xxx - SSE stream\n * - GET {basePath}/runs - List runs\n * - GET {basePath}/runs/subscribe - SSE stream of run updates\n * - GET {basePath}/run?runId=xxx - Get single run\n * - GET {basePath}/steps?runId=xxx - Get steps\n * - POST {basePath}/trigger - Trigger a job\n * - POST {basePath}/retrigger?runId=xxx - Create a fresh run from a terminal run\n * - POST {basePath}/cancel?runId=xxx - Cancel a run\n * - DELETE {basePath}/run?runId=xxx - Delete a run\n */\n handle(request: Request, basePath: string): Promise<Response>\n}\n\n/**\n * Options for createDurablyHandler\n */\nexport interface CreateDurablyHandlerOptions<\n TContext = undefined,\n TLabels extends Record<string, string> = Record<string, string>,\n> {\n /**\n * Called before handling each request (after authentication).\n * Use this to initialize Durably (migrate, start worker, etc.)\n */\n onRequest?: () => Promise<void> | void\n\n /**\n * Throttle interval in milliseconds for SSE progress events.\n * @default 100\n */\n sseThrottleMs?: number\n\n /**\n * Auth middleware. When set, authenticate is required and auth applies to ALL endpoints.\n */\n auth?: AuthConfig<TContext, TLabels>\n}\n\n/**\n * Valid status values for runs\n */\nconst VALID_STATUSES = [\n 'pending',\n 'leased',\n 'completed',\n 'failed',\n 'cancelled',\n] as const satisfies readonly RunFilter['status'][]\n\nconst VALID_STATUSES_SET: ReadonlySet<string> = new Set(VALID_STATUSES)\n\n/**\n * Parse label.* query params into a Record<string, string>\n */\nfunction parseLabelsFromParams(\n searchParams: URLSearchParams,\n): Record<string, string> | undefined {\n const labels: Record<string, string> = {}\n for (const [key, value] of searchParams.entries()) {\n if (key.startsWith('label.')) {\n labels[key.slice(6)] = value\n }\n }\n return Object.keys(labels).length > 0 ? labels : undefined\n}\n\n/**\n * Parse and validate RunFilter from query params.\n * Returns the filter or an error Response.\n */\nfunction parseRunFilter(url: URL): RunFilter | Response {\n const jobNames = url.searchParams.getAll('jobName')\n const statusParam = url.searchParams.get('status')\n const limitParam = url.searchParams.get('limit')\n const offsetParam = url.searchParams.get('offset')\n const labels = parseLabelsFromParams(url.searchParams)\n\n // Validate status\n if (statusParam && !VALID_STATUSES_SET.has(statusParam)) {\n return errorResponse(\n `Invalid status: ${statusParam}. Must be one of: ${VALID_STATUSES.join(', ')}`,\n 400,\n )\n }\n\n // Validate limit\n let limit: number | undefined\n if (limitParam) {\n limit = Number.parseInt(limitParam, 10)\n if (Number.isNaN(limit) || limit < 0) {\n return errorResponse('Invalid limit: must be a non-negative integer', 400)\n }\n }\n\n // Validate offset\n let offset: number | undefined\n if (offsetParam) {\n offset = Number.parseInt(offsetParam, 10)\n if (Number.isNaN(offset) || offset < 0) {\n return errorResponse(\n 'Invalid offset: must be a non-negative integer',\n 400,\n )\n }\n }\n\n return {\n jobName: jobNames.length > 0 ? jobNames : undefined,\n status: statusParam as RunFilter['status'],\n labels,\n limit,\n offset,\n }\n}\n\n/**\n * Parse RunsSubscribeFilter from query params.\n */\nfunction parseRunsSubscribeFilter(url: URL): RunsSubscribeFilter {\n const jobNames = url.searchParams.getAll('jobName')\n const labels = parseLabelsFromParams(url.searchParams)\n\n return {\n jobName: jobNames.length > 0 ? jobNames : undefined,\n labels,\n }\n}\n\n/**\n * Check if event labels match filter labels (all filter labels must match)\n */\nfunction matchesLabels(\n eventLabels: Record<string, string>,\n filterLabels: Record<string, string>,\n): boolean {\n for (const [key, value] of Object.entries(filterLabels)) {\n if (eventLabels[key] !== value) return false\n }\n return true\n}\n\n/**\n * Create HTTP handlers for Durably\n * Uses Web Standard Request/Response for framework-agnostic usage\n */\n// biome-ignore lint/suspicious/noExplicitAny: TLabels must be inferred from Durably instance\nexport function createDurablyHandler<\n TContext = undefined,\n TLabels extends Record<string, string> = Record<string, string>,\n>(\n durably: Durably<any, TLabels>,\n options?: CreateDurablyHandlerOptions<TContext, TLabels>,\n): DurablyHandler {\n const throttleMs = options?.sseThrottleMs ?? 100\n const auth = options?.auth\n\n // Validate: auth requires authenticate\n if (auth && !auth.authenticate) {\n throw new Error(\n 'createDurablyHandler: auth.authenticate is required when auth is provided',\n )\n }\n\n // --- Shared helpers ---\n\n /** Wrap handler with try/catch that re-throws Response and catches everything else as 500 */\n async function withErrorHandling(\n fn: () => Promise<Response>,\n ): Promise<Response> {\n try {\n return await fn()\n } catch (error) {\n if (error instanceof Response) throw error\n return errorResponse(getErrorMessage(error), 500)\n }\n }\n\n /** Fetch run, check auth, return run or error Response */\n async function requireRunAccess(\n url: URL,\n ctx: TContext | undefined,\n operation: RunOperation,\n ): Promise<{ run: Run<TLabels>; runId: string } | Response> {\n const runId = getRequiredQueryParam(url, 'runId')\n if (runId instanceof Response) return runId\n\n const run = await durably.getRun(runId)\n if (!run) return errorResponse('Run not found', 404)\n\n if (auth?.onRunAccess && ctx !== undefined) {\n await auth.onRunAccess(ctx as TContext, run as Run<TLabels>, {\n operation,\n })\n }\n\n return { run: run as Run<TLabels>, runId }\n }\n\n // --- Private endpoint handlers (closure-scoped, not exposed on returned object) ---\n\n async function handleTrigger(\n request: Request,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const body = (await request.json()) as TriggerRequest<TLabels>\n\n if (!body.jobName) {\n return errorResponse('jobName is required', 400)\n }\n\n const job = durably.getJob(body.jobName)\n if (!job) {\n return errorResponse(`Job not found: ${body.jobName}`, 404)\n }\n\n // Auth hook: onTrigger (after validation)\n if (auth?.onTrigger && ctx !== undefined) {\n await auth.onTrigger(ctx as TContext, body)\n }\n\n const run = await job.trigger(\n (body.input ?? {}) as Record<string, unknown>,\n {\n idempotencyKey: body.idempotencyKey,\n concurrencyKey: body.concurrencyKey,\n labels: body.labels,\n },\n )\n\n const response: TriggerResponse = { runId: run.id }\n return jsonResponse(response)\n })\n }\n\n async function handleSubscribe(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n const result = await requireRunAccess(url, ctx, 'subscribe')\n if (result instanceof Response) return result\n\n const stream = durably.subscribe(result.runId)\n const sseStream = createThrottledSSEStreamFromReader(\n stream.getReader() as ReadableStreamDefaultReader<AnyEventInput>,\n throttleMs,\n )\n return createSSEResponse(sseStream)\n }\n\n async function handleRuns(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const filterOrError = parseRunFilter(url)\n if (filterOrError instanceof Response) return filterOrError\n\n let filter: RunFilter<TLabels> = filterOrError as RunFilter<TLabels>\n\n // Auth hook: scopeRuns\n if (auth?.scopeRuns && ctx !== undefined) {\n filter = await auth.scopeRuns(ctx as TContext, filter)\n }\n\n const runs = await durably.getRuns(filter)\n return jsonResponse(runs.map(toClientRun))\n })\n }\n\n async function handleRun(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'read')\n if (result instanceof Response) return result\n\n return jsonResponse(toClientRun(result.run))\n })\n }\n\n async function handleSteps(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'steps')\n if (result instanceof Response) return result\n\n const steps = await durably.storage.getSteps(result.runId)\n return jsonResponse(steps)\n })\n }\n\n async function handleRetrigger(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'retrigger')\n if (result instanceof Response) return result\n\n const run = await durably.retrigger(result.runId)\n return jsonResponse({ success: true, runId: run.id })\n })\n }\n\n async function handleCancel(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'cancel')\n if (result instanceof Response) return result\n\n await durably.cancel(result.runId)\n return successResponse()\n })\n }\n\n async function handleDelete(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n return withErrorHandling(async () => {\n const result = await requireRunAccess(url, ctx, 'delete')\n if (result instanceof Response) return result\n\n await durably.deleteRun(result.runId)\n return successResponse()\n })\n }\n\n async function handleRunsSubscribe(\n url: URL,\n ctx: TContext | undefined,\n ): Promise<Response> {\n let filter: RunsSubscribeFilter<TLabels>\n\n if (ctx !== undefined && auth?.scopeRunsSubscribe) {\n const parsed = parseRunsSubscribeFilter(\n url,\n ) as RunsSubscribeFilter<TLabels>\n filter = await auth.scopeRunsSubscribe(ctx as TContext, parsed)\n } else if (ctx !== undefined && auth?.scopeRuns) {\n // Fallback: use scopeRuns with subscribe-compatible filter\n const parsed = parseRunsSubscribeFilter(\n url,\n ) as RunsSubscribeFilter<TLabels>\n const scoped = await auth.scopeRuns(\n ctx as TContext,\n {\n ...parsed,\n } as RunFilter<TLabels>,\n )\n filter = { jobName: scoped.jobName, labels: scoped.labels }\n } else {\n filter = parseRunsSubscribeFilter(url) as RunsSubscribeFilter<TLabels>\n }\n\n return createRunsSSEStream(filter)\n }\n\n function createRunsSSEStream(filter: RunsSubscribeFilter): Response {\n const jobNameFilter = Array.isArray(filter.jobName)\n ? filter.jobName\n : filter.jobName\n ? [filter.jobName]\n : []\n const labelsFilter = filter.labels\n\n const matchesFilter = (\n jobName: string,\n labels?: Record<string, string>,\n ) => {\n if (jobNameFilter.length > 0 && !jobNameFilter.includes(jobName))\n return false\n if (\n labelsFilter &&\n (!labels ||\n !matchesLabels(labels, labelsFilter as Record<string, string>))\n )\n return false\n return true\n }\n\n const sseStream = createSSEStreamFromSubscriptions(\n (innerCtrl: SSEStreamController) => {\n const { controller: ctrl, dispose } = createThrottledSSEController(\n innerCtrl,\n throttleMs,\n )\n\n const unsubscribes = [\n durably.on('run:trigger', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:trigger',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:leased', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:leased',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:complete', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:complete',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:fail', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:fail',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:cancel', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:cancel',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:delete', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:delete',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('run:progress', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'run:progress',\n runId: event.runId,\n jobName: event.jobName,\n progress: event.progress,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('step:start', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'step:start',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('step:complete', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'step:complete',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('step:fail', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'step:fail',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n error: event.error,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('step:cancel', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'step:cancel',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n labels: event.labels,\n })\n }\n }),\n\n durably.on('log:write', (event) => {\n if (matchesFilter(event.jobName, event.labels)) {\n ctrl.enqueue({\n type: 'log:write',\n runId: event.runId,\n jobName: event.jobName,\n labels: event.labels,\n stepName: event.stepName,\n level: event.level,\n message: event.message,\n data: event.data,\n })\n }\n }),\n ]\n\n return [...unsubscribes, dispose]\n },\n )\n\n return createSSEResponse(sseStream)\n }\n\n // --- Public API: only handle() ---\n\n return {\n async handle(request: Request, basePath: string): Promise<Response> {\n try {\n // 1. Authenticate (fail fast before anything else)\n let ctx: TContext | undefined\n if (auth?.authenticate) {\n ctx = await auth.authenticate(request)\n }\n\n // 2. Run onRequest hook (lazy init: migrations, worker start)\n if (options?.onRequest) {\n await options.onRequest()\n }\n\n // 3. Route by path + method\n const url = new URL(request.url)\n const path = url.pathname.replace(basePath, '')\n const method = request.method\n\n // GET routes\n if (method === 'GET') {\n if (path === '/subscribe') return await handleSubscribe(url, ctx)\n if (path === '/runs') return await handleRuns(url, ctx)\n if (path === '/run') return await handleRun(url, ctx)\n if (path === '/steps') return await handleSteps(url, ctx)\n if (path === '/runs/subscribe')\n return await handleRunsSubscribe(url, ctx)\n }\n\n // POST routes\n if (method === 'POST') {\n if (path === '/trigger') return await handleTrigger(request, ctx)\n if (path === '/retrigger') return await handleRetrigger(url, ctx)\n if (path === '/cancel') return await handleCancel(url, ctx)\n }\n\n // DELETE routes\n if (method === 'DELETE') {\n if (path === '/run') return await handleDelete(url, ctx)\n }\n\n return new Response('Not Found', { status: 404 })\n } catch (error) {\n // Auth hooks throw Response to reject — return as-is\n if (error instanceof Response) return error\n return errorResponse(getErrorMessage(error), 500)\n }\n },\n }\n}\n"],"mappings":"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","names":["monotonicFactory","sql","row","ulid","monotonicFactory"]}