Skip to content

Typed errors

execute() throws, and a catch (e) gives you unknown. The builder already knows which errors its strategies can raise: a timeout raises TIMEOUT, a circuit breaker CIRCUIT_OPEN and CIRCUIT_ISOLATED. settle() returns them as a typed outcome instead of throwing, and Outcome.match() makes you handle each one.

import {
class ResiliencePipelineBuilder<T = any, E = never, C extends string = never>

Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), … Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.

The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().

ResiliencePipelineBuilder
,
type Outcome<T, E = unknown> = {
readonly ok: true;
readonly result: T;
} | {
readonly ok: false;
readonly error: E;
}
const Outcome: {
fromResult: <T>(result: T) => Outcome<T, never>;
fromError: <T = never, E = unknown>(error: E) => Outcome<T, E>;
is: (value: unknown) => value is Outcome<unknown, unknown>;
from: (value: unknown) => Outcome<unknown, unknown> | undefined;
try: {
<R>(fn: () => R): TryResult<R, unknown>;
<R, E>(options: {
try: () => R;
catch: (error: unknown) => E;
}): TryResult<R, E>;
};
all<const O extends readonly Outcome<any, any>[]>(outcomes: O): Outcome<{ -readonly [K in keyof O]: OutcomeValue<O[K]>; }, OutcomeError<O[number]>>;
partition<O extends Outcome<any, any>>(outcomes: readonly O[]): {
values: OutcomeValue<O>[];
errors: OutcomeError<O>[];
};
unwrap: <T>(outcome: Outcome<T, unknown>) => T;
unwrapErr<E, C extends ErrorCodes<E> = ErrorCodes<...>>(outcome: Outcome<unknown, E>, code?: C | undefined): ErrorWithCode<E, C>;
match<T, E, H extends OutcomeHandlers<T, E, unknown>>(outcome: Outcome<T, E>, handlers: H): ReturnType<H[keyof H] & ((...args: any) => any)>;
}

The result of an execution: either a value or an error. Strategies pass outcomes around instead of throwing. E is the error type when it's known (see pipeline.settle()), unknown otherwise.

Outcome
} from 'resilience-pipeline';
class
class NotFound
NotFound
extends
var Error: ErrorConstructor
Error
{
readonly
NotFound.code: "NOT_FOUND"
code
= 'NOT_FOUND' as
type const = "NOT_FOUND"
const
;
}
const
const pipeline: ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BrokenCircuitError>>
pipeline
= new
new ResiliencePipelineBuilder<any, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<any, never, never>

Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), … Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.

The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().

ResiliencePipelineBuilder
().
ResiliencePipelineBuilder<any, never, never>.addTimeout(options: Duration | TimeoutOptions): Next<any, never, never, TimeoutRejectedError>

addTimeout(5000), addTimeout('5s') or addTimeout({ timeout: '5s', onTimeout }).

addTimeout
('2s').
ResiliencePipelineBuilder<any, TimeoutRejectedError, never>.addCircuitBreaker(options?: CircuitBreakerOptions<any>): Next<any, TimeoutRejectedError, never, BrokenCircuitError>
addCircuitBreaker
().
ResiliencePipelineBuilder<any, WithErrors<TimeoutRejectedError, never, BrokenCircuitError>, never>.build(): ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BrokenCircuitError>>
build
();
const outcome = await
const pipeline: ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BrokenCircuitError>>
pipeline
.
ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BrokenCircuitError>>.settle<{
id: string;
name: string;
}, NotFound>(callback: (context: ResilienceContext) => MaybePromise<{
id: string;
name: string;
} | Outcome<{
id: string;
name: string;
}, NotFound>>, options?: ExecuteOptions): Promise<Outcome<{
id: string;
name: string;
}, NotFound | WithErrors<TimeoutRejectedError, never, BrokenCircuitError> | UnexpectedError>>

Like executeOutcome, but the error is typed: the errors this pipeline's strategies can raise (E), the errors your callback returns with Outcome.fromError(…) (E2), or UnexpectedError wrapping anything else (a thrown bug, a client error). Pair it with Outcome.match() for exhaustive handling:

