UNPKG

@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.

133 lines 4.57 kB
/** * @module Async */ import { AsyncHooks, callErrorPolicyOnThrow, callErrorPolicyOnValue, callInvokable, isInvokable, optionNone, optionSome, resolveOneOrMore, UnexpectedError, } from "../../../utilities/_module-exports.js"; import {} from "../../../async/middlewares/hedging/hedging.types.js"; /** * @internal */ function resolveFunctions(primaryFn, fallbacks, middlewares, signalBinder) { const resolvedFallbacks = resolveOneOrMore(fallbacks).map((fallback, index) => { if (isInvokable(fallback)) { return { name: `fallback-${String(index + 1)}`, invokable: fallback, }; } return fallback; }); return [ { name: "__primary", invokable: primaryFn, }, ...resolvedFallbacks, ].map((namedFn) => ({ name: namedFn.name, invokable: new AsyncHooks(namedFn.invokable, middlewares, { signalBinder, }), })); } /** * The `sequentialHedging` middleware executes the primary function and all fallback functions sequentially. * It returns the result of the first successful function and automatically cancels all remaining functions. * If all function fail than last error is thrown. * * IMPORT_PATH: `"@daiso-tech/core/async"` * @group Middlewares * @throws {HedgingAsyncError} {@link HedgingAsyncError} * * @example * ```ts * import { sequentialHedging } from "@daiso-tech/core/async"; * import { AsyncHooks } from "@daiso-tech/core/utilities"; * * async function fn1(signal?: AbortSignal): Promise<unknown> { * const response = await fetch("ENDPOINT-1", { signal }); * return await response.json(); * } * async function fn2(signal?: AbortSignal): Promise<unknown> { * const response = await fetch("ENDPOINT-2", { signal }); * return await response.json(); * } * async function fn3(signal?: AbortSignal): Promise<unknown> { * const response = await fetch("ENDPOINT-3", { signal }); * return await response.json(); * } * const fetchData = new AsyncHooks(fn1, [ * sequentialHedging({ * fallbacks: [ * fn2, * fn3 * ] * }) * ], { * signalBinder: { * getSignal: (args) => args[0], * forwardSignal: (args, signal) => { * args[0] = signal; * } * } * }); * * console.log(await fetchData.invoke()); * ``` */ export function sequentialHedging(settings) { const { middlewares = [], fallbacks, errorPolicy, onHedgingAttempt = () => { }, onHedgingError = () => { }, } = settings; return async (args, next, { context, signal, signalBinder }) => { const funcs = resolveFunctions(next, fallbacks, middlewares, signalBinder); let lastError = optionNone(); for (const { name, invokable: func } of funcs) { try { if (signal.aborted) { break; } callInvokable(onHedgingAttempt, { args, context, name, }); const value = await callInvokable(func, ...args); // Handle hedging if an Result type is returned if (!(await callErrorPolicyOnValue(errorPolicy, value))) { return value; } // We can cast type here because callErrorPolicyOnValue ensures the value is a ResultFailure const resultFailure = value; callInvokable(onHedgingError, { args, context, error: resultFailure.error, name, }); lastError = optionSome(resultFailure.error); // Handle hedging if an error is thrown } catch (error) { if (signal.aborted) { break; } if (await callErrorPolicyOnThrow(errorPolicy, error)) { lastError = optionSome(error); } else { throw error; } callInvokable(onHedgingError, { args, context, error, name, }); lastError = optionSome(error); } } if (lastError.type === "none") { throw new UnexpectedError("!!__MESSAGE__!!"); } throw lastError.value; }; } //# sourceMappingURL=sequential-hedging.middleware.js.map