@multiplier-labs/stepflow 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/.codekin/reports/code-review/2026-04-11_code-review-daily.md +146 -0
  2. package/.codekin/reports/code-review/2026-04-13_code-review-daily.md +74 -0
  3. package/.codekin/reports/comments/2026-04-11_comment-assessment.md +161 -0
  4. package/.codekin/reports/comments/2026-04-14_comment-assessment.md +34 -0
  5. package/.codekin/reports/complexity/2026-04-11_complexity-report.md +152 -0
  6. package/.codekin/reports/complexity/2026-04-14_complexity-report.md +34 -0
  7. package/.codekin/reports/dependencies/2026-04-11_dependency-health.md +93 -0
  8. package/.codekin/reports/docs-audit/2026-04-11_docs-audit.md +138 -0
  9. package/.codekin/reports/docs-audit/2026-04-13_docs-audit.md +121 -0
  10. package/.codekin/reports/security/2026-04-11_security-audit.md +297 -0
  11. package/.github/workflows/publish.yml +6 -0
  12. package/dist/{chunk-4VOKMGP6.js → chunk-5XJBAPXF.js} +517 -522
  13. package/dist/chunk-5XJBAPXF.js.map +1 -0
  14. package/dist/{chunk-KHTUWLVZ.js → chunk-FMPJBK2N.js} +55 -10
  15. package/dist/chunk-FMPJBK2N.js.map +1 -0
  16. package/dist/{chunk-ML35PIHX.js → chunk-JPXPB7U5.js} +52 -2
  17. package/dist/chunk-JPXPB7U5.js.map +1 -0
  18. package/dist/{chunk-GYPNJBDP.js → chunk-U3PCVK6O.js} +266 -47
  19. package/dist/chunk-U3PCVK6O.js.map +1 -0
  20. package/dist/events/index.d.ts +56 -3
  21. package/dist/events/index.js +1 -1
  22. package/dist/{index-Dzr437iW.d.ts → index-Cd0IZ40L.d.ts} +50 -11
  23. package/dist/index.d.ts +2 -12
  24. package/dist/index.js +764 -485
  25. package/dist/index.js.map +1 -1
  26. package/dist/scheduler/index.d.ts +1 -1
  27. package/dist/scheduler/index.js +2 -2
  28. package/dist/storage/index.d.ts +313 -47
  29. package/dist/storage/index.js +4 -2
  30. package/docs/stepflow-api-reference.md +3 -1
  31. package/package.json +7 -4
  32. package/src/core/engine/concurrency.ts +229 -0
  33. package/src/core/engine/lifecycle.ts +176 -0
  34. package/src/core/engine.test.ts +70 -0
  35. package/src/core/engine.ts +91 -185
  36. package/src/core/orchestrator/persistence.ts +165 -0
  37. package/src/core/orchestrator/plan-resolution.ts +140 -0
  38. package/src/core/orchestrator/state-transitions.ts +109 -0
  39. package/src/core/orchestrator/step-runner.ts +427 -0
  40. package/src/core/orchestrator.ts +40 -359
  41. package/src/events/webhook.test.ts +292 -0
  42. package/src/events/webhook.ts +320 -77
  43. package/src/planning/evaluate-condition.test.ts +153 -0
  44. package/src/planning/evaluate-condition.ts +105 -0
  45. package/src/planning/planner.ts +1 -83
  46. package/src/planning/registry.ts +3 -85
  47. package/src/scheduler/cron.test.ts +9 -1
  48. package/src/scheduler/cron.ts +26 -2
  49. package/src/scheduler/postgres-persistence.ts +27 -15
  50. package/src/scheduler/sqlite-persistence.ts +28 -3
  51. package/src/storage/postgres-core.ts +550 -0
  52. package/src/storage/postgres-migration.test.ts +69 -0
  53. package/src/storage/postgres-transaction-pagination.test.ts +107 -0
  54. package/src/storage/postgres.ts +171 -690
  55. package/src/storage/sqlite-core.ts +151 -0
  56. package/src/storage/sqlite.test.ts +177 -1
  57. package/src/storage/sqlite.ts +183 -93
  58. package/src/utils/index.ts +2 -0
  59. package/src/utils/pinned-fetch.test.ts +84 -0
  60. package/src/utils/pinned-fetch.ts +118 -0
  61. package/src/utils/postgres-deps.ts +16 -0
  62. package/src/utils/safe-json.test.ts +166 -0
  63. package/src/utils/safe-json.ts +107 -0
  64. package/src/utils/webhook-signature.test.ts +111 -0
  65. package/src/utils/webhook-signature.ts +74 -0
  66. package/dist/chunk-4VOKMGP6.js.map +0 -1
  67. package/dist/chunk-GYPNJBDP.js.map +0 -1
  68. package/dist/chunk-KHTUWLVZ.js.map +0 -1
  69. package/dist/chunk-ML35PIHX.js.map +0 -1