const outcome = await pipeline.settle(async () => (user ? user : Outcome.fromError(new NotFound(id))));
Outcome.match(outcome, { ok: (u) => u, NOT_FOUND: () => null, TIMEOUT: () => cached, CIRCUIT_OPEN: () => cached, UNEXPECTED: (e) => { throw e } });

Returned errors count as failures for the strategies (they can be retried) unless your predicates say otherwise.

settle
(async ({
signal: AbortSignal

Aborted when the caller cancels, or when a strategy (timeout, hedging) gives up on this execution.

signal
}) =>
const outcome: Outcome<{
id: string;
name: string;
}, NotFound | WithErrors<TimeoutRejectedError, never, BrokenCircuitError> | UnexpectedError>
(await
const db: {
findUser(id: string, signal: AbortSignal): Promise<{
id: string;
name: string;
} | null>;
}
db
.
function findUser(id: string, signal: AbortSignal): Promise<{
id: string;
name: string;
} | null>
findUser
('42',
signal: AbortSignal

Aborted when the caller cancels, or when a strategy (timeout, hedging) gives up on this execution.

signal
)) ??
const Outcome: {
fromResult: <T>(result: T) => Outcome<T, never>;
fromError: <T = never, E = unknown>(error: E) => Outcome<T, E>;
is: (value: unknown) => value is Outcome<unknown, unknown>;
from: (value: unknown) => Outcome<unknown, unknown> | undefined;
try: {
<R>(fn: () => R): TryResult<R, unknown>;
<R, E>(options: {
try: () => R;
catch: (error: unknown) => E;
}): TryResult<R, E>;
};
all<const O extends readonly Outcome<any, any>[]>(outcomes: O): Outcome<{ -readonly [K in keyof O]: OutcomeValue<O[K]>; }, OutcomeError<O[number]>>;
partition<O extends Outcome<any, any>>(outcomes: readonly O[]): {
values: OutcomeValue<O>[];
errors: OutcomeError<O>[];
};
unwrap: <T>(outcome: Outcome<T, unknown>) => T;
unwrapErr<E, C extends ErrorCodes<E> = ErrorCodes<...>>(outcome: Outcome<unknown, E>, code?: C | undefined): ErrorWithCode<E, C>;
match<T, E, H extends OutcomeHandlers<T, E, unknown>>(outcome: Outcome<T, E>, handlers: H): ReturnType<H[keyof H] & ((...args: any) => any)>;
}

The result of an execution: either a value or an error. Strategies pass outcomes around instead of throwing. E is the error type when it's known (see pipeline.settle()), unknown otherwise.

Outcome
.
fromError: <never, NotFound>(error: NotFound) => Outcome<never, NotFound>
fromError
(new
constructor NotFound(message?: string, options?: ErrorOptions): NotFound (+1 overload)
NotFound
('42')),
);
const
const user: any
user
=
const Outcome: {
fromResult: <T>(result: T) => Outcome<T, never>;
fromError: <T = never, E = unknown>(error: E) => Outcome<T, E>;
is: (value: unknown) => value is Outcome<unknown, unknown>;
from: (value: unknown) => Outcome<unknown, unknown> | undefined;
try: {
<R>(fn: () => R): TryResult<R, unknown>;
<R, E>(options: {
try: () => R;
catch: (error: unknown) => E;
}): TryResult<R, E>;
};
all<const O extends readonly Outcome<any, any>[]>(outcomes: O): Outcome<{ -readonly [K in keyof O]: OutcomeValue<O[K]>; }, OutcomeError<O[number]>>;
partition<O extends Outcome<any, any>>(outcomes: readonly O[]): {
values: OutcomeValue<O>[];
errors: OutcomeError<O>[];
};
unwrap: <T>(outcome: Outcome<T, unknown>) => T;
unwrapErr<E, C extends ErrorCodes<E> = ErrorCodes<...>>(outcome: Outcome<unknown, E>, code?: C | undefined): ErrorWithCode<E, C>;
match<T, E, H extends OutcomeHandlers<T, E, unknown>>(outcome: Outcome<T, E>, handlers: H): ReturnType<H[keyof H] & ((...args: any) => any)>;
}

The result of an execution: either a value or an error. Strategies pass outcomes around instead of throwing. E is the error type when it's known (see pipeline.settle()), unknown otherwise.

