@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
@@ -3,12 +3,7 @@
3
3
  * Implement StorageAdapter to use your preferred database.
4
4
  */
5
5
 
6
- import type {
7
- WorkflowKind,
8
- RunStatus,
9
- StepStatus,
10
- WorkflowError,
11
- } from "../core/types";
6
+ import type { WorkflowKind, RunStatus, StepStatus, WorkflowError } from '../core/types';
12
7
 
13
8
  // ============================================================================
14
9
  // Extended Status Types (New)
@@ -19,25 +14,25 @@ import type {
19
14
  * Adds 'pending' and 'timeout' to the core statuses.
20
15
  */
21
16
  export type ExtendedRunStatus =
22
- | "pending"
23
- | "queued"
24
- | "running"
25
- | "succeeded"
26
- | "failed"
27
- | "canceled"
28
- | "timeout";
17
+ | 'pending'
18
+ | 'queued'
19
+ | 'running'
20
+ | 'succeeded'
21
+ | 'failed'
22
+ | 'canceled'
23
+ | 'timeout';
29
24
 
30
25
  /**
31
26
  * Extended status of a workflow step.
32
27
  * Uses 'completed' instead of 'succeeded' for consistency.
33
28
  */
34
29
  export type ExtendedStepStatus =
35
- | "pending"
36
- | "running"
37
- | "completed"
38
- | "failed"
39
- | "skipped"
40
- | "canceled";
30
+ | 'pending'
31
+ | 'running'
32
+ | 'completed'
33
+ | 'failed'
34
+ | 'skipped'
35
+ | 'canceled';
41
36
 
42
37
  // ============================================================================
43
38
  // Legacy Record Types (Backward Compatible)
@@ -53,6 +48,7 @@ export interface WorkflowRunRecord {
53
48
  parentRunId?: string;
54
49
  input: Record<string, unknown>;
55
50
  context: Record<string, unknown>;
51
+ completedSteps?: string[];
56
52
  output?: Record<string, unknown>;
57
53
  error?: { code: string; message: string };
58
54
  metadata?: Record<string, unknown>;
@@ -87,7 +83,7 @@ export interface WorkflowEventRecord {
87
83
  runId: string;
88
84
  stepKey?: string;
89
85
  eventType: string;
90
- level: "info" | "warn" | "error";
86
+ level: 'info' | 'warn' | 'error';
91
87
  payload?: unknown;
92
88
  timestamp: Date;
93
89
  }
@@ -189,8 +185,8 @@ export interface ListRunsOptions {
189
185
  parentRunId?: string;
190
186
  limit?: number;
191
187
  offset?: number;
192
- orderBy?: "createdAt" | "startedAt" | "finishedAt";
193
- orderDirection?: "asc" | "desc";
188
+ orderBy?: 'createdAt' | 'startedAt' | 'finishedAt';
189
+ orderDirection?: 'asc' | 'desc';
194
190
  }
195
191
 
196
192
  /**
@@ -201,8 +197,8 @@ export interface ExtendedListRunsOptions {
201
197
  status?: ExtendedRunStatus | ExtendedRunStatus[];
202
198
  limit?: number;
203
199
  offset?: number;
204
- orderBy?: "createdAt" | "startedAt" | "finishedAt";
205
- orderDir?: "asc" | "desc";
200
+ orderBy?: 'createdAt' | 'startedAt' | 'finishedAt';
201
+ orderDir?: 'asc' | 'desc';
206
202
  }
207
203
 
208
204
  /**
@@ -210,7 +206,7 @@ export interface ExtendedListRunsOptions {
210
206
  */
211
207
  export interface ListEventsOptions {
212
208
  stepKey?: string;
213
- level?: "info" | "warn" | "error";
209
+ level?: 'info' | 'warn' | 'error';
214
210
  limit?: number;
215
211
  offset?: number;
216
212
  }
@@ -238,30 +234,21 @@ export interface WorkflowStorage {
238
234
  createRun(run: CreateRunInput): Promise<ExtendedWorkflowRunRecord>;
239
235
  getRun(id: string): Promise<ExtendedWorkflowRunRecord | null>;
240
236
  updateRun(id: string, updates: UpdateRunInput): Promise<void>;
241
- listRuns(
242
- options?: ExtendedListRunsOptions,
243
- ): Promise<PaginatedResult<ExtendedWorkflowRunRecord>>;
237
+ listRuns(options?: ExtendedListRunsOptions): Promise<PaginatedResult<ExtendedWorkflowRunRecord>>;
244
238
  deleteRun(id: string): Promise<void>;
245
239
 
246
240
  // Atomic dequeue for concurrency control
247
- dequeueRun(
248
- workflowKinds: string[],
249
- ): Promise<ExtendedWorkflowRunRecord | null>;
241
+ dequeueRun(workflowKinds: string[]): Promise<ExtendedWorkflowRunRecord | null>;
250
242
 
251
243
  // Stale workflow cleanup
252
244
  cleanupStaleRuns(defaultTimeoutMs?: number): Promise<number>;
253
245
  markRunsAsFailed(runIds: string[], reason: string): Promise<void>;
254
246
 
255
247
  // Step operations
256
- getStepResult(
257
- runId: string,
258
- stepName: string,
259
- ): Promise<StepResult | undefined>;
248
+ getStepResult(runId: string, stepName: string): Promise<StepResult | undefined>;
260
249
  getStepResults(runId: string): Promise<StepResult[]>;
261
250
  getStepsForRun(runId: string): Promise<StepRecord[]>;
262
- saveStepResult(
263
- result: Omit<StepResult, "id"> & { id?: string },
264
- ): Promise<void>;
251
+ saveStepResult(result: Omit<StepResult, 'id'> & { id?: string }): Promise<void>;
265
252
 
266
253
  // Lifecycle
267
254
  initialize(): Promise<void>;
@@ -278,32 +265,20 @@ export interface WorkflowStorage {
278
265
  */
279
266
  export interface StorageAdapter {
280
267
  // Run operations
281
- createRun(
282
- run: Omit<WorkflowRunRecord, "id" | "createdAt">,
283
- ): Promise<WorkflowRunRecord>;
268
+ createRun(run: Omit<WorkflowRunRecord, 'id' | 'createdAt'>): Promise<WorkflowRunRecord>;
284
269
  getRun(runId: string): Promise<WorkflowRunRecord | null>;
285
270
  updateRun(runId: string, updates: Partial<WorkflowRunRecord>): Promise<void>;
286
- listRuns(
287
- options?: ListRunsOptions,
288
- ): Promise<PaginatedResult<WorkflowRunRecord>>;
271
+ listRuns(options?: ListRunsOptions): Promise<PaginatedResult<WorkflowRunRecord>>;
289
272
 
290
273
  // Step operations
291
- createStep(
292
- step: Omit<WorkflowRunStepRecord, "id">,
293
- ): Promise<WorkflowRunStepRecord>;
274
+ createStep(step: Omit<WorkflowRunStepRecord, 'id'>): Promise<WorkflowRunStepRecord>;
294
275
  getStep(stepId: string): Promise<WorkflowRunStepRecord | null>;
295
- updateStep(
296
- stepId: string,
297
- updates: Partial<WorkflowRunStepRecord>,
298
- ): Promise<void>;
276
+ updateStep(stepId: string, updates: Partial<WorkflowRunStepRecord>): Promise<void>;
299
277
  getStepsForRun(runId: string): Promise<WorkflowRunStepRecord[]>;
300
278
 
301
279
  // Event operations
302
- saveEvent(event: Omit<WorkflowEventRecord, "id">): Promise<void>;
303
- getEventsForRun(
304
- runId: string,
305
- options?: ListEventsOptions,
306
- ): Promise<WorkflowEventRecord[]>;
280
+ saveEvent(event: Omit<WorkflowEventRecord, 'id'>): Promise<void>;
281
+ getEventsForRun(runId: string, options?: ListEventsOptions): Promise<WorkflowEventRecord[]>;
307
282
 
308
283
  /**
309
284
  * Optional transaction support.
@@ -363,6 +338,6 @@ export interface StepflowStepResultsTable {
363
338
  * Combined database schema for Stepflow.
364
339
  */
365
340
  export interface StepflowDatabase {
366
- "stepflow.runs": StepflowRunsTable;
367
- "stepflow.step_results": StepflowStepResultsTable;
341
+ 'stepflow.runs': StepflowRunsTable;
342
+ 'stepflow.step_results': StepflowStepResultsTable;
368
343
  }
@@ -1,4 +1,4 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, it, expect } from 'vitest';
2
2
  import {
3
3
  WorkflowEngineError,
4
4
  WorkflowNotFoundError,
@@ -8,145 +8,143 @@ import {
8
8
  StepTimeoutError,
9
9
  WorkflowCanceledError,
10
10
  WorkflowTimeoutError,
11
- } from "./errors";
11
+ } from './errors';
12
12
 
13
- describe("WorkflowEngineError", () => {
14
- it("should store code, message, and details", () => {
15
- const err = new WorkflowEngineError("TEST_CODE", "test message", {
16
- key: "val",
17
- });
13
+ describe('WorkflowEngineError', () => {
14
+ it('should store code, message, and details', () => {
15
+ const err = new WorkflowEngineError('TEST_CODE', 'test message', { key: 'val' });
18
16
 
19
- expect(err.code).toBe("TEST_CODE");
20
- expect(err.message).toBe("test message");
21
- expect(err.details).toEqual({ key: "val" });
22
- expect(err.name).toBe("WorkflowEngineError");
17
+ expect(err.code).toBe('TEST_CODE');
18
+ expect(err.message).toBe('test message');
19
+ expect(err.details).toEqual({ key: 'val' });
20
+ expect(err.name).toBe('WorkflowEngineError');
23
21
  expect(err).toBeInstanceOf(Error);
24
22
  });
25
23
 
26
- it("should convert to record", () => {
27
- const err = new WorkflowEngineError("CODE", "msg", { x: 1 });
24
+ it('should convert to record', () => {
25
+ const err = new WorkflowEngineError('CODE', 'msg', { x: 1 });
28
26
  const record = err.toRecord();
29
27
 
30
- expect(record.code).toBe("CODE");
31
- expect(record.message).toBe("msg");
28
+ expect(record.code).toBe('CODE');
29
+ expect(record.message).toBe('msg');
32
30
  expect(record.details).toEqual({ x: 1 });
33
- expect(record.stack).toBeDefined();
31
+ expect(record.stack).toBeUndefined();
34
32
  });
35
33
 
36
- describe("fromError", () => {
37
- it("should convert WorkflowEngineError to record", () => {
38
- const err = new WorkflowEngineError("MY_CODE", "my msg");
34
+ describe('fromError', () => {
35
+ it('should convert WorkflowEngineError to record', () => {
36
+ const err = new WorkflowEngineError('MY_CODE', 'my msg');
39
37
  const record = WorkflowEngineError.fromError(err);
40
38
 
41
- expect(record.code).toBe("MY_CODE");
42
- expect(record.message).toBe("my msg");
39
+ expect(record.code).toBe('MY_CODE');
40
+ expect(record.message).toBe('my msg');
43
41
  });
44
42
 
45
- it("should convert regular Error to record with default code", () => {
46
- const err = new Error("regular error");
43
+ it('should convert regular Error to record with default code', () => {
44
+ const err = new Error('regular error');
47
45
  const record = WorkflowEngineError.fromError(err);
48
46
 
49
- expect(record.code).toBe("UNKNOWN_ERROR");
50
- expect(record.message).toBe("regular error");
51
- expect(record.stack).toBeDefined();
47
+ expect(record.code).toBe('UNKNOWN_ERROR');
48
+ expect(record.message).toBe('regular error');
49
+ expect(record.stack).toBeUndefined();
52
50
  });
53
51
 
54
- it("should convert regular Error with custom code", () => {
55
- const err = new Error("oops");
56
- const record = WorkflowEngineError.fromError(err, "CUSTOM_CODE");
52
+ it('should convert regular Error with custom code', () => {
53
+ const err = new Error('oops');
54
+ const record = WorkflowEngineError.fromError(err, 'CUSTOM_CODE');
57
55
 
58
- expect(record.code).toBe("CUSTOM_CODE");
56
+ expect(record.code).toBe('CUSTOM_CODE');
59
57
  });
60
58
 
61
- it("should convert non-Error value to record", () => {
62
- const record = WorkflowEngineError.fromError("string error");
59
+ it('should convert non-Error value to record', () => {
60
+ const record = WorkflowEngineError.fromError('string error');
63
61
 
64
- expect(record.code).toBe("UNKNOWN_ERROR");
65
- expect(record.message).toBe("string error");
62
+ expect(record.code).toBe('UNKNOWN_ERROR');
63
+ expect(record.message).toBe('string error');
66
64
  expect(record.stack).toBeUndefined();
67
65
  });
68
66
 
69
- it("should convert number to record", () => {
67
+ it('should convert number to record', () => {
70
68
  const record = WorkflowEngineError.fromError(42);
71
69
 
72
- expect(record.message).toBe("42");
70
+ expect(record.message).toBe('42');
73
71
  });
74
72
  });
75
73
  });
76
74
 
77
- describe("WorkflowNotFoundError", () => {
78
- it("should have correct code and message", () => {
79
- const err = new WorkflowNotFoundError("my.workflow");
75
+ describe('WorkflowNotFoundError', () => {
76
+ it('should have correct code and message', () => {
77
+ const err = new WorkflowNotFoundError('my.workflow');
80
78
 
81
- expect(err.code).toBe("WORKFLOW_NOT_FOUND");
79
+ expect(err.code).toBe('WORKFLOW_NOT_FOUND');
82
80
  expect(err.message).toBe('Workflow "my.workflow" is not registered');
83
- expect(err.name).toBe("WorkflowNotFoundError");
84
- expect(err.details).toEqual({ kind: "my.workflow" });
81
+ expect(err.name).toBe('WorkflowNotFoundError');
82
+ expect(err.details).toEqual({ kind: 'my.workflow' });
85
83
  });
86
84
  });
87
85
 
88
- describe("WorkflowAlreadyRegisteredError", () => {
89
- it("should have correct code and message", () => {
90
- const err = new WorkflowAlreadyRegisteredError("dup.workflow");
86
+ describe('WorkflowAlreadyRegisteredError', () => {
87
+ it('should have correct code and message', () => {
88
+ const err = new WorkflowAlreadyRegisteredError('dup.workflow');
91
89
 
92
- expect(err.code).toBe("WORKFLOW_ALREADY_REGISTERED");
93
- expect(err.message).toContain("dup.workflow");
94
- expect(err.name).toBe("WorkflowAlreadyRegisteredError");
90
+ expect(err.code).toBe('WORKFLOW_ALREADY_REGISTERED');
91
+ expect(err.message).toContain('dup.workflow');
92
+ expect(err.name).toBe('WorkflowAlreadyRegisteredError');
95
93
  });
96
94
  });
97
95
 
98
- describe("RunNotFoundError", () => {
99
- it("should have correct code and message", () => {
100
- const err = new RunNotFoundError("run-123");
96
+ describe('RunNotFoundError', () => {
97
+ it('should have correct code and message', () => {
98
+ const err = new RunNotFoundError('run-123');
101
99
 
102
- expect(err.code).toBe("RUN_NOT_FOUND");
103
- expect(err.message).toContain("run-123");
104
- expect(err.name).toBe("RunNotFoundError");
100
+ expect(err.code).toBe('RUN_NOT_FOUND');
101
+ expect(err.message).toContain('run-123');
102
+ expect(err.name).toBe('RunNotFoundError');
105
103
  });
106
104
  });
107
105
 
108
- describe("StepError", () => {
109
- it("should store step key, attempt, and cause", () => {
110
- const cause = new Error("root cause");
111
- const err = new StepError("step1", "step failed", 3, cause);
106
+ describe('StepError', () => {
107
+ it('should store step key, attempt, and cause', () => {
108
+ const cause = new Error('root cause');
109
+ const err = new StepError('step1', 'step failed', 3, cause);
112
110
 
113
- expect(err.code).toBe("STEP_ERROR");
114
- expect(err.stepKey).toBe("step1");
111
+ expect(err.code).toBe('STEP_ERROR');
112
+ expect(err.stepKey).toBe('step1');
115
113
  expect(err.attempt).toBe(3);
116
114
  expect(err.cause).toBe(cause);
117
- expect(err.name).toBe("StepError");
115
+ expect(err.name).toBe('StepError');
118
116
  });
119
117
  });
120
118
 
121
- describe("StepTimeoutError", () => {
122
- it("should store step key and timeout", () => {
123
- const err = new StepTimeoutError("slow_step", 5000);
119
+ describe('StepTimeoutError', () => {
120
+ it('should store step key and timeout', () => {
121
+ const err = new StepTimeoutError('slow_step', 5000);
124
122
 
125
- expect(err.code).toBe("STEP_TIMEOUT");
126
- expect(err.stepKey).toBe("slow_step");
123
+ expect(err.code).toBe('STEP_TIMEOUT');
124
+ expect(err.stepKey).toBe('slow_step');
127
125
  expect(err.timeoutMs).toBe(5000);
128
- expect(err.message).toContain("5000ms");
129
- expect(err.name).toBe("StepTimeoutError");
126
+ expect(err.message).toContain('5000ms');
127
+ expect(err.name).toBe('StepTimeoutError');
130
128
  });
131
129
  });
132
130
 
133
- describe("WorkflowCanceledError", () => {
134
- it("should have correct code and message", () => {
135
- const err = new WorkflowCanceledError("run-456");
131
+ describe('WorkflowCanceledError', () => {
132
+ it('should have correct code and message', () => {
133
+ const err = new WorkflowCanceledError('run-456');
136
134
 
137
- expect(err.code).toBe("WORKFLOW_CANCELED");
138
- expect(err.message).toContain("run-456");
139
- expect(err.name).toBe("WorkflowCanceledError");
135
+ expect(err.code).toBe('WORKFLOW_CANCELED');
136
+ expect(err.message).toContain('run-456');
137
+ expect(err.name).toBe('WorkflowCanceledError');
140
138
  });
141
139
  });
142
140
 
143
- describe("WorkflowTimeoutError", () => {
144
- it("should store timeout value", () => {
145
- const err = new WorkflowTimeoutError("run-789", 30000);
141
+ describe('WorkflowTimeoutError', () => {
142
+ it('should store timeout value', () => {
143
+ const err = new WorkflowTimeoutError('run-789', 30000);
146
144
 
147
- expect(err.code).toBe("WORKFLOW_TIMEOUT");
145
+ expect(err.code).toBe('WORKFLOW_TIMEOUT');
148
146
  expect(err.timeoutMs).toBe(30000);
149
- expect(err.message).toContain("30000ms");
150
- expect(err.name).toBe("WorkflowTimeoutError");
147
+ expect(err.message).toContain('30000ms');
148
+ expect(err.name).toBe('WorkflowTimeoutError');
151
149
  });
152
150
  });
@@ -2,7 +2,7 @@
2
2
  * Custom error classes for the workflow engine.
3
3
  */
4
4
 
5
- import type { WorkflowError } from "../core/types";
5
+ import type { WorkflowError } from '../core/types';
6
6
 
7
7
  /**
8
8
  * Base error class for workflow-related errors.
@@ -11,13 +11,9 @@ export class WorkflowEngineError extends Error {
11
11
  readonly code: string;
12
12
  readonly details?: Record<string, unknown>;
13
13
 
14
- constructor(
15
- code: string,
16
- message: string,
17
- details?: Record<string, unknown>,
18
- ) {
14
+ constructor(code: string, message: string, details?: Record<string, unknown>) {
19
15
  super(message);
20
- this.name = "WorkflowEngineError";
16
+ this.name = 'WorkflowEngineError';
21
17
  this.code = code;
22
18
  this.details = details;
23
19
  }
@@ -29,7 +25,6 @@ export class WorkflowEngineError extends Error {
29
25
  return {
30
26
  code: this.code,
31
27
  message: this.message,
32
- stack: this.stack,
33
28
  details: this.details,
34
29
  };
35
30
  }
@@ -37,10 +32,7 @@ export class WorkflowEngineError extends Error {
37
32
  /**
38
33
  * Create a WorkflowError record from any error.
39
34
  */
40
- static fromError(
41
- error: unknown,
42
- defaultCode = "UNKNOWN_ERROR",
43
- ): WorkflowError {
35
+ static fromError(error: unknown, defaultCode = 'UNKNOWN_ERROR'): WorkflowError {
44
36
  if (error instanceof WorkflowEngineError) {
45
37
  return error.toRecord();
46
38
  }
@@ -49,7 +41,6 @@ export class WorkflowEngineError extends Error {
49
41
  return {
50
42
  code: defaultCode,
51
43
  message: error.message,
52
- stack: error.stack,
53
44
  };
54
45
  }
55
46
 
@@ -65,10 +56,8 @@ export class WorkflowEngineError extends Error {
65
56
  */
66
57
  export class WorkflowNotFoundError extends WorkflowEngineError {
67
58
  constructor(kind: string) {
68
- super("WORKFLOW_NOT_FOUND", `Workflow "${kind}" is not registered`, {
69
- kind,
70
- });
71
- this.name = "WorkflowNotFoundError";
59
+ super('WORKFLOW_NOT_FOUND', `Workflow "${kind}" is not registered`, { kind });
60
+ this.name = 'WorkflowNotFoundError';
72
61
  }
73
62
  }
74
63
 
@@ -77,12 +66,8 @@ export class WorkflowNotFoundError extends WorkflowEngineError {
77
66
  */
78
67
  export class WorkflowAlreadyRegisteredError extends WorkflowEngineError {
79
68
  constructor(kind: string) {
80
- super(
81
- "WORKFLOW_ALREADY_REGISTERED",
82
- `Workflow "${kind}" is already registered`,
83
- { kind },
84
- );
85
- this.name = "WorkflowAlreadyRegisteredError";
69
+ super('WORKFLOW_ALREADY_REGISTERED', `Workflow "${kind}" is already registered`, { kind });
70
+ this.name = 'WorkflowAlreadyRegisteredError';
86
71
  }
87
72
  }
88
73
 
@@ -91,8 +76,8 @@ export class WorkflowAlreadyRegisteredError extends WorkflowEngineError {
91
76
  */
92
77
  export class RunNotFoundError extends WorkflowEngineError {
93
78
  constructor(runId: string) {
94
- super("RUN_NOT_FOUND", `Run "${runId}" not found`, { runId });
95
- this.name = "RunNotFoundError";
79
+ super('RUN_NOT_FOUND', `Run "${runId}" not found`, { runId });
80
+ this.name = 'RunNotFoundError';
96
81
  }
97
82
  }
98
83
 
@@ -104,14 +89,9 @@ export class StepError extends WorkflowEngineError {
104
89
  readonly attempt: number;
105
90
  readonly cause?: Error;
106
91
 
107
- constructor(
108
- stepKey: string,
109
- message: string,
110
- attempt: number,
111
- cause?: Error,
112
- ) {
113
- super("STEP_ERROR", message, { stepKey, attempt });
114
- this.name = "StepError";
92
+ constructor(stepKey: string, message: string, attempt: number, cause?: Error) {
93
+ super('STEP_ERROR', message, { stepKey, attempt });
94
+ this.name = 'StepError';
115
95
  this.stepKey = stepKey;
116
96
  this.attempt = attempt;
117
97
  this.cause = cause;
@@ -126,11 +106,11 @@ export class StepTimeoutError extends WorkflowEngineError {
126
106
  readonly timeoutMs: number;
127
107
 
128
108
  constructor(stepKey: string, timeoutMs: number) {
129
- super("STEP_TIMEOUT", `Step "${stepKey}" timed out after ${timeoutMs}ms`, {
109
+ super('STEP_TIMEOUT', `Step "${stepKey}" timed out after ${timeoutMs}ms`, {
130
110
  stepKey,
131
111
  timeoutMs,
132
112
  });
133
- this.name = "StepTimeoutError";
113
+ this.name = 'StepTimeoutError';
134
114
  this.stepKey = stepKey;
135
115
  this.timeoutMs = timeoutMs;
136
116
  }
@@ -141,10 +121,8 @@ export class StepTimeoutError extends WorkflowEngineError {
141
121
  */
142
122
  export class WorkflowCanceledError extends WorkflowEngineError {
143
123
  constructor(runId: string) {
144
- super("WORKFLOW_CANCELED", `Workflow run "${runId}" was canceled`, {
145
- runId,
146
- });
147
- this.name = "WorkflowCanceledError";
124
+ super('WORKFLOW_CANCELED', `Workflow run "${runId}" was canceled`, { runId });
125
+ this.name = 'WorkflowCanceledError';
148
126
  }
149
127
  }
150
128
 
@@ -156,15 +134,11 @@ export class WaitForRunTimeoutError extends WorkflowEngineError {
156
134
  readonly timeoutMs: number;
157
135
 
158
136
  constructor(runId: string, timeoutMs: number) {
159
- super(
160
- "WAIT_FOR_RUN_TIMEOUT",
161
- `Timeout waiting for run ${runId} after ${timeoutMs}ms`,
162
- {
163
- runId,
164
- timeoutMs,
165
- },
166
- );
167
- this.name = "WaitForRunTimeoutError";
137
+ super('WAIT_FOR_RUN_TIMEOUT', `Timeout waiting for run ${runId} after ${timeoutMs}ms`, {
138
+ runId,
139
+ timeoutMs,
140
+ });
141
+ this.name = 'WaitForRunTimeoutError';
168
142
  this.runId = runId;
169
143
  this.timeoutMs = timeoutMs;
170
144
  }
@@ -177,15 +151,11 @@ export class WorkflowTimeoutError extends WorkflowEngineError {
177
151
  readonly timeoutMs: number;
178
152
 
179
153
  constructor(runId: string, timeoutMs: number) {
180
- super(
181
- "WORKFLOW_TIMEOUT",
182
- `Workflow run "${runId}" timed out after ${timeoutMs}ms`,
183
- {
184
- runId,
185
- timeoutMs,
186
- },
187
- );
188
- this.name = "WorkflowTimeoutError";
154
+ super('WORKFLOW_TIMEOUT', `Workflow run "${runId}" timed out after ${timeoutMs}ms`, {
155
+ runId,
156
+ timeoutMs,
157
+ });
158
+ this.name = 'WorkflowTimeoutError';
189
159
  this.timeoutMs = timeoutMs;
190
160
  }
191
161
  }
package/src/utils/id.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * ID generation utilities for the workflow engine.
3
3
  */
4
- import { randomBytes } from "node:crypto";
4
+
5
+ import { randomBytes } from 'node:crypto';
5
6
 
6
7
  /**
7
8
  * Generate a unique ID for database records.
@@ -10,12 +11,11 @@ import { randomBytes } from "node:crypto";
10
11
  * - Rough time-ordering (useful for debugging)
11
12
  * - High collision resistance
12
13
  * - URL-safe characters
13
- * - Cryptographically secure random component
14
14
  *
15
15
  * @returns A unique identifier string (approx 16 characters)
16
16
  */
17
17
  export function generateId(): string {
18
18
  const timestamp = Date.now().toString(36);
19
- const randomPart = randomBytes(6).toString("base64url");
19
+ const randomPart = randomBytes(6).toString('base64url');
20
20
  return `${timestamp}${randomPart}`;
21
21
  }
@@ -2,7 +2,9 @@
2
2
  * Utilities module exports.
3
3
  */
4
4
 
5
- export * from "./id";
6
- export * from "./errors";
7
- export * from "./logger";
8
- export * from "./retry";
5
+ export * from './id';
6
+ export * from './errors';
7
+ export * from './logger';
8
+ export * from './retry';
9
+ export * from './safe-json';
10
+ export * from './webhook-signature';