@@ -2,8 +2,8 @@ import {
2
2
  CronScheduler,
3
3
  PostgresSchedulePersistence,
4
4
  SQLiteSchedulePersistence
5
- } from "../chunk-KHTUWLVZ.js";
6
- import "../chunk-ML35PIHX.js";
5
+ } from "../chunk-FMPJBK2N.js";
6
+ import "../chunk-JPXPB7U5.js";
7
7
  export {
8
8
  CronScheduler,
9
9
  PostgresSchedulePersistence,
@@ -1,8 +1,9 @@
1
1
  import { i as StorageAdapter, j as WorkflowRunRecord, d as ListRunsOptions, P as PaginatedResult, k as WorkflowRunStepRecord, W as WorkflowEventRecord, L as ListEventsOptions, C as CreateRunInput, U as UpdateRunInput, e as StepResult } from '../types-DSEjiLMj.js';
2
2
  export { E as ExtendedListRunsOptions, a as ExtendedRunStatus, b as ExtendedStepStatus, c as ExtendedWorkflowRunRecord, S as StepRecord, f as StepflowDatabase, g as StepflowRunsTable, h as StepflowStepResultsTable, l as WorkflowStorage } from '../types-DSEjiLMj.js';
3
3
  import Database from 'better-sqlite3';
4
+ import { L as Logger } from '../types-K5Gjk3H_.js';
5
+ import { Kysely } from 'kysely';
4
6
  import { Pool, PoolConfig } from 'pg';
5
- import '../types-K5Gjk3H_.js';
6
7
 
7
8
  /**
8
9
  * In-memory storage adapter for development and testing.
@@ -69,6 +70,21 @@ declare class MemoryStorageAdapter implements StorageAdapter {
69
70
  * SQLite storage adapter using better-sqlite3.
70
71
  *
71
72
  * Provides durable persistence for workflow runs, steps, and events.
73
+ *
74
+ * ## Module layout
75
+ *
76
+ * - `SQLiteStorageAdapter` (this file) owns the better-sqlite3 connection,
77
+ * the prepared-statement cache, and every method on `StorageAdapter`. It
78
+ * is the only adapter class — unlike the Postgres adapter there is no
79
+ * transaction-scoped twin, so no abstract CRUD base class is required.
80
+ *
81
+ * - `sqlite-core.ts` holds the pure row-type interfaces and row mappers
82
+ * (`mapRunRow`, `mapStepRow`, `mapEventRow`). The mappers route every
83
+ * JSON-column parse through `safeParseField` / `safeParseOptionalField`
84
+ * in `src/utils/safe-json.ts`, sharing the corruption-counter +
85
+ * structured-warn behavior with the Postgres core (audit M2 / L3,
86
+ * PR #52). The split mirrors `postgres-core.ts` so both adapters
87
+ * expose mappers in the same shape.
72
88
  */
73
89
 
74
90
  /**
@@ -90,6 +106,12 @@ interface SQLiteStorageConfig {
90
106
  * Table names are always prefixed with 'workflow_'.
91
107
  */
92
108
  tablePrefix?: string;
109
+ /**
110
+ * Optional structured logger used to surface JSON corruption events.
111
+ * Without one, parse failures still increment the corruption counter
112
+ * exposed via `getJsonParseCorruptionCount()` but produce no log output.
113
+ */
114
+ logger?: Logger;
93
115
  }
94
116
  /**
95
117
  * SQLite implementation of StorageAdapter using better-sqlite3.
@@ -114,12 +136,31 @@ interface SQLiteStorageConfig {
114
136
  declare class SQLiteStorageAdapter implements StorageAdapter {
115
137
  private db;
116
138
  private prefix;
139
+ private logger;
117
140
  private stmts;
118
141
  constructor(config: SQLiteStorageConfig);
119
142
  /**
120
143
  * Create the workflow tables if they don't exist.
121
144
  */
