@coji/durably 0.6.0 → 0.7.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/errors.ts","../src/context.ts","../src/worker.ts","../src/define-job.ts","../src/plugins/log-persistence.ts","../src/server.ts"],"sourcesContent":["import type { Dialect } from 'kysely'\nimport { Kysely } from 'kysely'\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 dialect: Dialect\n pollingInterval?: number\n heartbeatInterval?: number\n staleThreshold?: number\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>): 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> = {\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>\n : never\n}\n\n/**\n * Durably instance with type-safe jobs\n */\nexport interface Durably<\n TJobs extends Record<string, JobHandle<string, unknown, unknown>> = Record<\n string,\n never\n >,\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>>\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 (returns unknown output type)\n */\n getRun(runId: string): Promise<Run | null>\n\n /**\n * Get runs with optional filtering\n */\n getRuns(filter?: RunFilter): Promise<Run[]>\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> | 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 migrating: Promise<void> | null\n migrated: boolean\n}\n\n/**\n * Create a Durably instance implementation\n */\nfunction createDurablyInstance<\n TJobs extends Record<string, JobHandle<string, unknown, unknown>>,\n>(state: DurablyState, jobs: TJobs): Durably<TJobs> {\n const { db, storage, eventEmitter, jobRegistry, worker } = state\n\n const durably: Durably<TJobs> = {\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>> {\n const newHandles = {} as TransformToHandles<TNewJobs>\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 )\n newHandles[key] = handle as TransformToHandles<TNewJobs>[typeof key]\n }\n\n // Create new instance with merged jobs\n const mergedJobs = { ...jobs, ...newHandles } as TJobs &\n TransformToHandles<TNewJobs>\n return createDurablyInstance(state, mergedJobs)\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> | 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 >\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 return new ReadableStream<DurablyEvent>({\n start: (controller) => {\n const unsubscribeStart = eventEmitter.on('run:start', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n })\n\n const unsubscribeComplete = eventEmitter.on(\n 'run:complete',\n (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n closed = true\n cleanup?.()\n controller.close()\n }\n },\n )\n\n const unsubscribeFail = eventEmitter.on('run:fail', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n // Don't close stream on fail - retry is possible\n }\n })\n\n const unsubscribeCancel = eventEmitter.on('run:cancel', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n // Don't close stream on cancel - retry is possible\n }\n })\n\n const unsubscribeRetry = eventEmitter.on('run:retry', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n })\n\n const unsubscribeProgress = eventEmitter.on(\n 'run:progress',\n (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n },\n )\n\n const unsubscribeStepStart = eventEmitter.on(\n 'step:start',\n (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n },\n )\n\n const unsubscribeStepComplete = eventEmitter.on(\n 'step:complete',\n (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n },\n )\n\n const unsubscribeStepFail = eventEmitter.on('step:fail', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n })\n\n const unsubscribeLog = eventEmitter.on('log:write', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n })\n\n // Assign cleanup function to outer scope for cancel handler\n cleanup = () => {\n unsubscribeStart()\n unsubscribeComplete()\n unsubscribeFail()\n unsubscribeCancel()\n unsubscribeRetry()\n unsubscribeProgress()\n unsubscribeStepStart()\n unsubscribeStepComplete()\n unsubscribeStepFail()\n unsubscribeLog()\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 })\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 })\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\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 */\nexport function createDurably(\n options: DurablyOptions,\n): Durably<Record<string, never>> {\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 migrating: null,\n migrated: false,\n }\n\n return createDurablyInstance(state, {})\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 payload: unknown\n}\n\n/**\n * Run start event\n */\nexport interface RunStartEvent extends BaseEvent {\n type: 'run:start'\n runId: string\n jobName: string\n payload: unknown\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}\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}\n\n/**\n * Run cancel event\n */\nexport interface RunCancelEvent extends BaseEvent {\n type: 'run:cancel'\n runId: string\n jobName: 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}\n\n/**\n * Run progress event\n */\nexport interface RunProgressEvent extends BaseEvent {\n type: 'run:progress'\n runId: string\n jobName: string\n progress: { current: number; total?: number; message?: 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}\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}\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}\n\n/**\n * Log write event\n */\nexport interface LogWriteEvent extends BaseEvent {\n type: 'log:write'\n runId: string\n stepName: string | null\n level: 'info' | 'warn' | 'error'\n message: string\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 | RunRetryEvent\n | RunProgressEvent\n | StepStartEvent\n | StepCompleteEvent\n | StepFailEvent\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:retry'>\n | EventInput<'run:progress'>\n | EventInput<'step:start'>\n | EventInput<'step:complete'>\n | EventInput<'step:fail'>\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 } from 'zod'\nimport type { JobDefinition } from './define-job'\nimport type { EventEmitter } from './events'\nimport type { Run, Storage } from './storage'\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: () => 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 payload: TInput,\n) => Promise<TOutput>\n\n/**\n * Trigger options\n */\nexport interface TriggerOptions {\n idempotencyKey?: string\n concurrencyKey?: string\n /** Timeout in milliseconds for triggerAndWait() */\n timeout?: number\n}\n\n/**\n * Run filter options\n */\nexport interface RunFilter {\n status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n jobName?: string\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 * Typed run with output type\n */\nexport interface TypedRun<TOutput> extends Omit<Run, 'output'> {\n output: TOutput | null\n}\n\n/**\n * Batch trigger input - either just the input or input with options\n */\nexport type BatchTriggerInput<TInput> =\n | TInput\n | { input: TInput; options?: TriggerOptions }\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<TName extends string, TInput, TOutput> {\n readonly name: TName\n\n /**\n * Trigger a new run\n */\n trigger(input: TInput, options?: TriggerOptions): Promise<TypedRun<TOutput>>\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?: TriggerOptions,\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>[],\n ): Promise<TypedRun<TOutput>[]>\n\n /**\n * Get a run by ID\n */\n getRun(id: string): Promise<TypedRun<TOutput> | null>\n\n /**\n * Get runs with optional filter\n */\n getRuns(filter?: Omit<RunFilter, 'jobName'>): Promise<TypedRun<TOutput>[]>\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 fn: JobFunction<TInput, TOutput>\n jobDef: JobDefinition<string, TInput, TOutput>\n handle: JobHandle<string, TInput, TOutput>\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<TName extends string, TInput, TOutput>(\n jobDef: JobDefinition<TName, TInput, TOutput>,\n storage: Storage,\n eventEmitter: EventEmitter,\n registry: JobRegistry,\n): JobHandle<TName, TInput, TOutput> {\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>\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> = {\n name: jobDef.name,\n\n async trigger(\n input: TInput,\n options?: TriggerOptions,\n ): Promise<TypedRun<TOutput>> {\n // Validate input\n const parseResult = inputSchema.safeParse(input)\n if (!parseResult.success) {\n throw new Error(`Invalid input: ${parseResult.error.message}`)\n }\n\n // Create the run\n const run = await storage.createRun({\n jobName: jobDef.name,\n payload: parseResult.data,\n idempotencyKey: options?.idempotencyKey,\n concurrencyKey: options?.concurrencyKey,\n })\n\n // Emit run:trigger event\n eventEmitter.emit({\n type: 'run:trigger',\n runId: run.id,\n jobName: jobDef.name,\n payload: parseResult.data,\n })\n\n return run as TypedRun<TOutput>\n },\n\n async triggerAndWait(\n input: TInput,\n options?: TriggerOptions,\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 cleanup = () => {\n if (resolved) return\n resolved = true\n unsubscribeComplete()\n unsubscribeFail()\n if (timeoutId) {\n clearTimeout(timeoutId)\n }\n }\n\n const unsubscribeComplete = 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 const unsubscribeFail = eventEmitter.on('run:fail', (event) => {\n if (event.runId === run.id && !resolved) {\n cleanup()\n reject(new Error(event.error))\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.getRun(run.id).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\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 })[],\n ): Promise<TypedRun<TOutput>[]> {\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 }\n }\n return { input: item as TInput, options: undefined }\n })\n\n // Validate all inputs first (before creating any runs)\n const validated: { payload: unknown; options?: TriggerOptions }[] = []\n for (let i = 0; i < normalized.length; i++) {\n const parseResult = inputSchema.safeParse(normalized[i].input)\n if (!parseResult.success) {\n throw new Error(\n `Invalid input at index ${i}: ${parseResult.error.message}`,\n )\n }\n validated.push({\n payload: parseResult.data,\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 payload: v.payload,\n idempotencyKey: v.options?.idempotencyKey,\n concurrencyKey: v.options?.concurrencyKey,\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 payload: validated[i].payload,\n })\n }\n\n return runs as TypedRun<TOutput>[]\n },\n\n async getRun(id: string): Promise<TypedRun<TOutput> | 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>\n },\n\n async getRuns(\n filter?: Omit<RunFilter, 'jobName'>,\n ): Promise<TypedRun<TOutput>[]> {\n const runs = await storage.getRuns({\n ...filter,\n jobName: jobDef.name,\n })\n return runs as TypedRun<TOutput>[]\n },\n }\n\n // Register the job with the handle\n registry.set({\n name: jobDef.name,\n inputSchema,\n outputSchema,\n fn: jobDef.run as JobFunction<unknown, unknown>,\n jobDef: jobDef as JobDefinition<string, TInput, TOutput>,\n handle: handle as JobHandle<string, TInput, TOutput>,\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\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('payload', 'text', (col) => col.notNull())\n .addColumn('status', 'text', (col) => col.notNull())\n .addColumn('idempotency_key', 'text')\n .addColumn('concurrency_key', 'text')\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('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 migration.up(db)\n\n await db\n .insertInto('durably_schema_versions')\n .values({\n version: migration.version,\n applied_at: new Date().toISOString(),\n })\n .execute()\n }\n }\n}\n","import type { Kysely } from 'kysely'\nimport { ulid } from 'ulidx'\nimport type { Database } from './schema'\n\n/**\n * Run data for creating a new run\n */\nexport interface CreateRunInput {\n jobName: string\n payload: unknown\n idempotencyKey?: string\n concurrencyKey?: string\n}\n\n/**\n * Run data returned from storage\n */\nexport interface Run {\n id: string\n jobName: string\n payload: 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 heartbeatAt: string\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}\n\n/**\n * Run filter options\n */\nexport interface RunFilter {\n status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n jobName?: string\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'\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'\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 * 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(runId: string): Promise<Run | null>\n getRuns(filter?: RunFilter): Promise<Run[]>\n getNextPendingRun(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 */\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 payload: JSON.parse(row.payload),\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 heartbeatAt: row.heartbeat_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 const id = ulid()\n const run: Database['durably_runs'] = {\n id,\n job_name: input.jobName,\n payload: JSON.stringify(input.payload),\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 heartbeat_at: now,\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 // 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 payload: JSON.stringify(input.payload),\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 heartbeat_at: now,\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\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(runId: string): Promise<Run | 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) : null\n },\n\n async getRuns(filter?: RunFilter): Promise<Run[]> {\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 query = query.where('durably_runs.job_name', '=', filter.jobName)\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)\n },\n\n async getNextPendingRun(\n excludeConcurrencyKeys: string[],\n ): Promise<Run | null> {\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 .where('durably_runs.status', '=', 'pending')\n .groupBy('durably_runs.id')\n .orderBy('durably_runs.created_at', 'asc')\n .limit(1)\n\n if (excludeConcurrencyKeys.length > 0) {\n query = query.where((eb) =>\n eb.or([\n eb('durably_runs.concurrency_key', 'is', null),\n eb(\n 'durably_runs.concurrency_key',\n 'not in',\n excludeConcurrencyKeys,\n ),\n ]),\n )\n }\n\n const row = await query.executeTakeFirst()\n return row ? rowToRun(row) : null\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","/**\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","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): StepContext {\n let stepIndex = run.currentStepIndex\n let currentStepName: string | null = null\n\n return {\n get runId(): string {\n return run.id\n },\n\n async run<T>(name: string, fn: () => T | Promise<T>): Promise<T> {\n // Check if run was cancelled before executing this step\n const currentRun = await storage.getRun(run.id)\n if (currentRun?.status === 'cancelled') {\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 })\n\n try {\n // Execute the step\n const result = await fn()\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 })\n\n return result\n } catch (error) {\n // Save failed step\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: 'failed',\n error: errorMessage,\n startedAt,\n })\n\n // Emit step:fail event\n eventEmitter.emit({\n type: 'step:fail',\n runId: run.id,\n jobName,\n stepName: name,\n stepIndex,\n error: errorMessage,\n })\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 })\n },\n\n log: {\n info(message: string, data?: unknown): void {\n eventEmitter.emit({\n type: 'log:write',\n runId: run.id,\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 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 stepName: currentStepName,\n level: 'error',\n message,\n data,\n })\n },\n },\n }\n}\n","import { createStepContext } from './context'\nimport { CancelledError } 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 * Extract error message from unknown error\n */\n function getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\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?.status === 'cancelled') {\n return\n }\n\n await storage.updateRun(runId, {\n status: 'completed',\n output,\n })\n\n eventEmitter.emit({\n type: 'run:complete',\n runId,\n jobName,\n output,\n duration: Date.now() - startTime,\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?.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 })\n\n eventEmitter.emit({\n type: 'run:fail',\n runId,\n jobName,\n error: errorMessage,\n failedStepName: failedStep?.name ?? 'unknown',\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: error instanceof Error ? error.message : String(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 payload: run.payload,\n })\n\n const startTime = Date.now()\n\n try {\n // Create step context and execute job\n const step = createStepContext(run, run.jobName, storage, eventEmitter)\n const output = await job.fn(step, run.payload)\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: ${parseResult.error.message}`)\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 // 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 // Get next pending run\n const run = await storage.getNextPendingRun(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 // Transition to running\n await storage.updateRun(run.id, {\n status: 'running',\n heartbeatAt: new Date().toISOString(),\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","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 payload: 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, payload) => {\n * const users = await step.run('fetch-users', () => fetchUsers(payload.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","import type { DurablyPlugin } from '../durably'\n\n/**\n * Plugin that persists log events to the database\n */\nexport function withLogPersistence(): DurablyPlugin {\n return {\n name: 'log-persistence',\n install(durably) {\n durably.on('log:write', async (event) => {\n await durably.storage.createLog({\n runId: event.runId,\n stepName: event.stepName,\n level: event.level,\n message: event.message,\n data: event.data,\n })\n })\n },\n }\n}\n","import type { Durably } from './durably'\nimport type { AnyEventInput } from './events'\n\n/**\n * Request body for triggering a job\n */\nexport interface TriggerRequest {\n jobName: string\n input: Record<string, unknown>\n idempotencyKey?: string\n concurrencyKey?: string\n}\n\n/**\n * Response for trigger endpoint\n */\nexport interface TriggerResponse {\n runId: string\n}\n\n/**\n * Request query params for listing runs\n */\nexport interface RunsRequest {\n jobName?: string\n status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n limit?: number\n offset?: number\n}\n\n/**\n * Handler interface for HTTP endpoints\n */\nexport interface DurablyHandler {\n /**\n * Handle all Durably HTTP requests with automatic routing\n *\n * Routes:\n * - GET {basePath}/subscribe?runId=xxx - SSE stream\n * - GET {basePath}/runs - List runs\n * - GET {basePath}/run?runId=xxx - Get single run\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 *\n * @param request - The incoming HTTP request\n * @param basePath - The base path to strip from the URL (e.g., '/api/durably')\n * @returns Response or null if route not matched\n *\n * @example\n * ```ts\n * // React Router / Remix\n * export async function loader({ request }) {\n * return durablyHandler.handle(request, '/api/durably')\n * }\n * export async function action({ request }) {\n * return durablyHandler.handle(request, '/api/durably')\n * }\n * ```\n */\n handle(request: Request, basePath: string): Promise<Response>\n\n /**\n * Handle job trigger request\n * Expects POST with JSON body: { jobName, input, idempotencyKey?, concurrencyKey? }\n * Returns JSON: { runId }\n */\n trigger(request: Request): Promise<Response>\n\n /**\n * Handle subscription request\n * Expects GET with query param: runId\n * Returns SSE stream of events\n */\n subscribe(request: Request): Response\n\n /**\n * Handle runs list request\n * Expects GET with optional query params: jobName, status, limit, offset\n * Returns JSON array of runs\n */\n runs(request: Request): Promise<Response>\n\n /**\n * Handle single run request\n * Expects GET with query param: runId\n * Returns JSON run object or 404\n */\n run(request: Request): Promise<Response>\n\n /**\n * Handle retry request\n * Expects POST with query param: runId\n * Returns JSON: { success: true }\n */\n retry(request: Request): Promise<Response>\n\n /**\n * Handle cancel request\n * Expects POST with query param: runId\n * Returns JSON: { success: true }\n */\n cancel(request: Request): Promise<Response>\n\n /**\n * Handle delete request\n * Expects DELETE with query param: runId\n * Returns JSON: { success: true }\n */\n delete(request: Request): Promise<Response>\n\n /**\n * Handle steps request\n * Expects GET with query param: runId\n * Returns JSON array of steps\n */\n steps(request: Request): Promise<Response>\n\n /**\n * Handle runs subscription request\n * Expects GET with optional query param: jobName\n * Returns SSE stream of run update notifications\n */\n runsSubscribe(request: Request): Response\n}\n\n/**\n * Options for createDurablyHandler\n */\nexport interface CreateDurablyHandlerOptions {\n /**\n * Called before handling each request.\n * Use this to initialize Durably (migrate, start worker, etc.)\n *\n * @example\n * ```ts\n * const durablyHandler = createDurablyHandler(durably, {\n * onRequest: async () => {\n * await durably.migrate()\n * durably.start()\n * }\n * })\n * ```\n */\n onRequest?: () => Promise<void> | void\n}\n\n/**\n * Create HTTP handlers for Durably\n * Uses Web Standard Request/Response for framework-agnostic usage\n */\nexport function createDurablyHandler(\n durably: Durably,\n options?: CreateDurablyHandlerOptions,\n): DurablyHandler {\n const handler: DurablyHandler = {\n async handle(request: Request, basePath: string): Promise<Response> {\n // Run onRequest hook if provided\n if (options?.onRequest) {\n await options.onRequest()\n }\n\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 handler.subscribe(request)\n if (path === '/runs') return handler.runs(request)\n if (path === '/run') return handler.run(request)\n if (path === '/steps') return handler.steps(request)\n if (path === '/runs/subscribe') return handler.runsSubscribe(request)\n }\n\n // POST routes\n if (method === 'POST') {\n if (path === '/trigger') return handler.trigger(request)\n if (path === '/retry') return handler.retry(request)\n if (path === '/cancel') return handler.cancel(request)\n }\n\n // DELETE routes\n if (method === 'DELETE') {\n if (path === '/run') return handler.delete(request)\n }\n\n return new Response('Not Found', { status: 404 })\n },\n\n async trigger(request: Request): Promise<Response> {\n try {\n const body = (await request.json()) as TriggerRequest\n\n if (!body.jobName) {\n return new Response(\n JSON.stringify({ error: 'jobName is required' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n const job = durably.getJob(body.jobName)\n if (!job) {\n return new Response(\n JSON.stringify({ error: `Job not found: ${body.jobName}` }),\n { status: 404, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n const run = await job.trigger(body.input ?? {}, {\n idempotencyKey: body.idempotencyKey,\n concurrencyKey: body.concurrencyKey,\n })\n\n const response: TriggerResponse = { runId: run.id }\n return new Response(JSON.stringify(response), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n },\n\n subscribe(request: Request): Response {\n const url = new URL(request.url)\n const runId = url.searchParams.get('runId')\n\n if (!runId) {\n return new Response(\n JSON.stringify({ error: 'runId query parameter is required' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n const stream = durably.subscribe(runId)\n\n // Transform stream to SSE format\n const encoder = new TextEncoder()\n const sseStream = new ReadableStream({\n async start(controller) {\n const reader = stream.getReader()\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n controller.close()\n break\n }\n\n // Format as SSE\n const event = value as AnyEventInput\n const data = `data: ${JSON.stringify(event)}\\n\\n`\n controller.enqueue(encoder.encode(data))\n }\n } catch (error) {\n controller.error(error)\n }\n },\n })\n\n return new Response(sseStream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n })\n },\n\n async runs(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const jobName = url.searchParams.get('jobName') ?? undefined\n const status = url.searchParams.get('status') as RunsRequest['status']\n const limit = url.searchParams.get('limit')\n const offset = url.searchParams.get('offset')\n\n const runs = await durably.getRuns({\n jobName,\n status,\n limit: limit ? Number.parseInt(limit, 10) : undefined,\n offset: offset ? Number.parseInt(offset, 10) : undefined,\n })\n\n return new Response(JSON.stringify(runs), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n },\n\n async run(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = url.searchParams.get('runId')\n\n if (!runId) {\n return new Response(\n JSON.stringify({ error: 'runId query parameter is required' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n const run = await durably.getRun(runId)\n\n if (!run) {\n return new Response(JSON.stringify({ error: 'Run not found' }), {\n status: 404,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n\n return new Response(JSON.stringify(run), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n },\n\n async retry(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = url.searchParams.get('runId')\n\n if (!runId) {\n return new Response(\n JSON.stringify({ error: 'runId query parameter is required' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n await durably.retry(runId)\n\n return new Response(JSON.stringify({ success: true }), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n },\n\n async cancel(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = url.searchParams.get('runId')\n\n if (!runId) {\n return new Response(\n JSON.stringify({ error: 'runId query parameter is required' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n await durably.cancel(runId)\n\n return new Response(JSON.stringify({ success: true }), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n },\n\n async delete(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = url.searchParams.get('runId')\n\n if (!runId) {\n return new Response(\n JSON.stringify({ error: 'runId query parameter is required' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n await durably.deleteRun(runId)\n\n return new Response(JSON.stringify({ success: true }), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n },\n\n async steps(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = url.searchParams.get('runId')\n\n if (!runId) {\n return new Response(\n JSON.stringify({ error: 'runId query parameter is required' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n const steps = await durably.storage.getSteps(runId)\n\n return new Response(JSON.stringify(steps), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n },\n\n runsSubscribe(request: Request): Response {\n const url = new URL(request.url)\n const jobNameFilter = url.searchParams.get('jobName')\n\n const encoder = new TextEncoder()\n let closed = false\n\n const sseStream = new ReadableStream({\n start(controller) {\n // Subscribe to run lifecycle events\n const unsubscribeTrigger = durably.on('run:trigger', (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'run:trigger',\n runId: event.runId,\n jobName: event.jobName,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n const unsubscribeStart = durably.on('run:start', (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'run:start',\n runId: event.runId,\n jobName: event.jobName,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n const unsubscribeComplete = durably.on('run:complete', (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'run:complete',\n runId: event.runId,\n jobName: event.jobName,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n const unsubscribeFail = durably.on('run:fail', (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'run:fail',\n runId: event.runId,\n jobName: event.jobName,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n const unsubscribeCancel = durably.on('run:cancel', (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'run:cancel',\n runId: event.runId,\n jobName: event.jobName,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n const unsubscribeRetry = durably.on('run:retry', (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'run:retry',\n runId: event.runId,\n jobName: event.jobName,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n const unsubscribeProgress = durably.on('run:progress', (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'run:progress',\n runId: event.runId,\n jobName: event.jobName,\n progress: event.progress,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n const unsubscribeStepStart = durably.on('step:start', (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'step:start',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n const unsubscribeStepComplete = durably.on(\n 'step:complete',\n (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'step:complete',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n },\n )\n\n const unsubscribeStepFail = durably.on('step:fail', (event) => {\n if (closed) return\n if (jobNameFilter && event.jobName !== jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\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 })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n const unsubscribeLogWrite = durably.on('log:write', (event) => {\n if (closed) return\n // log:write doesn't have jobName, so we can't filter by it\n // Send all logs when no filter, or skip if filter is set\n if (jobNameFilter) return\n\n const data = `data: ${JSON.stringify({\n type: 'log:write',\n runId: event.runId,\n stepName: event.stepName,\n level: event.level,\n message: event.message,\n data: event.data,\n })}\\n\\n`\n controller.enqueue(encoder.encode(data))\n })\n\n // Store cleanup function for cancel\n ;(controller as unknown as { cleanup: () => void }).cleanup = () => {\n closed = true\n unsubscribeTrigger()\n unsubscribeStart()\n unsubscribeComplete()\n unsubscribeFail()\n unsubscribeCancel()\n unsubscribeRetry()\n unsubscribeProgress()\n unsubscribeStepStart()\n unsubscribeStepComplete()\n unsubscribeStepFail()\n unsubscribeLogWrite()\n }\n },\n cancel(controller) {\n const cleanup = (controller as unknown as { cleanup: () => void })\n .cleanup\n if (cleanup) cleanup()\n },\n })\n\n return new Response(sseStream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n })\n },\n }\n\n return handler\n}\n"],"mappings":"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","names":[]}
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 { 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 dialect: Dialect\n pollingInterval?: number\n heartbeatInterval?: number\n staleThreshold?: number\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>): 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> = {\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>\n : never\n}\n\n/**\n * Durably instance with type-safe jobs\n */\nexport interface Durably<\n TJobs extends Record<string, JobHandle<string, unknown, unknown>> = Record<\n string,\n never\n >,\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>>\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 & { payload: { userId: string }; output: { count: number } | null }\n * const typedRun = await durably.getRun<MyRun>(runId)\n * ```\n */\n getRun<T extends Run = Run>(runId: string): 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 & { payload: { userId: string }; output: { count: number } | null }\n * const typedRuns = await durably.getRuns<MyRun>({ jobName: 'my-job' })\n * ```\n */\n getRuns<T extends Run = Run>(filter?: RunFilter): 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> | 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 migrating: Promise<void> | null\n migrated: boolean\n}\n\n/**\n * Create a Durably instance implementation\n */\nfunction createDurablyInstance<\n TJobs extends Record<string, JobHandle<string, unknown, unknown>>,\n>(state: DurablyState, jobs: TJobs): Durably<TJobs> {\n const { db, storage, eventEmitter, jobRegistry, worker } = state\n\n const durably: Durably<TJobs> = {\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>> {\n const newHandles = {} as TransformToHandles<TNewJobs>\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 )\n newHandles[key] = handle as TransformToHandles<TNewJobs>[typeof key]\n }\n\n // Create new instance with merged jobs\n const mergedJobs = { ...jobs, ...newHandles } as TJobs &\n TransformToHandles<TNewJobs>\n return createDurablyInstance(state, mergedJobs)\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> | 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 >\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 return new ReadableStream<DurablyEvent>({\n start: (controller) => {\n const unsubscribeStart = eventEmitter.on('run:start', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n })\n\n const unsubscribeComplete = eventEmitter.on(\n 'run:complete',\n (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n closed = true\n cleanup?.()\n controller.close()\n }\n },\n )\n\n const unsubscribeFail = eventEmitter.on('run:fail', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n // Don't close stream on fail - retry is possible\n }\n })\n\n const unsubscribeCancel = eventEmitter.on('run:cancel', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n // Don't close stream on cancel - retry is possible\n }\n })\n\n const unsubscribeRetry = eventEmitter.on('run:retry', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n })\n\n const unsubscribeProgress = eventEmitter.on(\n 'run:progress',\n (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n },\n )\n\n const unsubscribeStepStart = eventEmitter.on(\n 'step:start',\n (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n },\n )\n\n const unsubscribeStepComplete = eventEmitter.on(\n 'step:complete',\n (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n },\n )\n\n const unsubscribeStepFail = eventEmitter.on('step:fail', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n })\n\n const unsubscribeLog = eventEmitter.on('log:write', (event) => {\n if (!closed && event.runId === runId) {\n controller.enqueue(event)\n }\n })\n\n // Assign cleanup function to outer scope for cancel handler\n cleanup = () => {\n unsubscribeStart()\n unsubscribeComplete()\n unsubscribeFail()\n unsubscribeCancel()\n unsubscribeRetry()\n unsubscribeProgress()\n unsubscribeStepStart()\n unsubscribeStepComplete()\n unsubscribeStepFail()\n unsubscribeLog()\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 })\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 })\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\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 */\nexport function createDurably(\n options: DurablyOptions,\n): Durably<Record<string, never>> {\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 migrating: null,\n migrated: false,\n }\n\n return createDurablyInstance(state, {})\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 payload: unknown\n}\n\n/**\n * Run start event\n */\nexport interface RunStartEvent extends BaseEvent {\n type: 'run:start'\n runId: string\n jobName: string\n payload: unknown\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}\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}\n\n/**\n * Run cancel event\n */\nexport interface RunCancelEvent extends BaseEvent {\n type: 'run:cancel'\n runId: string\n jobName: 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}\n\n/**\n * Run progress event\n */\nexport interface RunProgressEvent extends BaseEvent {\n type: 'run:progress'\n runId: string\n jobName: string\n progress: { current: number; total?: number; message?: 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}\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}\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}\n\n/**\n * Log write event\n */\nexport interface LogWriteEvent extends BaseEvent {\n type: 'log:write'\n runId: string\n stepName: string | null\n level: 'info' | 'warn' | 'error'\n message: string\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 | RunRetryEvent\n | RunProgressEvent\n | StepStartEvent\n | StepCompleteEvent\n | StepFailEvent\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:retry'>\n | EventInput<'run:progress'>\n | EventInput<'step:start'>\n | EventInput<'step:complete'>\n | EventInput<'step:fail'>\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 } from './events'\nimport type { Run, Storage } from './storage'\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: () => 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 payload: TInput,\n) => Promise<TOutput>\n\n/**\n * Trigger options\n */\nexport interface TriggerOptions {\n idempotencyKey?: string\n concurrencyKey?: string\n /** Timeout in milliseconds for triggerAndWait() */\n timeout?: number\n}\n\n/**\n * Run filter options\n */\nexport interface RunFilter {\n status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n jobName?: string\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 * Typed run with output type\n */\nexport interface TypedRun<TOutput> extends Omit<Run, 'output'> {\n output: TOutput | null\n}\n\n/**\n * Batch trigger input - either just the input or input with options\n */\nexport type BatchTriggerInput<TInput> =\n | TInput\n | { input: TInput; options?: TriggerOptions }\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<TName extends string, TInput, TOutput> {\n readonly name: TName\n\n /**\n * Trigger a new run\n */\n trigger(input: TInput, options?: TriggerOptions): Promise<TypedRun<TOutput>>\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?: TriggerOptions,\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>[],\n ): Promise<TypedRun<TOutput>[]>\n\n /**\n * Get a run by ID\n */\n getRun(id: string): Promise<TypedRun<TOutput> | null>\n\n /**\n * Get runs with optional filter\n */\n getRuns(filter?: Omit<RunFilter, 'jobName'>): Promise<TypedRun<TOutput>[]>\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 fn: JobFunction<TInput, TOutput>\n jobDef: JobDefinition<string, TInput, TOutput>\n handle: JobHandle<string, TInput, TOutput>\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<TName extends string, TInput, TOutput>(\n jobDef: JobDefinition<TName, TInput, TOutput>,\n storage: Storage,\n eventEmitter: EventEmitter,\n registry: JobRegistry,\n): JobHandle<TName, TInput, TOutput> {\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>\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> = {\n name: jobDef.name,\n\n async trigger(\n input: TInput,\n options?: TriggerOptions,\n ): Promise<TypedRun<TOutput>> {\n // Validate input\n const validatedInput = validateJobInputOrThrow(inputSchema, input)\n\n // Create the run\n const run = await storage.createRun({\n jobName: jobDef.name,\n payload: validatedInput,\n idempotencyKey: options?.idempotencyKey,\n concurrencyKey: options?.concurrencyKey,\n })\n\n // Emit run:trigger event\n eventEmitter.emit({\n type: 'run:trigger',\n runId: run.id,\n jobName: jobDef.name,\n payload: validatedInput,\n })\n\n return run as TypedRun<TOutput>\n },\n\n async triggerAndWait(\n input: TInput,\n options?: TriggerOptions,\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 cleanup = () => {\n if (resolved) return\n resolved = true\n unsubscribeComplete()\n unsubscribeFail()\n if (timeoutId) {\n clearTimeout(timeoutId)\n }\n }\n\n const unsubscribeComplete = 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 const unsubscribeFail = eventEmitter.on('run:fail', (event) => {\n if (event.runId === run.id && !resolved) {\n cleanup()\n reject(new Error(event.error))\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.getRun(run.id).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\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 })[],\n ): Promise<TypedRun<TOutput>[]> {\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 }\n }\n return { input: item as TInput, options: undefined }\n })\n\n // Validate all inputs first (before creating any runs)\n const validated: { payload: unknown; options?: TriggerOptions }[] = []\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 validated.push({\n payload: 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 payload: v.payload,\n idempotencyKey: v.options?.idempotencyKey,\n concurrencyKey: v.options?.concurrencyKey,\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 payload: validated[i].payload,\n })\n }\n\n return runs as TypedRun<TOutput>[]\n },\n\n async getRun(id: string): Promise<TypedRun<TOutput> | 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>\n },\n\n async getRuns(\n filter?: Omit<RunFilter, 'jobName'>,\n ): Promise<TypedRun<TOutput>[]> {\n const runs = await storage.getRuns({\n ...filter,\n jobName: jobDef.name,\n })\n return runs as TypedRun<TOutput>[]\n },\n }\n\n // Register the job with the handle\n registry.set({\n name: jobDef.name,\n inputSchema,\n outputSchema,\n fn: jobDef.run as JobFunction<unknown, unknown>,\n jobDef: jobDef as JobDefinition<string, TInput, TOutput>,\n handle: handle as JobHandle<string, TInput, TOutput>,\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\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('payload', 'text', (col) => col.notNull())\n .addColumn('status', 'text', (col) => col.notNull())\n .addColumn('idempotency_key', 'text')\n .addColumn('concurrency_key', 'text')\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('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 migration.up(db)\n\n await db\n .insertInto('durably_schema_versions')\n .values({\n version: migration.version,\n applied_at: new Date().toISOString(),\n })\n .execute()\n }\n }\n}\n","import type { Kysely } from 'kysely'\nimport { ulid } from 'ulidx'\nimport type { Database } from './schema'\n\n/**\n * Run data for creating a new run\n */\nexport interface CreateRunInput {\n jobName: string\n payload: unknown\n idempotencyKey?: string\n concurrencyKey?: string\n}\n\n/**\n * Run data returned from storage\n */\nexport interface Run {\n id: string\n jobName: string\n payload: 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 heartbeatAt: string\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}\n\n/**\n * Run filter options\n */\nexport interface RunFilter {\n status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n jobName?: string\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'\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'\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 * 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 getNextPendingRun(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 */\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 payload: JSON.parse(row.payload),\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 heartbeatAt: row.heartbeat_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 const id = ulid()\n const run: Database['durably_runs'] = {\n id,\n job_name: input.jobName,\n payload: JSON.stringify(input.payload),\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 heartbeat_at: now,\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 // 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 payload: JSON.stringify(input.payload),\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 heartbeat_at: now,\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\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 query = query.where('durably_runs.job_name', '=', filter.jobName)\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 getNextPendingRun(\n excludeConcurrencyKeys: string[],\n ): Promise<Run | null> {\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 .where('durably_runs.status', '=', 'pending')\n .groupBy('durably_runs.id')\n .orderBy('durably_runs.created_at', 'asc')\n .orderBy('durably_runs.id', 'asc')\n .limit(1)\n\n if (excludeConcurrencyKeys.length > 0) {\n query = query.where((eb) =>\n eb.or([\n eb('durably_runs.concurrency_key', 'is', null),\n eb(\n 'durably_runs.concurrency_key',\n 'not in',\n excludeConcurrencyKeys,\n ),\n ]),\n )\n }\n\n const row = await query.executeTakeFirst()\n return row ? rowToRun(row) : null\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?.status === 'cancelled') {\n return\n }\n\n await storage.updateRun(runId, {\n status: 'completed',\n output,\n })\n\n eventEmitter.emit({\n type: 'run:complete',\n runId,\n jobName,\n output,\n duration: Date.now() - startTime,\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?.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 })\n\n eventEmitter.emit({\n type: 'run:fail',\n runId,\n jobName,\n error: errorMessage,\n failedStepName: failedStep?.name ?? 'unknown',\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 payload: run.payload,\n })\n\n const startTime = Date.now()\n\n try {\n // Create step context and execute job\n const step = createStepContext(run, run.jobName, storage, eventEmitter)\n const output = await job.fn(step, run.payload)\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 // 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 // Get next pending run\n const run = await storage.getNextPendingRun(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 // Transition to running\n await storage.updateRun(run.id, {\n status: 'running',\n heartbeatAt: new Date().toISOString(),\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): StepContext {\n let stepIndex = run.currentStepIndex\n let currentStepName: string | null = null\n\n return {\n get runId(): string {\n return run.id\n },\n\n async run<T>(name: string, fn: () => T | Promise<T>): Promise<T> {\n // Check if run was cancelled before executing this step\n const currentRun = await storage.getRun(run.id)\n if (currentRun?.status === 'cancelled') {\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 })\n\n try {\n // Execute the step\n const result = await fn()\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 })\n\n return result\n } catch (error) {\n // Save failed step\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: 'failed',\n error: errorMessage,\n startedAt,\n })\n\n // Emit step:fail event\n eventEmitter.emit({\n type: 'step:fail',\n runId: run.id,\n jobName,\n stepName: name,\n stepIndex,\n error: errorMessage,\n })\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 })\n },\n\n log: {\n info(message: string, data?: unknown): void {\n eventEmitter.emit({\n type: 'log:write',\n runId: run.id,\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 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 stepName: currentStepName,\n level: 'error',\n message,\n data,\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 payload: 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, payload) => {\n * const users = await step.run('fetch-users', () => fetchUsers(payload.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 * 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","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 createSSEStreamFromReader,\n createSSEStreamFromSubscriptions,\n type SSEStreamController,\n} from './sse'\n\n/**\n * Request body for triggering a job\n */\nexport interface TriggerRequest {\n jobName: string\n input: Record<string, unknown>\n idempotencyKey?: string\n concurrencyKey?: string\n}\n\n/**\n * Response for trigger endpoint\n */\nexport interface TriggerResponse {\n runId: string\n}\n\n/**\n * Request query params for listing runs\n */\nexport interface RunsRequest {\n jobName?: string\n status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n limit?: number\n offset?: number\n}\n\n/**\n * Handler interface for HTTP endpoints\n */\nexport interface DurablyHandler {\n /**\n * Handle all Durably HTTP requests with automatic routing\n *\n * Routes:\n * - GET {basePath}/subscribe?runId=xxx - SSE stream\n * - GET {basePath}/runs - List runs\n * - GET {basePath}/run?runId=xxx - Get single run\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 *\n * @param request - The incoming HTTP request\n * @param basePath - The base path to strip from the URL (e.g., '/api/durably')\n * @returns Response or null if route not matched\n *\n * @example\n * ```ts\n * // React Router / Remix\n * export async function loader({ request }) {\n * return durablyHandler.handle(request, '/api/durably')\n * }\n * export async function action({ request }) {\n * return durablyHandler.handle(request, '/api/durably')\n * }\n * ```\n */\n handle(request: Request, basePath: string): Promise<Response>\n\n /**\n * Handle job trigger request\n * Expects POST with JSON body: { jobName, input, idempotencyKey?, concurrencyKey? }\n * Returns JSON: { runId }\n */\n trigger(request: Request): Promise<Response>\n\n /**\n * Handle subscription request\n * Expects GET with query param: runId\n * Returns SSE stream of events\n */\n subscribe(request: Request): Response\n\n /**\n * Handle runs list request\n * Expects GET with optional query params: jobName, status, limit, offset\n * Returns JSON array of runs\n */\n runs(request: Request): Promise<Response>\n\n /**\n * Handle single run request\n * Expects GET with query param: runId\n * Returns JSON run object or 404\n */\n run(request: Request): Promise<Response>\n\n /**\n * Handle retry request\n * Expects POST with query param: runId\n * Returns JSON: { success: true }\n */\n retry(request: Request): Promise<Response>\n\n /**\n * Handle cancel request\n * Expects POST with query param: runId\n * Returns JSON: { success: true }\n */\n cancel(request: Request): Promise<Response>\n\n /**\n * Handle delete request\n * Expects DELETE with query param: runId\n * Returns JSON: { success: true }\n */\n delete(request: Request): Promise<Response>\n\n /**\n * Handle steps request\n * Expects GET with query param: runId\n * Returns JSON array of steps\n */\n steps(request: Request): Promise<Response>\n\n /**\n * Handle runs subscription request\n * Expects GET with optional query param: jobName\n * Returns SSE stream of run update notifications\n */\n runsSubscribe(request: Request): Response\n}\n\n/**\n * Options for createDurablyHandler\n */\nexport interface CreateDurablyHandlerOptions {\n /**\n * Called before handling each request.\n * Use this to initialize Durably (migrate, start worker, etc.)\n *\n * @example\n * ```ts\n * const durablyHandler = createDurablyHandler(durably, {\n * onRequest: async () => {\n * await durably.migrate()\n * durably.start()\n * }\n * })\n * ```\n */\n onRequest?: () => Promise<void> | void\n}\n\n/**\n * Create HTTP handlers for Durably\n * Uses Web Standard Request/Response for framework-agnostic usage\n */\nexport function createDurablyHandler(\n durably: Durably,\n options?: CreateDurablyHandlerOptions,\n): DurablyHandler {\n const handler: DurablyHandler = {\n async handle(request: Request, basePath: string): Promise<Response> {\n // Run onRequest hook if provided\n if (options?.onRequest) {\n await options.onRequest()\n }\n\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 handler.subscribe(request)\n if (path === '/runs') return handler.runs(request)\n if (path === '/run') return handler.run(request)\n if (path === '/steps') return handler.steps(request)\n if (path === '/runs/subscribe') return handler.runsSubscribe(request)\n }\n\n // POST routes\n if (method === 'POST') {\n if (path === '/trigger') return handler.trigger(request)\n if (path === '/retry') return handler.retry(request)\n if (path === '/cancel') return handler.cancel(request)\n }\n\n // DELETE routes\n if (method === 'DELETE') {\n if (path === '/run') return handler.delete(request)\n }\n\n return new Response('Not Found', { status: 404 })\n },\n\n async trigger(request: Request): Promise<Response> {\n try {\n const body = (await request.json()) as TriggerRequest\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 const run = await job.trigger(body.input ?? {}, {\n idempotencyKey: body.idempotencyKey,\n concurrencyKey: body.concurrencyKey,\n })\n\n const response: TriggerResponse = { runId: run.id }\n return jsonResponse(response)\n } catch (error) {\n return errorResponse(getErrorMessage(error), 500)\n }\n },\n\n subscribe(request: Request): Response {\n const url = new URL(request.url)\n const runId = getRequiredQueryParam(url, 'runId')\n if (runId instanceof Response) return runId\n\n const stream = durably.subscribe(runId)\n const sseStream = createSSEStreamFromReader(\n stream.getReader() as ReadableStreamDefaultReader<AnyEventInput>,\n )\n\n return createSSEResponse(sseStream)\n },\n\n async runs(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const jobName = url.searchParams.get('jobName') ?? undefined\n const status = url.searchParams.get('status') as RunsRequest['status']\n const limit = url.searchParams.get('limit')\n const offset = url.searchParams.get('offset')\n\n const runs = await durably.getRuns({\n jobName,\n status,\n limit: limit ? Number.parseInt(limit, 10) : undefined,\n offset: offset ? Number.parseInt(offset, 10) : undefined,\n })\n\n return jsonResponse(runs)\n } catch (error) {\n return errorResponse(getErrorMessage(error), 500)\n }\n },\n\n async run(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = getRequiredQueryParam(url, 'runId')\n if (runId instanceof Response) return runId\n\n const run = await durably.getRun(runId)\n\n if (!run) {\n return errorResponse('Run not found', 404)\n }\n\n return jsonResponse(run)\n } catch (error) {\n return errorResponse(getErrorMessage(error), 500)\n }\n },\n\n async retry(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = getRequiredQueryParam(url, 'runId')\n if (runId instanceof Response) return runId\n\n await durably.retry(runId)\n\n return successResponse()\n } catch (error) {\n return errorResponse(getErrorMessage(error), 500)\n }\n },\n\n async cancel(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = getRequiredQueryParam(url, 'runId')\n if (runId instanceof Response) return runId\n\n await durably.cancel(runId)\n\n return successResponse()\n } catch (error) {\n return errorResponse(getErrorMessage(error), 500)\n }\n },\n\n async delete(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = getRequiredQueryParam(url, 'runId')\n if (runId instanceof Response) return runId\n\n await durably.deleteRun(runId)\n\n return successResponse()\n } catch (error) {\n return errorResponse(getErrorMessage(error), 500)\n }\n },\n\n async steps(request: Request): Promise<Response> {\n try {\n const url = new URL(request.url)\n const runId = getRequiredQueryParam(url, 'runId')\n if (runId instanceof Response) return runId\n\n const steps = await durably.storage.getSteps(runId)\n\n return jsonResponse(steps)\n } catch (error) {\n return errorResponse(getErrorMessage(error), 500)\n }\n },\n\n runsSubscribe(request: Request): Response {\n const url = new URL(request.url)\n const jobNameFilter = url.searchParams.get('jobName')\n\n // Helper to check job name filter\n const matchesFilter = (jobName: string) =>\n !jobNameFilter || jobName === jobNameFilter\n\n const sseStream = createSSEStreamFromSubscriptions(\n (ctrl: SSEStreamController) => [\n durably.on('run:trigger', (event) => {\n if (matchesFilter(event.jobName)) {\n ctrl.enqueue({\n type: 'run:trigger',\n runId: event.runId,\n jobName: event.jobName,\n })\n }\n }),\n\n durably.on('run:start', (event) => {\n if (matchesFilter(event.jobName)) {\n ctrl.enqueue({\n type: 'run:start',\n runId: event.runId,\n jobName: event.jobName,\n })\n }\n }),\n\n durably.on('run:complete', (event) => {\n if (matchesFilter(event.jobName)) {\n ctrl.enqueue({\n type: 'run:complete',\n runId: event.runId,\n jobName: event.jobName,\n })\n }\n }),\n\n durably.on('run:fail', (event) => {\n if (matchesFilter(event.jobName)) {\n ctrl.enqueue({\n type: 'run:fail',\n runId: event.runId,\n jobName: event.jobName,\n })\n }\n }),\n\n durably.on('run:cancel', (event) => {\n if (matchesFilter(event.jobName)) {\n ctrl.enqueue({\n type: 'run:cancel',\n runId: event.runId,\n jobName: event.jobName,\n })\n }\n }),\n\n durably.on('run:retry', (event) => {\n if (matchesFilter(event.jobName)) {\n ctrl.enqueue({\n type: 'run:retry',\n runId: event.runId,\n jobName: event.jobName,\n })\n }\n }),\n\n durably.on('run:progress', (event) => {\n if (matchesFilter(event.jobName)) {\n ctrl.enqueue({\n type: 'run:progress',\n runId: event.runId,\n jobName: event.jobName,\n progress: event.progress,\n })\n }\n }),\n\n durably.on('step:start', (event) => {\n if (matchesFilter(event.jobName)) {\n ctrl.enqueue({\n type: 'step:start',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n })\n }\n }),\n\n durably.on('step:complete', (event) => {\n if (matchesFilter(event.jobName)) {\n ctrl.enqueue({\n type: 'step:complete',\n runId: event.runId,\n jobName: event.jobName,\n stepName: event.stepName,\n stepIndex: event.stepIndex,\n })\n }\n }),\n\n durably.on('step:fail', (event) => {\n if (matchesFilter(event.jobName)) {\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 })\n }\n }),\n\n durably.on('log:write', (event) => {\n // log:write doesn't have jobName, so we can't filter by it\n // Send all logs when no filter, or skip if filter is set\n if (!jobNameFilter) {\n ctrl.enqueue({\n type: 'log:write',\n runId: event.runId,\n stepName: event.stepName,\n level: event.level,\n message: event.message,\n data: event.data,\n })\n }\n }),\n ],\n )\n\n return createSSEResponse(sseStream)\n },\n }\n\n return handler\n}\n"],"mappings":"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","names":["prettifyError","prettifyError"]}
@@ -1,8 +1,3 @@
1
- /**
2
- * durably plugins
3
- *
4
- * This package is under development. See https://github.com/coji/durably for updates.
5
- */
6
- declare function withLogPersistence(): unknown;
7
-
8
- export { withLogPersistence };
1
+ export { w as withLogPersistence } from '../index-4aPZWn8r.js';
2
+ import 'kysely';
3
+ import 'zod';