@multiplier-labs/stepflow 0.3.3 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +9 -0
  2. package/dist/{chunk-GYPNJBDP.js → chunk-D5HW7VEG.js} +269 -48
  3. package/dist/chunk-D5HW7VEG.js.map +1 -0
  4. package/dist/{chunk-KHTUWLVZ.js → chunk-FMPJBK2N.js} +55 -10
  5. package/dist/chunk-FMPJBK2N.js.map +1 -0
  6. package/dist/{chunk-ML35PIHX.js → chunk-JPXPB7U5.js} +52 -2
  7. package/dist/chunk-JPXPB7U5.js.map +1 -0
  8. package/dist/{chunk-4VOKMGP6.js → chunk-JUPZEBR3.js} +519 -522
  9. package/dist/chunk-JUPZEBR3.js.map +1 -0
  10. package/dist/events/index.d.ts +58 -3
  11. package/dist/events/index.js +1 -1
  12. package/dist/{index-Dzr437iW.d.ts → index-Cd0IZ40L.d.ts} +50 -11
  13. package/dist/index.d.ts +2 -12
  14. package/dist/index.js +764 -485
  15. package/dist/index.js.map +1 -1
  16. package/dist/scheduler/index.d.ts +1 -1
  17. package/dist/scheduler/index.js +2 -2
  18. package/dist/storage/index.d.ts +313 -47
  19. package/dist/storage/index.js +4 -2
  20. package/package.json +27 -13
  21. package/.codekin/reports/comments/2026-04-04_comment-assessment.md +0 -179
  22. package/.codekin/reports/complexity/2026-04-03_complexity-report.md +0 -152
  23. package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +0 -94
  24. package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +0 -10
  25. package/.codekin/reports/security/2026-04-03_security-audit.md +0 -208
  26. package/.github/CODEOWNERS +0 -15
  27. package/.github/dependabot.yml +0 -23
  28. package/.github/workflows/ci.yml +0 -38
  29. package/.github/workflows/publish.yml +0 -36
  30. package/.github/workflows/security.yml +0 -29
  31. package/CHANGELOG.md +0 -34
  32. package/SECURITY.md +0 -40
  33. package/dist/chunk-4VOKMGP6.js.map +0 -1
  34. package/dist/chunk-GYPNJBDP.js.map +0 -1
  35. package/dist/chunk-KHTUWLVZ.js.map +0 -1
  36. package/dist/chunk-ML35PIHX.js.map +0 -1
  37. package/docs/github-packages-setup.md +0 -97
  38. package/docs/stepflow-api-reference.md +0 -1661
  39. package/src/core/engine.test.ts +0 -1365
  40. package/src/core/engine.ts +0 -692
  41. package/src/core/index.ts +0 -7
  42. package/src/core/orchestrator.ts +0 -571
  43. package/src/core/types.ts +0 -220
  44. package/src/events/index.ts +0 -8
  45. package/src/events/memory.test.ts +0 -107
  46. package/src/events/memory.ts +0 -92
  47. package/src/events/socketio.test.ts +0 -378
  48. package/src/events/socketio.ts +0 -262
  49. package/src/events/types.ts +0 -118
  50. package/src/events/webhook.test.ts +0 -657
  51. package/src/events/webhook.ts +0 -564
  52. package/src/index.ts +0 -270
  53. package/src/planning/index.ts +0 -55
  54. package/src/planning/planner.ts +0 -576
  55. package/src/planning/planning.test.ts +0 -727
  56. package/src/planning/registry.test.ts +0 -398
  57. package/src/planning/registry.ts +0 -365
  58. package/src/planning/types.ts +0 -566
  59. package/src/scheduler/cron.test.ts +0 -471
  60. package/src/scheduler/cron.ts +0 -414
  61. package/src/scheduler/index.ts +0 -8
  62. package/src/scheduler/postgres-persistence.ts +0 -518
  63. package/src/scheduler/sqlite-persistence.test.ts +0 -235
  64. package/src/scheduler/sqlite-persistence.ts +0 -261
  65. package/src/scheduler/types.ts +0 -73
  66. package/src/storage/index.ts +0 -27
  67. package/src/storage/memory.test.ts +0 -349
  68. package/src/storage/memory.ts +0 -227
  69. package/src/storage/postgres.ts +0 -1354
  70. package/src/storage/sqlite.test.ts +0 -466
  71. package/src/storage/sqlite.ts +0 -627
  72. package/src/storage/types.ts +0 -343
  73. package/src/utils/errors.test.ts +0 -150
  74. package/src/utils/errors.ts +0 -161
  75. package/src/utils/id.ts +0 -21
  76. package/src/utils/index.ts +0 -8
  77. package/src/utils/logger.test.ts +0 -121
  78. package/src/utils/logger.ts +0 -96
  79. package/src/utils/postgres-deps.ts +0 -63
  80. package/src/utils/retry.test.ts +0 -177
  81. package/src/utils/retry.ts +0 -121
  82. package/tsconfig.json +0 -21
  83. package/tsup.config.ts +0 -14
  84. package/vitest.config.ts +0 -9
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  ConsoleLogger,
3
3
  generateId,
4
- loadPostgresDeps
5
- } from "./chunk-ML35PIHX.js";
4
+ loadPostgresDeps,
5
+ safeJsonParse
6
+ } from "./chunk-JPXPB7U5.js";
6
7
 
7
8
  // src/scheduler/cron.ts
8
9
  import { CronExpressionParser } from "cron-parser";
@@ -33,6 +34,14 @@ var CronScheduler = class {
33
34
  this.logger.warn("Scheduler is already running");
34
35
  return;
35
36
  }
37
+ if (this.eventUnsubscribe) {
38
+ this.eventUnsubscribe();
39
+ this.eventUnsubscribe = null;
40
+ }
41
+ if (this.pollTimer) {
42
+ clearInterval(this.pollTimer);
43
+ this.pollTimer = null;
44
+ }
36
45
  this.logger.info("Starting scheduler...");