122
145
  private createTables;
146
+ /**
147
+ * Apply additive schema migrations to existing tables.
148
+ *
149
+ * `CREATE TABLE IF NOT EXISTS` is a no-op when the table already exists, so
150
+ * any column added to {@link CREATE_TABLES_SQL} after the initial schema is
151
+ * silently absent on databases created by older versions of this adapter.
152
+ * Without this step, the first INSERT/SELECT crashes with
153
+ * `SqliteError: table X has no column named Y`. This mirrors the
154
+ * `ALTER TABLE ADD COLUMN IF NOT EXISTS` block in `postgres.ts`'s
155
+ * `createTables()`.
156
+ *
157
+ * For each entry in {@link SCHEMA_MIGRATIONS}: if the table exists and the
158
+ * column does not, run `ALTER TABLE ADD COLUMN`. The ALTER is wrapped in a
159
+ * try/catch that tolerates `duplicate column` errors so concurrent startups
160
+ * (or any race between the existence check and the ALTER) do not crash.
161
+ * Other SQLite errors propagate.
162
+ */
163
+ private runMigrations;
123
164
  /**
124
165
  * Prepare all SQL statements for better performance.
125
166
  */
@@ -137,10 +178,17 @@ declare class SQLiteStorageAdapter implements StorageAdapter {
137
178
  /**
138
179
  * Execute a function within a database transaction (async interface).
139
180
  *
140
- * @deprecated Use `transactionSync()` instead. This method only works when
141
- * the callback performs purely synchronous operations wrapped in async/await.
142
- * If the callback awaits real async I/O (network, timers, etc.), it will
143
- * throw an error. `transactionSync()` makes the synchronous requirement explicit.
181
+ * @deprecated Use {@link transactionSync} instead. better-sqlite3 transactions
182
+ * are synchronous: COMMIT happens the moment the callback returns control to
183
+ * `db.transaction(fn)()`. If the callback awaits real async I/O (network,
184
+ * timers, etc.), the COMMIT has already happened by the time the awaited work
185
+ * runs, so its operations are **not** transactional.
186
+ *
187
+ * To make the footgun visible the adapter installs a runtime guard: when the
188
+ * callback's promise has not settled by the time the COMMIT returns, the
189
+ * deprecated method emits a `console.warn` so that the misuse surfaces in
190
+ * production logs. The promise is still returned so existing callers keep
191
+ * working — switch to `transactionSync` to silence the warning.
144
192
  */
145
193
  transaction<T>(fn: (tx: StorageAdapter) => Promise<T>): Promise<T>;
146
194
  /**
@@ -172,9 +220,8 @@ declare class SQLiteStorageAdapter implements StorageAdapter {
172
220
  * Useful for resuming from a checkpoint.
173
221
  */
174
222
  getLastCompletedStep(runId: string): Promise<WorkflowRunStepRecord | null>;
175
- private mapRunRow;
176
- private mapStepRow;
177
- private mapEventRow;
223
+ /** Mapper context paired with the adapter's component label + optional logger. */
224
+ private get mapperCtx();
178
225
  /**
179
226
  * Close the database connection.
180
227
  */
@@ -189,13 +236,215 @@ declare class SQLiteStorageAdapter implements StorageAdapter {
189
236
  };
190
237
  }
191
238
 
