@multiplier-labs/stepflow 0.3.1 → 0.3.3
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/.codekin/reports/comments/2026-04-04_comment-assessment.md +179 -0
- package/.codekin/reports/complexity/2026-04-03_complexity-report.md +152 -0
- package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +94 -0
- package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +10 -0
- package/.codekin/reports/security/2026-04-03_security-audit.md +208 -0
- package/.github/CODEOWNERS +15 -0
- package/.github/dependabot.yml +23 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/publish.yml +11 -5
- package/.github/workflows/security.yml +29 -0
- package/CHANGELOG.md +34 -0
- package/README.md +24 -0
- package/SECURITY.md +40 -0
- package/dist/{chunk-FQCT3I2A.js → chunk-4VOKMGP6.js} +23 -42
- package/dist/chunk-4VOKMGP6.js.map +1 -0
- package/dist/{chunk-XCF4PLN7.js → chunk-GYPNJBDP.js} +12 -16
- package/dist/chunk-GYPNJBDP.js.map +1 -0
- package/dist/{chunk-U5QGN3RT.js → chunk-KHTUWLVZ.js} +14 -47
- package/dist/chunk-KHTUWLVZ.js.map +1 -0
- package/dist/{chunk-GXY66V7Q.js → chunk-ML35PIHX.js} +1 -1
- package/dist/chunk-ML35PIHX.js.map +1 -0
- package/dist/events/index.d.ts +4 -4
- package/dist/events/index.js +1 -1
- package/dist/{index-CPfGsmrp.d.ts → index-Dzr437iW.d.ts} +6 -6
- package/dist/index.d.ts +14 -12
- package/dist/index.js +40 -82
- package/dist/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +4 -4
- package/dist/scheduler/index.js +2 -2
- package/dist/storage/index.d.ts +13 -13
- package/dist/storage/index.js +2 -2
- package/dist/{types-BlwFEGzj.d.ts → types-DSEjiLMj.d.ts} +16 -15
- package/dist/{types-DzkEqnQJ.d.ts → types-DZUdWmCc.d.ts} +2 -2
- package/dist/{types-BVShq7zT.d.ts → types-K5Gjk3H_.d.ts} +4 -4
- package/docs/stepflow-api-reference.md +2 -3
- package/package.json +7 -6
- package/src/core/engine.test.ts +466 -522
- package/src/core/engine.ts +42 -67
- package/src/core/index.ts +3 -3
- package/src/core/orchestrator.ts +64 -83
- package/src/core/types.ts +7 -29
- package/src/events/index.ts +4 -4
- package/src/events/memory.test.ts +39 -41
- package/src/events/memory.ts +7 -10
- package/src/events/socketio.test.ts +112 -162
- package/src/events/socketio.ts +20 -27
- package/src/events/types.ts +16 -19
- package/src/events/webhook.test.ts +197 -235
- package/src/events/webhook.ts +44 -71
- package/src/index.ts +19 -19
- package/src/planning/index.ts +6 -3
- package/src/planning/planner.ts +66 -91
- package/src/planning/planning.test.ts +303 -366
- package/src/planning/registry.test.ts +204 -251
- package/src/planning/registry.ts +39 -43
- package/src/planning/types.ts +23 -27
- package/src/scheduler/cron.test.ts +141 -150
- package/src/scheduler/cron.ts +32 -67
- package/src/scheduler/index.ts +4 -14
- package/src/scheduler/postgres-persistence.ts +71 -99
- package/src/scheduler/sqlite-persistence.test.ts +96 -96
- package/src/scheduler/sqlite-persistence.ts +12 -19
- package/src/scheduler/types.ts +4 -9
- package/src/storage/index.ts +5 -5
- package/src/storage/memory.test.ts +133 -183
- package/src/storage/memory.ts +21 -42
- package/src/storage/postgres.ts +197 -393
- package/src/storage/sqlite.test.ts +154 -281
- package/src/storage/sqlite.ts +50 -85
- package/src/storage/types.ts +33 -58
- package/src/utils/errors.test.ts +78 -80
- package/src/utils/errors.ts +27 -57
- package/src/utils/id.ts +3 -3
- package/src/utils/index.ts +4 -4
- package/src/utils/logger.test.ts +52 -58
- package/src/utils/logger.ts +4 -4
- package/src/utils/postgres-deps.ts +4 -4
- package/src/utils/retry.test.ts +47 -67
- package/src/utils/retry.ts +9 -9
- package/dist/chunk-FQCT3I2A.js.map +0 -1
- package/dist/chunk-GXY66V7Q.js.map +0 -1
- package/dist/chunk-U5QGN3RT.js.map +0 -1
- package/dist/chunk-XCF4PLN7.js.map +0 -1
- package/src/scheduler/postgres-persistence.test.ts +0 -906
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/scheduler/cron.ts","../src/scheduler/sqlite-persistence.ts","../src/scheduler/postgres-persistence.ts"],"sourcesContent":["/**\n * CronScheduler - Scheduler implementation using cron expressions.\n *\n * Provides time-based and workflow-completion-based triggers for workflows.\n */\n\nimport { CronExpressionParser } from 'cron-parser';\nimport type { WorkflowKind, RunStatus, Logger } from '../core/types';\nimport type { WorkflowEngine } from '../core/engine';\nimport type { Scheduler, WorkflowSchedule, TriggerType } from './types';\nimport type { WorkflowEvent } from '../events/types';\nimport { generateId } from '../utils/id';\nimport { ConsoleLogger } from '../utils/logger';\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\n/**\n * Configuration for the CronScheduler.\n */\nexport interface CronSchedulerConfig {\n /** The workflow engine instance */\n engine: WorkflowEngine;\n\n /** Logger instance */\n logger?: Logger;\n\n /** Poll interval for checking schedules (ms, default: 1000) */\n pollInterval?: number;\n\n /** Optional persistence adapter for schedules */\n persistence?: SchedulePersistence;\n}\n\n/**\n * Interface for schedule persistence.\n * Implement this to persist schedules to a database.\n */\nexport interface SchedulePersistence {\n /** Load all schedules from storage */\n loadSchedules(): Promise<WorkflowSchedule[]>;\n\n /** Save a schedule */\n saveSchedule(schedule: WorkflowSchedule): Promise<void>;\n\n /** Update a schedule */\n updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void>;\n\n /** Delete a schedule */\n deleteSchedule(scheduleId: string): Promise<void>;\n}\n\n// ============================================================================\n// CronScheduler Class\n// ============================================================================\n\n/**\n * Scheduler implementation that supports cron expressions and workflow completion triggers.\n *\n * @example\n * ```typescript\n * const scheduler = new CronScheduler({\n * engine,\n * pollInterval: 1000,\n * });\n *\n * // Add a cron schedule (every day at midnight)\n * await scheduler.addSchedule({\n * workflowKind: 'cleanup.daily',\n * triggerType: 'cron',\n * cronExpression: '0 0 * * *',\n * enabled: true,\n * });\n *\n * // Add a workflow completion trigger\n * await scheduler.addSchedule({\n * workflowKind: 'notification.send',\n * triggerType: 'workflow_completed',\n * triggerOnWorkflowKind: 'order.process',\n * triggerOnStatus: ['succeeded'],\n * enabled: true,\n * });\n *\n * await scheduler.start();\n * ```\n */\nexport class CronScheduler implements Scheduler {\n private engine: WorkflowEngine;\n private logger: Logger;\n private pollInterval: number;\n private persistence?: SchedulePersistence;\n\n private schedules = new Map<string, WorkflowSchedule>();\n private running = false;\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private eventUnsubscribe: (() => void) | null = null;\n\n constructor(config: CronSchedulerConfig) {\n this.engine = config.engine;\n this.logger = config.logger ?? new ConsoleLogger();\n this.pollInterval = config.pollInterval ?? 1000;\n this.persistence = config.persistence;\n }\n\n // ============================================================================\n // Scheduler Interface Implementation\n // ============================================================================\n\n /**\n * Start the scheduler.\n * Begins polling for cron schedules and subscribes to workflow completion events.\n */\n async start(): Promise<void> {\n if (this.running) {\n this.logger.warn('Scheduler is already running');\n return;\n }\n\n this.logger.info('Starting scheduler...');\n\n // Load schedules from persistence if available\n if (this.persistence) {\n const loaded = await this.persistence.loadSchedules();\n for (const schedule of loaded) {\n this.schedules.set(schedule.id, schedule);\n }\n this.logger.info(`Loaded ${loaded.length} schedules from persistence`);\n }\n\n // Calculate next run times for cron schedules\n for (const schedule of this.schedules.values()) {\n if (schedule.triggerType === 'cron' && schedule.cronExpression) {\n this.updateNextRunTime(schedule);\n }\n }\n\n // Start polling for cron schedules\n this.pollTimer = setInterval(() => this.checkSchedules(), this.pollInterval);\n\n // Subscribe to workflow completion events\n this.eventUnsubscribe = this.engine.subscribeToAll((event) => {\n this.handleWorkflowEvent(event);\n });\n\n this.running = true;\n this.logger.info('Scheduler started');\n }\n\n /**\n * Stop the scheduler.\n * Stops polling and unsubscribes from events.\n */\n async stop(): Promise<void> {\n if (!this.running) {\n return;\n }\n\n this.logger.info('Stopping scheduler...');\n\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n\n if (this.eventUnsubscribe) {\n this.eventUnsubscribe();\n this.eventUnsubscribe = null;\n }\n\n this.running = false;\n this.logger.info('Scheduler stopped');\n }\n\n /**\n * Add a new schedule.\n */\n async addSchedule(scheduleData: Omit<WorkflowSchedule, 'id'>): Promise<WorkflowSchedule> {\n const schedule: WorkflowSchedule = {\n ...scheduleData,\n id: generateId(),\n };\n\n // Validate cron expression if provided\n if (schedule.triggerType === 'cron' && schedule.cronExpression) {\n try {\n CronExpressionParser.parse(schedule.cronExpression, {\n tz: schedule.timezone,\n });\n } catch (error) {\n throw new Error(`Invalid cron expression: ${schedule.cronExpression}`);\n }\n\n // Calculate next run time\n this.updateNextRunTime(schedule);\n }\n\n // Validate workflow completion trigger\n if (schedule.triggerType === 'workflow_completed') {\n if (!schedule.triggerOnWorkflowKind) {\n throw new Error('triggerOnWorkflowKind is required for workflow_completed triggers');\n }\n }\n\n this.schedules.set(schedule.id, schedule);\n\n // Persist if available\n if (this.persistence) {\n await this.persistence.saveSchedule(schedule);\n }\n\n this.logger.info(`Added schedule: ${schedule.id} (${schedule.workflowKind})`);\n\n return schedule;\n }\n\n /**\n * Remove a schedule.\n */\n async removeSchedule(scheduleId: string): Promise<void> {\n if (!this.schedules.has(scheduleId)) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n this.schedules.delete(scheduleId);\n\n if (this.persistence) {\n await this.persistence.deleteSchedule(scheduleId);\n }\n\n this.logger.info(`Removed schedule: ${scheduleId}`);\n }\n\n /**\n * Update a schedule.\n */\n async updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void> {\n const schedule = this.schedules.get(scheduleId);\n if (!schedule) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n // Don't allow changing the ID\n delete updates.id;\n\n // Update the schedule\n Object.assign(schedule, updates);\n\n // Revalidate and update next run time if cron expression changed\n if (updates.cronExpression !== undefined || updates.timezone !== undefined) {\n if (schedule.cronExpression) {\n try {\n CronExpressionParser.parse(schedule.cronExpression, {\n tz: schedule.timezone,\n });\n this.updateNextRunTime(schedule);\n } catch (error) {\n throw new Error(`Invalid cron expression: ${schedule.cronExpression}`);\n }\n }\n }\n\n if (this.persistence) {\n await this.persistence.updateSchedule(scheduleId, updates);\n }\n\n this.logger.debug(`Updated schedule: ${scheduleId}`);\n }\n\n /**\n * Get all schedules.\n */\n async getSchedules(): Promise<WorkflowSchedule[]> {\n return Array.from(this.schedules.values());\n }\n\n /**\n * Get a schedule by ID.\n */\n getSchedule(scheduleId: string): WorkflowSchedule | undefined {\n return this.schedules.get(scheduleId);\n }\n\n /**\n * Manually trigger a scheduled workflow.\n */\n async triggerNow(scheduleId: string): Promise<string> {\n const schedule = this.schedules.get(scheduleId);\n if (!schedule) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n return this.executeSchedule(schedule);\n }\n\n // ============================================================================\n // Internal Methods\n // ============================================================================\n\n /**\n * Check all cron schedules and execute those that are due.\n */\n private checkSchedules(): void {\n const now = new Date();\n\n for (const schedule of this.schedules.values()) {\n if (!schedule.enabled) continue;\n if (schedule.triggerType !== 'cron') continue;\n if (!schedule.nextRunAt) continue;\n\n if (schedule.nextRunAt <= now) {\n // Execute the schedule\n this.executeSchedule(schedule).catch((error) => {\n this.logger.error(`Failed to execute schedule ${schedule.id}:`, error);\n });\n\n // Update next run time\n this.updateNextRunTime(schedule);\n }\n }\n }\n\n /**\n * Handle workflow events for completion triggers.\n */\n private handleWorkflowEvent(event: WorkflowEvent): void {\n // Only handle run completion events\n // Note: orchestrator emits 'run.completed' for success, 'run.failed' for failure\n if (event.eventType !== 'run.completed' && event.eventType !== 'run.failed') {\n return;\n }\n\n const completedStatus: RunStatus = event.eventType === 'run.completed' ? 'succeeded' : 'failed';\n const completedKind = event.kind;\n\n // Find schedules triggered by this workflow completion\n for (const schedule of this.schedules.values()) {\n if (!schedule.enabled) continue;\n if (schedule.triggerType !== 'workflow_completed') continue;\n if (schedule.triggerOnWorkflowKind !== completedKind) continue;\n\n // Check if the status matches\n if (schedule.triggerOnStatus && !schedule.triggerOnStatus.includes(completedStatus)) {\n continue;\n }\n\n // Execute the schedule\n this.executeSchedule(schedule, {\n triggerRunId: event.runId,\n triggerStatus: completedStatus,\n }).catch((error) => {\n this.logger.error(`Failed to execute schedule ${schedule.id}:`, error);\n });\n }\n }\n\n /**\n * Execute a schedule by starting the workflow.\n */\n private async executeSchedule(\n schedule: WorkflowSchedule,\n triggerContext?: { triggerRunId?: string; triggerStatus?: RunStatus }\n ): Promise<string> {\n this.logger.info(`Executing schedule: ${schedule.id} (${schedule.workflowKind})`);\n\n // Build metadata with trigger info\n const metadata = {\n ...schedule.metadata,\n scheduleId: schedule.id,\n triggerType: schedule.triggerType,\n ...(triggerContext ?? {}),\n };\n\n // Start the workflow\n const runId = await this.engine.startRun({\n kind: schedule.workflowKind,\n input: schedule.input,\n metadata,\n });\n\n // Update schedule state\n schedule.lastRunAt = new Date();\n schedule.lastRunId = runId;\n\n if (this.persistence) {\n await this.persistence.updateSchedule(schedule.id, {\n lastRunAt: schedule.lastRunAt,\n lastRunId: schedule.lastRunId,\n nextRunAt: schedule.nextRunAt,\n });\n }\n\n return runId;\n }\n\n /**\n * Update the next run time for a cron schedule.\n */\n private updateNextRunTime(schedule: WorkflowSchedule): void {\n if (!schedule.cronExpression) return;\n\n try {\n const interval = CronExpressionParser.parse(schedule.cronExpression, {\n currentDate: new Date(),\n tz: schedule.timezone,\n });\n\n schedule.nextRunAt = interval.next().toDate();\n } catch (error) {\n this.logger.error(`Failed to parse cron expression for schedule ${schedule.id}:`, error);\n schedule.nextRunAt = undefined;\n }\n }\n}\n","/**\n * SQLite persistence adapter for schedules.\n *\n * Stores workflow schedules in a SQLite database table.\n */\n\nimport type { Database, Statement } from 'better-sqlite3';\nimport type { WorkflowSchedule } from './types';\nimport type { SchedulePersistence } from './cron';\nimport { generateId } from '../utils/id';\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\n/**\n * Configuration for SQLiteSchedulePersistence.\n */\nexport interface SQLiteSchedulePersistenceConfig {\n /** SQLite database instance */\n db: Database;\n\n /** Table name for schedules (default: workflow_schedules) */\n tableName?: string;\n}\n\n// ============================================================================\n// SQLiteSchedulePersistence Class\n// ============================================================================\n\n/**\n * SQLite-based persistence for workflow schedules.\n *\n * @example\n * ```typescript\n * import Database from 'better-sqlite3';\n *\n * const db = new Database('workflow.db');\n * const persistence = new SQLiteSchedulePersistence({ db });\n *\n * const scheduler = new CronScheduler({\n * engine,\n * persistence,\n * });\n * ```\n */\nexport class SQLiteSchedulePersistence implements SchedulePersistence {\n private db: Database;\n private tableName: string;\n private stmts: {\n insert: Statement;\n update: Statement;\n delete: Statement;\n getAll: Statement;\n getById: Statement;\n } | null = null;\n\n constructor(config: SQLiteSchedulePersistenceConfig) {\n this.db = config.db;\n this.tableName = config.tableName ?? 'workflow_schedules';\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(this.tableName)) {\n throw new Error(`Invalid table name: ${this.tableName}`);\n }\n this.initializeDatabase();\n }\n\n // ============================================================================\n // Database Initialization\n // ============================================================================\n\n private initializeDatabase(): void {\n // Create schedules table.\n // Schema layout:\n // id — unique schedule identifier (TEXT PK)\n // workflow_kind — target workflow type to spawn\n // trigger_type — 'cron', 'workflow_completed', or 'manual'\n // cron_expression — cron expression for time-based triggers (nullable)\n // timezone — IANA timezone for cron evaluation (nullable)\n // trigger_on_* — fields for workflow-completion triggers\n // input / metadata — JSON-serialized payloads for spawned runs\n // enabled — whether this schedule is active (0/1)\n // last_run_at/id — most recent execution tracking\n // next_run_at — next scheduled execution time\n // created_at/updated_at — audit timestamps\n // No migration strategy is currently implemented; table is created idempotently.\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n id TEXT PRIMARY KEY,\n workflow_kind TEXT NOT NULL,\n trigger_type TEXT NOT NULL,\n cron_expression TEXT,\n timezone TEXT,\n trigger_on_workflow_kind TEXT,\n trigger_on_status TEXT,\n input TEXT,\n metadata TEXT,\n enabled INTEGER NOT NULL DEFAULT 1,\n last_run_at TEXT,\n last_run_id TEXT,\n next_run_at TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `);\n\n // Create indexes\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_workflow_kind\n ON ${this.tableName}(workflow_kind);\n\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_enabled\n ON ${this.tableName}(enabled);\n\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_trigger_type\n ON ${this.tableName}(trigger_type);\n `);\n\n // Prepare statements\n this.stmts = {\n insert: this.db.prepare(`\n INSERT INTO ${this.tableName} (\n id, workflow_kind, trigger_type, cron_expression, timezone,\n trigger_on_workflow_kind, trigger_on_status, input, metadata,\n enabled, last_run_at, last_run_id, next_run_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `),\n update: this.db.prepare(`\n UPDATE ${this.tableName}\n SET workflow_kind = COALESCE(?, workflow_kind),\n trigger_type = COALESCE(?, trigger_type),\n cron_expression = ?,\n timezone = ?,\n trigger_on_workflow_kind = ?,\n trigger_on_status = ?,\n input = ?,\n metadata = ?,\n enabled = COALESCE(?, enabled),\n last_run_at = ?,\n last_run_id = ?,\n next_run_at = ?,\n updated_at = datetime('now')\n WHERE id = ?\n `),\n delete: this.db.prepare(`\n DELETE FROM ${this.tableName} WHERE id = ?\n `),\n getAll: this.db.prepare(`\n SELECT * FROM ${this.tableName}\n `),\n getById: this.db.prepare(`\n SELECT * FROM ${this.tableName} WHERE id = ?\n `),\n };\n }\n\n // ============================================================================\n // SchedulePersistence Interface Implementation\n // ============================================================================\n\n async loadSchedules(): Promise<WorkflowSchedule[]> {\n const rows = this.stmts!.getAll.all() as ScheduleRow[];\n return rows.map(row => this.rowToSchedule(row));\n }\n\n async saveSchedule(schedule: WorkflowSchedule): Promise<void> {\n this.stmts!.insert.run(\n schedule.id,\n schedule.workflowKind,\n schedule.triggerType,\n schedule.cronExpression ?? null,\n schedule.timezone ?? null,\n schedule.triggerOnWorkflowKind ?? null,\n schedule.triggerOnStatus ? JSON.stringify(schedule.triggerOnStatus) : null,\n schedule.input ? JSON.stringify(schedule.input) : null,\n schedule.metadata ? JSON.stringify(schedule.metadata) : null,\n schedule.enabled ? 1 : 0,\n schedule.lastRunAt?.toISOString() ?? null,\n schedule.lastRunId ?? null,\n schedule.nextRunAt?.toISOString() ?? null\n );\n }\n\n async updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void> {\n // Get existing schedule to merge with updates\n const existing = this.stmts!.getById.get(scheduleId) as ScheduleRow | undefined;\n if (!existing) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n const merged = {\n ...this.rowToSchedule(existing),\n ...updates,\n };\n\n this.stmts!.update.run(\n updates.workflowKind ?? null,\n updates.triggerType ?? null,\n merged.cronExpression ?? null,\n merged.timezone ?? null,\n merged.triggerOnWorkflowKind ?? null,\n merged.triggerOnStatus ? JSON.stringify(merged.triggerOnStatus) : null,\n merged.input ? JSON.stringify(merged.input) : null,\n merged.metadata ? JSON.stringify(merged.metadata) : null,\n updates.enabled !== undefined ? (updates.enabled ? 1 : 0) : null,\n merged.lastRunAt?.toISOString() ?? null,\n merged.lastRunId ?? null,\n merged.nextRunAt?.toISOString() ?? null,\n scheduleId\n );\n }\n\n async deleteSchedule(scheduleId: string): Promise<void> {\n this.stmts!.delete.run(scheduleId);\n }\n\n // ============================================================================\n // Helper Methods\n // ============================================================================\n\n private rowToSchedule(row: ScheduleRow): WorkflowSchedule {\n return {\n id: row.id,\n workflowKind: row.workflow_kind,\n triggerType: row.trigger_type as WorkflowSchedule['triggerType'],\n cronExpression: row.cron_expression ?? undefined,\n timezone: row.timezone ?? undefined,\n triggerOnWorkflowKind: row.trigger_on_workflow_kind ?? undefined,\n triggerOnStatus: row.trigger_on_status\n ? JSON.parse(row.trigger_on_status)\n : undefined,\n input: row.input ? JSON.parse(row.input) : undefined,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n enabled: row.enabled === 1,\n lastRunAt: row.last_run_at ? new Date(row.last_run_at) : undefined,\n lastRunId: row.last_run_id ?? undefined,\n nextRunAt: row.next_run_at ? new Date(row.next_run_at) : undefined,\n };\n }\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface ScheduleRow {\n id: string;\n workflow_kind: string;\n trigger_type: string;\n cron_expression: string | null;\n timezone: string | null;\n trigger_on_workflow_kind: string | null;\n trigger_on_status: string | null;\n input: string | null;\n metadata: string | null;\n enabled: number;\n last_run_at: string | null;\n last_run_id: string | null;\n next_run_at: string | null;\n created_at: string;\n updated_at: string;\n}\n","/**\n * PostgreSQL persistence adapter for schedules.\n *\n * Stores workflow schedules in a PostgreSQL database table using Kysely.\n */\n\nimport type { Kysely as KyselyType } from 'kysely';\nimport type { Pool, PoolConfig } from 'pg';\nimport { loadPostgresDeps } from '../utils/postgres-deps.js';\n\n// Lazy-loaded dependencies - populated by loadPostgresDeps() during initialize()\nlet Kysely: any;\nlet PostgresDialect: any;\nlet sql: any;\nlet pgModule: any;\nimport type { WorkflowSchedule } from './types.js';\nimport type { SchedulePersistence } from './cron.js';\n\n// ============================================================================\n// Database Types (Kysely schema)\n// ============================================================================\n\n/** Kysely row type for the workflow_schedules table. */\ninterface WorkflowSchedulesTable {\n id: string;\n /** Workflow kind this schedule targets. */\n workflow_kind: string;\n /** Trigger type: 'cron', 'workflow_completed', or 'manual'. */\n trigger_type: string;\n /** Cron expression for time-based triggers, null for non-cron schedules. */\n cron_expression: string | null;\n /** IANA timezone for cron evaluation, null defaults to UTC. */\n timezone: string | null;\n /** Workflow kind that triggers on completion, null for non-completion triggers. */\n trigger_on_workflow_kind: string | null;\n /** JSON-serialized array of RunStatus values that activate the trigger. */\n trigger_on_status: string | null;\n /** JSON-serialized input payload for spawned runs. */\n input_json: string | null;\n /** JSON-serialized arbitrary metadata for spawned runs. */\n metadata_json: string | null;\n /** Whether this schedule is active. */\n enabled: boolean;\n /** Timestamp of the most recent run spawned by this schedule. */\n last_run_at: Date | null;\n /** Run ID of the most recent run spawned by this schedule. */\n last_run_id: string | null;\n /** Next scheduled execution time. */\n next_run_at: Date | null;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface SchedulesDatabase {\n workflow_schedules: WorkflowSchedulesTable;\n}\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\n/**\n * Configuration for PostgresSchedulePersistence.\n */\nexport interface PostgresSchedulePersistenceConfig {\n /**\n * PostgreSQL connection string.\n * Example: \"postgresql://user:pass@localhost:5432/dbname\"\n */\n connectionString?: string;\n\n /**\n * Existing pg.Pool instance for connection sharing with application.\n * If provided, the adapter will not close this pool on close().\n */\n pool?: Pool;\n\n /**\n * Pool configuration options (if not providing pool or connectionString).\n */\n poolConfig?: PoolConfig;\n\n /**\n * Schema name for the schedules table.\n * @default 'public'\n */\n schema?: string;\n\n /**\n * Table name for schedules.\n * @default 'workflow_schedules'\n */\n tableName?: string;\n\n /**\n * Automatically create tables on initialization.\n * @default true\n */\n autoMigrate?: boolean;\n}\n\n// ============================================================================\n// PostgresSchedulePersistence Class\n// ============================================================================\n\n/**\n * PostgreSQL-based persistence for workflow schedules.\n *\n * @example\n * ```typescript\n * const persistence = new PostgresSchedulePersistence({\n * connectionString: process.env.DATABASE_URL,\n * });\n *\n * await persistence.initialize();\n *\n * const scheduler = new CronScheduler({\n * engine,\n * persistence,\n * });\n * ```\n *\n * @example Sharing connection pool\n * ```typescript\n * import pg from 'pg';\n *\n * const pool = new pg.Pool({\n * connectionString: process.env.DATABASE_URL,\n * });\n *\n * const persistence = new PostgresSchedulePersistence({ pool });\n * await persistence.initialize();\n * ```\n */\nexport class PostgresSchedulePersistence implements SchedulePersistence {\n private db!: KyselyType<SchedulesDatabase>;\n private pool!: Pool;\n private ownsPool = false;\n private schema: string;\n private tableName: string;\n private autoMigrate: boolean;\n private initialized = false;\n private config: PostgresSchedulePersistenceConfig;\n\n constructor(config: PostgresSchedulePersistenceConfig) {\n this.schema = config.schema ?? 'public';\n if (!/^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/.test(this.schema)) {\n throw new Error(\n `Invalid schema name \"${this.schema}\". Schema must start with a letter or underscore, ` +\n `contain only alphanumeric characters and underscores, and be at most 63 characters.`\n );\n }\n this.tableName = config.tableName ?? 'workflow_schedules';\n if (!/^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/.test(this.tableName)) {\n throw new Error(\n `Invalid table name \"${this.tableName}\". Table name must start with a letter or underscore, ` +\n `contain only alphanumeric characters and underscores, and be at most 63 characters.`\n );\n }\n this.autoMigrate = config.autoMigrate !== false;\n this.config = config;\n }\n\n /**\n * Get a schema-scoped query builder.\n * All queries MUST use this instead of this.db directly to respect config.schema.\n */\n private get qb() {\n return this.db.withSchema(this.schema);\n }\n\n private ensureInitialized(): void {\n if (!this.initialized) {\n throw new Error(\n 'PostgresSchedulePersistence is not initialized. Call initialize() before using the adapter.'\n );\n }\n }\n\n /**\n * Initialize the persistence layer.\n * Creates the schedules table if autoMigrate is enabled.\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n const deps = await loadPostgresDeps();\n Kysely = deps.Kysely;\n PostgresDialect = deps.PostgresDialect;\n sql = deps.sql;\n pgModule = deps.pgModule;\n\n if (this.config.pool) {\n this.pool = this.config.pool;\n this.ownsPool = false;\n } else if (this.config.connectionString) {\n this.pool = new pgModule.Pool({ connectionString: this.config.connectionString });\n this.ownsPool = true;\n } else if (this.config.poolConfig) {\n this.pool = new pgModule.Pool(this.config.poolConfig);\n this.ownsPool = true;\n } else {\n throw new Error(\n 'PostgresSchedulePersistenceConfig must include either pool, connectionString, or poolConfig'\n );\n }\n\n this.db = new Kysely({\n dialect: new PostgresDialect({\n pool: this.pool,\n }),\n });\n\n if (this.autoMigrate) {\n await this.createTables();\n }\n\n this.initialized = true;\n }\n\n /**\n * Close the database connection.\n * Only closes the pool if it was created by this adapter.\n */\n async close(): Promise<void> {\n await this.db.destroy();\n if (this.ownsPool) {\n await this.pool.end();\n }\n }\n\n // ============================================================================\n // Database Initialization\n // ============================================================================\n\n private async createTables(): Promise<void> {\n const fullTableName = this.schema === 'public'\n ? this.tableName\n : `${this.schema}.${this.tableName}`;\n\n // Create schema if not public\n if (this.schema !== 'public') {\n await sql`CREATE SCHEMA IF NOT EXISTS ${sql.ref(this.schema)}`.execute(this.db);\n }\n\n // Create schedules table.\n // Schema layout:\n // id — unique schedule identifier (TEXT PK)\n // workflow_kind — target workflow type to spawn\n // trigger_type — constrained to 'cron' | 'workflow_completed' | 'manual'\n // cron_expression / timezone — cron trigger configuration\n // trigger_on_workflow_kind — completion trigger source kind\n // trigger_on_status — JSONB array of RunStatus values for completion triggers\n // input_json / metadata_json — JSONB payloads for spawned runs\n // enabled — whether this schedule is active\n // last_run_at / last_run_id — most recent execution tracking\n // next_run_at — next scheduled execution time\n // created_at / updated_at — audit timestamps (TIMESTAMPTZ)\n // No migration strategy is currently implemented; table is created idempotently.\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql.table(fullTableName)} (\n id TEXT PRIMARY KEY,\n workflow_kind TEXT NOT NULL,\n trigger_type TEXT NOT NULL,\n cron_expression TEXT,\n timezone TEXT DEFAULT 'UTC',\n trigger_on_workflow_kind TEXT,\n trigger_on_status JSONB,\n input_json JSONB NOT NULL DEFAULT '{}',\n metadata_json JSONB NOT NULL DEFAULT '{}',\n enabled BOOLEAN NOT NULL DEFAULT true,\n last_run_at TIMESTAMPTZ,\n last_run_id TEXT,\n next_run_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n CONSTRAINT ${sql.ref(`${this.tableName}_trigger_type_check`)} CHECK (\n trigger_type IN ('cron', 'workflow_completed', 'manual')\n )\n )\n `.execute(this.db);\n\n // Create indexes\n await sql`\n CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${this.tableName}_workflow_kind`)}\n ON ${sql.table(fullTableName)} (workflow_kind)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${this.tableName}_enabled`)}\n ON ${sql.table(fullTableName)} (enabled)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${this.tableName}_trigger_type`)}\n ON ${sql.table(fullTableName)} (trigger_type)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${this.tableName}_next_run`)}\n ON ${sql.table(fullTableName)} (next_run_at)\n WHERE enabled = true\n `.execute(this.db);\n }\n\n // ============================================================================\n // SchedulePersistence Interface Implementation\n // ============================================================================\n\n async loadSchedules(): Promise<WorkflowSchedule[]> {\n this.ensureInitialized();\n const rows = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .execute() as WorkflowSchedulesTable[];\n\n return rows.map(row => this.rowToSchedule(row));\n }\n\n async saveSchedule(schedule: WorkflowSchedule): Promise<void> {\n this.ensureInitialized();\n await this.qb\n .insertInto(this.tableName as any)\n .values({\n id: schedule.id,\n workflow_kind: schedule.workflowKind,\n trigger_type: schedule.triggerType,\n cron_expression: schedule.cronExpression ?? null,\n timezone: schedule.timezone ?? null,\n trigger_on_workflow_kind: schedule.triggerOnWorkflowKind ?? null,\n trigger_on_status: schedule.triggerOnStatus ? JSON.stringify(schedule.triggerOnStatus) : null,\n input_json: schedule.input ? JSON.stringify(schedule.input) : null,\n metadata_json: schedule.metadata ? JSON.stringify(schedule.metadata) : null,\n enabled: schedule.enabled,\n last_run_at: schedule.lastRunAt ?? null,\n last_run_id: schedule.lastRunId ?? null,\n next_run_at: schedule.nextRunAt ?? null,\n created_at: new Date(),\n updated_at: new Date(),\n })\n .execute();\n }\n\n async updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void> {\n this.ensureInitialized();\n // Partial update pattern: fetch the existing row, merge with incoming updates,\n // then build a column-level update object. Each field is only included if it was\n // explicitly provided in `updates` (or affected by the merge), so unchanged\n // columns are left untouched in the database.\n const existing = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('id', '=', scheduleId)\n .executeTakeFirst() as WorkflowSchedulesTable | undefined;\n\n if (!existing) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n const merged = {\n ...this.rowToSchedule(existing),\n ...updates,\n };\n\n // Field mapping: { domainKey, dbKey, serialize? }\n // Each entry maps a WorkflowSchedule field to its DB column, with optional serialization.\n const fieldMappings: Array<{\n domainKey: keyof WorkflowSchedule;\n dbKey: keyof WorkflowSchedulesTable;\n serialize?: (value: any) => any;\n }> = [\n { domainKey: 'workflowKind', dbKey: 'workflow_kind' },\n { domainKey: 'triggerType', dbKey: 'trigger_type' },\n { domainKey: 'cronExpression', dbKey: 'cron_expression' },\n { domainKey: 'timezone', dbKey: 'timezone' },\n { domainKey: 'triggerOnWorkflowKind', dbKey: 'trigger_on_workflow_kind' },\n { domainKey: 'triggerOnStatus', dbKey: 'trigger_on_status', serialize: v => v ? JSON.stringify(v) : null },\n { domainKey: 'input', dbKey: 'input_json', serialize: v => v ? JSON.stringify(v) : null },\n { domainKey: 'metadata', dbKey: 'metadata_json', serialize: v => v ? JSON.stringify(v) : null },\n { domainKey: 'enabled', dbKey: 'enabled' },\n { domainKey: 'lastRunAt', dbKey: 'last_run_at' },\n { domainKey: 'lastRunId', dbKey: 'last_run_id' },\n { domainKey: 'nextRunAt', dbKey: 'next_run_at' },\n ];\n\n const updateData: Partial<WorkflowSchedulesTable> = {\n updated_at: new Date(),\n };\n\n for (const { domainKey, dbKey, serialize } of fieldMappings) {\n if (updates[domainKey] !== undefined || merged[domainKey] !== undefined) {\n const value = serialize\n ? serialize(merged[domainKey])\n : (merged[domainKey] ?? null);\n (updateData as any)[dbKey] = value;\n }\n }\n\n await this.qb\n .updateTable(this.tableName as any)\n .set(updateData)\n .where('id', '=', scheduleId)\n .execute();\n }\n\n async deleteSchedule(scheduleId: string): Promise<void> {\n this.ensureInitialized();\n await this.qb\n .deleteFrom(this.tableName as any)\n .where('id', '=', scheduleId)\n .execute();\n }\n\n // ============================================================================\n // Additional Methods\n // ============================================================================\n\n /**\n * Get a schedule by ID.\n */\n async getSchedule(scheduleId: string): Promise<WorkflowSchedule | null> {\n this.ensureInitialized();\n const row = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('id', '=', scheduleId)\n .executeTakeFirst() as WorkflowSchedulesTable | undefined;\n\n return row ? this.rowToSchedule(row) : null;\n }\n\n /**\n * Get all enabled schedules that are due to run.\n */\n async getDueSchedules(): Promise<WorkflowSchedule[]> {\n this.ensureInitialized();\n const now = new Date();\n\n const rows = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('enabled', '=', true)\n .where('trigger_type', '=', 'cron')\n .where('next_run_at', '<=', now)\n .execute() as WorkflowSchedulesTable[];\n\n return rows.map(row => this.rowToSchedule(row));\n }\n\n /**\n * Get schedules by workflow kind.\n */\n async getSchedulesByWorkflowKind(workflowKind: string): Promise<WorkflowSchedule[]> {\n this.ensureInitialized();\n const rows = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('workflow_kind', '=', workflowKind)\n .execute() as WorkflowSchedulesTable[];\n\n return rows.map(row => this.rowToSchedule(row));\n }\n\n /**\n * Get workflow completion triggers for a specific workflow kind.\n */\n async getCompletionTriggers(triggerOnWorkflowKind: string): Promise<WorkflowSchedule[]> {\n this.ensureInitialized();\n const rows = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('enabled', '=', true)\n .where('trigger_type', '=', 'workflow_completed')\n .where('trigger_on_workflow_kind', '=', triggerOnWorkflowKind)\n .execute() as WorkflowSchedulesTable[];\n\n return rows.map(row => this.rowToSchedule(row));\n }\n\n // ============================================================================\n // Helper Methods\n // ============================================================================\n\n private rowToSchedule(row: WorkflowSchedulesTable): WorkflowSchedule {\n return {\n id: row.id,\n workflowKind: row.workflow_kind,\n triggerType: row.trigger_type as WorkflowSchedule['triggerType'],\n cronExpression: row.cron_expression ?? undefined,\n timezone: row.timezone ?? undefined,\n triggerOnWorkflowKind: row.trigger_on_workflow_kind ?? undefined,\n triggerOnStatus: row.trigger_on_status\n ? (typeof row.trigger_on_status === 'string'\n ? JSON.parse(row.trigger_on_status)\n : row.trigger_on_status)\n : undefined,\n input: row.input_json\n ? (typeof row.input_json === 'string'\n ? JSON.parse(row.input_json)\n : row.input_json)\n : undefined,\n metadata: row.metadata_json\n ? (typeof row.metadata_json === 'string'\n ? JSON.parse(row.metadata_json)\n : row.metadata_json)\n : undefined,\n enabled: row.enabled,\n lastRunAt: row.last_run_at ? new Date(row.last_run_at) : undefined,\n lastRunId: row.last_run_id ?? undefined,\n nextRunAt: row.next_run_at ? new Date(row.next_run_at) : undefined,\n };\n }\n}\n\n// Convenience alias for shorter naming\nexport type { PostgresSchedulePersistenceConfig as PostgresSchedulePersistenceOptions };\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/id.ts","../src/utils/logger.ts","../src/utils/postgres-deps.ts"],"sourcesContent":["/**\n * ID generation utilities for the workflow engine.\n */\n\nimport { randomBytes } from 'node:crypto';\n\n/**\n * Generate a unique ID for database records.\n * Uses a ULID-like format: base36 timestamp + random suffix.\n * This provides:\n * - Rough time-ordering (useful for debugging)\n * - High collision resistance\n * - URL-safe characters\n *\n * @returns A unique identifier string (approx 16 characters)\n */\nexport function generateId(): string {\n const timestamp = Date.now().toString(36);\n const randomPart = randomBytes(6).toString('base64url');\n return `${timestamp}${randomPart}`;\n}\n","/**\n * Logger utilities for the workflow engine.\n */\n\nimport type { Logger, WorkflowError } from '../core/types';\n\n/** Log level for ConsoleLogger. Levels are ordered: debug < info < warn < error. */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Console-based logger implementation.\n * Uses console.log with prefixes for different levels.\n * Supports a configurable minimum log level (default: 'info').\n */\nexport class ConsoleLogger implements Logger {\n private prefix: string;\n private minLevel: number;\n\n constructor(prefix = '[workflow]', level: LogLevel = 'info') {\n this.prefix = prefix;\n this.minLevel = LOG_LEVEL_PRIORITY[level];\n }\n\n debug(message: string, ...args: unknown[]): void {\n if (this.minLevel <= LOG_LEVEL_PRIORITY.debug) {\n console.debug(`${this.prefix} [DEBUG]`, message, ...args);\n }\n }\n\n info(message: string, ...args: unknown[]): void {\n if (this.minLevel <= LOG_LEVEL_PRIORITY.info) {\n console.info(`${this.prefix} [INFO]`, message, ...args);\n }\n }\n\n warn(message: string, ...args: unknown[]): void {\n if (this.minLevel <= LOG_LEVEL_PRIORITY.warn) {\n console.warn(`${this.prefix} [WARN]`, message, ...args);\n }\n }\n\n error(message: string, ...args: unknown[]): void {\n if (this.minLevel <= LOG_LEVEL_PRIORITY.error) {\n console.error(`${this.prefix} [ERROR]`, message, ...args);\n }\n }\n}\n\n/**\n * Silent logger that does nothing.\n * Useful for testing or when logs are not wanted.\n */\nexport class SilentLogger implements Logger {\n debug(): void {}\n info(): void {}\n warn(): void {}\n error(): void {}\n}\n\n/**\n * Strip stack traces from a WorkflowError before persisting to storage.\n * Stack traces expose internal file paths and should be kept in logs only.\n */\nexport function sanitizeErrorForStorage(error: WorkflowError): WorkflowError {\n const { stack: _stack, ...rest } = error;\n return rest;\n}\n\n/**\n * Create a scoped logger that includes run/step context.\n */\nexport function createScopedLogger(\n logger: Logger,\n runId: string,\n stepKey?: string\n): Logger {\n const prefix = stepKey ? `[run:${runId}][step:${stepKey}]` : `[run:${runId}]`;\n\n return {\n debug: (message: string, ...args: unknown[]) =>\n logger.debug(`${prefix} ${message}`, ...args),\n info: (message: string, ...args: unknown[]) =>\n logger.info(`${prefix} ${message}`, ...args),\n warn: (message: string, ...args: unknown[]) =>\n logger.warn(`${prefix} ${message}`, ...args),\n error: (message: string, ...args: unknown[]) =>\n logger.error(`${prefix} ${message}`, ...args),\n };\n}\n","/**\n * Shared lazy-loader for PostgreSQL dependencies (kysely + pg).\n *\n * Both `PostgresStorageAdapter` and `PostgresSchedulePersistence` need the same\n * set of optional peer dependencies. This module loads them once and caches the\n * result so the logic isn't duplicated across files.\n */\n\n/**\n * Dynamic imports prevent static type resolution for these modules;\n * `any` is intentional here — callers cast to the appropriate Kysely/pg types.\n */\nexport interface PostgresDeps {\n Kysely: any;\n PostgresDialect: any;\n sql: any;\n pgModule: any;\n}\n\nlet cached: PostgresDeps | undefined;\nlet loadingPromise: Promise<PostgresDeps> | undefined;\n\n/**\n * Lazily load and cache the PostgreSQL peer dependencies (kysely + pg).\n * @returns The cached `{Kysely, PostgresDialect, sql, pgModule}` object.\n * @throws {Error} If the `kysely` optional peer dependency is not installed.\n * @throws {Error} If the `pg` optional peer dependency is not installed.\n */\nexport async function loadPostgresDeps(): Promise<PostgresDeps> {\n if (cached) return cached;\n if (loadingPromise) return loadingPromise;\n\n loadingPromise = (async () => {\n let Kysely: any;\n let PostgresDialect: any;\n let sql: any;\n let pgModule: any;\n\n try {\n const kyselyMod = await import('kysely');\n Kysely = kyselyMod.Kysely;\n PostgresDialect = kyselyMod.PostgresDialect;\n sql = kyselyMod.sql;\n } catch {\n throw new Error(\n 'PostgreSQL support requires the \"kysely\" package. Install it with: npm install kysely'\n );\n }\n try {\n const pg = await import('pg');\n pgModule = pg.default ?? pg;\n } catch {\n throw new Error(\n 'PostgreSQL support requires the \"pg\" package. Install it with: npm install pg'\n );\n }\n\n cached = { Kysely, PostgresDialect, sql, pgModule };\n return cached;\n })();\n\n return loadingPromise;\n}\n"],"mappings":"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","names":[]}
|
package/dist/events/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as EventTransport, W as WorkflowEvent, E as EventCallback, U as Unsubscribe, b as WorkflowEventType } from '../types-
|
|
2
|
-
export { B as BuiltInEventType } from '../types-
|
|
3
|
-
import { L as Logger } from '../types-
|
|
1
|
+
import { a as EventTransport, W as WorkflowEvent, E as EventCallback, U as Unsubscribe, b as WorkflowEventType } from '../types-DZUdWmCc.js';
|
|
2
|
+
export { B as BuiltInEventType } from '../types-DZUdWmCc.js';
|
|
3
|
+
import { L as Logger } from '../types-K5Gjk3H_.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* In-memory event transport using Node.js EventEmitter.
|
|
@@ -227,7 +227,7 @@ interface WebhookPayload {
|
|
|
227
227
|
* The `Date` is converted to a string before delivery so that JSON consumers
|
|
228
228
|
* receive a portable, timezone-aware representation without needing Date parsing.
|
|
229
229
|
*/
|
|
230
|
-
event: Omit<WorkflowEvent,
|
|
230
|
+
event: Omit<WorkflowEvent, 'timestamp'> & {
|
|
231
231
|
timestamp: string;
|
|
232
232
|
};
|
|
233
233
|
/** ISO 8601 timestamp of when this payload was delivered. */
|
package/dist/events/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { L as Logger, g as WorkflowDefinition, W as WorkflowKind, R as RunStatus } from './types-
|
|
2
|
-
import { i as StorageAdapter, j as WorkflowRunRecord } from './types-
|
|
3
|
-
import { a as EventTransport, E as EventCallback, U as Unsubscribe } from './types-
|
|
1
|
+
import { L as Logger, g as WorkflowDefinition, W as WorkflowKind, R as RunStatus } from './types-K5Gjk3H_.js';
|
|
2
|
+
import { i as StorageAdapter, j as WorkflowRunRecord } from './types-DSEjiLMj.js';
|
|
3
|
+
import { a as EventTransport, E as EventCallback, U as Unsubscribe } from './types-DZUdWmCc.js';
|
|
4
4
|
import { Database } from 'better-sqlite3';
|
|
5
5
|
import { Pool, PoolConfig } from 'pg';
|
|
6
6
|
|
|
@@ -226,7 +226,7 @@ declare class WorkflowEngine {
|
|
|
226
226
|
/**
|
|
227
227
|
* Schedule trigger types.
|
|
228
228
|
*/
|
|
229
|
-
type TriggerType =
|
|
229
|
+
type TriggerType = 'cron' | 'workflow_completed' | 'manual';
|
|
230
230
|
/**
|
|
231
231
|
* Schedule definition.
|
|
232
232
|
*/
|
|
@@ -265,7 +265,7 @@ interface Scheduler {
|
|
|
265
265
|
/** Stop the scheduler */
|
|
266
266
|
stop(): Promise<void>;
|
|
267
267
|
/** Add a schedule */
|
|
268
|
-
addSchedule(schedule: Omit<WorkflowSchedule,
|
|
268
|
+
addSchedule(schedule: Omit<WorkflowSchedule, 'id'>): Promise<WorkflowSchedule>;
|
|
269
269
|
/** Remove a schedule */
|
|
270
270
|
removeSchedule(scheduleId: string): Promise<void>;
|
|
271
271
|
/** Update a schedule */
|
|
@@ -362,7 +362,7 @@ declare class CronScheduler implements Scheduler {
|
|
|
362
362
|
/**
|
|
363
363
|
* Add a new schedule.
|
|
364
364
|
*/
|
|
365
|
-
addSchedule(scheduleData: Omit<WorkflowSchedule,
|
|
365
|
+
addSchedule(scheduleData: Omit<WorkflowSchedule, 'id'>): Promise<WorkflowSchedule>;
|
|
366
366
|
/**
|
|
367
367
|
* Remove a schedule.
|
|
368
368
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { a as WorkflowError, L as Logger, W as WorkflowKind, b as StepErrorStrategy, c as WorkflowStep } from './types-
|
|
2
|
-
export { d as RunResult, R as RunStatus, e as SpawnChildOptions, S as StepStatus, f as WorkflowContext, g as WorkflowDefinition, h as WorkflowHooks } from './types-
|
|
3
|
-
export { C as CronScheduler, a as CronSchedulerConfig, P as PostgresSchedulePersistence, b as PostgresSchedulePersistenceConfig, S as SQLiteSchedulePersistence, c as SQLiteSchedulePersistenceConfig, d as SchedulePersistence, e as Scheduler, f as StartRunOptions, T as TriggerType, W as WorkflowEngine, g as WorkflowEngineConfig, h as WorkflowSchedule } from './index-
|
|
4
|
-
export { C as CreateRunInput, E as ExtendedListRunsOptions, a as ExtendedRunStatus, b as ExtendedStepStatus, c as ExtendedWorkflowRunRecord, L as ListEventsOptions, d as ListRunsOptions, P as PaginatedResult, S as StepRecord, e as StepResult, f as StepflowDatabase, g as StepflowRunsTable, h as StepflowStepResultsTable, i as StorageAdapter, U as UpdateRunInput, W as WorkflowEventRecord, j as WorkflowRunRecord, k as WorkflowRunStepRecord, l as WorkflowStorage } from './types-
|
|
1
|
+
import { a as WorkflowError, L as Logger, W as WorkflowKind, b as StepErrorStrategy, c as WorkflowStep } from './types-K5Gjk3H_.js';
|
|
2
|
+
export { d as RunResult, R as RunStatus, e as SpawnChildOptions, S as StepStatus, f as WorkflowContext, g as WorkflowDefinition, h as WorkflowHooks } from './types-K5Gjk3H_.js';
|
|
3
|
+
export { C as CronScheduler, a as CronSchedulerConfig, P as PostgresSchedulePersistence, b as PostgresSchedulePersistenceConfig, S as SQLiteSchedulePersistence, c as SQLiteSchedulePersistenceConfig, d as SchedulePersistence, e as Scheduler, f as StartRunOptions, T as TriggerType, W as WorkflowEngine, g as WorkflowEngineConfig, h as WorkflowSchedule } from './index-Dzr437iW.js';
|
|
4
|
+
export { C as CreateRunInput, E as ExtendedListRunsOptions, a as ExtendedRunStatus, b as ExtendedStepStatus, c as ExtendedWorkflowRunRecord, L as ListEventsOptions, d as ListRunsOptions, P as PaginatedResult, S as StepRecord, e as StepResult, f as StepflowDatabase, g as StepflowRunsTable, h as StepflowStepResultsTable, i as StorageAdapter, U as UpdateRunInput, W as WorkflowEventRecord, j as WorkflowRunRecord, k as WorkflowRunStepRecord, l as WorkflowStorage } from './types-DSEjiLMj.js';
|
|
5
5
|
export { MemoryStorageAdapter, PostgresStorage, PostgresStorage as PostgresStorageAdapter, PostgresStorageConfig, SQLiteStorageAdapter, SQLiteStorageConfig } from './storage/index.js';
|
|
6
|
-
export { B as BuiltInEventType, E as EventCallback, a as EventTransport, U as Unsubscribe, W as WorkflowEvent, b as WorkflowEventType } from './types-
|
|
6
|
+
export { B as BuiltInEventType, E as EventCallback, a as EventTransport, U as Unsubscribe, W as WorkflowEvent, b as WorkflowEventType } from './types-DZUdWmCc.js';
|
|
7
7
|
export { MemoryEventTransport, SocketIOAuthorizeFn, SocketIOEventTransport, SocketIOEventTransportConfig, SocketIOServer, SocketIOSocket, WebhookEndpoint, WebhookEventTransport, WebhookEventTransportConfig, WebhookPayload } from './events/index.js';
|
|
8
8
|
import 'better-sqlite3';
|
|
9
9
|
import 'pg';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* ID generation utilities for the workflow engine.
|
|
13
|
+
*/
|
|
11
14
|
/**
|
|
12
15
|
* Generate a unique ID for database records.
|
|
13
16
|
* Uses a ULID-like format: base36 timestamp + random suffix.
|
|
@@ -15,7 +18,6 @@ import 'pg';
|
|
|
15
18
|
* - Rough time-ordering (useful for debugging)
|
|
16
19
|
* - High collision resistance
|
|
17
20
|
* - URL-safe characters
|
|
18
|
-
* - Cryptographically secure random component
|
|
19
21
|
*
|
|
20
22
|
* @returns A unique identifier string (approx 16 characters)
|
|
21
23
|
*/
|
|
@@ -103,7 +105,7 @@ declare class WorkflowTimeoutError extends WorkflowEngineError {
|
|
|
103
105
|
*/
|
|
104
106
|
|
|
105
107
|
/** Log level for ConsoleLogger. Levels are ordered: debug < info < warn < error. */
|
|
106
|
-
type LogLevel =
|
|
108
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
107
109
|
/**
|
|
108
110
|
* Console-based logger implementation.
|
|
109
111
|
* Uses console.log with prefixes for different levels.
|
|
@@ -187,7 +189,7 @@ declare function calculateRetryDelay(attempt: number, baseDelay: number, backoff
|
|
|
187
189
|
/**
|
|
188
190
|
* Comparison operators for recipe conditions.
|
|
189
191
|
*/
|
|
190
|
-
type ConditionOperator =
|
|
192
|
+
type ConditionOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'matches' | 'exists' | 'notExists';
|
|
191
193
|
/**
|
|
192
194
|
* A condition that determines when a recipe should be selected.
|
|
193
195
|
* Conditions are evaluated against the workflow input.
|
|
@@ -291,7 +293,7 @@ interface Recipe {
|
|
|
291
293
|
/**
|
|
292
294
|
* Types of modifications that can be made to a plan.
|
|
293
295
|
*/
|
|
294
|
-
type PlanModificationType =
|
|
296
|
+
type PlanModificationType = 'add_step' | 'remove_step' | 'modify_step' | 'reorder_steps' | 'set_default';
|
|
295
297
|
/**
|
|
296
298
|
* A modification to apply to a recipe when generating a plan.
|
|
297
299
|
*/
|
|
@@ -386,7 +388,7 @@ interface Plan {
|
|
|
386
388
|
/**
|
|
387
389
|
* Priority modes for planning.
|
|
388
390
|
*/
|
|
389
|
-
type PlanningPriority =
|
|
391
|
+
type PlanningPriority = 'speed' | 'quality' | 'cost' | 'balanced';
|
|
390
392
|
/**
|
|
391
393
|
* Constraints that limit planning and execution.
|
|
392
394
|
*/
|
|
@@ -486,7 +488,7 @@ interface RegisteredStepHandler<TInput = Record<string, unknown>> {
|
|
|
486
488
|
/** Human-readable description */
|
|
487
489
|
description?: string;
|
|
488
490
|
/** The handler function */
|
|
489
|
-
handler: WorkflowStep<TInput>[
|
|
491
|
+
handler: WorkflowStep<TInput>['handler'];
|
|
490
492
|
/** Tags for categorization */
|
|
491
493
|
tags?: string[];
|
|
492
494
|
}
|
|
@@ -600,7 +602,7 @@ declare class MemoryStepHandlerRegistry implements StepHandlerRegistry {
|
|
|
600
602
|
* Resolve a handler reference to a WorkflowStep handler function.
|
|
601
603
|
* Returns undefined if the handler is not found.
|
|
602
604
|
*/
|
|
603
|
-
resolve(handlerRef: string): WorkflowStep[
|
|
605
|
+
resolve(handlerRef: string): WorkflowStep['handler'] | undefined;
|
|
604
606
|
/**
|
|
605
607
|
* Clear all registered handlers (useful for testing).
|
|
606
608
|
*/
|
package/dist/index.js
CHANGED
|
@@ -2,24 +2,24 @@ import {
|
|
|
2
2
|
MemoryStorageAdapter,
|
|
3
3
|
PostgresStorageAdapter,
|
|
4
4
|
SQLiteStorageAdapter
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4VOKMGP6.js";
|
|
6
6
|
import {
|
|
7
7
|
MemoryEventTransport,
|
|
8
8
|
SocketIOEventTransport,
|
|
9
9
|
WebhookEventTransport
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GYPNJBDP.js";
|
|
11
11
|
import {
|
|
12
12
|
CronScheduler,
|
|
13
13
|
PostgresSchedulePersistence,
|
|
14
14
|
SQLiteSchedulePersistence
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-KHTUWLVZ.js";
|
|
16
16
|
import {
|
|
17
17
|
ConsoleLogger,
|
|
18
18
|
SilentLogger,
|
|
19
19
|
createScopedLogger,
|
|
20
20
|
generateId,
|
|
21
21
|
sanitizeErrorForStorage
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ML35PIHX.js";
|
|
23
23
|
|
|
24
24
|
// src/utils/errors.ts
|
|
25
25
|
var WorkflowEngineError = class _WorkflowEngineError extends Error {
|
|
@@ -38,7 +38,6 @@ var WorkflowEngineError = class _WorkflowEngineError extends Error {
|
|
|
38
38
|
return {
|
|
39
39
|
code: this.code,
|
|
40
40
|
message: this.message,
|
|
41
|
-
stack: this.stack,
|
|
42
41
|
details: this.details
|
|
43
42
|
};
|
|
44
43
|
}
|
|
@@ -52,8 +51,7 @@ var WorkflowEngineError = class _WorkflowEngineError extends Error {
|
|
|
52
51
|
if (error instanceof Error) {
|
|
53
52
|
return {
|
|
54
53
|
code: defaultCode,
|
|
55
|
-
message: error.message
|
|
56
|
-
stack: error.stack
|
|
54
|
+
message: error.message
|
|
57
55
|
};
|
|
58
56
|
}
|
|
59
57
|
return {
|
|
@@ -64,19 +62,13 @@ var WorkflowEngineError = class _WorkflowEngineError extends Error {
|
|
|
64
62
|
};
|
|
65
63
|
var WorkflowNotFoundError = class extends WorkflowEngineError {
|
|
66
64
|
constructor(kind) {
|
|
67
|
-
super("WORKFLOW_NOT_FOUND", `Workflow "${kind}" is not registered`, {
|
|
68
|
-
kind
|
|
69
|
-
});
|
|
65
|
+
super("WORKFLOW_NOT_FOUND", `Workflow "${kind}" is not registered`, { kind });
|
|
70
66
|
this.name = "WorkflowNotFoundError";
|
|
71
67
|
}
|
|
72
68
|
};
|
|
73
69
|
var WorkflowAlreadyRegisteredError = class extends WorkflowEngineError {
|
|
74
70
|
constructor(kind) {
|
|
75
|
-
super(
|
|
76
|
-
"WORKFLOW_ALREADY_REGISTERED",
|
|
77
|
-
`Workflow "${kind}" is already registered`,
|
|
78
|
-
{ kind }
|
|
79
|
-
);
|
|
71
|
+
super("WORKFLOW_ALREADY_REGISTERED", `Workflow "${kind}" is already registered`, { kind });
|
|
80
72
|
this.name = "WorkflowAlreadyRegisteredError";
|
|
81
73
|
}
|
|
82
74
|
};
|
|
@@ -113,9 +105,7 @@ var StepTimeoutError = class extends WorkflowEngineError {
|
|
|
113
105
|
};
|
|
114
106
|
var WorkflowCanceledError = class extends WorkflowEngineError {
|
|
115
107
|
constructor(runId) {
|
|
116
|
-
super("WORKFLOW_CANCELED", `Workflow run "${runId}" was canceled`, {
|
|
117
|
-
runId
|
|
118
|
-
});
|
|
108
|
+
super("WORKFLOW_CANCELED", `Workflow run "${runId}" was canceled`, { runId });
|
|
119
109
|
this.name = "WorkflowCanceledError";
|
|
120
110
|
}
|
|
121
111
|
};
|
|
@@ -123,14 +113,10 @@ var WaitForRunTimeoutError = class extends WorkflowEngineError {
|
|
|
123
113
|
runId;
|
|
124
114
|
timeoutMs;
|
|
125
115
|
constructor(runId, timeoutMs) {
|
|
126
|
-
super(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
runId,
|
|
131
|
-
timeoutMs
|
|
132
|
-
}
|
|
133
|
-
);
|
|
116
|
+
super("WAIT_FOR_RUN_TIMEOUT", `Timeout waiting for run ${runId} after ${timeoutMs}ms`, {
|
|
117
|
+
runId,
|
|
118
|
+
timeoutMs
|
|
119
|
+
});
|
|
134
120
|
this.name = "WaitForRunTimeoutError";
|
|
135
121
|
this.runId = runId;
|
|
136
122
|
this.timeoutMs = timeoutMs;
|
|
@@ -139,14 +125,10 @@ var WaitForRunTimeoutError = class extends WorkflowEngineError {
|
|
|
139
125
|
var WorkflowTimeoutError = class extends WorkflowEngineError {
|
|
140
126
|
timeoutMs;
|
|
141
127
|
constructor(runId, timeoutMs) {
|
|
142
|
-
super(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
runId,
|
|
147
|
-
timeoutMs
|
|
148
|
-
}
|
|
149
|
-
);
|
|
128
|
+
super("WORKFLOW_TIMEOUT", `Workflow run "${runId}" timed out after ${timeoutMs}ms`, {
|
|
129
|
+
runId,
|
|
130
|
+
timeoutMs
|
|
131
|
+
});
|
|
150
132
|
this.name = "WorkflowTimeoutError";
|
|
151
133
|
this.timeoutMs = timeoutMs;
|
|
152
134
|
}
|
|
@@ -188,7 +170,7 @@ async function withRetry(fn, options = {}) {
|
|
|
188
170
|
let currentDelay = opts.delay;
|
|
189
171
|
for (let attempt = 1; attempt <= opts.maxRetries + 1; attempt++) {
|
|
190
172
|
if (opts.signal?.aborted) {
|
|
191
|
-
throw new
|
|
173
|
+
throw new WorkflowCanceledError("run");
|
|
192
174
|
}
|
|
193
175
|
try {
|
|
194
176
|
return await fn();
|
|
@@ -235,6 +217,7 @@ async function executeWorkflow(options) {
|
|
|
235
217
|
timestamp: /* @__PURE__ */ new Date(),
|
|
236
218
|
payload: isResume ? { resumedFrom: Array.from(checkpoint.completedStepKeys) } : void 0
|
|
237
219
|
});
|
|
220
|
+
const completedSteps = checkpoint ? Array.from(checkpoint.completedStepKeys) : [];
|
|
238
221
|
const context = {
|
|
239
222
|
runId,
|
|
240
223
|
stepId: "",
|
|
@@ -260,10 +243,7 @@ async function executeWorkflow(options) {
|
|
|
260
243
|
let workflowTimeoutError;
|
|
261
244
|
if (definition.timeout) {
|
|
262
245
|
workflowTimeoutId = setTimeout(() => {
|
|
263
|
-
workflowTimeoutError = new WorkflowTimeoutError(
|
|
264
|
-
runId,
|
|
265
|
-
definition.timeout
|
|
266
|
-
);
|
|
246
|
+
workflowTimeoutError = new WorkflowTimeoutError(runId, definition.timeout);
|
|
267
247
|
abortController.abort();
|
|
268
248
|
}, definition.timeout);
|
|
269
249
|
}
|
|
@@ -277,9 +257,7 @@ async function executeWorkflow(options) {
|
|
|
277
257
|
}
|
|
278
258
|
context.currentStep = step.key;
|
|
279
259
|
if (checkpoint?.completedStepKeys.has(step.key)) {
|
|
280
|
-
logger.info(
|
|
281
|
-
`Skipping step "${step.key}" (already completed in previous run)`
|
|
282
|
-
);
|
|
260
|
+
logger.info(`Skipping step "${step.key}" (already completed in previous run)`);
|
|
283
261
|
emitEvent(events, logger, {
|
|
284
262
|
runId,
|
|
285
263
|
kind: definition.kind,
|
|
@@ -312,8 +290,10 @@ async function executeWorkflow(options) {
|
|
|
312
290
|
abortController
|
|
313
291
|
});
|
|
314
292
|
context.results[step.key] = result;
|
|
293
|
+
completedSteps.push(step.key);
|
|
315
294
|
await storage.updateRun(runId, {
|
|
316
|
-
context: { ...context.results }
|
|
295
|
+
context: { ...context.results },
|
|
296
|
+
completedSteps: [...completedSteps]
|
|
317
297
|
});
|
|
318
298
|
}
|
|
319
299
|
if (workflowTimeoutId) {
|
|
@@ -362,7 +342,7 @@ async function executeWorkflow(options) {
|
|
|
362
342
|
try {
|
|
363
343
|
await definition.hooks.afterRun(context, runResult);
|
|
364
344
|
} catch (hookError) {
|
|
365
|
-
logger.error("afterRun hook failed:", hookError);
|
|
345
|
+
logger.error("afterRun hook failed for run " + runId + ":", hookError);
|
|
366
346
|
}
|
|
367
347
|
}
|
|
368
348
|
await storage.updateRun(runId, {
|
|
@@ -469,7 +449,7 @@ async function executeStep(step, context, options) {
|
|
|
469
449
|
try {
|
|
470
450
|
await definition.hooks.onStepError(context, step, lastError);
|
|
471
451
|
} catch (hookError) {
|
|
472
|
-
logger.error("onStepError hook failed:", hookError);
|
|
452
|
+
logger.error("onStepError hook failed for run " + context.runId + ":", hookError);
|
|
473
453
|
}
|
|
474
454
|
}
|
|
475
455
|
emitEvent(events, logger, {
|
|
@@ -494,9 +474,7 @@ async function executeStep(step, context, options) {
|
|
|
494
474
|
}
|
|
495
475
|
if (onError === "retry" && attempt <= maxRetries) {
|
|
496
476
|
const delay = calculateRetryDelay(attempt, retryDelay, retryBackoff);
|
|
497
|
-
logger.warn(
|
|
498
|
-
`Step "${step.key}" failed, retrying in ${delay}ms (attempt ${attempt}/${maxRetries})`
|
|
499
|
-
);
|
|
477
|
+
logger.warn(`Step "${step.key}" failed, retrying in ${delay}ms (attempt ${attempt}/${maxRetries})`);
|
|
500
478
|
emitEvent(events, logger, {
|
|
501
479
|
runId: context.runId,
|
|
502
480
|
kind: context.kind,
|
|
@@ -692,11 +670,7 @@ var WorkflowEngine = class _WorkflowEngine {
|
|
|
692
670
|
kind: options.kind,
|
|
693
671
|
eventType: "run.created",
|
|
694
672
|
timestamp: /* @__PURE__ */ new Date(),
|
|
695
|
-
payload: {
|
|
696
|
-
input: options.input,
|
|
697
|
-
metadata: options.metadata,
|
|
698
|
-
priority: options.priority ?? 0
|
|
699
|
-
}
|
|
673
|
+
payload: { input: options.input, metadata: options.metadata, priority: options.priority ?? 0 }
|
|
700
674
|
});
|
|
701
675
|
if (this.hasCapacity()) {
|
|
702
676
|
this.executeRun(
|
|
@@ -715,9 +689,7 @@ var WorkflowEngine = class _WorkflowEngine {
|
|
|
715
689
|
priority: options.priority ?? 0,
|
|
716
690
|
queuedAt: /* @__PURE__ */ new Date()
|
|
717
691
|
});
|
|
718
|
-
this.logger.debug(
|
|
719
|
-
`Run ${runId} queued (${this.runQueue.length} in queue)`
|
|
720
|
-
);
|
|
692
|
+
this.logger.debug(`Run ${runId} queued (${this.runQueue.length} in queue)`);
|
|
721
693
|
this.events.emit({
|
|
722
694
|
runId,
|
|
723
695
|
kind: options.kind,
|
|
@@ -795,7 +767,12 @@ var WorkflowEngine = class _WorkflowEngine {
|
|
|
795
767
|
eventType: "run.dequeued",
|
|
796
768
|
timestamp: /* @__PURE__ */ new Date()
|
|
797
769
|
});
|
|
798
|
-
this.executeRun(
|
|
770
|
+
this.executeRun(
|
|
771
|
+
next.runId,
|
|
772
|
+
next.definition,
|
|
773
|
+
next.input,
|
|
774
|
+
next.metadata
|
|
775
|
+
);
|
|
799
776
|
}
|
|
800
777
|
}
|
|
801
778
|
/**
|
|
@@ -853,18 +830,8 @@ var WorkflowEngine = class _WorkflowEngine {
|
|
|
853
830
|
async getRunStatus(runId) {
|
|
854
831
|
return this.storage.getRun(runId);
|
|
855
832
|
}
|
|
856
|
-
static TERMINAL_STATUSES = [
|
|
857
|
-
|
|
858
|
-
"failed",
|
|
859
|
-
"canceled",
|
|
860
|
-
"timeout"
|
|
861
|
-
];
|
|
862
|
-
static TERMINAL_EVENT_TYPES = [
|
|
863
|
-
"run.completed",
|
|
864
|
-
"run.failed",
|
|
865
|
-
"run.canceled",
|
|
866
|
-
"run.timeout"
|
|
867
|
-
];
|
|
833
|
+
static TERMINAL_STATUSES = ["succeeded", "failed", "canceled", "timeout"];
|
|
834
|
+
static TERMINAL_EVENT_TYPES = ["run.completed", "run.failed", "run.canceled", "run.timeout"];
|
|
868
835
|
/**
|
|
869
836
|
* Wait for a run to complete.
|
|
870
837
|
* Subscribes to run events and resolves when a terminal event fires.
|
|
@@ -941,7 +908,7 @@ var WorkflowEngine = class _WorkflowEngine {
|
|
|
941
908
|
return runId;
|
|
942
909
|
}
|
|
943
910
|
this.logger.info(`Resuming run ${runId} from checkpoint`);
|
|
944
|
-
const completedStepKeys = new Set(
|
|
911
|
+
const completedStepKeys = new Set(run.completedSteps ?? []);
|
|
945
912
|
this.launchRun(runId, definition, run.input, run.metadata, void 0, {
|
|
946
913
|
completedStepKeys,
|
|
947
914
|
results: run.context
|
|
@@ -976,9 +943,7 @@ var WorkflowEngine = class _WorkflowEngine {
|
|
|
976
943
|
this.logger.error(`Failed to resume run ${run.id}:`, error);
|
|
977
944
|
}
|
|
978
945
|
} else {
|
|
979
|
-
this.logger.warn(
|
|
980
|
-
`Cannot resume run ${run.id}: workflow "${run.kind}" not registered`
|
|
981
|
-
);
|
|
946
|
+
this.logger.warn(`Cannot resume run ${run.id}: workflow "${run.kind}" not registered`);
|
|
982
947
|
}
|
|
983
948
|
}
|
|
984
949
|
return resumedIds;
|
|
@@ -1525,9 +1490,7 @@ var RuleBasedPlanner = class {
|
|
|
1525
1490
|
}
|
|
1526
1491
|
if (this.validateHandlers && this.handlerRegistry) {
|
|
1527
1492
|
if (!this.handlerRegistry.has(step.handlerRef)) {
|
|
1528
|
-
errors.push(
|
|
1529
|
-
`Step '${step.key}' references unknown handler: ${step.handlerRef}`
|
|
1530
|
-
);
|
|
1493
|
+
errors.push(`Step '${step.key}' references unknown handler: ${step.handlerRef}`);
|
|
1531
1494
|
}
|
|
1532
1495
|
}
|
|
1533
1496
|
}
|
|
@@ -1548,12 +1511,7 @@ var RuleBasedPlanner = class {
|
|
|
1548
1511
|
* Estimate resources required for a plan.
|
|
1549
1512
|
*/
|
|
1550
1513
|
estimateResources(plan) {
|
|
1551
|
-
const {
|
|
1552
|
-
apiCallsPerStep,
|
|
1553
|
-
tokensPerStep,
|
|
1554
|
-
durationPerStep,
|
|
1555
|
-
apiCallsPerChild
|
|
1556
|
-
} = this.resourceEstimates;
|
|
1514
|
+
const { apiCallsPerStep, tokensPerStep, durationPerStep, apiCallsPerChild } = this.resourceEstimates;
|
|
1557
1515
|
const stepCount = plan.steps.length;
|
|
1558
1516
|
const childCount = plan.childWorkflows?.length ?? 0;
|
|
1559
1517
|
return {
|