@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.
Files changed (84) hide show
  1. package/.codekin/reports/comments/2026-04-04_comment-assessment.md +179 -0
  2. package/.codekin/reports/complexity/2026-04-03_complexity-report.md +152 -0
  3. package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +94 -0
  4. package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +10 -0
  5. package/.codekin/reports/security/2026-04-03_security-audit.md +208 -0
  6. package/.github/CODEOWNERS +15 -0
  7. package/.github/dependabot.yml +23 -0
  8. package/.github/workflows/ci.yml +38 -0
  9. package/.github/workflows/publish.yml +11 -5
  10. package/.github/workflows/security.yml +29 -0
  11. package/CHANGELOG.md +34 -0
  12. package/README.md +24 -0
  13. package/SECURITY.md +40 -0
  14. package/dist/{chunk-FQCT3I2A.js → chunk-4VOKMGP6.js} +23 -42
  15. package/dist/chunk-4VOKMGP6.js.map +1 -0
  16. package/dist/{chunk-XCF4PLN7.js → chunk-GYPNJBDP.js} +12 -16
  17. package/dist/chunk-GYPNJBDP.js.map +1 -0
  18. package/dist/{chunk-U5QGN3RT.js → chunk-KHTUWLVZ.js} +14 -47
  19. package/dist/chunk-KHTUWLVZ.js.map +1 -0
  20. package/dist/{chunk-GXY66V7Q.js → chunk-ML35PIHX.js} +1 -1
  21. package/dist/chunk-ML35PIHX.js.map +1 -0
  22. package/dist/events/index.d.ts +4 -4
  23. package/dist/events/index.js +1 -1
  24. package/dist/{index-CPfGsmrp.d.ts → index-Dzr437iW.d.ts} +6 -6
  25. package/dist/index.d.ts +14 -12
  26. package/dist/index.js +40 -82
  27. package/dist/index.js.map +1 -1
  28. package/dist/scheduler/index.d.ts +4 -4
  29. package/dist/scheduler/index.js +2 -2
  30. package/dist/storage/index.d.ts +13 -13
  31. package/dist/storage/index.js +2 -2
  32. package/dist/{types-BlwFEGzj.d.ts → types-DSEjiLMj.d.ts} +16 -15
  33. package/dist/{types-DzkEqnQJ.d.ts → types-DZUdWmCc.d.ts} +2 -2
  34. package/dist/{types-BVShq7zT.d.ts → types-K5Gjk3H_.d.ts} +4 -4
  35. package/docs/stepflow-api-reference.md +2 -3
  36. package/package.json +7 -6
  37. package/src/core/engine.test.ts +466 -522
  38. package/src/core/engine.ts +42 -67
  39. package/src/core/index.ts +3 -3
  40. package/src/core/orchestrator.ts +64 -83
  41. package/src/core/types.ts +7 -29
  42. package/src/events/index.ts +4 -4
  43. package/src/events/memory.test.ts +39 -41
  44. package/src/events/memory.ts +7 -10
  45. package/src/events/socketio.test.ts +112 -162
  46. package/src/events/socketio.ts +20 -27
  47. package/src/events/types.ts +16 -19
  48. package/src/events/webhook.test.ts +197 -235
  49. package/src/events/webhook.ts +44 -71
  50. package/src/index.ts +19 -19
  51. package/src/planning/index.ts +6 -3
  52. package/src/planning/planner.ts +66 -91
  53. package/src/planning/planning.test.ts +303 -366
  54. package/src/planning/registry.test.ts +204 -251
  55. package/src/planning/registry.ts +39 -43
  56. package/src/planning/types.ts +23 -27
  57. package/src/scheduler/cron.test.ts +141 -150
  58. package/src/scheduler/cron.ts +32 -67
  59. package/src/scheduler/index.ts +4 -14
  60. package/src/scheduler/postgres-persistence.ts +71 -99
  61. package/src/scheduler/sqlite-persistence.test.ts +96 -96
  62. package/src/scheduler/sqlite-persistence.ts +12 -19
  63. package/src/scheduler/types.ts +4 -9
  64. package/src/storage/index.ts +5 -5
  65. package/src/storage/memory.test.ts +133 -183
  66. package/src/storage/memory.ts +21 -42
  67. package/src/storage/postgres.ts +197 -393
  68. package/src/storage/sqlite.test.ts +154 -281
  69. package/src/storage/sqlite.ts +50 -85
  70. package/src/storage/types.ts +33 -58
  71. package/src/utils/errors.test.ts +78 -80
  72. package/src/utils/errors.ts +27 -57
  73. package/src/utils/id.ts +3 -3
  74. package/src/utils/index.ts +4 -4
  75. package/src/utils/logger.test.ts +52 -58
  76. package/src/utils/logger.ts +4 -4
  77. package/src/utils/postgres-deps.ts +4 -4
  78. package/src/utils/retry.test.ts +47 -67
  79. package/src/utils/retry.ts +9 -9
  80. package/dist/chunk-FQCT3I2A.js.map +0 -1
  81. package/dist/chunk-GXY66V7Q.js.map +0 -1
  82. package/dist/chunk-U5QGN3RT.js.map +0 -1
  83. package/dist/chunk-XCF4PLN7.js.map +0 -1
  84. 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":[]}
