@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.
- package/.codekin/reports/code-review/2026-04-11_code-review-daily.md +146 -0
- package/.codekin/reports/code-review/2026-04-13_code-review-daily.md +74 -0
- package/.codekin/reports/comments/2026-04-04_comment-assessment.md +179 -0
- package/.codekin/reports/comments/2026-04-11_comment-assessment.md +161 -0
- package/.codekin/reports/comments/2026-04-14_comment-assessment.md +34 -0
- package/.codekin/reports/complexity/2026-04-03_complexity-report.md +152 -0
- package/.codekin/reports/complexity/2026-04-11_complexity-report.md +152 -0
- package/.codekin/reports/complexity/2026-04-14_complexity-report.md +34 -0
- package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +94 -0
- package/.codekin/reports/dependencies/2026-04-11_dependency-health.md +93 -0
- package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +10 -0
- package/.codekin/reports/docs-audit/2026-04-11_docs-audit.md +138 -0
- package/.codekin/reports/docs-audit/2026-04-13_docs-audit.md +121 -0
- package/.codekin/reports/security/2026-04-03_security-audit.md +208 -0
- package/.codekin/reports/security/2026-04-11_security-audit.md +297 -0
- package/.github/CODEOWNERS +15 -0
- package/.github/dependabot.yml +23 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/publish.yml +17 -5
- package/.github/workflows/security.yml +29 -0
- package/CHANGELOG.md +34 -0
- package/README.md +24 -0
- package/SECURITY.md +40 -0
- package/dist/{chunk-FQCT3I2A.js → chunk-5XJBAPXF.js} +527 -551
- package/dist/chunk-5XJBAPXF.js.map +1 -0
- package/dist/{chunk-U5QGN3RT.js → chunk-FMPJBK2N.js} +67 -55
- package/dist/chunk-FMPJBK2N.js.map +1 -0
- package/dist/{chunk-GXY66V7Q.js → chunk-JPXPB7U5.js} +52 -2
- package/dist/chunk-JPXPB7U5.js.map +1 -0
- package/dist/{chunk-XCF4PLN7.js → chunk-U3PCVK6O.js} +276 -61
- package/dist/chunk-U3PCVK6O.js.map +1 -0
- package/dist/events/index.d.ts +60 -7
- package/dist/events/index.js +1 -1
- package/dist/{index-CPfGsmrp.d.ts → index-Cd0IZ40L.d.ts} +56 -17
- package/dist/index.d.ts +15 -23
- package/dist/index.js +784 -547
- package/dist/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +4 -4
- package/dist/scheduler/index.js +2 -2
- package/dist/storage/index.d.ts +322 -56
- package/dist/storage/index.js +4 -2
- package/dist/{types-BlwFEGzj.d.ts → types-DSEjiLMj.d.ts} +16 -15
- package/dist/{types-DzkEqnQJ.d.ts → types-DZUdWmCc.d.ts} +2 -2
- package/dist/{types-BVShq7zT.d.ts → types-K5Gjk3H_.d.ts} +4 -4
- package/docs/stepflow-api-reference.md +5 -4
- package/package.json +12 -8
- package/src/core/engine/concurrency.ts +229 -0
- package/src/core/engine/lifecycle.ts +176 -0
- package/src/core/engine.test.ts +536 -522
- package/src/core/engine.ts +116 -235
- package/src/core/index.ts +3 -3
- package/src/core/orchestrator/persistence.ts +165 -0
- package/src/core/orchestrator/plan-resolution.ts +140 -0
- package/src/core/orchestrator/state-transitions.ts +109 -0
- package/src/core/orchestrator/step-runner.ts +427 -0
- package/src/core/orchestrator.ts +57 -395
- package/src/core/types.ts +7 -29
- package/src/events/index.ts +4 -4
- package/src/events/memory.test.ts +39 -41
- package/src/events/memory.ts +7 -10
- package/src/events/socketio.test.ts +112 -162
- package/src/events/socketio.ts +20 -27
- package/src/events/types.ts +16 -19
- package/src/events/webhook.test.ts +489 -235
- package/src/events/webhook.ts +355 -139
- package/src/index.ts +19 -19
- package/src/planning/evaluate-condition.test.ts +153 -0
- package/src/planning/evaluate-condition.ts +105 -0
- package/src/planning/index.ts +6 -3
- package/src/planning/planner.ts +45 -152
- package/src/planning/planning.test.ts +303 -366
- package/src/planning/registry.test.ts +204 -251
- package/src/planning/registry.ts +20 -106
- package/src/planning/types.ts +23 -27
- package/src/scheduler/cron.test.ts +149 -150
- package/src/scheduler/cron.ts +58 -69
- package/src/scheduler/index.ts +4 -14
- package/src/scheduler/postgres-persistence.ts +89 -105
- package/src/scheduler/sqlite-persistence.test.ts +96 -96
- package/src/scheduler/sqlite-persistence.ts +40 -22
- package/src/scheduler/types.ts +4 -9
- package/src/storage/index.ts +5 -5
- package/src/storage/memory.test.ts +133 -183
- package/src/storage/memory.ts +21 -42
- package/src/storage/postgres-core.ts +550 -0
- package/src/storage/postgres-migration.test.ts +69 -0
- package/src/storage/postgres-transaction-pagination.test.ts +107 -0
- package/src/storage/postgres.ts +222 -937
- package/src/storage/sqlite-core.ts +151 -0
- package/src/storage/sqlite.test.ts +330 -281
- package/src/storage/sqlite.ts +225 -170
- package/src/storage/types.ts +33 -58
- package/src/utils/errors.test.ts +78 -80
- package/src/utils/errors.ts +27 -57
- package/src/utils/id.ts +3 -3
- package/src/utils/index.ts +6 -4
- package/src/utils/logger.test.ts +52 -58
- package/src/utils/logger.ts +4 -4
- package/src/utils/pinned-fetch.test.ts +84 -0
- package/src/utils/pinned-fetch.ts +118 -0
- package/src/utils/postgres-deps.ts +20 -4
- package/src/utils/retry.test.ts +47 -67
- package/src/utils/retry.ts +9 -9
- package/src/utils/safe-json.test.ts +166 -0
- package/src/utils/safe-json.ts +107 -0
- package/src/utils/webhook-signature.test.ts +111 -0
- package/src/utils/webhook-signature.ts +74 -0
- package/dist/chunk-FQCT3I2A.js.map +0 -1
- package/dist/chunk-GXY66V7Q.js.map +0 -1
- package/dist/chunk-U5QGN3RT.js.map +0 -1
- package/dist/chunk-XCF4PLN7.js.map +0 -1
- package/src/scheduler/postgres-persistence.test.ts +0 -906
|
@@ -1,96 +1,94 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from
|
|
2
|
-
import { MemoryEventTransport } from
|
|
3
|
-
import type { WorkflowEvent, WorkflowEventType } from
|
|
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:
|
|
10
|
-
runId:
|
|
11
|
-
kind:
|
|
12
|
-
timestamp: new Date(
|
|
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(
|
|
15
|
+
describe('MemoryEventTransport', () => {
|
|
18
16
|
let transport: MemoryEventTransport;
|
|
19
17
|
|
|
20
18
|
beforeEach(() => {
|
|
21
19
|
transport = new MemoryEventTransport();
|
|
22
20
|
});
|
|
23
21
|
|
|
24
|
-
describe(
|
|
25
|
-
it(
|
|
22
|
+
describe('subscribeToType', () => {
|
|
23
|
+
it('should call callback when event of matching type is emitted', () => {
|
|
26
24
|
const callback = vi.fn();
|
|
27
|
-
transport.subscribeToType(
|
|
25
|
+
transport.subscribeToType('run.completed', callback);
|
|
28
26
|
|
|
29
|
-
const event = createTestEvent({ eventType:
|
|
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(
|
|
33
|
+
it('should not call callback for events of a different type', () => {
|
|
36
34
|
const callback = vi.fn();
|
|
37
|
-
transport.subscribeToType(
|
|
35
|
+
transport.subscribeToType('run.completed', callback);
|
|
38
36
|
|
|
39
|
-
transport.emit(createTestEvent({ eventType:
|
|
37
|
+
transport.emit(createTestEvent({ eventType: 'run.started' }));
|
|
40
38
|
|
|
41
39
|
expect(callback).not.toHaveBeenCalled();
|
|
42
40
|
});
|
|
43
41
|
|
|
44
|
-
it(
|
|
42
|
+
it('should unsubscribe when calling the returned function', () => {
|
|
45
43
|
const callback = vi.fn();
|
|
46
|
-
const unsubscribe = transport.subscribeToType(
|
|
44
|
+
const unsubscribe = transport.subscribeToType('run.completed', callback);
|
|
47
45
|
|
|
48
46
|
// Emit once before unsubscribe
|
|
49
|
-
transport.emit(createTestEvent({ eventType:
|
|
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:
|
|
53
|
+
transport.emit(createTestEvent({ eventType: 'run.completed' }));
|
|
56
54
|
expect(callback).toHaveBeenCalledTimes(1);
|
|
57
55
|
});
|
|
58
56
|
|
|
59
|
-
it(
|
|
57
|
+
it('should support multiple type subscriptions independently', () => {
|
|
60
58
|
const completedCb = vi.fn();
|
|
61
59
|
const failedCb = vi.fn();
|
|
62
|
-
transport.subscribeToType(
|
|
63
|
-
transport.subscribeToType(
|
|
60
|
+
transport.subscribeToType('run.completed', completedCb);
|
|
61
|
+
transport.subscribeToType('run.failed', failedCb);
|
|
64
62
|
|
|
65
|
-
transport.emit(createTestEvent({ eventType:
|
|
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(
|
|
73
|
-
it(
|
|
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(
|
|
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(
|
|
85
|
-
transport.subscribe(
|
|
86
|
-
transport.subscribe(
|
|
87
|
-
transport.subscribe(
|
|
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(
|
|
90
|
-
expect(transport.getListenerCount(
|
|
87
|
+
expect(transport.getListenerCount('run:run-1')).toBe(2);
|
|
88
|
+
expect(transport.getListenerCount('run:run-2')).toBe(1);
|
|
91
89
|
});
|
|
92
90
|
|
|
93
|
-
it(
|
|
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(
|
|
102
|
-
transport.subscribeToType(
|
|
103
|
-
transport.subscribeToType(
|
|
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(
|
|
106
|
-
expect(transport.getListenerCount(
|
|
103
|
+
expect(transport.getListenerCount('type:step.completed')).toBe(2);
|
|
104
|
+
expect(transport.getListenerCount('type:step.failed')).toBe(0);
|
|
107
105
|
});
|
|
108
106
|
});
|
|
109
107
|
});
|
package/src/events/memory.ts
CHANGED
|
@@ -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
|
|
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
|
|
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(
|
|
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(
|
|
59
|
+
this.emitter.on('event', callback);
|
|
60
60
|
return () => {
|
|
61
|
-
this.emitter.off(
|
|
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(
|
|
90
|
+
return this.emitter.listenerCount('event');
|
|
94
91
|
}
|
|
95
92
|
}
|