@multiplier-labs/stepflow 0.3.1 → 0.3.4

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 (112) hide show
  1. package/.codekin/reports/code-review/2026-04-11_code-review-daily.md +146 -0
  2. package/.codekin/reports/code-review/2026-04-13_code-review-daily.md +74 -0
  3. package/.codekin/reports/comments/2026-04-04_comment-assessment.md +179 -0
  4. package/.codekin/reports/comments/2026-04-11_comment-assessment.md +161 -0
  5. package/.codekin/reports/comments/2026-04-14_comment-assessment.md +34 -0
  6. package/.codekin/reports/complexity/2026-04-03_complexity-report.md +152 -0
  7. package/.codekin/reports/complexity/2026-04-11_complexity-report.md +152 -0
  8. package/.codekin/reports/complexity/2026-04-14_complexity-report.md +34 -0
  9. package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +94 -0
  10. package/.codekin/reports/dependencies/2026-04-11_dependency-health.md +93 -0
  11. package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +10 -0
  12. package/.codekin/reports/docs-audit/2026-04-11_docs-audit.md +138 -0
  13. package/.codekin/reports/docs-audit/2026-04-13_docs-audit.md +121 -0
  14. package/.codekin/reports/security/2026-04-03_security-audit.md +208 -0
  15. package/.codekin/reports/security/2026-04-11_security-audit.md +297 -0
  16. package/.github/CODEOWNERS +15 -0
  17. package/.github/dependabot.yml +23 -0
  18. package/.github/workflows/ci.yml +38 -0
  19. package/.github/workflows/publish.yml +17 -5
  20. package/.github/workflows/security.yml +29 -0
  21. package/CHANGELOG.md +34 -0
  22. package/README.md +24 -0
  23. package/SECURITY.md +40 -0
  24. package/dist/{chunk-FQCT3I2A.js → chunk-5XJBAPXF.js} +527 -551
  25. package/dist/chunk-5XJBAPXF.js.map +1 -0
  26. package/dist/{chunk-U5QGN3RT.js → chunk-FMPJBK2N.js} +67 -55
  27. package/dist/chunk-FMPJBK2N.js.map +1 -0
  28. package/dist/{chunk-GXY66V7Q.js → chunk-JPXPB7U5.js} +52 -2
  29. package/dist/chunk-JPXPB7U5.js.map +1 -0
  30. package/dist/{chunk-XCF4PLN7.js → chunk-U3PCVK6O.js} +276 -61
  31. package/dist/chunk-U3PCVK6O.js.map +1 -0
  32. package/dist/events/index.d.ts +60 -7
  33. package/dist/events/index.js +1 -1
  34. package/dist/{index-CPfGsmrp.d.ts → index-Cd0IZ40L.d.ts} +56 -17
  35. package/dist/index.d.ts +15 -23
  36. package/dist/index.js +784 -547
  37. package/dist/index.js.map +1 -1
  38. package/dist/scheduler/index.d.ts +4 -4
  39. package/dist/scheduler/index.js +2 -2
  40. package/dist/storage/index.d.ts +322 -56
  41. package/dist/storage/index.js +4 -2
  42. package/dist/{types-BlwFEGzj.d.ts → types-DSEjiLMj.d.ts} +16 -15
  43. package/dist/{types-DzkEqnQJ.d.ts → types-DZUdWmCc.d.ts} +2 -2
  44. package/dist/{types-BVShq7zT.d.ts → types-K5Gjk3H_.d.ts} +4 -4
  45. package/docs/stepflow-api-reference.md +5 -4
  46. package/package.json +12 -8
  47. package/src/core/engine/concurrency.ts +229 -0
  48. package/src/core/engine/lifecycle.ts +176 -0
  49. package/src/core/engine.test.ts +536 -522
  50. package/src/core/engine.ts +116 -235
  51. package/src/core/index.ts +3 -3
  52. package/src/core/orchestrator/persistence.ts +165 -0
  53. package/src/core/orchestrator/plan-resolution.ts +140 -0
  54. package/src/core/orchestrator/state-transitions.ts +109 -0
  55. package/src/core/orchestrator/step-runner.ts +427 -0
  56. package/src/core/orchestrator.ts +57 -395
  57. package/src/core/types.ts +7 -29
  58. package/src/events/index.ts +4 -4
  59. package/src/events/memory.test.ts +39 -41
  60. package/src/events/memory.ts +7 -10
  61. package/src/events/socketio.test.ts +112 -162
  62. package/src/events/socketio.ts +20 -27
  63. package/src/events/types.ts +16 -19
  64. package/src/events/webhook.test.ts +489 -235
  65. package/src/events/webhook.ts +355 -139
  66. package/src/index.ts +19 -19
  67. package/src/planning/evaluate-condition.test.ts +153 -0
  68. package/src/planning/evaluate-condition.ts +105 -0
  69. package/src/planning/index.ts +6 -3
  70. package/src/planning/planner.ts +45 -152
  71. package/src/planning/planning.test.ts +303 -366
  72. package/src/planning/registry.test.ts +204 -251
  73. package/src/planning/registry.ts +20 -106
  74. package/src/planning/types.ts +23 -27
  75. package/src/scheduler/cron.test.ts +149 -150
  76. package/src/scheduler/cron.ts +58 -69
  77. package/src/scheduler/index.ts +4 -14
  78. package/src/scheduler/postgres-persistence.ts +89 -105
  79. package/src/scheduler/sqlite-persistence.test.ts +96 -96
  80. package/src/scheduler/sqlite-persistence.ts +40 -22
  81. package/src/scheduler/types.ts +4 -9
  82. package/src/storage/index.ts +5 -5
  83. package/src/storage/memory.test.ts +133 -183
  84. package/src/storage/memory.ts +21 -42
  85. package/src/storage/postgres-core.ts +550 -0
  86. package/src/storage/postgres-migration.test.ts +69 -0
  87. package/src/storage/postgres-transaction-pagination.test.ts +107 -0
  88. package/src/storage/postgres.ts +222 -937
  89. package/src/storage/sqlite-core.ts +151 -0
  90. package/src/storage/sqlite.test.ts +330 -281
  91. package/src/storage/sqlite.ts +225 -170
  92. package/src/storage/types.ts +33 -58
  93. package/src/utils/errors.test.ts +78 -80
  94. package/src/utils/errors.ts +27 -57
  95. package/src/utils/id.ts +3 -3
  96. package/src/utils/index.ts +6 -4
  97. package/src/utils/logger.test.ts +52 -58
  98. package/src/utils/logger.ts +4 -4
  99. package/src/utils/pinned-fetch.test.ts +84 -0
  100. package/src/utils/pinned-fetch.ts +118 -0
  101. package/src/utils/postgres-deps.ts +20 -4
  102. package/src/utils/retry.test.ts +47 -67
  103. package/src/utils/retry.ts +9 -9
  104. package/src/utils/safe-json.test.ts +166 -0
  105. package/src/utils/safe-json.ts +107 -0
  106. package/src/utils/webhook-signature.test.ts +111 -0
  107. package/src/utils/webhook-signature.ts +74 -0
  108. package/dist/chunk-FQCT3I2A.js.map +0 -1
  109. package/dist/chunk-GXY66V7Q.js.map +0 -1
  110. package/dist/chunk-U5QGN3RT.js.map +0 -1
  111. package/dist/chunk-XCF4PLN7.js.map +0 -1
  112. package/src/scheduler/postgres-persistence.test.ts +0 -906
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Helpers for signing and verifying inbound / outbound webhook payloads.
3
+ *
4
+ * Both sides use HMAC-SHA256 with a `sha256=<lowercase-hex>` envelope —
5
+ * the same shape produced by `WebhookEventTransport.signPayload`, matching
6
+ * common third-party providers (Stripe, GitHub, etc.).
7
+ *
8
+ * Audit: 2026-04-27 finding L2 — without a shipped verifier, consumers
9
+ * receiving inbound webhooks tend to roll their own comparisons that are
10
+ * vulnerable to timing attacks.
11
+ */
12
+
13
+ import { createHmac, timingSafeEqual } from 'node:crypto';
14
+
15
+ /**
16
+ * Compute the canonical `X-Webhook-Signature` value for a request body.
17
+ *
18
+ * Returns a string of the form `sha256=<lowercase-hex>`.
19
+ */
20
+ export function signWebhookPayload(body: string | Uint8Array, secret: string): string {
21
+ const hmac = createHmac('sha256', secret);
22
+ hmac.update(body);
23
+ return `sha256=${hmac.digest('hex')}`;
24
+ }
25
+
26
+ /**
27
+ * Constant-time verification of an inbound webhook signature.
28
+ *
29
+ * @param body The raw request body that was signed.
30
+ * @param signature The header value sent by the producer (e.g. `sha256=…`).
31
+ * @param secret The shared HMAC secret.
32
+ * @returns `true` only if the computed digest matches the supplied signature.
33
+ *
34
+ * Properties:
35
+ * - Accepts an optional `sha256=` prefix on the signature, but does not
36
+ * require it (mirrors common third-party header conventions).
37
+ * - Uses `timingSafeEqual` so an attacker cannot probe digit-by-digit.
38
+ * - Returns `false` for missing/empty values, non-hex input, and length
39
+ * mismatches without throwing.
40
+ * - Returns `false` if the secret is empty — an empty secret is almost
41
+ * always a misconfiguration we do not want to silently accept.
42
+ */
43
+ export function verifyWebhookSignature(
44
+ body: string | Uint8Array,
45
+ signature: string | undefined | null,
46
+ secret: string,
47
+ ): boolean {
48
+ if (!secret || typeof signature !== 'string' || signature.length === 0) {
49
+ return false;
50
+ }
51
+
52
+ const provided = signature.startsWith('sha256=')
53
+ ? signature.slice('sha256='.length)
54
+ : signature;
55
+
56
+ // Hex-encoded SHA-256 is always exactly 64 characters; reject anything
57
+ // else so we don't mistake other algorithms or junk strings for ours.
58
+ if (!/^[0-9a-f]+$/i.test(provided)) {
59
+ return false;
60
+ }
61
+
62
+ const expectedHex = signWebhookPayload(body, secret).slice('sha256='.length);
63
+
64
+ // Length mismatch — `timingSafeEqual` would throw; exposing the digest
65
+ // length is fine since it's a public property of SHA-256.
66
+ if (provided.length !== expectedHex.length) {
67
+ return false;
68
+ }
69
+
70
+ const a = Buffer.from(provided.toLowerCase(), 'hex');
71
+ const b = Buffer.from(expectedHex, 'hex');
72
+ if (a.length !== b.length || a.length === 0) return false;
73
+ return timingSafeEqual(a, b);
74
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/storage/memory.ts","../src/storage/sqlite.ts","../src/storage/postgres.ts"],"sourcesContent":["/**\n * In-memory storage adapter for development and testing.\n * All data is lost when the process exits.\n *\n * NOTE: This adapter is intended for development and testing only. It stores\n * all state in-process with no persistence or multi-process safety guarantees.\n */\n\nimport { generateId } from \"../utils/id\";\nimport type {\n StorageAdapter,\n WorkflowRunRecord,\n WorkflowRunStepRecord,\n WorkflowEventRecord,\n ListRunsOptions,\n ListEventsOptions,\n PaginatedResult,\n} from \"./types\";\n\n/**\n * In-memory implementation of StorageAdapter.\n * Useful for development, testing, and lightweight deployments.\n *\n * @example\n * ```typescript\n * import { WorkflowEngine } from 'stepflow';\n * import { MemoryStorageAdapter } from 'stepflow/storage';\n *\n * const storage = new MemoryStorageAdapter();\n * const engine = new WorkflowEngine({ storage });\n * ```\n */\nexport class MemoryStorageAdapter implements StorageAdapter {\n private runs = new Map<string, WorkflowRunRecord>();\n private steps = new Map<string, WorkflowRunStepRecord>();\n private events = new Map<string, WorkflowEventRecord>();\n\n // ============================================================================\n // Run Operations\n // ============================================================================\n\n /** Create and persist a new workflow run record. */\n async createRun(\n run: Omit<WorkflowRunRecord, \"id\" | \"createdAt\">,\n ): Promise<WorkflowRunRecord> {\n const record: WorkflowRunRecord = {\n ...run,\n id: generateId(),\n createdAt: new Date(),\n };\n this.runs.set(record.id, record);\n return record;\n }\n\n /** Retrieve a workflow run by ID, or null if not found. */\n async getRun(runId: string): Promise<WorkflowRunRecord | null> {\n return this.runs.get(runId) ?? null;\n }\n\n /** Apply partial updates to an existing workflow run. No-op if the run does not exist. */\n async updateRun(\n runId: string,\n updates: Partial<WorkflowRunRecord>,\n ): Promise<void> {\n const run = this.runs.get(runId);\n if (run) {\n Object.assign(run, updates);\n }\n }\n\n /** List workflow runs with optional filtering, sorting, and pagination. */\n async listRuns(\n options: ListRunsOptions = {},\n ): Promise<PaginatedResult<WorkflowRunRecord>> {\n let items = Array.from(this.runs.values());\n\n // Filter by kind\n if (options.kind) {\n items = items.filter((r) => r.kind === options.kind);\n }\n\n // Filter by status\n if (options.status) {\n const statuses = Array.isArray(options.status)\n ? options.status\n : [options.status];\n items = items.filter((r) => statuses.includes(r.status));\n }\n\n // Filter by parentRunId\n if (options.parentRunId !== undefined) {\n items = items.filter((r) => r.parentRunId === options.parentRunId);\n }\n\n // Sort\n const orderBy = options.orderBy ?? \"createdAt\";\n const direction = options.orderDirection ?? \"desc\";\n items.sort((a, b) => {\n const aVal = a[orderBy]?.getTime() ?? 0;\n const bVal = b[orderBy]?.getTime() ?? 0;\n return direction === \"asc\" ? aVal - bVal : bVal - aVal;\n });\n\n const total = items.length;\n const offset = options.offset ?? 0;\n const limit = options.limit ?? 50;\n\n items = items.slice(offset, offset + limit);\n\n return { items, total, limit, offset };\n }\n\n // ============================================================================\n // Step Operations\n // ============================================================================\n\n /** Create and persist a new step execution record. */\n async createStep(\n step: Omit<WorkflowRunStepRecord, \"id\">,\n ): Promise<WorkflowRunStepRecord> {\n const record: WorkflowRunStepRecord = {\n ...step,\n id: generateId(),\n };\n this.steps.set(record.id, record);\n return record;\n }\n\n /** Retrieve a step record by ID, or null if not found. */\n async getStep(stepId: string): Promise<WorkflowRunStepRecord | null> {\n return this.steps.get(stepId) ?? null;\n }\n\n /** Apply partial updates to an existing step record. No-op if the step does not exist. */\n async updateStep(\n stepId: string,\n updates: Partial<WorkflowRunStepRecord>,\n ): Promise<void> {\n const step = this.steps.get(stepId);\n if (step) {\n Object.assign(step, updates);\n }\n }\n\n /** Retrieve all step records for a workflow run, ordered by start time ascending. */\n async getStepsForRun(runId: string): Promise<WorkflowRunStepRecord[]> {\n return Array.from(this.steps.values())\n .filter((s) => s.runId === runId)\n .sort(\n (a, b) => (a.startedAt?.getTime() ?? 0) - (b.startedAt?.getTime() ?? 0),\n );\n }\n\n // ============================================================================\n // Event Operations\n // ============================================================================\n\n /** Persist a workflow event record. */\n async saveEvent(event: Omit<WorkflowEventRecord, \"id\">): Promise<void> {\n const record: WorkflowEventRecord = {\n ...event,\n id: generateId(),\n };\n this.events.set(record.id, record);\n }\n\n /** Retrieve events for a workflow run with optional filtering and pagination. */\n async getEventsForRun(\n runId: string,\n options: ListEventsOptions = {},\n ): Promise<WorkflowEventRecord[]> {\n let items = Array.from(this.events.values()).filter(\n (e) => e.runId === runId,\n );\n\n // Filter by step\n if (options.stepKey) {\n items = items.filter((e) => e.stepKey === options.stepKey);\n }\n\n // Filter by level\n if (options.level) {\n items = items.filter((e) => e.level === options.level);\n }\n\n // Sort by timestamp\n items.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n\n // Apply pagination\n const offset = options.offset ?? 0;\n const limit = options.limit ?? 100;\n\n return items.slice(offset, offset + limit);\n }\n\n // ============================================================================\n // Optional Operations\n // ============================================================================\n\n /** Delete runs (and their associated steps and events) created before the given date. Returns the number of deleted runs. */\n async deleteOldRuns(olderThan: Date): Promise<number> {\n let deleted = 0;\n for (const [id, run] of this.runs) {\n if (run.createdAt < olderThan) {\n // Delete associated steps\n for (const [stepId, step] of this.steps) {\n if (step.runId === id) {\n this.steps.delete(stepId);\n }\n }\n // Delete associated events\n for (const [eventId, event] of this.events) {\n if (event.runId === id) {\n this.events.delete(eventId);\n }\n }\n // Delete run\n this.runs.delete(id);\n deleted++;\n }\n }\n return deleted;\n }\n\n // ============================================================================\n // Testing Utilities\n // ============================================================================\n\n /**\n * Clear all stored data. Useful for testing.\n */\n clear(): void {\n this.runs.clear();\n this.steps.clear();\n this.events.clear();\n }\n\n /**\n * Get counts of stored records. Useful for testing.\n */\n getStats(): { runs: number; steps: number; events: number } {\n return {\n runs: this.runs.size,\n steps: this.steps.size,\n events: this.events.size,\n };\n }\n}\n","/**\n * SQLite storage adapter using better-sqlite3.\n *\n * Provides durable persistence for workflow runs, steps, and events.\n */\n\nimport type Database from \"better-sqlite3\";\nimport { generateId } from \"../utils/id\";\nimport type {\n StorageAdapter,\n WorkflowRunRecord,\n WorkflowRunStepRecord,\n WorkflowEventRecord,\n ListRunsOptions,\n ListEventsOptions,\n PaginatedResult,\n} from \"./types\";\nimport type { RunStatus, StepStatus, WorkflowError } from \"../core/types\";\nimport { sanitizeErrorForStorage } from \"../utils/logger\";\n\n// ============================================================================\n// Schema SQL\n// ============================================================================\n\n/**\n * SQL statements to create the workflow tables.\n * These create the workflow tables in your database.\n */\nconst CREATE_TABLES_SQL = `\n-- Workflow runs table\nCREATE TABLE IF NOT EXISTS workflow_runs (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL,\n status TEXT NOT NULL,\n parent_run_id TEXT,\n input_json TEXT NOT NULL,\n metadata_json TEXT NOT NULL,\n context_json TEXT NOT NULL,\n error_json TEXT,\n created_at TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT\n);\n\nCREATE INDEX IF NOT EXISTS idx_workflow_runs_kind_status ON workflow_runs(kind, status);\nCREATE INDEX IF NOT EXISTS idx_workflow_runs_parent ON workflow_runs(parent_run_id);\nCREATE INDEX IF NOT EXISTS idx_workflow_runs_created ON workflow_runs(created_at);\nCREATE INDEX IF NOT EXISTS idx_workflow_runs_status ON workflow_runs(status);\n\n-- Workflow run steps table\nCREATE TABLE IF NOT EXISTS workflow_run_steps (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL,\n step_key TEXT NOT NULL,\n step_name TEXT,\n status TEXT NOT NULL,\n attempt INTEGER NOT NULL DEFAULT 1,\n result_json TEXT,\n error_json TEXT,\n started_at TEXT,\n finished_at TEXT,\n FOREIGN KEY (run_id) REFERENCES workflow_runs(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_workflow_run_steps_run ON workflow_run_steps(run_id);\nCREATE INDEX IF NOT EXISTS idx_workflow_run_steps_run_key ON workflow_run_steps(run_id, step_key);\n\n-- Workflow events table\nCREATE TABLE IF NOT EXISTS workflow_events (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL,\n step_key TEXT,\n event_type TEXT NOT NULL,\n level TEXT NOT NULL,\n payload_json TEXT,\n timestamp TEXT NOT NULL,\n FOREIGN KEY (run_id) REFERENCES workflow_runs(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_workflow_events_run ON workflow_events(run_id);\nCREATE INDEX IF NOT EXISTS idx_workflow_events_run_ts ON workflow_events(run_id, timestamp);\n`;\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/**\n * Configuration options for the SQLite storage adapter.\n */\nexport interface SQLiteStorageConfig {\n /**\n * The better-sqlite3 database instance.\n * Can be an in-memory database (`:memory:`) or a file path.\n */\n db: Database.Database;\n\n /**\n * Whether to automatically create tables on initialization.\n * Default: true\n */\n autoCreateTables?: boolean;\n\n /**\n * @deprecated This option is not currently supported and will be ignored.\n * Table names are always prefixed with 'workflow_'.\n */\n tablePrefix?: string;\n}\n\n// ============================================================================\n// SQLite Storage Adapter\n// ============================================================================\n\n/**\n * SQLite implementation of StorageAdapter using better-sqlite3.\n *\n * Features:\n * - Synchronous operations (better-sqlite3 is sync by design)\n * - Automatic table creation\n * - Transaction support\n * - Customizable table prefix\n *\n * @example\n * ```typescript\n * import Database from 'better-sqlite3';\n * import { SQLiteStorageAdapter } from 'stepflow/storage';\n *\n * const db = new Database('./workflows.db');\n * const storage = new SQLiteStorageAdapter({ db });\n *\n * const engine = new WorkflowEngine({ storage });\n * ```\n */\nexport class SQLiteStorageAdapter implements StorageAdapter {\n private db: Database.Database;\n private prefix: string;\n\n // Prepared statements (cached for performance)\n private stmts: {\n insertRun: Database.Statement;\n getRun: Database.Statement;\n updateRun: Database.Statement;\n listRuns: Database.Statement;\n countRuns: Database.Statement;\n insertStep: Database.Statement;\n getStep: Database.Statement;\n updateStep: Database.Statement;\n getStepsForRun: Database.Statement;\n insertEvent: Database.Statement;\n getEventsForRun: Database.Statement;\n deleteOldRuns: Database.Statement;\n deleteStepsForRuns: Database.Statement;\n deleteEventsForRuns: Database.Statement;\n getInterruptedRuns: Database.Statement;\n getLastCompletedStep: Database.Statement;\n } | null = null;\n\n constructor(config: SQLiteStorageConfig) {\n this.db = config.db;\n this.prefix = config.tablePrefix ?? \"workflow\";\n\n // Enable foreign keys\n this.db.pragma(\"foreign_keys = ON\");\n\n // Create tables if needed\n if (config.autoCreateTables !== false) {\n this.createTables();\n }\n\n // Prepare statements\n this.prepareStatements();\n }\n\n /**\n * Create the workflow tables if they don't exist.\n */\n private createTables(): void {\n this.db.exec(CREATE_TABLES_SQL);\n }\n\n /**\n * Prepare all SQL statements for better performance.\n */\n private prepareStatements(): void {\n this.stmts = {\n insertRun: this.db.prepare(`\n INSERT INTO workflow_runs (id, kind, status, parent_run_id, input_json, metadata_json, context_json, error_json, created_at, started_at, finished_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `),\n getRun: this.db.prepare(`\n SELECT * FROM workflow_runs WHERE id = ?\n `),\n updateRun: this.db.prepare(`\n UPDATE workflow_runs\n SET status = COALESCE(?, status),\n context_json = COALESCE(?, context_json),\n error_json = COALESCE(?, error_json),\n started_at = COALESCE(?, started_at),\n finished_at = COALESCE(?, finished_at)\n WHERE id = ?\n `),\n // json_each() is a SQLite table-valued function that expands a JSON array into rows.\n // We use it here to support filtering by multiple statuses in a single prepared statement:\n // the caller passes a JSON array like '[\"running\",\"failed\"]', and json_each unpacks it\n // so the IN clause can match against each value. When status is NULL, the entire\n // condition is bypassed via the (? IS NULL OR ...) guard.\n listRuns: this.db.prepare(`\n SELECT * FROM workflow_runs\n WHERE (? IS NULL OR kind = ?)\n AND (? IS NULL OR status IN (SELECT value FROM json_each(?)))\n AND (? IS NULL OR parent_run_id = ?)\n ORDER BY created_at DESC\n LIMIT ? OFFSET ?\n `),\n countRuns: this.db.prepare(`\n SELECT COUNT(*) as count FROM workflow_runs\n WHERE (? IS NULL OR kind = ?)\n AND (? IS NULL OR status IN (SELECT value FROM json_each(?)))\n AND (? IS NULL OR parent_run_id = ?)\n `),\n insertStep: this.db.prepare(`\n INSERT INTO workflow_run_steps (id, run_id, step_key, step_name, status, attempt, result_json, error_json, started_at, finished_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `),\n getStep: this.db.prepare(`\n SELECT * FROM workflow_run_steps WHERE id = ?\n `),\n updateStep: this.db.prepare(`\n UPDATE workflow_run_steps\n SET status = COALESCE(?, status),\n attempt = COALESCE(?, attempt),\n result_json = COALESCE(?, result_json),\n error_json = COALESCE(?, error_json),\n finished_at = COALESCE(?, finished_at)\n WHERE id = ?\n `),\n getStepsForRun: this.db.prepare(`\n SELECT * FROM workflow_run_steps WHERE run_id = ? ORDER BY started_at ASC\n `),\n insertEvent: this.db.prepare(`\n INSERT INTO workflow_events (id, run_id, step_key, event_type, level, payload_json, timestamp)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `),\n getEventsForRun: this.db.prepare(`\n SELECT * FROM workflow_events\n WHERE run_id = ?\n AND (? IS NULL OR step_key = ?)\n AND (? IS NULL OR level = ?)\n ORDER BY timestamp ASC\n LIMIT ? OFFSET ?\n `),\n deleteOldRuns: this.db.prepare(`\n DELETE FROM workflow_runs WHERE created_at < ?\n `),\n deleteStepsForRuns: this.db.prepare(`\n DELETE FROM workflow_run_steps WHERE run_id IN (SELECT id FROM workflow_runs WHERE created_at < ?)\n `),\n deleteEventsForRuns: this.db.prepare(`\n DELETE FROM workflow_events WHERE run_id IN (SELECT id FROM workflow_runs WHERE created_at < ?)\n `),\n getInterruptedRuns: this.db.prepare(`\n SELECT * FROM workflow_runs\n WHERE status IN ('queued', 'running')\n ORDER BY created_at ASC\n `),\n getLastCompletedStep: this.db.prepare(`\n SELECT * FROM workflow_run_steps\n WHERE run_id = ? AND status = 'succeeded'\n ORDER BY finished_at DESC\n LIMIT 1\n `),\n };\n }\n\n // ============================================================================\n // Run Operations\n // ============================================================================\n\n async createRun(\n run: Omit<WorkflowRunRecord, \"id\" | \"createdAt\">,\n ): Promise<WorkflowRunRecord> {\n const id = generateId();\n const createdAt = new Date();\n\n this.stmts!.insertRun.run(\n id,\n run.kind,\n run.status,\n run.parentRunId ?? null,\n JSON.stringify(run.input),\n JSON.stringify(run.metadata),\n JSON.stringify(run.context),\n run.error ? JSON.stringify(sanitizeErrorForStorage(run.error)) : null,\n createdAt.toISOString(),\n run.startedAt?.toISOString() ?? null,\n run.finishedAt?.toISOString() ?? null,\n );\n\n return {\n ...run,\n id,\n createdAt,\n };\n }\n\n async getRun(runId: string): Promise<WorkflowRunRecord | null> {\n const row = this.stmts!.getRun.get(runId) as SQLiteRunRow | undefined;\n return row ? this.mapRunRow(row) : null;\n }\n\n async updateRun(\n runId: string,\n updates: Partial<WorkflowRunRecord>,\n ): Promise<void> {\n this.stmts!.updateRun.run(\n updates.status ?? null,\n updates.context ? JSON.stringify(updates.context) : null,\n updates.error\n ? JSON.stringify(sanitizeErrorForStorage(updates.error))\n : null,\n updates.startedAt?.toISOString() ?? null,\n updates.finishedAt?.toISOString() ?? null,\n runId,\n );\n }\n\n async listRuns(\n options: ListRunsOptions = {},\n ): Promise<PaginatedResult<WorkflowRunRecord>> {\n const limit = options.limit ?? 50;\n const offset = options.offset ?? 0;\n const kind = options.kind ?? null;\n const status = options.status ?? null;\n const statusJson = status\n ? JSON.stringify(Array.isArray(status) ? status : [status])\n : null;\n const parentRunId = options.parentRunId ?? null;\n\n const rows = this.stmts!.listRuns.all(\n kind,\n kind,\n statusJson,\n statusJson,\n parentRunId,\n parentRunId,\n limit,\n offset,\n ) as SQLiteRunRow[];\n\n const countResult = this.stmts!.countRuns.get(\n kind,\n kind,\n statusJson,\n statusJson,\n parentRunId,\n parentRunId,\n ) as { count: number };\n\n return {\n items: rows.map((row) => this.mapRunRow(row)),\n total: countResult.count,\n limit,\n offset,\n };\n }\n\n // ============================================================================\n // Step Operations\n // ============================================================================\n\n async createStep(\n step: Omit<WorkflowRunStepRecord, \"id\">,\n ): Promise<WorkflowRunStepRecord> {\n const id = generateId();\n\n this.stmts!.insertStep.run(\n id,\n step.runId,\n step.stepKey,\n step.stepName,\n step.status,\n step.attempt,\n step.result !== undefined ? JSON.stringify(step.result) : null,\n step.error ? JSON.stringify(sanitizeErrorForStorage(step.error)) : null,\n step.startedAt?.toISOString() ?? null,\n step.finishedAt?.toISOString() ?? null,\n );\n\n return { ...step, id };\n }\n\n async getStep(stepId: string): Promise<WorkflowRunStepRecord | null> {\n const row = this.stmts!.getStep.get(stepId) as SQLiteStepRow | undefined;\n return row ? this.mapStepRow(row) : null;\n }\n\n async updateStep(\n stepId: string,\n updates: Partial<WorkflowRunStepRecord>,\n ): Promise<void> {\n this.stmts!.updateStep.run(\n updates.status ?? null,\n updates.attempt ?? null,\n updates.result !== undefined ? JSON.stringify(updates.result) : null,\n updates.error\n ? JSON.stringify(sanitizeErrorForStorage(updates.error))\n : null,\n updates.finishedAt?.toISOString() ?? null,\n stepId,\n );\n }\n\n async getStepsForRun(runId: string): Promise<WorkflowRunStepRecord[]> {\n const rows = this.stmts!.getStepsForRun.all(runId) as SQLiteStepRow[];\n return rows.map((row) => this.mapStepRow(row));\n }\n\n // ============================================================================\n // Event Operations\n // ============================================================================\n\n async saveEvent(event: Omit<WorkflowEventRecord, \"id\">): Promise<void> {\n const id = generateId();\n\n this.stmts!.insertEvent.run(\n id,\n event.runId,\n event.stepKey ?? null,\n event.eventType,\n event.level,\n event.payload !== undefined ? JSON.stringify(event.payload) : null,\n event.timestamp.toISOString(),\n );\n }\n\n async getEventsForRun(\n runId: string,\n options: ListEventsOptions = {},\n ): Promise<WorkflowEventRecord[]> {\n const limit = options.limit ?? 1000;\n const offset = options.offset ?? 0;\n const stepKey = options.stepKey ?? null;\n const level = options.level ?? null;\n\n const rows = this.stmts!.getEventsForRun.all(\n runId,\n stepKey,\n stepKey,\n level,\n level,\n limit,\n offset,\n ) as SQLiteEventRow[];\n\n return rows.map((row) => this.mapEventRow(row));\n }\n\n // ============================================================================\n // Transaction Support\n // ============================================================================\n\n /**\n * Execute a function within a database transaction (async interface).\n *\n * @deprecated Use `transactionSync()` instead. This method only works when\n * the callback performs purely synchronous operations wrapped in async/await.\n * If the callback awaits real async I/O (network, timers, etc.), it will\n * throw an error. `transactionSync()` makes the synchronous requirement explicit.\n */\n async transaction<T>(fn: (tx: StorageAdapter) => Promise<T>): Promise<T> {\n let fnPromise!: Promise<T>;\n this.transactionSync(() => {\n fnPromise = fn(this);\n });\n return fnPromise;\n }\n\n /**\n * Execute a synchronous transaction (preferred for better-sqlite3).\n * Use this when you need transaction guarantees.\n *\n * @example\n * ```typescript\n * storage.transactionSync(() => {\n * // Multiple operations in a single transaction\n * const run = storage.createRunSync(...);\n * storage.updateRunSync(run.id, ...);\n * });\n * ```\n */\n transactionSync<T>(fn: () => T): T {\n return this.db.transaction(fn)();\n }\n\n // ============================================================================\n // Cleanup Operations\n // ============================================================================\n\n /**\n * Delete runs older than the specified date.\n * Also deletes associated steps and events (via CASCADE).\n */\n async deleteOldRuns(olderThan: Date): Promise<number> {\n const isoDate = olderThan.toISOString();\n\n // Due to CASCADE, steps and events are deleted automatically\n // But we do it explicitly for databases without CASCADE support\n return this.db.transaction(() => {\n this.stmts!.deleteEventsForRuns.run(isoDate);\n this.stmts!.deleteStepsForRuns.run(isoDate);\n const result = this.stmts!.deleteOldRuns.run(isoDate);\n return result.changes;\n })();\n }\n\n // ============================================================================\n // Resume Support\n // ============================================================================\n\n /**\n * Get all runs that were interrupted (status is 'running' or 'queued').\n * These runs can potentially be resumed.\n */\n async getInterruptedRuns(): Promise<WorkflowRunRecord[]> {\n const rows = this.stmts!.getInterruptedRuns.all() as SQLiteRunRow[];\n return rows.map((row) => this.mapRunRow(row));\n }\n\n /**\n * Get the last completed step for a run.\n * Useful for resuming from a checkpoint.\n */\n async getLastCompletedStep(\n runId: string,\n ): Promise<WorkflowRunStepRecord | null> {\n const row = this.stmts!.getLastCompletedStep.get(runId) as\n | SQLiteStepRow\n | undefined;\n return row ? this.mapStepRow(row) : null;\n }\n\n // ============================================================================\n // Row Mapping\n // ============================================================================\n\n private mapRunRow(row: SQLiteRunRow): WorkflowRunRecord {\n return {\n id: row.id,\n kind: row.kind,\n status: row.status as RunStatus,\n parentRunId: row.parent_run_id ?? undefined,\n input: JSON.parse(row.input_json),\n metadata: JSON.parse(row.metadata_json),\n context: JSON.parse(row.context_json),\n error: row.error_json ? JSON.parse(row.error_json) : undefined,\n createdAt: new Date(row.created_at),\n startedAt: row.started_at ? new Date(row.started_at) : undefined,\n finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,\n };\n }\n\n private mapStepRow(row: SQLiteStepRow): WorkflowRunStepRecord {\n return {\n id: row.id,\n runId: row.run_id,\n stepKey: row.step_key,\n stepName: row.step_name,\n status: row.status as StepStatus,\n attempt: row.attempt,\n result: row.result_json ? JSON.parse(row.result_json) : undefined,\n error: row.error_json ? JSON.parse(row.error_json) : undefined,\n startedAt: row.started_at ? new Date(row.started_at) : undefined,\n finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,\n };\n }\n\n private mapEventRow(row: SQLiteEventRow): WorkflowEventRecord {\n return {\n id: row.id,\n runId: row.run_id,\n stepKey: row.step_key ?? undefined,\n eventType: row.event_type,\n level: row.level as \"info\" | \"warn\" | \"error\",\n payload: row.payload_json ? JSON.parse(row.payload_json) : undefined,\n timestamp: new Date(row.timestamp),\n };\n }\n\n // ============================================================================\n // Utility Methods\n // ============================================================================\n\n /**\n * Close the database connection.\n */\n close(): void {\n this.db.close();\n }\n\n /**\n * Get database statistics.\n */\n getStats(): { runs: number; steps: number; events: number } {\n const runsCount = this.db\n .prepare(\"SELECT COUNT(*) as count FROM workflow_runs\")\n .get() as { count: number };\n const stepsCount = this.db\n .prepare(\"SELECT COUNT(*) as count FROM workflow_run_steps\")\n .get() as { count: number };\n const eventsCount = this.db\n .prepare(\"SELECT COUNT(*) as count FROM workflow_events\")\n .get() as { count: number };\n\n return {\n runs: runsCount.count,\n steps: stepsCount.count,\n events: eventsCount.count,\n };\n }\n}\n\n// ============================================================================\n// SQLite Row Types\n// ============================================================================\n\ninterface SQLiteRunRow {\n id: string;\n kind: string;\n status: string;\n parent_run_id: string | null;\n input_json: string;\n metadata_json: string;\n context_json: string;\n error_json: string | null;\n created_at: string;\n started_at: string | null;\n finished_at: string | null;\n}\n\ninterface SQLiteStepRow {\n id: string;\n run_id: string;\n step_key: string;\n step_name: string;\n status: string;\n attempt: number;\n result_json: string | null;\n error_json: string | null;\n started_at: string | null;\n finished_at: string | null;\n}\n\ninterface SQLiteEventRow {\n id: string;\n run_id: string;\n step_key: string | null;\n event_type: string;\n level: string;\n payload_json: string | null;\n timestamp: string;\n}\n","/**\n * PostgreSQL storage adapter using Kysely.\n *\n * Provides durable persistence for workflow runs, steps, and events\n * with support for distributed deployments and connection pooling.\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 { generateId } from \"../utils/id.js\";\nimport type {\n StorageAdapter,\n WorkflowRunRecord,\n WorkflowRunStepRecord,\n WorkflowEventRecord,\n ListRunsOptions,\n ListEventsOptions,\n PaginatedResult,\n CreateRunInput,\n UpdateRunInput,\n StepResult,\n ExtendedWorkflowRunRecord,\n ExtendedRunStatus,\n ExtendedStepStatus,\n} from \"./types.js\";\nimport type { RunStatus, StepStatus, WorkflowError } from \"../core/types.js\";\nimport { sanitizeErrorForStorage } from \"../utils/logger.js\";\n\n/** Strip `stack` from a generic error record before persistence. */\nfunction stripStack<T extends Record<string, unknown>>(\n error: T,\n): Omit<T, \"stack\"> {\n const { stack: _stack, ...rest } = error;\n return rest as Omit<T, \"stack\">;\n}\n\n// ============================================================================\n// Database Types (Kysely schema)\n// ============================================================================\n\n/** Kysely row type for the workflow_runs table. */\ninterface WorkflowRunsTable {\n id: string;\n /** Workflow kind identifier (e.g. 'order-processing'). */\n kind: string;\n /** Current run status (maps to RunStatus). */\n status: string;\n /** Parent run ID for child workflows, null for top-level runs. */\n parent_run_id: string | null;\n /** JSON-serialized input payload. */\n input_json: string;\n /** JSON-serialized arbitrary metadata. */\n metadata_json: string;\n /** JSON-serialized workflow context (accumulated step results). */\n context_json: string;\n /** JSON-serialized output payload, null until run completes. */\n output_json: string | null;\n /** JSON-serialized error object, null unless run failed. */\n error_json: string | null;\n /** Execution priority (lower number = higher precedence). */\n priority: number;\n /** Workflow-level timeout in milliseconds, null for no timeout. */\n timeout_ms: number | null;\n created_at: Date;\n started_at: Date | null;\n finished_at: Date | null;\n}\n\n/** Kysely row type for the workflow_run_steps table. */\ninterface WorkflowRunStepsTable {\n id: string;\n /** Foreign key to workflow_runs.id. */\n run_id: string;\n /** Unique step identifier within the workflow definition. */\n step_key: string;\n /** Human-readable step name. */\n step_name: string;\n /** Current step status (maps to StepStatus). */\n status: string;\n /** Current retry attempt number (1-based). */\n attempt: number;\n /** JSON-serialized step result, null until step completes. */\n result_json: string | null;\n /** JSON-serialized error object, null unless step failed. */\n error_json: string | null;\n started_at: Date | null;\n finished_at: Date | null;\n}\n\n/** Kysely row type for the stepflow_step_results table (extended storage). */\ninterface StepflowStepResultsTable {\n id: string;\n /** Foreign key to runs.id. */\n run_id: string;\n /** Step name identifier. */\n step_name: string;\n /** Current step status (maps to ExtendedStepStatus). */\n status: string;\n /** JSON-serialized step output, null until step completes. */\n output_json: string | null;\n /** JSON-serialized error object, null unless step failed. */\n error_json: string | null;\n /** Current retry attempt number (1-based). */\n attempt: number;\n started_at: Date | null;\n completed_at: Date | null;\n}\n\n/** Kysely row type for the workflow_events table. */\ninterface WorkflowEventsTable {\n id: string;\n /** Foreign key to workflow_runs.id. */\n run_id: string;\n /** Step key that emitted this event, null for run-level events. */\n step_key: string | null;\n /** Event type identifier (e.g. 'run.started', 'step.completed'). */\n event_type: string;\n /** Severity level: 'info', 'warn', or 'error'. */\n level: string;\n /** JSON-serialized event payload, null if no extra data. */\n payload_json: string | null;\n timestamp: Date;\n}\n\ninterface StepflowDatabase {\n runs: WorkflowRunsTable;\n workflow_run_steps: WorkflowRunStepsTable;\n workflow_events: WorkflowEventsTable;\n stepflow_step_results: StepflowStepResultsTable;\n}\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/**\n * Configuration options for the PostgreSQL storage adapter.\n */\nexport interface PostgresStorageConfig {\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 Stepflow tables.\n * @default 'public'\n */\n schema?: string;\n\n /**\n * Automatically create tables on initialize().\n * @default true\n */\n autoMigrate?: boolean;\n}\n\n// ============================================================================\n// PostgreSQL Storage Adapter\n// ============================================================================\n\n/**\n * PostgreSQL implementation of StorageAdapter using Kysely.\n *\n * Features:\n * - Connection pooling (shared or dedicated)\n * - Automatic table creation\n * - Transaction support\n * - Atomic dequeue operations for distributed workers\n * - JSONB storage for flexible data\n *\n * @example\n * ```typescript\n * import { PostgresStorageAdapter } from 'stepflow/storage';\n *\n * const storage = new PostgresStorageAdapter({\n * connectionString: process.env.DATABASE_URL,\n * });\n *\n * await storage.initialize();\n *\n * const engine = new WorkflowEngine({ storage });\n * ```\n *\n * @example Sharing connection pool\n * ```typescript\n * import pg from 'pg';\n * import { PostgresStorageAdapter } from 'stepflow/storage';\n *\n * // Application's existing pool\n * const pool = new pg.Pool({\n * connectionString: process.env.DATABASE_URL,\n * max: 20,\n * });\n *\n * // Share with Stepflow\n * const storage = new PostgresStorageAdapter({ pool });\n * ```\n */\nexport class PostgresStorageAdapter implements StorageAdapter {\n private db!: KyselyType<StepflowDatabase>;\n private pool!: Pool;\n private ownsPool = false;\n private schema: string;\n private autoMigrate: boolean;\n private initialized = false;\n private config: PostgresStorageConfig;\n\n constructor(config: PostgresStorageConfig) {\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.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 \"PostgresStorageAdapter is not initialized. Call initialize() before using the adapter.\",\n );\n }\n }\n\n /**\n * Initialize the storage adapter.\n * Creates tables 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({\n connectionString: this.config.connectionString,\n });\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 \"PostgresStorageConfig 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 // Kysely's destroy() already calls pool.end() via PostgresDialect,\n // so we must NOT call pool.end() again.\n }\n\n // ============================================================================\n // Schema Creation\n // ============================================================================\n\n /**\n * Create the workflow tables if they don't exist.\n */\n private async createTables(): Promise<void> {\n // Create schema if not public\n if (this.schema !== \"public\") {\n await sql`CREATE SCHEMA IF NOT EXISTS ${sql.ref(this.schema)}`.execute(\n this.db,\n );\n }\n\n // Create runs table with new columns\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql.table(`${this.schema}.runs`)} (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL,\n status TEXT NOT NULL,\n parent_run_id TEXT,\n input_json JSONB NOT NULL DEFAULT '{}',\n metadata_json JSONB NOT NULL DEFAULT '{}',\n context_json JSONB NOT NULL DEFAULT '{}',\n output_json JSONB,\n error_json JSONB,\n priority INTEGER NOT NULL DEFAULT 0,\n timeout_ms INTEGER,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n started_at TIMESTAMPTZ,\n finished_at TIMESTAMPTZ,\n CONSTRAINT runs_status_check CHECK (\n status IN ('pending', 'queued', 'running', 'succeeded', 'failed', 'canceled', 'timeout')\n )\n )\n `.execute(this.db);\n\n // Add new columns if they don't exist (for migrations)\n await sql`\n ALTER TABLE ${sql.table(`${this.schema}.runs`)}\n ADD COLUMN IF NOT EXISTS output_json JSONB,\n ADD COLUMN IF NOT EXISTS priority INTEGER NOT NULL DEFAULT 0,\n ADD COLUMN IF NOT EXISTS timeout_ms INTEGER\n `\n .execute(this.db)\n .catch(() => {\n // Ignore if columns already exist or syntax not supported\n });\n\n // Create indexes for runs\n await sql`\n CREATE INDEX IF NOT EXISTS idx_runs_kind_status\n ON ${sql.table(`${this.schema}.runs`)} (kind, status)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS idx_runs_parent\n ON ${sql.table(`${this.schema}.runs`)} (parent_run_id)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS idx_runs_created\n ON ${sql.table(`${this.schema}.runs`)} (created_at DESC)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS idx_runs_status\n ON ${sql.table(`${this.schema}.runs`)} (status)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS idx_runs_priority\n ON ${sql.table(`${this.schema}.runs`)} (priority DESC, created_at ASC)\n `.execute(this.db);\n\n // Create workflow_run_steps table (legacy)\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql.table(`${this.schema}.workflow_run_steps`)} (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES ${sql.table(`${this.schema}.runs`)} (id) ON DELETE CASCADE,\n step_key TEXT NOT NULL,\n step_name TEXT,\n status TEXT NOT NULL,\n attempt INTEGER NOT NULL DEFAULT 1,\n result_json JSONB,\n error_json JSONB,\n started_at TIMESTAMPTZ,\n finished_at TIMESTAMPTZ,\n CONSTRAINT workflow_run_steps_status_check CHECK (\n status IN ('pending', 'running', 'succeeded', 'failed', 'skipped', 'completed')\n )\n )\n `.execute(this.db);\n\n // Create indexes for workflow_run_steps\n await sql`\n CREATE INDEX IF NOT EXISTS idx_workflow_run_steps_run\n ON ${sql.table(`${this.schema}.workflow_run_steps`)} (run_id)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS idx_workflow_run_steps_run_key\n ON ${sql.table(`${this.schema}.workflow_run_steps`)} (run_id, step_key)\n `.execute(this.db);\n\n // Create stepflow_step_results table (new)\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql.table(`${this.schema}.stepflow_step_results`)} (\n id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,\n run_id TEXT NOT NULL REFERENCES ${sql.table(`${this.schema}.runs`)} (id) ON DELETE CASCADE,\n step_name TEXT NOT NULL,\n status TEXT NOT NULL,\n output_json JSONB,\n error_json JSONB,\n attempt INTEGER NOT NULL DEFAULT 1,\n started_at TIMESTAMPTZ,\n completed_at TIMESTAMPTZ,\n CONSTRAINT stepflow_step_results_status_check CHECK (\n status IN ('pending', 'running', 'completed', 'failed', 'skipped')\n ),\n UNIQUE (run_id, step_name)\n )\n `.execute(this.db);\n\n // Create indexes for stepflow_step_results\n await sql`\n CREATE INDEX IF NOT EXISTS idx_stepflow_step_results_run\n ON ${sql.table(`${this.schema}.stepflow_step_results`)} (run_id)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS idx_stepflow_step_results_run_name\n ON ${sql.table(`${this.schema}.stepflow_step_results`)} (run_id, step_name)\n `.execute(this.db);\n\n // Create workflow_events table\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql.table(`${this.schema}.workflow_events`)} (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES ${sql.table(`${this.schema}.runs`)} (id) ON DELETE CASCADE,\n step_key TEXT,\n event_type TEXT NOT NULL,\n level TEXT NOT NULL,\n payload_json JSONB,\n timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n CONSTRAINT workflow_events_level_check CHECK (\n level IN ('info', 'warn', 'error')\n )\n )\n `.execute(this.db);\n\n // Create indexes for workflow_events\n await sql`\n CREATE INDEX IF NOT EXISTS idx_workflow_events_run\n ON ${sql.table(`${this.schema}.workflow_events`)} (run_id)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS idx_workflow_events_run_ts\n ON ${sql.table(`${this.schema}.workflow_events`)} (run_id, timestamp)\n `.execute(this.db);\n }\n\n // ============================================================================\n // Run Operations\n // ============================================================================\n\n /**\n * Create a new workflow run.\n * Supports both legacy and new CreateRunInput interfaces.\n */\n async createRun(\n run: CreateRunInput | Omit<WorkflowRunRecord, \"id\" | \"createdAt\">,\n ): Promise<WorkflowRunRecord> {\n this.ensureInitialized();\n const id = \"id\" in run && run.id ? run.id : generateId();\n const createdAt = new Date();\n\n await this.qb\n .insertInto(\"runs\")\n .values({\n id,\n kind: run.kind,\n status: run.status,\n parent_run_id: \"parentRunId\" in run ? (run.parentRunId ?? null) : null,\n input_json: JSON.stringify(run.input),\n metadata_json: JSON.stringify(run.metadata ?? {}),\n context_json: JSON.stringify(run.context ?? {}), // Default to empty object\n output_json: null,\n error_json:\n \"error\" in run && run.error\n ? JSON.stringify(sanitizeErrorForStorage(run.error))\n : null,\n priority: \"priority\" in run ? (run.priority ?? 0) : 0,\n timeout_ms: \"timeoutMs\" in run ? (run.timeoutMs ?? null) : null,\n created_at: createdAt,\n started_at: null,\n finished_at: null,\n })\n .execute();\n\n return {\n id,\n kind: run.kind,\n status: run.status as RunStatus,\n parentRunId: \"parentRunId\" in run ? run.parentRunId : undefined,\n input: run.input,\n context: run.context ?? {},\n output: undefined,\n error: undefined,\n metadata: run.metadata ?? {},\n priority: \"priority\" in run ? (run.priority ?? 0) : 0,\n timeoutMs: \"timeoutMs\" in run ? run.timeoutMs : undefined,\n createdAt,\n };\n }\n\n async getRun(runId: string): Promise<WorkflowRunRecord | null> {\n this.ensureInitialized();\n const row = await this.qb\n .selectFrom(\"runs\")\n .selectAll()\n .where(\"id\", \"=\", runId)\n .executeTakeFirst();\n\n return row ? this.mapRunRow(row) : null;\n }\n\n /**\n * Update a workflow run.\n * Supports both legacy Partial<WorkflowRunRecord> and new UpdateRunInput interfaces.\n */\n async updateRun(\n runId: string,\n updates: UpdateRunInput | Partial<WorkflowRunRecord>,\n ): Promise<void> {\n this.ensureInitialized();\n const updateData: Partial<WorkflowRunsTable> = {};\n\n if (updates.status !== undefined) {\n updateData.status = updates.status;\n }\n if (updates.context !== undefined) {\n updateData.context_json = JSON.stringify(updates.context);\n }\n if (\"output\" in updates && updates.output !== undefined) {\n updateData.output_json = JSON.stringify(updates.output);\n }\n if (updates.error !== undefined) {\n updateData.error_json = JSON.stringify(\n sanitizeErrorForStorage(updates.error),\n );\n }\n if (updates.startedAt !== undefined) {\n updateData.started_at = updates.startedAt;\n }\n if (updates.finishedAt !== undefined) {\n updateData.finished_at = updates.finishedAt;\n }\n\n if (Object.keys(updateData).length > 0) {\n await this.qb\n .updateTable(\"runs\")\n .set(updateData)\n .where(\"id\", \"=\", runId)\n .execute();\n }\n }\n\n /**\n * List workflow runs with filtering and pagination.\n */\n /**\n * Apply common run filters to a Kysely query builder.\n * Used by both the data query and count query in listRuns to avoid duplication.\n */\n private applyRunsFilters<T extends { where(col: any, op: any, val: any): T }>(\n query: T,\n options: ListRunsOptions,\n ): T {\n if (options.kind) {\n query = query.where(\"kind\", \"=\", options.kind);\n }\n if (options.status) {\n const statuses = Array.isArray(options.status)\n ? options.status\n : [options.status];\n query = query.where(\"status\", \"in\", statuses);\n }\n if (options.parentRunId !== undefined) {\n query = query.where(\"parent_run_id\", \"=\", options.parentRunId);\n }\n return query;\n }\n\n async listRuns(\n options: ListRunsOptions = {},\n ): Promise<PaginatedResult<WorkflowRunRecord>> {\n this.ensureInitialized();\n const limit = options.limit ?? 50;\n const offset = options.offset ?? 0;\n\n let query = this.applyRunsFilters(\n this.qb.selectFrom(\"runs\").selectAll(),\n options,\n );\n\n const countQuery = this.applyRunsFilters(\n this.qb.selectFrom(\"runs\").select(sql<number>`count(*)`.as(\"count\")),\n options,\n );\n\n const countResult = (await countQuery.executeTakeFirst()) as\n | { count?: string | number }\n | undefined;\n const total = Number(countResult?.count ?? 0);\n\n // Order\n const orderBy = options.orderBy ?? \"createdAt\";\n const orderDirection = options.orderDirection ?? \"desc\";\n const orderColumn =\n orderBy === \"createdAt\"\n ? \"created_at\"\n : orderBy === \"startedAt\"\n ? \"started_at\"\n : \"finished_at\";\n\n query = query.orderBy(orderColumn, orderDirection);\n\n // Apply pagination\n query = query.limit(limit).offset(offset);\n\n const rows = await query.execute();\n\n return {\n items: rows.map((row) => this.mapRunRow(row)),\n total,\n };\n }\n\n // ============================================================================\n // Step Operations\n // ============================================================================\n\n async createStep(\n step: Omit<WorkflowRunStepRecord, \"id\">,\n ): Promise<WorkflowRunStepRecord> {\n this.ensureInitialized();\n const id = generateId();\n\n await this.qb\n .insertInto(\"workflow_run_steps\")\n .values({\n id,\n run_id: step.runId,\n step_key: step.stepKey,\n step_name: step.stepName,\n status: step.status,\n attempt: step.attempt,\n result_json:\n step.result !== undefined ? JSON.stringify(step.result) : null,\n error_json: step.error\n ? JSON.stringify(sanitizeErrorForStorage(step.error))\n : null,\n started_at: step.startedAt ?? null,\n finished_at: step.finishedAt ?? null,\n })\n .execute();\n\n return { ...step, id };\n }\n\n async getStep(stepId: string): Promise<WorkflowRunStepRecord | null> {\n this.ensureInitialized();\n const row = await this.qb\n .selectFrom(\"workflow_run_steps\")\n .selectAll()\n .where(\"id\", \"=\", stepId)\n .executeTakeFirst();\n\n return row ? this.mapStepRow(row) : null;\n }\n\n async updateStep(\n stepId: string,\n updates: Partial<WorkflowRunStepRecord>,\n ): Promise<void> {\n this.ensureInitialized();\n const updateData: Partial<WorkflowRunStepsTable> = {};\n\n if (updates.status !== undefined) {\n updateData.status = updates.status;\n }\n if (updates.attempt !== undefined) {\n updateData.attempt = updates.attempt;\n }\n if (updates.result !== undefined) {\n updateData.result_json = JSON.stringify(updates.result);\n }\n if (updates.error !== undefined) {\n updateData.error_json = JSON.stringify(\n sanitizeErrorForStorage(updates.error),\n );\n }\n if (updates.finishedAt !== undefined) {\n updateData.finished_at = updates.finishedAt;\n }\n\n if (Object.keys(updateData).length > 0) {\n await this.qb\n .updateTable(\"workflow_run_steps\")\n .set(updateData)\n .where(\"id\", \"=\", stepId)\n .execute();\n }\n }\n\n async getStepsForRun(runId: string): Promise<WorkflowRunStepRecord[]> {\n this.ensureInitialized();\n const rows = await this.qb\n .selectFrom(\"workflow_run_steps\")\n .selectAll()\n .where(\"run_id\", \"=\", runId)\n .orderBy(\"started_at\", \"asc\")\n .execute();\n\n return rows.map((row) => this.mapStepRow(row));\n }\n\n // ============================================================================\n // Event Operations\n // ============================================================================\n\n async saveEvent(event: Omit<WorkflowEventRecord, \"id\">): Promise<void> {\n this.ensureInitialized();\n const id = generateId();\n\n await this.qb\n .insertInto(\"workflow_events\")\n .values({\n id,\n run_id: event.runId,\n step_key: event.stepKey ?? null,\n event_type: event.eventType,\n level: event.level,\n payload_json:\n event.payload !== undefined ? JSON.stringify(event.payload) : null,\n timestamp: event.timestamp,\n })\n .execute();\n }\n\n async getEventsForRun(\n runId: string,\n options: ListEventsOptions = {},\n ): Promise<WorkflowEventRecord[]> {\n this.ensureInitialized();\n const limit = options.limit ?? 1000;\n const offset = options.offset ?? 0;\n\n let query = this.qb\n .selectFrom(\"workflow_events\")\n .selectAll()\n .where(\"run_id\", \"=\", runId);\n\n if (options.stepKey) {\n query = query.where(\"step_key\", \"=\", options.stepKey);\n }\n\n if (options.level) {\n query = query.where(\"level\", \"=\", options.level);\n }\n\n query = query.orderBy(\"timestamp\", \"asc\").limit(limit).offset(offset);\n\n const rows = await query.execute();\n\n return rows.map((row) => this.mapEventRow(row));\n }\n\n // ============================================================================\n // Transaction Support\n // ============================================================================\n\n /**\n * Execute a function within a database transaction.\n */\n async transaction<T>(fn: (tx: StorageAdapter) => Promise<T>): Promise<T> {\n this.ensureInitialized();\n return await this.db.transaction().execute(async (trx) => {\n // Create a transactional adapter wrapper\n const txAdapter = new PostgresTransactionAdapter(trx, this.schema);\n return await fn(txAdapter);\n });\n }\n\n // ============================================================================\n // Cleanup Operations\n // ============================================================================\n\n /**\n * Delete runs older than the specified date.\n * Also deletes associated steps and events (via CASCADE).\n */\n async deleteOldRuns(olderThan: Date): Promise<number> {\n this.ensureInitialized();\n const result = await this.qb\n .deleteFrom(\"runs\")\n .where(\"created_at\", \"<\", olderThan)\n .executeTakeFirst();\n\n return Number(result.numDeletedRows);\n }\n\n // ============================================================================\n // Resume Support\n // ============================================================================\n\n /**\n * Get all runs that were interrupted (status is 'running' or 'queued').\n * These runs can potentially be resumed.\n */\n async getInterruptedRuns(): Promise<WorkflowRunRecord[]> {\n this.ensureInitialized();\n const rows = await this.qb\n .selectFrom(\"runs\")\n .selectAll()\n .where(\"status\", \"in\", [\"queued\", \"running\"])\n .orderBy(\"created_at\", \"asc\")\n .execute();\n\n return rows.map((row) => this.mapRunRow(row));\n }\n\n /**\n * Get the last completed step for a run.\n * Useful for resuming from a checkpoint.\n */\n async getLastCompletedStep(\n runId: string,\n ): Promise<WorkflowRunStepRecord | null> {\n this.ensureInitialized();\n const row = await this.qb\n .selectFrom(\"workflow_run_steps\")\n .selectAll()\n .where(\"run_id\", \"=\", runId)\n .where(\"status\", \"=\", \"succeeded\")\n .orderBy(\"finished_at\", \"desc\")\n .limit(1)\n .executeTakeFirst();\n\n return row ? this.mapStepRow(row) : null;\n }\n\n // ============================================================================\n // Atomic Dequeue (for distributed workers)\n // ============================================================================\n\n /**\n * Atomically dequeue a run for processing.\n * Uses FOR UPDATE SKIP LOCKED for safe concurrent access.\n *\n * @param workflowKinds - Optional list of workflow kinds to filter by\n * @returns The dequeued run, or null if no runs are available\n */\n async dequeueRun(\n workflowKinds?: string[],\n ): Promise<WorkflowRunRecord | null> {\n this.ensureInitialized();\n // Use raw SQL for FOR UPDATE SKIP LOCKED\n const kindFilter =\n workflowKinds && workflowKinds.length > 0\n ? sql`AND kind = ANY(${workflowKinds}::text[])`\n : sql``;\n\n const result = await sql<WorkflowRunsTable>`\n UPDATE ${sql.table(`${this.schema}.runs`)}\n SET status = 'running', started_at = NOW()\n WHERE id = (\n SELECT id FROM ${sql.table(`${this.schema}.runs`)}\n WHERE status = 'queued' ${kindFilter}\n ORDER BY priority DESC, created_at ASC\n LIMIT 1\n FOR UPDATE SKIP LOCKED\n )\n RETURNING *\n `.execute(this.db);\n\n const row = result.rows[0];\n return row ? this.mapRunRow(row) : null;\n }\n\n // ============================================================================\n // Row Mapping\n // ============================================================================\n\n private mapRunRow(row: WorkflowRunsTable): WorkflowRunRecord {\n return {\n id: row.id,\n kind: row.kind,\n status: row.status as RunStatus,\n parentRunId: row.parent_run_id ?? undefined,\n input:\n typeof row.input_json === \"string\"\n ? JSON.parse(row.input_json)\n : row.input_json,\n context:\n typeof row.context_json === \"string\"\n ? JSON.parse(row.context_json)\n : row.context_json,\n output: row.output_json\n ? typeof row.output_json === \"string\"\n ? JSON.parse(row.output_json)\n : row.output_json\n : undefined,\n error: row.error_json\n ? typeof row.error_json === \"string\"\n ? JSON.parse(row.error_json)\n : row.error_json\n : undefined,\n metadata:\n typeof row.metadata_json === \"string\"\n ? JSON.parse(row.metadata_json)\n : row.metadata_json,\n priority: row.priority ?? 0,\n timeoutMs: row.timeout_ms ?? undefined,\n createdAt: new Date(row.created_at),\n startedAt: row.started_at ? new Date(row.started_at) : undefined,\n finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,\n };\n }\n\n /**\n * Map a database row to an extended workflow run record.\n */\n private mapExtendedRunRow(row: WorkflowRunsTable): ExtendedWorkflowRunRecord {\n return {\n id: row.id,\n kind: row.kind,\n status: row.status as ExtendedRunStatus,\n input:\n typeof row.input_json === \"string\"\n ? JSON.parse(row.input_json)\n : row.input_json,\n metadata:\n typeof row.metadata_json === \"string\"\n ? JSON.parse(row.metadata_json)\n : row.metadata_json,\n context:\n typeof row.context_json === \"string\"\n ? JSON.parse(row.context_json)\n : row.context_json,\n output: row.output_json\n ? typeof row.output_json === \"string\"\n ? JSON.parse(row.output_json)\n : row.output_json\n : undefined,\n error: row.error_json\n ? typeof row.error_json === \"string\"\n ? JSON.parse(row.error_json)\n : row.error_json\n : undefined,\n priority: row.priority ?? 0,\n timeoutMs: row.timeout_ms ?? undefined,\n createdAt: new Date(row.created_at),\n startedAt: row.started_at ? new Date(row.started_at) : undefined,\n finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,\n };\n }\n\n private mapStepResultRow(row: StepflowStepResultsTable): StepResult {\n return {\n id: row.id,\n runId: row.run_id,\n stepName: row.step_name,\n status: row.status as ExtendedStepStatus,\n output: row.output_json\n ? typeof row.output_json === \"string\"\n ? JSON.parse(row.output_json)\n : row.output_json\n : undefined,\n error: row.error_json\n ? typeof row.error_json === \"string\"\n ? JSON.parse(row.error_json)\n : row.error_json\n : undefined,\n attempt: row.attempt,\n startedAt: row.started_at ? new Date(row.started_at) : undefined,\n completedAt: row.completed_at ? new Date(row.completed_at) : undefined,\n };\n }\n\n private mapStepRow(row: WorkflowRunStepsTable): WorkflowRunStepRecord {\n return {\n id: row.id,\n runId: row.run_id,\n stepKey: row.step_key,\n stepName: row.step_name,\n status: row.status as StepStatus,\n attempt: row.attempt,\n result: row.result_json\n ? typeof row.result_json === \"string\"\n ? JSON.parse(row.result_json)\n : row.result_json\n : undefined,\n error: row.error_json\n ? typeof row.error_json === \"string\"\n ? JSON.parse(row.error_json)\n : row.error_json\n : undefined,\n startedAt: row.started_at ? new Date(row.started_at) : undefined,\n finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,\n };\n }\n\n private mapEventRow(row: WorkflowEventsTable): WorkflowEventRecord {\n return {\n id: row.id,\n runId: row.run_id,\n stepKey: row.step_key ?? undefined,\n eventType: row.event_type,\n level: row.level as \"info\" | \"warn\" | \"error\",\n payload: row.payload_json\n ? typeof row.payload_json === \"string\"\n ? JSON.parse(row.payload_json)\n : row.payload_json\n : undefined,\n timestamp: new Date(row.timestamp),\n };\n }\n\n // ============================================================================\n // New WorkflowStorage Methods\n // ============================================================================\n\n /**\n * Delete a workflow run by ID.\n * Also deletes associated steps and events (via CASCADE).\n */\n async deleteRun(id: string): Promise<void> {\n this.ensureInitialized();\n await this.qb.deleteFrom(\"runs\").where(\"id\", \"=\", id).execute();\n }\n\n /**\n * Cleanup stale runs that have exceeded their timeout.\n * Marks them as 'timeout' status with an appropriate error.\n *\n * @param defaultTimeoutMs - Default timeout in ms for runs without explicit timeout (default: 600000 = 10 minutes)\n * @returns Number of runs marked as timed out\n */\n async cleanupStaleRuns(defaultTimeoutMs: number = 600000): Promise<number> {\n this.ensureInitialized();\n const result = await sql<{ id: string }>`\n UPDATE ${sql.table(`${this.schema}.runs`)}\n SET\n status = 'timeout',\n error_json = jsonb_build_object(\n 'code', 'WORKFLOW_TIMEOUT',\n 'message', 'Workflow exceeded maximum execution time and was marked as timed out'\n ),\n finished_at = NOW()\n WHERE status = 'running'\n AND started_at IS NOT NULL\n AND (\n (timeout_ms IS NOT NULL AND started_at < NOW() - (timeout_ms || ' milliseconds')::interval)\n OR\n (timeout_ms IS NULL AND started_at < NOW() - (${defaultTimeoutMs} || ' milliseconds')::interval)\n )\n RETURNING id\n `.execute(this.db);\n\n return result.rows.length;\n }\n\n /**\n * Mark multiple runs as failed with a given reason.\n * Useful for cleanup when a worker shuts down unexpectedly.\n *\n * @param runIds - Array of run IDs to mark as failed\n * @param reason - Reason message for the failure\n */\n async markRunsAsFailed(runIds: string[], reason: string): Promise<void> {\n this.ensureInitialized();\n if (runIds.length === 0) return;\n\n await sql`\n UPDATE ${sql.table(`${this.schema}.runs`)}\n SET\n status = 'failed',\n error_json = jsonb_build_object(\n 'code', 'WORKER_SHUTDOWN',\n 'message', ${reason}\n ),\n finished_at = NOW()\n WHERE id = ANY(${runIds})\n AND status = 'running'\n `.execute(this.db);\n }\n\n /**\n * Get a specific step result by run ID and step name.\n */\n async getStepResult(\n runId: string,\n stepName: string,\n ): Promise<StepResult | undefined> {\n this.ensureInitialized();\n const row = await this.qb\n .selectFrom(\"stepflow_step_results\")\n .selectAll()\n .where(\"run_id\", \"=\", runId)\n .where(\"step_name\", \"=\", stepName)\n .executeTakeFirst();\n\n return row ? this.mapStepResultRow(row) : undefined;\n }\n\n /**\n * Get all step results for a run.\n */\n async getStepResults(runId: string): Promise<StepResult[]> {\n this.ensureInitialized();\n const rows = await this.qb\n .selectFrom(\"stepflow_step_results\")\n .selectAll()\n .where(\"run_id\", \"=\", runId)\n .orderBy(\"started_at\", \"asc\")\n .execute();\n\n return rows.map((row) => this.mapStepResultRow(row));\n }\n\n /**\n * Save or update a step result.\n * Uses upsert to handle both new and existing results.\n */\n async saveStepResult(\n result: Omit<StepResult, \"id\"> & { id?: string },\n ): Promise<void> {\n this.ensureInitialized();\n const id = result.id ?? generateId();\n\n await this.qb\n .insertInto(\"stepflow_step_results\")\n .values({\n id,\n run_id: result.runId,\n step_name: result.stepName,\n status: result.status,\n output_json: result.output ? JSON.stringify(result.output) : null,\n error_json: result.error\n ? JSON.stringify(stripStack(result.error))\n : null,\n attempt: result.attempt,\n started_at: result.startedAt ?? null,\n completed_at: result.completedAt ?? null,\n })\n .onConflict((oc) =>\n oc.columns([\"run_id\", \"step_name\"]).doUpdateSet({\n status: result.status,\n output_json: result.output ? JSON.stringify(result.output) : null,\n error_json: result.error\n ? JSON.stringify(stripStack(result.error))\n : null,\n attempt: result.attempt,\n started_at: result.startedAt ?? null,\n completed_at: result.completedAt ?? null,\n }),\n )\n .execute();\n }\n\n // ============================================================================\n // Utility Methods\n // ============================================================================\n\n /**\n * Get database statistics.\n */\n async getStats(): Promise<{ runs: number; steps: number; events: number }> {\n this.ensureInitialized();\n const [runsCount, stepsCount, eventsCount] = (await Promise.all([\n this.qb\n .selectFrom(\"runs\")\n .select(sql`count(*)`.as(\"count\"))\n .executeTakeFirst(),\n this.qb\n .selectFrom(\"workflow_run_steps\")\n .select(sql`count(*)`.as(\"count\"))\n .executeTakeFirst(),\n this.qb\n .selectFrom(\"workflow_events\")\n .select(sql`count(*)`.as(\"count\"))\n .executeTakeFirst(),\n ])) as { count?: string | number }[];\n\n return {\n runs: Number(runsCount?.count ?? 0),\n steps: Number(stepsCount?.count ?? 0),\n events: Number(eventsCount?.count ?? 0),\n };\n }\n}\n\n// ============================================================================\n// Transaction Adapter (wraps Kysely transaction)\n// ============================================================================\n\n/**\n * A StorageAdapter wrapper for use within transactions.\n * This is used internally by PostgresStorageAdapter.transaction().\n */\nclass PostgresTransactionAdapter implements StorageAdapter {\n private qb: ReturnType<KyselyType<StepflowDatabase>[\"withSchema\"]>;\n\n constructor(\n private trx: KyselyType<StepflowDatabase>,\n private schema: string,\n ) {\n this.qb = trx.withSchema(schema);\n }\n\n async createRun(\n run: CreateRunInput | Omit<WorkflowRunRecord, \"id\" | \"createdAt\">,\n ): Promise<WorkflowRunRecord> {\n const id = \"id\" in run && run.id ? run.id : generateId();\n const createdAt = new Date();\n\n await this.qb\n .insertInto(\"runs\")\n .values({\n id,\n kind: run.kind,\n status: run.status,\n parent_run_id: \"parentRunId\" in run ? (run.parentRunId ?? null) : null,\n input_json: JSON.stringify(run.input),\n metadata_json: JSON.stringify(run.metadata ?? {}),\n context_json: JSON.stringify(run.context ?? {}),\n output_json: null,\n error_json:\n \"error\" in run && run.error\n ? JSON.stringify(sanitizeErrorForStorage(run.error))\n : null,\n priority: \"priority\" in run ? (run.priority ?? 0) : 0,\n timeout_ms: \"timeoutMs\" in run ? (run.timeoutMs ?? null) : null,\n created_at: createdAt,\n started_at: null,\n finished_at: null,\n })\n .execute();\n\n return {\n id,\n kind: run.kind,\n status: run.status as RunStatus,\n parentRunId: \"parentRunId\" in run ? run.parentRunId : undefined,\n input: run.input,\n context: run.context ?? {},\n output: undefined,\n error: undefined,\n metadata: run.metadata ?? {},\n priority: \"priority\" in run ? (run.priority ?? 0) : 0,\n timeoutMs: \"timeoutMs\" in run ? run.timeoutMs : undefined,\n createdAt,\n };\n }\n\n async getRun(runId: string): Promise<WorkflowRunRecord | null> {\n const row = await this.qb\n .selectFrom(\"runs\")\n .selectAll()\n .where(\"id\", \"=\", runId)\n .executeTakeFirst();\n\n return row ? this.mapRunRow(row) : null;\n }\n\n async updateRun(\n runId: string,\n updates: UpdateRunInput | Partial<WorkflowRunRecord>,\n ): Promise<void> {\n const updateData: Partial<WorkflowRunsTable> = {};\n\n if (updates.status !== undefined) updateData.status = updates.status;\n if (updates.context !== undefined)\n updateData.context_json = JSON.stringify(updates.context);\n if (\"output\" in updates && updates.output !== undefined)\n updateData.output_json = JSON.stringify(updates.output);\n if (updates.error !== undefined)\n updateData.error_json = JSON.stringify(\n sanitizeErrorForStorage(updates.error as WorkflowError),\n );\n if (updates.startedAt !== undefined)\n updateData.started_at = updates.startedAt;\n if (updates.finishedAt !== undefined)\n updateData.finished_at = updates.finishedAt;\n\n if (Object.keys(updateData).length > 0) {\n await this.qb\n .updateTable(\"runs\")\n .set(updateData)\n .where(\"id\", \"=\", runId)\n .execute();\n }\n }\n\n async listRuns(\n options: ListRunsOptions = {},\n ): Promise<PaginatedResult<WorkflowRunRecord>> {\n const limit = options.limit ?? 50;\n const offset = options.offset ?? 0;\n\n let query = this.qb.selectFrom(\"runs\").selectAll();\n\n if (options.kind) query = query.where(\"kind\", \"=\", options.kind);\n if (options.status) {\n const statuses = Array.isArray(options.status)\n ? options.status\n : [options.status];\n query = query.where(\"status\", \"in\", statuses);\n }\n if (options.parentRunId !== undefined) {\n query = query.where(\"parent_run_id\", \"=\", options.parentRunId);\n }\n\n const orderColumn =\n (options.orderBy ?? \"createdAt\") === \"createdAt\"\n ? \"created_at\"\n : options.orderBy === \"startedAt\"\n ? \"started_at\"\n : \"finished_at\";\n const orderDirection = options.orderDirection ?? \"desc\";\n\n query = query\n .orderBy(orderColumn, orderDirection)\n .limit(limit)\n .offset(offset);\n\n const rows = await query.execute();\n\n return {\n items: rows.map((row) => this.mapRunRow(row)),\n total: rows.length, // Simplified for transaction context\n };\n }\n\n async createStep(\n step: Omit<WorkflowRunStepRecord, \"id\">,\n ): Promise<WorkflowRunStepRecord> {\n const id = generateId();\n\n await this.qb\n .insertInto(\"workflow_run_steps\")\n .values({\n id,\n run_id: step.runId,\n step_key: step.stepKey,\n step_name: step.stepName,\n status: step.status,\n attempt: step.attempt,\n result_json:\n step.result !== undefined ? JSON.stringify(step.result) : null,\n error_json: step.error\n ? JSON.stringify(sanitizeErrorForStorage(step.error))\n : null,\n started_at: step.startedAt ?? null,\n finished_at: step.finishedAt ?? null,\n })\n .execute();\n\n return { ...step, id };\n }\n\n async getStep(stepId: string): Promise<WorkflowRunStepRecord | null> {\n const row = await this.qb\n .selectFrom(\"workflow_run_steps\")\n .selectAll()\n .where(\"id\", \"=\", stepId)\n .executeTakeFirst();\n\n return row ? this.mapStepRow(row) : null;\n }\n\n async updateStep(\n stepId: string,\n updates: Partial<WorkflowRunStepRecord>,\n ): Promise<void> {\n const updateData: Partial<WorkflowRunStepsTable> = {};\n\n if (updates.status !== undefined) updateData.status = updates.status;\n if (updates.attempt !== undefined) updateData.attempt = updates.attempt;\n if (updates.result !== undefined)\n updateData.result_json = JSON.stringify(updates.result);\n if (updates.error !== undefined)\n updateData.error_json = JSON.stringify(\n sanitizeErrorForStorage(updates.error as WorkflowError),\n );\n if (updates.finishedAt !== undefined)\n updateData.finished_at = updates.finishedAt;\n\n if (Object.keys(updateData).length > 0) {\n await this.qb\n .updateTable(\"workflow_run_steps\")\n .set(updateData)\n .where(\"id\", \"=\", stepId)\n .execute();\n }\n }\n\n async getStepsForRun(runId: string): Promise<WorkflowRunStepRecord[]> {\n const rows = await this.qb\n .selectFrom(\"workflow_run_steps\")\n .selectAll()\n .where(\"run_id\", \"=\", runId)\n .orderBy(\"started_at\", \"asc\")\n .execute();\n\n return rows.map((row) => this.mapStepRow(row));\n }\n\n async saveEvent(event: Omit<WorkflowEventRecord, \"id\">): Promise<void> {\n const id = generateId();\n\n await this.qb\n .insertInto(\"workflow_events\")\n .values({\n id,\n run_id: event.runId,\n step_key: event.stepKey ?? null,\n event_type: event.eventType,\n level: event.level,\n payload_json:\n event.payload !== undefined ? JSON.stringify(event.payload) : null,\n timestamp: event.timestamp,\n })\n .execute();\n }\n\n async getEventsForRun(\n runId: string,\n options: ListEventsOptions = {},\n ): Promise<WorkflowEventRecord[]> {\n let query = this.qb\n .selectFrom(\"workflow_events\")\n .selectAll()\n .where(\"run_id\", \"=\", runId);\n\n if (options.stepKey) query = query.where(\"step_key\", \"=\", options.stepKey);\n if (options.level) query = query.where(\"level\", \"=\", options.level);\n\n const rows = await query\n .orderBy(\"timestamp\", \"asc\")\n .limit(options.limit ?? 1000)\n .offset(options.offset ?? 0)\n .execute();\n\n return rows.map((row) => this.mapEventRow(row));\n }\n\n private mapRunRow(row: WorkflowRunsTable): WorkflowRunRecord {\n return {\n id: row.id,\n kind: row.kind,\n status: row.status as RunStatus,\n parentRunId: row.parent_run_id ?? undefined,\n input:\n typeof row.input_json === \"string\"\n ? JSON.parse(row.input_json)\n : row.input_json,\n context:\n typeof row.context_json === \"string\"\n ? JSON.parse(row.context_json)\n : row.context_json,\n output: row.output_json\n ? typeof row.output_json === \"string\"\n ? JSON.parse(row.output_json)\n : row.output_json\n : undefined,\n error: row.error_json\n ? typeof row.error_json === \"string\"\n ? JSON.parse(row.error_json)\n : row.error_json\n : undefined,\n metadata:\n typeof row.metadata_json === \"string\"\n ? JSON.parse(row.metadata_json)\n : row.metadata_json,\n priority: row.priority ?? 0,\n timeoutMs: row.timeout_ms ?? undefined,\n createdAt: new Date(row.created_at),\n startedAt: row.started_at ? new Date(row.started_at) : undefined,\n finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,\n };\n }\n\n private mapStepRow(row: WorkflowRunStepsTable): WorkflowRunStepRecord {\n return {\n id: row.id,\n runId: row.run_id,\n stepKey: row.step_key,\n stepName: row.step_name,\n status: row.status as StepStatus,\n attempt: row.attempt,\n result: row.result_json\n ? typeof row.result_json === \"string\"\n ? JSON.parse(row.result_json)\n : row.result_json\n : undefined,\n error: row.error_json\n ? typeof row.error_json === \"string\"\n ? JSON.parse(row.error_json)\n : row.error_json\n : undefined,\n startedAt: row.started_at ? new Date(row.started_at) : undefined,\n finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,\n };\n }\n\n private mapEventRow(row: WorkflowEventsTable): WorkflowEventRecord {\n return {\n id: row.id,\n runId: row.run_id,\n stepKey: row.step_key ?? undefined,\n eventType: row.event_type,\n level: row.level as \"info\" | \"warn\" | \"error\",\n payload: row.payload_json\n ? typeof row.payload_json === \"string\"\n ? JSON.parse(row.payload_json)\n : row.payload_json\n : undefined,\n timestamp: new Date(row.timestamp),\n };\n }\n}\n\n// Convenience aliases for shorter naming\nexport { PostgresStorageAdapter as PostgresStorage };\nexport type { PostgresStorageConfig as PostgresStorageOptions };\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
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 */\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 * - Cryptographically secure random component\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 +0,0 @@
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(\n scheduleId: string,\n updates: Partial<WorkflowSchedule>,\n ): 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(\n () => this.checkSchedules(),\n this.pollInterval,\n );\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(\n scheduleData: Omit<WorkflowSchedule, \"id\">,\n ): 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(\n \"triggerOnWorkflowKind is required for workflow_completed triggers\",\n );\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(\n `Added schedule: ${schedule.id} (${schedule.workflowKind})`,\n );\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(\n scheduleId: string,\n updates: Partial<WorkflowSchedule>,\n ): 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 (\n updates.cronExpression !== undefined ||\n updates.timezone !== undefined\n ) {\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(\n `Invalid cron expression: ${schedule.cronExpression}`,\n );\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(\n `Failed to execute schedule ${schedule.id}:`,\n error,\n );\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 (\n event.eventType !== \"run.completed\" &&\n event.eventType !== \"run.failed\"\n ) {\n return;\n }\n\n const completedStatus: RunStatus =\n 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 (\n schedule.triggerOnStatus &&\n !schedule.triggerOnStatus.includes(completedStatus)\n ) {\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(\n `Executing schedule: ${schedule.id} (${schedule.workflowKind})`,\n );\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(\n `Failed to parse cron expression for schedule ${schedule.id}:`,\n error,\n );\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\n ? JSON.stringify(schedule.triggerOnStatus)\n : 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(\n scheduleId: string,\n updates: Partial<WorkflowSchedule>,\n ): Promise<void> {\n // Get existing schedule to merge with updates\n const existing = this.stmts!.getById.get(scheduleId) as\n | ScheduleRow\n | 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({\n connectionString: this.config.connectionString,\n });\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 =\n 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(\n this.db,\n );\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\n ? JSON.stringify(schedule.triggerOnStatus)\n : null,\n input_json: schedule.input ? JSON.stringify(schedule.input) : null,\n metadata_json: schedule.metadata\n ? JSON.stringify(schedule.metadata)\n : 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(\n scheduleId: string,\n updates: Partial<WorkflowSchedule>,\n ): 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 {\n domainKey: \"triggerOnStatus\",\n dbKey: \"trigger_on_status\",\n serialize: (v) => (v ? JSON.stringify(v) : null),\n },\n {\n domainKey: \"input\",\n dbKey: \"input_json\",\n serialize: (v) => (v ? JSON.stringify(v) : null),\n },\n {\n domainKey: \"metadata\",\n dbKey: \"metadata_json\",\n serialize: (v) => (v ? JSON.stringify(v) : null),\n },\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(\n workflowKind: string,\n ): 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(\n triggerOnWorkflowKind: string,\n ): 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":";;;;;;;AAMA,SAAS,4BAA4B;AAoF9B,IAAM,gBAAN,MAAyC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,oBAAI,IAA8B;AAAA,EAC9C,UAAU;AAAA,EACV,YAAmD;AAAA,EACnD,mBAAwC;AAAA,EAEhD,YAAY,QAA6B;AACvC,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS,OAAO,UAAU,IAAI,cAAc;AACjD,SAAK,eAAe,OAAO,gBAAgB;AAC3C,SAAK,cAAc,OAAO;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,QAAuB;AAC3B,QAAI,KAAK,SAAS;AAChB,WAAK,OAAO,KAAK,8BAA8B;AAC/C;AAAA,IACF;AAEA,SAAK,OAAO,KAAK,uBAAuB;AAGxC,QAAI,KAAK,aAAa;AACpB,YAAM,SAAS,MAAM,KAAK,YAAY,cAAc;AACpD,iBAAW,YAAY,QAAQ;AAC7B,aAAK,UAAU,IAAI,SAAS,IAAI,QAAQ;AAAA,MAC1C;AACA,WAAK,OAAO,KAAK,UAAU,OAAO,MAAM,6BAA6B;AAAA,IACvE;AAGA,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC9C,UAAI,SAAS,gBAAgB,UAAU,SAAS,gBAAgB;AAC9D,aAAK,kBAAkB,QAAQ;AAAA,MACjC;AAAA,IACF;AAGA,SAAK,YAAY;AAAA,MACf,MAAM,KAAK,eAAe;AAAA,MAC1B,KAAK;AAAA,IACP;AAGA,SAAK,mBAAmB,KAAK,OAAO,eAAe,CAAC,UAAU;AAC5D,WAAK,oBAAoB,KAAK;AAAA,IAChC,CAAC;AAED,SAAK,UAAU;AACf,SAAK,OAAO,KAAK,mBAAmB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAsB;AAC1B,QAAI,CAAC,KAAK,SAAS;AACjB;AAAA,IACF;AAEA,SAAK,OAAO,KAAK,uBAAuB;AAExC,QAAI,KAAK,WAAW;AAClB,oBAAc,KAAK,SAAS;AAC5B,WAAK,YAAY;AAAA,IACnB;AAEA,QAAI,KAAK,kBAAkB;AACzB,WAAK,iBAAiB;AACtB,WAAK,mBAAmB;AAAA,IAC1B;AAEA,SAAK,UAAU;AACf,SAAK,OAAO,KAAK,mBAAmB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YACJ,cAC2B;AAC3B,UAAM,WAA6B;AAAA,MACjC,GAAG;AAAA,MACH,IAAI,WAAW;AAAA,IACjB;AAGA,QAAI,SAAS,gBAAgB,UAAU,SAAS,gBAAgB;AAC9D,UAAI;AACF,6BAAqB,MAAM,SAAS,gBAAgB;AAAA,UAClD,IAAI,SAAS;AAAA,QACf,CAAC;AAAA,MACH,SAAS,OAAO;AACd,cAAM,IAAI,MAAM,4BAA4B,SAAS,cAAc,EAAE;AAAA,MACvE;AAGA,WAAK,kBAAkB,QAAQ;AAAA,IACjC;AAGA,QAAI,SAAS,gBAAgB,sBAAsB;AACjD,UAAI,CAAC,SAAS,uBAAuB;AACnC,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,SAAK,UAAU,IAAI,SAAS,IAAI,QAAQ;AAGxC,QAAI,KAAK,aAAa;AACpB,YAAM,KAAK,YAAY,aAAa,QAAQ;AAAA,IAC9C;AAEA,SAAK,OAAO;AAAA,MACV,mBAAmB,SAAS,EAAE,KAAK,SAAS,YAAY;AAAA,IAC1D;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,YAAmC;AACtD,QAAI,CAAC,KAAK,UAAU,IAAI,UAAU,GAAG;AACnC,YAAM,IAAI,MAAM,uBAAuB,UAAU,EAAE;AAAA,IACrD;AAEA,SAAK,UAAU,OAAO,UAAU;AAEhC,QAAI,KAAK,aAAa;AACpB,YAAM,KAAK,YAAY,eAAe,UAAU;AAAA,IAClD;AAEA,SAAK,OAAO,KAAK,qBAAqB,UAAU,EAAE;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eACJ,YACA,SACe;AACf,UAAM,WAAW,KAAK,UAAU,IAAI,UAAU;AAC9C,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,uBAAuB,UAAU,EAAE;AAAA,IACrD;AAGA,WAAO,QAAQ;AAGf,WAAO,OAAO,UAAU,OAAO;AAG/B,QACE,QAAQ,mBAAmB,UAC3B,QAAQ,aAAa,QACrB;AACA,UAAI,SAAS,gBAAgB;AAC3B,YAAI;AACF,+BAAqB,MAAM,SAAS,gBAAgB;AAAA,YAClD,IAAI,SAAS;AAAA,UACf,CAAC;AACD,eAAK,kBAAkB,QAAQ;AAAA,QACjC,SAAS,OAAO;AACd,gBAAM,IAAI;AAAA,YACR,4BAA4B,SAAS,cAAc;AAAA,UACrD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,KAAK,aAAa;AACpB,YAAM,KAAK,YAAY,eAAe,YAAY,OAAO;AAAA,IAC3D;AAEA,SAAK,OAAO,MAAM,qBAAqB,UAAU,EAAE;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAA4C;AAChD,WAAO,MAAM,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,YAAkD;AAC5D,WAAO,KAAK,UAAU,IAAI,UAAU;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,YAAqC;AACpD,UAAM,WAAW,KAAK,UAAU,IAAI,UAAU;AAC9C,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,uBAAuB,UAAU,EAAE;AAAA,IACrD;AAEA,WAAO,KAAK,gBAAgB,QAAQ;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,iBAAuB;AAC7B,UAAM,MAAM,oBAAI,KAAK;AAErB,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC9C,UAAI,CAAC,SAAS,QAAS;AACvB,UAAI,SAAS,gBAAgB,OAAQ;AACrC,UAAI,CAAC,SAAS,UAAW;AAEzB,UAAI,SAAS,aAAa,KAAK;AAE7B,aAAK,gBAAgB,QAAQ,EAAE,MAAM,CAAC,UAAU;AAC9C,eAAK,OAAO;AAAA,YACV,8BAA8B,SAAS,EAAE;AAAA,YACzC;AAAA,UACF;AAAA,QACF,CAAC;AAGD,aAAK,kBAAkB,QAAQ;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,OAA4B;AAGtD,QACE,MAAM,cAAc,mBACpB,MAAM,cAAc,cACpB;AACA;AAAA,IACF;AAEA,UAAM,kBACJ,MAAM,cAAc,kBAAkB,cAAc;AACtD,UAAM,gBAAgB,MAAM;AAG5B,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC9C,UAAI,CAAC,SAAS,QAAS;AACvB,UAAI,SAAS,gBAAgB,qBAAsB;AACnD,UAAI,SAAS,0BAA0B,cAAe;AAGtD,UACE,SAAS,mBACT,CAAC,SAAS,gBAAgB,SAAS,eAAe,GAClD;AACA;AAAA,MACF;AAGA,WAAK,gBAAgB,UAAU;AAAA,QAC7B,cAAc,MAAM;AAAA,QACpB,eAAe;AAAA,MACjB,CAAC,EAAE,MAAM,CAAC,UAAU;AAClB,aAAK,OAAO,MAAM,8BAA8B,SAAS,EAAE,KAAK,KAAK;AAAA,MACvE,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBACZ,UACA,gBACiB;AACjB,SAAK,OAAO;AAAA,MACV,uBAAuB,SAAS,EAAE,KAAK,SAAS,YAAY;AAAA,IAC9D;AAGA,UAAM,WAAW;AAAA,MACf,GAAG,SAAS;AAAA,MACZ,YAAY,SAAS;AAAA,MACrB,aAAa,SAAS;AAAA,MACtB,GAAI,kBAAkB,CAAC;AAAA,IACzB;AAGA,UAAM,QAAQ,MAAM,KAAK,OAAO,SAAS;AAAA,MACvC,MAAM,SAAS;AAAA,MACf,OAAO,SAAS;AAAA,MAChB;AAAA,IACF,CAAC;AAGD,aAAS,YAAY,oBAAI,KAAK;AAC9B,aAAS,YAAY;AAErB,QAAI,KAAK,aAAa;AACpB,YAAM,KAAK,YAAY,eAAe,SAAS,IAAI;AAAA,QACjD,WAAW,SAAS;AAAA,QACpB,WAAW,SAAS;AAAA,QACpB,WAAW,SAAS;AAAA,MACtB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,UAAkC;AAC1D,QAAI,CAAC,SAAS,eAAgB;AAE9B,QAAI;AACF,YAAM,WAAW,qBAAqB,MAAM,SAAS,gBAAgB;AAAA,QACnE,aAAa,oBAAI,KAAK;AAAA,QACtB,IAAI,SAAS;AAAA,MACf,CAAC;AAED,eAAS,YAAY,SAAS,KAAK,EAAE,OAAO;AAAA,IAC9C,SAAS,OAAO;AACd,WAAK,OAAO;AAAA,QACV,gDAAgD,SAAS,EAAE;AAAA,QAC3D;AAAA,MACF;AACA,eAAS,YAAY;AAAA,IACvB;AAAA,EACF;AACF;;;AClZO,IAAM,4BAAN,MAA+D;AAAA,EAC5D;AAAA,EACA;AAAA,EACA,QAMG;AAAA,EAEX,YAAY,QAAyC;AACnD,SAAK,KAAK,OAAO;AACjB,SAAK,YAAY,OAAO,aAAa;AACrC,QAAI,CAAC,2BAA2B,KAAK,KAAK,SAAS,GAAG;AACpD,YAAM,IAAI,MAAM,uBAAuB,KAAK,SAAS,EAAE;AAAA,IACzD;AACA,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAA2B;AAejC,SAAK,GAAG,KAAK;AAAA,mCACkB,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAiB5C;AAGD,SAAK,GAAG,KAAK;AAAA,uCACsB,KAAK,SAAS;AAAA,WAC1C,KAAK,SAAS;AAAA;AAAA,uCAEc,KAAK,SAAS;AAAA,WAC1C,KAAK,SAAS;AAAA;AAAA,uCAEc,KAAK,SAAS;AAAA,WAC1C,KAAK,SAAS;AAAA,KACpB;AAGD,SAAK,QAAQ;AAAA,MACX,QAAQ,KAAK,GAAG,QAAQ;AAAA,sBACR,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,OAK7B;AAAA,MACD,QAAQ,KAAK,GAAG,QAAQ;AAAA,iBACb,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAexB;AAAA,MACD,QAAQ,KAAK,GAAG,QAAQ;AAAA,sBACR,KAAK,SAAS;AAAA,OAC7B;AAAA,MACD,QAAQ,KAAK,GAAG,QAAQ;AAAA,wBACN,KAAK,SAAS;AAAA,OAC/B;AAAA,MACD,SAAS,KAAK,GAAG,QAAQ;AAAA,wBACP,KAAK,SAAS;AAAA,OAC/B;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBAA6C;AACjD,UAAM,OAAO,KAAK,MAAO,OAAO,IAAI;AACpC,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,cAAc,GAAG,CAAC;AAAA,EAClD;AAAA,EAEA,MAAM,aAAa,UAA2C;AAC5D,SAAK,MAAO,OAAO;AAAA,MACjB,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS,kBAAkB;AAAA,MAC3B,SAAS,YAAY;AAAA,MACrB,SAAS,yBAAyB;AAAA,MAClC,SAAS,kBACL,KAAK,UAAU,SAAS,eAAe,IACvC;AAAA,MACJ,SAAS,QAAQ,KAAK,UAAU,SAAS,KAAK,IAAI;AAAA,MAClD,SAAS,WAAW,KAAK,UAAU,SAAS,QAAQ,IAAI;AAAA,MACxD,SAAS,UAAU,IAAI;AAAA,MACvB,SAAS,WAAW,YAAY,KAAK;AAAA,MACrC,SAAS,aAAa;AAAA,MACtB,SAAS,WAAW,YAAY,KAAK;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,MAAM,eACJ,YACA,SACe;AAEf,UAAM,WAAW,KAAK,MAAO,QAAQ,IAAI,UAAU;AAGnD,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,uBAAuB,UAAU,EAAE;AAAA,IACrD;AAEA,UAAM,SAAS;AAAA,MACb,GAAG,KAAK,cAAc,QAAQ;AAAA,MAC9B,GAAG;AAAA,IACL;AAEA,SAAK,MAAO,OAAO;AAAA,MACjB,QAAQ,gBAAgB;AAAA,MACxB,QAAQ,eAAe;AAAA,MACvB,OAAO,kBAAkB;AAAA,MACzB,OAAO,YAAY;AAAA,MACnB,OAAO,yBAAyB;AAAA,MAChC,OAAO,kBAAkB,KAAK,UAAU,OAAO,eAAe,IAAI;AAAA,MAClE,OAAO,QAAQ,KAAK,UAAU,OAAO,KAAK,IAAI;AAAA,MAC9C,OAAO,WAAW,KAAK,UAAU,OAAO,QAAQ,IAAI;AAAA,MACpD,QAAQ,YAAY,SAAa,QAAQ,UAAU,IAAI,IAAK;AAAA,MAC5D,OAAO,WAAW,YAAY,KAAK;AAAA,MACnC,OAAO,aAAa;AAAA,MACpB,OAAO,WAAW,YAAY,KAAK;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,YAAmC;AACtD,SAAK,MAAO,OAAO,IAAI,UAAU;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAc,KAAoC;AACxD,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,cAAc,IAAI;AAAA,MAClB,aAAa,IAAI;AAAA,MACjB,gBAAgB,IAAI,mBAAmB;AAAA,MACvC,UAAU,IAAI,YAAY;AAAA,MAC1B,uBAAuB,IAAI,4BAA4B;AAAA,MACvD,iBAAiB,IAAI,oBACjB,KAAK,MAAM,IAAI,iBAAiB,IAChC;AAAA,MACJ,OAAO,IAAI,QAAQ,KAAK,MAAM,IAAI,KAAK,IAAI;AAAA,MAC3C,UAAU,IAAI,WAAW,KAAK,MAAM,IAAI,QAAQ,IAAI;AAAA,MACpD,SAAS,IAAI,YAAY;AAAA,MACzB,WAAW,IAAI,cAAc,IAAI,KAAK,IAAI,WAAW,IAAI;AAAA,MACzD,WAAW,IAAI,eAAe;AAAA,MAC9B,WAAW,IAAI,cAAc,IAAI,KAAK,IAAI,WAAW,IAAI;AAAA,IAC3D;AAAA,EACF;AACF;;;AC1OA,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AAwHG,IAAM,8BAAN,MAAiE;AAAA,EAC9D;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EAER,YAAY,QAA2C;AACrD,SAAK,SAAS,OAAO,UAAU;AAC/B,QAAI,CAAC,gCAAgC,KAAK,KAAK,MAAM,GAAG;AACtD,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,MAAM;AAAA,MAErC;AAAA,IACF;AACA,SAAK,YAAY,OAAO,aAAa;AACrC,QAAI,CAAC,gCAAgC,KAAK,KAAK,SAAS,GAAG;AACzD,YAAM,IAAI;AAAA,QACR,uBAAuB,KAAK,SAAS;AAAA,MAEvC;AAAA,IACF;AACA,SAAK,cAAc,OAAO,gBAAgB;AAC1C,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAY,KAAK;AACf,WAAO,KAAK,GAAG,WAAW,KAAK,MAAM;AAAA,EACvC;AAAA,EAEQ,oBAA0B;AAChC,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAA4B;AAChC,QAAI,KAAK,aAAa;AACpB;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,iBAAiB;AACpC,aAAS,KAAK;AACd,sBAAkB,KAAK;AACvB,UAAM,KAAK;AACX,eAAW,KAAK;AAEhB,QAAI,KAAK,OAAO,MAAM;AACpB,WAAK,OAAO,KAAK,OAAO;AACxB,WAAK,WAAW;AAAA,IAClB,WAAW,KAAK,OAAO,kBAAkB;AACvC,WAAK,OAAO,IAAI,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK,OAAO;AAAA,MAChC,CAAC;AACD,WAAK,WAAW;AAAA,IAClB,WAAW,KAAK,OAAO,YAAY;AACjC,WAAK,OAAO,IAAI,SAAS,KAAK,KAAK,OAAO,UAAU;AACpD,WAAK,WAAW;AAAA,IAClB,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,SAAK,KAAK,IAAI,OAAO;AAAA,MACnB,SAAS,IAAI,gBAAgB;AAAA,QAC3B,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH,CAAC;AAED,QAAI,KAAK,aAAa;AACpB,YAAM,KAAK,aAAa;AAAA,IAC1B;AAEA,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAuB;AAC3B,UAAM,KAAK,GAAG,QAAQ;AACtB,QAAI,KAAK,UAAU;AACjB,YAAM,KAAK,KAAK,IAAI;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,eAA8B;AAC1C,UAAM,gBACJ,KAAK,WAAW,WACZ,KAAK,YACL,GAAG,KAAK,MAAM,IAAI,KAAK,SAAS;AAGtC,QAAI,KAAK,WAAW,UAAU;AAC5B,YAAM,kCAAkC,IAAI,IAAI,KAAK,MAAM,CAAC,GAAG;AAAA,QAC7D,KAAK;AAAA,MACP;AAAA,IACF;AAgBA,UAAM;AAAA,mCACyB,IAAI,MAAM,aAAa,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAgBtC,IAAI,IAAI,GAAG,KAAK,SAAS,qBAAqB,CAAC;AAAA;AAAA;AAAA;AAAA,MAI9D,QAAQ,KAAK,EAAE;AAGjB,UAAM;AAAA,mCACyB,IAAI,IAAI,OAAO,KAAK,SAAS,gBAAgB,CAAC;AAAA,WACtE,IAAI,MAAM,aAAa,CAAC;AAAA,MAC7B,QAAQ,KAAK,EAAE;AAEjB,UAAM;AAAA,mCACyB,IAAI,IAAI,OAAO,KAAK,SAAS,UAAU,CAAC;AAAA,WAChE,IAAI,MAAM,aAAa,CAAC;AAAA,MAC7B,QAAQ,KAAK,EAAE;AAEjB,UAAM;AAAA,mCACyB,IAAI,IAAI,OAAO,KAAK,SAAS,eAAe,CAAC;AAAA,WACrE,IAAI,MAAM,aAAa,CAAC;AAAA,MAC7B,QAAQ,KAAK,EAAE;AAEjB,UAAM;AAAA,mCACyB,IAAI,IAAI,OAAO,KAAK,SAAS,WAAW,CAAC;AAAA,WACjE,IAAI,MAAM,aAAa,CAAC;AAAA;AAAA,MAE7B,QAAQ,KAAK,EAAE;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBAA6C;AACjD,SAAK,kBAAkB;AACvB,UAAM,OAAQ,MAAM,KAAK,GACtB,WAAW,KAAK,SAAgB,EAChC,UAAU,EACV,QAAQ;AAEX,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,cAAc,GAAG,CAAC;AAAA,EAClD;AAAA,EAEA,MAAM,aAAa,UAA2C;AAC5D,SAAK,kBAAkB;AACvB,UAAM,KAAK,GACR,WAAW,KAAK,SAAgB,EAChC,OAAO;AAAA,MACN,IAAI,SAAS;AAAA,MACb,eAAe,SAAS;AAAA,MACxB,cAAc,SAAS;AAAA,MACvB,iBAAiB,SAAS,kBAAkB;AAAA,MAC5C,UAAU,SAAS,YAAY;AAAA,MAC/B,0BAA0B,SAAS,yBAAyB;AAAA,MAC5D,mBAAmB,SAAS,kBACxB,KAAK,UAAU,SAAS,eAAe,IACvC;AAAA,MACJ,YAAY,SAAS,QAAQ,KAAK,UAAU,SAAS,KAAK,IAAI;AAAA,MAC9D,eAAe,SAAS,WACpB,KAAK,UAAU,SAAS,QAAQ,IAChC;AAAA,MACJ,SAAS,SAAS;AAAA,MAClB,aAAa,SAAS,aAAa;AAAA,MACnC,aAAa,SAAS,aAAa;AAAA,MACnC,aAAa,SAAS,aAAa;AAAA,MACnC,YAAY,oBAAI,KAAK;AAAA,MACrB,YAAY,oBAAI,KAAK;AAAA,IACvB,CAAC,EACA,QAAQ;AAAA,EACb;AAAA,EAEA,MAAM,eACJ,YACA,SACe;AACf,SAAK,kBAAkB;AAKvB,UAAM,WAAY,MAAM,KAAK,GAC1B,WAAW,KAAK,SAAgB,EAChC,UAAU,EACV,MAAM,MAAM,KAAK,UAAU,EAC3B,iBAAiB;AAEpB,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,uBAAuB,UAAU,EAAE;AAAA,IACrD;AAEA,UAAM,SAAS;AAAA,MACb,GAAG,KAAK,cAAc,QAAQ;AAAA,MAC9B,GAAG;AAAA,IACL;AAIA,UAAM,gBAID;AAAA,MACH,EAAE,WAAW,gBAAgB,OAAO,gBAAgB;AAAA,MACpD,EAAE,WAAW,eAAe,OAAO,eAAe;AAAA,MAClD,EAAE,WAAW,kBAAkB,OAAO,kBAAkB;AAAA,MACxD,EAAE,WAAW,YAAY,OAAO,WAAW;AAAA,MAC3C,EAAE,WAAW,yBAAyB,OAAO,2BAA2B;AAAA,MACxE;AAAA,QACE,WAAW;AAAA,QACX,OAAO;AAAA,QACP,WAAW,CAAC,MAAO,IAAI,KAAK,UAAU,CAAC,IAAI;AAAA,MAC7C;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,OAAO;AAAA,QACP,WAAW,CAAC,MAAO,IAAI,KAAK,UAAU,CAAC,IAAI;AAAA,MAC7C;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,OAAO;AAAA,QACP,WAAW,CAAC,MAAO,IAAI,KAAK,UAAU,CAAC,IAAI;AAAA,MAC7C;AAAA,MACA,EAAE,WAAW,WAAW,OAAO,UAAU;AAAA,MACzC,EAAE,WAAW,aAAa,OAAO,cAAc;AAAA,MAC/C,EAAE,WAAW,aAAa,OAAO,cAAc;AAAA,MAC/C,EAAE,WAAW,aAAa,OAAO,cAAc;AAAA,IACjD;AAEA,UAAM,aAA8C;AAAA,MAClD,YAAY,oBAAI,KAAK;AAAA,IACvB;AAEA,eAAW,EAAE,WAAW,OAAO,UAAU,KAAK,eAAe;AAC3D,UAAI,QAAQ,SAAS,MAAM,UAAa,OAAO,SAAS,MAAM,QAAW;AACvE,cAAM,QAAQ,YACV,UAAU,OAAO,SAAS,CAAC,IAC1B,OAAO,SAAS,KAAK;AAC1B,QAAC,WAAmB,KAAK,IAAI;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,KAAK,GACR,YAAY,KAAK,SAAgB,EACjC,IAAI,UAAU,EACd,MAAM,MAAM,KAAK,UAAU,EAC3B,QAAQ;AAAA,EACb;AAAA,EAEA,MAAM,eAAe,YAAmC;AACtD,SAAK,kBAAkB;AACvB,UAAM,KAAK,GACR,WAAW,KAAK,SAAgB,EAChC,MAAM,MAAM,KAAK,UAAU,EAC3B,QAAQ;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,YAAY,YAAsD;AACtE,SAAK,kBAAkB;AACvB,UAAM,MAAO,MAAM,KAAK,GACrB,WAAW,KAAK,SAAgB,EAChC,UAAU,EACV,MAAM,MAAM,KAAK,UAAU,EAC3B,iBAAiB;AAEpB,WAAO,MAAM,KAAK,cAAc,GAAG,IAAI;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAA+C;AACnD,SAAK,kBAAkB;AACvB,UAAM,MAAM,oBAAI,KAAK;AAErB,UAAM,OAAQ,MAAM,KAAK,GACtB,WAAW,KAAK,SAAgB,EAChC,UAAU,EACV,MAAM,WAAW,KAAK,IAAI,EAC1B,MAAM,gBAAgB,KAAK,MAAM,EACjC,MAAM,eAAe,MAAM,GAAG,EAC9B,QAAQ;AAEX,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,cAAc,GAAG,CAAC;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,2BACJ,cAC6B;AAC7B,SAAK,kBAAkB;AACvB,UAAM,OAAQ,MAAM,KAAK,GACtB,WAAW,KAAK,SAAgB,EAChC,UAAU,EACV,MAAM,iBAAiB,KAAK,YAAY,EACxC,QAAQ;AAEX,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,cAAc,GAAG,CAAC;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBACJ,uBAC6B;AAC7B,SAAK,kBAAkB;AACvB,UAAM,OAAQ,MAAM,KAAK,GACtB,WAAW,KAAK,SAAgB,EAChC,UAAU,EACV,MAAM,WAAW,KAAK,IAAI,EAC1B,MAAM,gBAAgB,KAAK,oBAAoB,EAC/C,MAAM,4BAA4B,KAAK,qBAAqB,EAC5D,QAAQ;AAEX,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,cAAc,GAAG,CAAC;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAc,KAA+C;AACnE,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,cAAc,IAAI;AAAA,MAClB,aAAa,IAAI;AAAA,MACjB,gBAAgB,IAAI,mBAAmB;AAAA,MACvC,UAAU,IAAI,YAAY;AAAA,MAC1B,uBAAuB,IAAI,4BAA4B;AAAA,MACvD,iBAAiB,IAAI,oBACjB,OAAO,IAAI,sBAAsB,WAC/B,KAAK,MAAM,IAAI,iBAAiB,IAChC,IAAI,oBACN;AAAA,MACJ,OAAO,IAAI,aACP,OAAO,IAAI,eAAe,WACxB,KAAK,MAAM,IAAI,UAAU,IACzB,IAAI,aACN;AAAA,MACJ,UAAU,IAAI,gBACV,OAAO,IAAI,kBAAkB,WAC3B,KAAK,MAAM,IAAI,aAAa,IAC5B,IAAI,gBACN;AAAA,MACJ,SAAS,IAAI;AAAA,MACb,WAAW,IAAI,cAAc,IAAI,KAAK,IAAI,WAAW,IAAI;AAAA,MACzD,WAAW,IAAI,eAAe;AAAA,MAC9B,WAAW,IAAI,cAAc,IAAI,KAAK,IAAI,WAAW,IAAI;AAAA,IAC3D;AAAA,EACF;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/events/memory.ts","../src/events/socketio.ts","../src/events/webhook.ts"],"sourcesContent":["/**\n * In-memory event transport using Node.js EventEmitter.\n * Default transport for development and single-process deployments.\n *\n * NOTE: This adapter is intended for development and testing only. It stores\n * all state in-process with no persistence or multi-process safety guarantees.\n */\n\nimport { EventEmitter } from \"events\";\nimport type {\n EventTransport,\n WorkflowEvent,\n WorkflowEventType,\n EventCallback,\n Unsubscribe,\n} from \"./types\";\n\n/**\n * In-memory event transport implementation using EventEmitter.\n * Events are emitted to subscribers in the same process.\n */\nexport class MemoryEventTransport implements EventTransport {\n private emitter = new EventEmitter();\n\n constructor() {\n // Increase max listeners to avoid warnings in workflows with many steps\n this.emitter.setMaxListeners(100);\n }\n\n /**\n * Emit an event to all subscribers.\n */\n emit(event: WorkflowEvent): void {\n // Emit to global subscribers\n this.emitter.emit(\"event\", event);\n\n // Emit to run-specific subscribers\n this.emitter.emit(`run:${event.runId}`, event);\n\n // Emit to event-type subscribers\n this.emitter.emit(`type:${event.eventType}`, event);\n }\n\n /**\n * Subscribe to events for a specific run.\n */\n subscribe(runId: string, callback: EventCallback): Unsubscribe {\n const channel = `run:${runId}`;\n this.emitter.on(channel, callback);\n return () => {\n this.emitter.off(channel, callback);\n };\n }\n\n /**\n * Subscribe to all events.\n */\n subscribeAll(callback: EventCallback): Unsubscribe {\n this.emitter.on(\"event\", callback);\n return () => {\n this.emitter.off(\"event\", callback);\n };\n }\n\n /**\n * Subscribe to events of a specific type.\n */\n subscribeToType(\n eventType: WorkflowEventType,\n callback: EventCallback,\n ): Unsubscribe {\n const channel = `type:${eventType}`;\n this.emitter.on(channel, callback);\n return () => {\n this.emitter.off(channel, callback);\n };\n }\n\n /**\n * Close the transport and remove all listeners.\n */\n close(): void {\n this.emitter.removeAllListeners();\n }\n\n /**\n * Get the number of listeners for a channel (for testing).\n */\n getListenerCount(channel?: string): number {\n if (channel) {\n return this.emitter.listenerCount(channel);\n }\n return this.emitter.listenerCount(\"event\");\n }\n}\n","/**\n * Socket.IO Event Transport\n *\n * Broadcasts workflow events to connected clients via Socket.IO.\n * Clients can subscribe to specific run IDs or receive all events.\n */\n\nimport type {\n EventTransport,\n EventCallback,\n Unsubscribe,\n WorkflowEvent,\n} from \"./types\";\nimport type { Logger } from \"../core/types\";\n\n/**\n * Minimal Socket.IO Server interface.\n * This allows using the adapter without a direct dependency on socket.io.\n */\nexport interface SocketIOServer {\n to(room: string): {\n emit(event: string, ...args: unknown[]): void;\n };\n emit(event: string, ...args: unknown[]): void;\n}\n\n/**\n * Minimal Socket.IO Socket interface.\n */\nexport interface SocketIOSocket {\n join(room: string): void;\n leave(room: string): void;\n on(event: string, callback: (...args: unknown[]) => void): void;\n}\n\n/**\n * Authorization callback for Socket.IO run subscriptions.\n * Return true to allow the subscription, false to deny it.\n */\nexport type SocketIOAuthorizeFn = (\n runId: string,\n socket: SocketIOSocket,\n) => boolean | Promise<boolean>;\n\n/**\n * Configuration for SocketIOEventTransport.\n */\nexport interface SocketIOEventTransportConfig {\n /** Socket.IO server instance */\n io: SocketIOServer;\n\n /** Event name to use for workflow events (default: 'workflow:event') */\n eventName?: string;\n\n /** Room prefix for run-specific subscriptions (default: 'run:') */\n roomPrefix?: string;\n\n /** Whether to also broadcast to a global room (default: true) */\n broadcastGlobal?: boolean;\n\n /** Global room name (default: 'workflow:all') */\n globalRoom?: string;\n\n /** Logger for transport errors (default: console-based) */\n logger?: Logger;\n}\n\n/**\n * Socket.IO-based event transport for real-time workflow events.\n *\n * @example\n * ```typescript\n * import { Server } from 'socket.io';\n * import { SocketIOEventTransport } from 'stepflow';\n *\n * const io = new Server(httpServer);\n * const eventTransport = new SocketIOEventTransport({ io });\n *\n * const engine = new WorkflowEngine({\n * storage,\n * events: eventTransport,\n * });\n *\n * // Client-side:\n * // socket.emit('workflow:subscribe', runId);\n * // socket.on('workflow:event', (event) => console.log(event));\n * ```\n */\nexport class SocketIOEventTransport implements EventTransport {\n private io: SocketIOServer;\n private eventName: string;\n private roomPrefix: string;\n private broadcastGlobal: boolean;\n private globalRoom: string;\n private logger: Logger;\n\n // Local subscribers for server-side subscriptions\n private runSubscribers = new Map<string, Set<EventCallback>>();\n private globalSubscribers = new Set<EventCallback>();\n\n constructor(config: SocketIOEventTransportConfig) {\n this.io = config.io;\n this.eventName = config.eventName ?? \"workflow:event\";\n this.roomPrefix = config.roomPrefix ?? \"run:\";\n this.broadcastGlobal = config.broadcastGlobal ?? true;\n this.globalRoom = config.globalRoom ?? \"workflow:all\";\n this.logger = config.logger ?? {\n debug() {},\n info() {},\n warn() {},\n error(message: string, ...args: unknown[]) {\n console.error(message, ...args);\n },\n };\n }\n\n /**\n * Emit an event to Socket.IO clients and local subscribers.\n */\n emit(event: WorkflowEvent): void {\n // Serialize the event (convert Date to ISO string for transport)\n const serializedEvent = {\n ...event,\n timestamp: event.timestamp.toISOString(),\n };\n\n // Emit to run-specific room\n const room = `${this.roomPrefix}${event.runId}`;\n this.io.to(room).emit(this.eventName, serializedEvent);\n\n // Emit to global room\n if (this.broadcastGlobal) {\n this.io.to(this.globalRoom).emit(this.eventName, serializedEvent);\n }\n\n // Notify local subscribers\n const runSubs = this.runSubscribers.get(event.runId);\n if (runSubs) {\n for (const callback of runSubs) {\n try {\n callback(event);\n } catch (error) {\n this.logger.error(\"Event callback error:\", error);\n }\n }\n }\n\n for (const callback of this.globalSubscribers) {\n try {\n callback(event);\n } catch (error) {\n this.logger.error(\"Event callback error:\", error);\n }\n }\n }\n\n /**\n * Subscribe to events for a specific run (server-side).\n */\n subscribe(runId: string, callback: EventCallback): Unsubscribe {\n let subs = this.runSubscribers.get(runId);\n if (!subs) {\n subs = new Set();\n this.runSubscribers.set(runId, subs);\n }\n subs.add(callback);\n\n return () => {\n subs!.delete(callback);\n if (subs!.size === 0) {\n this.runSubscribers.delete(runId);\n }\n };\n }\n\n /**\n * Subscribe to all events (server-side).\n */\n subscribeAll(callback: EventCallback): Unsubscribe {\n this.globalSubscribers.add(callback);\n return () => {\n this.globalSubscribers.delete(callback);\n };\n }\n\n /**\n * Set up client subscription handlers on a socket.\n * Call this when a client connects to enable subscription commands.\n *\n * An `authorize` callback is required to prevent unauthorized access to run events.\n * The callback receives the run ID (or `'*'` for global subscriptions) and the socket,\n * and must return `true` to allow or `false` to deny the subscription.\n *\n * @param socket - The Socket.IO socket to set up handlers on\n * @param authorize - Callback to check if a socket can access a run.\n *\n * @example\n * ```typescript\n * io.on('connection', (socket) => {\n * eventTransport.setupClientHandlers(socket, async (runId, sock) => {\n * // Check if the authenticated user owns this run\n * const userId = sock.data?.userId;\n * return userId ? await canUserAccessRun(userId, runId) : false;\n * });\n * });\n * ```\n */\n setupClientHandlers(\n socket: SocketIOSocket,\n authorize: SocketIOAuthorizeFn,\n ): void {\n // Subscribe to a specific run\n socket.on(\"workflow:subscribe\", async (...args: unknown[]) => {\n const runId = args[0];\n if (typeof runId !== \"string\") return;\n\n try {\n const allowed = await authorize(runId, socket);\n if (!allowed) {\n this.logger.warn(`Subscription denied for run ${runId}`);\n return;\n }\n } catch (error) {\n this.logger.error(\"Authorization check failed:\", error);\n return;\n }\n\n socket.join(`${this.roomPrefix}${runId}`);\n });\n\n // Unsubscribe from a specific run\n socket.on(\"workflow:unsubscribe\", (...args: unknown[]) => {\n const runId = args[0];\n if (typeof runId === \"string\") {\n socket.leave(`${this.roomPrefix}${runId}`);\n }\n });\n\n // Subscribe to all events (requires authorization to pass with '*' as runId)\n socket.on(\"workflow:subscribe:all\", async () => {\n try {\n const allowed = await authorize(\"*\", socket);\n if (!allowed) {\n this.logger.warn(\"Global subscription denied\");\n return;\n }\n } catch (error) {\n this.logger.error(\"Authorization check failed:\", error);\n return;\n }\n\n socket.join(this.globalRoom);\n });\n\n // Unsubscribe from all events\n socket.on(\"workflow:unsubscribe:all\", () => {\n socket.leave(this.globalRoom);\n });\n }\n\n /**\n * Clear all in-process server-side subscribers.\n * The underlying Socket.IO server socket is not closed here — manage its lifecycle externally.\n */\n close(): void {\n this.runSubscribers.clear();\n this.globalSubscribers.clear();\n }\n}\n","/**\n * Webhook Event Transport\n *\n * Posts workflow events to HTTP endpoints via webhooks.\n * Supports multiple webhooks with optional filtering by event type.\n */\n\nimport { promises as dns } from \"node:dns\";\nimport type {\n EventTransport,\n EventCallback,\n Unsubscribe,\n WorkflowEvent,\n WorkflowEventType,\n} from \"./types\";\nimport type { Logger } from \"../core/types\";\n\n/**\n * Webhook endpoint configuration.\n */\nexport interface WebhookEndpoint {\n /** Unique identifier for this endpoint */\n id: string;\n\n /** URL to POST events to */\n url: string;\n\n /** Optional secret for signing payloads (HMAC-SHA256) */\n secret?: string;\n\n /** Optional filter for specific event types (empty = all events) */\n eventTypes?: WorkflowEventType[];\n\n /** Optional filter for specific workflow kinds (empty = all kinds) */\n workflowKinds?: string[];\n\n /** Optional custom headers to include */\n headers?: Record<string, string>;\n\n /** Whether this endpoint is enabled (default: true) */\n enabled?: boolean;\n\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number;\n\n /** Number of retry attempts on failure (default: 3) */\n retries?: number;\n}\n\n/**\n * Configuration for WebhookEventTransport.\n */\nexport interface WebhookEventTransportConfig {\n /** Initial webhook endpoints */\n endpoints?: WebhookEndpoint[];\n\n /** Default timeout for all webhooks in ms (default: 5000) */\n defaultTimeout?: number;\n\n /** Default retry attempts (default: 3) */\n defaultRetries?: number;\n\n /** Retry delay in ms (default: 1000) */\n retryDelay?: number;\n\n /** Custom fetch function (for testing or custom HTTP clients) */\n fetchFn?: typeof fetch;\n\n /**\n * Whether to allow non-HTTPS URLs (default: false).\n * Set to true only in development environments.\n */\n allowInsecureUrls?: boolean;\n\n /**\n * Maximum payload size in bytes (default: 1048576 = 1 MB).\n * Payloads exceeding this limit will be rejected.\n */\n maxPayloadBytes?: number;\n\n /**\n * Maximum concurrent webhook requests across all endpoints (default: 50).\n * Requests beyond this limit are queued.\n */\n maxConcurrentRequests?: number;\n\n /** Logger for webhook transport errors (default: console-based) */\n logger?: import(\"../core/types\").Logger;\n}\n\n/**\n * Webhook payload structure sent to external endpoints.\n */\nexport interface WebhookPayload {\n /**\n * The workflow event with `timestamp` serialized as an ISO 8601 string.\n * The `Date` is converted to a string before delivery so that JSON consumers\n * receive a portable, timezone-aware representation without needing Date parsing.\n */\n event: Omit<WorkflowEvent, \"timestamp\"> & { timestamp: string };\n /** ISO 8601 timestamp of when this payload was delivered. */\n deliveredAt: string;\n /** ID of the webhook endpoint that received this delivery. */\n webhookId: string;\n}\n\n/**\n * Webhook-based event transport for integrating with external systems.\n *\n * @example\n * ```typescript\n * const webhookTransport = new WebhookEventTransport({\n * endpoints: [\n * {\n * id: 'slack-notifications',\n * url: 'https://hooks.slack.com/...',\n * eventTypes: ['run.completed', 'run.failed'],\n * },\n * {\n * id: 'analytics',\n * url: 'https://api.analytics.com/events',\n * secret: process.env.WEBHOOK_SECRET, // Use a cryptographically random secret (≥ 32 bytes)\n * },\n * ],\n * });\n *\n * const engine = new WorkflowEngine({\n * storage,\n * events: webhookTransport,\n * });\n * ```\n */\nexport class WebhookEventTransport implements EventTransport {\n private endpoints = new Map<string, WebhookEndpoint>();\n private defaultTimeout: number;\n private defaultRetries: number;\n private retryDelay: number;\n private fetchFn: typeof fetch;\n private allowInsecureUrls: boolean;\n private maxPayloadBytes: number;\n private maxConcurrentRequests: number;\n private activeRequests = 0;\n private requestQueue: Array<() => void> = [];\n private logger: Logger;\n\n // Local subscribers for server-side subscriptions\n private runSubscribers = new Map<string, Set<EventCallback>>();\n private globalSubscribers = new Set<EventCallback>();\n\n constructor(config: WebhookEventTransportConfig = {}) {\n this.defaultTimeout = config.defaultTimeout ?? 5000;\n this.defaultRetries = config.defaultRetries ?? 3;\n this.retryDelay = config.retryDelay ?? 1000;\n this.fetchFn = config.fetchFn ?? globalThis.fetch;\n this.allowInsecureUrls = config.allowInsecureUrls ?? false;\n this.maxPayloadBytes = config.maxPayloadBytes ?? 1_048_576; // 1 MB\n this.maxConcurrentRequests = config.maxConcurrentRequests ?? 50;\n this.logger = config.logger ?? {\n debug() {},\n info() {},\n warn() {},\n error(message: string, ...args: unknown[]) {\n console.error(message, ...args);\n },\n };\n\n // Register initial endpoints (validates URLs)\n if (config.endpoints) {\n for (const endpoint of config.endpoints) {\n this.addEndpoint(endpoint);\n }\n }\n }\n\n /**\n * Add a webhook endpoint.\n * Validates the URL to prevent SSRF attacks.\n * @throws Error if the URL scheme is not allowed\n */\n addEndpoint(endpoint: WebhookEndpoint): void {\n this.validateWebhookUrl(endpoint.url);\n this.endpoints.set(endpoint.id, { enabled: true, ...endpoint });\n }\n\n /**\n * Remove a webhook endpoint.\n */\n removeEndpoint(id: string): boolean {\n return this.endpoints.delete(id);\n }\n\n /**\n * Get all registered endpoints.\n */\n getEndpoints(): WebhookEndpoint[] {\n return Array.from(this.endpoints.values());\n }\n\n /**\n * Enable or disable an endpoint.\n */\n setEndpointEnabled(id: string, enabled: boolean): void {\n const endpoint = this.endpoints.get(id);\n if (endpoint) {\n endpoint.enabled = enabled;\n }\n }\n\n /**\n * Emit an event to all matching webhook endpoints.\n */\n emit(event: WorkflowEvent): void {\n // Notify local subscribers first (synchronously)\n const runSubs = this.runSubscribers.get(event.runId);\n if (runSubs) {\n for (const callback of runSubs) {\n try {\n callback(event);\n } catch (error) {\n this.logger.error(\"Event callback error:\", error);\n }\n }\n }\n\n for (const callback of this.globalSubscribers) {\n try {\n callback(event);\n } catch (error) {\n this.logger.error(\"Event callback error:\", error);\n }\n }\n\n // Send to webhooks asynchronously with concurrency control\n for (const endpoint of this.endpoints.values()) {\n if (!endpoint.enabled) continue;\n if (!this.matchesFilter(event, endpoint)) continue;\n\n this.enqueueRequest(() =>\n this.sendWebhook(endpoint, event).catch((error) => {\n this.logger.error(`Webhook ${endpoint.id} failed:`, error);\n }),\n );\n }\n }\n\n /**\n * Subscribe to events for a specific run (server-side).\n */\n subscribe(runId: string, callback: EventCallback): Unsubscribe {\n let subs = this.runSubscribers.get(runId);\n if (!subs) {\n subs = new Set();\n this.runSubscribers.set(runId, subs);\n }\n subs.add(callback);\n\n return () => {\n subs!.delete(callback);\n if (subs!.size === 0) {\n this.runSubscribers.delete(runId);\n }\n };\n }\n\n /**\n * Subscribe to all events (server-side).\n */\n subscribeAll(callback: EventCallback): Unsubscribe {\n this.globalSubscribers.add(callback);\n return () => {\n this.globalSubscribers.delete(callback);\n };\n }\n\n /**\n * Close the transport.\n */\n close(): void {\n this.runSubscribers.clear();\n this.globalSubscribers.clear();\n this.endpoints.clear();\n }\n\n /**\n * Check if an event matches an endpoint's filters.\n */\n private matchesFilter(\n event: WorkflowEvent,\n endpoint: WebhookEndpoint,\n ): boolean {\n // Check event type filter\n if (endpoint.eventTypes && endpoint.eventTypes.length > 0) {\n if (!endpoint.eventTypes.includes(event.eventType)) {\n return false;\n }\n }\n\n // Check workflow kind filter\n if (endpoint.workflowKinds && endpoint.workflowKinds.length > 0) {\n if (!endpoint.workflowKinds.includes(event.kind)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Send a webhook with retry support.\n */\n private async sendWebhook(\n endpoint: WebhookEndpoint,\n event: WorkflowEvent,\n ): Promise<void> {\n const timeout = endpoint.timeout ?? this.defaultTimeout;\n const maxRetries = endpoint.retries ?? this.defaultRetries;\n\n const payload: WebhookPayload = {\n event: {\n ...event,\n timestamp: event.timestamp.toISOString(),\n },\n deliveredAt: new Date().toISOString(),\n webhookId: endpoint.id,\n };\n\n const body = JSON.stringify(payload);\n\n // Enforce payload size limit to prevent excessive memory use and network abuse\n if (body.length > this.maxPayloadBytes) {\n throw new Error(\n `Webhook payload exceeds maximum size (${body.length} bytes > ${this.maxPayloadBytes} bytes)`,\n );\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...endpoint.headers,\n };\n\n // Sign payload if secret is provided\n if (endpoint.secret) {\n const signature = await this.signPayload(body, endpoint.secret);\n headers[\"X-Webhook-Signature\"] = signature;\n }\n\n // Resolve hostname and validate the resolved IP is not private/reserved (SSRF protection).\n // This prevents DNS rebinding attacks where a hostname initially resolves to a public IP\n // but later resolves to a private IP.\n await this.validateResolvedHost(endpoint.url);\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const response = await this.fetchFn(endpoint.url, {\n method: \"POST\",\n headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return; // Success\n }\n\n lastError = new Error(\n `Webhook returned ${response.status}: ${response.statusText}`,\n );\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n }\n\n // Wait before retrying (except on last attempt)\n if (attempt < maxRetries) {\n await this.sleep(this.retryDelay * Math.pow(2, attempt));\n }\n }\n\n throw lastError;\n }\n\n /**\n * Sign a payload using HMAC-SHA256.\n */\n private async signPayload(payload: string, secret: string): Promise<string> {\n // Use Web Crypto API for HMAC-SHA256\n const encoder = new TextEncoder();\n const keyData = encoder.encode(secret);\n const data = encoder.encode(payload);\n\n const key = await crypto.subtle.importKey(\n \"raw\",\n keyData,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n\n const signature = await crypto.subtle.sign(\"HMAC\", key, data);\n const hashArray = Array.from(new Uint8Array(signature));\n const hashHex = hashArray\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n return `sha256=${hashHex}`;\n }\n\n /**\n * Sleep helper.\n */\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Resolve the hostname from a URL and verify the resolved IP is not private/reserved.\n * Prevents DNS rebinding attacks where a public hostname resolves to a private IP at send time.\n */\n private async validateResolvedHost(url: string): Promise<void> {\n const parsed = new URL(url);\n const hostname = parsed.hostname.toLowerCase();\n\n // Skip DNS resolution for raw IP addresses — already checked by isBlockedHost in validateWebhookUrl\n if (/^(\\d{1,3}\\.){3}\\d{1,3}$/.test(hostname)) {\n return;\n }\n\n try {\n const { address } = await dns.lookup(hostname);\n if (isBlockedIp(address)) {\n throw new Error(\n `Webhook URL hostname \"${hostname}\" resolves to blocked IP ${address}`,\n );\n }\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"resolves to blocked IP\")\n ) {\n throw error;\n }\n throw new Error(\n `Failed to resolve webhook URL hostname \"${hostname}\": ${error}`,\n );\n }\n }\n\n /**\n * Validate a webhook URL to prevent SSRF attacks.\n * Blocks non-HTTPS schemes (unless allowInsecureUrls is set),\n * and rejects private/reserved IP ranges and cloud metadata endpoints.\n */\n private validateWebhookUrl(url: string): void {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new Error(`Invalid webhook URL: ${url}`);\n }\n\n // Scheme check\n if (!this.allowInsecureUrls && parsed.protocol !== \"https:\") {\n throw new Error(\n `Webhook URL must use HTTPS (got ${parsed.protocol}). Set allowInsecureUrls for development.`,\n );\n }\n\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") {\n throw new Error(\n `Webhook URL must use HTTP(S) scheme (got ${parsed.protocol})`,\n );\n }\n\n // Block dangerous hostnames\n const hostname = parsed.hostname.toLowerCase();\n if (isBlockedHost(hostname)) {\n throw new Error(\n `Webhook URL hostname is blocked (private/reserved): ${hostname}`,\n );\n }\n }\n\n /**\n * Enqueue a webhook request with concurrency limiting.\n */\n private enqueueRequest(fn: () => Promise<void>): void {\n const execute = () => {\n this.activeRequests++;\n fn().finally(() => {\n this.activeRequests--;\n const next = this.requestQueue.shift();\n if (next) next();\n });\n };\n\n if (this.activeRequests < this.maxConcurrentRequests) {\n execute();\n } else {\n this.requestQueue.push(execute);\n }\n }\n}\n\n/**\n * Check if a hostname resolves to a blocked (private/reserved) address.\n * Blocks loopback, RFC 1918 private ranges, link-local, and cloud metadata IPs.\n */\nfunction isBlockedHost(hostname: string): boolean {\n // Loopback (Node's URL parser wraps IPv6 in brackets, e.g. \"[::1]\")\n if (\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"::1\" ||\n hostname === \"[::1]\"\n ) {\n return true;\n }\n\n // Block all IPv6 address literals (wrapped in brackets by URL parser)\n if (hostname.startsWith(\"[\")) {\n return true;\n }\n\n // Cloud metadata endpoint\n if (hostname === \"169.254.169.254\") {\n return true;\n }\n\n // Check for IP addresses in private/reserved ranges\n const ipv4Match = hostname.match(\n /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/,\n );\n if (ipv4Match) {\n const [, a, b] = ipv4Match.map(Number);\n // 10.0.0.0/8\n if (a === 10) return true;\n // 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) return true;\n // 192.168.0.0/16\n if (a === 192 && b === 168) return true;\n // 127.0.0.0/8\n if (a === 127) return true;\n // 169.254.0.0/16 (link-local)\n if (a === 169 && b === 254) return true;\n // 0.0.0.0\n if (a === 0 && b === 0) return true;\n }\n\n // IPv6 link-local (fe80::)\n if (hostname.startsWith(\"fe80:\") || hostname.startsWith(\"[fe80:\")) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Check if a resolved IP address falls within a private/reserved range.\n * Used for runtime DNS resolution validation to prevent SSRF via DNS rebinding.\n */\nfunction isBlockedIp(ip: string): boolean {\n const ipv4Match = ip.match(/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/);\n if (ipv4Match) {\n const [, a, b] = ipv4Match.map(Number);\n // 127.0.0.0/8 (loopback)\n if (a === 127) return true;\n // 10.0.0.0/8\n if (a === 10) return true;\n // 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) return true;\n // 192.168.0.0/16\n if (a === 192 && b === 168) return true;\n // 169.254.0.0/16 (link-local, including cloud metadata 169.254.169.254)\n if (a === 169 && b === 254) return true;\n // 0.0.0.0\n if (a === 0 && b === 0) return true;\n }\n\n // IPv6 loopback\n if (ip === \"::1\") return true;\n // IPv6 link-local\n if (ip.startsWith(\"fe80:\")) return true;\n\n return false;\n}\n"],"mappings":"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","names":[]}