@@ -108,4 +108,4 @@ export {
108
108
  createScopedLogger,
109
109
  loadPostgresDeps
110
110
  };
111
- //# sourceMappingURL=chunk-GXY66V7Q.js.map
111
+ //# sourceMappingURL=chunk-ML35PIHX.js.map
@@ -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":[]}
@@ -1,6 +1,6 @@
1
- import { a as EventTransport, W as WorkflowEvent, E as EventCallback, U as Unsubscribe, b as WorkflowEventType } from '../types-DzkEqnQJ.js';
2
- export { B as BuiltInEventType } from '../types-DzkEqnQJ.js';
3
- import { L as Logger } from '../types-BVShq7zT.js';
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, "timestamp"> & {
230
+ event: Omit<WorkflowEvent, 'timestamp'> & {
231
231
  timestamp: string;
232
232
  };
233
233
  /** ISO 8601 timestamp of when this payload was delivered. */
@@ -2,7 +2,7 @@ import {
2
2
  MemoryEventTransport,
3
3
  SocketIOEventTransport,
4
4
  WebhookEventTransport
5
- } from "../chunk-XCF4PLN7.js";
5
+ } from "../chunk-GYPNJBDP.js";
6
6
  export {
7
7
  MemoryEventTransport,
8
8
  SocketIOEventTransport,
@@ -1,6 +1,6 @@
1
- import { L as Logger, g as WorkflowDefinition, W as WorkflowKind, R as RunStatus } from './types-BVShq7zT.js';
2
- import { i as StorageAdapter, j as WorkflowRunRecord } from './types-BlwFEGzj.js';
3
- import { a as EventTransport, E as EventCallback, U as Unsubscribe } from './types-DzkEqnQJ.js';
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 = "cron" | "workflow_completed" | "manual";
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, "id">): Promise<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, "id">): Promise<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-BVShq7zT.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-BVShq7zT.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-CPfGsmrp.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-BlwFEGzj.js';
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-DzkEqnQJ.js';
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 = "debug" | "info" | "warn" | "error";
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 = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "contains" | "matches" | "exists" | "notExists";
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 = "add_step" | "remove_step" | "modify_step" | "reorder_steps" | "set_default";
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 = "speed" | "quality" | "cost" | "balanced";
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>["handler"];
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["handler"] | undefined;
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-FQCT3I2A.js";
5
+ } from "./chunk-4VOKMGP6.js";
6
6
  import {
7
7
  MemoryEventTransport,
8
8
  SocketIOEventTransport,
9
9
  WebhookEventTransport
10
- } from "./chunk-XCF4PLN7.js";
10
+ } from "./chunk-GYPNJBDP.js";
11
11
  import {
12
12
  CronScheduler,
13
13
  PostgresSchedulePersistence,
14
14
  SQLiteSchedulePersistence
15
- } from "./chunk-U5QGN3RT.js";
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-GXY66V7Q.js";
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
- "WAIT_FOR_RUN_TIMEOUT",
128
- `Timeout waiting for run ${runId} after ${timeoutMs}ms`,
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
- "WORKFLOW_TIMEOUT",
144
- `Workflow run "${runId}" timed out after ${timeoutMs}ms`,
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 Error("Aborted");
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(next.runId, next.definition, next.input, next.metadata);
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
- "succeeded",
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(Object.keys(run.context));
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 {