@daiso-tech/core
Version:
The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.
103 lines • 3.75 kB
JavaScript
/**
* @module Resilience
*/
import { exponentialBackoff } from "../../../backoff-policies/_module.js";
import {} from "../../../hooks/_module.js";
import {} from "../../../resilience/middlewares/retry/retry.types.js";
import { RetryResilienceError } from "../../../resilience/resilience.errors.js";
import { Task } from "../../../task/implementations/_module.js";
import { callInvokable, optionNone, optionSome, OPTION, UnexpectedError, callErrorPolicyOnThrow, callErrorPolicyOnValue, } from "../../../utilities/_module.js";
/**
* The `retry` middleware enables automatic retries for all errors or specific errors, with configurable backoff policies.
* An error will be thrown when all retry attempts fail.
*
* IMPORT_PATH: `"@daiso-tech/core/resilience"`
* @group Middlewares
*
* @example
* ```ts
* import { retry } from "@daiso-tech/core/resilience";
* import { AsyncHooks } from "@daiso-tech/core/hooks";
* import { TimeSpan } from "@daiso-tech/core/time-span";
*
* const data = await new AsyncHooks(
* async (url: string, signal?: AbortSignal): Promise<unknown> => {
* const response = await fetch(url, { signal });
* const json = await response.json();
* if (!response.ok) {
* return json;
* }
* return json;
* },
* [retry()],
* {
* signalBinder: {
* getSignal: (args) => args[1],
* forwardSignal: (args, signal) => {
* args[1] = signal;
* }
* }
* }
* )
* .invoke("URL");
* ```
*/
export function retry(settings = {}) {
const { maxAttempts = 4, backoffPolicy = exponentialBackoff(), errorPolicy, onRetryDelay = () => { }, onExecutionAttempt = () => { }, } = settings;
return async (args, next, { context, signal }) => {
let result = optionNone();
const allErrors = [];
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
callInvokable(onExecutionAttempt, { attempt, args, context });
const value = await next(...args);
// Handle retrying if an Result type is returned
result = optionSome(value);
if (!callErrorPolicyOnValue(errorPolicy, value)) {
return value;
}
if (signal.aborted) {
break;
}
const waitTime = callInvokable(backoffPolicy, attempt, value);
callInvokable(onRetryDelay, {
error: value,
waitTime,
attempt,
args,
context,
});
await Task.delay(waitTime, signal);
// Handle retrying if an error is thrown
}
catch (error) {
if (signal.aborted) {
break;
}
if (await callErrorPolicyOnThrow(errorPolicy, error)) {
allErrors.push(error);
}
else {
throw error;
}
const waitTime = callInvokable(backoffPolicy, attempt, error);
callInvokable(onRetryDelay, {
error: error,
waitTime,
attempt,
args,
context,
});
await Task.delay(waitTime, signal);
}
}
if (allErrors.length !== 0) {
throw new RetryResilienceError(allErrors, "!!__MESSAGE__!!");
}
if (result.type === OPTION.SOME) {
return result.value;
}
throw new UnexpectedError("!!__MESSAGE__!!");
};
}
//# sourceMappingURL=retry.middleware.js.map