239
+ /**
240
+ * Safe JSON-parse helpers shared by storage and scheduler adapters.
241
+ *
242
+ * When a stored JSON column is corrupted, we want to surface the problem
243
+ * operationally — structured log + counter — instead of silently degrading.
244
+ * The logger payload is restricted to safe metadata (component, row id,
245
+ * column, payload length, payload type, parser error message). The raw
246
+ * value is intentionally never logged because it may contain user-provided
247
+ * run / step input.
248
+ *
249
+ * Audit: 2026-04-27 findings L3 (corruption visibility) and M2 (do not
250
+ * bypass the configured `Logger` or leak the raw value via `console`).
251
+ */
252
+
253
+ interface SafeJsonParseContext {
254
+ /** Component label, e.g. `SQLiteStorageAdapter`. */
255
+ component: string;
256
+ /** Optional database row id, surfaced in the warning so operators can locate the bad row. */
257
+ rowId?: string;
258
+ /** Optional column name for additional context. */
259
+ column?: string;
260
+ /** Optional structured logger; without one, only the counter is bumped. */
261
+ logger?: Logger;
262
+ }
263
+ /**
264
+ * Storage-adapter-level slice of `SafeJsonParseContext` — the part each
265
+ * adapter binds once (its component label + optional logger). Row mappers
266
+ * extend this with `rowId` and `column` per field, so corruption logs always
267
+ * identify the bad row.
268
+ *
269
+ * Shared between `src/storage/postgres-core.ts` and
270
+ * `src/storage/sqlite-core.ts` so all storage adapters use one mapper-context
271
+ * shape regardless of backend.
272
+ */
273
+ type MapperContext = Pick<SafeJsonParseContext, 'component' | 'logger'>;
274
+
275
+ /**
276
+ * Shared core for the PostgreSQL storage adapters.
277
+ *
278
+ * `PostgresStorageAdapter` (the public, pool-owning adapter) and
279
+ * `PostgresTransactionAdapter` (the in-transaction wrapper used by
280
+ * `PostgresStorageAdapter.transaction()`) both implement the same CRUD subset
281
+ * of `StorageAdapter`. Before this refactor they each carried their own copy
282
+ * of every CRUD method, every row mapper, and every JSON-parse helper. The
283
+ * only meaningful difference between them is which Kysely query builder they
284
+ * target — an instance-bound one (`db.withSchema(schema)`) versus a
285
+ * transaction-bound one (`trx.withSchema(schema)`).
286
+ *
287
+ * This module collapses that duplication:
288
+ *
289
+ * - Pure helpers (`safeJsonParse`, `safeParseField`, `safeParseOptionalField`,
290
+ * row mappers, `applyRunsFilters`) live as standalone functions so callers
291
+ * can use them directly without inheritance.
292
+ *
293
+ * - `PostgresStorageCore` is an abstract base class that implements the
294
+ * CRUD subset of `StorageAdapter` against an abstract `qb` accessor and
295
+ * an `ensureReady()` hook. Subclasses supply the schema-scoped query
296
+ * builder (and an optional pre-call guard, e.g.
297
+ * `PostgresStorageAdapter.ensureInitialized()`).
298
+ *
299
+ * No behavior change: every query and every JSON-parse path matches the
300
+ * previous in-line implementations. The C2 transaction-pagination fix
301
+ * (same-connection COUNT(*)) is preserved by the shared `listRuns`
302
+ * implementation. The C1 migration error-propagation fix lives on
303
+ * `PostgresStorageAdapter` and is unaffected by this module.
304
+ */
305
+
306
+ /** Kysely row type for the workflow_runs table. */
307
+ interface WorkflowRunsTable {
308
+ id: string;
309
+ kind: string;
310
+ status: string;
311
+ parent_run_id: string | null;
312
+ input_json: string;
313
+ metadata_json: string;
314
+ context_json: string;
315
+ output_json: string | null;
316
+ error_json: string | null;
317
+ priority: number;
318
+ timeout_ms: number | null;
319
+ created_at: Date;
320
+ started_at: Date | null;
321
+ finished_at: Date | null;
322
+ }
323
+ /** Kysely row type for the workflow_run_steps table. */
324
+ interface WorkflowRunStepsTable {
325
+ id: string;
326
+ run_id: string;
327
+ step_key: string;
328
+ step_name: string;
329
+ status: string;
330
+ attempt: number;
331
+ result_json: string | null;
332
+ error_json: string | null;
333
+ started_at: Date | null;
334
+ finished_at: Date | null;
335
+ }
336
+ /** Kysely row type for the workflow_events table. */
337
+ interface WorkflowEventsTable {
338
+ id: string;
339
+ run_id: string;
340
+ step_key: string | null;
341
+ event_type: string;
342
+ level: string;
343
+ payload_json: string | null;
344
+ timestamp: Date;
345
+ }
346
+ /** Combined Kysely schema covering the tables touched by the shared core. */
347
+ interface PostgresCoreDatabase {
348
+ runs: WorkflowRunsTable;
349
+ workflow_run_steps: WorkflowRunStepsTable;
350
+ workflow_events: WorkflowEventsTable;
351
+ }
352
+ /** Schema-scoped query builder produced by `db.withSchema(schemaName)`. */
353
+ type SchemaQueryBuilder<DB extends PostgresCoreDatabase = PostgresCoreDatabase> = ReturnType<Kysely<DB>['withSchema']>;
354
+ /** Concrete core-only schema query builder used inside the shared base class. */
355
+ type CoreSchemaQueryBuilder = SchemaQueryBuilder<PostgresCoreDatabase>;
356
+ /**
357
+ * Abstract base implementing the CRUD subset of `StorageAdapter` against a
358
+ * schema-scoped Kysely query builder. Subclasses must provide the query
359
+ * builder via `qb` and may override `ensureReady()` to gate calls (the
360
+ * pool-owning adapter uses this to enforce `initialize()` having run).
361
+ *
362
+ * The base class deliberately does **not** own the connection pool, schema
363
+ * migrations, or transaction control — those stay with `PostgresStorageAdapter`.
364
+ *
365
+ * The base class targets `PostgresCoreDatabase` (the three core tables) so
366
+ * Kysely's row-type machinery can fully resolve column types on every query.
367
+ * Subclasses with a wider schema (e.g. `PostgresStorageAdapter` with
368
+ * `stepflow_step_results`) keep their own typed handle for the extra tables
369
+ * and return a core-typed view here.
370
+ */
371
+ declare abstract class PostgresStorageCore implements StorageAdapter {
372
+ protected readonly schema: string;
373
+ protected readonly logger?: Logger | undefined;
374
+ protected constructor(schema: string, logger?: Logger | undefined);
375
+ /** Schema-scoped query builder. Subclasses return the bound builder. */
376
+ protected abstract get qb(): CoreSchemaQueryBuilder;
377
+ /** Component label used in corruption warnings; defines the JSON-parse `MapperContext`. */
378
+ protected abstract get warnLabel(): string;
379
+ /** Mapper context handed to row mappers — pairs the component label with the optional logger. */
380
+ protected get mapperCtx(): MapperContext;
381
+ /**
382
+ * Hook called at the start of every CRUD method. The pool-owning adapter
383
+ * overrides this to throw before `initialize()` has run; the transaction
384
+ * wrapper leaves it as a no-op.
385
+ */
386
+ protected ensureReady(): void;
387
+ createRun(run: CreateRunInput | Omit<WorkflowRunRecord, 'id' | 'createdAt'>): Promise<WorkflowRunRecord>;
388
+ getRun(runId: string): Promise<WorkflowRunRecord | null>;
389
+ updateRun(runId: string, updates: UpdateRunInput | Partial<WorkflowRunRecord>): Promise<void>;
390
+ listRuns(options?: ListRunsOptions): Promise<PaginatedResult<WorkflowRunRecord>>;
391
+ createStep(step: Omit<WorkflowRunStepRecord, 'id'>): Promise<WorkflowRunStepRecord>;
392
+ getStep(stepId: string): Promise<WorkflowRunStepRecord | null>;
393
+ updateStep(stepId: string, updates: Partial<WorkflowRunStepRecord>): Promise<void>;
394
+ getStepsForRun(runId: string): Promise<WorkflowRunStepRecord[]>;
395
+ saveEvent(event: Omit<WorkflowEventRecord, 'id'>): Promise<void>;
396
+ getEventsForRun(runId: string, options?: ListEventsOptions): Promise<WorkflowEventRecord[]>;
397
+ }
398
+
192
399
  /**
193
400
  * PostgreSQL storage adapter using Kysely.
194
401
  *
195
- * Provides durable persistence for workflow runs, steps, and events
196
- * with support for distributed deployments and connection pooling.
402
+ * Provides durable persistence for workflow runs, steps, and events with
403
+ * support for distributed deployments and connection pooling.
404
+ *
405
+ * ## Module layout
406
+ *
407
+ * - `PostgresStorageAdapter` (this file) owns the `pg.Pool` / Kysely
408
+ * instance, runs schema migrations on `initialize()`, and implements all
409
+ * non-CRUD methods: connection lifecycle (`close`, `transaction`),
410
+ * workflow-level operations (`dequeueRun`, `cleanupStaleRuns`,
411
+ * `markRunsAsFailed`, `getStats`, `deleteRun`, `deleteOldRuns`,
412
+ * `getInterruptedRuns`, `getLastCompletedStep`), and the
413
+ * `stepflow_step_results` accessors.
414
+ *
415
+ * - `PostgresTransactionAdapter` (this file) is a thin shell used by
416
+ * `PostgresStorageAdapter.transaction()`. It binds a transaction-scoped
417
+ * Kysely query builder; everything else comes from the shared core.
418
+ * Exported only for regression tests.
419
+ *
420
+ * - `PostgresStorageCore` (./postgres-core.ts) holds the shared CRUD
421
+ * methods (`createRun`, `getRun`, `updateRun`, `listRuns`, `createStep`,
422
+ * `getStep`, `updateStep`, `getStepsForRun`, `saveEvent`,
423
+ * `getEventsForRun`), the row mappers, and the JSON-parse helpers. Both
424
+ * classes here extend it.
197
425
  */
