@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,128 +1,103 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
2
- import { MemoryStorageAdapter } from "./memory";
3
- import type { WorkflowRunRecord, WorkflowRunStepRecord } from "./types";
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { MemoryStorageAdapter } from './memory';
3
+ import type { WorkflowRunRecord, WorkflowRunStepRecord } from './types';
4
4
 
5
- describe("MemoryStorageAdapter", () => {
5
+ describe('MemoryStorageAdapter', () => {
6
6
  let storage: MemoryStorageAdapter;
7
7
 
8
8
  beforeEach(() => {
9
9
  storage = new MemoryStorageAdapter();
10
10
  });
11
11
 
12
- describe("createRun / getRun", () => {
13
- it("should create and retrieve a run", async () => {
12
+ describe('createRun / getRun', () => {
13
+ it('should create and retrieve a run', async () => {
14
14
  const run = await storage.createRun({
15
- kind: "test.workflow",
16
- status: "queued",
15
+ kind: 'test.workflow',
16
+ status: 'queued',
17
17
  input: { x: 1 },
18
- metadata: { userId: "u1" },
18
+ metadata: { userId: 'u1' },
19
19
  context: {},
20
20
  });
21
21
 
22
22
  expect(run.id).toBeDefined();
23
- expect(run.kind).toBe("test.workflow");
23
+ expect(run.kind).toBe('test.workflow');
24
24
  expect(run.createdAt).toBeInstanceOf(Date);
25
25
 
26
26
  const fetched = await storage.getRun(run.id);
27
27
  expect(fetched).toEqual(run);
28
28
  });
29
29
 
30
- it("should return null for non-existent run", async () => {
31
- const run = await storage.getRun("nonexistent");
30
+ it('should return null for non-existent run', async () => {
31
+ const run = await storage.getRun('nonexistent');
32
32
  expect(run).toBeNull();
33
33
  });
34
34
  });
35
35
 
36
- describe("updateRun", () => {
37
- it("should update run fields", async () => {
36
+ describe('updateRun', () => {
37
+ it('should update run fields', async () => {
38
38
  const run = await storage.createRun({
39
- kind: "test",
40
- status: "queued",
39
+ kind: 'test',
40
+ status: 'queued',
41
41
  input: {},
42
42
  metadata: {},
43
43
  context: {},
44
44
  });
45
45
 
46
46
  await storage.updateRun(run.id, {
47
- status: "running",
47
+ status: 'running',
48
48
  startedAt: new Date(),
49
49
  });
50
50
 
51
51
  const updated = await storage.getRun(run.id);
52
- expect(updated?.status).toBe("running");
52
+ expect(updated?.status).toBe('running');
53
53
  expect(updated?.startedAt).toBeInstanceOf(Date);
54
54
  });
55
55
 
56
- it("should do nothing for non-existent run", async () => {
56
+ it('should do nothing for non-existent run', async () => {
57
57
  await expect(
58
- storage.updateRun("nonexistent", { status: "failed" }),
58
+ storage.updateRun('nonexistent', { status: 'failed' })
59
59
  ).resolves.not.toThrow();
60
60
  });
61
61
  });
62
62
 
63
- describe("listRuns", () => {
63
+ describe('listRuns', () => {
64
64
  beforeEach(async () => {
65
- await storage.createRun({
66
- kind: "a",
67
- status: "queued",
68
- input: {},
69
- metadata: {},
70
- context: {},
71
- });
72
- await storage.createRun({
73
- kind: "a",
74
- status: "running",
75
- input: {},
76
- metadata: {},
77
- context: {},
78
- });
79
- await storage.createRun({
80
- kind: "b",
81
- status: "succeeded",
82
- input: {},
83
- metadata: {},
84
- context: {},
85
- });
86
- await storage.createRun({
87
- kind: "a",
88
- status: "failed",
89
- input: {},
90
- metadata: {},
91
- context: {},
92
- parentRunId: "parent-1",
93
- });
65
+ await storage.createRun({ kind: 'a', status: 'queued', input: {}, metadata: {}, context: {} });
66
+ await storage.createRun({ kind: 'a', status: 'running', input: {}, metadata: {}, context: {} });
67
+ await storage.createRun({ kind: 'b', status: 'succeeded', input: {}, metadata: {}, context: {} });
68
+ await storage.createRun({ kind: 'a', status: 'failed', input: {}, metadata: {}, context: {}, parentRunId: 'parent-1' });
94
69
  });
95
70
 
96
- it("should list all runs with default pagination", async () => {
71
+ it('should list all runs with default pagination', async () => {
97
72
  const result = await storage.listRuns();
98
73
  expect(result.items).toHaveLength(4);
99
74
  expect(result.total).toBe(4);
100
75
  });
101
76
 
102
- it("should filter by kind", async () => {
103
- const result = await storage.listRuns({ kind: "a" });
77
+ it('should filter by kind', async () => {
78
+ const result = await storage.listRuns({ kind: 'a' });
104
79
  expect(result.items).toHaveLength(3);
105
- expect(result.items.every((r) => r.kind === "a")).toBe(true);
80
+ expect(result.items.every(r => r.kind === 'a')).toBe(true);
106
81
  });
107
82
 
108
- it("should filter by single status", async () => {
109
- const result = await storage.listRuns({ status: "queued" });
83
+ it('should filter by single status', async () => {
84
+ const result = await storage.listRuns({ status: 'queued' });
110
85
  expect(result.items).toHaveLength(1);
111
- expect(result.items[0].status).toBe("queued");
86
+ expect(result.items[0].status).toBe('queued');
112
87
  });
113
88
 
114
- it("should filter by multiple statuses", async () => {
115
- const result = await storage.listRuns({ status: ["queued", "running"] });
89
+ it('should filter by multiple statuses', async () => {
90
+ const result = await storage.listRuns({ status: ['queued', 'running'] });
116
91
  expect(result.items).toHaveLength(2);
117
92
  });
118
93
 
119
- it("should filter by parentRunId", async () => {
120
- const result = await storage.listRuns({ parentRunId: "parent-1" });
94
+ it('should filter by parentRunId', async () => {
95
+ const result = await storage.listRuns({ parentRunId: 'parent-1' });
121
96
  expect(result.items).toHaveLength(1);
122
- expect(result.items[0].parentRunId).toBe("parent-1");
97
+ expect(result.items[0].parentRunId).toBe('parent-1');
123
98
  });
124
99
 
125
- it("should apply limit and offset", async () => {
100
+ it('should apply limit and offset', async () => {
126
101
  const result = await storage.listRuns({ limit: 2, offset: 1 });
127
102
  expect(result.items).toHaveLength(2);
128
103
  expect(result.total).toBe(4);
@@ -130,196 +105,188 @@ describe("MemoryStorageAdapter", () => {
130
105
  expect(result.offset).toBe(1);
131
106
  });
132
107
 
133
- it("should sort by createdAt ascending", async () => {
134
- const result = await storage.listRuns({
135
- orderBy: "createdAt",
136
- orderDirection: "asc",
137
- });
138
- const times = result.items.map((r) => r.createdAt.getTime());
108
+ it('should sort by createdAt ascending', async () => {
109
+ const result = await storage.listRuns({ orderBy: 'createdAt', orderDirection: 'asc' });
110
+ const times = result.items.map(r => r.createdAt.getTime());
139
111
  for (let i = 1; i < times.length; i++) {
140
112
  expect(times[i]).toBeGreaterThanOrEqual(times[i - 1]);
141
113
  }
142
114
  });
143
115
  });
144
116
 
145
- describe("createStep / getStep / updateStep", () => {
146
- it("should create and retrieve a step", async () => {
117
+ describe('createStep / getStep / updateStep', () => {
118
+ it('should create and retrieve a step', async () => {
147
119
  const step = await storage.createStep({
148
- runId: "run-1",
149
- stepKey: "step1",
150
- stepName: "Step 1",
151
- status: "running",
120
+ runId: 'run-1',
121
+ stepKey: 'step1',
122
+ stepName: 'Step 1',
123
+ status: 'running',
152
124
  attempt: 1,
153
125
  startedAt: new Date(),
154
126
  });
155
127
 
156
128
  expect(step.id).toBeDefined();
157
- expect(step.stepKey).toBe("step1");
129
+ expect(step.stepKey).toBe('step1');
158
130
 
159
131
  const fetched = await storage.getStep(step.id);
160
132
  expect(fetched).toEqual(step);
161
133
  });
162
134
 
163
- it("should return null for non-existent step", async () => {
164
- const step = await storage.getStep("nonexistent");
135
+ it('should return null for non-existent step', async () => {
136
+ const step = await storage.getStep('nonexistent');
165
137
  expect(step).toBeNull();
166
138
  });
167
139
 
168
- it("should update step fields", async () => {
140
+ it('should update step fields', async () => {
169
141
  const step = await storage.createStep({
170
- runId: "run-1",
171
- stepKey: "step1",
172
- stepName: "Step 1",
173
- status: "running",
142
+ runId: 'run-1',
143
+ stepKey: 'step1',
144
+ stepName: 'Step 1',
145
+ status: 'running',
174
146
  attempt: 1,
175
147
  });
176
148
 
177
149
  await storage.updateStep(step.id, {
178
- status: "succeeded",
179
- result: { output: "done" },
150
+ status: 'succeeded',
151
+ result: { output: 'done' },
180
152
  finishedAt: new Date(),
181
153
  });
182
154
 
183
155
  const updated = await storage.getStep(step.id);
184
- expect(updated?.status).toBe("succeeded");
185
- expect(updated?.result).toEqual({ output: "done" });
156
+ expect(updated?.status).toBe('succeeded');
157
+ expect(updated?.result).toEqual({ output: 'done' });
186
158
  });
187
159
 
188
- it("should do nothing when updating non-existent step", async () => {
160
+ it('should do nothing when updating non-existent step', async () => {
189
161
  await expect(
190
- storage.updateStep("nonexistent", { status: "failed" }),
162
+ storage.updateStep('nonexistent', { status: 'failed' })
191
163
  ).resolves.not.toThrow();
192
164
  });
193
165
  });
194
166
 
195
- describe("getStepsForRun", () => {
196
- it("should return steps for a specific run sorted by startedAt", async () => {
167
+ describe('getStepsForRun', () => {
168
+ it('should return steps for a specific run sorted by startedAt', async () => {
197
169
  const now = Date.now();
198
170
  await storage.createStep({
199
- runId: "run-1",
200
- stepKey: "b",
201
- stepName: "B",
202
- status: "succeeded",
171
+ runId: 'run-1',
172
+ stepKey: 'b',
173
+ stepName: 'B',
174
+ status: 'succeeded',
203
175
  attempt: 1,
204
176
  startedAt: new Date(now + 100),
205
177
  });
206
178
  await storage.createStep({
207
- runId: "run-1",
208
- stepKey: "a",
209
- stepName: "A",
210
- status: "succeeded",
179
+ runId: 'run-1',
180
+ stepKey: 'a',
181
+ stepName: 'A',
182
+ status: 'succeeded',
211
183
  attempt: 1,
212
184
  startedAt: new Date(now),
213
185
  });
214
186
  await storage.createStep({
215
- runId: "run-2",
216
- stepKey: "c",
217
- stepName: "C",
218
- status: "running",
187
+ runId: 'run-2',
188
+ stepKey: 'c',
189
+ stepName: 'C',
190
+ status: 'running',
219
191
  attempt: 1,
220
192
  });
221
193
 
222
- const steps = await storage.getStepsForRun("run-1");
194
+ const steps = await storage.getStepsForRun('run-1');
223
195
  expect(steps).toHaveLength(2);
224
- expect(steps[0].stepKey).toBe("a");
225
- expect(steps[1].stepKey).toBe("b");
196
+ expect(steps[0].stepKey).toBe('a');
197
+ expect(steps[1].stepKey).toBe('b');
226
198
  });
227
199
  });
228
200
 
229
- describe("saveEvent / getEventsForRun", () => {
230
- it("should save and retrieve events", async () => {
201
+ describe('saveEvent / getEventsForRun', () => {
202
+ it('should save and retrieve events', async () => {
231
203
  await storage.saveEvent({
232
- runId: "run-1",
233
- eventType: "step.started",
234
- level: "info",
204
+ runId: 'run-1',
205
+ eventType: 'step.started',
206
+ level: 'info',
235
207
  timestamp: new Date(),
236
208
  });
237
209
  await storage.saveEvent({
238
- runId: "run-1",
239
- stepKey: "step1",
240
- eventType: "step.completed",
241
- level: "info",
210
+ runId: 'run-1',
211
+ stepKey: 'step1',
212
+ eventType: 'step.completed',
213
+ level: 'info',
242
214
  timestamp: new Date(),
243
215
  });
244
216
  await storage.saveEvent({
245
- runId: "run-2",
246
- eventType: "run.started",
247
- level: "info",
217
+ runId: 'run-2',
218
+ eventType: 'run.started',
219
+ level: 'info',
248
220
  timestamp: new Date(),
249
221
  });
250
222
 
251
- const events = await storage.getEventsForRun("run-1");
223
+ const events = await storage.getEventsForRun('run-1');
252
224
  expect(events).toHaveLength(2);
253
225
  });
254
226
 
255
- it("should filter events by stepKey", async () => {
227
+ it('should filter events by stepKey', async () => {
256
228
  await storage.saveEvent({
257
- runId: "run-1",
258
- eventType: "run.started",
259
- level: "info",
229
+ runId: 'run-1',
230
+ eventType: 'run.started',
231
+ level: 'info',
260
232
  timestamp: new Date(),
261
233
  });
262
234
  await storage.saveEvent({
263
- runId: "run-1",
264
- stepKey: "step1",
265
- eventType: "step.started",
266
- level: "info",
235
+ runId: 'run-1',
236
+ stepKey: 'step1',
237
+ eventType: 'step.started',
238
+ level: 'info',
267
239
  timestamp: new Date(),
268
240
  });
269
241
 
270
- const events = await storage.getEventsForRun("run-1", {
271
- stepKey: "step1",
272
- });
242
+ const events = await storage.getEventsForRun('run-1', { stepKey: 'step1' });
273
243
  expect(events).toHaveLength(1);
274
- expect(events[0].stepKey).toBe("step1");
244
+ expect(events[0].stepKey).toBe('step1');
275
245
  });
276
246
 
277
- it("should filter events by level", async () => {
247
+ it('should filter events by level', async () => {
278
248
  await storage.saveEvent({
279
- runId: "run-1",
280
- eventType: "info-event",
281
- level: "info",
249
+ runId: 'run-1',
250
+ eventType: 'info-event',
251
+ level: 'info',
282
252
  timestamp: new Date(),
283
253
  });
284
254
  await storage.saveEvent({
285
- runId: "run-1",
286
- eventType: "error-event",
287
- level: "error",
255
+ runId: 'run-1',
256
+ eventType: 'error-event',
257
+ level: 'error',
288
258
  timestamp: new Date(),
289
259
  });
290
260
 
291
- const events = await storage.getEventsForRun("run-1", { level: "error" });
261
+ const events = await storage.getEventsForRun('run-1', { level: 'error' });
292
262
  expect(events).toHaveLength(1);
293
- expect(events[0].level).toBe("error");
263
+ expect(events[0].level).toBe('error');
294
264
  });
295
265
 
296
- it("should paginate events", async () => {
266
+ it('should paginate events', async () => {
297
267
  for (let i = 0; i < 5; i++) {
298
268
  await storage.saveEvent({
299
- runId: "run-1",
269
+ runId: 'run-1',
300
270
  eventType: `event-${i}`,
301
- level: "info",
271
+ level: 'info',
302
272
  timestamp: new Date(Date.now() + i),
303
273
  });
304
274
  }
305
275
 
306
- const events = await storage.getEventsForRun("run-1", {
307
- limit: 2,
308
- offset: 1,
309
- });
276
+ const events = await storage.getEventsForRun('run-1', { limit: 2, offset: 1 });
310
277
  expect(events).toHaveLength(2);
311
278
  });
312
279
  });
313
280
 
314
- describe("deleteOldRuns", () => {
315
- it("should delete runs older than the cutoff date", async () => {
316
- const oldDate = new Date("2020-01-01");
317
- const newDate = new Date("2025-01-01");
281
+ describe('deleteOldRuns', () => {
282
+ it('should delete runs older than the cutoff date', async () => {
283
+ const oldDate = new Date('2020-01-01');
284
+ const newDate = new Date('2025-01-01');
318
285
 
319
286
  // Create an old run with steps and events
320
287
  const oldRun = await storage.createRun({
321
- kind: "test",
322
- status: "succeeded",
288
+ kind: 'test',
289
+ status: 'succeeded',
323
290
  input: {},
324
291
  metadata: {},
325
292
  context: {},
@@ -329,28 +296,28 @@ describe("MemoryStorageAdapter", () => {
329
296
 
330
297
  await storage.createStep({
331
298
  runId: oldRun.id,
332
- stepKey: "s1",
333
- stepName: "S1",
334
- status: "succeeded",
299
+ stepKey: 's1',
300
+ stepName: 'S1',
301
+ status: 'succeeded',
335
302
  attempt: 1,
336
303
  });
337
304
  await storage.saveEvent({
338
305
  runId: oldRun.id,
339
- eventType: "run.completed",
340
- level: "info",
306
+ eventType: 'run.completed',
307
+ level: 'info',
341
308
  timestamp: new Date(),
342
309
  });
343
310
 
344
311
  // Create a recent run
345
312
  const newRun = await storage.createRun({
346
- kind: "test",
347
- status: "succeeded",
313
+ kind: 'test',
314
+ status: 'succeeded',
348
315
  input: {},
349
316
  metadata: {},
350
317
  context: {},
351
318
  });
352
319
 
353
- const deleted = await storage.deleteOldRuns(new Date("2024-01-01"));
320
+ const deleted = await storage.deleteOldRuns(new Date('2024-01-01'));
354
321
 
355
322
  expect(deleted).toBe(1);
356
323
  expect(await storage.getRun(oldRun.id)).toBeNull();
@@ -364,28 +331,11 @@ describe("MemoryStorageAdapter", () => {
364
331
  });
365
332
  });
366
333
 
367
- describe("clear / getStats", () => {
368
- it("should clear all data", async () => {
369
- await storage.createRun({
370
- kind: "a",
371
- status: "queued",
372
- input: {},
373
- metadata: {},
374
- context: {},
375
- });
376
- await storage.createStep({
377
- runId: "r",
378
- stepKey: "s",
379
- stepName: "S",
380
- status: "running",
381
- attempt: 1,
382
- });
383
- await storage.saveEvent({
384
- runId: "r",
385
- eventType: "e",
386
- level: "info",
387
- timestamp: new Date(),
388
- });
334
+ describe('clear / getStats', () => {
335
+ it('should clear all data', async () => {
336
+ await storage.createRun({ kind: 'a', status: 'queued', input: {}, metadata: {}, context: {} });
337
+ await storage.createStep({ runId: 'r', stepKey: 's', stepName: 'S', status: 'running', attempt: 1 });
338
+ await storage.saveEvent({ runId: 'r', eventType: 'e', level: 'info', timestamp: new Date() });
389
339
 
390
340
  expect(storage.getStats().runs).toBe(1);
391
341
  expect(storage.getStats().steps).toBe(1);