Skip to content
Buy me a coffee

Bundle sizes

What you import (min+gzip, browser) Size Budget
retry() one-liner 4.1 KB 4.3 KB
PipelineBuilder + retry + timeout 4.8 KB 5.0 KB
PipelineBuilder + retry + circuit breaker + timeout 7.1 KB 7.3 KB
createResilientFetch() (/fetch, per-host breakers) 9.1 KB 9.3 KB
ResiliencePipelineBuilder (every strategy) 15.1 KB 15.3 KB

Measured for version 0.3.0 with scripts/size.mjs. The package has no dependencies, so these are the whole cost.

Each row is a tiny entry file that imports from the built ESM output and uses the API once, so nothing is tree-shaken away by accident:

// 'PipelineBuilder + retry + timeout'
import { PipelineBuilder, retryStrategy, timeoutStrategy } from '@ylcnfrht/resilience';
new PipelineBuilder().add(retryStrategy()).add(timeoutStrategy('2s')).build();

esbuild bundles it (bundle, minify, format: 'esm', platform: 'browser'), and the output is gzipped with Node’s zlib. The size is that gzip length divided by 1024.

The budget column is enforced in CI: npm run check:size fails when a scenario grows past it. To reproduce:

Terminal window
npm run build && node scripts/size.mjs

Your bundler, minifier and compression will give slightly different numbers; the ratios between rows hold.

import {
class PipelineBuilder<T = any, E = never, Caught extends string = never>

The minimal builder: only the strategies you add end up in your bundle.

import { PipelineBuilder, retryStrategy, timeoutStrategy } from '@ylcnfrht/resilience';
const pipeline = new PipelineBuilder().add(retryStrategy({ maxRetryAttempts: 3 })).add(timeoutStrategy('2s')).build();

ResiliencePipelineBuilder adds Polly-style addRetry() / addTimeout() / … shortcuts on top, at the cost of bundling every strategy. Strategies run in the order they are added: the first one added is the outermost. Options are validated and strategy state (e.g. circuit breaker) is created on build().

PipelineBuilder
,
const circuitBreakerStrategy: <T = any>(options?: CircuitBreakerOptions<T>) => StrategyDefinition<T, BrokenCircuitError>
circuitBreakerStrategy
,
const retryStrategy: <T = any>(options?: RetryOptions<T>) => StrategyDefinition<T>

new PipelineBuilder().add(retryStrategy({ ... })): same as addRetry, but only bundles retry.

retryStrategy
,
const timeoutStrategy: <T = any>(options: Duration | TimeoutOptions) => StrategyDefinition<T, TimeoutRejectedError>

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

timeoutStrategy
} from '@ylcnfrht/resilience';
// Only retry, circuit breaker and timeout end up in the bundle.
const
const pipeline: ResiliencePipeline<any, WithErrors<BrokenCircuitError, never, TimeoutRejectedError>>
pipeline
= new
new PipelineBuilder<any, never, never>(options?: ResiliencePipelineBuilderOptions): PipelineBuilder<any, never, never>

The minimal builder: only the strategies you add end up in your bundle.

import { PipelineBuilder, retryStrategy, timeoutStrategy } from '@ylcnfrht/resilience';
const pipeline = new PipelineBuilder().add(retryStrategy({ maxRetryAttempts: 3 })).add(timeoutStrategy('2s')).build();

ResiliencePipelineBuilder adds Polly-style addRetry() / addTimeout() / … shortcuts on top, at the cost of bundling every strategy. Strategies run in the order they are added: the first one added is the outermost. Options are validated and strategy state (e.g. circuit breaker) is created on build().

PipelineBuilder
()
.
PipelineBuilder<any, never, never>.add<StrategyDefinition<any, never>>(definition: StrategyDefinition<any, never>): PipelineBuilder<any, never, never>
add
(
retryStrategy<any>(options?: RetryOptions<any>): StrategyDefinition<any, never>

new PipelineBuilder().add(retryStrategy({ ... })): same as addRetry, but only bundles retry.

retryStrategy
({
RetryOptions<T>.maxRetryAttempts?: number | undefined

Retries in addition to the original call. Default 3. Infinity retries until aborted.

maxRetryAttempts
: 3,
RetryOptions<T>.backoffType?: BackoffType | undefined

Default 'constant'.

backoffType
: 'exponential' }))
.
PipelineBuilder<any, never, never>.add<StrategyDefinition<any, BrokenCircuitError>>(definition: StrategyDefinition<any, BrokenCircuitError>): PipelineBuilder<any, BrokenCircuitError, never>
add
(
circuitBreakerStrategy<any>(options?: CircuitBreakerOptions<any>): StrategyDefinition<any, BrokenCircuitError>
circuitBreakerStrategy
({
CircuitBreakerOptions<T>.consecutiveFailures?: number | undefined

Break after this many failures in a row (a success resets the count). Simple and good for low traffic. When set without failureRatio, the ratio check is off and only this rule applies.

consecutiveFailures
: 5 }))
.
PipelineBuilder<any, BrokenCircuitError, never>.add<StrategyDefinition<any, TimeoutRejectedError>>(definition: StrategyDefinition<any, TimeoutRejectedError>): PipelineBuilder<any, WithErrors<BrokenCircuitError, never, TimeoutRejectedError>, never>
add
(
timeoutStrategy<any>(options: Duration | TimeoutOptions): StrategyDefinition<any, TimeoutRejectedError>

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

timeoutStrategy
('2s'))
.
PipelineBuilder<any, WithErrors<BrokenCircuitError, never, TimeoutRejectedError>, never>.build(): ResiliencePipeline<any, WithErrors<BrokenCircuitError, never, TimeoutRejectedError>>
build
();
  • Use PipelineBuilder with xStrategy() factories in browser and edge code. ResiliencePipelineBuilder is the same builder with an addX() shortcut for every strategy, which is why it costs the full 15.1 KB. The two produce the same pipeline and mix freely.
  • Use the one-liners for one-off calls. retry() and withTimeout() build their strategy directly and skip the builder.
  • Circuit breaker extensions are separate modules. circuitHealthCheck() and sharedCircuitState() are only bundled when you import them.
  • Import subpaths directly. @ylcnfrht/resilience/outcome loads only the outcome helpers and error types. /fetch, /axios, /otel, /prometheus, /redis and the framework adapters are separate entry points and never enter a bundle that doesn’t import them.
  • On the server it rarely matters. For Node services, prefer ResiliencePipelineBuilder for readability.

Related: Bundle size, Runtimes.