@stone-js/pipeline
Version:
An implementation based on the Chain of Responsibility (aka CoR) design pattern.
405 lines (400 loc) • 15.8 kB
TypeScript
/**
* Represents a Promiseable type.
*/
type Promiseable<T> = T | Promise<T>;
/**
* A string type that represents a pipe alias.
*/
type PipeAlias = string;
/**
* A class type that represents a pipe.
*/
type PipeClass<T = unknown, R = T, Args extends any[] = any[]> = new (...args: Args) => PipeInstance<T, R>;
/**
* A function type that represents a pipe.
*/
type FunctionalPipe<T = unknown, R = T> = (passable: T, next: PipeExecutor<T, R>, ...params: any[]) => Promiseable<R>;
/**
* A factory function type that represents a pipe.
*/
type FactoryPipe<T = unknown, R = T, Args extends any[] = any[]> = (...args: Args) => FunctionalPipe<T, R>;
/**
* A type that can either be a function or a string, representing a pipeline step.
*
* A pipe can either be a function that performs an action or a string identifier to be resolved.
*/
type PipeType<T = unknown, R = T, Args extends any[] = any[]> = PipeAlias | PipeClass<T, R, Args> | FunctionalPipe<T, R> | FactoryPipe<T, R, Args>;
/**
* A mixed type that can be either a simple Pipe or a MetaPipe configuration.
*/
type MixedPipe<T = unknown, R = T, Args extends any[] = any[]> = PipeType<T, R, Args> | MetaPipe<T, R, Args>;
/**
* Pipe Executor function type.
*
* @param passable - The passable objects being sent through the pipeline.
* @returns The result of the execution, which could be a synchronous or asynchronous response.
*
* @template T - The type of the passable object.
* @template R - The type of the return value from the pipeline execution, defaulting to `T`.
*/
type PipeExecutor<T = unknown, R = T> = (passable: T) => Promiseable<R>;
/**
* Next Pipe Executor function type.
*
* @param passable - The passable objects being sent through the pipeline.
* @returns The result of the execution, which could be a synchronous or asynchronous response.
*
* @template T - The type of the passable object.
* @template R - The type of the return value from the pipeline execution, defaulting to `T`.
*/
type NextPipe<T = unknown, R = T> = PipeExecutor<T, R>;
/**
* Reducer callback function type used to build a sequence of pipe executions.
*
* @param previousPipeExecutor - The executor from the previous step in the pipeline.
* @param currentPipe - The current pipe being added to the sequence.
* @returns The combined executor function.
*
* @template T - The type of the passable object.
* @template R - The type of the return value from the pipeline execution, defaulting to `T`.
*/
type ReducerCallback<T = unknown, R = T, Args extends any[] = any[]> = (previousPipeExecutor: PipeExecutor<T, R>, currentPipe: MetaPipe<T, R, Args>) => PipeExecutor<T, R>;
/**
* A function type that represents a resolver for a given pipe.
*
* @typeParam T - The type of the passable object in the pipeline.
* @typeParam R - The type of the return value from the resolved pipe, which defaults to `T`.
* @param pipe - The pipe that needs to be resolved, which can be either a simple pipe or a MetaPipe.
* @returns The resolved pipe instance of type `PipeInstance<T, R> | undefined`.
*
* This type is used to provide a custom mechanism for resolving pipes before they are executed in the pipeline.
*/
type PipeResolver<T = unknown, R = T, Args extends any[] = any[]> = (pipe: MetaPipe<T, R, Args>) => PipeInstance<T, R> | undefined;
/**
* ConfigContextOptions.
*/
interface PipelineOptions<T = unknown, R = T, Args extends any[] = any[]> {
hooks?: PipelineHook<T, R, Args>;
resolver?: PipeResolver<T, R, Args>;
}
/**
* Represents a pipe instance that contains different pipe functions.
*
* @template T - The type of the passable object.
* @template R - The type of the return value from the pipeline execution, defaulting to `T`.
*
* An object representing a set of functions used as part of the pipeline.
* The keys represent function names, and the values are functions that take specific arguments.
*/
type PipeInstance<T = unknown, R = T> = PipeDefaultInstance<T, R> | PipeCustomInstance<T, R> | object;
/**
* Represents a pipe instance that contains a default pipe function.
*
* @template T - The type of the passable object.
* @template R - The type of the return value from the pipeline execution, defaulting to `T`.
*
* An object representing a default function used as part of the pipeline.
* The key is the `handle` property, which is a function that takes specific arguments.
*/
interface PipeDefaultInstance<T = unknown, R = T> {
handle: FunctionalPipe<T, R>;
}
/**
* Represents a pipe instance that contains different pipe functions.
*
* @template T - The type of the passable object.
* @template R - The type of the return value from the pipeline execution, defaulting to `T`.
*
* An object representing a set of functions used as part of the pipeline.
* The keys represent function names, and the values are functions that take specific arguments.
*/
interface PipeCustomInstance<T = unknown, R = T> {
[key: string]: FunctionalPipe<T, R>;
}
/**
* Represents a MetaPipe configuration item, with a pipe, parameters, and priority level.
*
* A configuration object used for managing pipes in the pipeline.
*/
interface MetaPipe<T = unknown, R = T, Args extends any[] = any[]> {
/** The pipe to execute, which can be a function or a string identifier. */
module: PipeType<T, R, Args>;
/** An optional array of parameters to pass to the pipe. */
params?: any[];
/** An optional priority level of the pipe. */
priority?: number;
/** An optional flag indicating whether the pipe is a class. */
isClass?: boolean;
/** An optional flag indicating whether the pipe is a container alias. */
isAlias?: boolean;
/** An optional flag indicating whether the pipe is a factory. */
isFactory?: boolean;
}
/**
* HookName Type.
*/
type HookName = 'onProcessingPipe' | 'onPipeProcessed';
/**
* Hook Type.
*
* Represents a hook that can either be synchronous or asynchronous.
*/
type PipelineHook<T = unknown, R = T, Args extends any[] = any[]> = Record<HookName, Array<PipelineHookListener<T, R, Args>>>;
/**
* PipelineHookContext Type.
*/
interface PipelineHookContext<T = unknown, R = T, Args extends any[] = any[]> {
passable: T;
pipe: MetaPipe<T, R, Args>;
instance: PipeCustomInstance<T, R>;
pipes: Array<MetaPipe<T, R, Args>>;
}
/**
* PipelineHookListener Type.
*
* Represents a listener hook that can either be synchronous or asynchronous.
*/
type PipelineHookListener<T = unknown, R = T, Args extends any[] = any[]> = (context: PipelineHookContext<T, R, Args>) => Promiseable<void>;
/**
* Class representing a Pipeline.
*
* @template T - The type of the passable object in the pipeline.
* @template R - The type of the return value from the pipeline execution.
*
* This class is responsible for managing and executing a series of operations
* on a set of passable values through multiple configurable pipes.
*/
declare class Pipeline<T = unknown, R = T, Args extends any[] = any[]> {
/** The passable objects sent through the pipeline */
private passable?;
/** The method name to call on each pipe */
private method;
/** Flag indicating whether the pipeline should run synchronously or asynchronously */
private isSync;
/** The default priority for the pipes in the pipeline */
private _defaultPriority;
/** The sorted metadata pipes that will be executed */
private sortedMetaPipes;
/** The pipeline hooks */
private readonly hooks;
/** The resolver function used to resolve pipes before they are executed in the pipeline. */
private readonly resolver?;
/**
* Create a pipeline instance.
*
* @param options - Optional Pipeline options.
* @returns The pipeline instance.
*/
static create<T = unknown, R = T, Args extends any[] = any[]>(options?: PipelineOptions<T, R, Args>): Pipeline<T, R>;
/**
* Initialize a new Pipeline instance.
*
* @param options - Optional Pipeline options.
*/
protected constructor(options?: PipelineOptions<T, R, Args>);
/**
* Set the default priority for pipes in the pipeline.
*
* @param value - The priority value to set.
* @returns The current Pipeline instance.
*/
defaultPriority(value: number): this;
/**
* Set the passable objects being sent through the pipeline.
*
* @param passable - The object to pass through the pipeline.
* @returns The current Pipeline instance.
*/
send(passable: T): this;
/**
* Set the pipes for the pipeline.
*
* @param pipes - The pipes or MetaPipe instances.
* @returns The current Pipeline instance.
*/
through(...pipes: Array<MixedPipe<T, R, Args>>): this;
/**
* Add additional pipes to the pipeline.
*
* @param {...MixedPipe} pipe - A single pipe or a list of pipes to add.
* @returns The current Pipeline instance.
*/
pipe(...pipe: Array<MixedPipe<T, R, Args>>): this;
/**
* Set the method to call on each pipe.
*
* @param method - The method name to use on each pipe.
* @returns The current Pipeline instance.
*/
via(method: string): this;
/**
* Configure the pipeline to run synchronously or asynchronously.
*
* @param value - Set true for sync, false for async (default is true).
* @returns The current Pipeline instance.
*/
sync(value?: boolean): this;
/**
* Add a hook to the pipeline.
*
* @param name - The name of the hook.
* @param listener - The hook listener function.
* @returns The current Pipeline instance.
*/
on(name: HookName, listener: PipelineHookListener<T, R, Args> | Array<PipelineHookListener<T, R, Args>>): this;
/**
* Run the pipeline with a final destination callback.
*
* @param destination - The final function to execute.
* @returns The result of the pipeline, either synchronously or as a Promise.
*/
then(destination: PipeExecutor<T, R>): Promiseable<R>;
/**
* Run the pipeline and return the result.
*
* @returns The result of the pipeline, either synchronously or as a Promise.
*/
thenReturn(): Promiseable<R>;
/**
* Get the asynchronous reducer to iterate over the pipes.
*
* @returns The asynchronous reducer callback.
*/
private asyncReducer;
/**
* Get the synchronous reducer to iterate over the pipes.
*
* @returns The synchronous reducer callback.
*/
private reducer;
/**
* Resolve and execute async pipe.
*
* @param currentPipe - The current pipe to execute (class or service alias string).
* @param passable - The passable object to send through the pipe.
* @param previousPipeExecutor - The previous pipe executor in the sequence.
* @returns The result of the pipe execution.
* @throws PipelineError If the pipe cannot be resolved or the method is missing.
*/
private executeAsyncPipe;
/**
* Resolve and execute a pipe.
*
* @param currentPipe - The current pipe to execute (class or service alias string).
* @param passable - The passable object to send through the pipe.
* @param previousPipeExecutor - The previous pipe executor in the sequence.
* @returns The result of the pipe execution.
* @throws PipelineError If the pipe cannot be resolved or the method is missing.
*/
private executePipe;
/**
* Resolve pipe.
*
* @param currentPipe - The current pipe to execute (class or service alias string).
* @returns The resolved pipe instance.
* @throws PipelineError If the pipe cannot be resolved or the method is missing.
*/
private resolvePipe;
/**
* Create an instance from the provided pipe.
*
* @param currentPipe - The pipe function to create an instance from.
* @returns The created instance or an object with the method.
*/
private createInstanceFromPipe;
/**
* Validate that the required method exists on the instance.
*
* @param instance - The instance to validate.
* @param currentPipe - The current pipe being executed.
* @throws {PipelineError} If the method does not exist on the instance.
*/
private validatePipeMethod;
/**
* Execute lifecycle async hooks.
*
* @param name - The hook's name.
* @param pipe - The current pipe instance.
*/
private executeAsyncHooks;
/**
* Execute lifecycle hooks.
*
* @param name - The hook's name.
* @param pipe - The current pipe instance.
*/
private executeHooks;
}
/**
* Custom error for pipeline operations.
*/
declare class PipelineError extends Error {
constructor(message: string);
}
/**
* Define a new middleware for the pipeline.
*
* @param module - The pipe module to add to the pipeline.
* @param options - Additional options for the middleware.
* @returns The metadata for the middleware.
*/
declare const defineMiddleware: <T = unknown, R = T, Args extends any[] = any[]>(module: PipeType<T, R, Args>, options?: Omit<MetaPipe<T, R, Args>, "module">) => MetaPipe<T, R, Args>;
/**
* Check if the value is a string.
*
* @param value - The value to check.
* @returns `true` if the value is an string, otherwise `false`.
*/
declare const isString: (value: unknown) => value is string;
/**
* Check if the value is a function.
*
* @param value - The value to check.
* @returns `true` if the value is a function, otherwise `false`.
*/
declare const isFunction: <ClassType = Function>(value: unknown) => value is ClassType;
/**
* Checks if the given value is a constructor function.
*
* @param value - The value to be checked.
* @returns True if the value is a constructor function, false otherwise.
*/
declare const isConstructor: <ClassType = any>(value: unknown) => value is new (...args: any[]) => ClassType;
/**
* Check if the meta pipe is a function pipe.
*
* @param metaPipe - The meta pipe to check.
* @returns `true` if the meta pipe is a function pipe, otherwise `false`.
*/
declare const isFunctionPipe: <T = unknown, R = T, Args extends any[] = any[]>(metaPipe: MetaPipe<T, R, Args>) => metaPipe is {
module: FunctionalPipe<T, R>;
};
/**
* Check if the meta pipe is an alias pipe.
*
* @param metaPipe - The meta pipe to check.
* @returns `true` if the meta pipe is an alias pipe, otherwise `false`.
*/
declare const isAliasPipe: <T = unknown, R = T, Args extends any[] = any[]>(metaPipe: MetaPipe<T, R, Args>) => metaPipe is {
module: PipeAlias;
};
/**
* Check if the meta pipe is a class pipe.
*
* @param metaPipe - The meta pipe to check.
* @returns `true` if the meta pipe is a class pipe, otherwise `false`.
*/
declare const isClassPipe: <T = unknown, R = T, Args extends any[] = any[]>(metaPipe: MetaPipe<T, R, Args>) => metaPipe is {
module: PipeClass<T, R, Args>;
};
/**
* Check if the meta pipe is a factory pipe.
*
* @param metaPipe - The meta pipe to check.
* @returns `true` if the meta pipe is a factory pipe, otherwise `false`.
*/
declare const isFactoryPipe: <T = unknown, R = T, Args extends any[] = any[]>(metaPipe: MetaPipe<T, R, Args>) => metaPipe is {
module: FactoryPipe<T, R, Args>;
};
export { Pipeline, PipelineError, defineMiddleware, isAliasPipe, isClassPipe, isConstructor, isFactoryPipe, isFunction, isFunctionPipe, isString };
export type { FactoryPipe, FunctionalPipe, HookName, MetaPipe, MixedPipe, NextPipe, PipeAlias, PipeClass, PipeCustomInstance, PipeDefaultInstance, PipeExecutor, PipeInstance, PipeResolver, PipeType, PipelineHook, PipelineHookContext, PipelineHookListener, PipelineOptions, Promiseable, ReducerCallback };