@bratsos/workflow-engine 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.
@@ -13,5 +13,5 @@ var StaleVersionError = class extends Error {
13
13
  };
14
14
 
15
15
  export { StaleVersionError };
16
- //# sourceMappingURL=chunk-2MWO6UVR.js.map
17
- //# sourceMappingURL=chunk-2MWO6UVR.js.map
16
+ //# sourceMappingURL=chunk-2HEV5ZJL.js.map
17
+ //# sourceMappingURL=chunk-2HEV5ZJL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/persistence/interface.ts"],"names":[],"mappings":";AAkDO,IAAM,iBAAA,GAAN,cAAgC,KAAA,CAAM;AAAA,EAC3C,WAAA,CACkB,MAAA,EACA,EAAA,EACA,QAAA,EACA,MAAA,EAChB;AACA,IAAA,KAAA;AAAA,MACE,oBAAoB,MAAM,CAAA,CAAA,EAAI,EAAE,CAAA,WAAA,EAAc,QAAQ,SAAS,MAAM,CAAA;AAAA,KACvE;AAPgB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAKhB,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EACd;AACF","file":"chunk-2HEV5ZJL.js","sourcesContent":["/**\n * Persistence Interfaces for Workflow Engine\n *\n * These interfaces abstract database operations to enable:\n * - Testing with mock implementations\n * - Future extraction into @bratsos/workflow-engine package\n * - Alternative database backends\n *\n * Implementations:\n * - PrismaWorkflowPersistence (default, in ./prisma/)\n * - InMemoryPersistence (for testing)\n */\n\n// ============================================================================\n// Unified Status Type\n// ============================================================================\n\n/**\n * Unified status type for workflows, stages, and jobs.\n *\n * - PENDING: Not started yet\n * - RUNNING: Currently executing\n * - SUSPENDED: Paused, waiting for external event (e.g., batch job completion)\n * - COMPLETED: Finished successfully\n * - FAILED: Finished with error\n * - CANCELLED: Manually stopped by user\n * - SKIPPED: Stage-specific - bypassed due to condition\n */\nexport type Status =\n | \"PENDING\"\n | \"RUNNING\"\n | \"SUSPENDED\"\n | \"COMPLETED\"\n | \"FAILED\"\n | \"CANCELLED\"\n | \"SKIPPED\";\n\n/** @deprecated Use Status instead */\nexport type WorkflowStatus = Status;\n\n/** @deprecated Use Status instead */\nexport type WorkflowStageStatus = Status;\n\n/** @deprecated Use Status instead. Note: PROCESSING is now RUNNING. */\nexport type JobStatus = Status;\n\nexport type LogLevel = \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\";\n\nexport type ArtifactType = \"STAGE_OUTPUT\" | \"ARTIFACT\" | \"METADATA\";\n\nexport class StaleVersionError extends Error {\n constructor(\n public readonly entity: string,\n public readonly id: string,\n public readonly expected: number,\n public readonly actual: number,\n ) {\n super(\n `Stale version on ${entity} ${id}: expected ${expected}, got ${actual}`,\n );\n this.name = \"StaleVersionError\";\n }\n}\n\n// ============================================================================\n// Record Types (minimal fields needed by the workflow engine)\n// ============================================================================\n\nexport interface WorkflowRunRecord {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n version: number;\n workflowId: string;\n workflowName: string;\n workflowType: string;\n status: WorkflowStatus;\n startedAt: Date | null;\n completedAt: Date | null;\n duration: number | null;\n input: unknown;\n output: unknown | null;\n config: unknown;\n totalCost: number;\n totalTokens: number;\n priority: number;\n metadata: unknown | null;\n}\n\nexport interface WorkflowStageRecord {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n version: number;\n workflowRunId: string;\n stageId: string;\n stageName: string;\n stageNumber: number;\n executionGroup: number;\n status: WorkflowStageStatus;\n startedAt: Date | null;\n completedAt: Date | null;\n duration: number | null;\n inputData: unknown | null;\n outputData: unknown | null;\n config: unknown | null;\n suspendedState: unknown | null;\n resumeData: unknown | null;\n nextPollAt: Date | null;\n pollInterval: number | null;\n maxWaitUntil: Date | null;\n metrics: unknown | null;\n embeddingInfo: unknown | null;\n errorMessage: string | null;\n}\n\nexport interface WorkflowLogRecord {\n id: string;\n createdAt: Date;\n workflowStageId: string | null;\n workflowRunId: string | null;\n level: LogLevel;\n message: string;\n metadata: unknown | null;\n}\n\nexport interface WorkflowArtifactRecord {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n workflowRunId: string;\n workflowStageId: string | null;\n key: string;\n type: ArtifactType;\n data: unknown;\n size: number;\n metadata: unknown | null;\n}\n\n// ============================================================================\n// Outbox and Idempotency Record Types (for kernel transactional outbox)\n// ============================================================================\n\nexport interface OutboxRecord {\n id: string;\n workflowRunId: string;\n sequence: number;\n eventType: string;\n payload: unknown;\n causationId: string;\n occurredAt: Date;\n publishedAt: Date | null;\n retryCount: number;\n dlqAt: Date | null;\n}\n\nexport interface CreateOutboxEventInput {\n workflowRunId: string;\n eventType: string;\n payload: unknown;\n causationId: string;\n occurredAt: Date;\n}\n\nexport interface IdempotencyRecord {\n key: string;\n commandType: string;\n result: unknown;\n createdAt: Date;\n}\n\n// ============================================================================\n// AI Call Record Types\n// ============================================================================\n\nexport interface AICallRecord {\n id: string;\n createdAt: Date;\n topic: string;\n callType: string;\n modelKey: string;\n modelId: string;\n prompt: string;\n response: string;\n inputTokens: number;\n outputTokens: number;\n cost: number;\n metadata: unknown | null;\n}\n\nexport interface JobRecord {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n workflowRunId: string;\n workflowId: string;\n stageId: string;\n status: JobStatus;\n priority: number;\n workerId: string | null;\n lockedAt: Date | null;\n startedAt: Date | null;\n completedAt: Date | null;\n attempt: number;\n maxAttempts: number;\n lastError: string | null;\n nextPollAt: Date | null;\n payload: Record<string, unknown>;\n}\n\n// ============================================================================\n// Input Types (for creating/updating records)\n// ============================================================================\n\nexport interface CreateRunInput {\n id?: string;\n workflowId: string;\n workflowName: string;\n workflowType: string;\n input: unknown;\n config?: unknown;\n priority?: number;\n /** Optional metadata stored as JSON on the run record. NOT spread into Prisma fields. */\n metadata?: Record<string, unknown>;\n}\n\nexport interface UpdateRunInput {\n status?: WorkflowStatus;\n startedAt?: Date;\n completedAt?: Date | null;\n duration?: number | null;\n output?: unknown;\n totalCost?: number;\n totalTokens?: number;\n expectedVersion?: number;\n}\n\nexport interface CreateStageInput {\n workflowRunId: string;\n stageId: string;\n stageName: string;\n stageNumber: number;\n executionGroup: number;\n status?: WorkflowStageStatus;\n startedAt?: Date;\n config?: unknown;\n inputData?: unknown;\n}\n\nexport interface UpdateStageInput {\n status?: WorkflowStageStatus;\n startedAt?: Date;\n completedAt?: Date;\n duration?: number;\n outputData?: unknown;\n config?: unknown;\n suspendedState?: unknown;\n resumeData?: unknown;\n nextPollAt?: Date | null;\n pollInterval?: number;\n maxWaitUntil?: Date;\n metrics?: unknown;\n embeddingInfo?: unknown;\n artifacts?: unknown;\n errorMessage?: string;\n expectedVersion?: number;\n}\n\nexport interface UpsertStageInput {\n workflowRunId: string;\n stageId: string;\n create: CreateStageInput;\n update: UpdateStageInput;\n}\n\nexport interface CreateLogInput {\n workflowRunId?: string;\n workflowStageId?: string;\n level: LogLevel;\n message: string;\n metadata?: unknown;\n}\n\nexport interface SaveArtifactInput {\n workflowRunId: string;\n workflowStageId?: string;\n key: string;\n type: ArtifactType;\n data: unknown;\n size: number;\n metadata?: unknown;\n}\n\nexport interface CreateAICallInput {\n topic: string;\n callType: string;\n modelKey: string;\n modelId: string;\n prompt: string;\n response: string;\n inputTokens: number;\n outputTokens: number;\n cost: number;\n metadata?: unknown;\n}\n\nexport interface EnqueueJobInput {\n workflowRunId: string;\n workflowId: string;\n stageId: string;\n priority?: number;\n payload?: Record<string, unknown>;\n scheduledFor?: Date;\n}\n\nexport interface DequeueResult {\n jobId: string;\n workflowRunId: string;\n workflowId: string;\n stageId: string;\n priority: number;\n attempt: number;\n maxAttempts: number;\n payload: Record<string, unknown>;\n}\n\n// ============================================================================\n// WorkflowPersistence Interface\n// ============================================================================\n\nexport interface WorkflowPersistence {\n /** Execute operations within a transaction boundary. */\n withTransaction<T>(fn: (tx: WorkflowPersistence) => Promise<T>): Promise<T>;\n\n // WorkflowRun operations\n createRun(data: CreateRunInput): Promise<WorkflowRunRecord>;\n updateRun(id: string, data: UpdateRunInput): Promise<void>;\n getRun(id: string): Promise<WorkflowRunRecord | null>;\n getRunStatus(id: string): Promise<WorkflowStatus | null>;\n getRunsByStatus(status: WorkflowStatus): Promise<WorkflowRunRecord[]>;\n getStuckRuns(stuckSince: Date): Promise<WorkflowRunRecord[]>;\n\n /**\n * Atomically claim a pending workflow run for processing.\n * Uses atomic update with WHERE status = 'PENDING' to prevent race conditions.\n *\n * @param id - The workflow run ID to claim\n * @returns true if successfully claimed, false if already claimed by another worker\n */\n claimPendingRun(id: string): Promise<boolean>;\n\n /**\n * Atomically find and claim the next pending workflow run.\n * Uses FOR UPDATE SKIP LOCKED pattern (in Postgres) to prevent race conditions\n * when multiple workers try to claim workflows simultaneously.\n *\n * Priority ordering: higher priority first, then oldest (FIFO within same priority).\n *\n * @returns The claimed workflow run (now with status RUNNING), or null if no pending runs\n */\n claimNextPendingRun(): Promise<WorkflowRunRecord | null>;\n\n // WorkflowStage operations\n createStage(data: CreateStageInput): Promise<WorkflowStageRecord>;\n upsertStage(data: UpsertStageInput): Promise<WorkflowStageRecord>;\n updateStage(id: string, data: UpdateStageInput): Promise<void>;\n updateStageByRunAndStageId(\n workflowRunId: string,\n stageId: string,\n data: UpdateStageInput,\n ): Promise<void>;\n getStage(runId: string, stageId: string): Promise<WorkflowStageRecord | null>;\n getStageById(id: string): Promise<WorkflowStageRecord | null>;\n getStagesByRun(\n runId: string,\n options?: { status?: WorkflowStageStatus; orderBy?: \"asc\" | \"desc\" },\n ): Promise<WorkflowStageRecord[]>;\n getSuspendedStages(beforeDate: Date): Promise<WorkflowStageRecord[]>;\n getFirstSuspendedStageReadyToResume(\n runId: string,\n ): Promise<WorkflowStageRecord | null>;\n getFirstFailedStage(runId: string): Promise<WorkflowStageRecord | null>;\n getLastCompletedStage(runId: string): Promise<WorkflowStageRecord | null>;\n getLastCompletedStageBefore(\n runId: string,\n executionGroup: number,\n ): Promise<WorkflowStageRecord | null>;\n deleteStage(id: string): Promise<void>;\n\n // WorkflowLog operations\n createLog(data: CreateLogInput): Promise<void>;\n\n // WorkflowArtifact operations (for StageStorage)\n saveArtifact(data: SaveArtifactInput): Promise<void>;\n loadArtifact(runId: string, key: string): Promise<unknown>;\n hasArtifact(runId: string, key: string): Promise<boolean>;\n deleteArtifact(runId: string, key: string): Promise<void>;\n listArtifacts(runId: string): Promise<WorkflowArtifactRecord[]>;\n getStageIdForArtifact(runId: string, stageId: string): Promise<string | null>;\n\n // Stage output convenience methods (replaces separate StageStorage)\n saveStageOutput(\n runId: string,\n workflowType: string,\n stageId: string,\n output: unknown,\n ): Promise<string>;\n\n // Outbox DLQ operations\n /** Increment retry count for a failed outbox event. Returns new count. */\n incrementOutboxRetryCount(id: string): Promise<number>;\n\n /** Move an outbox event to DLQ (sets dlqAt). */\n moveOutboxEventToDLQ(id: string): Promise<void>;\n\n /** Reset DLQ events so they can be reprocessed by outbox.flush. Returns count reset. */\n replayDLQEvents(maxEvents: number): Promise<number>;\n\n // Outbox operations\n /** Write events to the outbox. Sequences are auto-assigned per workflowRunId. */\n appendOutboxEvents(events: CreateOutboxEventInput[]): Promise<void>;\n\n /** Read unpublished events ordered by (workflowRunId, sequence). */\n getUnpublishedOutboxEvents(limit?: number): Promise<OutboxRecord[]>;\n\n /** Mark events as published. */\n markOutboxEventsPublished(ids: string[]): Promise<void>;\n\n // Idempotency operations\n /** Atomically acquire an idempotency key for command execution. */\n acquireIdempotencyKey(\n key: string,\n commandType: string,\n ): Promise<\n | { status: \"acquired\" }\n | { status: \"replay\"; result: unknown }\n | { status: \"in_progress\" }\n >;\n\n /** Mark an idempotency key as completed and cache the command result. */\n completeIdempotencyKey(\n key: string,\n commandType: string,\n result: unknown,\n ): Promise<void>;\n\n /** Release an in-progress idempotency key after command failure. */\n releaseIdempotencyKey(key: string, commandType: string): Promise<void>;\n}\n\n// ============================================================================\n// AICallLogger Interface\n// ============================================================================\n\nexport interface AIHelperStats {\n totalCalls: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCost: number;\n perModel: Record<\n string,\n { calls: number; inputTokens: number; outputTokens: number; cost: number }\n >;\n}\n\nexport interface AICallLogger {\n /**\n * Log a single AI call (fire and forget)\n */\n logCall(call: CreateAICallInput): void;\n\n /**\n * Log batch results (for recording batch API results)\n */\n logBatchResults(batchId: string, results: CreateAICallInput[]): Promise<void>;\n\n /**\n * Get aggregated stats for a topic prefix\n */\n getStats(topicPrefix: string): Promise<AIHelperStats>;\n\n /**\n * Check if batch results are already recorded\n */\n isRecorded(batchId: string): Promise<boolean>;\n}\n\n// ============================================================================\n// JobQueue Interface\n// ============================================================================\n\nexport interface JobQueue {\n /**\n * Add a new job to the queue\n */\n enqueue(options: EnqueueJobInput): Promise<string>;\n\n /**\n * Enqueue multiple stages in parallel (same execution group)\n */\n enqueueParallel(jobs: EnqueueJobInput[]): Promise<string[]>;\n\n /**\n * Atomically dequeue the next available job\n */\n dequeue(): Promise<DequeueResult | null>;\n\n /**\n * Mark job as completed\n */\n complete(jobId: string): Promise<void>;\n\n /**\n * Mark job as suspended (for async-batch)\n */\n suspend(jobId: string, nextPollAt: Date): Promise<void>;\n\n /**\n * Mark job as failed\n */\n fail(jobId: string, error: string, shouldRetry?: boolean): Promise<void>;\n\n /**\n * Get suspended jobs that are ready to be checked\n */\n getSuspendedJobsReadyToPoll(): Promise<\n Array<{ jobId: string; stageId: string; workflowRunId: string }>\n >;\n\n /**\n * Release stale locks (for crashed workers)\n */\n releaseStaleJobs(staleThresholdMs?: number): Promise<number>;\n\n /**\n * Cancel all pending/suspended jobs for a workflow run.\n * Returns count of cancelled jobs.\n */\n cancelByRun(workflowRunId: string): Promise<number>;\n}\n\n// ============================================================================\n// Default Implementations (lazy loaded to avoid circular deps)\n// ============================================================================\n\n// Re-export from prisma implementations for convenience\n// These will be the default implementations used when no custom persistence is provided\n"]}
@@ -1,4 +1,4 @@
1
- import { StaleVersionError } from './chunk-2MWO6UVR.js';
1
+ import { StaleVersionError } from './chunk-2HEV5ZJL.js';
2
2
  import { z } from 'zod';
3
3
 
4
4
  z.object({
@@ -1437,5 +1437,5 @@ function createPluginRunner(config) {
1437
1437
  }
1438
1438
 
1439
1439
  export { IdempotencyInProgressError, createKernel, createPluginRunner, definePlugin, loadWorkflowContext, saveStageOutput };
1440
- //# sourceMappingURL=chunk-HOGDFLCG.js.map
1441
- //# sourceMappingURL=chunk-HOGDFLCG.js.map
1440
+ //# sourceMappingURL=chunk-5C7LRNM7.js.map
1441
+ //# sourceMappingURL=chunk-5C7LRNM7.js.map