import { ContainerBuilder } from "./ContainerBuilder"; import { key } from "./traits"; export interface IDestroyable { destroy(): void; } /** * @template S Карта доступных зависимостей */ export interface Resolver { /** * Функция для разрешения зависимостей, поддерживает создание фабричных методов, * отложенную активацию и значение по-умолчанию для сервисов * @template K Ключ сервиса из {@linkcode S} * @template O Тип параметра {@linkcode opts} используется для выведения типа * возвращаемого значения. * @param name Ключ сервиса, который будет разрешен. * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация, * будет возвращен фабричный метод для получения зависимости. Если не указан, * то считается `false`. * @param {any=} opts.default Значение по умолчанию, если в контейнере указанный * сервис не зарегистрирован * @returns Либо фабричный метод для получения зависимости, либо значение зависимости * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию */ (name: K, opts?: O): () => NonNullable | InferDefault; (name: K, opts?: O): NonNullable | InferDefault; } export type DepsMap = { [k in key]: Refs | keyof S; }; export type Refs = { [k in keyof S]: Ref; }[keyof S]; export type Ref = { /** The name of the service */ name: K; /** Make a lazy reference, the resolved dependency will be a function */ lazy?: boolean; /** The default value for the case where the service isn't defined. * When specified the dependency becomes optional, the default value can be * `null` or `undefined` */ default?: D | null }; export type Lazy = L extends true ? () => T : T; /** Возвращает тип свойства `default` в типе {@link T} */ export type InferDefault = T extends { default: infer D } ? D : never; export type InferLazy = R extends { lazy: infer L } ? L extends true ? true : false : false; export type Resolve = R extends keyof S ? NonNullable : R extends Ref ? K extends keyof S ? Lazy | InferDefault, InferLazy> : never : never; /** * Интерфейс для конфигурирования сервиса в контейнере. Конфигурирование сервиса * состоит из настройки различных параметров вызовами методов {@linkcode wants}, * {@linkcode lifetime}, {@linkcode override}, {@linkcode cleanup}. Завершение настройки * сервиса осуществляется вызовом одного из методов {@linkcode factory} либо * {@linkcode value}. * * @template S Карта сервисов контейнера, доступных при описании дескриптора * @template T Тип сервиса * @template R Карта зависимостей, которая передается параметром фабрике * @template U Имена пользовательских сервисов, доступных для переопределения */ export interface IDescriptorBuilder { /** Указывает фабрика для создания экземпляра сервиса, фабрика передается * в виде параметра. При вызове фабрике будет передан объект с зависимостями, * которые были предварительно указаны вызовами метода `wants(...)` * * Вызов данного метода завершает конфигурирование сервиса. * * @param f Фабрика для создания экземпляра сервиса. */ factory(f: (refs: R) => NonNullable): void; /** * Используется для указания зависимостей, которые потребуются фабричному * методу при создании нового экземпляра сервиса. Данный метод может быть * вызван несколько раз подряд, при этом вызовы этого метода имеют * кумулятивный эффект. * * @template X Тип объекта с зависимостями, которые требуется получить при * создании экземпляра фабрики при помощи фабричного метода. * @param refs Объект с описанием зависимостей * @returns Возвращает дескриптор сервиса, в котором указаны необходимые * зависимости */ wants & Record>(refs: X): IDescriptorBuilder; }, U> override(name: K, builder: BuildDescriptorFn): this; override>(services: X): this; lifetime(lifetime: "singleton", typeId: string | number | object): this; lifetime(lifetime: ILifetime | Exclude): this; /** Указывает функцию для освобождения экземпляра сервиса для случаев, когда * время жизни привязано к контейнеру. */ cleanup(cb: (item: T) => void): this; /** * Регистрирует в контейнере постоянное значение в качестве реализации сервиса. * * @param v Экземпляр реализации сервиса. */ value(v: NonNullable): void; } export type BuildDescriptorFn = (d: IDescriptorBuilder, U>) => void; /** * Конфигурация контейнера, состоит из набора функций, которые выполняют конфигурацию. * * Все параметры конфигурации являются обязательными, если требуется ввести * необязательные параметры, то нужно ограничить параметр типа {@linkcode K} * * @template S Сервисы доступные в контейнере * @template K Сервисы участвующие в конфигурации */ export type ConfigurationMap = { [k in K]-?: BuildDescriptorFn }; export type ConfigurationMapConstraint = { [k in X]-?: k extends U ? BuildDescriptorFn : never; }; /** * The type constraint useful to restrict type parameters to prevent defining * the services with the {@link ContainerKeys} names. * * The constraint doesn't exclude using this keys but declares them as `never` * which effectively will lead using this keys to the error. */ export type ContainerServicesConstraint = { [k in keyof S]: k extends ContainerKeys ? never : S[k]; }; export interface Descriptor { /** This flags indicates that this registration can be replaced or overridden. */ readonly configurable?: boolean; /** If specified signals the activation context that a new service scope * should be created to isolate service overrides. */ readonly hasOverrides?: boolean; activate(context: IActivationContext): NonNullable; } /** The context used to initialize lifetime instance {@linkcode ILifetime} */ export interface ILifetimeContext { ownerSlot(slotId: string | number): ILifetimeSlot; contextSlot(slotId: string | number): ILifetimeSlot; containerSlot(slotId: string | number): ILifetimeSlot; } export interface IActivationContext extends ILifetimeContext, ServiceLocator { register(name: K, service: DescriptorMap[K]): void; fail(error: unknown): never; selfContainer(): ServiceLocator; createChildContainer(): IContainerBuilder>; } /** * Descriptors map for the specified services {@linkcode S}. All entries are * optional regardless the required or optional services in the original map. * * @template S Сервисы контекста активации * @template U Карта сервисов которые создаются дескрипторами */ export type DescriptorMap = { [k in keyof S]?: Descriptor; }; type ContainerKeys = keyof ContainerProvided; export type ContainerProvided = { container: ServiceLocator>; childContainer: IContainerBuilder; }; /** * Таблица сервисов, которые предоставляет контейнер. * * Сервисы, предоставляемые контейнером не могут быть null или undefined. */ export type ContainerServices = { [k in (keyof S) | ContainerKeys]: k extends ContainerKeys ? ContainerProvided[k] : k extends keyof S ? S[k] : never }; /** * Returns the service declared in the type map {@link S}. * * */ export interface ServiceLocator { resolve(name: K): NonNullable; resolve(name: K, def: T): NonNullable | T; } export interface IContainerBuilder { createServiceBuilder(name: K): IDescriptorBuilder, U>; build(): ServiceLocator; } export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call"; /** * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет * свой собственный объект `ILifetime`, который создается при первой активации */ export type ILifetime = (context: ILifetimeContext) => ILifetimeSlot; export interface ILifetimeSlot { readonly has: () => boolean; readonly get: () => T; readonly initialize: () => void; readonly store: (item: T, cleanup?: (item: T) => void) => void; readonly remove: () => void; readonly cleanup: () => void; } export interface ILifetimeManager extends IDestroyable { slot(id: string | number): ILifetimeSlot; } export type ExtractRequired = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] }; export type ExtractRequiredKeys = { [p in K]-?: undefined extends T[p] ? never : p }[K];