Outcome
.
match<{
id: string;
name: string;
}, NotFound | WithErrors<TimeoutRejectedError, never, BrokenCircuitError> | UnexpectedError, {
ok: (u: {
id: string;
name: string;
}) => {
id: string;
name: string;
};
NOT_FOUND: () => null;
TIMEOUT: () => {
id: string;
name: string;
} | null;
CIRCUIT_OPEN: () => {
id: string;
name: string;
} | null;
CIRCUIT_ISOLATED: () => {
id: string;
name: string;
} | null;
UNEXPECTED: (e: UnexpectedError) => never;
}>(outcome: Outcome<{
id: string;
name: string;
}, NotFound | WithErrors<...> | UnexpectedError>, handlers: {
ok: (u: {
id: string;
name: string;
}) => {
id: string;
name: string;
};
NOT_FOUND: () => null;
TIMEOUT: () => {
id: string;
name: string;
} | null;
CIRCUIT_OPEN: () => {
id: string;
name: string;
} | null;
CIRCUIT_ISOLATED: () => {
id: string;
name: string;
} | null;
UNEXPECTED: (e: UnexpectedError) => never;
}): any

Exhaustive handling by error code: Outcome.match(await pipeline.settle(cb), { ok: (v) => …, TIMEOUT: (e) => …, CIRCUIT_OPEN: (e) => …, UNEXPECTED: (e) => … }). Add a breaker and forget its handler: compile error.

match
(
const outcome: Outcome<{
id: string;
name: string;
}, NotFound | WithErrors<TimeoutRejectedError, never, BrokenCircuitError> | UnexpectedError>
outcome
, {
ok: (u: {
id: string;
name: string;
}) => {
id: string;
name: string;
}
ok
: (
u: {
id: string;
name: string;
}
u
) =>
u: {
id: string;
name: string;
}
u
,
type NOT_FOUND: () => null
NOT_FOUND
: () => null,
type TIMEOUT: () => {
id: string;
name: string;
} | null
TIMEOUT
: () =>
function cachedUser(id: string): {
id: string;
name: string;
} | null
cachedUser
('42'),
type CIRCUIT_OPEN: () => {
id: string;
name: string;
} | null
CIRCUIT_OPEN
: () =>
function cachedUser(id: string): {
id: string;
name: string;
} | null
cachedUser
('42'),
type CIRCUIT_ISOLATED: () => {
id: string;
name: string;
} | null
CIRCUIT_ISOLATED
: () =>
function cachedUser(id: string): {
id: string;
name: string;
} | null
cachedUser
('42'),
type UNEXPECTED: (e: UnexpectedError) => never
UNEXPECTED
: (
e: UnexpectedError
e
) => {
throw
e: UnexpectedError
e
.
Error.cause?: unknown
cause
;
},
});

The error type is the union of three sources:

Source Example Code
Errors the strategies can raise TimeoutRejectedError, BrokenCircuitError TIMEOUT, CIRCUIT_OPEN…
Errors the callback returns with Outcome.fromError(…) NotFound your code
Anything else, wrapped a thrown bug, a 4xx, a driver error UNEXPECTED (UnexpectedError, original in .cause)

Outcome.match needs a handler for ok and for every code in the union. Forget one and the call doesn’t compile. At runtime it dispatches on error.code.

  • Returned errors are still failures. Strategies see them like thrown errors, so they can be retried or counted by the breaker (subject to shouldHandle).
  • Opt-in. execute() and executeOutcome() don’t change.
  • Give your own errors a literal code (readonly code = 'NOT_FOUND' as const); that is what match keys on.

A strategy can take errors out of the type. Only errors of strategies added after it are affected, because those are the ones it wraps.

// 1. A fallback without shouldHandle catches everything inside: no TIMEOUT, no CIRCUIT_*.
const
const a: ResiliencePipeline<string, never>
a
= new
new ResiliencePipelineBuilder<string, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<string, never, never>

Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), … Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.

The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().

ResiliencePipelineBuilder
<string>()
.
ResiliencePipelineBuilder<string, never, never>.addFallback(options: FallbackOptions<string> & {
shouldHandle?: undefined;
}): ResiliencePipelineBuilder<string, never, string> (+1 overload)

