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.

212 lines (211 loc) 8.37 kB
/** * @module Utilities */ import { type IInvokableObject, type Invokable, type InvokableFn, type OneOrMore, type Promisable } from "../../../utilities/_module-exports.js"; import type { HookContext } from "../../../utilities/classes/hooks/types.js"; /** * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks */ export type GetSignal<TParameters extends unknown[] = unknown[]> = Invokable<[ arguments_: TParameters ], AbortSignal | undefined>; /** * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks */ export type ForwardSignal<TParameters> = Invokable<[ arguments_: TParameters, signal: AbortSignal ], void>; /** * With {@link AbortSignalBinder | `AbortSignalBinder`}, you can bind an {@link AbortSignal | `AbortSignal`} to the middleware, enabling two-way abortion control. * This means the middleware can abort the function, or the function can abort the middleware if the input function supports an {@link AbortSignal | `AbortSignal`}. * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks * * @example * ```ts * import { AsyncHooks } from "@daiso-tech/core/utilities"; * * const abortController = new AbortController(); * * // You can abort the function based on by your on criteria * abortController.abort("Aborted") // Remove this code if you want the function to aborted after 2 seconds by the middleware. * * const data = await new AsyncHooks(async (url: string, signal?: AbortSignal): Promise<unknown> => { * const response = await fetch(url, { signal }); * return await response.json(); * }, [ * (args, next, { abort, signal }) => { * // We abort the function when it execdes 2 seconds. * const id = setTimeout(() => abort("Timed out"), 2000); * // We remove the timeout if function is aborted before it execdes 2 seconds. * signal.addEventListener("abort", () => clearTimeout(id), { once: true }); * return next(...args); * } * ], { * signalBinder: { * getSignal: (args) => args[1], * forwardSignal: (args, signal) => { * args[1] = signal; * } * } * }) * .invoke("url", abortController.signal); * * console.log("DATA:", data) * ``` */ export type AbortSignalBinder<TParameters extends unknown[] = unknown[]> = { getSignal: GetSignal<TParameters>; forwardSignal: ForwardSignal<TParameters>; }; /** * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks */ export type AsyncNextFunc<TParameters extends unknown[] = unknown[], TReturn = unknown> = InvokableFn<TParameters, PromiseLike<TReturn>>; /** * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks */ export type AsyncContext<TContext extends HookContext = HookContext> = { name: string; context: TContext; abort: (error: unknown) => void; signal: AbortSignal; }; /** * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks */ export type AsyncMiddlewareFn<TParameters extends unknown[] = unknown[], TReturn = unknown, TContext extends HookContext = HookContext> = InvokableFn<[ arguments_: TParameters, next: AsyncNextFunc<TParameters, TReturn>, settings: AsyncContext<TContext> ], Promisable<TReturn>>; /** * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks */ export type IAsyncMiddlewareObject<TParameters extends unknown[] = unknown[], TReturn = unknown, TContext extends HookContext = HookContext> = IInvokableObject<[ arguments_: TParameters, next: AsyncNextFunc<TParameters, TReturn>, settings: AsyncContext<TContext> ], Promisable<TReturn>>; /** * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks */ export type AsyncMiddleware<TParameters extends unknown[] = unknown[], TReturn = unknown, TContext extends HookContext = HookContext> = IAsyncMiddlewareObject<TParameters, TReturn, TContext> | AsyncMiddlewareFn<TParameters, TReturn, TContext>; /** * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks */ export type AsyncHooksSettings<TParameters extends unknown[] = unknown[], TContext extends HookContext = HookContext> = { /** * The name of the function which can be used for logging inside the middleware. * By default, it takes the function or method name. If an anonymous function is provided, the name defaults to "func". */ name?: string; /** * If the input function accepts an {@link AbortSignal | `AbortSignal`}, you can bind it to the middleware by providing an {@link AbortSignalBinder | `AbortSignalBinder`}. * This enables two-way abortion control: you can either abort the function from within the middleware, or abort the middleware from the function itself. */ signalBinder?: AbortSignalBinder<TParameters>; /** * You can provide addtional context that can be used in the middleware. */ context?: TContext; }; /** * The `AsyncHooks` class provides a convenient way to change and inspect arguments and return value of both async and sync functions. * For example `AsyncHooks` class can be used to log function arguments and return values. Note this class will always return promise and is immutable. * * Middlewares apply left to right: each wraps the next, with the leftmost being the outermost layer and the rightmost wrapping the original function. * * IMPORT_PATH: `"@daiso-tech/core/utilities"` * @group Hooks */ export declare class AsyncHooks<TParameters extends unknown[] = unknown[], TReturn = unknown, TContext extends HookContext = HookContext> implements IInvokableObject<TParameters, Promise<TReturn>> { private readonly invokable; private readonly middlewares; private readonly settings; private static defaultAbortSignalBinder; private static resolveSignalBinder; private static init; private readonly func; /** * @example * ```ts * import { AsyncHooks, type AsyncMiddlewareFn } from "@daiso-tech/core/utilities"; * * function log<TParameters extends unknown[], TReturn>(): AsyncMiddlewareFn<TParameters, TReturn> { * return async (args, next, { name: funcName }) => { * console.log("FUNCTION_NAME:", funcName); * console.log("ARGUMENTS:", args); * const value = await next(...args); * console.log("RETURN:", value); * return value; * } * } * * function time<TParameters extends unknown[], TReturn>(): AsyncMiddlewareFn<TParameters, TReturn> { * return async (args, next) => { * const start = performance.now(); * const value = await next(...args); * const end = performance.now(); * const time = end - start; * console.log("TIME:", `${String(time)}ms`); * return value; * } * } * * function add(a: number, b: number): number { * return a + b; * } * * const enhancedAdd = new AsyncHooks(add, [ * log(), * time() * ], { * // You can provide addtional data to be used the middleware. * context: {}, * }); * * // Will log the function name, arguments and return value. * // Will also log the execution time. * const result = await enhancedAdd.invoke(1, 2); * * // Will be 3. * console.log(result); * ``` */ constructor(invokable: Invokable<TParameters, Promisable<TReturn>>, middlewares: NoInfer<OneOrMore<AsyncMiddleware<TParameters, TReturn, TContext>>>, settings?: AsyncHooksSettings<TParameters, TContext>); /** * The `pipe` method returns a new `AsyncHooks` instance with the additional `middlewares` applied. */ pipe(middlewares: OneOrMore<AsyncMiddleware<TParameters, TReturn, TContext>>): AsyncHooks<TParameters, TReturn, TContext>; /** * The `pipeWhen` method conditionally applies additional `middlewares`, returning a new `AsyncHooks` instance only if the specified condition is met. */ pipeWhen(condition: boolean, middlewares: OneOrMore<AsyncMiddleware<TParameters, TReturn, TContext>>): AsyncHooks<TParameters, TReturn, TContext>; /** * The `toFunc` will return the function with all middlewares applied. */ toFunc(): InvokableFn<TParameters, PromiseLike<TReturn>>; /** * The `invoke` method executes the constructor's input function, applying all middlewares. */ invoke(...arguments_: TParameters): Promise<TReturn>; }