runsheet 0.0.1 → 0.4.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 +85 -14
- package/dist/index.cjs +206 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +115 -5
- package/dist/index.d.ts +115 -5
- package/dist/index.js +205 -18
- package/dist/index.js.map +1 -1
- package/llms.txt +95 -6
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -247,6 +247,59 @@ const placeOrder = createPipeline<{ orderId: string }>('placeOrder')
|
|
|
247
247
|
.build();
|
|
248
248
|
```
|
|
249
249
|
|
|
250
|
+
## Retry and timeout
|
|
251
|
+
|
|
252
|
+
Steps can declare retry policies and timeouts directly:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
const callExternalApi = defineStep({
|
|
256
|
+
name: 'callExternalApi',
|
|
257
|
+
provides: z.object({ response: z.string() }),
|
|
258
|
+
retry: { count: 3, delay: 200, backoff: 'exponential' },
|
|
259
|
+
timeout: 5000,
|
|
260
|
+
run: async () => {
|
|
261
|
+
const res = await fetch('https://api.example.com/data');
|
|
262
|
+
return { response: await res.text() };
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Retry** re-executes the step's `run` function on failure. The `retryIf`
|
|
268
|
+
predicate lets you inspect errors and decide whether to retry:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
retry: {
|
|
272
|
+
count: 3,
|
|
273
|
+
retryIf: (errors) => errors.some((e) => e.message.includes('ECONNRESET')),
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Timeout** races `run` against a timer. If the step exceeds the limit, it fails
|
|
278
|
+
with a `RunsheetError` code `'TIMEOUT'`. When both are set, each retry attempt
|
|
279
|
+
gets its own timeout.
|
|
280
|
+
|
|
281
|
+
## Parallel steps
|
|
282
|
+
|
|
283
|
+
Run steps concurrently with `parallel()`. Outputs merge in array order:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { parallel } from 'runsheet';
|
|
287
|
+
|
|
288
|
+
const placeOrder = buildPipeline({
|
|
289
|
+
name: 'placeOrder',
|
|
290
|
+
steps: [
|
|
291
|
+
validateOrder,
|
|
292
|
+
parallel(reserveInventory, chargePayment),
|
|
293
|
+
sendConfirmation,
|
|
294
|
+
],
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
On partial failure, succeeded inner steps are rolled back before the error
|
|
299
|
+
propagates. Inner steps retain their own `requires`/`provides` validation,
|
|
300
|
+
`retry`, and `timeout` behavior. Conditional steps (via `when()`) work inside
|
|
301
|
+
`parallel()`.
|
|
302
|
+
|
|
250
303
|
## Rollback
|
|
251
304
|
|
|
252
305
|
When a step fails, rollback handlers for all previously completed steps execute
|
|
@@ -314,13 +367,15 @@ Define a pipeline step. Returns a strongly typed `TypedStep` — `run`,
|
|
|
314
367
|
`rollback`, `requires`, and `provides` all carry concrete types matching the
|
|
315
368
|
schemas or generics you provide.
|
|
316
369
|
|
|
317
|
-
| Option | Type | Description
|
|
318
|
-
| ---------- | ----------------------- |
|
|
319
|
-
| `name` | `string` | Step name (used in metadata and rollback reports)
|
|
320
|
-
| `requires` | `ZodSchema` | Optional schema for required context keys
|
|
321
|
-
| `provides` | `ZodSchema` | Optional schema for provided context keys
|
|
322
|
-
| `run` | `(ctx) => output` | Step implementation (sync or async)
|
|
323
|
-
| `rollback` | `(ctx, output) => void` | Optional rollback handler
|
|
370
|
+
| Option | Type | Description |
|
|
371
|
+
| ---------- | ----------------------- | -------------------------------------------------- |
|
|
372
|
+
| `name` | `string` | Step name (used in metadata and rollback reports) |
|
|
373
|
+
| `requires` | `ZodSchema` | Optional schema for required context keys |
|
|
374
|
+
| `provides` | `ZodSchema` | Optional schema for provided context keys |
|
|
375
|
+
| `run` | `(ctx) => output` | Step implementation (sync or async) |
|
|
376
|
+
| `rollback` | `(ctx, output) => void` | Optional rollback handler |
|
|
377
|
+
| `retry` | `RetryPolicy` | Optional retry policy for transient failures |
|
|
378
|
+
| `timeout` | `number` | Optional max duration in ms for the `run` function |
|
|
324
379
|
|
|
325
380
|
### `buildPipeline(config)`
|
|
326
381
|
|
|
@@ -328,14 +383,15 @@ Build a pipeline from an array of steps. The result type is inferred from the
|
|
|
328
383
|
steps — `pipeline.run()` returns a `PipelineResult` whose `data` is the
|
|
329
384
|
intersection of all step output types.
|
|
330
385
|
|
|
331
|
-
| Option | Type | Description
|
|
332
|
-
| ------------ | ------------------ |
|
|
333
|
-
| `name` | `string` | Pipeline name
|
|
334
|
-
| `steps` | `Step[]` | Steps to execute in order
|
|
335
|
-
| `middleware` | `StepMiddleware[]` | Optional middleware
|
|
336
|
-
| `argsSchema` | `ZodSchema` | Optional schema for pipeline input validation
|
|
386
|
+
| Option | Type | Description |
|
|
387
|
+
| ------------ | ------------------ | ----------------------------------------------------------------- |
|
|
388
|
+
| `name` | `string` | Pipeline name |
|
|
389
|
+
| `steps` | `Step[]` | Steps to execute in order |
|
|
390
|
+
| `middleware` | `StepMiddleware[]` | Optional middleware |
|
|
391
|
+
| `argsSchema` | `ZodSchema` | Optional schema for pipeline input validation |
|
|
392
|
+
| `strict` | `boolean` | Optional — throws at build time if two steps provide the same key |
|
|
337
393
|
|
|
338
|
-
### `createPipeline(name, argsSchema?)`
|
|
394
|
+
### `createPipeline(name, argsSchema?, options?)`
|
|
339
395
|
|
|
340
396
|
Start a fluent pipeline builder. Returns a `PipelineBuilder` with:
|
|
341
397
|
|
|
@@ -343,6 +399,21 @@ Start a fluent pipeline builder. Returns a `PipelineBuilder` with:
|
|
|
343
399
|
- `.use(...middleware)` — add middleware
|
|
344
400
|
- `.build()` — produce the pipeline
|
|
345
401
|
|
|
402
|
+
The second argument accepts a schema (for runtime args validation) or an options
|
|
403
|
+
object:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
createPipeline('order', z.object({ id: z.string() }));
|
|
407
|
+
createPipeline('order', { strict: true });
|
|
408
|
+
createPipeline('order', z.object({ id: z.string() }), { strict: true });
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### `parallel(...steps)`
|
|
412
|
+
|
|
413
|
+
Run steps concurrently and merge their outputs. Returns a single step usable
|
|
414
|
+
anywhere a regular step is accepted. On partial failure, succeeded inner steps
|
|
415
|
+
are rolled back before the error propagates. p
|
|
416
|
+
|
|
346
417
|
### `when(predicate, step)`
|
|
347
418
|
|
|
348
419
|
Wrap a step with a conditional predicate. The step only executes when the
|
package/dist/index.cjs
CHANGED
|
@@ -24,27 +24,13 @@ __export(index_exports, {
|
|
|
24
24
|
buildPipeline: () => buildPipeline,
|
|
25
25
|
createPipeline: () => createPipeline,
|
|
26
26
|
defineStep: () => defineStep,
|
|
27
|
+
parallel: () => parallel,
|
|
27
28
|
when: () => when
|
|
28
29
|
});
|
|
29
30
|
module.exports = __toCommonJS(index_exports);
|
|
30
31
|
|
|
31
32
|
// src/define-step.ts
|
|
32
33
|
var import_composable_functions = require("composable-functions");
|
|
33
|
-
function defineStep(config) {
|
|
34
|
-
const wrappedRun = (0, import_composable_functions.composable)(config.run);
|
|
35
|
-
return Object.freeze({
|
|
36
|
-
name: config.name,
|
|
37
|
-
requires: config.requires ?? void 0,
|
|
38
|
-
provides: config.provides ?? void 0,
|
|
39
|
-
run: wrappedRun,
|
|
40
|
-
rollback: config.rollback ? async (ctx, output) => {
|
|
41
|
-
await config.rollback(
|
|
42
|
-
ctx,
|
|
43
|
-
output
|
|
44
|
-
);
|
|
45
|
-
} : void 0
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
34
|
|
|
49
35
|
// src/errors.ts
|
|
50
36
|
var RunsheetError = class extends Error {
|
|
@@ -61,6 +47,72 @@ var RunsheetError = class extends Error {
|
|
|
61
47
|
}
|
|
62
48
|
};
|
|
63
49
|
|
|
50
|
+
// src/define-step.ts
|
|
51
|
+
function withTimeout(run, stepName, ms) {
|
|
52
|
+
return async (ctx) => {
|
|
53
|
+
let timer;
|
|
54
|
+
const timeout = new Promise((resolve) => {
|
|
55
|
+
timer = setTimeout(() => {
|
|
56
|
+
const error = new RunsheetError("TIMEOUT", `${stepName} timed out after ${ms}ms`);
|
|
57
|
+
resolve({ success: false, errors: [error] });
|
|
58
|
+
}, ms);
|
|
59
|
+
});
|
|
60
|
+
try {
|
|
61
|
+
return await Promise.race([run(ctx), timeout]);
|
|
62
|
+
} finally {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function computeDelay(policy, attempt) {
|
|
68
|
+
const base = policy.delay ?? 0;
|
|
69
|
+
if (base === 0) return 0;
|
|
70
|
+
const strategy = policy.backoff ?? "linear";
|
|
71
|
+
return strategy === "exponential" ? base * 2 ** (attempt - 1) : base * attempt;
|
|
72
|
+
}
|
|
73
|
+
function withRetry(run, stepName, policy) {
|
|
74
|
+
return async (ctx) => {
|
|
75
|
+
let lastResult = await run(ctx);
|
|
76
|
+
if (lastResult.success) return lastResult;
|
|
77
|
+
for (let attempt = 1; attempt <= policy.count; attempt++) {
|
|
78
|
+
if (policy.retryIf && !policy.retryIf(lastResult.errors)) return lastResult;
|
|
79
|
+
const delay = computeDelay(policy, attempt);
|
|
80
|
+
if (delay > 0) await new Promise((r) => setTimeout(r, delay));
|
|
81
|
+
lastResult = await run(ctx);
|
|
82
|
+
if (lastResult.success) return lastResult;
|
|
83
|
+
}
|
|
84
|
+
const error = new RunsheetError(
|
|
85
|
+
"RETRY_EXHAUSTED",
|
|
86
|
+
`${stepName} failed after ${policy.count} retries`
|
|
87
|
+
);
|
|
88
|
+
return { success: false, errors: [...lastResult.errors, error] };
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function wrapWithTimeoutAndRetry(run, stepName, timeout, retry) {
|
|
92
|
+
let wrapped = run;
|
|
93
|
+
if (timeout !== void 0) wrapped = withTimeout(wrapped, stepName, timeout);
|
|
94
|
+
if (retry !== void 0) wrapped = withRetry(wrapped, stepName, retry);
|
|
95
|
+
return wrapped;
|
|
96
|
+
}
|
|
97
|
+
function defineStep(config) {
|
|
98
|
+
const baseRun = (0, import_composable_functions.composable)(config.run);
|
|
99
|
+
const wrappedRun = wrapWithTimeoutAndRetry(baseRun, config.name, config.timeout, config.retry);
|
|
100
|
+
return Object.freeze({
|
|
101
|
+
name: config.name,
|
|
102
|
+
requires: config.requires ?? void 0,
|
|
103
|
+
provides: config.provides ?? void 0,
|
|
104
|
+
run: wrappedRun,
|
|
105
|
+
rollback: config.rollback ? async (ctx, output) => {
|
|
106
|
+
await config.rollback(
|
|
107
|
+
ctx,
|
|
108
|
+
output
|
|
109
|
+
);
|
|
110
|
+
} : void 0,
|
|
111
|
+
retry: config.retry ?? void 0,
|
|
112
|
+
timeout: config.timeout ?? void 0
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
64
116
|
// src/middleware.ts
|
|
65
117
|
function applyMiddleware(middlewares, step, executor) {
|
|
66
118
|
return middlewares.reduceRight((next, mw) => mw(step, next), executor);
|
|
@@ -78,6 +130,24 @@ function isConditionalStep(step) {
|
|
|
78
130
|
}
|
|
79
131
|
|
|
80
132
|
// src/pipeline.ts
|
|
133
|
+
function checkStrictOverlap(steps) {
|
|
134
|
+
const seen = /* @__PURE__ */ new Map();
|
|
135
|
+
for (const step of steps) {
|
|
136
|
+
if (!step.provides) continue;
|
|
137
|
+
const shape = step.provides.shape;
|
|
138
|
+
if (!shape || typeof shape !== "object") continue;
|
|
139
|
+
for (const key of Object.keys(shape)) {
|
|
140
|
+
const existing = seen.get(key);
|
|
141
|
+
if (existing) {
|
|
142
|
+
throw new RunsheetError(
|
|
143
|
+
"STRICT_OVERLAP",
|
|
144
|
+
`strict mode: key "${key}" is provided by both "${existing}" and "${step.name}"`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
seen.set(key, step.name);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
81
151
|
function validateSchema(schema, data, label, code) {
|
|
82
152
|
if (!schema) return { success: true, data };
|
|
83
153
|
const parsed = schema.safeParse(data);
|
|
@@ -236,12 +306,117 @@ async function executePipeline(config, args) {
|
|
|
236
306
|
return pipelineSuccess(config.name, args, state);
|
|
237
307
|
}
|
|
238
308
|
function buildPipeline(config) {
|
|
309
|
+
if (config.strict) checkStrictOverlap(config.steps);
|
|
239
310
|
return Object.freeze({
|
|
240
311
|
name: config.name,
|
|
241
312
|
run: (args) => executePipeline(config, args)
|
|
242
313
|
});
|
|
243
314
|
}
|
|
244
315
|
|
|
316
|
+
// src/parallel.ts
|
|
317
|
+
function validateInnerSchema(schema, data, label, code) {
|
|
318
|
+
if (!schema) return null;
|
|
319
|
+
const parsed = schema.safeParse(data);
|
|
320
|
+
if (parsed.success) return null;
|
|
321
|
+
return parsed.error.issues.map(
|
|
322
|
+
(issue) => new RunsheetError(code, `${label}: ${issue.path.join(".")}: ${issue.message}`)
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
async function executeInner(step, ctx) {
|
|
326
|
+
try {
|
|
327
|
+
if (isConditionalStep(step) && !step.predicate(ctx)) {
|
|
328
|
+
return { step, skipped: true };
|
|
329
|
+
}
|
|
330
|
+
} catch (err) {
|
|
331
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
332
|
+
const error = new RunsheetError("PREDICATE", `${step.name} predicate: ${message}`);
|
|
333
|
+
if (err instanceof Error) error.cause = err;
|
|
334
|
+
return { step, skipped: false, errors: [error] };
|
|
335
|
+
}
|
|
336
|
+
const requiresErrors = validateInnerSchema(
|
|
337
|
+
step.requires,
|
|
338
|
+
ctx,
|
|
339
|
+
`${step.name} requires`,
|
|
340
|
+
"REQUIRES_VALIDATION"
|
|
341
|
+
);
|
|
342
|
+
if (requiresErrors) return { step, skipped: false, errors: requiresErrors };
|
|
343
|
+
const result = await step.run(ctx);
|
|
344
|
+
if (!result.success) return { step, skipped: false, errors: [...result.errors] };
|
|
345
|
+
const providesErrors = validateInnerSchema(
|
|
346
|
+
step.provides,
|
|
347
|
+
result.data,
|
|
348
|
+
`${step.name} provides`,
|
|
349
|
+
"PROVIDES_VALIDATION"
|
|
350
|
+
);
|
|
351
|
+
if (providesErrors) return { step, skipped: false, errors: providesErrors };
|
|
352
|
+
return { step, skipped: false, output: result.data };
|
|
353
|
+
}
|
|
354
|
+
function parallel(...steps) {
|
|
355
|
+
const name = `parallel(${steps.map((s) => s.name).join(", ")})`;
|
|
356
|
+
const innerSteps = steps;
|
|
357
|
+
const run = async (ctx) => {
|
|
358
|
+
const settled = await Promise.allSettled(innerSteps.map((step) => executeInner(step, ctx)));
|
|
359
|
+
const succeeded = [];
|
|
360
|
+
const allErrors = [];
|
|
361
|
+
for (const s of settled) {
|
|
362
|
+
if (s.status === "rejected") {
|
|
363
|
+
allErrors.push(s.reason instanceof Error ? s.reason : new Error(String(s.reason)));
|
|
364
|
+
} else {
|
|
365
|
+
const r = s.value;
|
|
366
|
+
if (r.skipped) continue;
|
|
367
|
+
if (r.output) {
|
|
368
|
+
succeeded.push({ step: r.step, output: r.output });
|
|
369
|
+
} else if (r.errors) {
|
|
370
|
+
allErrors.push(...r.errors);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (allErrors.length > 0) {
|
|
375
|
+
for (let i = succeeded.length - 1; i >= 0; i--) {
|
|
376
|
+
const { step, output } = succeeded[i];
|
|
377
|
+
if (step.rollback) {
|
|
378
|
+
try {
|
|
379
|
+
await step.rollback(ctx, output);
|
|
380
|
+
} catch {
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return { success: false, errors: allErrors };
|
|
385
|
+
}
|
|
386
|
+
const merged = {};
|
|
387
|
+
for (const { output } of succeeded) {
|
|
388
|
+
Object.assign(merged, output);
|
|
389
|
+
}
|
|
390
|
+
return { success: true, data: merged, errors: [] };
|
|
391
|
+
};
|
|
392
|
+
const rollback = async (ctx, mergedOutput) => {
|
|
393
|
+
const errors = [];
|
|
394
|
+
for (let i = innerSteps.length - 1; i >= 0; i--) {
|
|
395
|
+
const step = innerSteps[i];
|
|
396
|
+
if (!step.rollback) continue;
|
|
397
|
+
try {
|
|
398
|
+
await step.rollback(ctx, mergedOutput);
|
|
399
|
+
} catch (err) {
|
|
400
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (errors.length > 0) {
|
|
404
|
+
const error = new Error(`${name}: ${errors.length} rollback(s) failed`);
|
|
405
|
+
error.cause = errors;
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
return Object.freeze({
|
|
410
|
+
name,
|
|
411
|
+
requires: void 0,
|
|
412
|
+
provides: void 0,
|
|
413
|
+
run,
|
|
414
|
+
rollback,
|
|
415
|
+
retry: void 0,
|
|
416
|
+
timeout: void 0
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
245
420
|
// src/builder.ts
|
|
246
421
|
function makeBuilder(state) {
|
|
247
422
|
return Object.freeze({
|
|
@@ -257,16 +432,28 @@ function makeBuilder(state) {
|
|
|
257
432
|
name: state.name,
|
|
258
433
|
steps: state.steps,
|
|
259
434
|
middleware: state.middleware.length > 0 ? state.middleware : void 0,
|
|
260
|
-
argsSchema: state.argsSchema
|
|
435
|
+
argsSchema: state.argsSchema,
|
|
436
|
+
strict: state.strict || void 0
|
|
261
437
|
})
|
|
262
438
|
});
|
|
263
439
|
}
|
|
264
|
-
function createPipeline(name,
|
|
440
|
+
function createPipeline(name, schemaOrOptions, options) {
|
|
441
|
+
let argsSchema;
|
|
442
|
+
let strict = false;
|
|
443
|
+
if (schemaOrOptions != null) {
|
|
444
|
+
if ("safeParse" in schemaOrOptions) {
|
|
445
|
+
argsSchema = schemaOrOptions;
|
|
446
|
+
strict = options?.strict ?? false;
|
|
447
|
+
} else {
|
|
448
|
+
strict = schemaOrOptions.strict ?? false;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
265
451
|
return makeBuilder({
|
|
266
452
|
name,
|
|
267
453
|
steps: [],
|
|
268
454
|
middleware: [],
|
|
269
|
-
argsSchema
|
|
455
|
+
argsSchema,
|
|
456
|
+
strict
|
|
270
457
|
});
|
|
271
458
|
}
|
|
272
459
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -275,6 +462,7 @@ function createPipeline(name, argsSchema) {
|
|
|
275
462
|
buildPipeline,
|
|
276
463
|
createPipeline,
|
|
277
464
|
defineStep,
|
|
465
|
+
parallel,
|
|
278
466
|
when
|
|
279
467
|
});
|
|
280
468
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/define-step.ts","../src/errors.ts","../src/middleware.ts","../src/when.ts","../src/pipeline.ts","../src/builder.ts"],"sourcesContent":["export { defineStep } from './define-step.js';\nexport { RunsheetError } from './errors.js';\nexport type { RunsheetErrorCode } from './errors.js';\nexport { buildPipeline } from './pipeline.js';\nexport type { Pipeline, PipelineConfig } from './pipeline.js';\nexport { when } from './when.js';\nexport type { ConditionalStep } from './when.js';\nexport type { StepMiddleware, StepInfo, StepExecutor } from './middleware.js';\nexport { createPipeline } from './builder.js';\nexport type { PipelineBuilder } from './builder.js';\n\nexport type {\n Step,\n TypedStep,\n StepConfig,\n StepContext,\n StepOutput,\n PipelineResult,\n PipelineSuccess,\n PipelineFailure,\n PipelineExecutionMeta,\n RollbackReport,\n RollbackFailure,\n} from './types.js';\n\n// Re-export Result types so consumers never need to import composable-functions\nexport type { Result, Success, Failure } from 'composable-functions';\n","import { composable } from 'composable-functions';\nimport type { StepConfig, StepContext, Step, TypedStep } from './types.js';\n\n/**\n * Define a pipeline step.\n *\n * Returns a frozen {@link TypedStep} with concrete types for `run`,\n * `rollback`, `requires`, and `provides`. The `run` function can be\n * sync or async — both are supported.\n *\n * **With schemas** (runtime validation + type inference):\n * ```ts\n * const charge = defineStep({\n * name: 'charge',\n * requires: z.object({ amount: z.number() }),\n * provides: z.object({ chargeId: z.string() }),\n * run: async (ctx) => ({ chargeId: 'ch_123' }),\n * });\n * ```\n *\n * **With generics only** (no runtime validation):\n * ```ts\n * const log = defineStep<{ order: Order }, { loggedAt: Date }>({\n * name: 'log',\n * run: async (ctx) => ({ loggedAt: new Date() }),\n * });\n * ```\n *\n * **Invariants:**\n * - The returned step object is always frozen (immutable).\n * - The `run` function is wrapped with `composable()` from\n * composable-functions, which catches thrown errors and produces\n * `Result` values. Step authors should throw to signal failure.\n * - This is the single type-erasure cast point in the library.\n *\n * @typeParam Requires - The context shape this step reads from.\n * @typeParam Provides - The output shape this step produces.\n * @param config - The step configuration. See {@link StepConfig}.\n * @returns A frozen {@link TypedStep} ready for use in pipelines.\n */\nexport function defineStep<Requires extends StepContext, Provides extends StepContext>(\n config: StepConfig<Requires, Provides>,\n): TypedStep<Requires, Provides> {\n const wrappedRun = composable(config.run);\n\n // The cast below is the single point where typed step functions are erased\n // to the runtime Step representation. This is safe because:\n // 1. Schema validation at step boundaries (requires/provides) enforces\n // correct types at runtime before and after each step executes.\n // 2. The pipeline accumulates context immutably, so the runtime object\n // structurally matches what the typed function expects.\n // 3. The phantom brands on TypedStep preserve compile-time type tracking\n // through the builder API without affecting runtime behavior.\n return Object.freeze({\n name: config.name,\n requires: config.requires ?? undefined,\n provides: config.provides ?? undefined,\n run: wrappedRun as unknown as Step['run'],\n rollback: config.rollback\n ? async (ctx: Readonly<StepContext>, output: Readonly<StepContext>) => {\n await (config.rollback as NonNullable<typeof config.rollback>)(\n ctx as Readonly<Requires>,\n output as Readonly<Provides>,\n );\n }\n : undefined,\n }) as TypedStep<Requires, Provides>;\n}\n","/**\n * Error codes for errors produced by the runsheet library itself.\n *\n * Use these to distinguish library errors from application errors\n * in a pipeline's `errors` array:\n *\n * ```ts\n * if (!result.success) {\n * for (const error of result.errors) {\n * if (error instanceof RunsheetError) {\n * console.log(error.code); // 'REQUIRES_VALIDATION', etc.\n * }\n * }\n * }\n * ```\n */\nexport type RunsheetErrorCode =\n | 'REQUIRES_VALIDATION'\n | 'PROVIDES_VALIDATION'\n | 'ARGS_VALIDATION'\n | 'PREDICATE';\n\n/**\n * Base error class for all errors produced by the runsheet library.\n *\n * Application errors (thrown by step `run` or `rollback` functions)\n * are never wrapped in `RunsheetError` — they pass through as-is.\n * If you see a `RunsheetError` in a result's `errors` array, the\n * library itself produced it.\n *\n * Use `instanceof RunsheetError` to distinguish library errors from\n * application errors, and the `code` property to identify the\n * specific failure.\n */\nexport class RunsheetError extends Error {\n /** Discriminant code identifying the type of library error. */\n readonly code: RunsheetErrorCode;\n\n /**\n * @param code - The error code.\n * @param message - A human-readable description of the failure.\n */\n constructor(code: RunsheetErrorCode, message: string) {\n super(message);\n this.name = 'RunsheetError';\n this.code = code;\n }\n}\n","import type { Result } from 'composable-functions';\nimport type { Step, StepContext, StepOutput } from './types.js';\n\n/**\n * Metadata about the step being executed, passed to middleware.\n *\n * This is a read-only view of the step's public configuration.\n * Middleware can use it for logging, metrics, or conditional behavior.\n */\nexport type StepInfo = {\n /** The step's name. */\n readonly name: string;\n /** The step's requires schema, or `undefined` if not provided. */\n readonly requires: Step['requires'];\n /** The step's provides schema, or `undefined` if not provided. */\n readonly provides: Step['provides'];\n};\n\n/**\n * A function that executes a step (or the next middleware in the chain).\n *\n * Receives the frozen accumulated context and returns a `Result` — either\n * `{ success: true, data }` or `{ success: false, errors }`.\n */\nexport type StepExecutor = (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>>;\n\n/**\n * Middleware that wraps the entire step lifecycle, including schema\n * validation.\n *\n * A middleware receives the step metadata and a `next` function, and\n * returns a new executor. Call `next(ctx)` to proceed to the next\n * middleware (or the actual step execution). You can:\n *\n * - **Observe**: read the context or result for logging/metrics.\n * - **Transform**: modify the result before returning it.\n * - **Short-circuit**: return a `Result` without calling `next`.\n *\n * If a middleware throws, the pipeline catches it and treats it as a\n * step failure (triggering rollback for previously completed steps).\n *\n * @example\n * ```ts\n * const timing: StepMiddleware = (step, next) => async (ctx) => {\n * const start = performance.now();\n * const result = await next(ctx);\n * console.log(`${step.name}: ${performance.now() - start}ms`);\n * return result;\n * };\n * ```\n *\n * @param step - Metadata about the step being wrapped.\n * @param next - The next executor in the chain. Call it to continue.\n * @returns A new executor that wraps `next`.\n */\nexport type StepMiddleware = (step: StepInfo, next: StepExecutor) => StepExecutor;\n\n/**\n * Compose an array of middlewares around a step executor.\n *\n * First middleware in the array is the outermost wrapper (executes\n * first on the way in, last on the way out).\n *\n * @param middlewares - Middleware functions, in declaration order.\n * @param step - Metadata about the step being wrapped.\n * @param executor - The base step executor to wrap.\n * @returns A composed executor with all middleware applied.\n */\nexport function applyMiddleware(\n middlewares: readonly StepMiddleware[],\n step: StepInfo,\n executor: StepExecutor,\n): StepExecutor {\n return middlewares.reduceRight<StepExecutor>((next, mw) => mw(step, next), executor);\n}\n","import type { Step, StepContext, TypedStep } from './types.js';\n\n/**\n * A step with a conditional predicate attached.\n *\n * The pipeline engine checks for the `predicate` property to decide\n * whether to execute or skip the step. Use the {@link when} function\n * to create conditional steps — don't construct this type directly.\n */\nexport type ConditionalStep = Step & {\n /** Returns `true` to execute the step, `false` to skip it. */\n readonly predicate: (ctx: Readonly<StepContext>) => boolean;\n};\n\n/**\n * Wrap a step with a guard predicate.\n *\n * The step only executes when the predicate returns `true`. When\n * skipped:\n * - No context snapshot is taken.\n * - No rollback entry is created.\n * - The step name is recorded in `result.meta.stepsSkipped`.\n *\n * If the predicate throws, the pipeline treats it as a step failure\n * and triggers rollback for any previously completed steps.\n *\n * @example\n * ```ts\n * const steps = [\n * validateOrder,\n * when((ctx) => ctx.order.amount > 100, notifyManager),\n * sendConfirmation,\n * ];\n * ```\n *\n * @typeParam Requires - Inferred from the step's requires type.\n * @typeParam Provides - Inferred from the step's provides type.\n * @param predicate - Guard function. Receives the current accumulated\n * context (frozen). Return `true` to execute, `false` to skip.\n * @param step - The step to conditionally execute.\n * @returns A frozen {@link TypedStep} with the predicate attached.\n */\nexport function when<Requires extends StepContext, Provides extends StepContext>(\n predicate: (ctx: Readonly<Requires>) => boolean,\n step: TypedStep<Requires, Provides>,\n): TypedStep<Requires, Provides> {\n return Object.freeze({\n ...step,\n predicate: predicate as ConditionalStep['predicate'],\n }) as TypedStep<Requires, Provides>;\n}\n\n/** Type guard for conditional steps. */\nexport function isConditionalStep(step: Step): step is ConditionalStep {\n return 'predicate' in step && typeof (step as StepContext)['predicate'] === 'function';\n}\n","import type { ParserSchema, Result } from 'composable-functions';\nimport type {\n ExtractProvides,\n PipelineFailure,\n PipelineResult,\n PipelineSuccess,\n RollbackFailure,\n RollbackReport,\n Step,\n StepContext,\n StepOutput,\n UnionToIntersection,\n} from './types.js';\nimport type { StepMiddleware } from './middleware.js';\nimport type { RunsheetErrorCode } from './errors.js';\nimport { RunsheetError } from './errors.js';\nimport { applyMiddleware } from './middleware.js';\nimport { isConditionalStep } from './when.js';\n\n// ---------------------------------------------------------------------------\n// Pipeline configuration\n// ---------------------------------------------------------------------------\n\n/**\n * Internal configuration shape for the pipeline engine.\n *\n * Users typically don't construct this directly — use `buildPipeline()`\n * or `createPipeline()` instead.\n */\nexport type PipelineConfig = {\n /** Pipeline name, used in execution metadata and error messages. */\n readonly name: string;\n /** Steps to execute in order. */\n readonly steps: readonly Step[];\n /** Optional middleware applied to every step. First in array = outermost. */\n readonly middleware?: readonly StepMiddleware[];\n /** Optional schema that validates the pipeline's input arguments. */\n readonly argsSchema?: ParserSchema<StepContext>;\n};\n\n// ---------------------------------------------------------------------------\n// Pipeline\n// ---------------------------------------------------------------------------\n\n/**\n * A built pipeline, ready to execute.\n *\n * Call `run(args)` to execute the pipeline. The result is a\n * {@link PipelineResult} — either a success with the fully accumulated\n * context, or a failure with error details and a rollback report.\n *\n * Pipeline objects are frozen (immutable) and can be called repeatedly.\n *\n * @typeParam Args - The input type accepted by `run()`.\n * @typeParam Ctx - The accumulated output type on success.\n */\nexport type Pipeline<Args extends StepContext, Ctx> = {\n /** The pipeline's name, as provided at build time. */\n readonly name: string;\n /**\n * Execute the pipeline.\n *\n * @param args - The initial arguments. Merged into the context before\n * the first step runs. Validated against `argsSchema` if one was\n * provided.\n * @returns A {@link PipelineResult} — discriminate on `success` to\n * access `data` (on success) or `errors`/`rollback` (on failure).\n */\n readonly run: (args: Args) => Promise<PipelineResult<Ctx>>;\n};\n\n// ---------------------------------------------------------------------------\n// Schema validation\n// ---------------------------------------------------------------------------\n\nfunction validateSchema<T>(\n schema: ParserSchema<T> | undefined,\n data: unknown,\n label: string,\n code: RunsheetErrorCode,\n): { success: true; data: T } | { success: false; errors: RunsheetError[] } {\n if (!schema) return { success: true, data: data as T };\n\n const parsed = schema.safeParse(data);\n if (parsed.success) return { success: true, data: parsed.data };\n\n const errors = parsed.error.issues.map(\n (issue) => new RunsheetError(code, `${label}: ${issue.path.join('.')}: ${issue.message}`),\n );\n return { success: false, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Rollback\n// ---------------------------------------------------------------------------\n\nasync function executeRollback(\n executedSteps: readonly Step[],\n snapshots: readonly StepContext[],\n outputs: readonly StepOutput[],\n): Promise<RollbackReport> {\n const completed: string[] = [];\n const failed: RollbackFailure[] = [];\n\n for (let i = executedSteps.length - 1; i >= 0; i--) {\n const step = executedSteps[i];\n if (!step.rollback) continue;\n\n try {\n await step.rollback(snapshots[i], outputs[i]);\n completed.push(step.name);\n } catch (err) {\n failed.push({\n step: step.name,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n return Object.freeze({ completed, failed });\n}\n\n// ---------------------------------------------------------------------------\n// Execution state — accumulated during pipeline run\n// ---------------------------------------------------------------------------\n\ntype ExecutionState = {\n context: StepContext;\n readonly snapshots: StepContext[];\n readonly outputs: StepOutput[];\n readonly executedSteps: Step[];\n readonly stepsExecuted: string[];\n readonly stepsSkipped: string[];\n};\n\nfunction createExecutionState(args: StepContext): ExecutionState {\n return {\n context: Object.freeze({ ...args }),\n snapshots: [],\n outputs: [],\n executedSteps: [],\n stepsExecuted: [],\n stepsSkipped: [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Result constructors\n// ---------------------------------------------------------------------------\n\nfunction pipelineFailure(\n pipelineName: string,\n args: StepContext,\n state: ExecutionState,\n failedStep: string,\n errors: Error[],\n rollback: RollbackReport,\n): PipelineFailure {\n return Object.freeze({\n success: false as const,\n errors,\n meta: Object.freeze({\n pipeline: pipelineName,\n args,\n stepsExecuted: state.stepsExecuted,\n stepsSkipped: state.stepsSkipped,\n }),\n failedStep,\n rollback,\n });\n}\n\nfunction pipelineSuccess(\n pipelineName: string,\n args: StepContext,\n state: ExecutionState,\n): PipelineSuccess<StepContext> {\n return Object.freeze({\n success: true as const,\n data: state.context,\n errors: [] as [],\n meta: Object.freeze({\n pipeline: pipelineName,\n args,\n stepsExecuted: state.stepsExecuted,\n stepsSkipped: state.stepsSkipped,\n }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Step executor — the full lifecycle (validate requires → run → validate provides)\n// ---------------------------------------------------------------------------\n\nfunction createStepExecutor(\n step: Step,\n): (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>> {\n return async (ctx) => {\n // Validate requires\n const requiresCheck = validateSchema(\n step.requires,\n ctx,\n `${step.name} requires`,\n 'REQUIRES_VALIDATION',\n );\n if (!requiresCheck.success) {\n return { success: false as const, errors: requiresCheck.errors };\n }\n\n // Execute step run\n const result = await step.run(ctx);\n if (!result.success) return result;\n\n // Validate provides\n const providesCheck = validateSchema(\n step.provides,\n result.data,\n `${step.name} provides`,\n 'PROVIDES_VALIDATION',\n );\n if (!providesCheck.success) {\n return { success: false as const, errors: providesCheck.errors };\n }\n\n return {\n success: true as const,\n data: providesCheck.data as StepOutput,\n errors: [] as [],\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline execution\n// ---------------------------------------------------------------------------\n\nasync function executePipeline(\n config: PipelineConfig,\n args: StepContext,\n): Promise<PipelineResult<StepContext>> {\n // Validate pipeline args if schema provided\n if (config.argsSchema) {\n const argsCheck = validateSchema(\n config.argsSchema,\n args,\n `${config.name} args`,\n 'ARGS_VALIDATION',\n );\n if (!argsCheck.success) {\n const state = createExecutionState(args);\n return pipelineFailure(\n config.name,\n args,\n state,\n config.name,\n argsCheck.errors,\n Object.freeze({ completed: [], failed: [] }),\n );\n }\n }\n\n const state = createExecutionState(args);\n const middlewares = config.middleware ?? [];\n\n for (const step of config.steps) {\n // Evaluate conditional predicate\n try {\n if (isConditionalStep(step) && !step.predicate(state.context)) {\n state.stepsSkipped.push(step.name);\n continue;\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const error = new RunsheetError('PREDICATE', `${step.name} predicate: ${message}`);\n if (err instanceof Error) error.cause = err;\n const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);\n return pipelineFailure(config.name, args, state, step.name, [error], rollback);\n }\n\n // Snapshot pre-step context\n state.snapshots.push(state.context);\n\n // Build executor with middleware wrapping the full lifecycle\n const baseExecutor = createStepExecutor(step);\n const executor = applyMiddleware(\n middlewares,\n { name: step.name, requires: step.requires, provides: step.provides },\n baseExecutor,\n );\n\n // Execute (try/catch handles middleware throws outside the Result boundary)\n let result: Result<StepOutput>;\n try {\n result = await executor(state.context);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n state.snapshots.pop();\n const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);\n return pipelineFailure(config.name, args, state, step.name, [error], rollback);\n }\n\n if (!result.success) {\n // Remove the snapshot we just pushed — the step didn't complete\n state.snapshots.pop();\n const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);\n return pipelineFailure(config.name, args, state, step.name, result.errors, rollback);\n }\n\n // Track step output and accumulate context\n const output = result.data;\n state.outputs.push(output);\n state.executedSteps.push(step);\n state.stepsExecuted.push(step.name);\n state.context = Object.freeze({ ...state.context, ...output });\n }\n\n return pipelineSuccess(config.name, args, state);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a pipeline from an array of steps.\n *\n * The result type is inferred from the steps — `pipeline.run()` returns\n * a {@link PipelineResult} whose `data` is the intersection of the\n * initial `Args` and all step output types.\n *\n * @example\n * ```ts\n * const pipeline = buildPipeline({\n * name: 'placeOrder',\n * steps: [validateOrder, chargePayment, sendConfirmation],\n * middleware: [logging, timing],\n * argsSchema: z.object({ orderId: z.string() }),\n * });\n *\n * const result = await pipeline.run({ orderId: '123' });\n * if (result.success) {\n * result.data.chargeId; // string — fully typed\n * }\n * ```\n *\n * **Execution semantics:**\n * - Steps execute sequentially in array order.\n * - Context is frozen (`Object.freeze`) at every step boundary.\n * - Conditional steps (wrapped with `when()`) are skipped when their\n * predicate returns false — no snapshot, no rollback entry.\n * - On step failure, rollback handlers for all previously completed\n * steps execute in reverse order (best-effort).\n * - Middleware wraps the full step lifecycle including schema validation.\n *\n * **Invariants:**\n * - The returned pipeline object is frozen (immutable).\n * - Errors thrown by steps, predicates, or middleware are caught and\n * returned as `PipelineFailure` — `run()` never throws.\n *\n * @typeParam Args - The pipeline's input type. Inferred from `argsSchema`\n * if provided, otherwise defaults to `StepContext`.\n * @typeParam S - The step types in the array. Inferred automatically —\n * do not specify manually.\n * @param config - Pipeline configuration.\n * @param config.name - Pipeline name, used in metadata and error messages.\n * @param config.steps - Steps to execute in order.\n * @param config.middleware - Optional middleware applied to every step.\n * First in array = outermost wrapper.\n * @param config.argsSchema - Optional schema that validates `args` before\n * any steps run. Validation failure produces a `PipelineFailure` with\n * `failedStep` set to the pipeline name.\n * @returns A frozen {@link Pipeline} whose `run()` method executes the\n * steps and returns a {@link PipelineResult}.\n */\nexport function buildPipeline<\n Args extends StepContext = StepContext,\n S extends Step = Step,\n>(config: {\n readonly name: string;\n readonly steps: readonly S[];\n readonly middleware?: readonly StepMiddleware[];\n readonly argsSchema?: ParserSchema<Args>;\n}): Pipeline<Args, Args & UnionToIntersection<ExtractProvides<S>>> {\n return Object.freeze({\n name: config.name,\n run: (args: Args) => executePipeline(config as PipelineConfig, args),\n }) as Pipeline<Args, Args & UnionToIntersection<ExtractProvides<S>>>;\n}\n","import type { ParserSchema } from 'composable-functions';\nimport type { Step, StepContext, TypedStep } from './types.js';\nimport type { StepMiddleware } from './middleware.js';\nimport { buildPipeline } from './pipeline.js';\nimport type { Pipeline } from './pipeline.js';\n\n// ---------------------------------------------------------------------------\n// Builder types\n// ---------------------------------------------------------------------------\n\n/**\n * A fluent pipeline builder that progressively narrows the accumulated\n * context type as steps are added.\n *\n * Each method returns a new, frozen builder — builders are immutable.\n * This means you can safely fork a builder to create variants:\n *\n * ```ts\n * const base = createPipeline('order').step(validate);\n * const withCharge = base.step(charge).build();\n * const withoutCharge = base.build(); // unaffected by the fork\n * ```\n *\n * @typeParam Args - The pipeline's initial input type.\n * @typeParam Ctx - The accumulated context type so far (grows with each `.step()`).\n */\nexport type PipelineBuilder<Args extends StepContext, Ctx extends StepContext> = {\n /**\n * Add a step to the pipeline.\n *\n * The step's `Requires` type must be satisfied by the current `Ctx`.\n * The returned builder's `Ctx` expands to include the step's `Provides`.\n *\n * @typeParam Provides - The output type of the step being added.\n * @param step - A {@link TypedStep} (from `defineStep` or `when`).\n * @returns A new builder with the expanded context type.\n */\n readonly step: <Provides extends StepContext>(\n step: TypedStep<Ctx, Provides>,\n ) => PipelineBuilder<Args, Ctx & Provides>;\n\n /**\n * Add middleware to the pipeline.\n *\n * Middleware is applied to every step. Multiple `.use()` calls\n * accumulate — earlier middleware is outermost (executes first).\n *\n * @param middleware - One or more {@link StepMiddleware} functions.\n * @returns A new builder with the middleware added.\n */\n readonly use: (...middleware: StepMiddleware[]) => PipelineBuilder<Args, Ctx>;\n\n /**\n * Build the pipeline.\n *\n * @returns A frozen {@link Pipeline} ready to execute with `run()`.\n */\n readonly build: () => Pipeline<Args, Ctx>;\n};\n\n// ---------------------------------------------------------------------------\n// Internal builder state (immutable — each method returns a new builder)\n// ---------------------------------------------------------------------------\n\ntype BuilderState = {\n readonly name: string;\n readonly steps: readonly Step[];\n readonly middleware: readonly StepMiddleware[];\n readonly argsSchema: ParserSchema<StepContext> | undefined;\n};\n\nfunction makeBuilder<Args extends StepContext, Ctx extends StepContext>(\n state: BuilderState,\n): PipelineBuilder<Args, Ctx> {\n return Object.freeze({\n step: <Provides extends StepContext>(step: TypedStep<Ctx, Provides>) =>\n makeBuilder<Args, Ctx & Provides>({\n ...state,\n steps: [...state.steps, step],\n }),\n\n use: (...middleware: StepMiddleware[]) =>\n makeBuilder<Args, Ctx>({\n ...state,\n middleware: [...state.middleware, ...middleware],\n }),\n\n build: () =>\n buildPipeline({\n name: state.name,\n steps: state.steps,\n middleware: state.middleware.length > 0 ? state.middleware : undefined,\n argsSchema: state.argsSchema as ParserSchema<Args> | undefined,\n }) as Pipeline<Args, Ctx>,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Start building a pipeline with the fluent builder API.\n *\n * The builder gives progressive type narrowing — each `.step()` call\n * extends the known context type, so TypeScript catches mismatches\n * at compile time.\n *\n * **Type-only args** (no runtime validation):\n * ```ts\n * createPipeline<{ orderId: string }>('placeOrder')\n * .step(validateOrder)\n * .step(chargePayment)\n * .build();\n * ```\n *\n * **Schema args** (runtime validation + type inference):\n * ```ts\n * createPipeline('placeOrder', z.object({ orderId: z.string() }))\n * .step(validateOrder)\n * .step(chargePayment)\n * .build();\n * ```\n *\n * @typeParam Args - The pipeline's input type. Inferred from `argsSchema`\n * if provided, otherwise specify via generic parameter.\n * @param name - Pipeline name, used in metadata and error messages.\n * @param argsSchema - Optional schema that validates pipeline input\n * at runtime. When provided, `Args` is inferred from the schema.\n * @returns A frozen {@link PipelineBuilder} ready for `.step()`,\n * `.use()`, and `.build()`.\n */\nexport function createPipeline<Args extends StepContext>(\n name: string,\n argsSchema?: ParserSchema<Args>,\n): PipelineBuilder<Args, Args> {\n return makeBuilder<Args, Args>({\n name,\n steps: [],\n middleware: [],\n argsSchema,\n });\n}\n"],"mappings":"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","names":["state"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/define-step.ts","../src/errors.ts","../src/middleware.ts","../src/when.ts","../src/pipeline.ts","../src/parallel.ts","../src/builder.ts"],"sourcesContent":["export { defineStep } from './define-step.js';\nexport { RunsheetError } from './errors.js';\nexport type { RunsheetErrorCode } from './errors.js';\nexport { buildPipeline } from './pipeline.js';\nexport type { Pipeline, PipelineConfig } from './pipeline.js';\nexport { when } from './when.js';\nexport type { ConditionalStep } from './when.js';\nexport { parallel } from './parallel.js';\nexport type { StepMiddleware, StepInfo, StepExecutor } from './middleware.js';\nexport { createPipeline } from './builder.js';\nexport type { PipelineBuilder } from './builder.js';\n\nexport type {\n Step,\n TypedStep,\n StepConfig,\n StepContext,\n StepOutput,\n ExtractRequires,\n ExtractProvides,\n RetryPolicy,\n PipelineResult,\n PipelineSuccess,\n PipelineFailure,\n PipelineExecutionMeta,\n RollbackReport,\n RollbackFailure,\n} from './types.js';\n\n// Re-export Result types so consumers never need to import composable-functions\nexport type { Result, Success, Failure } from 'composable-functions';\n","import { composable } from 'composable-functions';\nimport type { Result } from 'composable-functions';\nimport type { RetryPolicy, StepConfig, StepContext, StepOutput, Step, TypedStep } from './types.js';\nimport { RunsheetError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Timeout and retry wrappers\n// ---------------------------------------------------------------------------\n\nfunction withTimeout(\n run: (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>>,\n stepName: string,\n ms: number,\n): (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>> {\n return async (ctx) => {\n let timer: ReturnType<typeof setTimeout>;\n const timeout = new Promise<Result<StepOutput>>((resolve) => {\n timer = setTimeout(() => {\n const error = new RunsheetError('TIMEOUT', `${stepName} timed out after ${ms}ms`);\n resolve({ success: false as const, errors: [error] });\n }, ms);\n });\n try {\n return await Promise.race([run(ctx), timeout]);\n } finally {\n clearTimeout(timer!);\n }\n };\n}\n\nfunction computeDelay(policy: RetryPolicy, attempt: number): number {\n const base = policy.delay ?? 0;\n if (base === 0) return 0;\n const strategy = policy.backoff ?? 'linear';\n return strategy === 'exponential' ? base * 2 ** (attempt - 1) : base * attempt;\n}\n\nfunction withRetry(\n run: (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>>,\n stepName: string,\n policy: RetryPolicy,\n): (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>> {\n return async (ctx) => {\n let lastResult = await run(ctx);\n if (lastResult.success) return lastResult;\n\n for (let attempt = 1; attempt <= policy.count; attempt++) {\n // Check if the failure is retryable\n if (policy.retryIf && !policy.retryIf(lastResult.errors)) return lastResult;\n\n const delay = computeDelay(policy, attempt);\n if (delay > 0) await new Promise((r) => setTimeout(r, delay));\n lastResult = await run(ctx);\n if (lastResult.success) return lastResult;\n }\n\n // Wrap the last failure with RETRY_EXHAUSTED\n const error = new RunsheetError(\n 'RETRY_EXHAUSTED',\n `${stepName} failed after ${policy.count} retries`,\n );\n return { success: false as const, errors: [...lastResult.errors, error] };\n };\n}\n\nfunction wrapWithTimeoutAndRetry(\n run: (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>>,\n stepName: string,\n timeout: number | undefined,\n retry: RetryPolicy | undefined,\n): (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>> {\n // Timeout wraps the individual run call, retry wraps the timeout+run combo\n let wrapped = run;\n if (timeout !== undefined) wrapped = withTimeout(wrapped, stepName, timeout);\n if (retry !== undefined) wrapped = withRetry(wrapped, stepName, retry);\n return wrapped;\n}\n\n/**\n * Define a pipeline step.\n *\n * Returns a frozen {@link TypedStep} with concrete types for `run`,\n * `rollback`, `requires`, and `provides`. The `run` function can be\n * sync or async — both are supported.\n *\n * **With schemas** (runtime validation + type inference):\n * ```ts\n * const charge = defineStep({\n * name: 'charge',\n * requires: z.object({ amount: z.number() }),\n * provides: z.object({ chargeId: z.string() }),\n * run: async (ctx) => ({ chargeId: 'ch_123' }),\n * });\n * ```\n *\n * **With generics only** (no runtime validation):\n * ```ts\n * const log = defineStep<{ order: Order }, { loggedAt: Date }>({\n * name: 'log',\n * run: async (ctx) => ({ loggedAt: new Date() }),\n * });\n * ```\n *\n * **Invariants:**\n * - The returned step object is always frozen (immutable).\n * - The `run` function is wrapped with `composable()` from\n * composable-functions, which catches thrown errors and produces\n * `Result` values. Step authors should throw to signal failure.\n * - This is the single type-erasure cast point in the library.\n *\n * @typeParam Requires - The context shape this step reads from.\n * @typeParam Provides - The output shape this step produces.\n * @param config - The step configuration. See {@link StepConfig}.\n * @returns A frozen {@link TypedStep} ready for use in pipelines.\n */\nexport function defineStep<Requires extends StepContext, Provides extends StepContext>(\n config: StepConfig<Requires, Provides>,\n): TypedStep<Requires, Provides> {\n const baseRun = composable(config.run) as unknown as (\n ctx: Readonly<StepContext>,\n ) => Promise<Result<StepOutput>>;\n const wrappedRun = wrapWithTimeoutAndRetry(baseRun, config.name, config.timeout, config.retry);\n\n // The cast below is the single point where typed step functions are erased\n // to the runtime Step representation. This is safe because:\n // 1. Schema validation at step boundaries (requires/provides) enforces\n // correct types at runtime before and after each step executes.\n // 2. The pipeline accumulates context immutably, so the runtime object\n // structurally matches what the typed function expects.\n // 3. The phantom brands on TypedStep preserve compile-time type tracking\n // through the builder API without affecting runtime behavior.\n return Object.freeze({\n name: config.name,\n requires: config.requires ?? undefined,\n provides: config.provides ?? undefined,\n run: wrappedRun as unknown as Step['run'],\n rollback: config.rollback\n ? async (ctx: Readonly<StepContext>, output: Readonly<StepContext>) => {\n await (config.rollback as NonNullable<typeof config.rollback>)(\n ctx as Readonly<Requires>,\n output as Readonly<Provides>,\n );\n }\n : undefined,\n retry: config.retry ?? undefined,\n timeout: config.timeout ?? undefined,\n }) as TypedStep<Requires, Provides>;\n}\n","/**\n * Error codes for errors produced by the runsheet library itself.\n *\n * Use these to distinguish library errors from application errors\n * in a pipeline's `errors` array:\n *\n * ```ts\n * if (!result.success) {\n * for (const error of result.errors) {\n * if (error instanceof RunsheetError) {\n * console.log(error.code); // 'REQUIRES_VALIDATION', etc.\n * }\n * }\n * }\n * ```\n */\nexport type RunsheetErrorCode =\n | 'REQUIRES_VALIDATION'\n | 'PROVIDES_VALIDATION'\n | 'ARGS_VALIDATION'\n | 'PREDICATE'\n | 'TIMEOUT'\n | 'RETRY_EXHAUSTED'\n | 'STRICT_OVERLAP';\n\n/**\n * Base error class for all errors produced by the runsheet library.\n *\n * Application errors (thrown by step `run` or `rollback` functions)\n * are never wrapped in `RunsheetError` — they pass through as-is.\n * If you see a `RunsheetError` in a result's `errors` array, the\n * library itself produced it.\n *\n * Use `instanceof RunsheetError` to distinguish library errors from\n * application errors, and the `code` property to identify the\n * specific failure.\n */\nexport class RunsheetError extends Error {\n /** Discriminant code identifying the type of library error. */\n readonly code: RunsheetErrorCode;\n\n /**\n * @param code - The error code.\n * @param message - A human-readable description of the failure.\n */\n constructor(code: RunsheetErrorCode, message: string) {\n super(message);\n this.name = 'RunsheetError';\n this.code = code;\n }\n}\n","import type { Result } from 'composable-functions';\nimport type { Step, StepContext, StepOutput } from './types.js';\n\n/**\n * Metadata about the step being executed, passed to middleware.\n *\n * This is a read-only view of the step's public configuration.\n * Middleware can use it for logging, metrics, or conditional behavior.\n */\nexport type StepInfo = {\n /** The step's name. */\n readonly name: string;\n /** The step's requires schema, or `undefined` if not provided. */\n readonly requires: Step['requires'];\n /** The step's provides schema, or `undefined` if not provided. */\n readonly provides: Step['provides'];\n};\n\n/**\n * A function that executes a step (or the next middleware in the chain).\n *\n * Receives the frozen accumulated context and returns a `Result` — either\n * `{ success: true, data }` or `{ success: false, errors }`.\n */\nexport type StepExecutor = (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>>;\n\n/**\n * Middleware that wraps the entire step lifecycle, including schema\n * validation.\n *\n * A middleware receives the step metadata and a `next` function, and\n * returns a new executor. Call `next(ctx)` to proceed to the next\n * middleware (or the actual step execution). You can:\n *\n * - **Observe**: read the context or result for logging/metrics.\n * - **Transform**: modify the result before returning it.\n * - **Short-circuit**: return a `Result` without calling `next`.\n *\n * If a middleware throws, the pipeline catches it and treats it as a\n * step failure (triggering rollback for previously completed steps).\n *\n * @example\n * ```ts\n * const timing: StepMiddleware = (step, next) => async (ctx) => {\n * const start = performance.now();\n * const result = await next(ctx);\n * console.log(`${step.name}: ${performance.now() - start}ms`);\n * return result;\n * };\n * ```\n *\n * @param step - Metadata about the step being wrapped.\n * @param next - The next executor in the chain. Call it to continue.\n * @returns A new executor that wraps `next`.\n */\nexport type StepMiddleware = (step: StepInfo, next: StepExecutor) => StepExecutor;\n\n/**\n * Compose an array of middlewares around a step executor.\n *\n * First middleware in the array is the outermost wrapper (executes\n * first on the way in, last on the way out).\n *\n * @param middlewares - Middleware functions, in declaration order.\n * @param step - Metadata about the step being wrapped.\n * @param executor - The base step executor to wrap.\n * @returns A composed executor with all middleware applied.\n */\nexport function applyMiddleware(\n middlewares: readonly StepMiddleware[],\n step: StepInfo,\n executor: StepExecutor,\n): StepExecutor {\n return middlewares.reduceRight<StepExecutor>((next, mw) => mw(step, next), executor);\n}\n","import type { Step, StepContext, TypedStep } from './types.js';\n\n/**\n * A step with a conditional predicate attached.\n *\n * The pipeline engine checks for the `predicate` property to decide\n * whether to execute or skip the step. Use the {@link when} function\n * to create conditional steps — don't construct this type directly.\n */\nexport type ConditionalStep = Step & {\n /** Returns `true` to execute the step, `false` to skip it. */\n readonly predicate: (ctx: Readonly<StepContext>) => boolean;\n};\n\n/**\n * Wrap a step with a guard predicate.\n *\n * The step only executes when the predicate returns `true`. When\n * skipped:\n * - No context snapshot is taken.\n * - No rollback entry is created.\n * - The step name is recorded in `result.meta.stepsSkipped`.\n *\n * If the predicate throws, the pipeline treats it as a step failure\n * and triggers rollback for any previously completed steps.\n *\n * @example\n * ```ts\n * const steps = [\n * validateOrder,\n * when((ctx) => ctx.order.amount > 100, notifyManager),\n * sendConfirmation,\n * ];\n * ```\n *\n * @typeParam Requires - Inferred from the step's requires type.\n * @typeParam Provides - Inferred from the step's provides type.\n * @param predicate - Guard function. Receives the current accumulated\n * context (frozen). Return `true` to execute, `false` to skip.\n * @param step - The step to conditionally execute.\n * @returns A frozen {@link TypedStep} with the predicate attached.\n */\nexport function when<Requires extends StepContext, Provides extends StepContext>(\n predicate: (ctx: Readonly<Requires>) => boolean,\n step: TypedStep<Requires, Provides>,\n): TypedStep<Requires, Provides> {\n return Object.freeze({\n ...step,\n predicate: predicate as ConditionalStep['predicate'],\n }) as TypedStep<Requires, Provides>;\n}\n\n/** Type guard for conditional steps. */\nexport function isConditionalStep(step: Step): step is ConditionalStep {\n return 'predicate' in step && typeof (step as StepContext)['predicate'] === 'function';\n}\n","import type { ParserSchema, Result } from 'composable-functions';\nimport type {\n ExtractProvides,\n PipelineFailure,\n PipelineResult,\n PipelineSuccess,\n RollbackFailure,\n RollbackReport,\n Step,\n StepContext,\n StepOutput,\n UnionToIntersection,\n} from './types.js';\nimport type { StepMiddleware } from './middleware.js';\nimport type { RunsheetErrorCode } from './errors.js';\nimport { RunsheetError } from './errors.js';\nimport { applyMiddleware } from './middleware.js';\nimport { isConditionalStep } from './when.js';\n\n// ---------------------------------------------------------------------------\n// Pipeline configuration\n// ---------------------------------------------------------------------------\n\n/**\n * Internal configuration shape for the pipeline engine.\n *\n * Users typically don't construct this directly — use `buildPipeline()`\n * or `createPipeline()` instead.\n */\nexport type PipelineConfig = {\n /** Pipeline name, used in execution metadata and error messages. */\n readonly name: string;\n /** Steps to execute in order. */\n readonly steps: readonly Step[];\n /** Optional middleware applied to every step. First in array = outermost. */\n readonly middleware?: readonly StepMiddleware[];\n /** Optional schema that validates the pipeline's input arguments. */\n readonly argsSchema?: ParserSchema<StepContext>;\n /**\n * When `true`, throws at build time if two or more steps provide the\n * same key. Only checks steps that have a `provides` schema with an\n * inspectable `.shape` property (e.g., Zod objects). Steps without\n * provides schemas are not checked.\n */\n readonly strict?: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// Strict mode — detect provides key collisions at build time\n// ---------------------------------------------------------------------------\n\nfunction checkStrictOverlap(steps: readonly Step[]): void {\n const seen = new Map<string, string>(); // key → step name\n\n for (const step of steps) {\n if (!step.provides) continue;\n\n // Extract keys from schemas that expose .shape (e.g., Zod objects)\n const shape = (step.provides as Record<string, unknown>).shape;\n if (!shape || typeof shape !== 'object') continue;\n\n for (const key of Object.keys(shape)) {\n const existing = seen.get(key);\n if (existing) {\n throw new RunsheetError(\n 'STRICT_OVERLAP',\n `strict mode: key \"${key}\" is provided by both \"${existing}\" and \"${step.name}\"`,\n );\n }\n seen.set(key, step.name);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline\n// ---------------------------------------------------------------------------\n\n/**\n * A built pipeline, ready to execute.\n *\n * Call `run(args)` to execute the pipeline. The result is a\n * {@link PipelineResult} — either a success with the fully accumulated\n * context, or a failure with error details and a rollback report.\n *\n * Pipeline objects are frozen (immutable) and can be called repeatedly.\n *\n * @typeParam Args - The input type accepted by `run()`.\n * @typeParam Ctx - The accumulated output type on success.\n */\nexport type Pipeline<Args extends StepContext, Ctx> = {\n /** The pipeline's name, as provided at build time. */\n readonly name: string;\n /**\n * Execute the pipeline.\n *\n * @param args - The initial arguments. Merged into the context before\n * the first step runs. Validated against `argsSchema` if one was\n * provided.\n * @returns A {@link PipelineResult} — discriminate on `success` to\n * access `data` (on success) or `errors`/`rollback` (on failure).\n */\n readonly run: (args: Args) => Promise<PipelineResult<Ctx>>;\n};\n\n// ---------------------------------------------------------------------------\n// Schema validation\n// ---------------------------------------------------------------------------\n\nfunction validateSchema<T>(\n schema: ParserSchema<T> | undefined,\n data: unknown,\n label: string,\n code: RunsheetErrorCode,\n): { success: true; data: T } | { success: false; errors: RunsheetError[] } {\n if (!schema) return { success: true, data: data as T };\n\n const parsed = schema.safeParse(data);\n if (parsed.success) return { success: true, data: parsed.data };\n\n const errors = parsed.error.issues.map(\n (issue) => new RunsheetError(code, `${label}: ${issue.path.join('.')}: ${issue.message}`),\n );\n return { success: false, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Rollback\n// ---------------------------------------------------------------------------\n\nasync function executeRollback(\n executedSteps: readonly Step[],\n snapshots: readonly StepContext[],\n outputs: readonly StepOutput[],\n): Promise<RollbackReport> {\n const completed: string[] = [];\n const failed: RollbackFailure[] = [];\n\n for (let i = executedSteps.length - 1; i >= 0; i--) {\n const step = executedSteps[i];\n if (!step.rollback) continue;\n\n try {\n await step.rollback(snapshots[i], outputs[i]);\n completed.push(step.name);\n } catch (err) {\n failed.push({\n step: step.name,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n return Object.freeze({ completed, failed });\n}\n\n// ---------------------------------------------------------------------------\n// Execution state — accumulated during pipeline run\n// ---------------------------------------------------------------------------\n\ntype ExecutionState = {\n context: StepContext;\n readonly snapshots: StepContext[];\n readonly outputs: StepOutput[];\n readonly executedSteps: Step[];\n readonly stepsExecuted: string[];\n readonly stepsSkipped: string[];\n};\n\nfunction createExecutionState(args: StepContext): ExecutionState {\n return {\n context: Object.freeze({ ...args }),\n snapshots: [],\n outputs: [],\n executedSteps: [],\n stepsExecuted: [],\n stepsSkipped: [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Result constructors\n// ---------------------------------------------------------------------------\n\nfunction pipelineFailure(\n pipelineName: string,\n args: StepContext,\n state: ExecutionState,\n failedStep: string,\n errors: Error[],\n rollback: RollbackReport,\n): PipelineFailure {\n return Object.freeze({\n success: false as const,\n errors,\n meta: Object.freeze({\n pipeline: pipelineName,\n args,\n stepsExecuted: state.stepsExecuted,\n stepsSkipped: state.stepsSkipped,\n }),\n failedStep,\n rollback,\n });\n}\n\nfunction pipelineSuccess(\n pipelineName: string,\n args: StepContext,\n state: ExecutionState,\n): PipelineSuccess<StepContext> {\n return Object.freeze({\n success: true as const,\n data: state.context,\n errors: [] as [],\n meta: Object.freeze({\n pipeline: pipelineName,\n args,\n stepsExecuted: state.stepsExecuted,\n stepsSkipped: state.stepsSkipped,\n }),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Step executor — the full lifecycle (validate requires → run → validate provides)\n// ---------------------------------------------------------------------------\n\nfunction createStepExecutor(\n step: Step,\n): (ctx: Readonly<StepContext>) => Promise<Result<StepOutput>> {\n return async (ctx) => {\n // Validate requires\n const requiresCheck = validateSchema(\n step.requires,\n ctx,\n `${step.name} requires`,\n 'REQUIRES_VALIDATION',\n );\n if (!requiresCheck.success) {\n return { success: false as const, errors: requiresCheck.errors };\n }\n\n // Execute step run\n const result = await step.run(ctx);\n if (!result.success) return result;\n\n // Validate provides\n const providesCheck = validateSchema(\n step.provides,\n result.data,\n `${step.name} provides`,\n 'PROVIDES_VALIDATION',\n );\n if (!providesCheck.success) {\n return { success: false as const, errors: providesCheck.errors };\n }\n\n return {\n success: true as const,\n data: providesCheck.data as StepOutput,\n errors: [] as [],\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline execution\n// ---------------------------------------------------------------------------\n\nasync function executePipeline(\n config: PipelineConfig,\n args: StepContext,\n): Promise<PipelineResult<StepContext>> {\n // Validate pipeline args if schema provided\n if (config.argsSchema) {\n const argsCheck = validateSchema(\n config.argsSchema,\n args,\n `${config.name} args`,\n 'ARGS_VALIDATION',\n );\n if (!argsCheck.success) {\n const state = createExecutionState(args);\n return pipelineFailure(\n config.name,\n args,\n state,\n config.name,\n argsCheck.errors,\n Object.freeze({ completed: [], failed: [] }),\n );\n }\n }\n\n const state = createExecutionState(args);\n const middlewares = config.middleware ?? [];\n\n for (const step of config.steps) {\n // Evaluate conditional predicate\n try {\n if (isConditionalStep(step) && !step.predicate(state.context)) {\n state.stepsSkipped.push(step.name);\n continue;\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const error = new RunsheetError('PREDICATE', `${step.name} predicate: ${message}`);\n if (err instanceof Error) error.cause = err;\n const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);\n return pipelineFailure(config.name, args, state, step.name, [error], rollback);\n }\n\n // Snapshot pre-step context\n state.snapshots.push(state.context);\n\n // Build executor with middleware wrapping the full lifecycle\n const baseExecutor = createStepExecutor(step);\n const executor = applyMiddleware(\n middlewares,\n { name: step.name, requires: step.requires, provides: step.provides },\n baseExecutor,\n );\n\n // Execute (try/catch handles middleware throws outside the Result boundary)\n let result: Result<StepOutput>;\n try {\n result = await executor(state.context);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n state.snapshots.pop();\n const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);\n return pipelineFailure(config.name, args, state, step.name, [error], rollback);\n }\n\n if (!result.success) {\n // Remove the snapshot we just pushed — the step didn't complete\n state.snapshots.pop();\n const rollback = await executeRollback(state.executedSteps, state.snapshots, state.outputs);\n return pipelineFailure(config.name, args, state, step.name, result.errors, rollback);\n }\n\n // Track step output and accumulate context\n const output = result.data;\n state.outputs.push(output);\n state.executedSteps.push(step);\n state.stepsExecuted.push(step.name);\n state.context = Object.freeze({ ...state.context, ...output });\n }\n\n return pipelineSuccess(config.name, args, state);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a pipeline from an array of steps.\n *\n * The result type is inferred from the steps — `pipeline.run()` returns\n * a {@link PipelineResult} whose `data` is the intersection of the\n * initial `Args` and all step output types.\n *\n * @example\n * ```ts\n * const pipeline = buildPipeline({\n * name: 'placeOrder',\n * steps: [validateOrder, chargePayment, sendConfirmation],\n * middleware: [logging, timing],\n * argsSchema: z.object({ orderId: z.string() }),\n * });\n *\n * const result = await pipeline.run({ orderId: '123' });\n * if (result.success) {\n * result.data.chargeId; // string — fully typed\n * }\n * ```\n *\n * **Execution semantics:**\n * - Steps execute sequentially in array order.\n * - Context is frozen (`Object.freeze`) at every step boundary.\n * - Conditional steps (wrapped with `when()`) are skipped when their\n * predicate returns false — no snapshot, no rollback entry.\n * - On step failure, rollback handlers for all previously completed\n * steps execute in reverse order (best-effort).\n * - Middleware wraps the full step lifecycle including schema validation.\n *\n * **Invariants:**\n * - The returned pipeline object is frozen (immutable).\n * - Errors thrown by steps, predicates, or middleware are caught and\n * returned as `PipelineFailure` — `run()` never throws.\n *\n * @typeParam Args - The pipeline's input type. Inferred from `argsSchema`\n * if provided, otherwise defaults to `StepContext`.\n * @typeParam S - The step types in the array. Inferred automatically —\n * do not specify manually.\n * @param config - Pipeline configuration.\n * @param config.name - Pipeline name, used in metadata and error messages.\n * @param config.steps - Steps to execute in order.\n * @param config.middleware - Optional middleware applied to every step.\n * First in array = outermost wrapper.\n * @param config.argsSchema - Optional schema that validates `args` before\n * any steps run. Validation failure produces a `PipelineFailure` with\n * `failedStep` set to the pipeline name.\n * @returns A frozen {@link Pipeline} whose `run()` method executes the\n * steps and returns a {@link PipelineResult}.\n */\nexport function buildPipeline<\n Args extends StepContext = StepContext,\n S extends Step = Step,\n>(config: {\n readonly name: string;\n readonly steps: readonly S[];\n readonly middleware?: readonly StepMiddleware[];\n readonly argsSchema?: ParserSchema<Args>;\n readonly strict?: boolean;\n}): Pipeline<Args, Args & UnionToIntersection<ExtractProvides<S>>> {\n if (config.strict) checkStrictOverlap(config.steps);\n\n return Object.freeze({\n name: config.name,\n run: (args: Args) => executePipeline(config as PipelineConfig, args),\n }) as Pipeline<Args, Args & UnionToIntersection<ExtractProvides<S>>>;\n}\n","import type {\n ExtractProvides,\n ExtractRequires,\n Step,\n StepContext,\n StepOutput,\n TypedStep,\n UnionToIntersection,\n} from './types.js';\n\n/** Ensure a type satisfies StepContext, falling back to StepContext. */\ntype AsContext<T> = T extends StepContext ? T : StepContext;\nimport { RunsheetError } from './errors.js';\nimport { isConditionalStep } from './when.js';\n\n// ---------------------------------------------------------------------------\n// Inner step execution result\n// ---------------------------------------------------------------------------\n\ntype InnerResult = {\n step: Step;\n skipped: boolean;\n output?: StepOutput;\n errors?: Error[];\n};\n\n// ---------------------------------------------------------------------------\n// Schema validation (mirrors pipeline.ts — kept local to avoid coupling)\n// ---------------------------------------------------------------------------\n\nfunction validateInnerSchema(\n schema: Step['requires'] | Step['provides'],\n data: unknown,\n label: string,\n code: 'REQUIRES_VALIDATION' | 'PROVIDES_VALIDATION',\n): RunsheetError[] | null {\n if (!schema) return null;\n const parsed = schema.safeParse(data);\n if (parsed.success) return null;\n return parsed.error.issues.map(\n (issue) => new RunsheetError(code, `${label}: ${issue.path.join('.')}: ${issue.message}`),\n );\n}\n\n// ---------------------------------------------------------------------------\n// Execute a single inner step\n// ---------------------------------------------------------------------------\n\nasync function executeInner(step: Step, ctx: Readonly<StepContext>): Promise<InnerResult> {\n // Conditional check\n try {\n if (isConditionalStep(step) && !step.predicate(ctx)) {\n return { step, skipped: true };\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const error = new RunsheetError('PREDICATE', `${step.name} predicate: ${message}`);\n if (err instanceof Error) error.cause = err;\n return { step, skipped: false, errors: [error] };\n }\n\n // Validate requires\n const requiresErrors = validateInnerSchema(\n step.requires,\n ctx,\n `${step.name} requires`,\n 'REQUIRES_VALIDATION',\n );\n if (requiresErrors) return { step, skipped: false, errors: requiresErrors };\n\n // Run step (composable() wrapper inside defineStep catches throws)\n const result = await step.run(ctx);\n if (!result.success) return { step, skipped: false, errors: [...result.errors] };\n\n // Validate provides\n const providesErrors = validateInnerSchema(\n step.provides,\n result.data,\n `${step.name} provides`,\n 'PROVIDES_VALIDATION',\n );\n if (providesErrors) return { step, skipped: false, errors: providesErrors };\n\n return { step, skipped: false, output: result.data };\n}\n\n// ---------------------------------------------------------------------------\n// parallel()\n// ---------------------------------------------------------------------------\n\n/**\n * Run multiple steps concurrently and merge their outputs.\n *\n * All inner steps receive the same pre-parallel context snapshot and\n * execute via `Promise.allSettled`. On success, outputs are merged in\n * array order (deterministic). On partial failure, succeeded inner\n * steps are rolled back in reverse array order before the failure\n * propagates.\n *\n * The returned step acts as a single step from the pipeline's\n * perspective — middleware wraps the group as a whole.\n *\n * Inner steps retain their own `requires`/`provides` validation,\n * `retry`, and `timeout` behavior. Conditional steps (via `when()`)\n * are evaluated per inner step.\n *\n * @example\n * ```ts\n * const pipeline = buildPipeline({\n * name: 'checkout',\n * steps: [\n * validateOrder,\n * parallel(reserveInventory, chargePayment),\n * sendConfirmation,\n * ],\n * });\n * ```\n *\n * @param steps - Two or more steps to execute concurrently.\n * @returns A frozen {@link TypedStep} whose `Requires` is the\n * intersection of all inner steps' requires, and `Provides` is the\n * intersection of all inner steps' provides.\n */\nexport function parallel<S extends readonly TypedStep[]>(\n ...steps: [...S]\n): TypedStep<\n AsContext<UnionToIntersection<ExtractRequires<S[number]>>>,\n AsContext<UnionToIntersection<ExtractProvides<S[number]>>>\n> {\n const name = `parallel(${(steps as readonly Step[]).map((s) => s.name).join(', ')})`;\n const innerSteps = steps as readonly Step[];\n\n // ----- run: execute all inner steps concurrently ----- //\n const run: Step['run'] = async (ctx) => {\n const settled = await Promise.allSettled(innerSteps.map((step) => executeInner(step, ctx)));\n\n const succeeded: { step: Step; output: StepOutput }[] = [];\n const allErrors: Error[] = [];\n\n for (const s of settled) {\n if (s.status === 'rejected') {\n allErrors.push(s.reason instanceof Error ? s.reason : new Error(String(s.reason)));\n } else {\n const r = s.value;\n if (r.skipped) continue;\n if (r.output) {\n succeeded.push({ step: r.step, output: r.output });\n } else if (r.errors) {\n allErrors.push(...r.errors);\n }\n }\n }\n\n if (allErrors.length > 0) {\n // Rollback succeeded inner steps in reverse array order (best-effort)\n for (let i = succeeded.length - 1; i >= 0; i--) {\n const { step, output } = succeeded[i];\n if (step.rollback) {\n try {\n await step.rollback(ctx, output);\n } catch {\n // Best-effort — inner rollback errors during partial failure\n // are not surfaced. The pipeline's own rollback report covers\n // the parallel step as a whole.\n }\n }\n }\n return { success: false as const, errors: allErrors };\n }\n\n // Merge outputs in array order (deterministic)\n const merged: StepOutput = {};\n for (const { output } of succeeded) {\n Object.assign(merged, output);\n }\n\n return { success: true as const, data: merged, errors: [] as [] };\n };\n\n // ----- rollback: called when a later sequential step fails ----- //\n // The pipeline passes the merged output. Each inner step's rollback\n // receives the full merged output (a superset of what it produced).\n // This is safe because rollback handlers only read their own keys.\n const rollback: NonNullable<Step['rollback']> = async (ctx, mergedOutput) => {\n const errors: Error[] = [];\n for (let i = innerSteps.length - 1; i >= 0; i--) {\n const step = innerSteps[i];\n if (!step.rollback) continue;\n try {\n await step.rollback(ctx, mergedOutput);\n } catch (err) {\n errors.push(err instanceof Error ? err : new Error(String(err)));\n }\n }\n if (errors.length > 0) {\n const error = new Error(`${name}: ${errors.length} rollback(s) failed`);\n error.cause = errors;\n throw error;\n }\n };\n\n return Object.freeze({\n name,\n requires: undefined,\n provides: undefined,\n run,\n rollback,\n retry: undefined,\n timeout: undefined,\n }) as unknown as TypedStep<\n AsContext<UnionToIntersection<ExtractRequires<S[number]>>>,\n AsContext<UnionToIntersection<ExtractProvides<S[number]>>>\n >;\n}\n","import type { ParserSchema } from 'composable-functions';\nimport type { Step, StepContext, TypedStep } from './types.js';\nimport type { StepMiddleware } from './middleware.js';\nimport { buildPipeline } from './pipeline.js';\nimport type { Pipeline } from './pipeline.js';\n\n// ---------------------------------------------------------------------------\n// Builder types\n// ---------------------------------------------------------------------------\n\n/**\n * A fluent pipeline builder that progressively narrows the accumulated\n * context type as steps are added.\n *\n * Each method returns a new, frozen builder — builders are immutable.\n * This means you can safely fork a builder to create variants:\n *\n * ```ts\n * const base = createPipeline('order').step(validate);\n * const withCharge = base.step(charge).build();\n * const withoutCharge = base.build(); // unaffected by the fork\n * ```\n *\n * @typeParam Args - The pipeline's initial input type.\n * @typeParam Ctx - The accumulated context type so far (grows with each `.step()`).\n */\nexport type PipelineBuilder<Args extends StepContext, Ctx extends StepContext> = {\n /**\n * Add a step to the pipeline.\n *\n * The step's `Requires` type must be satisfied by the current `Ctx`.\n * The returned builder's `Ctx` expands to include the step's `Provides`.\n *\n * @typeParam Provides - The output type of the step being added.\n * @param step - A {@link TypedStep} (from `defineStep` or `when`).\n * @returns A new builder with the expanded context type.\n */\n readonly step: <Provides extends StepContext>(\n step: TypedStep<Ctx, Provides>,\n ) => PipelineBuilder<Args, Ctx & Provides>;\n\n /**\n * Add middleware to the pipeline.\n *\n * Middleware is applied to every step. Multiple `.use()` calls\n * accumulate — earlier middleware is outermost (executes first).\n *\n * @param middleware - One or more {@link StepMiddleware} functions.\n * @returns A new builder with the middleware added.\n */\n readonly use: (...middleware: StepMiddleware[]) => PipelineBuilder<Args, Ctx>;\n\n /**\n * Build the pipeline.\n *\n * @returns A frozen {@link Pipeline} ready to execute with `run()`.\n */\n readonly build: () => Pipeline<Args, Ctx>;\n};\n\n// ---------------------------------------------------------------------------\n// Internal builder state (immutable — each method returns a new builder)\n// ---------------------------------------------------------------------------\n\ntype BuilderState = {\n readonly name: string;\n readonly steps: readonly Step[];\n readonly middleware: readonly StepMiddleware[];\n readonly argsSchema: ParserSchema<StepContext> | undefined;\n readonly strict: boolean;\n};\n\nfunction makeBuilder<Args extends StepContext, Ctx extends StepContext>(\n state: BuilderState,\n): PipelineBuilder<Args, Ctx> {\n return Object.freeze({\n step: <Provides extends StepContext>(step: TypedStep<Ctx, Provides>) =>\n makeBuilder<Args, Ctx & Provides>({\n ...state,\n steps: [...state.steps, step],\n }),\n\n use: (...middleware: StepMiddleware[]) =>\n makeBuilder<Args, Ctx>({\n ...state,\n middleware: [...state.middleware, ...middleware],\n }),\n\n build: () =>\n buildPipeline({\n name: state.name,\n steps: state.steps,\n middleware: state.middleware.length > 0 ? state.middleware : undefined,\n argsSchema: state.argsSchema as ParserSchema<Args> | undefined,\n strict: state.strict || undefined,\n }) as Pipeline<Args, Ctx>,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Start building a pipeline with the fluent builder API.\n *\n * The builder gives progressive type narrowing — each `.step()` call\n * extends the known context type, so TypeScript catches mismatches\n * at compile time.\n *\n * **Type-only args** (no runtime validation):\n * ```ts\n * createPipeline<{ orderId: string }>('placeOrder')\n * .step(validateOrder)\n * .step(chargePayment)\n * .build();\n * ```\n *\n * **Schema args** (runtime validation + type inference):\n * ```ts\n * createPipeline('placeOrder', z.object({ orderId: z.string() }))\n * .step(validateOrder)\n * .step(chargePayment)\n * .build();\n * ```\n *\n * **Strict mode** (no schema):\n * ```ts\n * createPipeline('placeOrder', { strict: true })\n * ```\n *\n * **Schema + strict mode:**\n * ```ts\n * createPipeline('placeOrder', z.object({ orderId: z.string() }), { strict: true })\n * ```\n *\n * @typeParam Args - The pipeline's input type. Inferred from `argsSchema`\n * if provided, otherwise specify via generic parameter.\n * @param name - Pipeline name, used in metadata and error messages.\n * @param schemaOrOptions - A schema for runtime args validation, or a\n * {@link PipelineOptions} object.\n * @param options - When the second argument is a schema, pass options here.\n * @returns A frozen {@link PipelineBuilder} ready for `.step()`,\n * `.use()`, and `.build()`.\n */\nexport type PipelineOptions = {\n strict?: boolean;\n};\n\n// Overload: name only\nexport function createPipeline<Args extends StepContext>(name: string): PipelineBuilder<Args, Args>;\n\n// Overload: name + schema\nexport function createPipeline<Args extends StepContext>(\n name: string,\n argsSchema: ParserSchema<Args>,\n): PipelineBuilder<Args, Args>;\n\n// Overload: name + options (no schema)\nexport function createPipeline<Args extends StepContext>(\n name: string,\n options: PipelineOptions,\n): PipelineBuilder<Args, Args>;\n\n// Overload: name + schema + options\nexport function createPipeline<Args extends StepContext>(\n name: string,\n argsSchema: ParserSchema<Args>,\n options: PipelineOptions,\n): PipelineBuilder<Args, Args>;\n\n// Implementation\nexport function createPipeline<Args extends StepContext>(\n name: string,\n schemaOrOptions?: ParserSchema<Args> | PipelineOptions,\n options?: PipelineOptions,\n): PipelineBuilder<Args, Args> {\n let argsSchema: ParserSchema<Args> | undefined;\n let strict = false;\n\n if (schemaOrOptions != null) {\n if ('safeParse' in schemaOrOptions) {\n argsSchema = schemaOrOptions;\n strict = options?.strict ?? false;\n } else {\n strict = schemaOrOptions.strict ?? false;\n }\n }\n\n return makeBuilder<Args, Args>({\n name,\n steps: [],\n middleware: [],\n argsSchema,\n strict,\n });\n}\n"],"mappings":"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","names":["state"]}
|