Without shouldHandle it catches every failure of the strategies added after it, so their errors leave the error type.

addFallback
({
FallbackOptions<string>.fallbackValue?: string | undefined

A fixed substitute value (cockatiel's fallback(policy, value)).

fallbackValue
: 'cached' })
.
ResiliencePipelineBuilder<string, never, string>.addTimeout(options: Duration | TimeoutOptions): Next<string, never, string, TimeoutRejectedError>

addTimeout(5000), addTimeout('5s') or addTimeout({ timeout: '5s', onTimeout }).

addTimeout
('2s')
.
ResiliencePipelineBuilder<string, never, string>.addCircuitBreaker(options?: CircuitBreakerOptions<string>): Next<string, never, string, BrokenCircuitError>
addCircuitBreaker
()
.
ResiliencePipelineBuilder<string, never, string>.build(): ResiliencePipeline<string, never>
build
();
// 2. addFallbackOn catches the listed codes only. CIRCUIT_OPEN / CIRCUIT_ISOLATED stay.
const
const b: ResiliencePipeline<string, BrokenCircuitError>
b
= new
new ResiliencePipelineBuilder<string, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<string, never, never>

Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), … Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.

The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().

ResiliencePipelineBuilder
<string>()
.
ResiliencePipelineBuilder<string, never, never>.addFallbackOn<"TIMEOUT">(codes: readonly "TIMEOUT"[], fallbackAction: (args: OutcomeArgs<string>) => MaybePromise<string>, options?: Omit<FallbackOptions<string>, "fallbackAction" | "shouldHandle"> | undefined): ResiliencePipelineBuilder<string, never, "TIMEOUT">

Fallback for these error codes only; they leave the settle() error type of the strategies added after it.

addFallbackOn
(['TIMEOUT'], () => 'cached')
.
ResiliencePipelineBuilder<string, never, "TIMEOUT">.addTimeout(options: Duration | TimeoutOptions): Next<string, never, "TIMEOUT", TimeoutRejectedError>

addTimeout(5000), addTimeout('5s') or addTimeout({ timeout: '5s', onTimeout }).

addTimeout
('2s')
.
ResiliencePipelineBuilder<string, never, "TIMEOUT">.addCircuitBreaker(options?: CircuitBreakerOptions<string>): Next<string, never, "TIMEOUT", BrokenCircuitError>
addCircuitBreaker
()
.
ResiliencePipelineBuilder<string, BrokenCircuitError, "TIMEOUT">.build(): ResiliencePipeline<string, BrokenCircuitError>
build
();
// 3. addMapError turns every inner failure into your own error type.
const
const c: ResiliencePipeline<any, PaymentUnavailable>
c
= new
new ResiliencePipelineBuilder<any, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<any, never, never>

Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), … Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.

The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().

ResiliencePipelineBuilder
()
.
ResiliencePipelineBuilder<any, never, never>.addMapError<PaymentUnavailable>(mapper: (error: unknown, context: ResilienceContext) => PaymentUnavailable): ResiliencePipelineBuilder<any, PaymentUnavailable, string>

Turns every failure of the strategies added after it into your own error type (theirs leave the error type).

addMapError
((
e: unknown
e
) => new
constructor PaymentUnavailable(message?: string, options?: ErrorOptions): PaymentUnavailable (+1 overload)
PaymentUnavailable
('payments down', {
ErrorOptions.cause?: unknown
cause
:
e: unknown
e
}))
.
ResiliencePipelineBuilder<any, PaymentUnavailable, string>.addTimeout(options: Duration | TimeoutOptions): Next<any, PaymentUnavailable, string, TimeoutRejectedError>

addTimeout(5000), addTimeout('5s') or addTimeout({ timeout: '5s', onTimeout }).

addTimeout
('2s')
.
ResiliencePipelineBuilder<any, PaymentUnavailable, string>.addCircuitBreaker(options?: CircuitBreakerOptions<any>): Next<any, PaymentUnavailable, string, BrokenCircuitError>
addCircuitBreaker
()
.
ResiliencePipelineBuilder<any, PaymentUnavailable, string>.build(): ResiliencePipeline<any, PaymentUnavailable>
build
();
const
const outcome: Outcome<string, PaymentUnavailable | UnexpectedError>
outcome
= await
const c: ResiliencePipeline<any, PaymentUnavailable>
c
.
ResiliencePipeline<any, PaymentUnavailable>.settle<string, never>(callback: (context: ResilienceContext) => MaybePromise<string | Outcome<string, never>>, options?: ExecuteOptions): Promise<Outcome<string, PaymentUnavailable | UnexpectedError>>

