runsheet 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -351,7 +351,7 @@ declare function defineStep<Requires extends StepContext, Provides extends StepC
351
351
  * }
352
352
  * ```
353
353
  */
354
- type RunsheetErrorCode = 'REQUIRES_VALIDATION' | 'PROVIDES_VALIDATION' | 'ARGS_VALIDATION' | 'PREDICATE' | 'TIMEOUT' | 'RETRY_EXHAUSTED' | 'STRICT_OVERLAP';
354
+ type RunsheetErrorCode = 'REQUIRES_VALIDATION' | 'PROVIDES_VALIDATION' | 'ARGS_VALIDATION' | 'PREDICATE' | 'TIMEOUT' | 'RETRY_EXHAUSTED' | 'STRICT_OVERLAP' | 'CHOICE_NO_MATCH' | 'ROLLBACK' | 'UNKNOWN';
355
355
  /**
356
356
  * Base error class for all errors produced by the runsheet library.
357
357
  *
@@ -361,8 +361,8 @@ type RunsheetErrorCode = 'REQUIRES_VALIDATION' | 'PROVIDES_VALIDATION' | 'ARGS_V
361
361
  * library itself produced it.
362
362
  *
363
363
  * Use `instanceof RunsheetError` to distinguish library errors from
364
- * application errors, and the `code` property to identify the
365
- * specific failure.
364
+ * application errors. Use `instanceof` on a subclass (e.g.,
365
+ * `TimeoutError`) or check the `code` property for specific failures.
366
366
  */
367
367
  declare class RunsheetError extends Error {
368
368
  /** Discriminant code identifying the type of library error. */
@@ -373,6 +373,56 @@ declare class RunsheetError extends Error {
373
373
  */
374
374
  constructor(code: RunsheetErrorCode, message: string);
375
375
  }
376
+ /** Schema validation failed on the accumulated context before a step ran. */
377
+ declare class RequiresValidationError extends RunsheetError {
378
+ constructor(message: string);
379
+ }
380
+ /** Schema validation failed on a step's output after it ran. */
381
+ declare class ProvidesValidationError extends RunsheetError {
382
+ constructor(message: string);
383
+ }
384
+ /** Schema validation failed on the pipeline's input arguments. */
385
+ declare class ArgsValidationError extends RunsheetError {
386
+ constructor(message: string);
387
+ }
388
+ /** A `when()` or `choice()` predicate threw an error. */
389
+ declare class PredicateError extends RunsheetError {
390
+ constructor(message: string);
391
+ }
392
+ /** A step exceeded its configured timeout. */
393
+ declare class TimeoutError extends RunsheetError {
394
+ /** The timeout duration in milliseconds that was exceeded. */
395
+ readonly timeoutMs: number;
396
+ constructor(message: string, timeoutMs: number);
397
+ }
398
+ /** A step failed after exhausting all retry attempts. */
399
+ declare class RetryExhaustedError extends RunsheetError {
400
+ /** Total number of attempts (initial + retries). */
401
+ readonly attempts: number;
402
+ constructor(message: string, attempts: number);
403
+ }
404
+ /** Two steps provide the same key (strict mode, detected at build time). */
405
+ declare class StrictOverlapError extends RunsheetError {
406
+ /** The key that is provided by multiple steps. */
407
+ readonly key: string;
408
+ /** The names of the two steps that both provide the key. */
409
+ readonly steps: readonly [string, string];
410
+ constructor(message: string, key: string, steps: readonly [string, string]);
411
+ }
412
+ /** No branch matched in a `choice()` step. */
413
+ declare class ChoiceNoMatchError extends RunsheetError {
414
+ constructor(message: string);
415
+ }
416
+ /** A non-Error value was thrown and caught by the pipeline engine. */
417
+ declare class UnknownError extends RunsheetError {
418
+ /** The original thrown value before stringification. */
419
+ readonly originalValue: unknown;
420
+ constructor(message: string, originalValue: unknown);
421
+ }
422
+ /** One or more rollback handlers failed in a combinator. */
423
+ declare class RollbackError extends RunsheetError {
424
+ constructor(message: string);
425
+ }
376
426
 
377
427
  /**
378
428
  * Metadata about the step being executed, passed to middleware.
@@ -577,6 +627,7 @@ declare function when<Requires extends StepContext, Provides extends StepContext
577
627
 
578
628
  /** Ensure a type satisfies StepContext, falling back to StepContext. */
579
629
  type AsContext<T> = T extends StepContext ? T : StepContext;
630
+
580
631
  /**
581
632
  * Run multiple steps concurrently and merge their outputs.
582
633
  *
@@ -612,6 +663,168 @@ type AsContext<T> = T extends StepContext ? T : StepContext;
612
663
  */
613
664
  declare function parallel<S extends readonly TypedStep[]>(...steps: [...S]): TypedStep<AsContext<UnionToIntersection<ExtractRequires<S[number]>>>, AsContext<UnionToIntersection<ExtractProvides<S[number]>>>>;
614
665
 
666
+ /** A [predicate, step] tuple used by {@link choice}. */
667
+ type BranchTuple = readonly [(ctx: Readonly<StepContext>) => boolean, TypedStep];
668
+ /** Extract the Requires type from a branch tuple's step. */
669
+ type BranchRequires<T> = T extends readonly [unknown, infer S extends Step] ? ExtractRequires<S> : T extends Step ? ExtractRequires<T> : StepContext;
670
+ /** Extract the Provides type from a branch tuple's step. */
671
+ type BranchProvides<T> = T extends readonly [unknown, infer S extends Step] ? ExtractProvides<S> : T extends Step ? ExtractProvides<T> : StepContext;
672
+ /**
673
+ * Execute the first branch whose predicate returns `true`.
674
+ *
675
+ * Similar to an AWS Step Functions Choice state — predicates are evaluated
676
+ * in order, and the first match wins. Exactly one branch executes. If no
677
+ * predicate matches, the step fails with a `CHOICE_NO_MATCH` error.
678
+ *
679
+ * A bare step (without a predicate tuple) can be passed as the last argument
680
+ * to serve as a default branch — it is equivalent to `[() => true, step]`.
681
+ *
682
+ * All branches should provide the same output shape so that subsequent
683
+ * steps can rely on a consistent context type.
684
+ *
685
+ * @example
686
+ * ```ts
687
+ * const pipeline = buildPipeline({
688
+ * name: 'payment',
689
+ * steps: [
690
+ * validateOrder,
691
+ * choice(
692
+ * [(ctx) => ctx.method === 'card', chargeCard],
693
+ * [(ctx) => ctx.method === 'bank', chargeBankTransfer],
694
+ * chargeDefault, // default
695
+ * ),
696
+ * sendReceipt,
697
+ * ],
698
+ * });
699
+ * ```
700
+ *
701
+ * @param branches - One or more `[predicate, step]` tuples, optionally
702
+ * followed by a bare step as the default.
703
+ * @returns A frozen {@link TypedStep} that executes the first matching branch.
704
+ */
705
+ declare function choice<B extends readonly BranchTuple[]>(...branches: [...B]): TypedStep<AsContext<UnionToIntersection<BranchRequires<B[number]>>>, AsContext<UnionToIntersection<BranchProvides<B[number]>>>>;
706
+ declare function choice<B extends readonly BranchTuple[], D extends TypedStep>(...args: [...B, D]): TypedStep<AsContext<UnionToIntersection<BranchRequires<B[number]> | ExtractRequires<D>>>, AsContext<UnionToIntersection<BranchProvides<B[number]> | ExtractProvides<D>>>>;
707
+
708
+ /**
709
+ * Iterate over a collection and run a function or step per item, concurrently.
710
+ *
711
+ * Similar to an AWS Step Functions Map state — extracts a collection from
712
+ * the pipeline context, runs the callback for each item via
713
+ * `Promise.allSettled`, and collects results into an array under the
714
+ * given key.
715
+ *
716
+ * **Function form:** `(item, ctx) => result` — items can be any type.
717
+ *
718
+ * **Step form:** each item must be an object whose keys are spread into
719
+ * the pipeline context before the step runs (i.e., the step receives
720
+ * `{ ...ctx, ...item }`). The step's own `requires`/`provides`
721
+ * validation, `retry`, and `timeout` apply per item. On partial failure,
722
+ * succeeded items are rolled back (if the step has a rollback handler).
723
+ *
724
+ * @example
725
+ * ```ts
726
+ * // Function form
727
+ * const pipeline = buildPipeline({
728
+ * name: 'notify',
729
+ * steps: [
730
+ * map('emails', (ctx) => ctx.users, async (user) => {
731
+ * await sendEmail(user.email);
732
+ * return { email: user.email, sentAt: new Date() };
733
+ * }),
734
+ * ],
735
+ * });
736
+ *
737
+ * // Step form
738
+ * const pipeline = buildPipeline({
739
+ * name: 'process',
740
+ * steps: [
741
+ * map('results', (ctx) => ctx.items, processItem),
742
+ * ],
743
+ * });
744
+ * ```
745
+ *
746
+ * @param key - The output key under which results are collected.
747
+ * @param collection - A selector that extracts the collection from context.
748
+ * @param fnOrStep - A per-item function or a step to execute for each item.
749
+ * @returns A frozen {@link TypedStep} that provides `{ [key]: Result[] }`.
750
+ */
751
+ declare function map<K extends string, Item, Result>(key: K, collection: (ctx: Readonly<StepContext>) => Item[], fn: (item: Item, ctx: Readonly<StepContext>) => Result | Promise<Result>): TypedStep<StepContext, Record<K, Awaited<Result>[]>>;
752
+ declare function map<K extends string, S extends TypedStep>(key: K, collection: (ctx: Readonly<StepContext>) => StepContext[], step: S): TypedStep<StepContext, Record<K, ExtractProvides<S>[]>>;
753
+
754
+ /**
755
+ * Filter a collection from context using a predicate, concurrently.
756
+ *
757
+ * Extracts a collection from the pipeline context, evaluates the
758
+ * predicate for each item via `Promise.allSettled`, and collects
759
+ * items that pass into an array under the given key. Original order
760
+ * is preserved.
761
+ *
762
+ * The predicate can be sync or async. If any predicate throws, the
763
+ * entire step fails — no partial results are returned.
764
+ *
765
+ * There is no rollback (filtering is a pure operation with nothing
766
+ * to undo).
767
+ *
768
+ * @example
769
+ * ```ts
770
+ * const pipeline = buildPipeline({
771
+ * name: 'notify',
772
+ * steps: [
773
+ * filter('eligible', (ctx) => ctx.users, (user) => user.optedIn),
774
+ * map('emails', (ctx) => ctx.eligible, sendEmail),
775
+ * ],
776
+ * });
777
+ *
778
+ * // Async predicate
779
+ * filter('valid', (ctx) => ctx.orders, async (order) => {
780
+ * const inventory = await checkInventory(order.sku);
781
+ * return inventory.available >= order.quantity;
782
+ * });
783
+ * ```
784
+ *
785
+ * @param key - The output key under which filtered results are collected.
786
+ * @param collection - A selector that extracts the collection from context.
787
+ * @param predicate - A per-item predicate. Return `true` to keep, `false` to discard.
788
+ * @returns A frozen {@link TypedStep} that provides `{ [key]: Item[] }`.
789
+ */
790
+ declare function filter<K extends string, Item>(key: K, collection: (ctx: Readonly<StepContext>) => Item[], predicate: (item: Item, ctx: Readonly<StepContext>) => boolean | Promise<boolean>): TypedStep<StepContext, Record<K, Item[]>>;
791
+
792
+ /**
793
+ * Map each item in a collection to an array, then flatten one level.
794
+ *
795
+ * Extracts a collection from the pipeline context, runs the callback
796
+ * for each item via `Promise.allSettled`, and flattens the per-item
797
+ * arrays into a single array under the given key.
798
+ *
799
+ * The callback can be sync or async. If any callback throws, the
800
+ * entire step fails — no partial results are returned.
801
+ *
802
+ * There is no rollback (pure transformation with nothing to undo).
803
+ *
804
+ * @example
805
+ * ```ts
806
+ * // Expand orders into line items
807
+ * const pipeline = buildPipeline({
808
+ * name: 'process',
809
+ * steps: [
810
+ * flatMap('lineItems', (ctx) => ctx.orders, (order) => order.items),
811
+ * ],
812
+ * });
813
+ *
814
+ * // Async callback
815
+ * flatMap('emails', (ctx) => ctx.teams, async (team) => {
816
+ * const members = await fetchMembers(team.id);
817
+ * return members.map((m) => m.email);
818
+ * });
819
+ * ```
820
+ *
821
+ * @param key - The output key under which flattened results are collected.
822
+ * @param collection - A selector that extracts the collection from context.
823
+ * @param fn - A per-item callback that returns an array (or Promise of array).
824
+ * @returns A frozen {@link TypedStep} that provides `{ [key]: Item[] }`.
825
+ */
826
+ declare function flatMap<K extends string, Item, Result>(key: K, collection: (ctx: Readonly<StepContext>) => Item[], fn: (item: Item, ctx: Readonly<StepContext>) => Result[] | Promise<Result[]>): TypedStep<StepContext, Record<K, Result[]>>;
827
+
615
828
  /**
616
829
  * A fluent pipeline builder that progressively narrows the accumulated
617
830
  * context type as steps are added.
@@ -707,4 +920,4 @@ declare function createPipeline<Args extends StepContext>(name: string, argsSche
707
920
  declare function createPipeline<Args extends StepContext>(name: string, options: PipelineOptions): PipelineBuilder<Args, Args>;
708
921
  declare function createPipeline<Args extends StepContext>(name: string, argsSchema: ParserSchema<Args>, options: PipelineOptions): PipelineBuilder<Args, Args>;
709
922
 
710
- export { type ConditionalStep, type ExtractProvides, type ExtractRequires, type Pipeline, type PipelineBuilder, type PipelineConfig, type PipelineExecutionMeta, type PipelineFailure, type PipelineResult, type PipelineSuccess, type RetryPolicy, type RollbackFailure, type RollbackReport, RunsheetError, type RunsheetErrorCode, type Step, type StepConfig, type StepContext, type StepExecutor, type StepInfo, type StepMiddleware, type StepOutput, type TypedStep, buildPipeline, createPipeline, defineStep, parallel, when };
923
+ export { ArgsValidationError, ChoiceNoMatchError, type ConditionalStep, type ExtractProvides, type ExtractRequires, type Pipeline, type PipelineBuilder, type PipelineConfig, type PipelineExecutionMeta, type PipelineFailure, type PipelineResult, type PipelineSuccess, PredicateError, ProvidesValidationError, RequiresValidationError, RetryExhaustedError, type RetryPolicy, RollbackError, type RollbackFailure, type RollbackReport, RunsheetError, type RunsheetErrorCode, type Step, type StepConfig, type StepContext, type StepExecutor, type StepInfo, type StepMiddleware, type StepOutput, StrictOverlapError, TimeoutError, type TypedStep, UnknownError, buildPipeline, choice, createPipeline, defineStep, filter, flatMap, map, parallel, when };