@multiplier-labs/stepflow 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.codekin/reports/comments/2026-04-04_comment-assessment.md +179 -0
  2. package/.codekin/reports/complexity/2026-04-03_complexity-report.md +152 -0
  3. package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +94 -0
  4. package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +10 -0
  5. package/.codekin/reports/security/2026-04-03_security-audit.md +208 -0
  6. package/.github/CODEOWNERS +15 -0
  7. package/.github/dependabot.yml +23 -0
  8. package/.github/workflows/ci.yml +38 -0
  9. package/.github/workflows/publish.yml +11 -5
  10. package/.github/workflows/security.yml +29 -0
  11. package/CHANGELOG.md +34 -0
  12. package/README.md +24 -0
  13. package/SECURITY.md +40 -0
  14. package/dist/{chunk-FQCT3I2A.js → chunk-4VOKMGP6.js} +23 -42
  15. package/dist/chunk-4VOKMGP6.js.map +1 -0
  16. package/dist/{chunk-XCF4PLN7.js → chunk-GYPNJBDP.js} +12 -16
  17. package/dist/chunk-GYPNJBDP.js.map +1 -0
  18. package/dist/{chunk-U5QGN3RT.js → chunk-KHTUWLVZ.js} +14 -47
  19. package/dist/chunk-KHTUWLVZ.js.map +1 -0
  20. package/dist/{chunk-GXY66V7Q.js → chunk-ML35PIHX.js} +1 -1
  21. package/dist/chunk-ML35PIHX.js.map +1 -0
  22. package/dist/events/index.d.ts +4 -4
  23. package/dist/events/index.js +1 -1
  24. package/dist/{index-CPfGsmrp.d.ts → index-Dzr437iW.d.ts} +6 -6
  25. package/dist/index.d.ts +14 -12
  26. package/dist/index.js +40 -82
  27. package/dist/index.js.map +1 -1
  28. package/dist/scheduler/index.d.ts +4 -4
  29. package/dist/scheduler/index.js +2 -2
  30. package/dist/storage/index.d.ts +13 -13
  31. package/dist/storage/index.js +2 -2
  32. package/dist/{types-BlwFEGzj.d.ts → types-DSEjiLMj.d.ts} +16 -15
  33. package/dist/{types-DzkEqnQJ.d.ts → types-DZUdWmCc.d.ts} +2 -2
  34. package/dist/{types-BVShq7zT.d.ts → types-K5Gjk3H_.d.ts} +4 -4
  35. package/docs/stepflow-api-reference.md +2 -3
  36. package/package.json +7 -6
  37. package/src/core/engine.test.ts +466 -522
  38. package/src/core/engine.ts +42 -67
  39. package/src/core/index.ts +3 -3
  40. package/src/core/orchestrator.ts +64 -83
  41. package/src/core/types.ts +7 -29
  42. package/src/events/index.ts +4 -4
  43. package/src/events/memory.test.ts +39 -41
  44. package/src/events/memory.ts +7 -10
  45. package/src/events/socketio.test.ts +112 -162
  46. package/src/events/socketio.ts +20 -27
  47. package/src/events/types.ts +16 -19
  48. package/src/events/webhook.test.ts +197 -235
  49. package/src/events/webhook.ts +44 -71
  50. package/src/index.ts +19 -19
  51. package/src/planning/index.ts +6 -3
  52. package/src/planning/planner.ts +66 -91
  53. package/src/planning/planning.test.ts +303 -366
  54. package/src/planning/registry.test.ts +204 -251
  55. package/src/planning/registry.ts +39 -43
  56. package/src/planning/types.ts +23 -27
  57. package/src/scheduler/cron.test.ts +141 -150
  58. package/src/scheduler/cron.ts +32 -67
  59. package/src/scheduler/index.ts +4 -14
  60. package/src/scheduler/postgres-persistence.ts +71 -99
  61. package/src/scheduler/sqlite-persistence.test.ts +96 -96
  62. package/src/scheduler/sqlite-persistence.ts +12 -19
  63. package/src/scheduler/types.ts +4 -9
  64. package/src/storage/index.ts +5 -5
  65. package/src/storage/memory.test.ts +133 -183
  66. package/src/storage/memory.ts +21 -42
  67. package/src/storage/postgres.ts +197 -393
  68. package/src/storage/sqlite.test.ts +154 -281
  69. package/src/storage/sqlite.ts +50 -85
  70. package/src/storage/types.ts +33 -58
  71. package/src/utils/errors.test.ts +78 -80
  72. package/src/utils/errors.ts +27 -57
  73. package/src/utils/id.ts +3 -3
  74. package/src/utils/index.ts +4 -4
  75. package/src/utils/logger.test.ts +52 -58
  76. package/src/utils/logger.ts +4 -4
  77. package/src/utils/postgres-deps.ts +4 -4
  78. package/src/utils/retry.test.ts +47 -67
  79. package/src/utils/retry.ts +9 -9
  80. package/dist/chunk-FQCT3I2A.js.map +0 -1
  81. package/dist/chunk-GXY66V7Q.js.map +0 -1
  82. package/dist/chunk-U5QGN3RT.js.map +0 -1
  83. package/dist/chunk-XCF4PLN7.js.map +0 -1
  84. package/src/scheduler/postgres-persistence.test.ts +0 -906
