import {DependencyKey, Instantiable, StaticClass, TypedDependencyKey} from './types' import NamedFactory from './factory/NamedFactory' import {AbstractFactory} from './factory/AbstractFactory' import {Factory} from './factory/Factory' import {ClosureFactory} from './factory/ClosureFactory' import {Collection, collect} from '../util/collection/Collection' import {Subscription, Unsubscribe} from '../util/support/BehaviorSubject' /** Simple type alias for a callback to a container's onResolve method. */ export type ContainerResolutionCallback = (() => unknown) | ((t: T) => unknown) /** * Blueprint for newly-created containers. * * This is used to allow global helpers like `@Singleton()` * or `@CLIDirective()` while still supporting multiple * global Container instances at once. */ export class ContainerBlueprint { private static instance?: ContainerBlueprint public static getContainerBlueprint(): ContainerBlueprint { if ( !this.instance ) { this.instance = new ContainerBlueprint() } return this.instance } protected factories: Collection<(() => AbstractFactory)> = collect() protected constructableFactories: Collection, any>> = collect() protected resolutionCallbacks: Collection<{key: TypedDependencyKey, callback: ContainerResolutionCallback}> = collect() /** * Register some factory class with the container. Should take no construction params. * @param factory */ registerFactory(factory: StaticClass, any>): this { this.constructableFactories.push(factory) return this } /** * Register a basic instantiable class as a standard Factory with this container, * identified by a string name rather than static class. * @param {string} name - unique name to identify the factory in the container * @param {Instantiable} dependency */ registerNamed(name: string, dependency: Instantiable): this { this.factories.push(() => new NamedFactory(name, dependency)) return this } /** * Register a basic instantiable class as a standard Factory with this container. * @param {Instantiable} dependency */ register(dependency: Instantiable): this { this.factories.push(() => new Factory(dependency)) return this } /** * Register a producer function as a ClosureFactory with this container. * @param key * @param producer */ registerProducer(key: DependencyKey, producer: () => any): this { this.factories.push(() => new ClosureFactory(key, producer)) return this } /** * Get an array of factory instances in the blueprint. */ resolve(): AbstractFactory[] { return this.factories.map(x => x()).all() } /** * Subscribe to new factories being registered. * Used by `Container` implementations to listen for factories being registered after the container is realized. * @param sub */ resolve$(sub: Subscription<() => AbstractFactory>): Unsubscribe { return this.factories.push$(sub) } /** * Register an onResolve callback to be added to all newly-created containers. * @param key * @param callback */ onResolve(key: TypedDependencyKey, callback: ContainerResolutionCallback): this { this.resolutionCallbacks.push({ key, callback, }) return this } /** * Get an array of static Factory classes that need to be instantiated by * the container itself. */ resolveConstructable(): StaticClass, any>[] { return this.constructableFactories.all() } /** * Subscribe to new constructable factories being registered. * Used by `Container` implementations to listen for factories registered after the container is realized. * @param sub */ resolveConstructable$(sub: Subscription, any>>): Unsubscribe { return this.constructableFactories.push$(sub) } /** * Get an array of DependencyKey-callback pairs to register with new containers. */ resolveResolutionCallbacks(): ({key: TypedDependencyKey, callback: ContainerResolutionCallback})[] { return this.resolutionCallbacks.all() } /** * Subscribe to new resolution callbacks being registered. * Used by `Container` implementations to listen for callbacks registered after the container is realized. * @param sub */ resolveResolutionCallbacks$(sub: Subscription<{key: TypedDependencyKey, callback: ContainerResolutionCallback}>): Unsubscribe { return this.resolutionCallbacks.push$(sub) } }