@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
@@ -5,16 +5,16 @@
5
5
  * with support for distributed deployments and connection pooling.
6
6
  */
7
7
 
8
- import type { Kysely as KyselyType } from "kysely";
9
- import type { Pool, PoolConfig } from "pg";
10
- import { loadPostgresDeps } from "../utils/postgres-deps.js";
8
+ import type { Kysely as KyselyType } from 'kysely';
9
+ import type { Pool, PoolConfig } from 'pg';
10
+ import { loadPostgresDeps } from '../utils/postgres-deps.js';
11
11
 
12
12
  // Lazy-loaded dependencies - populated by loadPostgresDeps() during initialize()
13
13
  let Kysely: any;
14
14
  let PostgresDialect: any;
15
15
  let sql: any;
16
16
  let pgModule: any;
17
- import { generateId } from "../utils/id.js";
17
+ import { generateId } from '../utils/id.js';
18
18
  import type {
19
19
  StorageAdapter,
20
20
  WorkflowRunRecord,
@@ -29,16 +29,14 @@ import type {
29
29
  ExtendedWorkflowRunRecord,
30
30
  ExtendedRunStatus,
31
31
  ExtendedStepStatus,
32
- } from "./types.js";
33
- import type { RunStatus, StepStatus, WorkflowError } from "../core/types.js";
34
- import { sanitizeErrorForStorage } from "../utils/logger.js";
32
+ } from './types.js';
33
+ import type { RunStatus, StepStatus, WorkflowError } from '../core/types.js';
34
+ import { sanitizeErrorForStorage } from '../utils/logger.js';
35
35
 
36
36
  /** Strip `stack` from a generic error record before persistence. */
