UNPKG

assemblerjs

Version:

A general purpose Dependency Injection library for node and browser.

661 lines (614 loc) 26.6 kB
import { Abstract } from '@assemblerjs/core'; import { Class } from '@assemblerjs/core'; import { Concrete } from '@assemblerjs/core'; import { Tuple } from '@assemblerjs/core'; /** * Base abstract class to implement an assemblage. */ export declare abstract class AbstractAssemblage { [key: string]: any; /** * Called on concrete class registration by assembler. * * @param { AssemblerContext } context The assembler's context. * @param { Reord<string, any> } configuration The configuration object. */ static onRegister(context: AssemblerContext, configuration?: Record<string, any>): void; /** * Called on instantiated class in less dependent to more dependent order after the dependency tree is fully resolved. * * @param { AssemblerContext } context The assembler's context. * @param { Reord<string, any> } configuration The configuration object. */ abstract onInit?(context: AssemblerContext, configuration?: Record<string, any>): void | Promise<void>; /** * Called by the `Assembler` when all classes have been inited with `onInit`, including the entry point, * in more dependent to less dependent order. * * @param { AssemblerContext } context The assembler's context. * @param { Reord<string, any> } configuration The configuration object. */ abstract onInited?(context: AssemblerContext, configuration?: Record<string, any>): void | Promise<void>; /** * Called when instance of class is disposed. * * @param { AssemblerContext } context The assembler's context. */ abstract onDispose?(context: AssemblerContext): void; /** * Dispose the assemblage instance. */ abstract dispose?(): void; /** * When decorated with '@Waitable', waits for a specific property to be truthy before resolving. */ abstract whenReady?(): Promise<void>; } /** * `Assembler` abstraction. */ export declare abstract class AbstractAssembler extends AbstractEventManager { abstract privateContext: AssemblerContext; abstract publicContext: AssemblerContext; abstract size: number; abstract register<T>(injection: Injection<T>, instance?: boolean): Injectable<T>; abstract use<T>(identifier: string | symbol, object: T): T; abstract prepareInitHook<T = AbstractAssemblage>(instance: T, configuration?: Record<string, any>): unknown[]; abstract addGlobal(key: string, value: any): void; abstract has<T>(identifier: Identifier<T>): boolean; abstract require<T>(identifier: Identifier<T> | string | symbol, configuration?: Record<string, any>): T; abstract concrete<T>(identifier: Identifier<T>): Concrete<T> | undefined; abstract tagged(...tags: string[]): any[]; abstract global(key: string): any | undefined; abstract dispose(): void; } export declare abstract class AbstractEventManager { abstract channels: Set<string>; abstract dispose(): void; abstract addChannels(...channels: string[]): AbstractEventManager; abstract removeChannels(...channels: string[]): AbstractEventManager; abstract on(channel: string, callback: Listener): AbstractEventManager; abstract once(channel: string, callback: Listener): AbstractEventManager; abstract off(channel: string, callback?: Listener): AbstractEventManager; abstract emit(channel: string, ...args: any[]): AbstractEventManager; } declare abstract class AbstractInjectable<T> { static of<TNew>(buildable: Buildable<TNew>, privateContext: AssemblerPrivateContext, publicContext: AssemblerContext): void; abstract readonly privateContext: AssemblerPrivateContext; abstract readonly publicContext: AssemblerContext; abstract readonly identifier: Identifier<T> | string | symbol; abstract readonly concrete: Concrete<T>; abstract readonly configuration: Record<string, any>; abstract dependencies: (Identifier<unknown> | any)[]; abstract definition: AssemblageDefinition; abstract isSingleton: boolean; abstract singleton: T | undefined; abstract injections: Injection<unknown>[]; abstract objects: InstanceInjection<unknown>[]; abstract tags: string[]; abstract events: string[]; abstract dispose(): void; abstract build(): T; } /** * An abstract class for `ListenerCollection` implementation. */ export declare abstract class AbstractListenerCollection { [key: EventChannel]: any; abstract listeners: Listener[]; abstract channels: EventChannel[]; abstract add(channel: EventChannel, listener: Listener): AbstractListenerCollection; abstract remove(channel: EventChannel, listener?: Listener): AbstractListenerCollection; abstract has(...args: (EventChannel | Listener)[]): boolean; abstract get(...args: (EventChannel | Listener)[]): (EventChannel | Listener)[]; abstract clear(): AbstractListenerCollection; abstract [Symbol.iterator](): Iterator<EventChannel>; } /** * Mark a class as `Assemblage` and cache its definition. * * @param { AssemblageDefinition } definition Definition of the assemblage that provides injections, etc. * @returns { ClassDecorator } The decorated class. */ export declare const Assemblage: <T>(definition?: AssemblageDefinition) => ClassDecorator; export declare interface AssemblageDefinition { singleton?: false; events?: string[]; inject?: Injection<unknown>[]; use?: InstanceInjection<unknown>[]; tags?: string | string[]; metadata?: Record<string, any>; global?: Record<string, any>; } /** * `assembler.js` dependency injection container and handler. */ export declare class Assembler extends EventManager implements AbstractAssembler { /** * Build the dependencies tree from an assemblage as entry point. * * @param { Concrete<T> } entry An assemblage concrete class. * @returns { T } An instance of `entry` marked as singleton. */ static build<T>(entry: Concrete<T>): T; protected injectables: Map<Identifier<unknown>, Injectable<unknown>>; protected objects: Map<string | symbol, unknown>; protected globals: Map<string, any>; private initCache; /** * Context passed to internal classes. */ readonly privateContext: AssemblerPrivateContext; /** * Context passed to assemblages. */ readonly publicContext: AssemblerContext; private constructor(); /** * Dispose assembler and all its injectables. * Note that injectables' instances will be disposed only if * the are singletons. * * Transient instances must be disposed by the user. */ dispose(): void; /** * Recursively register an `Injection` tuple and its inner injected dependencies. * * @param { Injection<T> } injection The injection tuple to register. * @param { boolean | undefined } instance Set to `true` if the injection binds an instance * to an identifier (defaults to `false`). * @returns { Injectable<T> } An injectable of type `T`. */ register<T>(injection: Injection<T>, instance?: boolean): Injectable<T>; /** * Register a string or symbol identifier with an object. * * @param { string | symbol } identifier The identifier to register. * @param { T } object The object to use with the identifier. * @returns { T } The object. * * @example * ```typescript * import express from 'express'; * * @Assemblage({ * use: [ * ['express', express] * ] * }); * class MyAssemblage implements AbstractAssemblage { * constructor(@Use('express) private express) { * // Use express. * } * } * ``` */ use<T>(identifier: string | symbol, object: T): T; /** * Cache an instance to be inited with `onInit` hook * when the dependency tree will be fully resolved. * * @param { T = AbstractAssemblage } instance The built instance. * @param { Record<string, any> | undefined } configuration The configuration object. * @returns { unknown[] } The instances to be inited as this point. */ prepareInitHook<T = AbstractAssemblage>(instance: T, configuration?: Record<string, any>): unknown[]; /** * Check if `Assembler` has given identifier registered. * * @param { Identifier<T> | string | symbol } identifier An abstract or concrete class, * or a string or symbol as identifier. * @returns { boolean } `true` if dependency has been registered. */ has<T>(identifier: Identifier<T> | string | symbol): boolean; /** * Get or instantiate an assemblage for given identifier. * * @param { Identifier<T> | string | symbol } identifier The identifier to get instance from. * @param { Record<string, any> | undefined } configuration Optional configuration * object to pass to a transient assemblage's constructor. * @returns { T } An instance of Concrete<T>. */ require<T>(identifier: Identifier<T> | string | symbol, configuration?: Record<string, any>): T; /** * Return a `Concrete` class for given identifier. * * @param { Identifier<T> } identifier The dentifier to get concrete class from. * @returns { Concrete<T> | undefined } A concrete class or `undefinedù if injectable is not set. */ concrete<T>(identifier: Identifier<T>): Concrete<T> | undefined; /** * Require dependencies by tag passed in assemblage's definition. * * @param { string[] } tags The tags to get dependencies. * @returns { unknown[] } An array of instances for the given tags. If registered * identifier is not marked as 'singleton', will resolve in a new instance. */ tagged(...tags: string[]): unknown[]; addGlobal(key: string, value: any): void; /** * Get a global value by key. * * @param { string } key The key to get global value. * @returns { any | undefined } The global value or `undefined` if not set. */ global(key: string): any | undefined; /** * Size of the assembler: number of registered dependencies. */ get size(): number; } /** * Assembler public context that provide * access to some useful `Assembler` methods. */ export declare interface AssemblerContext { has: AbstractAssembler['has']; require: AbstractAssembler['require']; concrete: AbstractAssembler['concrete']; tagged: AbstractAssembler['tagged']; global: AbstractAssembler['global']; dispose: AssemblerDispose; on: AbstractAssembler['on']; once: AbstractAssembler['once']; off: AbstractAssembler['off']; events: AbstractAssembler['channels']; } /** * `Assembler` dispose method type. */ export declare type AssemblerDispose = AbstractAssembler['dispose']; /** * Assembler private context that provide * access to some `Assembler` methods * used internally. */ declare interface AssemblerPrivateContext extends AssemblerContext { register: AbstractAssembler['register']; use: AbstractAssembler['use']; prepareInitHook: AbstractAssembler['prepareInitHook']; addGlobal: AbstractAssembler['addGlobal']; emit: AbstractAssembler['emit']; addChannels: AbstractAssembler['addChannels']; removeChannels: AbstractAssembler['removeChannels']; } /** * Check at given interval if a property is defined and truthy to call an async method. * * @param { string } property The name of the class proprty to wait for. * @param { number | undefined } interval The interval in milliseconds at which the value is checked (defaults to 25 milliseconds). * @returns { Promise<void> } A promise that calls the original method when resolving. * * @deprecated This method is deprecated in `assemblerjs` package. Use the same method from `@assemblerjs/core` package. */ export declare const Await: (property: string, interval?: number) => MethodDecorator; /** * Injectable binds a concrete class to an abstract class as identifier without configuration. */ declare type BaseInjection<T> = Tuple<[Abstract<T>, Concrete<T>]>; /** * Describes a buildable object. */ declare interface Buildable<T> { identifier: Identifier<T>; concrete: Concrete<T>; instance?: T; configuration: Record<string, any>; } /** * Injection binds a concrete class to itself as identifier * and provides a configuration object that will be passed to context. */ declare type ConcreteConfiguredInjection<T> = Tuple<[ Concrete<T>, Record<string, any> ]>; /** * Injectable binds a conrete class to itself as identifier. */ declare type ConcreteInjection<T> = Tuple<[Concrete<T>]>; /** * Injects the assemblage's configuration object. */ export declare const Configuration: () => ParameterDecorator; /** * Injectable binds a concrete class to an abstract class as identifier * and provides a configuration object that will be passed to context. */ declare type ConfiguredInjection<T> = Tuple<[ Abstract<T>, Concrete<T>, Record<string, any> ]>; /** * A custom decorator that adds a function called after the original constructor * and that can wrap an `Assemblage` with its own parameters decorator (e.g. @Use, @Context, ...). * Note that it must be placed before the `Assemnblage` decorator. * The `definition` optional parameter allows passing a configuration object to the decorator. * * @param { function(definition?: ObjectLiteral): void | undefined } fn A function to execute after `super`. * Do not use arrow function here if access to `this` is required. * @param { boolean | undefined } asAssemblage If `true` decorate the class as an assemblage (defaults to `true`). * @returns A new decorator. */ export declare const ConstructorDecorator: <T extends ObjectLiteral>(fn?: (definition?: T) => void, definition?: T) => any; /** * Injects the Assembler's context. */ export declare const Context: () => ParameterDecorator; /** * Create a custom decorator that adds a function called after the original constructor * and that can wrap an `Assemblage` with its own parameters decorator (e.g. @Use, @Context, ...). * Note that it must be placed before the `Assemblage` decorator. * The `definition` optional parameter allows passing a configuration object to the decorator. * * @param { function(definition?: ObjectLiteral): void | undefined } fn A function to execute after `super`. * Do not use arrow function here if access to `this` is required. * @returns A new decorator. */ export declare const createConstructorDecorator: <T extends ObjectLiteral>(fn?: (definition?: T) => void) => any; /** * Manually decorate a class to be an `Assemblage`. * * @param { Concrete<T> } target The class to decorate. * @param { AssemblageDefinition } definition Definition of the assemblage that provides injections, etc. * @returns */ export declare const decorateAssemblage: <T>(target: Concrete<T>, definition?: AssemblageDefinition) => Concrete<T>; /** * Decorator as a wrapper function. */ export declare const decorateGlobal: (identifier: string | symbol, target: any, index: number) => void; /** * Decorator as a wrapper function. */ export declare const decorateUse: (identifier: string | symbol, target: any, index: number) => void; /** * Injects the assemblage's definition object. */ export declare const Definition: () => ParameterDecorator; /** * Injects the Assembler's 'dispose' method. */ export declare const Dispose: () => ParameterDecorator; /** * `EventChannel` extends `string`. */ export declare type EventChannel = string; /** * Describes a list of event channels as Record<EventChannel, string> */ export declare type EventChannelList = Record<EventChannel, string>; export declare class EventManager implements AbstractEventManager { private readonly listeners; private readonly onceListeners; readonly channels: Set<string>; constructor(...allowedChannels: string[]); dispose(): void; addChannels(...channels: string[]): EventManager; removeChannels(...channels: string[]): EventManager; on(channel: string, callback: Listener): EventManager; once(channel: string, callback: Listener): EventManager; off(channel: string, callback?: Listener): EventManager; emit(channel: string, ...args: any[]): EventManager; private run; private cleanChannel; } export declare const getAssemblageContext: <T>(target: Concrete<T> | Function) => AssemblerContext; export declare const getAssemblageDefinition: <T>(target: Concrete<T>) => AssemblageDefinition; export declare const getDecoratedParametersIndexes: <T>(target: Concrete<T>) => ParametersDecoratorsIndexes; /** * Injects an object passed with `string` or `symbol` identifier. */ declare const Global_2: (identifier: string | symbol) => ParameterDecorator; export { Global_2 as Global } /** * An identifier can be an abstract or a concrete class. */ export declare type Identifier<T> = Class<T>; declare class Injectable<T> implements AbstractInjectable<T> { readonly privateContext: AssemblerPrivateContext; readonly publicContext: AssemblerContext; readonly identifier: Identifier<T> | string | symbol; readonly concrete: Concrete<T>; readonly configuration: Record<string, any>; private dependenciesIds; private singletonInstance; static of<TNew>(buildable: Buildable<TNew>, privateContext: AssemblerPrivateContext, publicContext: AssemblerContext): Injectable<TNew>; private constructor(); /** * Dispose the injectable by deleting its singleton if exists * and deleting all injectable's properties. */ dispose(): void; /** * Instantiate the assemblage or get its singleton instance. * * @param { Record<string, any> } [configuration] Optional configuration to pass to * a transient assemblage. * If not provided, the global assemblage's configuration will be used. * If the assemblage is a singleton, this parameter will be ignored. * @returns { T } The assemblage instance. */ build(configuration?: Record<string, any>): T; /** * Injectable assemblage's dependencies passed as 'constructor' parameters. */ get dependencies(): (Identifier<unknown> | any)[]; /** * Metadatas passed in assemblage's definition or in its parent definition. */ get definition(): AssemblageDefinition; /** * `true` if assemblage is a singleton. */ get isSingleton(): boolean; /** * The singleton instance if this `Injectable` wraps a singleton assemblage. */ get singleton(): T | undefined; /** * Injectable assemblage's own injections defined in its decorator's definition. */ get injections(): Injection<unknown>[]; /** * Injectable assemblage's own objects (e.g. instances) injections defined in its decorator's definition. */ get objects(): InstanceInjection<unknown>[]; /** * Tags passed in assemblage's definition. */ get tags(): string[]; /** * Global injections passed in assemblage's definition. * These injections are available in all assemblages and can be used * to provide global services or utilities. */ get globals(): Record<string, any> | undefined; /** * Event channels passed in assemblage's definition. */ get events(): string[]; } /** * A generic injection tuple. */ declare type Injection<T> = BaseInjection<T> | ConfiguredInjection<T> | ConcreteInjection<T> | ConcreteConfiguredInjection<T>; /** * Injectable binds an instance of a class to an identifier (abstract or concrete). */ declare type InstanceInjection<T> = Tuple<[Identifier<T> | string | symbol, T]>; /** * Check if a given class is an `Assemblage`. * * @param { Concrete<T> } target The class to test. * @returns `true` if the class is an assemblage. */ export declare const isAssemblage: <T>(target: Concrete<T>) => boolean; /** * Describes a listener type as a function taking any number of arguments and returning `void`. */ export declare type Listener = (...args: any[]) => void | Promise<void>; export declare class ListenerCollection implements AbstractListenerCollection { /** * Class is indexable by `EventChannel`. */ [key: string]: Listener[] | any; /** * Internal listeners `Object`. */ readonly collection: Record<EventChannel, Array<Listener>>; constructor(); /** * Clean up the collection by removing all listeners and channels * and deleting listeners private property. */ dispose(): void; /** * Add a listener to the collection. * * @param { EventChannel } channel The channel to add the listener to. * @param { Listener } listener The callback function to run when the event is emitted. * @returns { ListenerCollection } This collection. */ add(channel: EventChannel, listener: Listener): ListenerCollection; /** * Add a listener to the collection. * * @param { Tuple<EventChannel, Listener> } tuple The channel and its listener in a tuple. * @returns { ListenerCollection } This collection. */ add(tuple: Tuple<[EventChannel, Listener]>): ListenerCollection; /** * Removes a listener or all listeners for a given channel. * If the channel or the provided listener does not exist, fails silently. * * @param { EventChannel } channel The channel the listener is listening to. * @param { Listener } listener The listener to remove. If not provided, remove all listeners for given channel. * @returns { ListenerCollection } This collection. */ remove(channel: string, listener?: Listener): ListenerCollection; /** * Checks if the collection includes a specific channel or listener. * * @param { EventChannel | Listener } value The channel or the listener to find in the collection. * @returns { boolean } true if the collection includes this channel / this listener, * false if not. */ has(value: EventChannel): boolean; has(value: Listener): boolean; /** * Get a specific channel listeners array or a specific listener channels array. * * @param { EventChannel | Listener } value The channel or the listener to find in the collection. * @returns { EventChannel[] | Listener[] } An array of channels or listeners. */ get(value: EventChannel): Listener[]; get(value: Listener): EventChannel[]; /** * Clear the entire collction. * * @returns { ListenerCollection } This collection. */ clear(): ListenerCollection; /** * The listeners of this collection flatten in a single array. */ get listeners(): Listener[]; /** * The listeners collection channels. */ get channels(): EventChannel[]; /** * Returns the total listeners length. */ get length(): number; /** * Allows iterating over listeners in specific channel with 'for... of...' loop. * * @returns { Listener } A listener function. * * @example * // Iterates listeners in a specific channel. * for (const listener of myListenerCollection) { * // Calls the registered listener. * listener(); * } */ [Symbol.iterator](): Iterator<EventChannel>; } export declare interface ObjectLiteral { [key: string]: any; } declare interface ParametersDecoratorsIndexes { Context: number[]; Definition: number[]; Configuration: number[]; Dispose: number[]; Use: number[]; Global: number[]; } /** * Internal `Reflect` parameters decorators' indexes in constructor. */ export declare enum ReflectParamIndex { Context = "assembler:context.param.index", Dispose = "assembler:dispose.param.index", Definition = "assemblage:definition.param.index", Configuration = "assemblage:configuration.param.index", Use = "assemblage:use.param.index", Global = "assemblage:global.param.index" } /** * Internal `Reflect` parameters decorators' values. */ export declare enum ReflectParamValue { UseIdentifier = "assemblage:use.param.value", GlobalIdentifier = "assemblage:global.param.value" } /** * Injects an object passed with `string` or `symbol` identifier. */ export declare const Use: (identifier: string | symbol) => ParameterDecorator; export { }