@nlozgachev/pipelined 0.20.0 → 0.21.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/core.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as Maybe, W as WithValue, a as WithLog, R as Result, b as WithKind, c as WithError, T as Task, d as WithErrors, e as WithFirst, f as WithSecond } from './Task-JOnNAaPq.mjs';
2
- export { D as Deferred, E as Err, N as None, O as Ok, S as Some } from './Task-JOnNAaPq.mjs';
1
+ import { M as Maybe, W as WithValue, a as WithLog, D as Deferred, R as Result, b as WithKind, c as WithError, d as RetryOptions, e as TimeoutOptions, f as WithMs, g as WithTrailing, h as WithRetry, i as WithTimeout, j as WithLeading, k as WithMaxWait, l as WithN, m as WithOverflow, n as WithKey, o as WithPerKey, p as WithMinInterval, q as WithCooldown, r as WithMaxSize, s as WithDedupe, t as WithConcurrency, u as WithSize, T as Task, v as WithErrors, w as WithFirst, x as WithSecond } from './Task-CT8iwwuB.mjs';
2
+ export { E as Err, N as None, O as Ok, S as Some } from './Task-CT8iwwuB.mjs';
3
3
  import { N as NonEmptyList } from './NonEmptyList-BlGFjor5.mjs';
4
4
 
5
5
  /** Keys of T for which undefined is assignable (i.e. optional fields). */
@@ -399,6 +399,600 @@ declare namespace Logged {
399
399
  const run: <W, A>(data: Logged<W, A>) => readonly [A, ReadonlyArray<W>];
400
400
  }
401
401
 