Like executeOutcome, but the error is typed: the errors this pipeline's strategies can raise (E), the errors your callback returns with Outcome.fromError(…) (E2), or UnexpectedError wrapping anything else (a thrown bug, a client error). Pair it with Outcome.match() for exhaustive handling:

const outcome = await pipeline.settle(async () => (user ? user : Outcome.fromError(new NotFound(id))));
Outcome.match(outcome, { ok: (u) => u, NOT_FOUND: () => null, TIMEOUT: () => cached, CIRCUIT_OPEN: () => cached, UNEXPECTED: (e) => { throw e } });

Returned errors count as failures for the strategies (they can be retried) unless your predicates say otherwise.

settle
(() => 'paid');
const Outcome: {
fromResult: <T>(result: T) => Outcome<T, never>;
fromError: <T = never, E = unknown>(error: E) => Outcome<T, E>;
is: (value: unknown) => value is Outcome<unknown, unknown>;
from: (value: unknown) => Outcome<unknown, unknown> | undefined;
try: {
<R>(fn: () => R): TryResult<R, unknown>;
<R, E>(options: {
try: () => R;
catch: (error: unknown) => E;
}): TryResult<R, E>;
};
all<const O extends readonly Outcome<any, any>[]>(outcomes: O): Outcome<{ -readonly [K in keyof O]: OutcomeValue<O[K]>; }, OutcomeError<O[number]>>;
partition<O extends Outcome<any, any>>(outcomes: readonly O[]): {
values: OutcomeValue<O>[];
errors: OutcomeError<O>[];
};
unwrap: <T>(outcome: Outcome<T, unknown>) => T;
unwrapErr<E, C extends ErrorCodes<E> = ErrorCodes<...>>(outcome: Outcome<unknown, E>, code?: C | undefined): ErrorWithCode<E, C>;
match<T, E, H extends OutcomeHandlers<T, E, unknown>>(outcome: Outcome<T, E>, handlers: H): ReturnType<H[keyof H] & ((...args: any) => any)>;
}

The result of an execution: either a value or an error. Strategies pass outcomes around instead of throwing. E is the error type when it's known (see pipeline.settle()), unknown otherwise.

Outcome
.
match<string, PaymentUnavailable | UnexpectedError, {
ok: (v: unknown) => unknown;
PAYMENT_UNAVAILABLE: (e: PaymentUnavailable) => unknown;
UNEXPECTED: (e: UnexpectedError) => unknown;
}>(outcome: Outcome<string, PaymentUnavailable | UnexpectedError>, handlers: {
ok: (v: unknown) => unknown;
PAYMENT_UNAVAILABLE: (e: PaymentUnavailable) => unknown;
UNEXPECTED: (e: UnexpectedError) => unknown;
}): any

Exhaustive handling by error code: Outcome.match(await pipeline.settle(cb), { ok: (v) => …, TIMEOUT: (e) => …, CIRCUIT_OPEN: (e) => …, UNEXPECTED: (e) => … }). Add a breaker and forget its handler: compile error.

match
(
const outcome: Outcome<string, PaymentUnavailable | UnexpectedError>
outcome
, {
ok: (v: unknown) => unknown
ok
,
type PAYMENT_UNAVAILABLE: (e: PaymentUnavailable) => unknown
PAYMENT_UNAVAILABLE
: (
e: PaymentUnavailable
e
) =>
e: PaymentUnavailable
e
.
Error.cause?: unknown
cause
,
type UNEXPECTED: (e: UnexpectedError) => unknown
UNEXPECTED
: (
e: UnexpectedError
e
) =>
e: UnexpectedError
e
.
Error.cause?: unknown
cause
});
Builder method xStrategy() form Removes from the type
addFallback({ … }) without shouldHandle fallbackStrategy Every coded error of the strategies after it
addFallback({ shouldHandle, … }) fallbackStrategy Nothing (the predicate isn’t analysed)
addFallbackOn(codes, action) fallbackOnStrategy Exactly codes
addMapError(mapper) mapErrorStrategy Every coded error after it; adds the mapper’s return type