198
426
 
427
+ /** Kysely row type for the stepflow_step_results table (extended storage). */
428
+ interface StepflowStepResultsTable {
429
+ id: string;
430
+ /** Foreign key to runs.id. */
431
+ run_id: string;
432
+ /** Step name identifier. */
433
+ step_name: string;
434
+ /** Current step status (maps to ExtendedStepStatus). */
435
+ status: string;
436
+ /** JSON-serialized step output, null until step completes. */
437
+ output_json: string | null;
438
+ /** JSON-serialized error object, null unless step failed. */
439
+ error_json: string | null;
440
+ /** Current retry attempt number (1-based). */
441
+ attempt: number;
442
+ started_at: Date | null;
443
+ completed_at: Date | null;
444
+ }
445
+ interface StepflowDatabase extends PostgresCoreDatabase {
446
+ stepflow_step_results: StepflowStepResultsTable;
447
+ }
199
448
  /**
200
449
  * Configuration options for the PostgreSQL storage adapter.
201
450
  */
@@ -224,6 +473,12 @@ interface PostgresStorageConfig {
224
473
  * @default true
225
474
  */
226
475
  autoMigrate?: boolean;
476
+ /**
477
+ * Optional structured logger used to surface JSON corruption events.
478
+ * Without one, parse failures still increment the corruption counter
479
+ * exposed via `getJsonParseCorruptionCount()` but produce no log output.
480
+ */
481
+ logger?: Logger;
227
482
  }
