@multiplier-labs/stepflow 0.3.1

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 (76) hide show
  1. package/.github/workflows/publish.yml +30 -0
  2. package/README.md +408 -0
  3. package/dist/chunk-FQCT3I2A.js +1512 -0
  4. package/dist/chunk-FQCT3I2A.js.map +1 -0
  5. package/dist/chunk-GXY66V7Q.js +111 -0
  6. package/dist/chunk-GXY66V7Q.js.map +1 -0
  7. package/dist/chunk-U5QGN3RT.js +715 -0
  8. package/dist/chunk-U5QGN3RT.js.map +1 -0
  9. package/dist/chunk-XCF4PLN7.js +575 -0
  10. package/dist/chunk-XCF4PLN7.js.map +1 -0
  11. package/dist/events/index.d.ts +346 -0
  12. package/dist/events/index.js +11 -0
  13. package/dist/events/index.js.map +1 -0
  14. package/dist/index-CPfGsmrp.d.ts +567 -0
  15. package/dist/index.d.ts +748 -0
  16. package/dist/index.js +1707 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/scheduler/index.d.ts +6 -0
  19. package/dist/scheduler/index.js +12 -0
  20. package/dist/scheduler/index.js.map +1 -0
  21. package/dist/storage/index.d.ts +402 -0
  22. package/dist/storage/index.js +13 -0
  23. package/dist/storage/index.js.map +1 -0
  24. package/dist/types-BVShq7zT.d.ts +150 -0
  25. package/dist/types-BlwFEGzj.d.ts +276 -0
  26. package/dist/types-DzkEqnQJ.d.ts +75 -0
  27. package/docs/github-packages-setup.md +97 -0
  28. package/docs/stepflow-api-reference.md +1662 -0
  29. package/package.json +85 -0
  30. package/src/core/engine.test.ts +1421 -0
  31. package/src/core/engine.ts +717 -0
  32. package/src/core/index.ts +7 -0
  33. package/src/core/orchestrator.ts +590 -0
  34. package/src/core/types.ts +242 -0
  35. package/src/events/index.ts +8 -0
  36. package/src/events/memory.test.ts +109 -0
  37. package/src/events/memory.ts +95 -0
  38. package/src/events/socketio.test.ts +428 -0
  39. package/src/events/socketio.ts +269 -0
  40. package/src/events/types.ts +121 -0
  41. package/src/events/webhook.test.ts +695 -0
  42. package/src/events/webhook.ts +591 -0
  43. package/src/index.ts +270 -0
  44. package/src/planning/index.ts +52 -0
  45. package/src/planning/planner.ts +601 -0
  46. package/src/planning/planning.test.ts +790 -0
  47. package/src/planning/registry.test.ts +445 -0
  48. package/src/planning/registry.ts +369 -0
  49. package/src/planning/types.ts +570 -0
  50. package/src/scheduler/cron.test.ts +480 -0
  51. package/src/scheduler/cron.ts +449 -0
  52. package/src/scheduler/index.ts +18 -0
  53. package/src/scheduler/postgres-persistence.test.ts +906 -0
  54. package/src/scheduler/postgres-persistence.ts +546 -0
  55. package/src/scheduler/sqlite-persistence.test.ts +235 -0
  56. package/src/scheduler/sqlite-persistence.ts +268 -0
  57. package/src/scheduler/types.ts +78 -0
  58. package/src/storage/index.ts +27 -0
  59. package/src/storage/memory.test.ts +399 -0
  60. package/src/storage/memory.ts +248 -0
  61. package/src/storage/postgres.ts +1550 -0
  62. package/src/storage/sqlite.test.ts +593 -0
  63. package/src/storage/sqlite.ts +662 -0
  64. package/src/storage/types.ts +368 -0
  65. package/src/utils/errors.test.ts +152 -0
  66. package/src/utils/errors.ts +191 -0
  67. package/src/utils/id.ts +21 -0
  68. package/src/utils/index.ts +8 -0
  69. package/src/utils/logger.test.ts +127 -0
  70. package/src/utils/logger.ts +96 -0
  71. package/src/utils/postgres-deps.ts +63 -0
  72. package/src/utils/retry.test.ts +197 -0
  73. package/src/utils/retry.ts +121 -0
  74. package/tsconfig.json +21 -0
  75. package/tsup.config.ts +14 -0
  76. package/vitest.config.ts +9 -0
