runsheet 0.5.0 → 0.6.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 CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Type-safe, composable business logic pipelines for TypeScript.
7
7
 
8
- Built on [composable-functions] for `Result` semantics and error handling.
8
+ Pipelines are steps compose them freely, nest them arbitrarily.
9
9
 
10
10
  ## Why runsheet
11
11
 
@@ -44,7 +44,7 @@ A pipeline orchestration library with:
44
44
  on hover. Both sync and async `run` functions are supported.
45
45
  - **Type-safe accumulated context** — each step declares what it requires and
46
46
  provides. TypeScript enforces at compile time that requirements are satisfied,
47
- and `buildPipeline` infers the full output type from the steps you pass.
47
+ and `pipeline` infers the full output type from the steps you pass.
48
48
  - **Immutable step boundaries** — context is frozen between steps. Each step
49
49
  receives a snapshot and returns only what it adds.
50
50
  - **Rollback with snapshots** — on failure, rollback handlers execute in reverse
@@ -127,9 +127,9 @@ integrity from one step to the next.
127
127
  ### Build and run a pipeline
128
128
 
129
129
  ```typescript
130
- import { buildPipeline } from 'runsheet';
130
+ import { pipeline } from 'runsheet';
131
131
 
132
- const placeOrder = buildPipeline({
132
+ const placeOrder = pipeline({
133
133
  name: 'placeOrder',
134
134
  steps: [validateOrder, chargePayment, sendConfirmation],
135
135
  });
@@ -140,7 +140,7 @@ if (result.success) {
140
140
  console.log(result.data.chargeId); // string — fully typed
141
141
  console.log(result.data.sentAt); // Date
142
142
  } else {
143
- console.error(result.errors); // what went wrong
143
+ console.error(result.error); // what went wrong
144
144
  console.log(result.rollback); // { completed: [...], failed: [...] }
145
145
  }
146
146
  ```
