@multiplier-labs/stepflow 0.3.1 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/.codekin/reports/code-review/2026-04-11_code-review-daily.md +146 -0
  2. package/.codekin/reports/code-review/2026-04-13_code-review-daily.md +74 -0
  3. package/.codekin/reports/comments/2026-04-04_comment-assessment.md +179 -0
  4. package/.codekin/reports/comments/2026-04-11_comment-assessment.md +161 -0
  5. package/.codekin/reports/comments/2026-04-14_comment-assessment.md +34 -0
  6. package/.codekin/reports/complexity/2026-04-03_complexity-report.md +152 -0
  7. package/.codekin/reports/complexity/2026-04-11_complexity-report.md +152 -0
  8. package/.codekin/reports/complexity/2026-04-14_complexity-report.md +34 -0
  9. package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +94 -0
  10. package/.codekin/reports/dependencies/2026-04-11_dependency-health.md +93 -0
  11. package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +10 -0
  12. package/.codekin/reports/docs-audit/2026-04-11_docs-audit.md +138 -0
  13. package/.codekin/reports/docs-audit/2026-04-13_docs-audit.md +121 -0
  14. package/.codekin/reports/security/2026-04-03_security-audit.md +208 -0
  15. package/.codekin/reports/security/2026-04-11_security-audit.md +297 -0
  16. package/.github/CODEOWNERS +15 -0
  17. package/.github/dependabot.yml +23 -0
  18. package/.github/workflows/ci.yml +38 -0
  19. package/.github/workflows/publish.yml +17 -5
  20. package/.github/workflows/security.yml +29 -0
  21. package/CHANGELOG.md +34 -0
  22. package/README.md +24 -0
  23. package/SECURITY.md +40 -0
  24. package/dist/{chunk-FQCT3I2A.js → chunk-5XJBAPXF.js} +527 -551
  25. package/dist/chunk-5XJBAPXF.js.map +1 -0
  26. package/dist/{chunk-U5QGN3RT.js → chunk-FMPJBK2N.js} +67 -55
  27. package/dist/chunk-FMPJBK2N.js.map +1 -0
  28. package/dist/{chunk-GXY66V7Q.js → chunk-JPXPB7U5.js} +52 -2
  29. package/dist/chunk-JPXPB7U5.js.map +1 -0
  30. package/dist/{chunk-XCF4PLN7.js → chunk-U3PCVK6O.js} +276 -61
  31. package/dist/chunk-U3PCVK6O.js.map +1 -0
  32. package/dist/events/index.d.ts +60 -7
  33. package/dist/events/index.js +1 -1
  34. package/dist/{index-CPfGsmrp.d.ts → index-Cd0IZ40L.d.ts} +56 -17
  35. package/dist/index.d.ts +15 -23
  36. package/dist/index.js +784 -547
  37. package/dist/index.js.map +1 -1
  38. package/dist/scheduler/index.d.ts +4 -4
  39. package/dist/scheduler/index.js +2 -2
  40. package/dist/storage/index.d.ts +322 -56
  41. package/dist/storage/index.js +4 -2
  42. package/dist/{types-BlwFEGzj.d.ts → types-DSEjiLMj.d.ts} +16 -15
  43. package/dist/{types-DzkEqnQJ.d.ts → types-DZUdWmCc.d.ts} +2 -2
  44. package/dist/{types-BVShq7zT.d.ts → types-K5Gjk3H_.d.ts} +4 -4
  45. package/docs/stepflow-api-reference.md +5 -4
  46. package/package.json +12 -8
  47. package/src/core/engine/concurrency.ts +229 -0
  48. package/src/core/engine/lifecycle.ts +176 -0
  49. package/src/core/engine.test.ts +536 -522
  50. package/src/core/engine.ts +116 -235
  51. package/src/core/index.ts +3 -3
  52. package/src/core/orchestrator/persistence.ts +165 -0
  53. package/src/core/orchestrator/plan-resolution.ts +140 -0
  54. package/src/core/orchestrator/state-transitions.ts +109 -0
  55. package/src/core/orchestrator/step-runner.ts +427 -0
  56. package/src/core/orchestrator.ts +57 -395
  57. package/src/core/types.ts +7 -29
  58. package/src/events/index.ts +4 -4
  59. package/src/events/memory.test.ts +39 -41
  60. package/src/events/memory.ts +7 -10
  61. package/src/events/socketio.test.ts +112 -162
  62. package/src/events/socketio.ts +20 -27
  63. package/src/events/types.ts +16 -19
  64. package/src/events/webhook.test.ts +489 -235
  65. package/src/events/webhook.ts +355 -139
  66. package/src/index.ts +19 -19
  67. package/src/planning/evaluate-condition.test.ts +153 -0
  68. package/src/planning/evaluate-condition.ts +105 -0
  69. package/src/planning/index.ts +6 -3
  70. package/src/planning/planner.ts +45 -152
  71. package/src/planning/planning.test.ts +303 -366
  72. package/src/planning/registry.test.ts +204 -251
  73. package/src/planning/registry.ts +20 -106
  74. package/src/planning/types.ts +23 -27
  75. package/src/scheduler/cron.test.ts +149 -150
  76. package/src/scheduler/cron.ts +58 -69
  77. package/src/scheduler/index.ts +4 -14
  78. package/src/scheduler/postgres-persistence.ts +89 -105
  79. package/src/scheduler/sqlite-persistence.test.ts +96 -96
  80. package/src/scheduler/sqlite-persistence.ts +40 -22
  81. package/src/scheduler/types.ts +4 -9
  82. package/src/storage/index.ts +5 -5
  83. package/src/storage/memory.test.ts +133 -183
  84. package/src/storage/memory.ts +21 -42
  85. package/src/storage/postgres-core.ts +550 -0
  86. package/src/storage/postgres-migration.test.ts +69 -0
  87. package/src/storage/postgres-transaction-pagination.test.ts +107 -0
  88. package/src/storage/postgres.ts +222 -937
  89. package/src/storage/sqlite-core.ts +151 -0
  90. package/src/storage/sqlite.test.ts +330 -281
  91. package/src/storage/sqlite.ts +225 -170
  92. package/src/storage/types.ts +33 -58
  93. package/src/utils/errors.test.ts +78 -80
  94. package/src/utils/errors.ts +27 -57
  95. package/src/utils/id.ts +3 -3
  96. package/src/utils/index.ts +6 -4
  97. package/src/utils/logger.test.ts +52 -58
  98. package/src/utils/logger.ts +4 -4
  99. package/src/utils/pinned-fetch.test.ts +84 -0
  100. package/src/utils/pinned-fetch.ts +118 -0
  101. package/src/utils/postgres-deps.ts +20 -4
  102. package/src/utils/retry.test.ts +47 -67
  103. package/src/utils/retry.ts +9 -9
  104. package/src/utils/safe-json.test.ts +166 -0
  105. package/src/utils/safe-json.ts +107 -0
  106. package/src/utils/webhook-signature.test.ts +111 -0
  107. package/src/utils/webhook-signature.ts +74 -0
  108. package/dist/chunk-FQCT3I2A.js.map +0 -1
  109. package/dist/chunk-GXY66V7Q.js.map +0 -1
  110. package/dist/chunk-U5QGN3RT.js.map +0 -1
  111. package/dist/chunk-XCF4PLN7.js.map +0 -1
  112. package/src/scheduler/postgres-persistence.test.ts +0 -906