228
483
  /**
229
484
  * PostgreSQL implementation of StorageAdapter using Kysely.
@@ -263,20 +518,30 @@ interface PostgresStorageConfig {
263
518
  * const storage = new PostgresStorageAdapter({ pool });
264
519
  * ```
265
520
  */
266
- declare class PostgresStorageAdapter implements StorageAdapter {
521
+ declare class PostgresStorageAdapter extends PostgresStorageCore {
267
522
  private db;
268
523
  private pool;
269
524
  private ownsPool;
270
- private schema;
271
525
  private autoMigrate;
272
526
  private initialized;
273
527
  private config;
274
528
  constructor(config: PostgresStorageConfig);
275
529
  /**
276
- * Get a schema-scoped query builder.
277
- * All queries MUST use this instead of this.db directly to respect config.schema.
530
+ * Core-tables query builder consumed by `PostgresStorageCore`. Returns the
531
+ * schema-scoped Kysely view typed against the three core tables, which is
532
+ * what the shared CRUD methods need.
533
+ */
534
+ protected get qb(): CoreSchemaQueryBuilder;
535
+ /**
536
+ * Extended query builder for adapter-only tables (currently
537
+ * `stepflow_step_results`). Kept separate from `qb` so the base class stays
538
+ * typed against the core schema.
278
539
  */
279
- private get qb();
540
+ private get extQb();
541
+ /** Tag used by shared row mappers in `console.warn` messages. */
542
+ protected get warnLabel(): string;
543
+ /** Override of `PostgresStorageCore.ensureReady()` — enforces `initialize()`. */
544
+ protected ensureReady(): void;
280
545
  private ensureInitialized;
281
546
  /**
282
547
  * Initialize the storage adapter.
@@ -289,35 +554,28 @@ declare class PostgresStorageAdapter implements StorageAdapter {
289
554
  */
290
555
  close(): Promise<void>;
291
556
  /**
292
- * Create the workflow tables if they don't exist.
293
- */
294
- private createTables;
295
- /**
296
- * Create a new workflow run.
297
- * Supports both legacy and new CreateRunInput interfaces.
298
- */
299
- createRun(run: CreateRunInput | Omit<WorkflowRunRecord, 'id' | 'createdAt'>): Promise<WorkflowRunRecord>;
300
- getRun(runId: string): Promise<WorkflowRunRecord | null>;
301
- /**
302
- * Update a workflow run.
303
- * Supports both legacy Partial<WorkflowRunRecord> and new UpdateRunInput interfaces.
557
+ * Run a single migration step. Tolerates only known-benign Postgres errors
558
+ * (duplicate object codes that map to "already applied"), and rethrows
559
+ * everything else with a clear message so partial-state upgrades are not
560
+ * silently swallowed.
304
561
  */
305
- updateRun(runId: string, updates: UpdateRunInput | Partial<WorkflowRunRecord>): Promise<void>;
562
+ protected runMigration(label: string, fn: () => Promise<unknown>): Promise<void>;
306
563
  /**
307
- * List workflow runs with filtering and pagination.
564
+ * Postgres SQLSTATE codes that indicate a migration step has already been
565
+ * applied (object already exists). These are safe to ignore on idempotent
566
+ * re-runs; everything else must propagate.
567
+ *
568
+ * - 42701: duplicate_column
569
+ * - 42P07: duplicate_table
570
+ * - 42P06: duplicate_schema
571
+ * - 42710: duplicate_object (e.g., index, constraint)
308
572
  */
573
+ private static readonly BENIGN_MIGRATION_CODES;
574
+ private static isBenignMigrationError;
309
575
  /**
310
- * Apply common run filters to a Kysely query builder.
311
- * Used by both the data query and count query in listRuns to avoid duplication.
576
+ * Create the workflow tables if they don't exist.
312
577
  */
313
- private applyRunsFilters;
314
- listRuns(options?: ListRunsOptions): Promise<PaginatedResult<WorkflowRunRecord>>;
315
- createStep(step: Omit<WorkflowRunStepRecord, 'id'>): Promise<WorkflowRunStepRecord>;
316
- getStep(stepId: string): Promise<WorkflowRunStepRecord | null>;
317
- updateStep(stepId: string, updates: Partial<WorkflowRunStepRecord>): Promise<void>;
318
- getStepsForRun(runId: string): Promise<WorkflowRunStepRecord[]>;
319
- saveEvent(event: Omit<WorkflowEventRecord, 'id'>): Promise<void>;
320
- getEventsForRun(runId: string, options?: ListEventsOptions): Promise<WorkflowEventRecord[]>;
578
+ private createTables;
321
579
  /**
322
580
  * Execute a function within a database transaction.
323
581
  */
@@ -345,14 +603,7 @@ declare class PostgresStorageAdapter implements StorageAdapter {
345
603
  * @returns The dequeued run, or null if no runs are available
346
604
  */
347
605
  dequeueRun(workflowKinds?: string[]): Promise<WorkflowRunRecord | null>;
348
- private mapRunRow;
349
- /**
350
- * Map a database row to an extended workflow run record.
351
- */
352
- private mapExtendedRunRow;
353
606
  private mapStepResultRow;
354
- private mapStepRow;
355
- private mapEventRow;
356
607
  /**
357
608
  * Delete a workflow run by ID.
358
609
  * Also deletes associated steps and events (via CASCADE).
@@ -398,5 +649,20 @@ declare class PostgresStorageAdapter implements StorageAdapter {
398
649
  events: number;
399
650
  }>;
400
651
  }
652
+ /**
653
+ * A StorageAdapter wrapper for use within transactions.
654
+ *
655
+ * Used internally by `PostgresStorageAdapter.transaction()`. All CRUD methods
656
+ * are inherited from `PostgresStorageCore`; this class only binds the
657
+ * transaction-scoped query builder.
658
+ *
659
+ * Exported for regression testing; not part of the public API.
660
+ */
661
+ declare class PostgresTransactionAdapter extends PostgresStorageCore {
662
+ private readonly _qb;
663
+ constructor(trx: Kysely<StepflowDatabase>, schema: string, logger?: Logger);
664
+ protected get qb(): CoreSchemaQueryBuilder;
665
+ protected get warnLabel(): string;
666
+ }
401
667
 
402
- export { CreateRunInput, ListEventsOptions, ListRunsOptions, MemoryStorageAdapter, PaginatedResult, PostgresStorageAdapter as PostgresStorage, PostgresStorageAdapter, type PostgresStorageConfig, type PostgresStorageConfig as PostgresStorageOptions, SQLiteStorageAdapter, type SQLiteStorageConfig, StepResult, StorageAdapter, UpdateRunInput, WorkflowEventRecord, WorkflowRunRecord, WorkflowRunStepRecord };
668
+ export { CreateRunInput, ListEventsOptions, ListRunsOptions, MemoryStorageAdapter, PaginatedResult, PostgresStorageAdapter as PostgresStorage, PostgresStorageAdapter, type PostgresStorageConfig, type PostgresStorageConfig as PostgresStorageOptions, PostgresTransactionAdapter, SQLiteStorageAdapter, type SQLiteStorageConfig, StepResult, StorageAdapter, UpdateRunInput, WorkflowEventRecord, WorkflowRunRecord, WorkflowRunStepRecord };
@@ -1,13 +1,15 @@
1
1
  import {
2
2
  MemoryStorageAdapter,
3
3
  PostgresStorageAdapter,
4
+ PostgresTransactionAdapter,
4
5
  SQLiteStorageAdapter
5
- } from "../chunk-4VOKMGP6.js";
6
- import "../chunk-ML35PIHX.js";
6
+ } from "../chunk-5XJBAPXF.js";
7
+ import "../chunk-JPXPB7U5.js";
7
8
  export {
8
9
  MemoryStorageAdapter,
9
10
  PostgresStorageAdapter as PostgresStorage,
10
11
  PostgresStorageAdapter,
12
+ PostgresTransactionAdapter,
11
13
  SQLiteStorageAdapter
12
14
  };
13
15
  //# sourceMappingURL=index.js.map
@@ -1244,6 +1244,7 @@ import {
1244
1244
  StepTimeoutError,
1245
1245
  WorkflowCanceledError,
1246
1246
  WorkflowTimeoutError,
1247
+ WaitForRunTimeoutError,
1247
1248
  } from '@multiplier-labs/stepflow';
1248
1249
  ```
1249
1250
 
@@ -1257,6 +1258,7 @@ import {
1257
1258
  | `StepTimeoutError` | `STEP_TIMEOUT` | `(stepKey, timeoutMs)` |
1258
1259
  | `WorkflowCanceledError` | `WORKFLOW_CANCELED` | `(runId)` |
1259
1260
  | `WorkflowTimeoutError` | `WORKFLOW_TIMEOUT` | `(runId, timeoutMs)` |
1261
+ | `WaitForRunTimeoutError` | `WAIT_FOR_RUN_TIMEOUT` | `(runId, timeoutMs)` |
1260
1262
 
1261
1263
  ### WorkflowError Record
1262
1264
 
@@ -1422,7 +1424,7 @@ import {
1422
1424
  WorkflowEngineError, WorkflowNotFoundError,
1423
1425
  WorkflowAlreadyRegisteredError, RunNotFoundError,
1424
1426
  StepError, StepTimeoutError,
1425
- WorkflowCanceledError, WorkflowTimeoutError,
1427
+ WorkflowCanceledError, WorkflowTimeoutError, WaitForRunTimeoutError,
1426
1428
  ConsoleLogger, SilentLogger, createScopedLogger,
1427
1429
  generateId, sleep, withRetry, calculateRetryDelay,
1428
1430
  DEFAULT_RETRY_OPTIONS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplier-labs/stepflow",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "A durable, type-safe workflow orchestration engine for Node.js with SQLite persistence, scheduling, and real-time events",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "peerDependencies": {
59
59
  "better-sqlite3": ">=11.0.0",
60
- "kysely": ">=0.28.14",
60
+ "kysely": ">=0.28.16",
61
61
  "pg": ">=8.13.0"
62
62
  },
63
63
  "peerDependenciesMeta": {
@@ -77,10 +77,13 @@
77
77
  "@types/pg": "^8.18.0",
78
78
  "@vitest/coverage-v8": "^4.1.4",
79
79
  "better-sqlite3": "^12.8.0",
80
- "kysely": "^0.28.14",
80
+ "kysely": "^0.28.16",
81
81
  "pg": "^8.20.0",
82
- "tsup": "^8.3.5",
82
+ "tsup": "^8.5.1",
83
83
  "typescript": "^5.7.2",
84
84
  "vitest": "^4.1.4"
85
+ },
86
+ "overrides": {
87
+ "postcss": ">=8.5.10"
85
88
  }
86
89
  }