37
- function stripStack<T extends Record<string, unknown>>(
38
- error: T,
39
- ): Omit<T, "stack"> {
37
+ function stripStack<T extends Record<string, unknown>>(error: T): Omit<T, 'stack'> {
40
38
  const { stack: _stack, ...rest } = error;
41
- return rest as Omit<T, "stack">;
39
+ return rest as Omit<T, 'stack'>;
42
40
  }
43
41
 
44
42
  // ============================================================================
@@ -226,11 +224,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
226
224
  private config: PostgresStorageConfig;
227
225
 
228
226
  constructor(config: PostgresStorageConfig) {
229
- this.schema = config.schema ?? "public";
227
+ this.schema = config.schema ?? 'public';
230
228
  if (!/^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/.test(this.schema)) {
231
229
  throw new Error(
232
230
  `Invalid schema name "${this.schema}". Schema must start with a letter or underscore, ` +
233
- `contain only alphanumeric characters and underscores, and be at most 63 characters.`,
231
+ `contain only alphanumeric characters and underscores, and be at most 63 characters.`
234
232
  );
235
233
  }
236
234
  this.autoMigrate = config.autoMigrate !== false;
@@ -248,7 +246,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
248
246
  private ensureInitialized(): void {
249
247
  if (!this.initialized) {
250
248
  throw new Error(
251
- "PostgresStorageAdapter is not initialized. Call initialize() before using the adapter.",
249
+ 'PostgresStorageAdapter is not initialized. Call initialize() before using the adapter.'
252
250
  );
253
251
  }
254
252
  }
@@ -272,16 +270,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
272
270
  this.pool = this.config.pool;
273
271
  this.ownsPool = false;
274
272
  } else if (this.config.connectionString) {
275
- this.pool = new pgModule.Pool({
276
- connectionString: this.config.connectionString,
277
- });
273
+ this.pool = new pgModule.Pool({ connectionString: this.config.connectionString });
278
274
  this.ownsPool = true;
279
275
  } else if (this.config.poolConfig) {
280
276
  this.pool = new pgModule.Pool(this.config.poolConfig);
281
277
  this.ownsPool = true;
282
278
  } else {
283
279
  throw new Error(
284
- "PostgresStorageConfig must include either pool, connectionString, or poolConfig",
280
+ 'PostgresStorageConfig must include either pool, connectionString, or poolConfig'
285
281
  );
286
282
  }
287
283
 
@@ -317,10 +313,8 @@ export class PostgresStorageAdapter implements StorageAdapter {
317
313
  */
318
314
  private async createTables(): Promise<void> {
319
315
  // Create schema if not public
320
- if (this.schema !== "public") {
321
- await sql`CREATE SCHEMA IF NOT EXISTS ${sql.ref(this.schema)}`.execute(
322
- this.db,
323
- );
316
+ if (this.schema !== 'public') {
317
+ await sql`CREATE SCHEMA IF NOT EXISTS ${sql.ref(this.schema)}`.execute(this.db);
324
318
  }
325
319
 
326
320
  // Create runs table with new columns
@@ -352,11 +346,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
352
346
  ADD COLUMN IF NOT EXISTS output_json JSONB,
353
347
  ADD COLUMN IF NOT EXISTS priority INTEGER NOT NULL DEFAULT 0,
354
348
  ADD COLUMN IF NOT EXISTS timeout_ms INTEGER
355
- `
356
- .execute(this.db)
357
- .catch(() => {
358
- // Ignore if columns already exist or syntax not supported
359
- });
349
+ `.execute(this.db).catch(() => {
350
+ // Ignore if columns already exist or syntax not supported
351
+ });
360
352
 
361
353
  // Create indexes for runs
362
354
  await sql`
@@ -480,30 +472,25 @@ export class PostgresStorageAdapter implements StorageAdapter {
480
472
  * Create a new workflow run.
481
473
  * Supports both legacy and new CreateRunInput interfaces.
482
474
  */
483
- async createRun(
484
- run: CreateRunInput | Omit<WorkflowRunRecord, "id" | "createdAt">,
485
- ): Promise<WorkflowRunRecord> {
475
+ async createRun(run: CreateRunInput | Omit<WorkflowRunRecord, 'id' | 'createdAt'>): Promise<WorkflowRunRecord> {
486
476
  this.ensureInitialized();
487
- const id = "id" in run && run.id ? run.id : generateId();
477
+ const id = 'id' in run && run.id ? run.id : generateId();
488
478
  const createdAt = new Date();
489
479
 
490
480
  await this.qb
491
- .insertInto("runs")
481
+ .insertInto('runs')
492
482
  .values({
493
483
  id,
494
484
  kind: run.kind,
495
485
  status: run.status,
496
- parent_run_id: "parentRunId" in run ? (run.parentRunId ?? null) : null,
486
+ parent_run_id: 'parentRunId' in run ? (run.parentRunId ?? null) : null,
497
487
  input_json: JSON.stringify(run.input),
498
488
  metadata_json: JSON.stringify(run.metadata ?? {}),
499
489
  context_json: JSON.stringify(run.context ?? {}), // Default to empty object
500
490
  output_json: null,
501
- error_json:
502
- "error" in run && run.error
503
- ? JSON.stringify(sanitizeErrorForStorage(run.error))
504
- : null,
505
- priority: "priority" in run ? (run.priority ?? 0) : 0,
506
- timeout_ms: "timeoutMs" in run ? (run.timeoutMs ?? null) : null,
491
+ error_json: 'error' in run && run.error ? JSON.stringify(sanitizeErrorForStorage(run.error)) : null,
492
+ priority: 'priority' in run ? (run.priority ?? 0) : 0,
493
+ timeout_ms: 'timeoutMs' in run ? (run.timeoutMs ?? null) : null,
507
494
  created_at: createdAt,
508
495
  started_at: null,
509
496
  finished_at: null,
@@ -514,14 +501,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
514
501
  id,
515
502
  kind: run.kind,
516
503
  status: run.status as RunStatus,
517
- parentRunId: "parentRunId" in run ? run.parentRunId : undefined,
504
+ parentRunId: 'parentRunId' in run ? run.parentRunId : undefined,
518
505
  input: run.input,
519
506
  context: run.context ?? {},
520
507
  output: undefined,
521
508
  error: undefined,
522
509
  metadata: run.metadata ?? {},
523
- priority: "priority" in run ? (run.priority ?? 0) : 0,
524
- timeoutMs: "timeoutMs" in run ? run.timeoutMs : undefined,
510
+ priority: 'priority' in run ? (run.priority ?? 0) : 0,
511
+ timeoutMs: 'timeoutMs' in run ? run.timeoutMs : undefined,
525
512
  createdAt,
526
513
  };
527
514
  }
@@ -529,9 +516,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
529
516
  async getRun(runId: string): Promise<WorkflowRunRecord | null> {
530
517
  this.ensureInitialized();
531
518
  const row = await this.qb
532
- .selectFrom("runs")
519
+ .selectFrom('runs')
533
520
  .selectAll()
534
- .where("id", "=", runId)
521
+ .where('id', '=', runId)
535
522
  .executeTakeFirst();
536
523
 
537
524
  return row ? this.mapRunRow(row) : null;
@@ -541,10 +528,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
541
528
  * Update a workflow run.
542
529
  * Supports both legacy Partial<WorkflowRunRecord> and new UpdateRunInput interfaces.
543
530
  */
544
- async updateRun(
545
- runId: string,
546
- updates: UpdateRunInput | Partial<WorkflowRunRecord>,
547
- ): Promise<void> {
531
+ async updateRun(runId: string, updates: UpdateRunInput | Partial<WorkflowRunRecord>): Promise<void> {
548
532
  this.ensureInitialized();
549
533
  const updateData: Partial<WorkflowRunsTable> = {};
550
534
 
@@ -554,13 +538,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
554
538
  if (updates.context !== undefined) {
555
539
  updateData.context_json = JSON.stringify(updates.context);
556
540
  }
557
- if ("output" in updates && updates.output !== undefined) {
541
+ if ('output' in updates && updates.output !== undefined) {
558
542
  updateData.output_json = JSON.stringify(updates.output);
559
543
  }
560
544
  if (updates.error !== undefined) {
561
- updateData.error_json = JSON.stringify(
562
- sanitizeErrorForStorage(updates.error),
563
- );
545
+ updateData.error_json = JSON.stringify(sanitizeErrorForStorage(updates.error));
564
546
  }
565
547
  if (updates.startedAt !== undefined) {
566
548
  updateData.started_at = updates.startedAt;
@@ -571,9 +553,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
571
553
 
572
554
  if (Object.keys(updateData).length > 0) {
573
555
  await this.qb
574
- .updateTable("runs")
556
+ .updateTable('runs')
575
557
  .set(updateData)
576
- .where("id", "=", runId)
558
+ .where('id', '=', runId)
577
559
  .execute();
578
560
  }
579
561
  }
@@ -587,54 +569,44 @@ export class PostgresStorageAdapter implements StorageAdapter {
587
569
  */
588
570
  private applyRunsFilters<T extends { where(col: any, op: any, val: any): T }>(
589
571
  query: T,
590
- options: ListRunsOptions,
572
+ options: ListRunsOptions
591
573
  ): T {
592
574
  if (options.kind) {
593
- query = query.where("kind", "=", options.kind);
575
+ query = query.where('kind', '=', options.kind);
594
576
  }
595
577
  if (options.status) {
596
- const statuses = Array.isArray(options.status)
597
- ? options.status
598
- : [options.status];
599
- query = query.where("status", "in", statuses);
578
+ const statuses = Array.isArray(options.status) ? options.status : [options.status];
579
+ query = query.where('status', 'in', statuses);
600
580
  }
601
581
  if (options.parentRunId !== undefined) {
602
- query = query.where("parent_run_id", "=", options.parentRunId);
582
+ query = query.where('parent_run_id', '=', options.parentRunId);
603
583
  }
604
584
  return query;
605
585
  }
606
586
 
607
- async listRuns(
608
- options: ListRunsOptions = {},
609
- ): Promise<PaginatedResult<WorkflowRunRecord>> {
587
+ async listRuns(options: ListRunsOptions = {}): Promise<PaginatedResult<WorkflowRunRecord>> {
610
588
  this.ensureInitialized();
611
589
  const limit = options.limit ?? 50;
612
590
  const offset = options.offset ?? 0;
613
591
 
614
592
  let query = this.applyRunsFilters(
615
- this.qb.selectFrom("runs").selectAll(),
616
- options,
593
+ this.qb.selectFrom('runs').selectAll(),
594
+ options
617
595
  );
618
596
 
619
597
  const countQuery = this.applyRunsFilters(
620
- this.qb.selectFrom("runs").select(sql<number>`count(*)`.as("count")),
621
- options,
598
+ this.qb.selectFrom('runs').select(sql<number>`count(*)`.as('count')),
599
+ options
622
600
  );
623
601
 
624
- const countResult = (await countQuery.executeTakeFirst()) as
625
- | { count?: string | number }
626
- | undefined;
602
+ const countResult = await countQuery.executeTakeFirst() as { count?: string | number } | undefined;
627
603
  const total = Number(countResult?.count ?? 0);
628
604
 
629
605
  // Order
630
- const orderBy = options.orderBy ?? "createdAt";
631
- const orderDirection = options.orderDirection ?? "desc";
632
- const orderColumn =
633
- orderBy === "createdAt"
634
- ? "created_at"
635
- : orderBy === "startedAt"
636
- ? "started_at"
637
- : "finished_at";
606
+ const orderBy = options.orderBy ?? 'createdAt';
607
+ const orderDirection = options.orderDirection ?? 'desc';
608
+ const orderColumn = orderBy === 'createdAt' ? 'created_at' :
609
+ orderBy === 'startedAt' ? 'started_at' : 'finished_at';
638
610
 
639
611
  query = query.orderBy(orderColumn, orderDirection);
640
612
 
@@ -644,7 +616,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
644
616
  const rows = await query.execute();
645
617
 
646
618
  return {
647
- items: rows.map((row) => this.mapRunRow(row)),
619
+ items: rows.map(row => this.mapRunRow(row)),
648
620
  total,
649
621
  };
650
622
  }
@@ -653,14 +625,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
653
625
  // Step Operations
654
626
  // ============================================================================
655
627
 
656
- async createStep(
657
- step: Omit<WorkflowRunStepRecord, "id">,
658
- ): Promise<WorkflowRunStepRecord> {
628
+ async createStep(step: Omit<WorkflowRunStepRecord, 'id'>): Promise<WorkflowRunStepRecord> {
659
629
  this.ensureInitialized();
660
630
  const id = generateId();
661
631
 
662
632
  await this.qb
663
- .insertInto("workflow_run_steps")
633
+ .insertInto('workflow_run_steps')
664
634
  .values({
665
635
  id,
666
636
  run_id: step.runId,
@@ -668,11 +638,8 @@ export class PostgresStorageAdapter implements StorageAdapter {
668
638
  step_name: step.stepName,
669
639
  status: step.status,
670
640
  attempt: step.attempt,
671
- result_json:
672
- step.result !== undefined ? JSON.stringify(step.result) : null,
673
- error_json: step.error
674
- ? JSON.stringify(sanitizeErrorForStorage(step.error))
675
- : null,
641
+ result_json: step.result !== undefined ? JSON.stringify(step.result) : null,
642
+ error_json: step.error ? JSON.stringify(sanitizeErrorForStorage(step.error)) : null,
676
643
  started_at: step.startedAt ?? null,
677
644
  finished_at: step.finishedAt ?? null,
678
645
  })
@@ -684,18 +651,15 @@ export class PostgresStorageAdapter implements StorageAdapter {
684
651
  async getStep(stepId: string): Promise<WorkflowRunStepRecord | null> {
685
652
  this.ensureInitialized();
686
653
  const row = await this.qb
687
- .selectFrom("workflow_run_steps")
654
+ .selectFrom('workflow_run_steps')
688
655
  .selectAll()
689
- .where("id", "=", stepId)
656
+ .where('id', '=', stepId)
690
657
  .executeTakeFirst();
691
658
 
692
659
  return row ? this.mapStepRow(row) : null;
693
660
  }
694
661
 
695
- async updateStep(
696
- stepId: string,
697
- updates: Partial<WorkflowRunStepRecord>,
698
- ): Promise<void> {
662
+ async updateStep(stepId: string, updates: Partial<WorkflowRunStepRecord>): Promise<void> {
699
663
  this.ensureInitialized();
700
664
  const updateData: Partial<WorkflowRunStepsTable> = {};
701
665
 
@@ -709,9 +673,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
709
673
  updateData.result_json = JSON.stringify(updates.result);
710
674
  }
711
675
  if (updates.error !== undefined) {
712
- updateData.error_json = JSON.stringify(
713
- sanitizeErrorForStorage(updates.error),
714
- );
676
+ updateData.error_json = JSON.stringify(sanitizeErrorForStorage(updates.error));
715
677
  }
716
678
  if (updates.finishedAt !== undefined) {
717
679
  updateData.finished_at = updates.finishedAt;
@@ -719,9 +681,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
719
681
 
720
682
  if (Object.keys(updateData).length > 0) {
721
683
  await this.qb
722
- .updateTable("workflow_run_steps")
684
+ .updateTable('workflow_run_steps')
723
685
  .set(updateData)
724
- .where("id", "=", stepId)
686
+ .where('id', '=', stepId)
725
687
  .execute();
726
688
  }
727
689
  }
@@ -729,64 +691,60 @@ export class PostgresStorageAdapter implements StorageAdapter {
729
691
  async getStepsForRun(runId: string): Promise<WorkflowRunStepRecord[]> {
730
692
  this.ensureInitialized();
731
693
  const rows = await this.qb
732
- .selectFrom("workflow_run_steps")
694
+ .selectFrom('workflow_run_steps')
733
695
  .selectAll()
734
- .where("run_id", "=", runId)
735
- .orderBy("started_at", "asc")
696
+ .where('run_id', '=', runId)
697
+ .orderBy('started_at', 'asc')
736
698
  .execute();
737
699
 
738
- return rows.map((row) => this.mapStepRow(row));
700
+ return rows.map(row => this.mapStepRow(row));
739
701
  }
740
702
 
741
703
  // ============================================================================
742
704
  // Event Operations
743
705
  // ============================================================================
744
706
 
745
- async saveEvent(event: Omit<WorkflowEventRecord, "id">): Promise<void> {
707
+ async saveEvent(event: Omit<WorkflowEventRecord, 'id'>): Promise<void> {
746
708
  this.ensureInitialized();
747
709
  const id = generateId();
748
710
 
749
711
  await this.qb
750
- .insertInto("workflow_events")
712
+ .insertInto('workflow_events')
751
713
  .values({
752
714
  id,
753
715
  run_id: event.runId,
754
716
  step_key: event.stepKey ?? null,
755
717
  event_type: event.eventType,
756
718
  level: event.level,
757
- payload_json:
758
- event.payload !== undefined ? JSON.stringify(event.payload) : null,
719
+ payload_json: event.payload !== undefined ? JSON.stringify(event.payload) : null,
759
720
  timestamp: event.timestamp,
760
721
  })
761
722
  .execute();
762
723
  }
763
724
 
764
- async getEventsForRun(
765
- runId: string,
766
- options: ListEventsOptions = {},
767
- ): Promise<WorkflowEventRecord[]> {
725
+ async getEventsForRun(runId: string, options: ListEventsOptions = {}): Promise<WorkflowEventRecord[]> {
768
726
  this.ensureInitialized();
769
727
  const limit = options.limit ?? 1000;
770
728
  const offset = options.offset ?? 0;
771
729
 
772
730
  let query = this.qb
773
- .selectFrom("workflow_events")
731
+ .selectFrom('workflow_events')
774
732
  .selectAll()
775
- .where("run_id", "=", runId);
733
+ .where('run_id', '=', runId);
776
734
 
777
735
  if (options.stepKey) {
778
- query = query.where("step_key", "=", options.stepKey);
736
+ query = query.where('step_key', '=', options.stepKey);
779
737
  }
780
738
 
781
739
  if (options.level) {
782
- query = query.where("level", "=", options.level);
740
+ query = query.where('level', '=', options.level);
783
741
  }
784
742
 
785
- query = query.orderBy("timestamp", "asc").limit(limit).offset(offset);
743
+ query = query.orderBy('timestamp', 'asc').limit(limit).offset(offset);
786
744
 
787
745
  const rows = await query.execute();
788
746
 
789
- return rows.map((row) => this.mapEventRow(row));
747
+ return rows.map(row => this.mapEventRow(row));
790
748
  }
791
749
 
792
750
  // ============================================================================
@@ -816,8 +774,8 @@ export class PostgresStorageAdapter implements StorageAdapter {
816
774
  async deleteOldRuns(olderThan: Date): Promise<number> {
817
775
  this.ensureInitialized();
818
776
  const result = await this.qb
819
- .deleteFrom("runs")
820
- .where("created_at", "<", olderThan)
777
+ .deleteFrom('runs')
778
+ .where('created_at', '<', olderThan)
821
779
  .executeTakeFirst();
822
780
 
823
781
  return Number(result.numDeletedRows);
@@ -834,29 +792,27 @@ export class PostgresStorageAdapter implements StorageAdapter {
834
792
  async getInterruptedRuns(): Promise<WorkflowRunRecord[]> {
835
793
  this.ensureInitialized();
836
794
  const rows = await this.qb
837
- .selectFrom("runs")
795
+ .selectFrom('runs')
838
796
  .selectAll()
839
- .where("status", "in", ["queued", "running"])
840
- .orderBy("created_at", "asc")
797
+ .where('status', 'in', ['queued', 'running'])
798
+ .orderBy('created_at', 'asc')
841
799
  .execute();
842
800
 
843
- return rows.map((row) => this.mapRunRow(row));
801
+ return rows.map(row => this.mapRunRow(row));
844
802
  }
845
803
 
846
804
  /**
847
805
  * Get the last completed step for a run.
848
806
  * Useful for resuming from a checkpoint.
849
807
  */
850
- async getLastCompletedStep(
851
- runId: string,
852
- ): Promise<WorkflowRunStepRecord | null> {
808
+ async getLastCompletedStep(runId: string): Promise<WorkflowRunStepRecord | null> {
853
809
  this.ensureInitialized();
854
810
  const row = await this.qb
855
- .selectFrom("workflow_run_steps")
811
+ .selectFrom('workflow_run_steps')
856
812
  .selectAll()
857
- .where("run_id", "=", runId)
858
- .where("status", "=", "succeeded")
859
- .orderBy("finished_at", "desc")
813
+ .where('run_id', '=', runId)
814
+ .where('status', '=', 'succeeded')
815
+ .orderBy('finished_at', 'desc')
860
816
  .limit(1)
861
817
  .executeTakeFirst();
862
818
 
@@ -874,15 +830,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
874
830
  * @param workflowKinds - Optional list of workflow kinds to filter by
875
831
  * @returns The dequeued run, or null if no runs are available
876
832
  */
877
- async dequeueRun(
878
- workflowKinds?: string[],
879
- ): Promise<WorkflowRunRecord | null> {
833
+ async dequeueRun(workflowKinds?: string[]): Promise<WorkflowRunRecord | null> {
880
834
  this.ensureInitialized();
881
835
  // Use raw SQL for FOR UPDATE SKIP LOCKED
882
- const kindFilter =
883
- workflowKinds && workflowKinds.length > 0
884
- ? sql`AND kind = ANY(${workflowKinds}::text[])`
885
- : sql``;
836
+ const kindFilter = workflowKinds && workflowKinds.length > 0
837
+ ? sql`AND kind = ANY(${workflowKinds}::text[])`
838
+ : sql``;
886
839
 
887
840
  const result = await sql<WorkflowRunsTable>`
888
841
  UPDATE ${sql.table(`${this.schema}.runs`)}
@@ -911,28 +864,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
911
864
  kind: row.kind,
912
865
  status: row.status as RunStatus,
913
866
  parentRunId: row.parent_run_id ?? undefined,
914
- input:
915
- typeof row.input_json === "string"
916
- ? JSON.parse(row.input_json)
917
- : row.input_json,
918
- context:
919
- typeof row.context_json === "string"
920
- ? JSON.parse(row.context_json)
921
- : row.context_json,
922
- output: row.output_json
923
- ? typeof row.output_json === "string"
924
- ? JSON.parse(row.output_json)
925
- : row.output_json
926
- : undefined,
927
- error: row.error_json
928
- ? typeof row.error_json === "string"
929
- ? JSON.parse(row.error_json)
930
- : row.error_json
931
- : undefined,
932
- metadata:
933
- typeof row.metadata_json === "string"
934
- ? JSON.parse(row.metadata_json)
935
- : row.metadata_json,
867
+ input: typeof row.input_json === 'string' ? JSON.parse(row.input_json) : row.input_json,
868
+ context: typeof row.context_json === 'string' ? JSON.parse(row.context_json) : row.context_json,
869
+ output: row.output_json ? (typeof row.output_json === 'string' ? JSON.parse(row.output_json) : row.output_json) : undefined,
870
+ error: row.error_json ? (typeof row.error_json === 'string' ? JSON.parse(row.error_json) : row.error_json) : undefined,
871
+ metadata: typeof row.metadata_json === 'string' ? JSON.parse(row.metadata_json) : row.metadata_json,
936
872
  priority: row.priority ?? 0,
937
873
  timeoutMs: row.timeout_ms ?? undefined,
938
874
  createdAt: new Date(row.created_at),
@@ -949,28 +885,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
949
885
  id: row.id,
950
886
  kind: row.kind,
951
887
  status: row.status as ExtendedRunStatus,
952
- input:
953
- typeof row.input_json === "string"
954
- ? JSON.parse(row.input_json)
955
- : row.input_json,
956
- metadata:
957
- typeof row.metadata_json === "string"
958
- ? JSON.parse(row.metadata_json)
959
- : row.metadata_json,
960
- context:
961
- typeof row.context_json === "string"
962
- ? JSON.parse(row.context_json)
963
- : row.context_json,
964
- output: row.output_json
965
- ? typeof row.output_json === "string"
966
- ? JSON.parse(row.output_json)
967
- : row.output_json
968
- : undefined,
969
- error: row.error_json
970
- ? typeof row.error_json === "string"
971
- ? JSON.parse(row.error_json)
972
- : row.error_json
973
- : undefined,
888
+ input: typeof row.input_json === 'string' ? JSON.parse(row.input_json) : row.input_json,
889
+ metadata: typeof row.metadata_json === 'string' ? JSON.parse(row.metadata_json) : row.metadata_json,
890
+ context: typeof row.context_json === 'string' ? JSON.parse(row.context_json) : row.context_json,
891
+ output: row.output_json ? (typeof row.output_json === 'string' ? JSON.parse(row.output_json) : row.output_json) : undefined,
892
+ error: row.error_json ? (typeof row.error_json === 'string' ? JSON.parse(row.error_json) : row.error_json) : undefined,
974
893
  priority: row.priority ?? 0,
975
894
  timeoutMs: row.timeout_ms ?? undefined,
976
895
  createdAt: new Date(row.created_at),
@@ -985,16 +904,8 @@ export class PostgresStorageAdapter implements StorageAdapter {
985
904
  runId: row.run_id,
986
905
  stepName: row.step_name,
987
906
  status: row.status as ExtendedStepStatus,
988
- output: row.output_json
989
- ? typeof row.output_json === "string"
990
- ? JSON.parse(row.output_json)
991
- : row.output_json
992
- : undefined,
993
- error: row.error_json
994
- ? typeof row.error_json === "string"
995
- ? JSON.parse(row.error_json)
996
- : row.error_json
997
- : undefined,
907
+ output: row.output_json ? (typeof row.output_json === 'string' ? JSON.parse(row.output_json) : row.output_json) : undefined,
908
+ error: row.error_json ? (typeof row.error_json === 'string' ? JSON.parse(row.error_json) : row.error_json) : undefined,
998
909
  attempt: row.attempt,
999
910
  startedAt: row.started_at ? new Date(row.started_at) : undefined,
1000
911
  completedAt: row.completed_at ? new Date(row.completed_at) : undefined,
@@ -1009,16 +920,8 @@ export class PostgresStorageAdapter implements StorageAdapter {
1009
920
  stepName: row.step_name,
1010
921
  status: row.status as StepStatus,
1011
922
  attempt: row.attempt,
1012
- result: row.result_json
1013
- ? typeof row.result_json === "string"
1014
- ? JSON.parse(row.result_json)
1015
- : row.result_json
1016
- : undefined,
1017
- error: row.error_json
1018
- ? typeof row.error_json === "string"
1019
- ? JSON.parse(row.error_json)
1020
- : row.error_json
1021
- : undefined,
923
+ result: row.result_json ? (typeof row.result_json === 'string' ? JSON.parse(row.result_json) : row.result_json) : undefined,
924
+ error: row.error_json ? (typeof row.error_json === 'string' ? JSON.parse(row.error_json) : row.error_json) : undefined,
1022
925
  startedAt: row.started_at ? new Date(row.started_at) : undefined,
1023
926
  finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
1024
927
  };
@@ -1030,12 +933,8 @@ export class PostgresStorageAdapter implements StorageAdapter {
1030
933
  runId: row.run_id,
1031
934
  stepKey: row.step_key ?? undefined,
1032
935
  eventType: row.event_type,
1033
- level: row.level as "info" | "warn" | "error",
1034
- payload: row.payload_json
1035
- ? typeof row.payload_json === "string"
1036
- ? JSON.parse(row.payload_json)
1037
- : row.payload_json
1038
- : undefined,
936
+ level: row.level as 'info' | 'warn' | 'error',
937
+ payload: row.payload_json ? (typeof row.payload_json === 'string' ? JSON.parse(row.payload_json) : row.payload_json) : undefined,
1039
938
  timestamp: new Date(row.timestamp),
1040
939
  };
1041
940
  }
@@ -1050,7 +949,10 @@ export class PostgresStorageAdapter implements StorageAdapter {
1050
949
  */
1051
950
  async deleteRun(id: string): Promise<void> {
1052
951
  this.ensureInitialized();
1053
- await this.qb.deleteFrom("runs").where("id", "=", id).execute();
952
+ await this.qb
953
+ .deleteFrom('runs')
954
+ .where('id', '=', id)
955
+ .execute();
1054
956
  }
1055
957
 
1056
958
  /**
@@ -1112,16 +1014,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
1112
1014
  /**
1113
1015
  * Get a specific step result by run ID and step name.
1114
1016
  */
1115
- async getStepResult(
1116
- runId: string,
1117
- stepName: string,
1118
- ): Promise<StepResult | undefined> {
1017
+ async getStepResult(runId: string, stepName: string): Promise<StepResult | undefined> {
1119
1018
  this.ensureInitialized();
1120
1019
  const row = await this.qb
1121
- .selectFrom("stepflow_step_results")
1020
+ .selectFrom('stepflow_step_results')
1122
1021
  .selectAll()
1123
- .where("run_id", "=", runId)
1124
- .where("step_name", "=", stepName)
1022
+ .where('run_id', '=', runId)
1023
+ .where('step_name', '=', stepName)
1125
1024
  .executeTakeFirst();
1126
1025
 
1127
1026
  return row ? this.mapStepResultRow(row) : undefined;
@@ -1133,51 +1032,45 @@ export class PostgresStorageAdapter implements StorageAdapter {
1133
1032
  async getStepResults(runId: string): Promise<StepResult[]> {
1134
1033
  this.ensureInitialized();
1135
1034
  const rows = await this.qb
1136
- .selectFrom("stepflow_step_results")
1035
+ .selectFrom('stepflow_step_results')
1137
1036
  .selectAll()
1138
- .where("run_id", "=", runId)
1139
- .orderBy("started_at", "asc")
1037
+ .where('run_id', '=', runId)
1038
+ .orderBy('started_at', 'asc')
1140
1039
  .execute();
1141
1040
 
1142
- return rows.map((row) => this.mapStepResultRow(row));
1041
+ return rows.map(row => this.mapStepResultRow(row));
1143
1042
  }
1144
1043
 
1145
1044
  /**
1146
1045
  * Save or update a step result.
1147
1046
  * Uses upsert to handle both new and existing results.
1148
1047
  */
1149
- async saveStepResult(
1150
- result: Omit<StepResult, "id"> & { id?: string },
1151
- ): Promise<void> {
1048
+ async saveStepResult(result: Omit<StepResult, 'id'> & { id?: string }): Promise<void> {
1152
1049
  this.ensureInitialized();
1153
1050
  const id = result.id ?? generateId();
1154
1051
 
1155
1052
  await this.qb
1156
- .insertInto("stepflow_step_results")
1053
+ .insertInto('stepflow_step_results')
1157
1054
  .values({
1158
1055
  id,
1159
1056
  run_id: result.runId,
1160
1057
  step_name: result.stepName,
1161
1058
  status: result.status,
1162
1059
  output_json: result.output ? JSON.stringify(result.output) : null,
1163
- error_json: result.error
1164
- ? JSON.stringify(stripStack(result.error))
1165
- : null,
1060
+ error_json: result.error ? JSON.stringify(stripStack(result.error)) : null,
1166
1061
  attempt: result.attempt,
1167
1062
  started_at: result.startedAt ?? null,
1168
1063
  completed_at: result.completedAt ?? null,
1169
1064
  })
1170
- .onConflict((oc) =>
1171
- oc.columns(["run_id", "step_name"]).doUpdateSet({
1065
+ .onConflict(oc =>
1066
+ oc.columns(['run_id', 'step_name']).doUpdateSet({
1172
1067
  status: result.status,
1173
1068
  output_json: result.output ? JSON.stringify(result.output) : null,
1174
- error_json: result.error
1175
- ? JSON.stringify(stripStack(result.error))
1176
- : null,
1069
+ error_json: result.error ? JSON.stringify(stripStack(result.error)) : null,
1177
1070
  attempt: result.attempt,
1178
1071
  started_at: result.startedAt ?? null,
1179
1072
  completed_at: result.completedAt ?? null,
1180
- }),
1073
+ })
1181
1074
  )
1182
1075
  .execute();
1183
1076
  }
@@ -1191,20 +1084,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
1191
1084
  */
1192
1085
  async getStats(): Promise<{ runs: number; steps: number; events: number }> {
1193
1086
  this.ensureInitialized();
1194
- const [runsCount, stepsCount, eventsCount] = (await Promise.all([
1195
- this.qb
1196
- .selectFrom("runs")
1197
- .select(sql`count(*)`.as("count"))
1198
- .executeTakeFirst(),
1199
- this.qb
1200
- .selectFrom("workflow_run_steps")
1201
- .select(sql`count(*)`.as("count"))
1202
- .executeTakeFirst(),
1203
- this.qb
1204
- .selectFrom("workflow_events")
1205
- .select(sql`count(*)`.as("count"))
1206
- .executeTakeFirst(),
1207
- ])) as { count?: string | number }[];
1087
+ const [runsCount, stepsCount, eventsCount] = await Promise.all([
1088
+ this.qb.selectFrom('runs').select(sql`count(*)`.as('count')).executeTakeFirst(),
1089
+ this.qb.selectFrom('workflow_run_steps').select(sql`count(*)`.as('count')).executeTakeFirst(),
1090
+ this.qb.selectFrom('workflow_events').select(sql`count(*)`.as('count')).executeTakeFirst(),
1091
+ ]) as { count?: string | number }[];
1208
1092
 
1209
1093
  return {
1210
1094
  runs: Number(runsCount?.count ?? 0),
@@ -1223,38 +1107,33 @@ export class PostgresStorageAdapter implements StorageAdapter {
1223
1107
  * This is used internally by PostgresStorageAdapter.transaction().
1224
1108
  */
1225
1109
  class PostgresTransactionAdapter implements StorageAdapter {
1226
- private qb: ReturnType<KyselyType<StepflowDatabase>["withSchema"]>;
1110
+ private qb: ReturnType<KyselyType<StepflowDatabase>['withSchema']>;
1227
1111
 
1228
1112
  constructor(
1229
1113
  private trx: KyselyType<StepflowDatabase>,
1230
- private schema: string,
1114
+ private schema: string
1231
1115
  ) {
1232
1116
  this.qb = trx.withSchema(schema);
1233
1117
  }
1234
1118
 
1235
- async createRun(
1236
- run: CreateRunInput | Omit<WorkflowRunRecord, "id" | "createdAt">,
1237
- ): Promise<WorkflowRunRecord> {
1238
- const id = "id" in run && run.id ? run.id : generateId();
1119
+ async createRun(run: CreateRunInput | Omit<WorkflowRunRecord, 'id' | 'createdAt'>): Promise<WorkflowRunRecord> {
1120
+ const id = 'id' in run && run.id ? run.id : generateId();
1239
1121
  const createdAt = new Date();
1240
1122
 
1241
1123
  await this.qb
1242
- .insertInto("runs")
1124
+ .insertInto('runs')
1243
1125
  .values({
1244
1126
  id,
1245
1127
  kind: run.kind,
1246
1128
  status: run.status,
1247
- parent_run_id: "parentRunId" in run ? (run.parentRunId ?? null) : null,
1129
+ parent_run_id: 'parentRunId' in run ? (run.parentRunId ?? null) : null,
1248
1130
  input_json: JSON.stringify(run.input),
1249
1131
  metadata_json: JSON.stringify(run.metadata ?? {}),
1250
1132
  context_json: JSON.stringify(run.context ?? {}),
1251
1133
  output_json: null,
1252
- error_json:
1253
- "error" in run && run.error
1254
- ? JSON.stringify(sanitizeErrorForStorage(run.error))
1255
- : null,
1256
- priority: "priority" in run ? (run.priority ?? 0) : 0,
1257
- timeout_ms: "timeoutMs" in run ? (run.timeoutMs ?? null) : null,
1134
+ error_json: 'error' in run && run.error ? JSON.stringify(sanitizeErrorForStorage(run.error)) : null,
1135
+ priority: 'priority' in run ? (run.priority ?? 0) : 0,
1136
+ timeout_ms: 'timeoutMs' in run ? (run.timeoutMs ?? null) : null,
1258
1137
  created_at: createdAt,
1259
1138
  started_at: null,
1260
1139
  finished_at: null,
@@ -1265,104 +1144,77 @@ class PostgresTransactionAdapter implements StorageAdapter {
1265
1144
  id,
1266
1145
  kind: run.kind,
1267
1146
  status: run.status as RunStatus,
1268
- parentRunId: "parentRunId" in run ? run.parentRunId : undefined,
1147
+ parentRunId: 'parentRunId' in run ? run.parentRunId : undefined,
1269
1148
  input: run.input,
1270
1149
  context: run.context ?? {},
1271
1150
  output: undefined,
1272
1151
  error: undefined,
1273
1152
  metadata: run.metadata ?? {},
1274
- priority: "priority" in run ? (run.priority ?? 0) : 0,
1275
- timeoutMs: "timeoutMs" in run ? run.timeoutMs : undefined,
1153
+ priority: 'priority' in run ? (run.priority ?? 0) : 0,
1154
+ timeoutMs: 'timeoutMs' in run ? run.timeoutMs : undefined,
1276
1155
  createdAt,
1277
1156
  };
1278
1157
  }
1279
1158
 
1280
1159
  async getRun(runId: string): Promise<WorkflowRunRecord | null> {
1281
1160
  const row = await this.qb
1282
- .selectFrom("runs")
1161
+ .selectFrom('runs')
1283
1162
  .selectAll()
1284
- .where("id", "=", runId)
1163
+ .where('id', '=', runId)
1285
1164
  .executeTakeFirst();
1286
1165
 
1287
1166
  return row ? this.mapRunRow(row) : null;
1288
1167
  }
1289
1168
 
1290
- async updateRun(
1291
- runId: string,
1292
- updates: UpdateRunInput | Partial<WorkflowRunRecord>,
1293
- ): Promise<void> {
1169
+ async updateRun(runId: string, updates: UpdateRunInput | Partial<WorkflowRunRecord>): Promise<void> {
1294
1170
  const updateData: Partial<WorkflowRunsTable> = {};
1295
1171
 
1296
1172
  if (updates.status !== undefined) updateData.status = updates.status;
1297
- if (updates.context !== undefined)
1298
- updateData.context_json = JSON.stringify(updates.context);
1299
- if ("output" in updates && updates.output !== undefined)
1300
- updateData.output_json = JSON.stringify(updates.output);
1301
- if (updates.error !== undefined)
1302
- updateData.error_json = JSON.stringify(
1303
- sanitizeErrorForStorage(updates.error as WorkflowError),
1304
- );
1305
- if (updates.startedAt !== undefined)
1306
- updateData.started_at = updates.startedAt;
1307
- if (updates.finishedAt !== undefined)
1308
- updateData.finished_at = updates.finishedAt;
1173
+ if (updates.context !== undefined) updateData.context_json = JSON.stringify(updates.context);
1174
+ if ('output' in updates && updates.output !== undefined) updateData.output_json = JSON.stringify(updates.output);
1175
+ if (updates.error !== undefined) updateData.error_json = JSON.stringify(sanitizeErrorForStorage(updates.error as WorkflowError));
1176
+ if (updates.startedAt !== undefined) updateData.started_at = updates.startedAt;
1177
+ if (updates.finishedAt !== undefined) updateData.finished_at = updates.finishedAt;
1309
1178
 
1310
1179
  if (Object.keys(updateData).length > 0) {
1311
- await this.qb
1312
- .updateTable("runs")
1313
- .set(updateData)
1314
- .where("id", "=", runId)
1315
- .execute();
1180
+ await this.qb.updateTable('runs').set(updateData).where('id', '=', runId).execute();
1316
1181
  }
1317
1182
  }
1318
1183
 
1319
- async listRuns(
1320
- options: ListRunsOptions = {},
1321
- ): Promise<PaginatedResult<WorkflowRunRecord>> {
1184
+ async listRuns(options: ListRunsOptions = {}): Promise<PaginatedResult<WorkflowRunRecord>> {
1322
1185
  const limit = options.limit ?? 50;
1323
1186
  const offset = options.offset ?? 0;
1324
1187
 
1325
- let query = this.qb.selectFrom("runs").selectAll();
1188
+ let query = this.qb.selectFrom('runs').selectAll();
1326
1189
 
1327
- if (options.kind) query = query.where("kind", "=", options.kind);
1190
+ if (options.kind) query = query.where('kind', '=', options.kind);
1328
1191
  if (options.status) {
1329
- const statuses = Array.isArray(options.status)
1330
- ? options.status
1331
- : [options.status];
1332
- query = query.where("status", "in", statuses);
1192
+ const statuses = Array.isArray(options.status) ? options.status : [options.status];
1193
+ query = query.where('status', 'in', statuses);
1333
1194
  }
1334
1195
  if (options.parentRunId !== undefined) {
1335
- query = query.where("parent_run_id", "=", options.parentRunId);
1196
+ query = query.where('parent_run_id', '=', options.parentRunId);
1336
1197
  }
1337
1198
 
1338
- const orderColumn =
1339
- (options.orderBy ?? "createdAt") === "createdAt"
1340
- ? "created_at"
1341
- : options.orderBy === "startedAt"
1342
- ? "started_at"
1343
- : "finished_at";
1344
- const orderDirection = options.orderDirection ?? "desc";
1199
+ const orderColumn = (options.orderBy ?? 'createdAt') === 'createdAt' ? 'created_at' :
1200
+ options.orderBy === 'startedAt' ? 'started_at' : 'finished_at';
1201
+ const orderDirection = options.orderDirection ?? 'desc';
1345
1202
 
1346
- query = query
1347
- .orderBy(orderColumn, orderDirection)
1348
- .limit(limit)
1349
- .offset(offset);
1203
+ query = query.orderBy(orderColumn, orderDirection).limit(limit).offset(offset);
1350
1204
 
1351
1205
  const rows = await query.execute();
1352
1206
 
1353
1207
  return {
1354
- items: rows.map((row) => this.mapRunRow(row)),
1208
+ items: rows.map(row => this.mapRunRow(row)),
1355
1209
  total: rows.length, // Simplified for transaction context
1356
1210
  };
1357
1211
  }
1358
1212
 
1359
- async createStep(
1360
- step: Omit<WorkflowRunStepRecord, "id">,
1361
- ): Promise<WorkflowRunStepRecord> {
1213
+ async createStep(step: Omit<WorkflowRunStepRecord, 'id'>): Promise<WorkflowRunStepRecord> {
1362
1214
  const id = generateId();
1363
1215
 
1364
1216
  await this.qb
1365
- .insertInto("workflow_run_steps")
1217
+ .insertInto('workflow_run_steps')
1366
1218
  .values({
1367
1219
  id,
1368
1220
  run_id: step.runId,
@@ -1370,11 +1222,8 @@ class PostgresTransactionAdapter implements StorageAdapter {
1370
1222
  step_name: step.stepName,
1371
1223
  status: step.status,
1372
1224
  attempt: step.attempt,
1373
- result_json:
1374
- step.result !== undefined ? JSON.stringify(step.result) : null,
1375
- error_json: step.error
1376
- ? JSON.stringify(sanitizeErrorForStorage(step.error))
1377
- : null,
1225
+ result_json: step.result !== undefined ? JSON.stringify(step.result) : null,
1226
+ error_json: step.error ? JSON.stringify(sanitizeErrorForStorage(step.error)) : null,
1378
1227
  started_at: step.startedAt ?? null,
1379
1228
  finished_at: step.finishedAt ?? null,
1380
1229
  })
@@ -1385,88 +1234,72 @@ class PostgresTransactionAdapter implements StorageAdapter {
1385
1234
 
1386
1235
  async getStep(stepId: string): Promise<WorkflowRunStepRecord | null> {
1387
1236
  const row = await this.qb
1388
- .selectFrom("workflow_run_steps")
1237
+ .selectFrom('workflow_run_steps')
1389
1238
  .selectAll()
1390
- .where("id", "=", stepId)
1239
+ .where('id', '=', stepId)
1391
1240
  .executeTakeFirst();
1392
1241
 
1393
1242
  return row ? this.mapStepRow(row) : null;
1394
1243
  }
1395
1244
 
1396
- async updateStep(
1397
- stepId: string,
1398
- updates: Partial<WorkflowRunStepRecord>,
1399
- ): Promise<void> {
1245
+ async updateStep(stepId: string, updates: Partial<WorkflowRunStepRecord>): Promise<void> {
1400
1246
  const updateData: Partial<WorkflowRunStepsTable> = {};
1401
1247
 
1402
1248
  if (updates.status !== undefined) updateData.status = updates.status;
1403
1249
  if (updates.attempt !== undefined) updateData.attempt = updates.attempt;
1404
- if (updates.result !== undefined)
1405
- updateData.result_json = JSON.stringify(updates.result);
1406
- if (updates.error !== undefined)
1407
- updateData.error_json = JSON.stringify(
1408
- sanitizeErrorForStorage(updates.error as WorkflowError),
1409
- );
1410
- if (updates.finishedAt !== undefined)
1411
- updateData.finished_at = updates.finishedAt;
1250
+ if (updates.result !== undefined) updateData.result_json = JSON.stringify(updates.result);
1251
+ if (updates.error !== undefined) updateData.error_json = JSON.stringify(sanitizeErrorForStorage(updates.error as WorkflowError));
1252
+ if (updates.finishedAt !== undefined) updateData.finished_at = updates.finishedAt;
1412
1253
 
1413
1254
  if (Object.keys(updateData).length > 0) {
1414
- await this.qb
1415
- .updateTable("workflow_run_steps")
1416
- .set(updateData)
1417
- .where("id", "=", stepId)
1418
- .execute();
1255
+ await this.qb.updateTable('workflow_run_steps').set(updateData).where('id', '=', stepId).execute();
1419
1256
  }
1420
1257
  }
1421
1258
 
1422
1259
  async getStepsForRun(runId: string): Promise<WorkflowRunStepRecord[]> {
1423
1260
  const rows = await this.qb
1424
- .selectFrom("workflow_run_steps")
1261
+ .selectFrom('workflow_run_steps')
1425
1262
  .selectAll()
1426
- .where("run_id", "=", runId)
1427
- .orderBy("started_at", "asc")
1263
+ .where('run_id', '=', runId)
1264
+ .orderBy('started_at', 'asc')
1428
1265
  .execute();
1429
1266
 
1430
- return rows.map((row) => this.mapStepRow(row));
1267
+ return rows.map(row => this.mapStepRow(row));
1431
1268
  }
1432
1269
 
1433
- async saveEvent(event: Omit<WorkflowEventRecord, "id">): Promise<void> {
1270
+ async saveEvent(event: Omit<WorkflowEventRecord, 'id'>): Promise<void> {
1434
1271
  const id = generateId();
1435
1272
 
1436
1273
  await this.qb
1437
- .insertInto("workflow_events")
1274
+ .insertInto('workflow_events')
1438
1275
  .values({
1439
1276
  id,
1440
1277
  run_id: event.runId,
1441
1278
  step_key: event.stepKey ?? null,
1442
1279
  event_type: event.eventType,
1443
1280
  level: event.level,
1444
- payload_json:
1445
- event.payload !== undefined ? JSON.stringify(event.payload) : null,
1281
+ payload_json: event.payload !== undefined ? JSON.stringify(event.payload) : null,
1446
1282
  timestamp: event.timestamp,
1447
1283
  })
1448
1284
  .execute();
1449
1285
  }
1450
1286
 
1451
- async getEventsForRun(
1452
- runId: string,
1453
- options: ListEventsOptions = {},
1454
- ): Promise<WorkflowEventRecord[]> {
1287
+ async getEventsForRun(runId: string, options: ListEventsOptions = {}): Promise<WorkflowEventRecord[]> {
1455
1288
  let query = this.qb
1456
- .selectFrom("workflow_events")
1289
+ .selectFrom('workflow_events')
1457
1290
  .selectAll()
1458
- .where("run_id", "=", runId);
1291
+ .where('run_id', '=', runId);
1459
1292
 
1460
- if (options.stepKey) query = query.where("step_key", "=", options.stepKey);
1461
- if (options.level) query = query.where("level", "=", options.level);
1293
+ if (options.stepKey) query = query.where('step_key', '=', options.stepKey);
1294
+ if (options.level) query = query.where('level', '=', options.level);
1462
1295
 
1463
1296
  const rows = await query
1464
- .orderBy("timestamp", "asc")
1297
+ .orderBy('timestamp', 'asc')
1465
1298
  .limit(options.limit ?? 1000)
1466
1299
  .offset(options.offset ?? 0)
1467
1300
  .execute();
1468
1301
 
1469
- return rows.map((row) => this.mapEventRow(row));
1302
+ return rows.map(row => this.mapEventRow(row));
1470
1303
  }
1471
1304
 
1472
1305
  private mapRunRow(row: WorkflowRunsTable): WorkflowRunRecord {
@@ -1475,28 +1308,11 @@ class PostgresTransactionAdapter implements StorageAdapter {
1475
1308
  kind: row.kind,
1476
1309
  status: row.status as RunStatus,
1477
1310
  parentRunId: row.parent_run_id ?? undefined,
1478
- input:
1479
- typeof row.input_json === "string"
1480
- ? JSON.parse(row.input_json)
1481
- : row.input_json,
1482
- context:
1483
- typeof row.context_json === "string"
1484
- ? JSON.parse(row.context_json)
1485
- : row.context_json,
1486
- output: row.output_json
1487
- ? typeof row.output_json === "string"
1488
- ? JSON.parse(row.output_json)
1489
- : row.output_json
1490
- : undefined,
1491
- error: row.error_json
1492
- ? typeof row.error_json === "string"
1493
- ? JSON.parse(row.error_json)
1494
- : row.error_json
1495
- : undefined,
1496
- metadata:
1497
- typeof row.metadata_json === "string"
1498
- ? JSON.parse(row.metadata_json)
1499
- : row.metadata_json,
1311
+ input: typeof row.input_json === 'string' ? JSON.parse(row.input_json) : row.input_json,
1312
+ context: typeof row.context_json === 'string' ? JSON.parse(row.context_json) : row.context_json,
1313
+ output: row.output_json ? (typeof row.output_json === 'string' ? JSON.parse(row.output_json) : row.output_json) : undefined,
1314
+ error: row.error_json ? (typeof row.error_json === 'string' ? JSON.parse(row.error_json) : row.error_json) : undefined,
1315
+ metadata: typeof row.metadata_json === 'string' ? JSON.parse(row.metadata_json) : row.metadata_json,
1500
1316
  priority: row.priority ?? 0,
1501
1317
  timeoutMs: row.timeout_ms ?? undefined,
1502
1318
  createdAt: new Date(row.created_at),
@@ -1513,16 +1329,8 @@ class PostgresTransactionAdapter implements StorageAdapter {
1513
1329
  stepName: row.step_name,
1514
1330
  status: row.status as StepStatus,
1515
1331
  attempt: row.attempt,
1516
- result: row.result_json
1517
- ? typeof row.result_json === "string"
1518
- ? JSON.parse(row.result_json)
1519
- : row.result_json
1520
- : undefined,
1521
- error: row.error_json
1522
- ? typeof row.error_json === "string"
1523
- ? JSON.parse(row.error_json)
1524
- : row.error_json
1525
- : undefined,
1332
+ result: row.result_json ? (typeof row.result_json === 'string' ? JSON.parse(row.result_json) : row.result_json) : undefined,
1333
+ error: row.error_json ? (typeof row.error_json === 'string' ? JSON.parse(row.error_json) : row.error_json) : undefined,
1526
1334
  startedAt: row.started_at ? new Date(row.started_at) : undefined,
1527
1335
  finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
1528
1336
  };
@@ -1534,12 +1342,8 @@ class PostgresTransactionAdapter implements StorageAdapter {
1534
1342
  runId: row.run_id,
1535
1343
  stepKey: row.step_key ?? undefined,
1536
1344
  eventType: row.event_type,
1537
- level: row.level as "info" | "warn" | "error",
1538
- payload: row.payload_json
1539
- ? typeof row.payload_json === "string"
1540
- ? JSON.parse(row.payload_json)
1541
- : row.payload_json
1542
- : undefined,
1345
+ level: row.level as 'info' | 'warn' | 'error',
1346
+ payload: row.payload_json ? (typeof row.payload_json === 'string' ? JSON.parse(row.payload_json) : row.payload_json) : undefined,
1543
1347
  timestamp: new Date(row.timestamp),
1544
1348
  };
1545
1349
  }