addMapError lets caller aborts through unchanged.

const
const pipeline: ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>>
pipeline
= new
new ResiliencePipelineBuilder<any, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<any, never, never>

Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), … Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.

The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().

ResiliencePipelineBuilder
().
ResiliencePipelineBuilder<any, never, never>.addTimeout(options: Duration | TimeoutOptions): Next<any, never, never, TimeoutRejectedError>

addTimeout(5000), addTimeout('5s') or addTimeout({ timeout: '5s', onTimeout }).

addTimeout
('2s').
ResiliencePipelineBuilder<any, TimeoutRejectedError, never>.addConcurrencyLimiter(options: number | ConcurrencyLimiterOptions, queueLimit?: number): Next<any, TimeoutRejectedError, never, BulkheadRejectedError>

addConcurrencyLimiter(10), addConcurrencyLimiter(10, 50) or full options.

addConcurrencyLimiter
(10).
ResiliencePipelineBuilder<any, WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>, never>.build(): ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>>
build
();
type
type Errors = TimeoutRejectedError | BulkheadRejectedError
Errors
=
type PipelineErrors<P> = P extends ResiliencePipeline<any, infer E> ? E : never

The error type of a built pipeline: PipelineErrors<typeof pipeline>.

PipelineErrors
<typeof
const pipeline: ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>>
pipeline
>; // TimeoutRejectedError | BulkheadRejectedError
try {
await
const pipeline: ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>>
pipeline
.
ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>>.execute<Response>(callback: (context: ResilienceContext) => MaybePromise<Response>, options?: ExecuteOptions): Promise<Response>

Runs the callback through the pipeline; throws the final error.

execute
(() =>
function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)
fetch
('https://example.com'));
} catch (
var e: unknown
e
) {
if (
const pipeline: ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>>
pipeline
.
ResiliencePipeline<any, WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>>.isError(error: unknown): error is WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>

Is this one of the errors this pipeline can produce (E)? Resilience errors and mapError results.

isError
(
var e: unknown
e
))
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@since ― v0.1.100

log
(
var e: WithErrors<TimeoutRejectedError, never, BulkheadRejectedError>
e
.
code: "TIMEOUT" | "BULKHEAD_FULL"
code
); // 'TIMEOUT' | 'BULKHEAD_FULL'
}

isError(e) is true for any resilience error and for errors produced by addMapError.

Outcome<T, E> is a plain { ok: true, result } | { ok: false, error } object.

Helper Does
Outcome.fromResult(v) / Outcome.fromError(e) Create an outcome
Outcome.is(v) Was v created by these helpers (from any copy of the package)?
Outcome.try(fn) Run fn, capture a throw or rejection as a failed outcome
Outcome.try({ try, catch }) Same, mapping the error to a typed one
Outcome.all(outcomes) ok([...values]) if all succeeded, else the first failure
Outcome.partition(outcomes) { values, errors }, order kept
Outcome.unwrap(o) The value, or throw the error
Outcome.unwrapErr(o, code?) The error (checking its code); throws if o succeeded
Outcome.match(o, handlers) Exhaustive dispatch on error.code
Outcome.from(v) Convert a foreign Result through a registered adapter
class
class ConfigError
ConfigError
extends
var Error: ErrorConstructor
Error
{
readonly
ConfigError.code: "BAD_CONFIG"
code
= 'BAD_CONFIG' as
type const = "BAD_CONFIG"
const
;
}
const
const config: Outcome<{
port: number;
}, ConfigError>
config
=
const Outcome: {
fromResult: <T>(result: T) => Outcome<T, never>;
fromError: <T = never, E = unknown>(error: E) => Outcome<T, E>;
is: (value: unknown) => value is Outcome<unknown, unknown>;
from: (value: unknown) => Outcome<unknown, unknown> | undefined;
try: {
<R>(fn: () => R): TryResult<R, unknown>;
<R, E>(options: {
try: () => R;
catch: (error: unknown) => E;
}): TryResult<R, E>;
};
all<const O extends readonly Outcome<any, any>[]>(outcomes: O): Outcome<{ -readonly [K in keyof O]: OutcomeValue<O[K]>; }, OutcomeError<O[number]>>;
partition<O extends Outcome<any, any>>(outcomes: readonly O[]): {
values: OutcomeValue<O>[];
errors: OutcomeError<O>[];
};
unwrap: <T>(outcome: Outcome<T, unknown>) => T;
unwrapErr<E, C extends ErrorCodes<E> = ErrorCodes<...>>(outcome: Outcome<unknown, E>, code?: C | undefined): ErrorWithCode<E, C>;
match<T, E, H extends OutcomeHandlers<T, E, unknown>>(outcome: Outcome<T, E>, handlers: H): ReturnType<H[keyof H] & ((...args: any) => any)>;
}

