@multiplier-labs/stepflow 0.3.4 → 0.3.8

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 (109) hide show
  1. package/README.md +9 -0
  2. package/dist/{chunk-5XJBAPXF.js → chunk-4IDPWWGS.js} +15 -4
  3. package/dist/chunk-4IDPWWGS.js.map +1 -0
  4. package/dist/{chunk-U3PCVK6O.js → chunk-T6GAZBTQ.js} +70 -38
  5. package/dist/chunk-T6GAZBTQ.js.map +1 -0
  6. package/dist/{chunk-FMPJBK2N.js → chunk-TVSADUYU.js} +3 -5
  7. package/dist/{chunk-FMPJBK2N.js.map → chunk-TVSADUYU.js.map} +1 -1
  8. package/dist/events/index.d.ts +43 -3
  9. package/dist/events/index.js +7 -3
  10. package/dist/{index-Cd0IZ40L.d.ts → index-CdXDv30T.d.ts} +10 -2
  11. package/dist/index.d.ts +5 -4
  12. package/dist/index.js +159 -46
  13. package/dist/index.js.map +1 -1
  14. package/dist/scheduler/index.d.ts +2 -2
  15. package/dist/scheduler/index.js +1 -1
  16. package/dist/storage/index.d.ts +6 -4
  17. package/dist/storage/index.js +1 -1
  18. package/dist/{types-DSEjiLMj.d.ts → types-BfWe5yP6.d.ts} +2 -0
  19. package/package.json +26 -14
  20. package/.codekin/reports/code-review/2026-04-11_code-review-daily.md +0 -146
  21. package/.codekin/reports/code-review/2026-04-13_code-review-daily.md +0 -74
  22. package/.codekin/reports/comments/2026-04-04_comment-assessment.md +0 -179
  23. package/.codekin/reports/comments/2026-04-11_comment-assessment.md +0 -161
  24. package/.codekin/reports/comments/2026-04-14_comment-assessment.md +0 -34
  25. package/.codekin/reports/complexity/2026-04-03_complexity-report.md +0 -152
  26. package/.codekin/reports/complexity/2026-04-11_complexity-report.md +0 -152
  27. package/.codekin/reports/complexity/2026-04-14_complexity-report.md +0 -34
  28. package/.codekin/reports/dependencies/2026-04-04_dependency-health.md +0 -94
  29. package/.codekin/reports/dependencies/2026-04-11_dependency-health.md +0 -93
  30. package/.codekin/reports/docs-audit/2026-04-04_docs-audit.md +0 -10
  31. package/.codekin/reports/docs-audit/2026-04-11_docs-audit.md +0 -138
  32. package/.codekin/reports/docs-audit/2026-04-13_docs-audit.md +0 -121
  33. package/.codekin/reports/security/2026-04-03_security-audit.md +0 -208
  34. package/.codekin/reports/security/2026-04-11_security-audit.md +0 -297
  35. package/.github/CODEOWNERS +0 -15
  36. package/.github/dependabot.yml +0 -23
  37. package/.github/workflows/ci.yml +0 -38
  38. package/.github/workflows/publish.yml +0 -42
  39. package/.github/workflows/security.yml +0 -29
  40. package/CHANGELOG.md +0 -34
  41. package/SECURITY.md +0 -40
  42. package/dist/chunk-5XJBAPXF.js.map +0 -1
  43. package/dist/chunk-U3PCVK6O.js.map +0 -1
  44. package/docs/github-packages-setup.md +0 -97
  45. package/docs/stepflow-api-reference.md +0 -1663
  46. package/src/core/engine/concurrency.ts +0 -229
  47. package/src/core/engine/lifecycle.ts +0 -176
  48. package/src/core/engine.test.ts +0 -1435
  49. package/src/core/engine.ts +0 -598
  50. package/src/core/index.ts +0 -7
  51. package/src/core/orchestrator/persistence.ts +0 -165
  52. package/src/core/orchestrator/plan-resolution.ts +0 -140
  53. package/src/core/orchestrator/state-transitions.ts +0 -109
  54. package/src/core/orchestrator/step-runner.ts +0 -427
  55. package/src/core/orchestrator.ts +0 -252
  56. package/src/core/types.ts +0 -220
  57. package/src/events/index.ts +0 -8
  58. package/src/events/memory.test.ts +0 -107
  59. package/src/events/memory.ts +0 -92
  60. package/src/events/socketio.test.ts +0 -378
  61. package/src/events/socketio.ts +0 -262
  62. package/src/events/types.ts +0 -118
  63. package/src/events/webhook.test.ts +0 -949
  64. package/src/events/webhook.ts +0 -807
  65. package/src/index.ts +0 -270
  66. package/src/planning/evaluate-condition.test.ts +0 -153
  67. package/src/planning/evaluate-condition.ts +0 -105
  68. package/src/planning/index.ts +0 -55
  69. package/src/planning/planner.ts +0 -494
  70. package/src/planning/planning.test.ts +0 -727
  71. package/src/planning/registry.test.ts +0 -398
  72. package/src/planning/registry.ts +0 -283
  73. package/src/planning/types.ts +0 -566
  74. package/src/scheduler/cron.test.ts +0 -479
  75. package/src/scheduler/cron.ts +0 -438
  76. package/src/scheduler/index.ts +0 -8
  77. package/src/scheduler/postgres-persistence.ts +0 -530
  78. package/src/scheduler/sqlite-persistence.test.ts +0 -235
  79. package/src/scheduler/sqlite-persistence.ts +0 -286
  80. package/src/scheduler/types.ts +0 -73
  81. package/src/storage/index.ts +0 -27
  82. package/src/storage/memory.test.ts +0 -349
  83. package/src/storage/memory.ts +0 -227
  84. package/src/storage/postgres-core.ts +0 -550
  85. package/src/storage/postgres-migration.test.ts +0 -69
  86. package/src/storage/postgres-transaction-pagination.test.ts +0 -107
  87. package/src/storage/postgres.ts +0 -835
  88. package/src/storage/sqlite-core.ts +0 -151
  89. package/src/storage/sqlite.test.ts +0 -642
  90. package/src/storage/sqlite.ts +0 -717
  91. package/src/storage/types.ts +0 -343
  92. package/src/utils/errors.test.ts +0 -150
  93. package/src/utils/errors.ts +0 -161
  94. package/src/utils/id.ts +0 -21
  95. package/src/utils/index.ts +0 -10
  96. package/src/utils/logger.test.ts +0 -121
  97. package/src/utils/logger.ts +0 -96
  98. package/src/utils/pinned-fetch.test.ts +0 -84
  99. package/src/utils/pinned-fetch.ts +0 -118
  100. package/src/utils/postgres-deps.ts +0 -79
  101. package/src/utils/retry.test.ts +0 -177
  102. package/src/utils/retry.ts +0 -121
  103. package/src/utils/safe-json.test.ts +0 -166
  104. package/src/utils/safe-json.ts +0 -107
  105. package/src/utils/webhook-signature.test.ts +0 -111
  106. package/src/utils/webhook-signature.ts +0 -74
  107. package/tsconfig.json +0 -21
  108. package/tsup.config.ts +0 -14
  109. package/vitest.config.ts +0 -9