37
46
  if (this.persistence) {
38
47
  const loaded = await this.persistence.loadSchedules();
@@ -43,7 +52,19 @@ var CronScheduler = class {
43
52
  }
44
53
  for (const schedule of this.schedules.values()) {
45
54
  if (schedule.triggerType === "cron" && schedule.cronExpression) {
46
- this.updateNextRunTime(schedule);
55
+ try {
56
+ CronExpressionParser.parse(schedule.cronExpression, {
57
+ tz: schedule.timezone
58
+ });
59
+ this.updateNextRunTime(schedule);
60
+ } catch (error) {
61
+ this.logger.error(
62
+ `Invalid cron expression "${schedule.cronExpression}" for schedule ${schedule.id}, disabling schedule:`,
63
+ error
64
+ );
65
+ schedule.enabled = false;
66
+ schedule.nextRunAt = void 0;
67
+ }
47
68
  }
48
69
  }
49
70
  this.pollTimer = setInterval(() => this.checkSchedules(), this.pollInterval);
@@ -258,6 +279,7 @@ var CronScheduler = class {
258
279
  var SQLiteSchedulePersistence = class {
259
280
  db;
260
281
  tableName;
282
+ logger;
261
283
  stmts = null;
262
284
  constructor(config) {
263
285
  this.db = config.db;
@@ -265,6 +287,7 @@ var SQLiteSchedulePersistence = class {
265
287
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(this.tableName)) {
266
288
  throw new Error(`Invalid table name: ${this.tableName}`);
267
289
  }
290
+ this.logger = config.logger;
268
291
  this.initializeDatabase();
269
292
  }
270
293
  // ============================================================================
@@ -391,6 +414,14 @@ var SQLiteSchedulePersistence = class {
391
414
  // ============================================================================
392
415
  // Helper Methods
393
416
  // ============================================================================
417
+ safeJsonParse(json, fallback, rowId, column) {
418
+ return safeJsonParse(json, fallback, {
419
+ component: "SQLiteSchedulePersistence",
420
+ rowId,
421
+ column,
422
+ logger: this.logger
423
+ });
424
+ }
394
425
  rowToSchedule(row) {
395
426
  return {
396
427
  id: row.id,
@@ -399,9 +430,9 @@ var SQLiteSchedulePersistence = class {
399
430
  cronExpression: row.cron_expression ?? void 0,
400
431
  timezone: row.timezone ?? void 0,
401
432
  triggerOnWorkflowKind: row.trigger_on_workflow_kind ?? void 0,
402
- triggerOnStatus: row.trigger_on_status ? JSON.parse(row.trigger_on_status) : void 0,
403
- input: row.input ? JSON.parse(row.input) : void 0,
404
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
433
+ triggerOnStatus: row.trigger_on_status ? this.safeJsonParse(row.trigger_on_status, void 0, row.id, "trigger_on_status") : void 0,
434
+ input: row.input ? this.safeJsonParse(row.input, void 0, row.id, "input") : void 0,
435
+ metadata: row.metadata ? this.safeJsonParse(row.metadata, void 0, row.id, "metadata") : void 0,
405
436
  enabled: row.enabled === 1,
406
437
  lastRunAt: row.last_run_at ? new Date(row.last_run_at) : void 0,
407
438
  lastRunId: row.last_run_id ?? void 0,
@@ -424,6 +455,7 @@ var PostgresSchedulePersistence = class {
424
455
  autoMigrate;
425
456
  initialized = false;
426
457
  config;
458
+ logger;
427
459
  constructor(config) {
428
460
  this.schema = config.schema ?? "public";
429
461
  if (!/^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/.test(this.schema)) {
@@ -439,6 +471,7 @@ var PostgresSchedulePersistence = class {
439
471
  }
440
472
  this.autoMigrate = config.autoMigrate !== false;
441
473
  this.config = config;
474
+ this.logger = config.logger;
442
475
  }
443
476
  /**
444
477
  * Get a schema-scoped query builder.
@@ -655,6 +688,18 @@ var PostgresSchedulePersistence = class {
655
688
  // ============================================================================
656
689
  // Helper Methods
657
690
  // ============================================================================
691
+ safeParseOptionalField(value, rowId, column) {
692
+ if (!value) return void 0;
693
+ if (typeof value === "string") {
694
+ return safeJsonParse(value, void 0, {
695
+ component: "PostgresSchedulePersistence",
696
+ rowId,
697
+ column,
698
+ logger: this.logger
699
+ });
700
+ }
701
+ return value;
702
+ }
658
703
  rowToSchedule(row) {
659
704
  return {
660
705
  id: row.id,
@@ -663,9 +708,9 @@ var PostgresSchedulePersistence = class {
663
708
  cronExpression: row.cron_expression ?? void 0,
664
709
  timezone: row.timezone ?? void 0,
665
710
  triggerOnWorkflowKind: row.trigger_on_workflow_kind ?? void 0,
666
- triggerOnStatus: row.trigger_on_status ? typeof row.trigger_on_status === "string" ? JSON.parse(row.trigger_on_status) : row.trigger_on_status : void 0,
667
- input: row.input_json ? typeof row.input_json === "string" ? JSON.parse(row.input_json) : row.input_json : void 0,
668
- metadata: row.metadata_json ? typeof row.metadata_json === "string" ? JSON.parse(row.metadata_json) : row.metadata_json : void 0,
711
+ triggerOnStatus: this.safeParseOptionalField(row.trigger_on_status, row.id, "trigger_on_status"),
712
+ input: this.safeParseOptionalField(row.input_json, row.id, "input_json"),
713
+ metadata: this.safeParseOptionalField(row.metadata_json, row.id, "metadata_json"),
669
714
  enabled: row.enabled,
670
715
  lastRunAt: row.last_run_at ? new Date(row.last_run_at) : void 0,
671
716
  lastRunId: row.last_run_id ?? void 0,
@@ -679,4 +724,4 @@ export {
679
724
  SQLiteSchedulePersistence,
680
725
  PostgresSchedulePersistence
681
726
  };
682
- //# sourceMappingURL=chunk-KHTUWLVZ.js.map
727
+ //# sourceMappingURL=chunk-FMPJBK2N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scheduler/cron.ts","../src/scheduler/sqlite-persistence.ts","../src/scheduler/postgres-persistence.ts"],"sourcesContent":["/**\n * CronScheduler - Scheduler implementation using cron expressions.\n *\n * Provides time-based and workflow-completion-based triggers for workflows.\n */\n\nimport { CronExpressionParser } from 'cron-parser';\nimport type { WorkflowKind, RunStatus, Logger } from '../core/types';\nimport type { WorkflowEngine } from '../core/engine';\nimport type { Scheduler, WorkflowSchedule, TriggerType } from './types';\nimport type { WorkflowEvent } from '../events/types';\nimport { generateId } from '../utils/id';\nimport { ConsoleLogger } from '../utils/logger';\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\n/**\n * Configuration for the CronScheduler.\n */\nexport interface CronSchedulerConfig {\n /** The workflow engine instance */\n engine: WorkflowEngine;\n\n /** Logger instance */\n logger?: Logger;\n\n /** Poll interval for checking schedules (ms, default: 1000) */\n pollInterval?: number;\n\n /** Optional persistence adapter for schedules */\n persistence?: SchedulePersistence;\n}\n\n/**\n * Interface for schedule persistence.\n * Implement this to persist schedules to a database.\n */\nexport interface SchedulePersistence {\n /** Load all schedules from storage */\n loadSchedules(): Promise<WorkflowSchedule[]>;\n\n /** Save a schedule */\n saveSchedule(schedule: WorkflowSchedule): Promise<void>;\n\n /** Update a schedule */\n updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void>;\n\n /** Delete a schedule */\n deleteSchedule(scheduleId: string): Promise<void>;\n}\n\n// ============================================================================\n// CronScheduler Class\n// ============================================================================\n\n/**\n * Scheduler implementation that supports cron expressions and workflow completion triggers.\n *\n * @example\n * ```typescript\n * const scheduler = new CronScheduler({\n * engine,\n * pollInterval: 1000,\n * });\n *\n * // Add a cron schedule (every day at midnight)\n * await scheduler.addSchedule({\n * workflowKind: 'cleanup.daily',\n * triggerType: 'cron',\n * cronExpression: '0 0 * * *',\n * enabled: true,\n * });\n *\n * // Add a workflow completion trigger\n * await scheduler.addSchedule({\n * workflowKind: 'notification.send',\n * triggerType: 'workflow_completed',\n * triggerOnWorkflowKind: 'order.process',\n * triggerOnStatus: ['succeeded'],\n * enabled: true,\n * });\n *\n * await scheduler.start();\n * ```\n */\nexport class CronScheduler implements Scheduler {\n private engine: WorkflowEngine;\n private logger: Logger;\n private pollInterval: number;\n private persistence?: SchedulePersistence;\n\n private schedules = new Map<string, WorkflowSchedule>();\n private running = false;\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private eventUnsubscribe: (() => void) | null = null;\n\n constructor(config: CronSchedulerConfig) {\n this.engine = config.engine;\n this.logger = config.logger ?? new ConsoleLogger();\n this.pollInterval = config.pollInterval ?? 1000;\n this.persistence = config.persistence;\n }\n\n // ============================================================================\n // Scheduler Interface Implementation\n // ============================================================================\n\n /**\n * Start the scheduler.\n * Begins polling for cron schedules and subscribes to workflow completion events.\n */\n async start(): Promise<void> {\n if (this.running) {\n this.logger.warn('Scheduler is already running');\n return;\n }\n\n // Clean up any leftover subscriptions from a previous start/stop cycle\n if (this.eventUnsubscribe) {\n this.eventUnsubscribe();\n this.eventUnsubscribe = null;\n }\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\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 // Wrap each schedule in try-catch so one invalid persisted cron expression\n // doesn't prevent the rest from loading.\n for (const schedule of this.schedules.values()) {\n if (schedule.triggerType === 'cron' && schedule.cronExpression) {\n try {\n CronExpressionParser.parse(schedule.cronExpression, {\n tz: schedule.timezone,\n });\n this.updateNextRunTime(schedule);\n } catch (error) {\n this.logger.error(\n `Invalid cron expression \"${schedule.cronExpression}\" for schedule ${schedule.id}, disabling schedule:`,\n error\n );\n schedule.enabled = false;\n schedule.nextRunAt = undefined;\n }\n }\n }\n\n // Start polling for cron schedules\n this.pollTimer = setInterval(() => this.checkSchedules(), this.pollInterval);\n\n // Subscribe to workflow completion events\n this.eventUnsubscribe = this.engine.subscribeToAll((event) => {\n this.handleWorkflowEvent(event);\n });\n\n this.running = true;\n this.logger.info('Scheduler started');\n }\n\n /**\n * Stop the scheduler.\n * Stops polling and unsubscribes from events.\n */\n async stop(): Promise<void> {\n if (!this.running) {\n return;\n }\n\n this.logger.info('Stopping scheduler...');\n\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n\n if (this.eventUnsubscribe) {\n this.eventUnsubscribe();\n this.eventUnsubscribe = null;\n }\n\n this.running = false;\n this.logger.info('Scheduler stopped');\n }\n\n /**\n * Add a new schedule.\n */\n async addSchedule(scheduleData: Omit<WorkflowSchedule, 'id'>): Promise<WorkflowSchedule> {\n const schedule: WorkflowSchedule = {\n ...scheduleData,\n id: generateId(),\n };\n\n // Validate cron expression if provided\n if (schedule.triggerType === 'cron' && schedule.cronExpression) {\n try {\n CronExpressionParser.parse(schedule.cronExpression, {\n tz: schedule.timezone,\n });\n } catch (error) {\n throw new Error(`Invalid cron expression: ${schedule.cronExpression}`);\n }\n\n // Calculate next run time\n this.updateNextRunTime(schedule);\n }\n\n // Validate workflow completion trigger\n if (schedule.triggerType === 'workflow_completed') {\n if (!schedule.triggerOnWorkflowKind) {\n throw new Error('triggerOnWorkflowKind is required for workflow_completed triggers');\n }\n }\n\n this.schedules.set(schedule.id, schedule);\n\n // Persist if available\n if (this.persistence) {\n await this.persistence.saveSchedule(schedule);\n }\n\n this.logger.info(`Added schedule: ${schedule.id} (${schedule.workflowKind})`);\n\n return schedule;\n }\n\n /**\n * Remove a schedule.\n */\n async removeSchedule(scheduleId: string): Promise<void> {\n if (!this.schedules.has(scheduleId)) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n this.schedules.delete(scheduleId);\n\n if (this.persistence) {\n await this.persistence.deleteSchedule(scheduleId);\n }\n\n this.logger.info(`Removed schedule: ${scheduleId}`);\n }\n\n /**\n * Update a schedule.\n */\n async updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void> {\n const schedule = this.schedules.get(scheduleId);\n if (!schedule) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n // Don't allow changing the ID\n delete updates.id;\n\n // Update the schedule\n Object.assign(schedule, updates);\n\n // Revalidate and update next run time if cron expression changed\n if (updates.cronExpression !== undefined || updates.timezone !== undefined) {\n if (schedule.cronExpression) {\n try {\n CronExpressionParser.parse(schedule.cronExpression, {\n tz: schedule.timezone,\n });\n this.updateNextRunTime(schedule);\n } catch (error) {\n throw new Error(`Invalid cron expression: ${schedule.cronExpression}`);\n }\n }\n }\n\n if (this.persistence) {\n await this.persistence.updateSchedule(scheduleId, updates);\n }\n\n this.logger.debug(`Updated schedule: ${scheduleId}`);\n }\n\n /**\n * Get all schedules.\n */\n async getSchedules(): Promise<WorkflowSchedule[]> {\n return Array.from(this.schedules.values());\n }\n\n /**\n * Get a schedule by ID.\n */\n getSchedule(scheduleId: string): WorkflowSchedule | undefined {\n return this.schedules.get(scheduleId);\n }\n\n /**\n * Manually trigger a scheduled workflow.\n */\n async triggerNow(scheduleId: string): Promise<string> {\n const schedule = this.schedules.get(scheduleId);\n if (!schedule) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n return this.executeSchedule(schedule);\n }\n\n // ============================================================================\n // Internal Methods\n // ============================================================================\n\n /**\n * Check all cron schedules and execute those that are due.\n */\n private checkSchedules(): void {\n const now = new Date();\n\n for (const schedule of this.schedules.values()) {\n if (!schedule.enabled) continue;\n if (schedule.triggerType !== 'cron') continue;\n if (!schedule.nextRunAt) continue;\n\n if (schedule.nextRunAt <= now) {\n // Execute the schedule\n this.executeSchedule(schedule).catch((error) => {\n this.logger.error(`Failed to execute schedule ${schedule.id}:`, error);\n });\n\n // Update next run time\n this.updateNextRunTime(schedule);\n }\n }\n }\n\n /**\n * Handle workflow events for completion triggers.\n */\n private handleWorkflowEvent(event: WorkflowEvent): void {\n // Only handle run completion events\n // Note: orchestrator emits 'run.completed' for success, 'run.failed' for failure\n if (event.eventType !== 'run.completed' && event.eventType !== 'run.failed') {\n return;\n }\n\n const completedStatus: RunStatus = event.eventType === 'run.completed' ? 'succeeded' : 'failed';\n const completedKind = event.kind;\n\n // Find schedules triggered by this workflow completion\n for (const schedule of this.schedules.values()) {\n if (!schedule.enabled) continue;\n if (schedule.triggerType !== 'workflow_completed') continue;\n if (schedule.triggerOnWorkflowKind !== completedKind) continue;\n\n // Check if the status matches\n if (schedule.triggerOnStatus && !schedule.triggerOnStatus.includes(completedStatus)) {\n continue;\n }\n\n // Execute the schedule\n this.executeSchedule(schedule, {\n triggerRunId: event.runId,\n triggerStatus: completedStatus,\n }).catch((error) => {\n this.logger.error(`Failed to execute schedule ${schedule.id}:`, error);\n });\n }\n }\n\n /**\n * Execute a schedule by starting the workflow.\n */\n private async executeSchedule(\n schedule: WorkflowSchedule,\n triggerContext?: { triggerRunId?: string; triggerStatus?: RunStatus }\n ): Promise<string> {\n this.logger.info(`Executing schedule: ${schedule.id} (${schedule.workflowKind})`);\n\n // Build metadata with trigger info\n const metadata = {\n ...schedule.metadata,\n scheduleId: schedule.id,\n triggerType: schedule.triggerType,\n ...(triggerContext ?? {}),\n };\n\n // Start the workflow\n const runId = await this.engine.startRun({\n kind: schedule.workflowKind,\n input: schedule.input,\n metadata,\n });\n\n // Update schedule state\n schedule.lastRunAt = new Date();\n schedule.lastRunId = runId;\n\n if (this.persistence) {\n await this.persistence.updateSchedule(schedule.id, {\n lastRunAt: schedule.lastRunAt,\n lastRunId: schedule.lastRunId,\n nextRunAt: schedule.nextRunAt,\n });\n }\n\n return runId;\n }\n\n /**\n * Update the next run time for a cron schedule.\n */\n private updateNextRunTime(schedule: WorkflowSchedule): void {\n if (!schedule.cronExpression) return;\n\n try {\n const interval = CronExpressionParser.parse(schedule.cronExpression, {\n currentDate: new Date(),\n tz: schedule.timezone,\n });\n\n schedule.nextRunAt = interval.next().toDate();\n } catch (error) {\n this.logger.error(`Failed to parse cron expression for schedule ${schedule.id}:`, error);\n schedule.nextRunAt = undefined;\n }\n }\n}\n","/**\n * SQLite persistence adapter for schedules.\n *\n * Stores workflow schedules in a SQLite database table.\n */\n\nimport type { Database, Statement } from 'better-sqlite3';\nimport type { WorkflowSchedule } from './types';\nimport type { SchedulePersistence } from './cron';\nimport type { Logger, RunStatus } from '../core/types';\nimport { generateId } from '../utils/id';\nimport { safeJsonParse as sharedSafeJsonParse } from '../utils/safe-json';\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 * Optional structured logger used to surface JSON corruption events.\n * Without one, parse failures still increment the corruption counter\n * exposed via `getJsonParseCorruptionCount()` but produce no log output.\n */\n logger?: Logger;\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 logger: Logger | undefined;\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.logger = config.logger;\n this.initializeDatabase();\n }\n\n // ============================================================================\n // Database Initialization\n // ============================================================================\n\n private initializeDatabase(): void {\n // Create schedules table.\n // Schema layout:\n // id — unique schedule identifier (TEXT PK)\n // workflow_kind — target workflow type to spawn\n // trigger_type — 'cron', 'workflow_completed', or 'manual'\n // cron_expression — cron expression for time-based triggers (nullable)\n // timezone — IANA timezone for cron evaluation (nullable)\n // trigger_on_* — fields for workflow-completion triggers\n // input / metadata — JSON-serialized payloads for spawned runs\n // enabled — whether this schedule is active (0/1)\n // last_run_at/id — most recent execution tracking\n // next_run_at — next scheduled execution time\n // created_at/updated_at — audit timestamps\n // No migration strategy is currently implemented; table is created idempotently.\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n id TEXT PRIMARY KEY,\n workflow_kind TEXT NOT NULL,\n trigger_type TEXT NOT NULL,\n cron_expression TEXT,\n timezone TEXT,\n trigger_on_workflow_kind TEXT,\n trigger_on_status TEXT,\n input TEXT,\n metadata TEXT,\n enabled INTEGER NOT NULL DEFAULT 1,\n last_run_at TEXT,\n last_run_id TEXT,\n next_run_at TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `);\n\n // Create indexes\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_workflow_kind\n ON ${this.tableName}(workflow_kind);\n\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_enabled\n ON ${this.tableName}(enabled);\n\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_trigger_type\n ON ${this.tableName}(trigger_type);\n `);\n\n // Prepare statements\n this.stmts = {\n insert: this.db.prepare(`\n INSERT INTO ${this.tableName} (\n id, workflow_kind, trigger_type, cron_expression, timezone,\n trigger_on_workflow_kind, trigger_on_status, input, metadata,\n enabled, last_run_at, last_run_id, next_run_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `),\n update: this.db.prepare(`\n UPDATE ${this.tableName}\n SET workflow_kind = COALESCE(?, workflow_kind),\n trigger_type = COALESCE(?, trigger_type),\n cron_expression = ?,\n timezone = ?,\n trigger_on_workflow_kind = ?,\n trigger_on_status = ?,\n input = ?,\n metadata = ?,\n enabled = COALESCE(?, enabled),\n last_run_at = ?,\n last_run_id = ?,\n next_run_at = ?,\n updated_at = datetime('now')\n WHERE id = ?\n `),\n delete: this.db.prepare(`\n DELETE FROM ${this.tableName} WHERE id = ?\n `),\n getAll: this.db.prepare(`\n SELECT * FROM ${this.tableName}\n `),\n getById: this.db.prepare(`\n SELECT * FROM ${this.tableName} WHERE id = ?\n `),\n };\n }\n\n // ============================================================================\n // SchedulePersistence Interface Implementation\n // ============================================================================\n\n async loadSchedules(): Promise<WorkflowSchedule[]> {\n const rows = this.stmts!.getAll.all() as ScheduleRow[];\n return rows.map(row => this.rowToSchedule(row));\n }\n\n async saveSchedule(schedule: WorkflowSchedule): Promise<void> {\n this.stmts!.insert.run(\n schedule.id,\n schedule.workflowKind,\n schedule.triggerType,\n schedule.cronExpression ?? null,\n schedule.timezone ?? null,\n schedule.triggerOnWorkflowKind ?? null,\n schedule.triggerOnStatus ? JSON.stringify(schedule.triggerOnStatus) : null,\n schedule.input ? JSON.stringify(schedule.input) : null,\n schedule.metadata ? JSON.stringify(schedule.metadata) : null,\n schedule.enabled ? 1 : 0,\n schedule.lastRunAt?.toISOString() ?? null,\n schedule.lastRunId ?? null,\n schedule.nextRunAt?.toISOString() ?? null\n );\n }\n\n async updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void> {\n // Get existing schedule to merge with updates\n const existing = this.stmts!.getById.get(scheduleId) as ScheduleRow | undefined;\n if (!existing) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n const merged = {\n ...this.rowToSchedule(existing),\n ...updates,\n };\n\n this.stmts!.update.run(\n updates.workflowKind ?? null,\n updates.triggerType ?? null,\n merged.cronExpression ?? null,\n merged.timezone ?? null,\n merged.triggerOnWorkflowKind ?? null,\n merged.triggerOnStatus ? JSON.stringify(merged.triggerOnStatus) : null,\n merged.input ? JSON.stringify(merged.input) : null,\n merged.metadata ? JSON.stringify(merged.metadata) : null,\n updates.enabled !== undefined ? (updates.enabled ? 1 : 0) : null,\n merged.lastRunAt?.toISOString() ?? null,\n merged.lastRunId ?? null,\n merged.nextRunAt?.toISOString() ?? null,\n scheduleId\n );\n }\n\n async deleteSchedule(scheduleId: string): Promise<void> {\n this.stmts!.delete.run(scheduleId);\n }\n\n // ============================================================================\n // Helper Methods\n // ============================================================================\n\n private safeJsonParse(\n json: string,\n fallback: unknown,\n rowId: string,\n column: string,\n ): unknown {\n return sharedSafeJsonParse(json, fallback, {\n component: 'SQLiteSchedulePersistence',\n rowId,\n column,\n logger: this.logger,\n });\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 ? this.safeJsonParse(row.trigger_on_status, undefined, row.id, 'trigger_on_status') as RunStatus[] | undefined\n : undefined,\n input: row.input ? this.safeJsonParse(row.input, undefined, row.id, 'input') as Record<string, unknown> | undefined : undefined,\n metadata: row.metadata ? this.safeJsonParse(row.metadata, undefined, row.id, 'metadata') as Record<string, unknown> | undefined : 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';\nimport type { Logger, RunStatus } from '../core/types.js';\nimport { safeJsonParse as sharedSafeJsonParse } from '../utils/safe-json.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 * Optional structured logger used to surface JSON corruption events.\n * Without one, parse failures still increment the corruption counter\n * exposed via `getJsonParseCorruptionCount()` but produce no log output.\n */\n logger?: Logger;\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 private logger: Logger | undefined;\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 this.logger = config.logger;\n }\n\n /**\n * Get a schema-scoped query builder.\n * All queries MUST use this instead of this.db directly to respect config.schema.\n */\n private get qb() {\n return this.db.withSchema(this.schema);\n }\n\n private ensureInitialized(): void {\n if (!this.initialized) {\n throw new Error(\n 'PostgresSchedulePersistence is not initialized. Call initialize() before using the adapter.'\n );\n }\n }\n\n /**\n * Initialize the persistence layer.\n * Creates the schedules table if autoMigrate is enabled.\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n const deps = await loadPostgresDeps();\n Kysely = deps.Kysely;\n PostgresDialect = deps.PostgresDialect;\n sql = deps.sql;\n pgModule = deps.pgModule;\n\n if (this.config.pool) {\n this.pool = this.config.pool;\n this.ownsPool = false;\n } else if (this.config.connectionString) {\n this.pool = new pgModule.Pool({ connectionString: this.config.connectionString });\n this.ownsPool = true;\n } else if (this.config.poolConfig) {\n this.pool = new pgModule.Pool(this.config.poolConfig);\n this.ownsPool = true;\n } else {\n throw new Error(\n 'PostgresSchedulePersistenceConfig must include either pool, connectionString, or poolConfig'\n );\n }\n\n this.db = new Kysely({\n dialect: new PostgresDialect({\n pool: this.pool,\n }),\n });\n\n if (this.autoMigrate) {\n await this.createTables();\n }\n\n this.initialized = true;\n }\n\n /**\n * Close the database connection.\n * Only closes the pool if it was created by this adapter.\n */\n async close(): Promise<void> {\n await this.db.destroy();\n if (this.ownsPool) {\n await this.pool.end();\n }\n }\n\n // ============================================================================\n // Database Initialization\n // ============================================================================\n\n private async createTables(): Promise<void> {\n const fullTableName = this.schema === 'public'\n ? this.tableName\n : `${this.schema}.${this.tableName}`;\n\n // Create schema if not public\n if (this.schema !== 'public') {\n await sql`CREATE SCHEMA IF NOT EXISTS ${sql.ref(this.schema)}`.execute(this.db);\n }\n\n // Create schedules table.\n // Schema layout:\n // id — unique schedule identifier (TEXT PK)\n // workflow_kind — target workflow type to spawn\n // trigger_type — constrained to 'cron' | 'workflow_completed' | 'manual'\n // cron_expression / timezone — cron trigger configuration\n // trigger_on_workflow_kind — completion trigger source kind\n // trigger_on_status — JSONB array of RunStatus values for completion triggers\n // input_json / metadata_json — JSONB payloads for spawned runs\n // enabled — whether this schedule is active\n // last_run_at / last_run_id — most recent execution tracking\n // next_run_at — next scheduled execution time\n // created_at / updated_at — audit timestamps (TIMESTAMPTZ)\n // No migration strategy is currently implemented; table is created idempotently.\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql.table(fullTableName)} (\n id TEXT PRIMARY KEY,\n workflow_kind TEXT NOT NULL,\n trigger_type TEXT NOT NULL,\n cron_expression TEXT,\n timezone TEXT DEFAULT 'UTC',\n trigger_on_workflow_kind TEXT,\n trigger_on_status JSONB,\n input_json JSONB NOT NULL DEFAULT '{}',\n metadata_json JSONB NOT NULL DEFAULT '{}',\n enabled BOOLEAN NOT NULL DEFAULT true,\n last_run_at TIMESTAMPTZ,\n last_run_id TEXT,\n next_run_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n CONSTRAINT ${sql.ref(`${this.tableName}_trigger_type_check`)} CHECK (\n trigger_type IN ('cron', 'workflow_completed', 'manual')\n )\n )\n `.execute(this.db);\n\n // Create indexes\n await sql`\n CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${this.tableName}_workflow_kind`)}\n ON ${sql.table(fullTableName)} (workflow_kind)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${this.tableName}_enabled`)}\n ON ${sql.table(fullTableName)} (enabled)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${this.tableName}_trigger_type`)}\n ON ${sql.table(fullTableName)} (trigger_type)\n `.execute(this.db);\n\n await sql`\n CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${this.tableName}_next_run`)}\n ON ${sql.table(fullTableName)} (next_run_at)\n WHERE enabled = true\n `.execute(this.db);\n }\n\n // ============================================================================\n // SchedulePersistence Interface Implementation\n // ============================================================================\n\n async loadSchedules(): Promise<WorkflowSchedule[]> {\n this.ensureInitialized();\n const rows = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .execute() as WorkflowSchedulesTable[];\n\n return rows.map(row => this.rowToSchedule(row));\n }\n\n async saveSchedule(schedule: WorkflowSchedule): Promise<void> {\n this.ensureInitialized();\n await this.qb\n .insertInto(this.tableName as any)\n .values({\n id: schedule.id,\n workflow_kind: schedule.workflowKind,\n trigger_type: schedule.triggerType,\n cron_expression: schedule.cronExpression ?? null,\n timezone: schedule.timezone ?? null,\n trigger_on_workflow_kind: schedule.triggerOnWorkflowKind ?? null,\n trigger_on_status: schedule.triggerOnStatus ? JSON.stringify(schedule.triggerOnStatus) : null,\n input_json: schedule.input ? JSON.stringify(schedule.input) : null,\n metadata_json: schedule.metadata ? JSON.stringify(schedule.metadata) : null,\n enabled: schedule.enabled,\n last_run_at: schedule.lastRunAt ?? null,\n last_run_id: schedule.lastRunId ?? null,\n next_run_at: schedule.nextRunAt ?? null,\n created_at: new Date(),\n updated_at: new Date(),\n })\n .execute();\n }\n\n async updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void> {\n this.ensureInitialized();\n // Partial update pattern: fetch the existing row, merge with incoming updates,\n // then build a column-level update object. Each field is only included if it was\n // explicitly provided in `updates` (or affected by the merge), so unchanged\n // columns are left untouched in the database.\n const existing = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('id', '=', scheduleId)\n .executeTakeFirst() as WorkflowSchedulesTable | undefined;\n\n if (!existing) {\n throw new Error(`Schedule not found: ${scheduleId}`);\n }\n\n const merged = {\n ...this.rowToSchedule(existing),\n ...updates,\n };\n\n // Field mapping: { domainKey, dbKey, serialize? }\n // Each entry maps a WorkflowSchedule field to its DB column, with optional serialization.\n const fieldMappings: Array<{\n domainKey: keyof WorkflowSchedule;\n dbKey: keyof WorkflowSchedulesTable;\n serialize?: (value: any) => any;\n }> = [\n { domainKey: 'workflowKind', dbKey: 'workflow_kind' },\n { domainKey: 'triggerType', dbKey: 'trigger_type' },\n { domainKey: 'cronExpression', dbKey: 'cron_expression' },\n { domainKey: 'timezone', dbKey: 'timezone' },\n { domainKey: 'triggerOnWorkflowKind', dbKey: 'trigger_on_workflow_kind' },\n { domainKey: 'triggerOnStatus', dbKey: 'trigger_on_status', serialize: v => v ? JSON.stringify(v) : null },\n { domainKey: 'input', dbKey: 'input_json', serialize: v => v ? JSON.stringify(v) : null },\n { domainKey: 'metadata', dbKey: 'metadata_json', serialize: v => v ? JSON.stringify(v) : null },\n { domainKey: 'enabled', dbKey: 'enabled' },\n { domainKey: 'lastRunAt', dbKey: 'last_run_at' },\n { domainKey: 'lastRunId', dbKey: 'last_run_id' },\n { domainKey: 'nextRunAt', dbKey: 'next_run_at' },\n ];\n\n const updateData: Partial<WorkflowSchedulesTable> = {\n updated_at: new Date(),\n };\n\n for (const { domainKey, dbKey, serialize } of fieldMappings) {\n if (updates[domainKey] !== undefined || merged[domainKey] !== undefined) {\n const value = serialize\n ? serialize(merged[domainKey])\n : (merged[domainKey] ?? null);\n (updateData as any)[dbKey] = value;\n }\n }\n\n await this.qb\n .updateTable(this.tableName as any)\n .set(updateData)\n .where('id', '=', scheduleId)\n .execute();\n }\n\n async deleteSchedule(scheduleId: string): Promise<void> {\n this.ensureInitialized();\n await this.qb\n .deleteFrom(this.tableName as any)\n .where('id', '=', scheduleId)\n .execute();\n }\n\n // ============================================================================\n // Additional Methods\n // ============================================================================\n\n /**\n * Get a schedule by ID.\n */\n async getSchedule(scheduleId: string): Promise<WorkflowSchedule | null> {\n this.ensureInitialized();\n const row = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('id', '=', scheduleId)\n .executeTakeFirst() as WorkflowSchedulesTable | undefined;\n\n return row ? this.rowToSchedule(row) : null;\n }\n\n /**\n * Get all enabled schedules that are due to run.\n */\n async getDueSchedules(): Promise<WorkflowSchedule[]> {\n this.ensureInitialized();\n const now = new Date();\n\n const rows = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('enabled', '=', true)\n .where('trigger_type', '=', 'cron')\n .where('next_run_at', '<=', now)\n .execute() as WorkflowSchedulesTable[];\n\n return rows.map(row => this.rowToSchedule(row));\n }\n\n /**\n * Get schedules by workflow kind.\n */\n async getSchedulesByWorkflowKind(workflowKind: string): Promise<WorkflowSchedule[]> {\n this.ensureInitialized();\n const rows = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('workflow_kind', '=', workflowKind)\n .execute() as WorkflowSchedulesTable[];\n\n return rows.map(row => this.rowToSchedule(row));\n }\n\n /**\n * Get workflow completion triggers for a specific workflow kind.\n */\n async getCompletionTriggers(triggerOnWorkflowKind: string): Promise<WorkflowSchedule[]> {\n this.ensureInitialized();\n const rows = await this.qb\n .selectFrom(this.tableName as any)\n .selectAll()\n .where('enabled', '=', true)\n .where('trigger_type', '=', 'workflow_completed')\n .where('trigger_on_workflow_kind', '=', triggerOnWorkflowKind)\n .execute() as WorkflowSchedulesTable[];\n\n return rows.map(row => this.rowToSchedule(row));\n }\n\n // ============================================================================\n // Helper Methods\n // ============================================================================\n\n private safeParseOptionalField(value: unknown, rowId: string, column: string): unknown {\n if (!value) return undefined;\n if (typeof value === 'string') {\n return sharedSafeJsonParse(value, undefined, {\n component: 'PostgresSchedulePersistence',\n rowId,\n column,\n logger: this.logger,\n });\n }\n return value;\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: this.safeParseOptionalField(row.trigger_on_status, row.id, 'trigger_on_status') as RunStatus[] | undefined,\n input: this.safeParseOptionalField(row.input_json, row.id, 'input_json') as Record<string, unknown> | undefined,\n metadata: this.safeParseOptionalField(row.metadata_json, row.id, 'metadata_json') as Record<string, unknown> | 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;AAiF9B,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;AAGA,QAAI,KAAK,kBAAkB;AACzB,WAAK,iBAAiB;AACtB,WAAK,mBAAmB;AAAA,IAC1B;AACA,QAAI,KAAK,WAAW;AAClB,oBAAc,KAAK,SAAS;AAC5B,WAAK,YAAY;AAAA,IACnB;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;AAKA,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC9C,UAAI,SAAS,gBAAgB,UAAU,SAAS,gBAAgB;AAC9D,YAAI;AACF,+BAAqB,MAAM,SAAS,gBAAgB;AAAA,YAClD,IAAI,SAAS;AAAA,UACf,CAAC;AACD,eAAK,kBAAkB,QAAQ;AAAA,QACjC,SAAS,OAAO;AACd,eAAK,OAAO;AAAA,YACV,4BAA4B,SAAS,cAAc,kBAAkB,SAAS,EAAE;AAAA,YAChF;AAAA,UACF;AACA,mBAAS,UAAU;AACnB,mBAAS,YAAY;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAGA,SAAK,YAAY,YAAY,MAAM,KAAK,eAAe,GAAG,KAAK,YAAY;AAG3E,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,YAAY,cAAuE;AACvF,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,MAAM,mEAAmE;AAAA,MACrF;AAAA,IACF;AAEA,SAAK,UAAU,IAAI,SAAS,IAAI,QAAQ;AAGxC,QAAI,KAAK,aAAa;AACpB,YAAM,KAAK,YAAY,aAAa,QAAQ;AAAA,IAC9C;AAEA,SAAK,OAAO,KAAK,mBAAmB,SAAS,EAAE,KAAK,SAAS,YAAY,GAAG;AAE5E,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,eAAe,YAAoB,SAAmD;AAC1F,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,QAAI,QAAQ,mBAAmB,UAAa,QAAQ,aAAa,QAAW;AAC1E,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,MAAM,4BAA4B,SAAS,cAAc,EAAE;AAAA,QACvE;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,MAAM,8BAA8B,SAAS,EAAE,KAAK,KAAK;AAAA,QACvE,CAAC;AAGD,aAAK,kBAAkB,QAAQ;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,OAA4B;AAGtD,QAAI,MAAM,cAAc,mBAAmB,MAAM,cAAc,cAAc;AAC3E;AAAA,IACF;AAEA,UAAM,kBAA6B,MAAM,cAAc,kBAAkB,cAAc;AACvF,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,UAAI,SAAS,mBAAmB,CAAC,SAAS,gBAAgB,SAAS,eAAe,GAAG;AACnF;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,KAAK,uBAAuB,SAAS,EAAE,KAAK,SAAS,YAAY,GAAG;AAGhF,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,MAAM,gDAAgD,SAAS,EAAE,KAAK,KAAK;AACvF,eAAS,YAAY;AAAA,IACvB;AAAA,EACF;AACF;;;AC9XO,IAAM,4BAAN,MAA+D;AAAA,EAC5D;AAAA,EACA;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,SAAS,OAAO;AACrB,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,SAAO,KAAK,cAAc,GAAG,CAAC;AAAA,EAChD;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,kBAAkB,KAAK,UAAU,SAAS,eAAe,IAAI;AAAA,MACtE,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,eAAe,YAAoB,SAAmD;AAE1F,UAAM,WAAW,KAAK,MAAO,QAAQ,IAAI,UAAU;AACnD,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,cACN,MACA,UACA,OACA,QACS;AACT,WAAO,cAAoB,MAAM,UAAU;AAAA,MACzC,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA,QAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEQ,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,cAAc,IAAI,mBAAmB,QAAW,IAAI,IAAI,mBAAmB,IAChF;AAAA,MACJ,OAAO,IAAI,QAAQ,KAAK,cAAc,IAAI,OAAO,QAAW,IAAI,IAAI,OAAO,IAA2C;AAAA,MACtH,UAAU,IAAI,WAAW,KAAK,cAAc,IAAI,UAAU,QAAW,IAAI,IAAI,UAAU,IAA2C;AAAA,MAClI,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;;;AC5PA,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AAiIG,IAAM,8BAAN,MAAiE;AAAA,EAC9D;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;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;AACd,SAAK,SAAS,OAAO;AAAA,EACvB;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,EAAE,kBAAkB,KAAK,OAAO,iBAAiB,CAAC;AAChF,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,gBAAgB,KAAK,WAAW,WAClC,KAAK,YACL,GAAG,KAAK,MAAM,IAAI,KAAK,SAAS;AAGpC,QAAI,KAAK,WAAW,UAAU;AAC5B,YAAM,kCAAkC,IAAI,IAAI,KAAK,MAAM,CAAC,GAAG,QAAQ,KAAK,EAAE;AAAA,IAChF;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,OAAO,MAAM,KAAK,GACrB,WAAW,KAAK,SAAgB,EAChC,UAAU,EACV,QAAQ;AAEX,WAAO,KAAK,IAAI,SAAO,KAAK,cAAc,GAAG,CAAC;AAAA,EAChD;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,kBAAkB,KAAK,UAAU,SAAS,eAAe,IAAI;AAAA,MACzF,YAAY,SAAS,QAAQ,KAAK,UAAU,SAAS,KAAK,IAAI;AAAA,MAC9D,eAAe,SAAS,WAAW,KAAK,UAAU,SAAS,QAAQ,IAAI;AAAA,MACvE,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,eAAe,YAAoB,SAAmD;AAC1F,SAAK,kBAAkB;AAKvB,UAAM,WAAW,MAAM,KAAK,GACzB,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,EAAE,WAAW,mBAAmB,OAAO,qBAAqB,WAAW,OAAK,IAAI,KAAK,UAAU,CAAC,IAAI,KAAK;AAAA,MACzG,EAAE,WAAW,SAAS,OAAO,cAAc,WAAW,OAAK,IAAI,KAAK,UAAU,CAAC,IAAI,KAAK;AAAA,MACxF,EAAE,WAAW,YAAY,OAAO,iBAAiB,WAAW,OAAK,IAAI,KAAK,UAAU,CAAC,IAAI,KAAK;AAAA,MAC9F,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,MAAM,MAAM,KAAK,GACpB,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,OAAO,MAAM,KAAK,GACrB,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,SAAO,KAAK,cAAc,GAAG,CAAC;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,2BAA2B,cAAmD;AAClF,SAAK,kBAAkB;AACvB,UAAM,OAAO,MAAM,KAAK,GACrB,WAAW,KAAK,SAAgB,EAChC,UAAU,EACV,MAAM,iBAAiB,KAAK,YAAY,EACxC,QAAQ;AAEX,WAAO,KAAK,IAAI,SAAO,KAAK,cAAc,GAAG,CAAC;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBAAsB,uBAA4D;AACtF,SAAK,kBAAkB;AACvB,UAAM,OAAO,MAAM,KAAK,GACrB,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,SAAO,KAAK,cAAc,GAAG,CAAC;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAMQ,uBAAuB,OAAgB,OAAe,QAAyB;AACrF,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,cAAoB,OAAO,QAAW;AAAA,QAC3C,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA,QAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,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,KAAK,uBAAuB,IAAI,mBAAmB,IAAI,IAAI,mBAAmB;AAAA,MAC/F,OAAO,KAAK,uBAAuB,IAAI,YAAY,IAAI,IAAI,YAAY;AAAA,MACvE,UAAU,KAAK,uBAAuB,IAAI,eAAe,IAAI,IAAI,eAAe;AAAA,MAChF,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":[]}
@@ -65,6 +65,44 @@ function createScopedLogger(logger, runId, stepKey) {
65
65
  };
66
66
  }
67
67
 
68
+ // src/utils/safe-json.ts
69
+ var corruptionCount = 0;
70
+ function safeJsonParse(json, fallback, ctx) {
71
+ try {
72
+ return JSON.parse(json);
73
+ } catch (error) {
74
+ if (error instanceof SyntaxError) {
75
+ corruptionCount++;
76
+ ctx.logger?.warn(
77
+ `[${ctx.component}] Corrupted JSON in database row, using fallback`,
78
+ {
79
+ rowId: ctx.rowId,
80
+ column: ctx.column,
81
+ length: typeof json === "string" ? json.length : void 0,
82
+ type: typeof json,
83
+ error: error.message,
84
+ corruptionCount
85
+ }
86
+ );
87
+ return fallback;
88
+ }
89
+ throw error;
90
+ }
91
+ }
92
+ function safeParseField(value, fallback, ctx) {
93
+ if (typeof value === "string") {
94
+ return safeJsonParse(value, fallback, ctx);
95
+ }
96
+ return value;
97
+ }
98
+ function safeParseOptionalField(value, ctx) {
99
+ if (!value) return void 0;
100
+ if (typeof value === "string") {
101
+ return safeJsonParse(value, void 0, ctx);
102
+ }
103
+ return value;
104
+ }
105
+
68
106
  // src/utils/postgres-deps.ts
69
107
  var cached;
70
108
  var loadingPromise;
@@ -99,6 +137,14 @@ async function loadPostgresDeps() {
99
137
  })();
100
138
  return loadingPromise;
101
139
  }
140
+ function requirePostgresDeps() {
141
+ if (!cached) {
142
+ throw new Error(
143
+ "PostgreSQL dependencies have not been loaded yet. Call loadPostgresDeps() (or PostgresStorageAdapter.initialize()) first."
144
+ );
145
+ }
146
+ return cached;
147
+ }
102
148
 
103
149
  export {
104
150
  generateId,
@@ -106,6 +152,10 @@ export {
106
152
  SilentLogger,
107
153
  sanitizeErrorForStorage,
108
154
  createScopedLogger,
109
- loadPostgresDeps
155
+ safeJsonParse,
156
+ safeParseField,
157
+ safeParseOptionalField,
158
+ loadPostgresDeps,
159
+ requirePostgresDeps
110
160
  };
111
- //# sourceMappingURL=chunk-ML35PIHX.js.map
161
+ //# sourceMappingURL=chunk-JPXPB7U5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/id.ts","../src/utils/logger.ts","../src/utils/safe-json.ts","../src/utils/postgres-deps.ts"],"sourcesContent":["/**\n * ID generation utilities for the workflow engine.\n */\n\nimport { randomBytes } from 'node:crypto';\n\n/**\n * Generate a unique ID for database records.\n * Uses a ULID-like format: base36 timestamp + random suffix.\n * This provides:\n * - Rough time-ordering (useful for debugging)\n * - High collision resistance\n * - URL-safe characters\n *\n * @returns A unique identifier string (approx 16 characters)\n */\nexport function generateId(): string {\n const timestamp = Date.now().toString(36);\n const randomPart = randomBytes(6).toString('base64url');\n return `${timestamp}${randomPart}`;\n}\n","/**\n * Logger utilities for the workflow engine.\n */\n\nimport type { Logger, WorkflowError } from '../core/types';\n\n/** Log level for ConsoleLogger. Levels are ordered: debug < info < warn < error. */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Console-based logger implementation.\n * Uses console.log with prefixes for different levels.\n * Supports a configurable minimum log level (default: 'info').\n */\nexport class ConsoleLogger implements Logger {\n private prefix: string;\n private minLevel: number;\n\n constructor(prefix = '[workflow]', level: LogLevel = 'info') {\n this.prefix = prefix;\n this.minLevel = LOG_LEVEL_PRIORITY[level];\n }\n\n debug(message: string, ...args: unknown[]): void {\n if (this.minLevel <= LOG_LEVEL_PRIORITY.debug) {\n console.debug(`${this.prefix} [DEBUG]`, message, ...args);\n }\n }\n\n info(message: string, ...args: unknown[]): void {\n if (this.minLevel <= LOG_LEVEL_PRIORITY.info) {\n console.info(`${this.prefix} [INFO]`, message, ...args);\n }\n }\n\n warn(message: string, ...args: unknown[]): void {\n if (this.minLevel <= LOG_LEVEL_PRIORITY.warn) {\n console.warn(`${this.prefix} [WARN]`, message, ...args);\n }\n }\n\n error(message: string, ...args: unknown[]): void {\n if (this.minLevel <= LOG_LEVEL_PRIORITY.error) {\n console.error(`${this.prefix} [ERROR]`, message, ...args);\n }\n }\n}\n\n/**\n * Silent logger that does nothing.\n * Useful for testing or when logs are not wanted.\n */\nexport class SilentLogger implements Logger {\n debug(): void {}\n info(): void {}\n warn(): void {}\n error(): void {}\n}\n\n/**\n * Strip stack traces from a WorkflowError before persisting to storage.\n * Stack traces expose internal file paths and should be kept in logs only.\n */\nexport function sanitizeErrorForStorage(error: WorkflowError): WorkflowError {\n const { stack: _stack, ...rest } = error;\n return rest;\n}\n\n/**\n * Create a scoped logger that includes run/step context.\n */\nexport function createScopedLogger(\n logger: Logger,\n runId: string,\n stepKey?: string\n): Logger {\n const prefix = stepKey ? `[run:${runId}][step:${stepKey}]` : `[run:${runId}]`;\n\n return {\n debug: (message: string, ...args: unknown[]) =>\n logger.debug(`${prefix} ${message}`, ...args),\n info: (message: string, ...args: unknown[]) =>\n logger.info(`${prefix} ${message}`, ...args),\n warn: (message: string, ...args: unknown[]) =>\n logger.warn(`${prefix} ${message}`, ...args),\n error: (message: string, ...args: unknown[]) =>\n logger.error(`${prefix} ${message}`, ...args),\n };\n}\n","/**\n * Safe JSON-parse helpers shared by storage and scheduler adapters.\n *\n * When a stored JSON column is corrupted, we want to surface the problem\n * operationally — structured log + counter — instead of silently degrading.\n * The logger payload is restricted to safe metadata (component, row id,\n * column, payload length, payload type, parser error message). The raw\n * value is intentionally never logged because it may contain user-provided\n * run / step input.\n *\n * Audit: 2026-04-27 findings L3 (corruption visibility) and M2 (do not\n * bypass the configured `Logger` or leak the raw value via `console`).\n */\n\nimport type { Logger } from '../core/types';\n\nlet corruptionCount = 0;\n\n/** Cumulative number of `safeJsonParse` failures since process start. */\nexport function getJsonParseCorruptionCount(): number {\n return corruptionCount;\n}\n\n/** Reset the corruption counter. Tests only. */\nexport function resetJsonParseCorruptionCount(): void {\n corruptionCount = 0;\n}\n\nexport interface SafeJsonParseContext {\n /** Component label, e.g. `SQLiteStorageAdapter`. */\n component: string;\n /** Optional database row id, surfaced in the warning so operators can locate the bad row. */\n rowId?: string;\n /** Optional column name for additional context. */\n column?: string;\n /** Optional structured logger; without one, only the counter is bumped. */\n logger?: Logger;\n}\n\n/**\n * Storage-adapter-level slice of `SafeJsonParseContext` — the part each\n * adapter binds once (its component label + optional logger). Row mappers\n * extend this with `rowId` and `column` per field, so corruption logs always\n * identify the bad row.\n *\n * Shared between `src/storage/postgres-core.ts` and\n * `src/storage/sqlite-core.ts` so all storage adapters use one mapper-context\n * shape regardless of backend.\n */\nexport type MapperContext = Pick<SafeJsonParseContext, 'component' | 'logger'>;\n\n/**\n * Parse a JSON string; on `SyntaxError` return `fallback`, increment the\n * corruption counter, and emit a structured warn log via `ctx.logger` (if\n * supplied). Non-syntax errors propagate so unexpected runtime issues are\n * still loud.\n */\nexport function safeJsonParse<T = unknown>(\n json: string,\n fallback: T,\n ctx: SafeJsonParseContext\n): T | unknown {\n try {\n return JSON.parse(json);\n } catch (error) {\n if (error instanceof SyntaxError) {\n corruptionCount++;\n ctx.logger?.warn(\n `[${ctx.component}] Corrupted JSON in database row, using fallback`,\n {\n rowId: ctx.rowId,\n column: ctx.column,\n length: typeof json === 'string' ? json.length : undefined,\n type: typeof json,\n error: error.message,\n corruptionCount,\n }\n );\n return fallback;\n }\n throw error;\n }\n}\n\n/** Parse a column value if it's still a JSON string; pass through otherwise. */\nexport function safeParseField(\n value: unknown,\n fallback: unknown,\n ctx: SafeJsonParseContext\n): unknown {\n if (typeof value === 'string') {\n return safeJsonParse(value, fallback, ctx);\n }\n return value;\n}\n\n/** Same as `safeParseField` but returns `undefined` for falsy/missing values. */\nexport function safeParseOptionalField(\n value: unknown,\n ctx: SafeJsonParseContext\n): unknown {\n if (!value) return undefined;\n if (typeof value === 'string') {\n return safeJsonParse(value, undefined, ctx);\n }\n return value;\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\n/**\n * Synchronously access the cached PostgreSQL deps. Throws if\n * `loadPostgresDeps()` has not yet completed. Used by code paths that run\n * after `PostgresStorageAdapter.initialize()` and need direct access to\n * `sql` / `Kysely` without re-awaiting.\n */\nexport function requirePostgresDeps(): PostgresDeps {\n if (!cached) {\n throw new Error(\n 'PostgreSQL dependencies have not been loaded yet. ' +\n 'Call loadPostgresDeps() (or PostgresStorageAdapter.initialize()) first.'\n );\n }\n return cached;\n}\n"],"mappings":";AAIA,SAAS,mBAAmB;AAYrB,SAAS,aAAqB;AACnC,QAAM,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE;AACxC,QAAM,aAAa,YAAY,CAAC,EAAE,SAAS,WAAW;AACtD,SAAO,GAAG,SAAS,GAAG,UAAU;AAClC;;;ACXA,IAAM,qBAA+C;AAAA,EACnD,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAOO,IAAM,gBAAN,MAAsC;AAAA,EACnC;AAAA,EACA;AAAA,EAER,YAAY,SAAS,cAAc,QAAkB,QAAQ;AAC3D,SAAK,SAAS;AACd,SAAK,WAAW,mBAAmB,KAAK;AAAA,EAC1C;AAAA,EAEA,MAAM,YAAoB,MAAuB;AAC/C,QAAI,KAAK,YAAY,mBAAmB,OAAO;AAC7C,cAAQ,MAAM,GAAG,KAAK,MAAM,YAAY,SAAS,GAAG,IAAI;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,KAAK,YAAoB,MAAuB;AAC9C,QAAI,KAAK,YAAY,mBAAmB,MAAM;AAC5C,cAAQ,KAAK,GAAG,KAAK,MAAM,WAAW,SAAS,GAAG,IAAI;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,KAAK,YAAoB,MAAuB;AAC9C,QAAI,KAAK,YAAY,mBAAmB,MAAM;AAC5C,cAAQ,KAAK,GAAG,KAAK,MAAM,WAAW,SAAS,GAAG,IAAI;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,MAAM,YAAoB,MAAuB;AAC/C,QAAI,KAAK,YAAY,mBAAmB,OAAO;AAC7C,cAAQ,MAAM,GAAG,KAAK,MAAM,YAAY,SAAS,GAAG,IAAI;AAAA,IAC1D;AAAA,EACF;AACF;AAMO,IAAM,eAAN,MAAqC;AAAA,EAC1C,QAAc;AAAA,EAAC;AAAA,EACf,OAAa;AAAA,EAAC;AAAA,EACd,OAAa;AAAA,EAAC;AAAA,EACd,QAAc;AAAA,EAAC;AACjB;AAMO,SAAS,wBAAwB,OAAqC;AAC3E,QAAM,EAAE,OAAO,QAAQ,GAAG,KAAK,IAAI;AACnC,SAAO;AACT;AAKO,SAAS,mBACd,QACA,OACA,SACQ;AACR,QAAM,SAAS,UAAU,QAAQ,KAAK,UAAU,OAAO,MAAM,QAAQ,KAAK;AAE1E,SAAO;AAAA,IACL,OAAO,CAAC,YAAoB,SAC1B,OAAO,MAAM,GAAG,MAAM,IAAI,OAAO,IAAI,GAAG,IAAI;AAAA,IAC9C,MAAM,CAAC,YAAoB,SACzB,OAAO,KAAK,GAAG,MAAM,IAAI,OAAO,IAAI,GAAG,IAAI;AAAA,IAC7C,MAAM,CAAC,YAAoB,SACzB,OAAO,KAAK,GAAG,MAAM,IAAI,OAAO,IAAI,GAAG,IAAI;AAAA,IAC7C,OAAO,CAAC,YAAoB,SAC1B,OAAO,MAAM,GAAG,MAAM,IAAI,OAAO,IAAI,GAAG,IAAI;AAAA,EAChD;AACF;;;AC/EA,IAAI,kBAAkB;AAyCf,SAAS,cACd,MACA,UACA,KACa;AACb,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,SAAS,OAAO;AACd,QAAI,iBAAiB,aAAa;AAChC;AACA,UAAI,QAAQ;AAAA,QACV,IAAI,IAAI,SAAS;AAAA,QACjB;AAAA,UACE,OAAO,IAAI;AAAA,UACX,QAAQ,IAAI;AAAA,UACZ,QAAQ,OAAO,SAAS,WAAW,KAAK,SAAS;AAAA,UACjD,MAAM,OAAO;AAAA,UACb,OAAO,MAAM;AAAA,UACb;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAGO,SAAS,eACd,OACA,UACA,KACS;AACT,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,cAAc,OAAO,UAAU,GAAG;AAAA,EAC3C;AACA,SAAO;AACT;AAGO,SAAS,uBACd,OACA,KACS;AACT,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,cAAc,OAAO,QAAW,GAAG;AAAA,EAC5C;AACA,SAAO;AACT;;;ACvFA,IAAI;AACJ,IAAI;AAQJ,eAAsB,mBAA0C;AAC9D,MAAI,OAAQ,QAAO;AACnB,MAAI,eAAgB,QAAO;AAE3B,oBAAkB,YAAY;AAC5B,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI;AACF,YAAM,YAAY,MAAM,OAAO,QAAQ;AACvC,eAAS,UAAU;AACnB,wBAAkB,UAAU;AAC5B,YAAM,UAAU;AAAA,IAClB,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI;AACF,YAAM,KAAK,MAAM,OAAO,IAAI;AAC5B,iBAAW,GAAG,WAAW;AAAA,IAC3B,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,aAAS,EAAE,QAAQ,iBAAiB,KAAK,SAAS;AAClD,WAAO;AAAA,EACT,GAAG;AAEH,SAAO;AACT;AAQO,SAAS,sBAAoC;AAClD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}