@@ -4,13 +4,13 @@
4
4
  * Provides time-based and workflow-completion-based triggers for workflows.
5
5
  */
6
6
 
7
- import { CronExpressionParser } from "cron-parser";
8
- import type { WorkflowKind, RunStatus, Logger } from "../core/types";
9
- import type { WorkflowEngine } from "../core/engine";
10
- import type { Scheduler, WorkflowSchedule, TriggerType } from "./types";
11
- import type { WorkflowEvent } from "../events/types";
12
- import { generateId } from "../utils/id";
13
- import { ConsoleLogger } from "../utils/logger";
7
+ import { CronExpressionParser } from 'cron-parser';
8
+ import type { WorkflowKind, RunStatus, Logger } from '../core/types';
9
+ import type { WorkflowEngine } from '../core/engine';
10
+ import type { Scheduler, WorkflowSchedule, TriggerType } from './types';
11
+ import type { WorkflowEvent } from '../events/types';
12
+ import { generateId } from '../utils/id';
13
+ import { ConsoleLogger } from '../utils/logger';
14
14
 
15
15
  // ============================================================================
16
16
  // Configuration Types
@@ -45,10 +45,7 @@ export interface SchedulePersistence {
45
45
  saveSchedule(schedule: WorkflowSchedule): Promise<void>;
46
46
 
47
47
  /** Update a schedule */
48
- updateSchedule(
49
- scheduleId: string,
50
- updates: Partial<WorkflowSchedule>,
51
- ): Promise<void>;
48
+ updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void>;
52
49
 
53
50
  /** Delete a schedule */
54
51
  deleteSchedule(scheduleId: string): Promise<void>;
@@ -116,11 +113,11 @@ export class CronScheduler implements Scheduler {
116
113
  */
117
114
  async start(): Promise<void> {
118
115
  if (this.running) {
119
- this.logger.warn("Scheduler is already running");
116
+ this.logger.warn('Scheduler is already running');
120
117
  return;
121
118
  }
122
119
 
123
- this.logger.info("Starting scheduler...");
120
+ this.logger.info('Starting scheduler...');
124
121
 
125
122
  // Load schedules from persistence if available
126
123
  if (this.persistence) {
@@ -133,16 +130,13 @@ export class CronScheduler implements Scheduler {
133
130
 
134
131
  // Calculate next run times for cron schedules
135
132
  for (const schedule of this.schedules.values()) {
136
- if (schedule.triggerType === "cron" && schedule.cronExpression) {
133
+ if (schedule.triggerType === 'cron' && schedule.cronExpression) {
137
134
  this.updateNextRunTime(schedule);
138
135
  }
139
136
  }
140
137
 
141
138
  // Start polling for cron schedules
142
- this.pollTimer = setInterval(
143
- () => this.checkSchedules(),
144
- this.pollInterval,
145
- );
139
+ this.pollTimer = setInterval(() => this.checkSchedules(), this.pollInterval);
146
140
 
147
141
  // Subscribe to workflow completion events
148
142
  this.eventUnsubscribe = this.engine.subscribeToAll((event) => {
@@ -150,7 +144,7 @@ export class CronScheduler implements Scheduler {
150
144
  });
151
145
 
152
146
  this.running = true;
153
- this.logger.info("Scheduler started");
147
+ this.logger.info('Scheduler started');
154
148
  }
155
149
 
156
150
  /**
@@ -162,7 +156,7 @@ export class CronScheduler implements Scheduler {
162
156
  return;
163
157
  }
164
158
 
165
- this.logger.info("Stopping scheduler...");
159
+ this.logger.info('Stopping scheduler...');
166
160
 
167
161
  if (this.pollTimer) {
168
162
  clearInterval(this.pollTimer);
@@ -175,22 +169,20 @@ export class CronScheduler implements Scheduler {
175
169
  }
176
170
 
177
171
  this.running = false;
178
- this.logger.info("Scheduler stopped");
172
+ this.logger.info('Scheduler stopped');
179
173
  }
180
174
 
181
175
  /**
182
176
  * Add a new schedule.
183
177
  */
184
- async addSchedule(
185
- scheduleData: Omit<WorkflowSchedule, "id">,
186
- ): Promise<WorkflowSchedule> {
178
+ async addSchedule(scheduleData: Omit<WorkflowSchedule, 'id'>): Promise<WorkflowSchedule> {
187
179
  const schedule: WorkflowSchedule = {
188
180
  ...scheduleData,
189
181
  id: generateId(),
190
182
  };
191
183
 
192
184
  // Validate cron expression if provided
193
- if (schedule.triggerType === "cron" && schedule.cronExpression) {
185
+ if (schedule.triggerType === 'cron' && schedule.cronExpression) {
194
186
  try {
195
187
  CronExpressionParser.parse(schedule.cronExpression, {
196
188
  tz: schedule.timezone,
@@ -204,11 +196,9 @@ export class CronScheduler implements Scheduler {
204
196
  }
205
197
 
206
198
  // Validate workflow completion trigger
207
- if (schedule.triggerType === "workflow_completed") {
199
+ if (schedule.triggerType === 'workflow_completed') {
208
200
  if (!schedule.triggerOnWorkflowKind) {
209
- throw new Error(
210
- "triggerOnWorkflowKind is required for workflow_completed triggers",
211
- );
201
+ throw new Error('triggerOnWorkflowKind is required for workflow_completed triggers');
212
202
  }
213
203
  }
214
204
 
@@ -219,9 +209,7 @@ export class CronScheduler implements Scheduler {
219
209
  await this.persistence.saveSchedule(schedule);
220
210
  }
221
211
 
222
- this.logger.info(
223
- `Added schedule: ${schedule.id} (${schedule.workflowKind})`,
224
- );
212
+ this.logger.info(`Added schedule: ${schedule.id} (${schedule.workflowKind})`);
225
213
 
226
214
  return schedule;
227
215
  }
@@ -246,10 +234,7 @@ export class CronScheduler implements Scheduler {
246
234
  /**
247
235
  * Update a schedule.
248
236
  */
249
- async updateSchedule(
250
- scheduleId: string,
251
- updates: Partial<WorkflowSchedule>,
252
- ): Promise<void> {
237
+ async updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void> {
253
238
  const schedule = this.schedules.get(scheduleId);
254
239
  if (!schedule) {
255
240
  throw new Error(`Schedule not found: ${scheduleId}`);
@@ -262,10 +247,7 @@ export class CronScheduler implements Scheduler {
262
247
  Object.assign(schedule, updates);
263
248
 
264
249
  // Revalidate and update next run time if cron expression changed
265
- if (
266
- updates.cronExpression !== undefined ||
267
- updates.timezone !== undefined
268
- ) {
250
+ if (updates.cronExpression !== undefined || updates.timezone !== undefined) {
269
251
  if (schedule.cronExpression) {
270
252
  try {
271
253
  CronExpressionParser.parse(schedule.cronExpression, {
@@ -273,9 +255,7 @@ export class CronScheduler implements Scheduler {
273
255
  });
274
256
  this.updateNextRunTime(schedule);
275
257
  } catch (error) {
276
- throw new Error(
277
- `Invalid cron expression: ${schedule.cronExpression}`,
278
- );
258
+ throw new Error(`Invalid cron expression: ${schedule.cronExpression}`);
279
259
  }
280
260
  }
281
261
  }
@@ -325,16 +305,13 @@ export class CronScheduler implements Scheduler {
325
305
 
326
306
  for (const schedule of this.schedules.values()) {
327
307
  if (!schedule.enabled) continue;
328
- if (schedule.triggerType !== "cron") continue;
308
+ if (schedule.triggerType !== 'cron') continue;
329
309
  if (!schedule.nextRunAt) continue;
330
310
 
331
311
  if (schedule.nextRunAt <= now) {
332
312
  // Execute the schedule
333
313
  this.executeSchedule(schedule).catch((error) => {
334
- this.logger.error(
335
- `Failed to execute schedule ${schedule.id}:`,
336
- error,
337
- );
314
+ this.logger.error(`Failed to execute schedule ${schedule.id}:`, error);
338
315
  });
339
316
 
340
317
  // Update next run time
@@ -349,28 +326,21 @@ export class CronScheduler implements Scheduler {
349
326
  private handleWorkflowEvent(event: WorkflowEvent): void {
350
327
  // Only handle run completion events
351
328
  // Note: orchestrator emits 'run.completed' for success, 'run.failed' for failure
352
- if (
353
- event.eventType !== "run.completed" &&
354
- event.eventType !== "run.failed"
355
- ) {
329
+ if (event.eventType !== 'run.completed' && event.eventType !== 'run.failed') {
356
330
  return;
357
331
  }
358
332
 
359
- const completedStatus: RunStatus =
360
- event.eventType === "run.completed" ? "succeeded" : "failed";
333
+ const completedStatus: RunStatus = event.eventType === 'run.completed' ? 'succeeded' : 'failed';
361
334
  const completedKind = event.kind;
362
335
 
363
336
  // Find schedules triggered by this workflow completion
364
337
  for (const schedule of this.schedules.values()) {
365
338
  if (!schedule.enabled) continue;
366
- if (schedule.triggerType !== "workflow_completed") continue;
339
+ if (schedule.triggerType !== 'workflow_completed') continue;
367
340
  if (schedule.triggerOnWorkflowKind !== completedKind) continue;
368
341
 
369
342
  // Check if the status matches
370
- if (
371
- schedule.triggerOnStatus &&
372
- !schedule.triggerOnStatus.includes(completedStatus)
373
- ) {
343
+ if (schedule.triggerOnStatus && !schedule.triggerOnStatus.includes(completedStatus)) {
374
344
  continue;
375
345
  }
376
346
 
@@ -389,11 +359,9 @@ export class CronScheduler implements Scheduler {
389
359
  */
390
360
  private async executeSchedule(
391
361
  schedule: WorkflowSchedule,
392
- triggerContext?: { triggerRunId?: string; triggerStatus?: RunStatus },
362
+ triggerContext?: { triggerRunId?: string; triggerStatus?: RunStatus }
393
363
  ): Promise<string> {
394
- this.logger.info(
395
- `Executing schedule: ${schedule.id} (${schedule.workflowKind})`,
396
- );
364
+ this.logger.info(`Executing schedule: ${schedule.id} (${schedule.workflowKind})`);
397
365
 
398
366
  // Build metadata with trigger info
399
367
  const metadata = {
@@ -439,10 +407,7 @@ export class CronScheduler implements Scheduler {
439
407
 
440
408
  schedule.nextRunAt = interval.next().toDate();
441
409
  } catch (error) {
442
- this.logger.error(
443
- `Failed to parse cron expression for schedule ${schedule.id}:`,
444
- error,
445
- );
410
+ this.logger.error(`Failed to parse cron expression for schedule ${schedule.id}:`, error);
446
411
  schedule.nextRunAt = undefined;
447
412
  }
448
413
  }
@@ -2,17 +2,7 @@
2
2
  * Scheduler module exports.
3
3
  */
4
4
 
5
- export * from "./types";
6
- export {
7
- CronScheduler,
8
- type CronSchedulerConfig,
9
- type SchedulePersistence,
10
- } from "./cron";
11
- export {
12
- SQLiteSchedulePersistence,
13
- type SQLiteSchedulePersistenceConfig,
14
- } from "./sqlite-persistence";
15
- export {
16
- PostgresSchedulePersistence,
17
- type PostgresSchedulePersistenceConfig,
18
- } from "./postgres-persistence";
5
+ export * from './types';
6
+ export { CronScheduler, type CronSchedulerConfig, type SchedulePersistence } from './cron';
7
+ export { SQLiteSchedulePersistence, type SQLiteSchedulePersistenceConfig } from './sqlite-persistence';
8
+ export { PostgresSchedulePersistence, type PostgresSchedulePersistenceConfig } from './postgres-persistence';
@@ -4,17 +4,17 @@
4
4
  * Stores workflow schedules in a PostgreSQL database table using Kysely.
5
5
  */
6
6
 
7
- import type { Kysely as KyselyType } from "kysely";
8
- import type { Pool, PoolConfig } from "pg";
9
- import { loadPostgresDeps } from "../utils/postgres-deps.js";
7
+ import type { Kysely as KyselyType } from 'kysely';
8
+ import type { Pool, PoolConfig } from 'pg';
9
+ import { loadPostgresDeps } from '../utils/postgres-deps.js';
10
10
 
11
11
  // Lazy-loaded dependencies - populated by loadPostgresDeps() during initialize()
12
12
  let Kysely: any;
13
13
  let PostgresDialect: any;
14
14
  let sql: any;
15
15
  let pgModule: any;
16
- import type { WorkflowSchedule } from "./types.js";
17
- import type { SchedulePersistence } from "./cron.js";
16
+ import type { WorkflowSchedule } from './types.js';
17
+ import type { SchedulePersistence } from './cron.js';
18
18
 
19
19
  // ============================================================================
20
20
  // Database Types (Kysely schema)
@@ -143,18 +143,18 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
143
143
  private config: PostgresSchedulePersistenceConfig;
144
144
 
145
145
  constructor(config: PostgresSchedulePersistenceConfig) {
146
- this.schema = config.schema ?? "public";
146
+ this.schema = config.schema ?? 'public';
147
147
  if (!/^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/.test(this.schema)) {
148
148
  throw new Error(
149
149
  `Invalid schema name "${this.schema}". Schema must start with a letter or underscore, ` +
150
- `contain only alphanumeric characters and underscores, and be at most 63 characters.`,
150
+ `contain only alphanumeric characters and underscores, and be at most 63 characters.`
151
151
  );
152
152
  }
153
- this.tableName = config.tableName ?? "workflow_schedules";
153
+ this.tableName = config.tableName ?? 'workflow_schedules';
154
154
  if (!/^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/.test(this.tableName)) {
155
155
  throw new Error(
156
156
  `Invalid table name "${this.tableName}". Table name must start with a letter or underscore, ` +
157
- `contain only alphanumeric characters and underscores, and be at most 63 characters.`,
157
+ `contain only alphanumeric characters and underscores, and be at most 63 characters.`
158
158
  );
159
159
  }
160
160
  this.autoMigrate = config.autoMigrate !== false;
@@ -172,7 +172,7 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
172
172
  private ensureInitialized(): void {
173
173
  if (!this.initialized) {
174
174
  throw new Error(
175
- "PostgresSchedulePersistence is not initialized. Call initialize() before using the adapter.",
175
+ 'PostgresSchedulePersistence is not initialized. Call initialize() before using the adapter.'
176
176
  );
177
177
  }
178
178
  }
@@ -196,16 +196,14 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
196
196
  this.pool = this.config.pool;
197
197
  this.ownsPool = false;
198
198
  } else if (this.config.connectionString) {
199
- this.pool = new pgModule.Pool({
200
- connectionString: this.config.connectionString,
201
- });
199
+ this.pool = new pgModule.Pool({ connectionString: this.config.connectionString });
202
200
  this.ownsPool = true;
203
201
  } else if (this.config.poolConfig) {
204
202
  this.pool = new pgModule.Pool(this.config.poolConfig);
205
203
  this.ownsPool = true;
206
204
  } else {
207
205
  throw new Error(
208
- "PostgresSchedulePersistenceConfig must include either pool, connectionString, or poolConfig",
206
+ 'PostgresSchedulePersistenceConfig must include either pool, connectionString, or poolConfig'
209
207
  );
210
208
  }
211
209
 
@@ -238,16 +236,13 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
238
236
  // ============================================================================
239
237
 
240
238
  private async createTables(): Promise<void> {
241
- const fullTableName =
242
- this.schema === "public"
243
- ? this.tableName
244
- : `${this.schema}.${this.tableName}`;
239
+ const fullTableName = this.schema === 'public'
240
+ ? this.tableName
241
+ : `${this.schema}.${this.tableName}`;
245
242
 
246
243
  // Create schema if not public
247
- if (this.schema !== "public") {
248
- await sql`CREATE SCHEMA IF NOT EXISTS ${sql.ref(this.schema)}`.execute(
249
- this.db,
250
- );
244
+ if (this.schema !== 'public') {
245
+ await sql`CREATE SCHEMA IF NOT EXISTS ${sql.ref(this.schema)}`.execute(this.db);
251
246
  }
252
247
 
253
248
  // Create schedules table.
@@ -316,12 +311,12 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
316
311
 
317
312
  async loadSchedules(): Promise<WorkflowSchedule[]> {
318
313
  this.ensureInitialized();
319
- const rows = (await this.qb
314
+ const rows = await this.qb
320
315
  .selectFrom(this.tableName as any)
321
316
  .selectAll()
322
- .execute()) as WorkflowSchedulesTable[];
317
+ .execute() as WorkflowSchedulesTable[];
323
318
 
324
- return rows.map((row) => this.rowToSchedule(row));
319
+ return rows.map(row => this.rowToSchedule(row));
325
320
  }
326
321
 
327
322
  async saveSchedule(schedule: WorkflowSchedule): Promise<void> {
@@ -335,13 +330,9 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
335
330
  cron_expression: schedule.cronExpression ?? null,
336
331
  timezone: schedule.timezone ?? null,
337
332
  trigger_on_workflow_kind: schedule.triggerOnWorkflowKind ?? null,
338
- trigger_on_status: schedule.triggerOnStatus
339
- ? JSON.stringify(schedule.triggerOnStatus)
340
- : null,
333
+ trigger_on_status: schedule.triggerOnStatus ? JSON.stringify(schedule.triggerOnStatus) : null,
341
334
  input_json: schedule.input ? JSON.stringify(schedule.input) : null,
342
- metadata_json: schedule.metadata
343
- ? JSON.stringify(schedule.metadata)
344
- : null,
335
+ metadata_json: schedule.metadata ? JSON.stringify(schedule.metadata) : null,
345
336
  enabled: schedule.enabled,
346
337
  last_run_at: schedule.lastRunAt ?? null,
347
338
  last_run_id: schedule.lastRunId ?? null,
@@ -352,20 +343,17 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
352
343
  .execute();
353
344
  }
354
345
 
355
- async updateSchedule(
356
- scheduleId: string,
357
- updates: Partial<WorkflowSchedule>,
358
- ): Promise<void> {
346
+ async updateSchedule(scheduleId: string, updates: Partial<WorkflowSchedule>): Promise<void> {
359
347
  this.ensureInitialized();
360
348
  // Partial update pattern: fetch the existing row, merge with incoming updates,
361
349
  // then build a column-level update object. Each field is only included if it was
362
350
  // explicitly provided in `updates` (or affected by the merge), so unchanged
363
351
  // columns are left untouched in the database.
364
- const existing = (await this.qb
352
+ const existing = await this.qb
365
353
  .selectFrom(this.tableName as any)
366
354
  .selectAll()
367
- .where("id", "=", scheduleId)
368
- .executeTakeFirst()) as WorkflowSchedulesTable | undefined;
355
+ .where('id', '=', scheduleId)
356
+ .executeTakeFirst() as WorkflowSchedulesTable | undefined;
369
357
 
370
358
  if (!existing) {
371
359
  throw new Error(`Schedule not found: ${scheduleId}`);
@@ -383,30 +371,18 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
383
371
  dbKey: keyof WorkflowSchedulesTable;
384
372
  serialize?: (value: any) => any;
385
373
  }> = [
386
- { domainKey: "workflowKind", dbKey: "workflow_kind" },
387
- { domainKey: "triggerType", dbKey: "trigger_type" },
388
- { domainKey: "cronExpression", dbKey: "cron_expression" },
389
- { domainKey: "timezone", dbKey: "timezone" },
390
- { domainKey: "triggerOnWorkflowKind", dbKey: "trigger_on_workflow_kind" },
391
- {
392
- domainKey: "triggerOnStatus",
393
- dbKey: "trigger_on_status",
394
- serialize: (v) => (v ? JSON.stringify(v) : null),
395
- },
396
- {
397
- domainKey: "input",
398
- dbKey: "input_json",
399
- serialize: (v) => (v ? JSON.stringify(v) : null),
400
- },
401
- {
402
- domainKey: "metadata",
403
- dbKey: "metadata_json",
404
- serialize: (v) => (v ? JSON.stringify(v) : null),
405
- },
406
- { domainKey: "enabled", dbKey: "enabled" },
407
- { domainKey: "lastRunAt", dbKey: "last_run_at" },
408
- { domainKey: "lastRunId", dbKey: "last_run_id" },
409
- { domainKey: "nextRunAt", dbKey: "next_run_at" },
374
+ { domainKey: 'workflowKind', dbKey: 'workflow_kind' },
375
+ { domainKey: 'triggerType', dbKey: 'trigger_type' },
376
+ { domainKey: 'cronExpression', dbKey: 'cron_expression' },
377
+ { domainKey: 'timezone', dbKey: 'timezone' },
378
+ { domainKey: 'triggerOnWorkflowKind', dbKey: 'trigger_on_workflow_kind' },
379
+ { domainKey: 'triggerOnStatus', dbKey: 'trigger_on_status', serialize: v => v ? JSON.stringify(v) : null },
380
+ { domainKey: 'input', dbKey: 'input_json', serialize: v => v ? JSON.stringify(v) : null },
381
+ { domainKey: 'metadata', dbKey: 'metadata_json', serialize: v => v ? JSON.stringify(v) : null },
382
+ { domainKey: 'enabled', dbKey: 'enabled' },
383
+ { domainKey: 'lastRunAt', dbKey: 'last_run_at' },
384
+ { domainKey: 'lastRunId', dbKey: 'last_run_id' },
385
+ { domainKey: 'nextRunAt', dbKey: 'next_run_at' },
410
386
  ];
411
387
 
412
388
  const updateData: Partial<WorkflowSchedulesTable> = {
@@ -425,7 +401,7 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
425
401
  await this.qb
426
402
  .updateTable(this.tableName as any)
427
403
  .set(updateData)
428
- .where("id", "=", scheduleId)
404
+ .where('id', '=', scheduleId)
429
405
  .execute();
430
406
  }
431
407
 
@@ -433,7 +409,7 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
433
409
  this.ensureInitialized();
434
410
  await this.qb
435
411
  .deleteFrom(this.tableName as any)
436
- .where("id", "=", scheduleId)
412
+ .where('id', '=', scheduleId)
437
413
  .execute();
438
414
  }
439
415
 
@@ -446,11 +422,11 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
446
422
  */
447
423
  async getSchedule(scheduleId: string): Promise<WorkflowSchedule | null> {
448
424
  this.ensureInitialized();
449
- const row = (await this.qb
425
+ const row = await this.qb
450
426
  .selectFrom(this.tableName as any)
451
427
  .selectAll()
452
- .where("id", "=", scheduleId)
453
- .executeTakeFirst()) as WorkflowSchedulesTable | undefined;
428
+ .where('id', '=', scheduleId)
429
+ .executeTakeFirst() as WorkflowSchedulesTable | undefined;
454
430
 
455
431
  return row ? this.rowToSchedule(row) : null;
456
432
  }
@@ -462,49 +438,45 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
462
438
  this.ensureInitialized();
463
439
  const now = new Date();
464
440
 
465
- const rows = (await this.qb
441
+ const rows = await this.qb
466
442
  .selectFrom(this.tableName as any)
467
443
  .selectAll()
468
- .where("enabled", "=", true)
469
- .where("trigger_type", "=", "cron")
470
- .where("next_run_at", "<=", now)
471
- .execute()) as WorkflowSchedulesTable[];
444
+ .where('enabled', '=', true)
445
+ .where('trigger_type', '=', 'cron')
446
+ .where('next_run_at', '<=', now)
447
+ .execute() as WorkflowSchedulesTable[];
472
448
 
473
- return rows.map((row) => this.rowToSchedule(row));
449
+ return rows.map(row => this.rowToSchedule(row));
474
450
  }
475
451
 
476
452
  /**
477
453
  * Get schedules by workflow kind.
478
454
  */
479
- async getSchedulesByWorkflowKind(
480
- workflowKind: string,
481
- ): Promise<WorkflowSchedule[]> {
455
+ async getSchedulesByWorkflowKind(workflowKind: string): Promise<WorkflowSchedule[]> {
482
456
  this.ensureInitialized();
483
- const rows = (await this.qb
457
+ const rows = await this.qb
484
458
  .selectFrom(this.tableName as any)
485
459
  .selectAll()
486
- .where("workflow_kind", "=", workflowKind)
487
- .execute()) as WorkflowSchedulesTable[];
460
+ .where('workflow_kind', '=', workflowKind)
461
+ .execute() as WorkflowSchedulesTable[];
488
462
 
489
- return rows.map((row) => this.rowToSchedule(row));
463
+ return rows.map(row => this.rowToSchedule(row));
490
464
  }
491
465
 
492
466
  /**
493
467
  * Get workflow completion triggers for a specific workflow kind.
494
468
  */
495
- async getCompletionTriggers(
496
- triggerOnWorkflowKind: string,
497
- ): Promise<WorkflowSchedule[]> {
469
+ async getCompletionTriggers(triggerOnWorkflowKind: string): Promise<WorkflowSchedule[]> {
498
470
  this.ensureInitialized();
499
- const rows = (await this.qb
471
+ const rows = await this.qb
500
472
  .selectFrom(this.tableName as any)
501
473
  .selectAll()
502
- .where("enabled", "=", true)
503
- .where("trigger_type", "=", "workflow_completed")
504
- .where("trigger_on_workflow_kind", "=", triggerOnWorkflowKind)
505
- .execute()) as WorkflowSchedulesTable[];
474
+ .where('enabled', '=', true)
475
+ .where('trigger_type', '=', 'workflow_completed')
476
+ .where('trigger_on_workflow_kind', '=', triggerOnWorkflowKind)
477
+ .execute() as WorkflowSchedulesTable[];
506
478
 
507
- return rows.map((row) => this.rowToSchedule(row));
479
+ return rows.map(row => this.rowToSchedule(row));
508
480
  }
509
481
 
510
482
  // ============================================================================
@@ -515,24 +487,24 @@ export class PostgresSchedulePersistence implements SchedulePersistence {
515
487
  return {
516
488
  id: row.id,
517
489
  workflowKind: row.workflow_kind,
518
- triggerType: row.trigger_type as WorkflowSchedule["triggerType"],
490
+ triggerType: row.trigger_type as WorkflowSchedule['triggerType'],
519
491
  cronExpression: row.cron_expression ?? undefined,
520
492
  timezone: row.timezone ?? undefined,
521
493
  triggerOnWorkflowKind: row.trigger_on_workflow_kind ?? undefined,
522
494
  triggerOnStatus: row.trigger_on_status
523
- ? typeof row.trigger_on_status === "string"
524
- ? JSON.parse(row.trigger_on_status)
525
- : row.trigger_on_status
495
+ ? (typeof row.trigger_on_status === 'string'
496
+ ? JSON.parse(row.trigger_on_status)
497
+ : row.trigger_on_status)
526
498
  : undefined,
527
499
  input: row.input_json
528
- ? typeof row.input_json === "string"
529
- ? JSON.parse(row.input_json)
530
- : row.input_json
500
+ ? (typeof row.input_json === 'string'
501
+ ? JSON.parse(row.input_json)
502
+ : row.input_json)
531
503
  : undefined,
532
504
  metadata: row.metadata_json
533
- ? typeof row.metadata_json === "string"
534
- ? JSON.parse(row.metadata_json)
535
- : row.metadata_json
505
+ ? (typeof row.metadata_json === 'string'
506
+ ? JSON.parse(row.metadata_json)
507
+ : row.metadata_json)
536
508
  : undefined,
537
509
  enabled: row.enabled,
538
510
  lastRunAt: row.last_run_at ? new Date(row.last_run_at) : undefined,