402
+ /**
403
+ * A reusable description of async work — decoupled from execution strategy and lifetime.
404
+ *
405
+ * Separate concerns:
406
+ * - **What** to do: encoded in the `Op` via `Op.create`
407
+ * - **How** to execute: chosen at `Op.interpret` time (restartable, exclusive, queue, etc.)
408
+ *
409
+ * An `Op` never runs on its own. It only executes when passed to `Op.interpret`, which
410
+ * attaches a concurrency strategy and returns a `Manager` that owns the execution.
411
+ *
412
+ * @example
413
+ * ```ts
414
+ * const fetchUser = Op.create(
415
+ * (id: string, signal) => fetch(`/users/${id}`, { signal }).then(r => r.json()),
416
+ * (e) => new ApiError(e),
417
+ * );
418
+ *
419
+ * const manager = Op.interpret(fetchUser, { strategy: "restartable" });
420
+ * manager.subscribe(state => {
421
+ * if (state.kind === "Pending") showSpinner();
422
+ * if (state.kind === "Ok") render(state.value);
423
+ * if (state.kind === "Err") showError(state.error);
424
+ * if (state.kind === "Nil") resetUI();
425
+ * });
426
+ * manager.run(userId);
427
+ * ```
428
+ */
429
+ type Op<I, E, A> = {
430
+ /**
431
+ * @internal — Used by `Op.interpret`. Do not call directly.
432
+ * Returns `null` when the operation was aborted (signal fired before factory resolved).
433
+ */
434
+ readonly _factory: (input: I, signal: AbortSignal) => Deferred<Result<E, A> | null>;
435
+ };
436
+ declare namespace Op {
437
+ /**
438
+ * The three terminal states of a completed async operation.
439
+ *
440
+ * - `Ok` — produced a value.
441
+ * - `Err` — produced a typed error.
442
+ * - `Nil` — completed without a value or error. The `reason` field says why:
443
+ * `"aborted"` — `abort()` was called; `"dropped"` — a new `run()` was ignored
444
+ * because the strategy was already busy; `"replaced"` — a newer `run()` took
445
+ * over a call that was already running; `"evicted"` — a newer `run()` took over
446
+ * a call that was waiting and had not yet started.
447
+ */
448
+ type Outcome<E, A> = Ok<A> | Err<E> | Nil;
449
+ /** A successful outcome with a value. */
450
+ type Ok<A> = WithKind<"Ok"> & WithValue<A>;
451
+ /** A failed outcome with a typed error. */
452
+ type Err<E> = WithKind<"Err"> & WithError<E>;
453
+ /**
454
+ * An outcome that produced nothing. `reason` identifies why:
455
+ * - `"aborted"` — `abort()` was called explicitly.
456
+ * - `"dropped"` — the invocation was ignored because the strategy was busy.
457
+ * - `"replaced"` — a newer invocation took over a call that was already running.
458
+ * - `"evicted"` — a newer invocation took a slot from a call that was waiting and
459
+ * had not yet started (buffered slot, debounce timer, throttle trailing slot).
460
+ */
461
+ type Nil = WithKind<"Nil"> & {
462
+ readonly reason: NilReason;
463
+ };
464
+ /** The reason a `Nil` outcome was produced. */
465
+ type NilReason = "aborted" | "dropped" | "replaced" | "evicted";
466
+ /** A `Nil` produced by an explicit `abort()` call. */
467
+ type AbortedNil = Nil & {
468
+ readonly reason: "aborted";
469
+ };
470
+ /** A `Nil` produced when an invocation was silently ignored (strategy was busy). */
471
+ type DroppedNil = Nil & {
472
+ readonly reason: "dropped";
473
+ };
474
+ /** A `Nil` produced when a newer invocation took over a call that was already running. */
475
+ type ReplacedNil = Nil & {
476
+ readonly reason: "replaced";
477
+ };
478
+ /** A `Nil` produced when a newer invocation took a slot from a call that was waiting and had not yet started. */
479
+ type EvictedNil = Nil & {
480
+ readonly reason: "evicted";
481
+ };
482
+ /** The full set of states a manager can emit, including transient states. */
483
+ type State<E, A> = Idle | Pending | Queued | Retrying<E> | Outcome<E, A>;
484
+ /** The manager has not been run yet (initial state). */
485
+ type Idle = WithKind<"Idle">;
486
+ /** An operation is in-flight. */
487
+ type Pending = WithKind<"Pending">;
488
+ /** An operation is waiting in a queue. `position` is 0-indexed (0 = next to run). */
489
+ type Queued = WithKind<"Queued"> & {
490
+ readonly position: number;
491
+ };
492
+ /** A retry attempt is about to start. */
493
+ type Retrying<E> = WithKind<"Retrying"> & {
494
+ readonly attempt: number;
495
+ readonly lastError: E;
496
+ readonly nextRetryIn?: number;
497
+ };
498
+ /**
499
+ * A stateful execution manager. `run()` both emits state transitions through
500
+ * subscribers and returns a `Deferred` tied to that specific invocation.
501
+ * `S` is narrowed to only the states reachable for this manager's strategy
502
+ * and configuration.
503
+ *
504
+ * @example
505
+ * ```ts
506
+ * const manager = Op.interpret(saveConfig, { strategy: "exclusive" });
507
+ *
508
+ * manager.subscribe(state => {
509
+ * if (state.kind === "Pending") lockForm();
510
+ * if (state.kind === "Ok") toast("Saved");
511
+ * if (state.kind === "Err") toast(`Error: ${state.error.message}`);
512
+ * });
513
+ *
514
+ * // Fire and subscribe (subscriber pattern)
515
+ * manager.run(formData);
516
+ *
517
+ * // Or await the specific invocation's outcome
518
+ * const result = await manager.run(formData);
519
+ * if (Op.isNil(result)) return; // dropped — another save was in-flight
520
+ * ```
521
+ */
522
+ type Manager<I, E, A, S extends State<E, A>> = {
523
+ /** The current state. Useful for synchronous reads (e.g., `useSyncExternalStore`). */
524
+ readonly state: S;
525
+ /**
526
+ * Submits an invocation. Emits state transitions via subscribers and returns a
527
+ * `Deferred` that resolves to the terminal outcome for this specific invocation.
528
+ * `Nil` means this invocation was not executed (dropped, replaced, or aborted).
529
+ */
530
+ run: (input: I) => Deferred<Exclude<S, Idle | Pending | Queued | Retrying<E>>>;
531
+ /**
532
+ * Cancels any in-flight operation and clears the queue.
533
+ * Every pending `run()` Deferred — including queued invocations — settles to `AbortedNil`.
534
+ * Resolution is asynchronous; no Deferred hangs indefinitely.
535
+ */
536
+ abort: () => void;
537
+ /**
538
+ * Registers a subscriber for state transitions. Returns an unsubscribe function.
539
+ * The callback fires immediately with the current state if the manager is not idle.
540
+ */
541
+ subscribe: (cb: (state: S) => void) => () => void;
542
+ };
543
+ /**
544
+ * A stateful manager that maintains independent per-key execution slots.
545
+ * Different keys run in parallel; the same key follows the `perKey` sub-strategy.
546
+ * `abort(key)` cancels a specific key; `abort()` cancels all.
547
+ * `state` is a map of each key's last known state — updated on every transition.
548
+ *
549
+ * @example
550
+ * ```ts
551
+ * const getUser = Op.interpret(fetchUser, {
552
+ * strategy: "keyed",
553
+ * key: (input) => input.id,
554
+ * perKey: "exclusive",
555
+ * });
556
+ *
557
+ * getUser.subscribe((map) => {
558
+ * for (const [id, state] of map) {
559
+ * if (state.kind === "Pending") showSpinner(id);
560
+ * if (state.kind === "Ok") render(id, state.value);
561
+ * }
562
+ * });
563
+ *
564
+ * getUser.run({ id: "user-1" }); // starts key "user-1"
565
+ * getUser.run({ id: "user-2" }); // starts key "user-2" in parallel
566
+ * ```
567
+ */
568
+ type KeyedManager<I, K, E, PerKeyS> = {
569
+ /** Current state map. Keys are present from first `run()` through their last terminal state. */
570
+ readonly state: ReadonlyMap<K, PerKeyS>;
571
+ /**
572
+ * Submits an invocation for the key derived from the input. Returns a `Deferred` tied
573
+ * to this specific invocation. Same-key behaviour is controlled by `perKey`.
574
+ */
575
+ run: (input: I) => Deferred<Exclude<PerKeyS, Pending | Retrying<E>>>;
576
+ /** Cancels the in-flight operation for a specific key, or all keys if omitted. */
577
+ abort: (key?: K) => void;
578
+ /**
579
+ * Registers a subscriber. The callback receives a fresh snapshot of the state map
580
+ * on every transition. Returns an unsubscribe function.
581
+ * Fires immediately with the current map if any key is active.
582
+ */
583
+ subscribe: (cb: (state: ReadonlyMap<K, PerKeyS>) => void) => () => void;
584
+ };
585
+ /** States reachable by a `once` manager (no retry). */
586
+ type OnceState<E, A> = Idle | Pending | Ok<A> | Err<E> | AbortedNil | DroppedNil;
587
+ /** States reachable by a `once` manager with retry configured. */
588
+ type RetryableOnceState<E, A> = Idle | Pending | Retrying<E> | Ok<A> | Err<E> | AbortedNil | DroppedNil;
589
+ /** States reachable by a `restartable` manager (no retry). */
590
+ type RestartableState<E, A> = Idle | Pending | Ok<A> | Err<E> | AbortedNil | ReplacedNil;
591
+ /** States reachable by a `restartable` manager with retry configured. */
592
+ type RetryableRestartableState<E, A> = Idle | Pending | Retrying<E> | Ok<A> | Err<E> | AbortedNil | ReplacedNil;
593
+ /** States reachable by an `exclusive` manager (no retry). */
594
+ type ExclusiveState<E, A> = Idle | Pending | Ok<A> | Err<E> | AbortedNil | DroppedNil;
595
+ /** States reachable by an `exclusive` manager with retry configured. */
596
+ type RetryableExclusiveState<E, A> = Idle | Pending | Retrying<E> | Ok<A> | Err<E> | AbortedNil | DroppedNil;
597
+ /** States reachable by a `queue` manager (no retry, no overflow, no dedupe). */
598
+ type QueueState<E, A> = Idle | Pending | Queued | Ok<A> | Err<E> | AbortedNil;
599
+ /** States reachable by a `queue` manager with retry (no overflow, no dedupe). */
600
+ type RetryableQueueState<E, A> = Idle | Pending | Queued | Retrying<E> | Ok<A> | Err<E> | AbortedNil;
601
+ /** States reachable by a `queue` manager with `overflow:"drop"` or `dedupe` (no retry). */
602
+ type QueueDropState<E, A> = Idle | Pending | Queued | Ok<A> | Err<E> | AbortedNil | DroppedNil;
603
+ /** States reachable by a `queue` manager with `overflow:"drop"` or `dedupe`, with retry. */
604
+ type RetryableQueueDropState<E, A> = Idle | Pending | Queued | Retrying<E> | Ok<A> | Err<E> | AbortedNil | DroppedNil;
605
+ /** States reachable by a `queue` manager with `overflow:"replace-last"` and no `dedupe` (no retry). */
606
+ type QueueReplaceState<E, A> = Idle | Pending | Queued | Ok<A> | Err<E> | AbortedNil | EvictedNil;
607
+ /** States reachable by a `queue` manager with `overflow:"replace-last"` and no `dedupe`, with retry. */
608
+ type RetryableQueueReplaceState<E, A> = Idle | Pending | Queued | Retrying<E> | Ok<A> | Err<E> | AbortedNil | EvictedNil;
609
+ /** States reachable by a `queue` manager with `overflow:"replace-last"` AND `dedupe` (no retry). */
610
+ type QueueDropAndReplaceState<E, A> = Idle | Pending | Queued | Ok<A> | Err<E> | AbortedNil | DroppedNil | EvictedNil;
611
+ /** States reachable by a `queue` manager with `overflow:"replace-last"` AND `dedupe`, with retry. */
612
+ type RetryableQueueDropAndReplaceState<E, A> = Idle | Pending | Queued | Retrying<E> | Ok<A> | Err<E> | AbortedNil | DroppedNil | EvictedNil;
613
+ /** States reachable by a `buffered` manager (no retry). */
614
+ type BufferedState<E, A> = Idle | Pending | Queued | Ok<A> | Err<E> | AbortedNil | EvictedNil;
615
+ /** States reachable by a `buffered` manager with retry configured. */
616
+ type RetryableBufferedState<E, A> = Idle | Pending | Queued | Retrying<E> | Ok<A> | Err<E> | AbortedNil | EvictedNil;
617
+ /** States reachable by a `debounced` manager (no retry). */
618
+ type DebouncedState<E, A> = Idle | Pending | Ok<A> | Err<E> | AbortedNil | EvictedNil;
619
+ /** States reachable by a `debounced` manager with retry configured. */
620
+ type RetryableDebouncedState<E, A> = Idle | Pending | Retrying<E> | Ok<A> | Err<E> | AbortedNil | EvictedNil;
621
+ /** States reachable by a `throttled` manager (leading-only, no retry). */
622
+ type ThrottledState<E, A> = Idle | Pending | Ok<A> | Err<E> | AbortedNil | DroppedNil;
623
+ /** States reachable by a `throttled` manager (leading-only, with retry). */
624
+ type RetryableThrottledState<E, A> = Idle | Pending | Retrying<E> | Ok<A> | Err<E> | AbortedNil | DroppedNil;
625
+ /** States reachable by a `throttled` manager with `trailing: true` (no retry). */
626
+ type ThrottledTrailingState<E, A> = Idle | Pending | Ok<A> | Err<E> | AbortedNil | EvictedNil;
627
+ /** States reachable by a `throttled` manager with `trailing: true` and retry. */
628
+ type RetryableThrottledTrailingState<E, A> = Idle | Pending | Retrying<E> | Ok<A> | Err<E> | AbortedNil | EvictedNil;
629
+ /** States reachable by a `concurrent` manager with `overflow: "queue"` (no retry). */
630
+ type ConcurrentQueueState<E, A> = Idle | Pending | Queued | Ok<A> | Err<E> | AbortedNil;
631
+ /** States reachable by a `concurrent` manager with `overflow: "queue"` and retry. */
632
+ type RetryableConcurrentQueueState<E, A> = Idle | Pending | Queued | Retrying<E> | Ok<A> | Err<E> | AbortedNil;
633
+ /** States reachable by a `concurrent` manager with `overflow: "drop"` (no retry). */
634
+ type ConcurrentDropState<E, A> = Idle | Pending | Ok<A> | Err<E> | AbortedNil | DroppedNil;
635
+ /** States reachable by a `concurrent` manager with `overflow: "drop"` and retry. */
636
+ type RetryableConcurrentDropState<E, A> = Idle | Pending | Retrying<E> | Ok<A> | Err<E> | AbortedNil | DroppedNil;
637
+ /** Per-key state union for a `keyed` manager with `perKey: "exclusive"`. */
638
+ type KeyedExclusivePerKey<E, A> = Pending | Ok<A> | Err<E> | AbortedNil | DroppedNil;
639
+ /** Per-key state union for a `keyed` manager with `perKey: "restartable"`. */
640
+ type KeyedRestartablePerKey<E, A> = Pending | Ok<A> | Err<E> | AbortedNil | ReplacedNil;
641
+ type RetryOptions<E> = RetryOptions<E>;
642
+ type TimeoutOptions<E> = TimeoutOptions<E>;
643
+ /**
644
+ * Creates a `Nil` outcome with a reason.
645
+ *
646
+ * @example
647
+ * ```ts
648
+ * Op.nil("aborted"); // { kind: "Nil", reason: "aborted" }
649
+ * Op.nil("dropped"); // { kind: "Nil", reason: "dropped" }
650
+ * Op.nil("replaced"); // { kind: "Nil", reason: "replaced" }
651
+ * Op.nil("evicted"); // { kind: "Nil", reason: "evicted" }
652
+ * ```
653
+ */
654
+ const nil: (reason: NilReason) => Nil;
655
+ /**
656
+ * Creates an `Op` from an async factory and an error mapper.
657
+ *
658
+ * The factory receives the input and an `AbortSignal`. Operations that support
659
+ * cancellation (like `fetch`) should thread the signal through. The error mapper
660
+ * converts any thrown value into a typed error; it is never called for aborts.
661
+ *
662
+ * @example
663
+ * ```ts
664
+ * const saveProfile = Op.create(
665
+ * (data: ProfileData, signal) =>
666
+ * fetch("/profile", { method: "POST", body: JSON.stringify(data), signal })
667
+ * .then(r => r.json()),
668
+ * (e) => new ApiError(e),
669
+ * );
670
+ * ```
671
+ */
672
+ const create: <I, E, A>(factory: (input: I, signal: AbortSignal) => Promise<A>, onError: (e: unknown) => E) => Op<I, E, A>;
673
+ /**
674
+ * Creates a successful Outcome.
675
+ *
676
+ * @example
677
+ * ```ts
678
+ * Op.ok(42); // { kind: "Ok", value: 42 }
679
+ * ```
680
+ */
681
+ const ok: <A>(value: A) => Ok<A>;
682
+ /**
683
+ * Creates a failed Outcome with a typed error.
684
+ *
685
+ * @example
686
+ * ```ts
687
+ * Op.err(new ApiError("not found")); // { kind: "Err", error: ApiError }
688
+ * ```
689
+ */
690
+ const err: <E>(error: E) => Err<E>;
691
+ /**
692
+ * Returns `true` if the Outcome is `Ok`.
693
+ *
694
+ * @example
695
+ * ```ts
696
+ * if (Op.isOk(outcome)) render(outcome.value);
697
+ * ```
698
+ */
699
+ const isOk: <E, A>(outcome: Outcome<E, A>) => outcome is Ok<A>;
700
+ /**
701
+ * Returns `true` if the Outcome is `Err`.
702
+ *
703
+ * @example
704
+ * ```ts
705
+ * if (Op.isErr(outcome)) logger.error(outcome.error);
706
+ * ```
707
+ */
708
+ const isErr: <E, A>(outcome: Outcome<E, A>) => outcome is Err<E>;
709
+ /**
710
+ * Returns `true` if the Outcome is `Nil`.
711
+ *
712
+ * @example
713
+ * ```ts
714
+ * if (Op.isNil(outcome)) resetUI();
715
+ * ```
716
+ */
717
+ const isNil: <E, A>(outcome: Outcome<E, A>) => outcome is Nil;
718
+ /**
719
+ * Pattern matches on an Outcome using named case handlers.
720
+ *
721
+ * @example
722
+ * ```ts
723
+ * Op.match({
724
+ * ok: (user) => render(user),
725
+ * err: (e) => showError(e.message),
726
+ * nil: () => resetUI(),
727
+ * })(outcome);
728
+ * ```
729
+ */
730
+ const match: <E, A, B>(cases: {
731
+ ok: (a: A) => B;
732
+ err: (e: E) => B;
733
+ nil: () => B;
734
+ }) => (outcome: Outcome<E, A>) => B;
735
+ /**
736
+ * Eliminates an Outcome with positional handlers.
737
+ * Order: `onErr`, `onOk`, `onNil` — mirrors `Result.fold` for the first two.
738
+ *
739
+ * @example
740
+ * ```ts
741
+ * Op.fold(
742
+ * (e) => `error: ${e.message}`,
743
+ * (v) => `value: ${v}`,
744
+ * () => "nothing",
745
+ * )(outcome);
746
+ * ```
747
+ */
748
+ const fold: <E, A, B>(onErr: (e: E) => B, onOk: (a: A) => B, onNil: () => B) => (outcome: Outcome<E, A>) => B;
749
+ /**
750
+ * Returns the success value, or the result of `defaultValue()` for `Err` or `Nil`.
751
+ *
752
+ * @example
753
+ * ```ts
754
+ * Op.getOrElse(() => [] as User[])(outcome);
755
+ * ```
756
+ */
757
+ const getOrElse: <E, A, B>(defaultValue: () => B) => (outcome: Outcome<E, A>) => A | B;
758
+ /**
759
+ * Transforms the success value. `Err` and `Nil` pass through unchanged.
760
+ *
761
+ * @example
762
+ * ```ts
763
+ * pipe(outcome, Op.map(user => user.name));
764
+ * ```
765
+ */
766
+ const map: <E, A, B>(f: (a: A) => B) => (outcome: Outcome<E, A>) => Outcome<E, B>;
767
+ /**
768
+ * Transforms the error value. `Ok` and `Nil` pass through unchanged.
769
+ *
770
+ * @example
771
+ * ```ts
772
+ * pipe(outcome, Op.mapError(e => e.message));
773
+ * ```
774
+ */
775
+ const mapError: <E, F, A>(f: (e: E) => F) => (outcome: Outcome<E, A>) => Outcome<F, A>;
776
+ /**
777
+ * Chains Outcome computations. Runs `f` on `Ok`; `Err` and `Nil` pass through.
778
+ *
779
+ * @example
780
+ * ```ts
781
+ * pipe(
782
+ * outcome,
783
+ * Op.chain(user => user.active ? Op.ok(user) : Op.err(new Error("inactive"))),
784
+ * );
785
+ * ```
786
+ */
787
+ const chain: <E, A, B>(f: (a: A) => Outcome<E, B>) => (outcome: Outcome<E, A>) => Outcome<E, B>;
788
+ /**
789
+ * Runs a side effect on the success value without changing the Outcome.
790
+ *
791
+ * @example
792
+ * ```ts
793
+ * pipe(outcome, Op.tap(user => console.log("loaded", user.id)));
794
+ * ```
795
+ */
796
+ const tap: <E, A>(f: (a: A) => void) => (outcome: Outcome<E, A>) => Outcome<E, A>;
797
+ /**
798
+ * Provides a fallback Outcome when the result is `Err`. `Ok` and `Nil` pass through.
799
+ *
800
+ * @example
801
+ * ```ts
802
+ * pipe(
803
+ * outcome,
804
+ * Op.recover(e => e.isRetryable ? Op.ok(cachedValue) : Op.err(e)),
805
+ * );
806
+ * ```
807
+ */
808
+ const recover: <E, A, B>(f: (e: E) => Outcome<E, B>) => (outcome: Outcome<E, A>) => Outcome<E, A | B>;
809
+ /**
810
+ * Converts an Outcome to a `Result`. `Nil` becomes `Err(onNil())`.
811
+ *
812
+ * @example
813
+ * ```ts
814
+ * Op.toResult(() => new ApiError("no result"))(outcome);
815
+ * ```
816
+ */
817
+ const toResult: <E, A>(onNil: () => E) => (outcome: Outcome<E, A>) => Result<E, A>;
818
+ /**
819
+ * Converts an Outcome to a `Maybe`. `Ok` becomes `Some`; `Err` and `Nil` become `None`.
820
+ *
821
+ * @example
822
+ * ```ts
823
+ * Op.toMaybe(outcome); // Maybe<User>
824
+ * ```
825
+ */
826
+ const toMaybe: <E, A>(outcome: Outcome<E, A>) => Maybe<A>;
827
+ /**
828
+ * Resolves when all invocations settle, returning their outcomes in order.
829
+ * An alternative to `Promise.all` that stays within the `Op` type system.
830
+ *
831
+ * @example
832
+ * ```ts
833
+ * const [a, b] = await Op.all([manager.run(inputA), manager.run(inputB)]);
834
+ * ```
835
+ */
836
+ const all: <E, A>(invocations: ReadonlyArray<Deferred<Outcome<E, A>>>) => Deferred<ReadonlyArray<Outcome<E, A>>>;
837
+ /**
838
+ * Resolves to the outcome of whichever invocation settles first.
839
+ * An alternative to `Promise.race` that stays within the `Op` type system.
840
+ *
841
+ * @example
842
+ * ```ts
843
+ * const winner = await Op.race([manager.run(inputA), manager.run(inputB)]);
844
+ * ```
845
+ */
846
+ const race: <E, A>(invocations: ReadonlyArray<Deferred<Outcome<E, A>>>) => Deferred<Outcome<E, A>>;
847
+ /**
848
+ * Attaches a concurrency strategy to an `Op`, returning a `Manager`.
849
+ *
850
+ * Strategy is data, not a method name. The `S` type parameter is narrowed to only
851
+ * the states reachable for the chosen strategy and options — subscribers cannot
852
+ * reference states that cannot occur.
853
+ *
854
+ * **Strategies:**
855
+ * - `once` — fires once. Only the first `run()` executes; subsequent calls
856
+ * return `DroppedNil` immediately. State is permanent after completion.
857
+ * - `restartable` — new call cancels the previous (`ReplacedNil`). Only the latest result matters.
858
+ * - `exclusive` — new calls while in-flight return `DroppedNil` immediately.
859
+ * - `queue` — calls run in submission order. `Queued` state shows position.
860
+ * - `buffered` — 1 in-flight + 1 waiting slot. Newer calls evict the slot (`EvictedNil`).
861
+ * - `debounced` — waits `ms` ms of quiet before starting. Earlier calls get `EvictedNil`.
862
+ *
863
+ * **`retry` and `timeout`** can be combined with any strategy. Both are applied
864
+ * internally per `run()` call — set the policy once, not at every call site.
865
+ * The timeout wraps the entire retry sequence (one deadline for all attempts).
866
+ * When `retry` is present, `Retrying` is added to the subscriber type.
867
+ *
868
+ * @example
869
+ * ```ts
870
+ * // Load once on mount — further calls are no-ops
871
+ * const getUser = Op.interpret(fetchUser, { strategy: "once" });
872
+ * getUser.subscribe(state => {
873
+ * if (state.kind === "Pending") showSpinner();
874
+ * if (state.kind === "Ok") render(state.value);
875
+ * });
876
+ * getUser.run(userId);
877
+ *
878
+ * // Search: cancel the previous query when a new one starts
879
+ * const search = Op.interpret(searchOp, { strategy: "restartable" });
880
+ *
881
+ * // Form submit: ignore double-clicks while in-flight
882
+ * const submit = Op.interpret(submitOp, {
883
+ * strategy: "exclusive",
884
+ * retry: { attempts: 3, backoff: n => n * 500 },
885
+ * timeout: { ms: 10_000, onTimeout: () => new ApiError("timed out") },
886
+ * });
887
+ *
888
+ * // Auto-save: current save commits fully; latest pending edit saves next
889
+ * const save = Op.interpret(saveOp, { strategy: "buffered" });
890
+ * ```
891
+ */
892
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
893
+ strategy: "throttled";
894
+ } & WithMs & WithTrailing & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableThrottledTrailingState<E, A>>;
895
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
896
+ strategy: "throttled";
897
+ } & WithMs & WithTrailing & WithTimeout<E>): Manager<I, E, A, ThrottledTrailingState<E, A>>;
898
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
899
+ strategy: "throttled";
900
+ } & WithMs & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableThrottledState<E, A>>;
901
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
902
+ strategy: "throttled";
903
+ } & WithMs & WithTimeout<E>): Manager<I, E, A, ThrottledState<E, A>>;
904
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
905
+ strategy: "debounced";
906
+ } & WithMs & WithLeading & WithMaxWait & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableDebouncedState<E, A>>;
907
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
908
+ strategy: "debounced";
909
+ } & WithMs & WithLeading & WithMaxWait & WithTimeout<E>): Manager<I, E, A, DebouncedState<E, A>>;
910
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
911
+ strategy: "debounced";
912
+ } & WithMs & WithLeading & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableDebouncedState<E, A>>;
913
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
914
+ strategy: "debounced";
915
+ } & WithMs & WithLeading & WithTimeout<E>): Manager<I, E, A, DebouncedState<E, A>>;
916
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
917
+ strategy: "debounced";
918
+ } & WithMs & WithMaxWait & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableDebouncedState<E, A>>;
919
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
920
+ strategy: "debounced";
921
+ } & WithMs & WithMaxWait & WithTimeout<E>): Manager<I, E, A, DebouncedState<E, A>>;
922
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
923
+ strategy: "debounced";
924
+ } & WithMs & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableDebouncedState<E, A>>;
925
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
926
+ strategy: "debounced";
927
+ } & WithMs & WithTimeout<E>): Manager<I, E, A, DebouncedState<E, A>>;
928
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
929
+ strategy: "concurrent";
930
+ } & WithN & WithOverflow<"queue"> & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableConcurrentQueueState<E, A>>;
931
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
932
+ strategy: "concurrent";
933
+ } & WithN & WithOverflow<"queue"> & WithTimeout<E>): Manager<I, E, A, ConcurrentQueueState<E, A>>;
934
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
935
+ strategy: "concurrent";
936
+ } & WithN & WithOverflow<"drop"> & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableConcurrentDropState<E, A>>;
937
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
938
+ strategy: "concurrent";
939
+ } & WithN & WithOverflow<"drop"> & WithTimeout<E>): Manager<I, E, A, ConcurrentDropState<E, A>>;
940
+ function interpret<I, K, E, A>(op: Op<I, E, A>, options: {
941
+ strategy: "keyed";
942
+ } & WithKey<I, K> & WithPerKey<"exclusive"> & WithTimeout<E>): KeyedManager<I, K, E, KeyedExclusivePerKey<E, A>>;
943
+ function interpret<I, K, E, A>(op: Op<I, E, A>, options: {
944
+ strategy: "keyed";
945
+ } & WithKey<I, K> & WithPerKey<"restartable"> & WithTimeout<E>): KeyedManager<I, K, E, KeyedRestartablePerKey<E, A>>;
946
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
947
+ strategy: "once";
948
+ } & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableOnceState<E, A>>;
949
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
950
+ strategy: "once";
951
+ } & WithTimeout<E>): Manager<I, E, A, OnceState<E, A>>;
952
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
953
+ strategy: "restartable";
954
+ } & WithMinInterval & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableRestartableState<E, A>>;
955
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
956
+ strategy: "restartable";
957
+ } & WithMinInterval & WithTimeout<E>): Manager<I, E, A, RestartableState<E, A>>;
958
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
959
+ strategy: "exclusive";
960
+ } & WithCooldown & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableExclusiveState<E, A>>;
961
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
962
+ strategy: "exclusive";
963
+ } & WithCooldown & WithTimeout<E>): Manager<I, E, A, ExclusiveState<E, A>>;
964
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
965
+ strategy: "queue";
966
+ } & WithMaxSize & WithOverflow<"replace-last"> & WithDedupe<I> & WithRetry<E> & WithConcurrency & WithTimeout<E>): Manager<I, E, A, RetryableQueueDropAndReplaceState<E, A>>;
967
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
968
+ strategy: "queue";
969
+ } & WithMaxSize & WithOverflow<"replace-last"> & WithDedupe<I> & WithConcurrency & WithTimeout<E>): Manager<I, E, A, QueueDropAndReplaceState<E, A>>;
970
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
971
+ strategy: "queue";
972
+ } & WithMaxSize & WithOverflow<"replace-last"> & WithRetry<E> & WithConcurrency & WithTimeout<E>): Manager<I, E, A, RetryableQueueReplaceState<E, A>>;
973
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
974
+ strategy: "queue";
975
+ } & WithMaxSize & WithOverflow<"replace-last"> & WithConcurrency & WithTimeout<E>): Manager<I, E, A, QueueReplaceState<E, A>>;
976
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
977
+ strategy: "queue";
978
+ } & (WithMaxSize | WithDedupe<I>) & WithRetry<E> & WithConcurrency & WithTimeout<E>): Manager<I, E, A, RetryableQueueDropState<E, A>>;
979
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
980
+ strategy: "queue";
981
+ } & (WithMaxSize | WithDedupe<I>) & WithConcurrency & WithTimeout<E>): Manager<I, E, A, QueueDropState<E, A>>;
982
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
983
+ strategy: "queue";
984
+ } & WithRetry<E> & WithConcurrency & WithTimeout<E>): Manager<I, E, A, RetryableQueueState<E, A>>;
985
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
986
+ strategy: "queue";
987
+ } & WithConcurrency & WithTimeout<E>): Manager<I, E, A, QueueState<E, A>>;
988
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
989
+ strategy: "buffered";
990
+ } & WithSize & WithRetry<E> & WithTimeout<E>): Manager<I, E, A, RetryableBufferedState<E, A>>;
991
+ function interpret<I, E, A>(op: Op<I, E, A>, options: {
992
+ strategy: "buffered";
993
+ } & WithSize & WithTimeout<E>): Manager<I, E, A, BufferedState<E, A>>;
994
+ }
995
+
402
996
  /**
403
997
  * A function from `A` to `A is B` — a type predicate paired with a runtime check.
404
998
  *
@@ -1122,108 +1716,6 @@ declare namespace TaskResult {
1122
1716
  * Useful for logging or debugging.
1123
1717
  */