The result of an execution: either a value or an error. Strategies pass outcomes around instead of throwing. E is the error type when it's known (see pipeline.settle()), unknown otherwise.

Outcome
.
try: <{
port: number;
}, ConfigError>(options: {
try: () => {
port: number;
};
catch: (error: unknown) => ConfigError;
}) => Outcome<{
port: number;
}, ConfigError> (+1 overload)
try
({
try: () => {
port: number;
}
try
: () =>
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.parse(text: string, reviver?: (this: any, key: string, value: any) => any): any

Converts a JavaScript Object Notation (JSON) string into an object.

@param ― text A valid JSON string.

@param ― reviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is.

@throws ― {SyntaxError} If text is not valid JSON.

parse
(
const text: string
text
) as {
port: number
port
: number },
catch: (error: unknown) => ConfigError
catch
: (
e: unknown
e
) => new
constructor ConfigError(message?: string, options?: ErrorOptions): ConfigError (+1 overload)
ConfigError
('invalid config', {
ErrorOptions.cause?: unknown
cause
:
e: unknown
e
}),
});
const
const port: number
port
=
const config: Outcome<{
port: number;
}, ConfigError>
config
.
ok: boolean
ok
?
const config: {
readonly ok: true;
readonly result: {
port: number;
};
}
config
.
result: {
port: number;
}
result
.
port: number
port
: 8080;

registerResultAdapter teaches settle() and Outcome.from() another library’s Result type (neverthrow, ts-results, Effect’s Either…). A returned Err then joins the typed channel like Outcome.fromError:

function registerResultAdapter(name: string, adapter: ResultAdapter): void

registerResultAdapter('neverthrow', { is: (v) => v instanceof Ok || v instanceof Err, toOutcome: (r) => r.isOk() ? Outcome.fromResult(r.value) : Outcome.fromError(r.error) })

