assemblerjs
Version:
A general purpose Dependency Injection library for node and browser.
661 lines (614 loc) • 26.6 kB
TypeScript
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 { }