1124
1718
  const tap: <E, A>(f: (a: A) => void) => (data: TaskResult<E, A>) => TaskResult<E, A>;
1125
- /**
1126
- * Re-runs a TaskResult on `Err` with configurable attempts, backoff, and retry condition.
1127
- * An `AbortSignal` passed at the call site is forwarded to each attempt; the loop also
1128
- * checks the signal before starting a new attempt so cancellation stops the loop promptly.
1129
- *
1130
- * @param options.attempts - Total number of attempts (1 = no retry, 3 = up to 3 tries)
1131
- * @param options.backoff - Fixed delay in ms, or a function `(attempt) => ms` for computed delay
1132
- * @param options.when - Only retry when this returns true; defaults to always retry on Err
1133
- *
1134
- * @example
1135
- * ```ts
1136
- * // Retry up to 3 times with exponential backoff
1137
- * pipe(
1138
- * fetchUser,
1139
- * TaskResult.retry({ attempts: 3, backoff: n => n * 1000 })
1140
- * );
1141
- *
1142
- * // Only retry on network errors, not auth errors
1143
- * pipe(
1144
- * fetchUser,
1145
- * TaskResult.retry({ attempts: 3, when: e => e instanceof NetworkError })
1146
- * );
1147
- * ```
1148
- */
1149
- const retry: <E>(options: {
1150
- attempts: number;
1151
- backoff?: number | ((attempt: number) => number);
1152
- when?: (error: E) => boolean;
1153
- }) => <A>(data: TaskResult<E, A>) => TaskResult<E, A>;
1154
- /**
1155
- * Polls a TaskResult repeatedly until the success value satisfies a predicate.
1156
- * Stops immediately and returns `Err` if the task fails.
1157
- * An `AbortSignal` passed at the call site is forwarded to each attempt; the loop
1158
- * also checks the signal before starting a new poll so cancellation stops promptly.
1159
- *
1160
- * `delay` accepts a fixed number of milliseconds or a function `(attempt) => ms`
1161
- * for a computed delay — useful for starting fast and slowing down over time.
1162
- *
1163
- * @example
1164
- * ```ts
1165
- * const checkJob = (id: string): TaskResult<string, { status: "pending" | "done" }> =>
1166
- * TaskResult.tryCatch(
1167
- * (signal) => fetch(`/jobs/${id}`, { signal }).then(r => r.json()),
1168
- * String
1169
- * );
1170
- *
1171
- * // Fixed delay: poll every 2s
1172
- * pipe(
1173
- * checkJob(jobId),
1174
- * TaskResult.pollUntil({ when: job => job.status === "done", delay: 2000 }),
1175
- * );
1176
- *
1177
- * // Computed delay: 1s, 2s, 3s, ...
1178
- * pipe(
1179
- * checkJob(jobId),
1180
- * TaskResult.pollUntil({ when: job => job.status === "done", delay: n => n * 1000 }),
1181
- * );
1182
- * ```
1183
- */
1184
- const pollUntil: <A>(options: {
1185
- when: (a: A) => boolean;
1186
- delay?: number | ((attempt: number) => number);
1187
- }) => <E>(task: TaskResult<E, A>) => TaskResult<E, A>;
1188
- /**
1189
- * Fails a TaskResult with a typed error if it does not resolve within the given time.
1190
- * The inner task receives an `AbortSignal` that fires when the deadline passes —
1191
- * operations like `fetch` that accept a signal are cancelled rather than left dangling.
1192
- *
1193
- * @example
1194
- * ```ts
1195
- * pipe(
1196
- * fetchUser,
1197
- * TaskResult.timeout(5000, () => new TimeoutError("fetch user timed out"))
1198
- * );
1199
- * ```
1200
- */
1201
- const timeout: <E>(ms: number, onTimeout: () => E) => <A>(data: TaskResult<E, A>) => TaskResult<E, A>;
1202
- /**
1203
- * Creates a TaskResult paired with an `abort` handle. When `abort()` is called the
1204
- * `AbortSignal` passed to the factory is fired, cancelling any in-flight operation.
1205
- * The abort error is transformed by `onError` into a typed `Err`.
1206
- *
1207
- * If an outer signal is also present (passed at the call site), aborting it
1208
- * propagates into the internal controller.
1209
- *
1210
- * @example
1211
- * ```ts
1212
- * const { task: req, abort } = TaskResult.abortable(
1213
- * (signal) => fetch(`/users/${id}`, { signal }).then(r => r.json()),
1214
- * String,
1215
- * );
1216
- *
1217
- * const result = pipe(req, TaskResult.retry({ attempts: 3 }));
1218
- *
1219
- * onCancel(abort);
1220
- * await result();
1221
- * ```
1222
- */
1223
- const abortable: <E, A>(factory: (signal: AbortSignal) => Promise<A>, onError: (e: unknown) => E) => {
1224
- task: TaskResult<E, A>;
1225
- abort: () => void;
1226
- };
1227
1719
  }
1228
1720
 
1229
1721
  /**
@@ -2326,4 +2818,4 @@ declare namespace Tuple {
2326
2818
  const tap: <A, B>(f: (a: A, b: B) => void) => (tuple: Tuple<A, B>) => Tuple<A, B>;
2327
2819
  }
2328
2820
 
2329
- export { type Failure, type Invalid, Lens, type Loading, Logged, Maybe, type NotAsked, Optional, Predicate, Reader, Refinement, RemoteData, Resource, Result, State, type Success, Task, TaskMaybe, TaskResult, TaskValidation, These, type TheseBoth, type TheseFirst, type TheseSecond, Tuple, type Valid, Validation };
2821
+ export { Deferred, type Failure, type Invalid, Lens, type Loading, Logged, Maybe, type NotAsked, Op, Optional, Predicate, Reader, Refinement, RemoteData, Resource, Result, State, type Success, Task, TaskMaybe, TaskResult, TaskValidation, These, type TheseBoth, type TheseFirst, type TheseSecond, Tuple, type Valid, Validation };