@@ -1,165 +0,0 @@
1
- /**
2
- * Orchestrator persistence layer.
3
- *
4
- * Wraps the storage adapter and event transport with intent-named helpers
5
- * so the orchestrator and step-runner can stay focused on control flow
6
- * instead of repeating field shapes for every status transition.
7
- *
8
- * All side-effect calls the orchestrator makes against storage or the event
9
- * transport flow through this module — keeping the rest of the pipeline
10
- * pure(-ish) and easy to read.
11
- */
12
- import type { StorageAdapter, WorkflowRunStepRecord } from '../../storage/types';
13
- import type { EventTransport, WorkflowEvent } from '../../events/types';
14
- import type { Logger, WorkflowError } from '../types';
15
- import { WorkflowEngineError } from '../../utils/errors';
16
-
17
- /**
18
- * Mark the run as running. On a fresh start, also stamp `startedAt`;
19
- * on resume, leave the original `startedAt` alone.
20
- */
21
- export async function markRunRunning(
22
- storage: StorageAdapter,
23
- runId: string,
24
- isResume: boolean
25
- ): Promise<void> {
26
- await storage.updateRun(runId, {
27
- status: 'running',
28
- ...(isResume ? {} : { startedAt: new Date() }),
29
- });
30
- }
31
-
32
- /**
33
- * Persist the workflow checkpoint after a step completes:
34
- * the accumulated step results plus the list of completed step keys.
35
- */
36
- export async function saveCheckpoint(
37
- storage: StorageAdapter,
38
- runId: string,
39
- results: Record<string, unknown>,
40
- completedSteps: string[]
41
- ): Promise<void> {
42
- await storage.updateRun(runId, {
43
- context: { ...results },
44
- completedSteps: [...completedSteps],
45
- });
46
- }
47
-
48
- /**
49
- * Mark the run as succeeded, persisting results and finishedAt.
50
- */
51
- export async function markRunSucceeded(
52
- storage: StorageAdapter,
53
- runId: string,
54
- results: Record<string, unknown>
55
- ): Promise<void> {
56
- await storage.updateRun(runId, {
57
- status: 'succeeded',
58
- context: results,
59
- finishedAt: new Date(),
60
- });
61
- }
62
-
63
- /**
64
- * Persist a non-success terminal state for the run (failed / canceled / timeout).
65
- * `status` is the storage-level status — the orchestrator decides this from the
66
- * raised error using `state-transitions.ts`.
67
- */
68
- export async function markRunFinal(
69
- storage: StorageAdapter,
70
- runId: string,
71
- status: 'failed' | 'canceled',
72
- results: Record<string, unknown>,
73
- error: WorkflowError
74
- ): Promise<void> {
75
- await storage.updateRun(runId, {
76
- status,
77
- context: results,
78
- error,
79
- finishedAt: new Date(),
80
- });
81
- }
82
-
83
- /**
84
- * Create a fresh step record in 'running' state for the given attempt.
85
- * Returns the persisted record so the caller can read its generated id.
86
- */
87
- export async function createStepRecord(
88
- storage: StorageAdapter,
89
- runId: string,
90
- stepKey: string,
91
- stepName: string,
92
- attempt: number
93
- ): Promise<WorkflowRunStepRecord> {
94
- return storage.createStep({
95
- runId,
96
- stepKey,
97
- stepName,
98
- status: 'running',
99
- attempt,
100
- startedAt: new Date(),
101
- });
102
- }
103
-
104
- /**
105
- * Mark a step as succeeded, recording its result and finishedAt.
106
- */
107
- export async function markStepSucceeded(
108
- storage: StorageAdapter,
109
- stepId: string,
110
- result: unknown
111
- ): Promise<void> {
112
- await storage.updateStep(stepId, {
113
- status: 'succeeded',
114
- result,
115
- finishedAt: new Date(),
116
- });
117
- }
118
-
119
- /**
120
- * Mark a step as failed (or canceled, if the abort signal already fired)
121
- * with the normalized engine error attached.
122
- */
123
- export async function markStepFailed(
124
- storage: StorageAdapter,
125
- stepId: string,
126
- error: unknown,
127
- isCanceled: boolean
128
- ): Promise<void> {
129
- await storage.updateStep(stepId, {
130
- status: isCanceled ? 'canceled' : 'failed',
131
- error: WorkflowEngineError.fromError(error),
132
- finishedAt: new Date(),
133
- });
134
- }
135
-
136
- /**
137
- * Mark a step as canceled without an error payload — used when an in-flight
138
- * retry sleep is interrupted by an abort signal.
139
- */
140
- export async function markStepCanceled(
141
- storage: StorageAdapter,
142
- stepId: string
143
- ): Promise<void> {
144
- await storage.updateStep(stepId, {
145
- status: 'canceled',
146
- finishedAt: new Date(),
147
- });
148
- }
149
-
150
- /**
151
- * Emit a workflow event without ever throwing back into the caller.
152
- * Event emission failures are logged but never abort the workflow,
153
- * so a flaky transport can't take down a run.
154
- */
155
- export function safeEmitEvent(
156
- events: EventTransport,
157
- logger: Logger,
158
- event: WorkflowEvent
159
- ): void {
160
- try {
161
- events.emit(event);
162
- } catch (error) {
163
- logger.error('Failed to emit event:', error);
164
- }
165
- }
@@ -1,140 +0,0 @@
1
- /**
2
- * Plan resolution — turns ExecuteOptions into the runtime artefacts
3
- * the orchestrator needs to start running steps.
4
- *
5
- * Today the engine has no separate planner module: the "plan" is just the
6
- * declarative WorkflowDefinition combined with the optional resume
7
- * checkpoint and the supplied input/metadata. This module is the seam
8
- * where, in the future, recipe/plan resolution and input validation can
9
- * grow without bloating execute-workflow.ts.
10
- *
11
- * Responsibilities:
12
- * 1. Build the WorkflowContext from the run options + checkpoint, with a
13
- * `context.emit` helper that routes through safeEmitEvent.
14
- * 2. Set up the workflow-level timeout: returns a handle the caller
15
- * clears in both the success and failure paths, and a getter for
16
- * the WorkflowTimeoutError that fired (so the catch block can
17
- * surface it instead of the abort-derived error).
18
- * 3. Compute the initial set of completed-step keys (used by the loop
19
- * to skip steps already done in a previous run).
20
- */
21
- import type {
22
- WorkflowDefinition,
23
- WorkflowContext,
24
- Logger,
25
- SpawnChildOptions,
26
- } from '../types';
27
- import type { EventTransport } from '../../events/types';
28
- import { WorkflowTimeoutError } from '../../utils/errors';
29
- import { createScopedLogger } from '../../utils/logger';
30
- import { safeEmitEvent } from './persistence';
31
-
32
- /**
33
- * Inputs needed to resolve a workflow plan + build its execution context.
34
- */
35
- export interface BuildContextOptions {
36
- runId: string;
37
- definition: WorkflowDefinition;
38
- input: Record<string, unknown>;
39
- metadata: Record<string, unknown>;
40
- events: EventTransport;
41
- logger: Logger;
42
- abortController: AbortController;
43
- spawnChild: (options: SpawnChildOptions) => Promise<string>;
44
- /** Existing checkpoint results to preload into context.results. */
45
- checkpointResults?: Record<string, unknown>;
46
- }
47
-
48
- /**
49
- * Build the WorkflowContext that will be threaded through every step
50
- * handler. On resume, preload `context.results` from the checkpoint.
51
- */
52
- export function buildContext(options: BuildContextOptions): WorkflowContext {
53
- const {
54
- runId,
55
- definition,
56
- input,
57
- metadata,
58
- events,
59
- logger,
60
- abortController,
61
- spawnChild,
62
- checkpointResults,
63
- } = options;
64
-
65
- return {
66
- runId,
67
- stepId: '', // set per-step by the step-runner before each handler call
68
- kind: definition.kind,
69
- input,
70
- results: checkpointResults ? { ...checkpointResults } : {},
71
- metadata,
72
- logger: createScopedLogger(logger, runId),
73
- signal: abortController.signal,
74
- spawnChild,
75
- emit: (eventType: string, payload?: unknown) => {
76
- safeEmitEvent(events, logger, {
77
- runId,
78
- kind: definition.kind,
79
- eventType,
80
- timestamp: new Date(),
81
- payload,
82
- });
83
- },
84
- };
85
- }
86
-
87
- /**
88
- * Handle returned by setupWorkflowTimeout. The orchestrator must call
89
- * `clear()` on both success and failure paths to release the timer, and
90
- * may read `getTimeoutError()` in the catch path: if non-null, the abort
91
- * was triggered by our timeout (not user cancellation), and the caller
92
- * should surface that error instead of the generic abort-derived one.
93
- */
94
- export interface WorkflowTimeoutHandle {
95
- clear: () => void;
96
- getTimeoutError: () => WorkflowTimeoutError | undefined;
97
- }
98
-
99
- /**
100
- * Arm a workflow-level timeout if the definition specifies one.
101
- *
102
- * On fire: constructs a WorkflowTimeoutError (recoverable via
103
- * `getTimeoutError`) and aborts the controller, which causes any
104
- * in-flight step handler racing the abort signal to reject.
105
- *
106
- * If the definition has no timeout, returns no-op handles.
107
- */
108
- export function setupWorkflowTimeout(
109
- runId: string,
110
- definition: WorkflowDefinition,
111
- abortController: AbortController
112
- ): WorkflowTimeoutHandle {
113
- if (!definition.timeout) {
114
- return {
115
- clear: () => {},
116
- getTimeoutError: () => undefined,
117
- };
118
- }
119
-
120
- let timeoutError: WorkflowTimeoutError | undefined;
121
- const timeoutId = setTimeout(() => {
122
- timeoutError = new WorkflowTimeoutError(runId, definition.timeout!);
123
- abortController.abort();
124
- }, definition.timeout);
125
-
126
- return {
127
- clear: () => clearTimeout(timeoutId),
128
- getTimeoutError: () => timeoutError,
129
- };
130
- }
131
-
132
- /**
133
- * Initial list of step keys to consider already complete — empty for a
134
- * fresh run, the checkpoint's set for a resume.
135
- */
136
- export function initialCompletedSteps(
137
- checkpoint?: { completedStepKeys: Set<string> }
138
- ): string[] {
139
- return checkpoint ? Array.from(checkpoint.completedStepKeys) : [];
140
- }
@@ -1,109 +0,0 @@
1
- /**
2
- * Pure state-transition decisions for the orchestrator.
3
- *
4
- * Every function in here is deterministic and side-effect-free: given
5
- * the current shape of an event, error, or step config they return what
6
- * the next state should be. The orchestrator and step-runner read these
7
- * decisions and apply them via persistence helpers.
8
- *
9
- * State-transition rules captured here:
10
- *
11
- * Run terminal status:
12
- * - WorkflowTimeoutError → status='failed', eventType='run.timeout'
13
- * - WorkflowCanceledError → status='canceled', eventType='run.canceled'
14
- * - any other error → status='failed', eventType='run.failed'
15
- *
16
- * Step error policy resolution (precedence):
17
- * - step.onError > definition.defaultOnError > 'fail'
18
- * - step.maxRetries > 3
19
- * - step.retryDelay (ms) > 1000
20
- * - step.retryBackoff (multiplier) > 2
21
- *
22
- * Step retry decision (after a non-cancellation failure):
23
- * - onError === 'skip' → 'skip'
24
- * - onError === 'retry' && attempt <= maxRetries → 'retry'
25
- * - otherwise (incl. retries exhausted, 'fail') → 'fail'
26
- */
27
- import type { WorkflowDefinition, WorkflowStep } from '../types';
28
- import { WorkflowCanceledError, WorkflowTimeoutError } from '../../utils/errors';
29
-
30
- /**
31
- * Storage-level statuses the orchestrator may persist for a non-success run.
32
- */
33
- export type RunFinalStatus = 'failed' | 'canceled';
34
-
35
- /**
36
- * Event types emitted on a non-success run completion.
37
- */
38
- export type RunFinalEventType = 'run.timeout' | 'run.canceled' | 'run.failed';
39
-
40
- /**
41
- * Result of mapping an error to a terminal run state.
42
- */
43
- export interface RunFinalDecision {
44
- status: RunFinalStatus;
45
- eventType: RunFinalEventType;
46
- }
47
-
48
- /**
49
- * Map a thrown workflow error to the final run status + event type.
50
- * Note: a workflow timeout is persisted as `failed` for storage compatibility,
51
- * but the emitted event distinguishes it via `run.timeout`.
52
- */
53
- export function decideRunFinalState(error: unknown): RunFinalDecision {
54
- if (error instanceof WorkflowTimeoutError) {
55
- return { status: 'failed', eventType: 'run.timeout' };
56
- }
57
- if (error instanceof WorkflowCanceledError) {
58
- return { status: 'canceled', eventType: 'run.canceled' };
59
- }
60
- return { status: 'failed', eventType: 'run.failed' };
61
- }
62
-
63
- /**
64
- * Step-level error policy resolved from a step + workflow definition.
65
- */
66
- export interface StepErrorPolicy {
67
- onError: 'fail' | 'skip' | 'retry';
68
- maxRetries: number;
69
- retryDelay: number;
70
- retryBackoff: number;
71
- }
72
-
73
- /**
74
- * Resolve the effective error policy for a step.
75
- * Step-level config overrides workflow defaults; workflow defaults override
76
- * the engine defaults (`fail` / 3 / 1000ms / 2x).
77
- */
78
- export function resolveStepErrorPolicy<TInput>(
79
- step: WorkflowStep<TInput>,
80
- definition: WorkflowDefinition<TInput>
81
- ): StepErrorPolicy {
82
- return {
83
- onError: step.onError ?? definition.defaultOnError ?? 'fail',
84
- maxRetries: step.maxRetries ?? 3,
85
- retryDelay: step.retryDelay ?? 1000,
86
- retryBackoff: step.retryBackoff ?? 2,
87
- };
88
- }
89
-
90
- /**
91
- * Decision returned after a step attempt fails.
92
- *
93
- * - 'skip' : swallow the error and treat the step as a no-op (returns undefined)
94
- * - 'retry' : back off and try again
95
- * - 'fail' : escalate (StepError) and abort the workflow
96
- */
97
- export type StepFailureAction = 'skip' | 'retry' | 'fail';
98
-
99
- /**
100
- * Decide what to do after a step attempt has failed (and was not canceled).
101
- */
102
- export function decideStepFailureAction(
103
- policy: StepErrorPolicy,
104
- attempt: number
105
- ): StepFailureAction {
106
- if (policy.onError === 'skip') return 'skip';
107
- if (policy.onError === 'retry' && attempt <= policy.maxRetries) return 'retry';
108
- return 'fail';
109
- }