UNPKG

@urql/exchange-retry

Version:

An exchange for operation retry support in urql

134 lines (125 loc) 4.78 kB
Object.defineProperty(exports, '__esModule', { value: true }); var wonka = require('wonka'); var core = require('@urql/core'); /** Input parameters for the {@link retryExchange}. */ /** Exchange factory that retries failed operations. * * @param options - A {@link RetriesExchangeOptions} configuration object. * @returns the created retry {@link Exchange}. * * @remarks * The `retryExchange` retries failed operations with specified delays * and exponential backoff. * * You may define a {@link RetryExchangeOptions.retryIf} or * {@link RetryExchangeOptions.retryWhen} function to only retry * certain kinds of operations, e.g. only queries. * * @example * ```ts * retryExchange({ * initialDelayMs: 1000, * maxDelayMs: 15000, * randomDelay: true, * maxNumberAttempts: 2, * retryIf: err => err && err.networkError, * }); * ``` */ var retryExchange = (options = {}) => { var { retryIf, retryWith } = options; var MIN_DELAY = options.initialDelayMs || 1000; var MAX_DELAY = options.maxDelayMs || 15_000; var MAX_ATTEMPTS = options.maxNumberAttempts || 2; var RANDOM_DELAY = options.randomDelay != null ? !!options.randomDelay : true; return ({ forward, dispatchDebug }) => operations$ => { var { source: retry$, next: nextRetryOperation } = wonka.makeSubject(); var retryWithBackoff$ = wonka.mergeMap(operation => { var retry = operation.context.retry || { count: 0, delay: null }; var retryCount = ++retry.count; var delayAmount = retry.delay || MIN_DELAY; var backoffFactor = Math.random() + 1.5; if (RANDOM_DELAY) { // if randomDelay is enabled and it won't exceed the max delay, apply a random // amount to the delay to avoid thundering herd problem if (delayAmount * backoffFactor < MAX_DELAY) { delayAmount *= backoffFactor; } else { delayAmount = MAX_DELAY; } } else { // otherwise, increase the delay proportionately by the initial delay delayAmount = Math.min(retryCount * MIN_DELAY, MAX_DELAY); } // ensure the delay is carried over to the next context retry.delay = delayAmount; // We stop the retries if a teardown event for this operation comes in // But if this event comes through regularly we also stop the retries, since it's // basically the query retrying itself, no backoff should be added! var teardown$ = wonka.filter(op => { return (op.kind === 'query' || op.kind === 'teardown') && op.key === operation.key; })(operations$); process.env.NODE_ENV !== 'production' ? dispatchDebug({ type: 'retryAttempt', message: `The operation has failed and a retry has been triggered (${retryCount} / ${MAX_ATTEMPTS})`, operation, data: { retryCount, delayAmount }, "source": "retryExchange" }) : undefined; // Add new retryDelay and retryCount to operation return ( // Stop retry if a teardown comes in wonka.takeUntil(teardown$)(wonka.debounce(() => delayAmount)(wonka.fromValue(core.makeOperation(operation.kind, operation, { ...operation.context, retry })))) ); })(retry$); return wonka.filter(res => { var retry = res.operation.context.retry; // Only retry if the error passes the conditional retryIf function (if passed) // or if the error contains a networkError if (!res.error || (retryIf ? !retryIf(res.error, res.operation) : !retryWith && !res.error.networkError)) { // Reset the delay state for a successful operation if (retry) { retry.count = 0; retry.delay = null; } return true; } var maxNumberAttemptsExceeded = (retry && retry.count || 0) >= MAX_ATTEMPTS - 1; if (!maxNumberAttemptsExceeded) { var _operation = retryWith ? retryWith(res.error, res.operation) : res.operation; if (!_operation) return true; // Send failed responses to be retried by calling next on the retry$ subject // Exclude operations that have been retried more than the specified max nextRetryOperation(_operation); return false; } process.env.NODE_ENV !== 'production' ? dispatchDebug({ type: 'retryExhausted', message: 'Maximum number of retries has been reached. No further retries will be performed.', operation: res.operation, "source": "retryExchange" }) : undefined; return true; })(forward(wonka.merge([operations$, retryWithBackoff$]))); }; }; exports.retryExchange = retryExchange; //# sourceMappingURL=urql-exchange-retry.js.map