@@ -148,19 +148,35 @@ if (result.success) {
148
148
  The pipeline's result type is inferred from the steps — `result.data` carries
149
149
  the intersection of all step outputs, not an erased `Record<string, unknown>`.
150
150
 
151
+ ### Pipeline composition
152
+
153
+ Pipelines are steps — use one pipeline as a step in another:
154
+
155
+ ```typescript
156
+ const checkout = pipeline({
157
+ name: 'checkout',
158
+ steps: [validateOrder, chargePayment, sendConfirmation],
159
+ });
160
+
161
+ const fullFlow = pipeline({
162
+ name: 'fullFlow',
163
+ steps: [checkout, shipOrder, notifyWarehouse],
164
+ });
165
+ ```
166
+
151
167
  ### Builder API
152
168
 
153
169
  For complex pipelines, the builder gives progressive type narrowing — each
154
170
  `.step()` call extends the known context type:
155
171
 
156
172
  ```typescript
157
- import { createPipeline } from 'runsheet';
173
+ import { pipeline } from 'runsheet';
158
174
  import { z } from 'zod';
159
175
 
160
- const placeOrder = createPipeline(
161
- 'placeOrder',
162
- z.object({ orderId: z.string() }),
163
- )
176
+ const placeOrder = pipeline({
177
+ name: 'placeOrder',
178
+ argsSchema: z.object({ orderId: z.string() }),
179
+ })
164
180
  .step(validateOrder) // context now includes order
165
181
  .step(chargePayment) // context now includes chargeId
166
182
  .step(sendConfirmation) // context now includes sentAt
@@ -170,7 +186,7 @@ const placeOrder = createPipeline(
170
186
  Type-only args (no runtime validation of pipeline input):
171
187
 
172
188
  ```typescript
173
- const placeOrder = createPipeline<{ orderId: string }>('placeOrder')
189
+ const placeOrder = pipeline<{ orderId: string }>({ name: 'placeOrder' })
174
190
  .step(validateOrder)
175
191
  .step(chargePayment)
176
192
  .step(sendConfirmation)
@@ -197,7 +213,7 @@ const logOrder = defineStep<{ order: { id: string } }, { loggedAt: Date }>({
197
213
  ```typescript
198
214
  import { when } from 'runsheet';
199
215
 
200
- const placeOrder = buildPipeline({
216
+ const placeOrder = pipeline({
201
217
  name: 'placeOrder',
202
218
  steps: [
203
219
  validateOrder,
@@ -208,15 +224,15 @@ const placeOrder = buildPipeline({
208
224
  });
209
225
  ```
210
226
 
211
- Skipped steps produce no snapshot, no rollback entry. The pipeline result tracks
212
- which steps were skipped in `result.meta.stepsSkipped`.
227
+ Skipped steps produce no snapshot, no rollback entry, and do not appear in
228
+ `result.meta.stepsExecuted`.
213
229
 
214
230
  ### Middleware
215
231
 
216
232
  Middleware wraps the entire step lifecycle including schema validation:
217
233
 
218
234
  ```typescript
219
- import { buildPipeline } from 'runsheet';
235
+ import { pipeline } from 'runsheet';
220
236
  import type { StepMiddleware } from 'runsheet';
221
237
 
222
238
  const timing: StepMiddleware = (step, next) => async (ctx) => {
@@ -233,7 +249,7 @@ const logging: StepMiddleware = (step, next) => async (ctx) => {
233
249
  return result;
234
250
  };
235
251
 
236
- const placeOrder = buildPipeline({
252
+ const placeOrder = pipeline({
237
253
  name: 'placeOrder',
238
254
  steps: [validateOrder, chargePayment, sendConfirmation],
239
255
  middleware: [logging, timing],
@@ -243,7 +259,7 @@ const placeOrder = buildPipeline({
243
259
  Middleware with the builder:
244
260
 
245
261
  ```typescript
246
- const placeOrder = createPipeline<{ orderId: string }>('placeOrder')
262
+ const placeOrder = pipeline<{ orderId: string }>({ name: 'placeOrder' })
247
263
  .use(logging, timing)
248
264
  .step(validateOrder)
249
265
  .step(chargePayment)
@@ -289,7 +305,7 @@ Run steps concurrently with `parallel()`. Outputs merge in array order:
289
305
  ```typescript
290
306
  import { parallel } from 'runsheet';
291
307
 
292
- const placeOrder = buildPipeline({
308
+ const placeOrder = pipeline({
293
309
  name: 'placeOrder',
294
310
  steps: [
295
311
  validateOrder,
@@ -323,16 +339,16 @@ const chargePayment = defineStep({
323
339
  },
324
340
  });
325
341
 
326
- const pipeline = createPipeline<{
342
+ const placeOrder = pipeline<{
327
343
  orderId: string;
328
344
  stripe: Stripe;
329
345
  db: Database;
330
- }>('placeOrder')
346
+ }>({ name: 'placeOrder' })
331
347
  .step(validateOrder)
332
348
  .step(chargePayment)
333
349
  .build();
334
350
 
335
- await pipeline.run({
351
+ await placeOrder.run({
336
352
  orderId: '123',
337
353
  stripe: stripeClient,
338
354
  db: dbClient,
@@ -352,7 +368,7 @@ Functions Choice state:
352
368
  ```typescript
353
369
  import { choice } from 'runsheet';
354
370
 
355
- const placeOrder = buildPipeline({
371
+ const placeOrder = pipeline({
356
372
  name: 'placeOrder',
357
373
  steps: [
358
374
  validateOrder,
@@ -380,7 +396,7 @@ like an AWS Step Functions Map state:
380
396
  import { map } from 'runsheet';
381
397
 
382
398
  // Function form — items can be any type
383
- const pipeline = buildPipeline({
399
+ const p = pipeline({
384
400
  name: 'notify',
385
401
  steps: [
386
402
  map(
@@ -395,7 +411,7 @@ const pipeline = buildPipeline({
395
411
  });
396
412
 
397
413
  // Step form — reuse existing steps
398
- const pipeline = buildPipeline({
414
+ const p = pipeline({
399
415
  name: 'process',
400
416
  steps: [map('results', (ctx) => ctx.items, processItem)],
401
417
  });
@@ -412,7 +428,7 @@ only).
412
428
  ```typescript
413
429
  import { filter, map } from 'runsheet';
414
430
 
415
- const pipeline = buildPipeline({
431
+ const p = pipeline({
416
432
  name: 'notify',
417
433
  steps: [
418
434
  filter(
@@ -444,7 +460,7 @@ pure operation).
444
460
  ```typescript
445
461
  import { flatMap } from 'runsheet';
446
462
 
447
- const pipeline = buildPipeline({
463
+ const p = pipeline({
448
464
  name: 'process',
449
465
  steps: [
450
466
  flatMap(
@@ -501,29 +517,27 @@ if (!result.success) {
501
517
  }
502
518
  ```
503
519
 
504
- ## Pipeline result
520
+ ## Step result
505
521
 
506
- Every pipeline returns a `PipelineResult` with execution metadata:
522
+ Every `run()` returns a `StepResult` with execution metadata:
507
523
 
508
524
  ```typescript
509
525
  // Success
510
526
  {
511
527
  success: true,
512
528
  data: { /* accumulated context — fully typed */ },
513
- errors: [],
514
529
  meta: {
515
- pipeline: 'placeOrder',
530
+ name: 'placeOrder',
516
531
  args: { orderId: '123' },
517
532
  stepsExecuted: ['validateOrder', 'chargePayment', 'sendConfirmation'],
518
- stepsSkipped: [],
519
533
  }
520
534
  }
521
535
 
522
536
  // Failure
523
537
  {
524
538
  success: false,
525
- errors: [Error],
526
- meta: { pipeline, args, stepsExecuted, stepsSkipped },
539
+ error: Error,
540
+ meta: { name, args, stepsExecuted },
527
541
  failedStep: 'chargePayment',
528
542
  rollback: { completed: [...], failed: [...] },
529
543
  }
@@ -547,42 +561,25 @@ schemas or generics you provide.
547
561
  | `retry` | `RetryPolicy` | Optional retry policy for transient failures |
548
562
  | `timeout` | `number` | Optional max duration in ms for the `run` function |
549
563
 
550
- ### `buildPipeline(config)`
564
+ ### `pipeline(config)`
551
565
 
552
- Build a pipeline from an array of steps. The result type is inferred from the
553
- steps `pipeline.run()` returns a `PipelineResult` whose `data` is the
554
- intersection of all step output types.
566
+ Create a pipeline. When `steps` is provided, returns an `AggregateStep`
567
+ immediately. When `steps` is omitted, returns a `PipelineBuilder` with
568
+ `.step()`, `.use()`, and `.build()` for progressive type narrowing.
555
569
 
556
570
  | Option | Type | Description |
557
571
  | ------------ | ------------------ | ----------------------------------------------------------------- |
558
572
  | `name` | `string` | Pipeline name |
559
- | `steps` | `Step[]` | Steps to execute in order |
573
+ | `steps` | `Step[]` | Steps to execute in order (omit for builder mode) |
560
574
  | `middleware` | `StepMiddleware[]` | Optional middleware |
561
575
  | `argsSchema` | `ZodSchema` | Optional schema for pipeline input validation |
562
576
  | `strict` | `boolean` | Optional — throws at build time if two steps provide the same key |
563
577
 
564
- ### `createPipeline(name, argsSchema?, options?)`
565
-
566
- Start a fluent pipeline builder. Returns a `PipelineBuilder` with:
567
-
568
- - `.step(step)` — add a step
569
- - `.use(...middleware)` — add middleware
570
- - `.build()` — produce the pipeline
571
-
572
- The second argument accepts a schema (for runtime args validation) or an options
573
- object:
574
-
575
- ```typescript
576
- createPipeline('order', z.object({ id: z.string() }));
577
- createPipeline('order', { strict: true });
578
- createPipeline('order', z.object({ id: z.string() }), { strict: true });
579
- ```
580
-
581
578
  ### `parallel(...steps)`
582
579
 
583
580
  Run steps concurrently and merge their outputs. Returns a single step usable
584
581
  anywhere a regular step is accepted. On partial failure, succeeded inner steps
585
- are rolled back before the error propagates. p
582
+ are rolled back before the error propagates.
586
583
 
587
584
  ### `choice(...branches)`
588
585
 
@@ -629,7 +626,6 @@ MIT
629
626
  [ci-badge]:
630
627
  https://github.com/shaug/runsheet-js/actions/workflows/ci.yml/badge.svg
631
628
  [ci-url]: https://github.com/shaug/runsheet-js/actions/workflows/ci.yml
632
- [composable-functions]: https://github.com/seasonedcc/composable-functions
633
629
  [license-badge]: https://img.shields.io/npm/l/runsheet
634
630
  [license-url]: https://github.com/shaug/runsheet-js/blob/main/LICENSE
635
631
  [npm-badge]: https://img.shields.io/npm/v/runsheet