@@ -1,96 +1,94 @@
1
- import { describe, it, expect, beforeEach, vi } from "vitest";
2
- import { MemoryEventTransport } from "./memory";
3
- import type { WorkflowEvent, WorkflowEventType } from "./types";
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { MemoryEventTransport } from './memory';
3
+ import type { WorkflowEvent, WorkflowEventType } from './types';
4
4
 
5
- function createTestEvent(
6
- overrides: Partial<WorkflowEvent> = {},
7
- ): WorkflowEvent {
5
+ function createTestEvent(overrides: Partial<WorkflowEvent> = {}): WorkflowEvent {
8
6
  return {
9
- eventType: "run.started",
10
- runId: "run-123",
11
- kind: "test.workflow",
12
- timestamp: new Date("2024-01-01T12:00:00Z"),
7
+ eventType: 'run.started',
8
+ runId: 'run-123',
9
+ kind: 'test.workflow',
10
+ timestamp: new Date('2024-01-01T12:00:00Z'),
13
11
  ...overrides,
14
12
  };
15
13
  }
16
14
 
17
- describe("MemoryEventTransport", () => {
15
+ describe('MemoryEventTransport', () => {
18
16
  let transport: MemoryEventTransport;
19
17
 
20
18
  beforeEach(() => {
21
19
  transport = new MemoryEventTransport();
22
20
  });
23
21
 
24
- describe("subscribeToType", () => {
25
- it("should call callback when event of matching type is emitted", () => {
22
+ describe('subscribeToType', () => {
23
+ it('should call callback when event of matching type is emitted', () => {
26
24
  const callback = vi.fn();
27
- transport.subscribeToType("run.completed", callback);
25
+ transport.subscribeToType('run.completed', callback);
28
26
 
29
- const event = createTestEvent({ eventType: "run.completed" });
27
+ const event = createTestEvent({ eventType: 'run.completed' });
30
28
  transport.emit(event);
31
29
 
32
30
  expect(callback).toHaveBeenCalledWith(event);
33
31
  });
34
32
 
35
- it("should not call callback for events of a different type", () => {
33
+ it('should not call callback for events of a different type', () => {
36
34
  const callback = vi.fn();
37
- transport.subscribeToType("run.completed", callback);
35
+ transport.subscribeToType('run.completed', callback);
38
36
 
39
- transport.emit(createTestEvent({ eventType: "run.started" }));
37
+ transport.emit(createTestEvent({ eventType: 'run.started' }));
40
38
 
41
39
  expect(callback).not.toHaveBeenCalled();
42
40
  });
43
41
 
44
- it("should unsubscribe when calling the returned function", () => {
42
+ it('should unsubscribe when calling the returned function', () => {
45
43
  const callback = vi.fn();
46
- const unsubscribe = transport.subscribeToType("run.completed", callback);
44
+ const unsubscribe = transport.subscribeToType('run.completed', callback);
47
45
 
48
46
  // Emit once before unsubscribe
49
- transport.emit(createTestEvent({ eventType: "run.completed" }));
47
+ transport.emit(createTestEvent({ eventType: 'run.completed' }));
50
48
  expect(callback).toHaveBeenCalledTimes(1);
51
49
 
52
50
  unsubscribe();
53
51
 
54
52
  // Emit again after unsubscribe
55
- transport.emit(createTestEvent({ eventType: "run.completed" }));
53
+ transport.emit(createTestEvent({ eventType: 'run.completed' }));
56
54
  expect(callback).toHaveBeenCalledTimes(1);
57
55
  });
58
56
 
59
- it("should support multiple type subscriptions independently", () => {
57
+ it('should support multiple type subscriptions independently', () => {
60
58
  const completedCb = vi.fn();
61
59
  const failedCb = vi.fn();
62
- transport.subscribeToType("run.completed", completedCb);
63
- transport.subscribeToType("run.failed", failedCb);
60
+ transport.subscribeToType('run.completed', completedCb);
61
+ transport.subscribeToType('run.failed', failedCb);
64
62
 
65
- transport.emit(createTestEvent({ eventType: "run.completed" }));
63
+ transport.emit(createTestEvent({ eventType: 'run.completed' }));
66
64
 
67
65
  expect(completedCb).toHaveBeenCalledTimes(1);
68
66
  expect(failedCb).not.toHaveBeenCalled();
69
67
  });
70
68
  });
71
69
 
72
- describe("getListenerCount", () => {
73
- it("should return 0 when no listeners are registered", () => {
70
+ describe('getListenerCount', () => {
71
+ it('should return 0 when no listeners are registered', () => {
74
72
  expect(transport.getListenerCount()).toBe(0);
75
73
  });
76
74
 
77
- it("should return global listener count when called without channel", () => {
75
+ it('should return global listener count when called without channel', () => {
78
76
  transport.subscribeAll(vi.fn());
79
77
  transport.subscribeAll(vi.fn());
80
78
 
81
79
  expect(transport.getListenerCount()).toBe(2);
82
80
  });
83
81
 
84
- it("should return channel-specific listener count when called with channel", () => {
85
- transport.subscribe("run-1", vi.fn());
86
- transport.subscribe("run-1", vi.fn());
87
- transport.subscribe("run-2", vi.fn());
82
+ it('should return channel-specific listener count when called with channel', () => {
83
+ transport.subscribe('run-1', vi.fn());
84
+ transport.subscribe('run-1', vi.fn());
85
+ transport.subscribe('run-2', vi.fn());
88
86
 
89
- expect(transport.getListenerCount("run:run-1")).toBe(2);
90
- expect(transport.getListenerCount("run:run-2")).toBe(1);
87
+ expect(transport.getListenerCount('run:run-1')).toBe(2);
88
+ expect(transport.getListenerCount('run:run-2')).toBe(1);
91
89
  });
92
90
 
93
- it("should update count after subscribe and unsubscribe", () => {
91
+ it('should update count after subscribe and unsubscribe', () => {
94
92
  const unsub = transport.subscribeAll(vi.fn());
95
93
  expect(transport.getListenerCount()).toBe(1);
96
94
 
@@ -98,12 +96,12 @@ describe("MemoryEventTransport", () => {
98
96
  expect(transport.getListenerCount()).toBe(0);
99
97
  });
100
98
 
101
- it("should return count for type-based channels", () => {
102
- transport.subscribeToType("step.completed", vi.fn());
103
- transport.subscribeToType("step.completed", vi.fn());
99
+ it('should return count for type-based channels', () => {
100
+ transport.subscribeToType('step.completed', vi.fn());
101
+ transport.subscribeToType('step.completed', vi.fn());
104
102
 
105
- expect(transport.getListenerCount("type:step.completed")).toBe(2);
106
- expect(transport.getListenerCount("type:step.failed")).toBe(0);
103
+ expect(transport.getListenerCount('type:step.completed')).toBe(2);
104
+ expect(transport.getListenerCount('type:step.failed')).toBe(0);
107
105
  });
108
106
  });
109
107
  });
@@ -6,14 +6,14 @@
6
6
  * all state in-process with no persistence or multi-process safety guarantees.
7
7
  */
8
8
 
9
- import { EventEmitter } from "events";
9
+ import { EventEmitter } from 'events';
10
10
  import type {
11
11
  EventTransport,
12
12
  WorkflowEvent,
13
13
  WorkflowEventType,
14
14
  EventCallback,
15
15
  Unsubscribe,
16
- } from "./types";
16
+ } from './types';
17
17
 
18
18
  /**
19
19
  * In-memory event transport implementation using EventEmitter.
@@ -32,7 +32,7 @@ export class MemoryEventTransport implements EventTransport {
32
32
  */
33
33
  emit(event: WorkflowEvent): void {
34
34
  // Emit to global subscribers
35
- this.emitter.emit("event", event);
35
+ this.emitter.emit('event', event);
36
36
 
37
37
  // Emit to run-specific subscribers
38
38
  this.emitter.emit(`run:${event.runId}`, event);
@@ -56,19 +56,16 @@ export class MemoryEventTransport implements EventTransport {
56
56
  * Subscribe to all events.
57
57
  */
58
58
  subscribeAll(callback: EventCallback): Unsubscribe {
59
- this.emitter.on("event", callback);
59
+ this.emitter.on('event', callback);
60
60
  return () => {
61
- this.emitter.off("event", callback);
61
+ this.emitter.off('event', callback);
62
62
  };
63
63
  }
64
64
 
65
65
  /**
66
66
  * Subscribe to events of a specific type.
67
67
  */
68
- subscribeToType(
69
- eventType: WorkflowEventType,
70
- callback: EventCallback,
71
- ): Unsubscribe {
68
+ subscribeToType(eventType: WorkflowEventType, callback: EventCallback): Unsubscribe {
72
69
  const channel = `type:${eventType}`;
73
70
  this.emitter.on(channel, callback);
74
71
  return () => {
@@ -90,6 +87,6 @@ export class MemoryEventTransport implements EventTransport {
90
87
  if (channel) {
91
88
  return this.emitter.listenerCount(channel);
92
89
  }
93
- return this.emitter.listenerCount("event");
90
+ return this.emitter.listenerCount('event');
94
91
  }
95
92
  }