runsheet 0.4.0 → 0.5.0
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/README.md +195 -0
- package/dist/index.cjs +433 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +217 -4
- package/dist/index.d.ts +217 -4
- package/dist/index.js +419 -51
- package/dist/index.js.map +1 -1
- package/llms.txt +138 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -33,6 +33,10 @@ and runsheet makes sure they execute in order with clear contracts between them.
|
|
|
33
33
|
|
|
34
34
|
## What this is
|
|
35
35
|
|
|
36
|
+
Args persist and outputs accumulate. That's the core model — initial arguments
|
|
37
|
+
flow through the entire pipeline, each step's output merges into the context,
|
|
38
|
+
and every step sees the full picture of everything before it.
|
|
39
|
+
|
|
36
40
|
A pipeline orchestration library with:
|
|
37
41
|
|
|
38
42
|
- **Strongly typed steps** — each step's `run`, `rollback`, `requires`, and
|
|
@@ -300,6 +304,172 @@ propagates. Inner steps retain their own `requires`/`provides` validation,
|
|
|
300
304
|
`retry`, and `timeout` behavior. Conditional steps (via `when()`) work inside
|
|
301
305
|
`parallel()`.
|
|
302
306
|
|
|
307
|
+
## Dependency injection
|
|
308
|
+
|
|
309
|
+
No special mechanism needed — pass dependencies as pipeline args and they're
|
|
310
|
+
available to every step through the accumulated context:
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
const chargePayment = defineStep({
|
|
314
|
+
name: 'chargePayment',
|
|
315
|
+
requires: z.object({
|
|
316
|
+
order: z.object({ total: z.number() }),
|
|
317
|
+
stripe: z.custom<Stripe>(),
|
|
318
|
+
}),
|
|
319
|
+
provides: z.object({ chargeId: z.string() }),
|
|
320
|
+
run: async (ctx) => {
|
|
321
|
+
const charge = await ctx.stripe.charges.create({ amount: ctx.order.total });
|
|
322
|
+
return { chargeId: charge.id };
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const pipeline = createPipeline<{
|
|
327
|
+
orderId: string;
|
|
328
|
+
stripe: Stripe;
|
|
329
|
+
db: Database;
|
|
330
|
+
}>('placeOrder')
|
|
331
|
+
.step(validateOrder)
|
|
332
|
+
.step(chargePayment)
|
|
333
|
+
.build();
|
|
334
|
+
|
|
335
|
+
await pipeline.run({
|
|
336
|
+
orderId: '123',
|
|
337
|
+
stripe: stripeClient,
|
|
338
|
+
db: dbClient,
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Args persist through the entire pipeline without any step needing to `provides`
|
|
343
|
+
them. TypeScript enforces at compile time that every step's `requires` are
|
|
344
|
+
satisfied by the accumulated context. For testing, swap in mocks at the call
|
|
345
|
+
site.
|
|
346
|
+
|
|
347
|
+
## Choice (branching)
|
|
348
|
+
|
|
349
|
+
Execute the first branch whose predicate returns `true` — like an AWS Step
|
|
350
|
+
Functions Choice state:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
import { choice } from 'runsheet';
|
|
354
|
+
|
|
355
|
+
const placeOrder = buildPipeline({
|
|
356
|
+
name: 'placeOrder',
|
|
357
|
+
steps: [
|
|
358
|
+
validateOrder,
|
|
359
|
+
choice(
|
|
360
|
+
[(ctx) => ctx.method === 'card', chargeCard],
|
|
361
|
+
[(ctx) => ctx.method === 'bank', chargeBankTransfer],
|
|
362
|
+
chargeDefault, // default (bare step)
|
|
363
|
+
),
|
|
364
|
+
sendConfirmation,
|
|
365
|
+
],
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Predicates are evaluated in order — first match wins. A bare step (without a
|
|
370
|
+
tuple) can be passed as the last argument to serve as a default — equivalent to
|
|
371
|
+
`[() => true, step]`. If no predicate matches, the step fails with a
|
|
372
|
+
`CHOICE_NO_MATCH` error. Only the matched branch participates in rollback.
|
|
373
|
+
|
|
374
|
+
## Map (collection iteration)
|
|
375
|
+
|
|
376
|
+
Iterate over a collection and run a function or step per item, concurrently —
|
|
377
|
+
like an AWS Step Functions Map state:
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
import { map } from 'runsheet';
|
|
381
|
+
|
|
382
|
+
// Function form — items can be any type
|
|
383
|
+
const pipeline = buildPipeline({
|
|
384
|
+
name: 'notify',
|
|
385
|
+
steps: [
|
|
386
|
+
map(
|
|
387
|
+
'emails',
|
|
388
|
+
(ctx) => ctx.users,
|
|
389
|
+
async (user) => {
|
|
390
|
+
await sendEmail(user.email);
|
|
391
|
+
return { email: user.email, sentAt: new Date() };
|
|
392
|
+
},
|
|
393
|
+
),
|
|
394
|
+
],
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Step form — reuse existing steps
|
|
398
|
+
const pipeline = buildPipeline({
|
|
399
|
+
name: 'process',
|
|
400
|
+
steps: [map('results', (ctx) => ctx.items, processItem)],
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Items run concurrently via `Promise.allSettled`. Results are collected into an
|
|
405
|
+
array under the given key. In step form, each item is spread into the pipeline
|
|
406
|
+
context (`{ ...ctx, ...item }`) so the step sees both pipeline-level and
|
|
407
|
+
per-item values. On partial failure, succeeded items are rolled back (step form
|
|
408
|
+
only).
|
|
409
|
+
|
|
410
|
+
### Filter (collection filtering)
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
import { filter, map } from 'runsheet';
|
|
414
|
+
|
|
415
|
+
const pipeline = buildPipeline({
|
|
416
|
+
name: 'notify',
|
|
417
|
+
steps: [
|
|
418
|
+
filter(
|
|
419
|
+
'eligible',
|
|
420
|
+
(ctx) => ctx.users,
|
|
421
|
+
(user) => user.optedIn,
|
|
422
|
+
),
|
|
423
|
+
map('emails', (ctx) => ctx.eligible, sendEmail),
|
|
424
|
+
],
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// Async predicate
|
|
428
|
+
filter(
|
|
429
|
+
'valid',
|
|
430
|
+
(ctx) => ctx.orders,
|
|
431
|
+
async (order) => {
|
|
432
|
+
const inventory = await checkInventory(order.sku);
|
|
433
|
+
return inventory.available >= order.quantity;
|
|
434
|
+
},
|
|
435
|
+
);
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Predicates run concurrently via `Promise.allSettled`. Original order is
|
|
439
|
+
preserved. If any predicate throws, the step fails. No rollback (filtering is a
|
|
440
|
+
pure operation).
|
|
441
|
+
|
|
442
|
+
### FlatMap (collection expansion)
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { flatMap } from 'runsheet';
|
|
446
|
+
|
|
447
|
+
const pipeline = buildPipeline({
|
|
448
|
+
name: 'process',
|
|
449
|
+
steps: [
|
|
450
|
+
flatMap(
|
|
451
|
+
'lineItems',
|
|
452
|
+
(ctx) => ctx.orders,
|
|
453
|
+
(order) => order.items,
|
|
454
|
+
),
|
|
455
|
+
],
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Async callback
|
|
459
|
+
flatMap(
|
|
460
|
+
'emails',
|
|
461
|
+
(ctx) => ctx.teams,
|
|
462
|
+
async (team) => {
|
|
463
|
+
const members = await fetchMembers(team.id);
|
|
464
|
+
return members.map((m) => m.email);
|
|
465
|
+
},
|
|
466
|
+
);
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Maps each item to an array, then flattens one level. Callbacks run concurrently
|
|
470
|
+
via `Promise.allSettled`. If any callback throws, the step fails. No rollback
|
|
471
|
+
(pure operation).
|
|
472
|
+
|
|
303
473
|
## Rollback
|
|
304
474
|
|
|
305
475
|
When a step fails, rollback handlers for all previously completed steps execute
|
|
@@ -414,6 +584,31 @@ Run steps concurrently and merge their outputs. Returns a single step usable
|
|
|
414
584
|
anywhere a regular step is accepted. On partial failure, succeeded inner steps
|
|
415
585
|
are rolled back before the error propagates. p
|
|
416
586
|
|
|
587
|
+
### `choice(...branches)`
|
|
588
|
+
|
|
589
|
+
Execute the first branch whose predicate returns `true`. Each branch is a
|
|
590
|
+
`[predicate, step]` tuple. A bare step can be passed as the last argument as a
|
|
591
|
+
default. Returns a single step usable anywhere a regular step is accepted. Only
|
|
592
|
+
the matched branch participates in rollback.
|
|
593
|
+
|
|
594
|
+
### `map(key, collection, fnOrStep)`
|
|
595
|
+
|
|
596
|
+
Iterate over a collection and run a function or step per item, concurrently.
|
|
597
|
+
Results are collected into `{ [key]: Result[] }`. Accepts a plain function
|
|
598
|
+
`(item, ctx) => result` or a `TypedStep` (items must be objects, spread into
|
|
599
|
+
context). Step form supports per-item rollback on partial and external failure.
|
|
600
|
+
|
|
601
|
+
### `filter(key, collection, predicate)`
|
|
602
|
+
|
|
603
|
+
Filter a collection from context using a sync or async predicate. Predicates run
|
|
604
|
+
concurrently. Items where the predicate returns `true` are kept; original order
|
|
605
|
+
is preserved. Results are collected into `{ [key]: Item[] }`. No rollback.
|
|
606
|
+
|
|
607
|
+
### `flatMap(key, collection, fn)`
|
|
608
|
+
|
|
609
|
+
Map each item in a collection to an array, then flatten one level. Callbacks run
|
|
610
|
+
concurrently. Results are collected into `{ [key]: Result[] }`. No rollback.
|
|
611
|
+
|
|
417
612
|
### `when(predicate, step)`
|
|
418
613
|
|
|
419
614
|
Wrap a step with a conditional predicate. The step only executes when the
|