registerResultAdapter
('neverthrow', {
ResultAdapter.is(value: unknown): boolean
is
: (
v: unknown
v
) =>
v: unknown
v
instanceof
class Ok<T>
Ok
||
v: unknown
v
instanceof
class Err<E>
Err
,
ResultAdapter.toOutcome(value: unknown): Outcome<unknown, unknown>
toOutcome
: (
v: unknown
v
) => (
v: unknown
v
instanceof
class Ok<T>
Ok
?
const Outcome: {
fromResult: <T>(result: T) => Outcome<T, never>;
fromError: <T = never, E = unknown>(error: E) => Outcome<T, E>;
is: (value: unknown) => value is Outcome<unknown, unknown>;
from: (value: unknown) => Outcome<unknown, unknown> | undefined;
try: {
<R>(fn: () => R): TryResult<R, unknown>;
<R, E>(options: {
try: () => R;
catch: (error: unknown) => E;
}): TryResult<R, E>;
};
all<const O extends readonly Outcome<any, any>[]>(outcomes: O): Outcome<{ -readonly [K in keyof O]: OutcomeValue<O[K]>; }, OutcomeError<O[number]>>;
partition<O extends Outcome<any, any>>(outcomes: readonly O[]): {
values: OutcomeValue<O>[];
errors: OutcomeError<O>[];
};
unwrap: <T>(outcome: Outcome<T, unknown>) => T;
unwrapErr<E, C extends ErrorCodes<E> = ErrorCodes<...>>(outcome: Outcome<unknown, E>, code?: C | undefined): ErrorWithCode<E, C>;
match<T, E, H extends OutcomeHandlers<T, E, unknown>>(outcome: Outcome<T, E>, handlers: H): ReturnType<H[keyof H] & ((...args: any) => any)>;
}

The result of an execution: either a value or an error. Strategies pass outcomes around instead of throwing. E is the error type when it's known (see pipeline.settle()), unknown otherwise.

Outcome
.
fromResult: <any>(result: any) => Outcome<any, never>
fromResult
(
v: Ok<any>
v
.
Ok<any>.value: any
value
) :
const Outcome: {
fromResult: <T>(result: T) => Outcome<T, never>;
fromError: <T = never, E = unknown>(error: E) => Outcome<T, E>;
is: (value: unknown) => value is Outcome<unknown, unknown>;
from: (value: unknown) => Outcome<unknown, unknown> | undefined;
try: {
<R>(fn: () => R): TryResult<R, unknown>;
<R, E>(options: {
try: () => R;
catch: (error: unknown) => E;
}): TryResult<R, E>;
};
all<const O extends readonly Outcome<any, any>[]>(outcomes: O): Outcome<{ -readonly [K in keyof O]: OutcomeValue<O[K]>; }, OutcomeError<O[number]>>;
partition<O extends Outcome<any, any>>(outcomes: readonly O[]): {
values: OutcomeValue<O>[];
errors: OutcomeError<O>[];
};
unwrap: <T>(outcome: Outcome<T, unknown>) => T;
unwrapErr<E, C extends ErrorCodes<E> = ErrorCodes<...>>(outcome: Outcome<unknown, E>, code?: C | undefined): ErrorWithCode<E, C>;
match<T, E, H extends OutcomeHandlers<T, E, unknown>>(outcome: Outcome<T, E>, handlers: H): ReturnType<H[keyof H] & ((...args: any) => any)>;
}

The result of an execution: either a value or an error. Strategies pass outcomes around instead of throwing. E is the error type when it's known (see pipeline.settle()), unknown otherwise.

Outcome
.
fromError: <unknown, unknown>(error: unknown) => Outcome<unknown, unknown>
fromError
((
v: unknown
v
as
class Err<E>
Err
<unknown>).
Err<unknown>.error: unknown
error
)),
});

resilience-pipeline/outcome exports the outcome helpers and error classes without any strategy, for code that only consumes results.

Every error a strategy raises extends ExecutionRejectedError and carries a stable code, a source ({ pipelineName, strategyName }) and toJSON().

Code Class Raised by
TIMEOUT TimeoutRejectedError Timeout
CIRCUIT_OPEN BrokenCircuitError Circuit breaker (open or half-open without a probe slot)
CIRCUIT_ISOLATED IsolatedCircuitError Circuit breaker held open by manualControl.isolate()
RATE_LIMITED RateLimiterRejectedError Token bucket, fixed/sliding window, partitioned limiters
BULKHEAD_FULL BulkheadRejectedError Concurrency limiter, adaptive concurrency limiter
PERMIT_LIMIT_EXCEEDED PermitLimitExceededError Window limiters: a request heavier than the limit
RATE_LIMIT_DENIED RateLimiterDeniedError Limiters with a matching deny rule
LOCK_NOT_ACQUIRED LockNotAcquiredError Lock: held by someone else past wait
UNEXPECTED UnexpectedError settle() only: wraps everything else

The rate limiters are typed as RateLimiterRejectedError, whose code covers RATE_LIMITED, BULKHEAD_FULL, PERMIT_LIMIT_EXCEEDED and RATE_LIMIT_DENIED. After addRateLimiter(), match asks for all four. HTTP status mapping and describeResilienceError() are in Error codes.

If two copies of the package are loaded (ESM and CJS, or nested installs), instanceof silently returns false. The guards check registered symbols instead and work across copies:

Guard True for
isResilienceError(e) Any ExecutionRejectedError
isTimeoutError(e) TIMEOUT
isBrokenCircuitError(e) CIRCUIT_OPEN, CIRCUIT_ISOLATED
isRateLimitError(e) RATE_LIMITED, BULKHEAD_FULL, PERMIT_LIMIT_EXCEEDED, RATE_LIMIT_DENIED
isRejection(e) Any error from a strategy that refused to start the call