@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
TypeScript
/**
* @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>;
}