@@ -0,0 +1,30 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ release:
5
+ types: [created]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: read
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - uses: actions/setup-node@v4
16
+ with:
17
+ node-version: '20'
18
+ registry-url: 'https://registry.npmjs.org'
19
+ scope: '@multiplier-labs'
20
+
21
+ - name: Install dependencies
22
+ run: npm ci
23
+
24
+ - name: Build
25
+ run: npm run build
26
+
27
+ - name: Publish
28
+ run: npm publish --access public
29
+ env:
30
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/README.md ADDED
@@ -0,0 +1,408 @@
1
+ # Stepflow
2
+
3
+ A durable, type-safe workflow orchestration engine for Node.js with SQLite or PostgreSQL persistence, scheduling, and real-time events.
4
+
5
+ ## Features
6
+
7
+ - **Durable Execution**: Steps are persisted to SQLite or PostgreSQL, allowing workflows to resume after crashes
8
+ - **Type-Safe**: Full TypeScript support with inferred types for inputs, outputs, and step results
9
+ - **Step Orchestration**: Sequential step execution with automatic retry and timeout support
10
+ - **Scheduling**: Cron-based scheduling and workflow completion triggers
11
+ - **Real-Time Events**: Socket.IO and webhook adapters for event streaming
12
+ - **Concurrency Control**: Limit concurrent workflow runs with priority queues
13
+ - **Multiple Storage Backends**: Choose SQLite for simplicity or PostgreSQL for distributed deployments
14
+ - **Planning System**: Rule-based recipe selection and dynamic plan generation
15
+
16
+ ## Installation
17
+
18
+ This package is published to GitHub Packages. See [docs/github-packages-setup.md](docs/github-packages-setup.md) for authentication setup.
19
+
20
+ ```bash
21
+ npm install @multiplier-labs/stepflow
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ```typescript
27
+ import Database from 'better-sqlite3';
28
+ import { WorkflowEngine, SQLiteStorageAdapter } from '@multiplier-labs/stepflow';
29
+
30
+ // Create engine with SQLite storage
31
+ const db = new Database('./workflows.db');
32
+ const engine = new WorkflowEngine({
33
+ storage: new SQLiteStorageAdapter({ db }),
34
+ });
35
+
36
+ // Register a workflow
37
+ engine.registerWorkflow({
38
+ kind: 'order.process',
39
+ name: 'Process Order',
40
+ steps: [
41
+ {
42
+ key: 'validateOrder',
43
+ name: 'Validate Order',
44
+ handler: async (ctx) => {
45
+ return { valid: true, total: 99.99 };
46
+ },
47
+ },
48
+ {
49
+ key: 'processPayment',
50
+ name: 'Process Payment',
51
+ handler: async (ctx) => {
52
+ const { total } = ctx.results.validateOrder;
53
+ return { transactionId: 'txn-123', amount: total };
54
+ },
55
+ },
56
+ {
57
+ key: 'sendConfirmation',
58
+ name: 'Send Confirmation',
59
+ handler: async (ctx) => {
60
+ return { emailSent: true };
61
+ },
62
+ },
63
+ ],
64
+ });
65
+
66
+ // Start a workflow run
67
+ const runId = await engine.startRun({
68
+ kind: 'order.process',
69
+ input: { orderId: 'order-123', items: ['item-1', 'item-2'] },
70
+ });
71
+
72
+ // Wait for completion
73
+ const result = await engine.waitForRun(runId);
74
+ console.log(result.status); // 'succeeded'
75
+ ```
76
+
77
+ ## Core Concepts
78
+
79
+ ### Workflows
80
+
81
+ A workflow is a series of steps that execute sequentially. Each step's result is persisted, allowing the workflow to resume from where it left off if interrupted.
82
+
83
+ ```typescript
84
+ engine.registerWorkflow({
85
+ kind: 'my.workflow',
86
+ name: 'My Workflow',
87
+ steps: [
88
+ {
89
+ key: 'step1',
90
+ name: 'Step 1',
91
+ handler: async (ctx) => {
92
+ return { data: 'step1 result' };
93
+ },
94
+ },
95
+ {
96
+ key: 'step2',
97
+ name: 'Step 2',
98
+ handler: async (ctx) => {
99
+ // Access previous step results
100
+ const step1Result = ctx.results.step1;
101
+ return { combined: step1Result.data + ' plus step2' };
102
+ },
103
+ },
104
+ ],
105
+ });
106
+ ```
107
+
108
+ ### Step Options
109
+
110
+ Steps support retry logic and timeouts:
111
+
112
+ ```typescript
113
+ engine.registerWorkflow({
114
+ kind: 'resilient.workflow',
115
+ name: 'Resilient Workflow',
116
+ steps: [
117
+ {
118
+ key: 'fetchData',
119
+ name: 'Fetch Data',
120
+ handler: async (ctx) => {
121
+ const response = await fetch(ctx.input.url);
122
+ return response.json();
123
+ },
124
+ onError: 'retry',
125
+ maxRetries: 3,
126
+ retryDelay: 1000,
127
+ timeout: 30000,
128
+ },
129
+ ],
130
+ });
131
+ ```
132
+
133
+ ### Workflow Timeouts
134
+
135
+ Set a maximum duration for entire workflow runs via engine settings:
136
+
137
+ ```typescript
138
+ const engine = new WorkflowEngine({
139
+ storage,
140
+ settings: {
141
+ defaultTimeout: 60000, // 1 minute default timeout for all runs
142
+ },
143
+ });
144
+ ```
145
+
146
+ ### Concurrency Control
147
+
148
+ Limit concurrent workflow executions with priority queues:
149
+
150
+ ```typescript
151
+ const engine = new WorkflowEngine({
152
+ storage,
153
+ settings: {
154
+ maxConcurrency: 5, // Max 5 concurrent runs
155
+ },
156
+ });
157
+
158
+ // Higher priority runs execute first
159
+ await engine.startRun({
160
+ kind: 'urgent.workflow',
161
+ input: { data: 'urgent' },
162
+ priority: 10,
163
+ });
164
+
165
+ await engine.startRun({
166
+ kind: 'normal.workflow',
167
+ input: { data: 'normal' },
168
+ priority: 1,
169
+ });
170
+ ```
171
+
172
+ ## Lifecycle
173
+
174
+ The engine supports explicit initialization and shutdown:
175
+
176
+ ```typescript
177
+ const engine = new WorkflowEngine({
178
+ storage: new PostgresStorageAdapter({
179
+ connectionString: 'postgresql://localhost:5432/myapp',
180
+ }),
181
+ });
182
+
183
+ // Initialize storage (required for PostgreSQL, creates tables)
184
+ await engine.initialize();
185
+
186
+ // ... use the engine ...
187
+
188
+ // Graceful shutdown (cancels active runs, closes connections)
189
+ await engine.shutdown();
190
+ ```
191
+
192
+ ## Storage
193
+
194
+ ### In-Memory Storage
195
+
196
+ For testing and development:
197
+
198
+ ```typescript
199
+ import { MemoryStorageAdapter } from '@multiplier-labs/stepflow';
200
+
201
+ const storage = new MemoryStorageAdapter();
202
+ ```
203
+
204
+ ### SQLite Storage
205
+
206
+ For single-process deployments:
207
+
208
+ ```typescript
209
+ import Database from 'better-sqlite3';
210
+ import { SQLiteStorageAdapter } from '@multiplier-labs/stepflow';
211
+
212
+ const db = new Database('./workflows.db'); // File path or ':memory:'
213
+ const storage = new SQLiteStorageAdapter({ db });
214
+ ```
215
+
216
+ ### PostgreSQL Storage
217
+
218
+ For production use with distributed workers:
219
+
220
+ ```typescript
221
+ import { PostgresStorageAdapter } from '@multiplier-labs/stepflow';
222
+
223
+ // Option 1: Connection string
224
+ const storage = new PostgresStorageAdapter({
225
+ connectionString: 'postgresql://user:pass@localhost:5432/myapp',
226
+ schema: 'myapp', // Optional, defaults to 'public'
227
+ });
228
+
229
+ // Option 2: Share existing connection pool
230
+ import pg from 'pg';
231
+
232
+ const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
233
+ const storage = new PostgresStorageAdapter({ pool });
234
+ ```
235
+
236
+ **Note:** `pg` and `kysely` are optional peer dependencies. They are loaded dynamically at runtime only when `PostgresStorageAdapter` is initialized, so users of other storage backends are not affected.
237
+
238
+ PostgreSQL storage provides:
239
+ - **Atomic dequeue**: Safe concurrent processing with `FOR UPDATE SKIP LOCKED`
240
+ - **Connection pooling**: Share database connections with your application
241
+ - **Schema isolation**: Custom schema names for multi-tenant deployments
242
+ - **Stale workflow cleanup**: Automatic timeout handling
243
+
244
+ ## Event Transports
245
+
246
+ ### Memory Events
247
+
248
+ Simple in-memory event emitter:
249
+
250
+ ```typescript
251
+ import { MemoryEventTransport } from '@multiplier-labs/stepflow';
252
+
253
+ const events = new MemoryEventTransport();
254
+ const engine = new WorkflowEngine({ storage, events });
255
+
256
+ // Subscribe to events
257
+ events.subscribeAll((event) => {
258
+ console.log(`Event: ${event.eventType} for run ${event.runId}`);
259
+ });
260
+ ```
261
+
262
+ ### Socket.IO Events
263
+
264
+ Real-time events via Socket.IO:
265
+
266
+ ```typescript
267
+ import { Server } from 'socket.io';
268
+ import { SocketIOEventTransport } from '@multiplier-labs/stepflow';
269
+
270
+ const io = new Server(httpServer);
271
+ const events = new SocketIOEventTransport({ io });
272
+
273
+ const engine = new WorkflowEngine({ storage, events });
274
+
275
+ // Set up client handlers with authorization
276
+ io.on('connection', (socket) => {
277
+ events.setupClientHandlers(socket, async (runId, socket) => {
278
+ return /* authorization check */;
279
+ });
280
+ });
281
+
282
+ // Client-side:
283
+ // socket.emit('workflow:subscribe', runId);
284
+ // socket.on('workflow:event', (event) => console.log(event));
285
+ ```
286
+
287
+ ### Webhook Events
288
+
289
+ POST events to HTTP endpoints:
290
+
291
+ ```typescript
292
+ import { WebhookEventTransport } from '@multiplier-labs/stepflow';
293
+
294
+ const events = new WebhookEventTransport({
295
+ endpoints: [
296
+ {
297
+ id: 'slack',
298
+ url: 'https://hooks.slack.com/...',
299
+ eventTypes: ['run.completed', 'run.failed'],
300
+ },
301
+ {
302
+ id: 'analytics',
303
+ url: 'https://api.analytics.com/events',
304
+ secret: 'webhook-secret', // HMAC-SHA256 signing
305
+ },
306
+ ],
307
+ });
308
+
309
+ const engine = new WorkflowEngine({ storage, events });
310
+ ```
311
+
312
+ ## Scheduling
313
+
314
+ ### Cron Scheduler
315
+
316
+ Schedule workflows using cron expressions:
317
+
318
+ ```typescript
319
+ import Database from 'better-sqlite3';
320
+ import { CronScheduler, SQLiteSchedulePersistence, SQLiteStorageAdapter } from '@multiplier-labs/stepflow';
321
+
322
+ const db = new Database('./workflows.db');
323
+ const storage = new SQLiteStorageAdapter({ db });
324
+ const engine = new WorkflowEngine({ storage });
325
+
326
+ const schedulePersistence = new SQLiteSchedulePersistence({ db });
327
+ const scheduler = new CronScheduler({
328
+ engine,
329
+ persistence: schedulePersistence,
330
+ });
331
+
332
+ // Schedule a workflow to run every hour
333
+ await scheduler.addSchedule({
334
+ id: 'hourly-cleanup',
335
+ workflowKind: 'cleanup.workflow',
336
+ triggerType: 'cron',
337
+ cronExpression: '0 * * * *', // Every hour
338
+ timezone: 'America/New_York',
339
+ input: { dryRun: false },
340
+ });
341
+
342
+ // Start the scheduler
343
+ await scheduler.start();
344
+ ```
345
+
346
+ ### Workflow Completion Triggers
347
+
348
+ Trigger workflows when other workflows complete:
349
+
350
+ ```typescript
351
+ await scheduler.addSchedule({
352
+ id: 'post-order-notification',
353
+ workflowKind: 'notification.send',
354
+ triggerType: 'workflow_completed',
355
+ triggerOnWorkflowKind: 'order.process',
356
+ triggerOnStatus: ['succeeded', 'failed'],
357
+ input: { template: 'order-status' },
358
+ });
359
+ ```
360
+
361
+ ## Event Types
362
+
363
+ The engine emits the following event types:
364
+
365
+ - `run.created` - Workflow run created
366
+ - `run.queued` - Run queued (when at max concurrency)
367
+ - `run.dequeued` - Run dequeued and starting
368
+ - `run.started` - Run execution started
369
+ - `run.resumed` - Run resumed after interruption
370
+ - `run.completed` - Run completed successfully
371
+ - `run.failed` - Run failed with error
372
+ - `run.canceled` - Run was canceled
373
+ - `run.timeout` - Run exceeded timeout
374
+ - `step.started` - Step execution started
375
+ - `step.completed` - Step completed successfully
376
+ - `step.failed` - Step failed (may retry)
377
+ - `step.skipped` - Step skipped (already completed)
378
+ - `step.retry` - Step retrying after failure
379
+
380
+ ## Planning System
381
+
382
+ Stepflow includes a rule-based planning system for dynamic workflow generation:
383
+
384
+ ```typescript
385
+ import {
386
+ RuleBasedPlanner,
387
+ MemoryRecipeRegistry,
388
+ MemoryStepHandlerRegistry,
389
+ createRegistry,
390
+ } from '@multiplier-labs/stepflow';
391
+
392
+ // Create registries for recipes and step handlers
393
+ const { recipes, handlers } = createRegistry();
394
+
395
+ // Register recipes and handlers, then create plans
396
+ const planner = new RuleBasedPlanner({ recipeRegistry: recipes, handlerRegistry: handlers });
397
+ const plan = await planner.plan('process-order', { orderId: '123' });
398
+ ```
399
+
400
+ See the [API Reference](docs/stepflow-api-reference.md#planning-system) for full documentation on recipes, step handlers, and planner configuration.
401
+
402
+ ## API Reference
403
+
404
+ For the complete API reference including all classes, interfaces, and configuration options, see [docs/stepflow-api-reference.md](docs/stepflow-api-reference.md).
405
+
406
+ ## License
407
+
408
+ MIT