|
|
import { key } from "./traits";
|
|
|
|
|
|
export interface IDestroyable {
|
|
|
destroy(): void;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @template S Карта доступных зависимостей
|
|
|
*/
|
|
|
export interface Resolver<S extends object> {
|
|
|
/**
|
|
|
* Функция для разрешения зависимостей, поддерживает создание фабричных методов,
|
|
|
* отложенную активацию и значение по-умолчанию для сервисов
|
|
|
* @template K Ключ сервиса из {@link S}
|
|
|
* @template O Тип параметра {@link opts} используется для выведения типа
|
|
|
* возвращаемого значения.
|
|
|
* @param name Ключ сервиса, который будет разрешен.
|
|
|
* @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
|
|
|
* будет возвращен фабричный метод для получения зависимости. Если не указан,
|
|
|
* то считается `false`.
|
|
|
* @param {any=} opts.default Значение по умолчанию, если в контейнере указанный
|
|
|
* сервис не зарегистрирован
|
|
|
* @returns Либо фабричный метод для получения зависимости, либо значение зависимости
|
|
|
* @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
|
|
|
*/
|
|
|
<K extends keyof S, O extends { lazy: true; default?: unknown }>(name: K, opts?: O): () => (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
|
|
|
<K extends keyof S, O extends { lazy?: false; default?: unknown }>(name: K, opts?: O): (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
|
|
|
}
|
|
|
|
|
|
export type DepsMap<S extends object> = {
|
|
|
[k in key]: Refs<S>
|
|
|
};
|
|
|
|
|
|
export type Refs<S extends object> = {
|
|
|
[k in keyof S]: Ref<k, boolean, S[k]>;
|
|
|
}[keyof S] | keyof S;
|
|
|
|
|
|
export type Ref<K extends key, L extends boolean, D> = { name: K, lazy?: L, default?: D | null };
|
|
|
|
|
|
export type Lazy<T, L extends boolean> = L extends true ? () => T : T;
|
|
|
|
|
|
export type InferDefault<T> = T extends { default: infer D } ? D : never;
|
|
|
|
|
|
export type Resolve<S extends object, R> =
|
|
|
R extends keyof S ? NonNullable<S[R]> :
|
|
|
R extends Ref<infer K, infer L, unknown> ?
|
|
|
K extends keyof S ? Lazy<NonNullable<S[K]> | InferDefault<R>, L> :
|
|
|
never:
|
|
|
never;
|
|
|
|
|
|
export interface IDescriptorBuilder<S extends object, T, R extends object, O extends keyof S> {
|
|
|
|
|
|
/**
|
|
|
*
|
|
|
* @param f
|
|
|
*/
|
|
|
factory(f: (refs: R) => T): void;
|
|
|
|
|
|
wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X):
|
|
|
IDescriptorBuilder<S, T, R & {
|
|
|
[k in keyof X]: Resolve<S, X[k]>;
|
|
|
}, O>
|
|
|
|
|
|
override<K extends O>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
|
|
|
override<K extends O>(services: { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }): this;
|
|
|
|
|
|
lifetime(lifetime: "singleton", typeId: string | number | object): this;
|
|
|
lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
|
|
|
|
|
|
cleanup(cb: (item: T) => void): this;
|
|
|
|
|
|
value(v: T): void;
|
|
|
}
|
|
|
|
|
|
export type RegistrationBuilder<S extends object, T> = (d: IDescriptorBuilder<S, T, object, ConfigurableKeys<S>>) => void;
|
|
|
|
|
|
export type RegistrationBuildersMap<S extends Configurable<S>, K extends keyof S = keyof S> = {
|
|
|
[k in K]-?: RegistrationBuilder<ContainerServices<S>, NonNullable<S[k]>>
|
|
|
};
|
|
|
|
|
|
export interface Descriptor<S extends object, T> {
|
|
|
activate(context: IActivationContext<S>): T;
|
|
|
}
|
|
|
|
|
|
export interface IActivationContext<S extends object> extends ServiceLocator<S> {
|
|
|
createLifetime<T>(): ILifetime<T>;
|
|
|
|
|
|
createContainerLifetime<T>(): ILifetime<T>;
|
|
|
}
|
|
|
|
|
|
export type RegistrationMap<S extends object, K extends keyof S = keyof S> = {
|
|
|
[k in K]-?: Descriptor<S, S[k]>;
|
|
|
};
|
|
|
|
|
|
export interface ContainerProvided<S extends Configurable<S>> {
|
|
|
container: ServiceLocator<ContainerServices<S>>;
|
|
|
|
|
|
childContainer: IContainerBuilder<S>;
|
|
|
}
|
|
|
|
|
|
export type Configurable<S> = { [k in keyof S & (keyof ContainerProvided<never>)]: never; };
|
|
|
|
|
|
export type ProvidedKeys = keyof ContainerProvided<never>;
|
|
|
|
|
|
export type ContainerServices<S extends Configurable<S>> = S & ContainerProvided<S>;
|
|
|
|
|
|
export type ConfigurableKeys<S extends object> = Exclude<keyof S, ProvidedKeys>;
|
|
|
|
|
|
export type ConfigurableServices<S extends object> = Pick<S, ConfigurableKeys<S>>;
|
|
|
|
|
|
export type ContainerKeys<S extends Configurable<S>> = keyof S | keyof ContainerProvided<never>;
|
|
|
|
|
|
export interface ServiceLocator<S extends object> {
|
|
|
resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
|
|
|
resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
|
|
|
}
|
|
|
|
|
|
export interface LifetimeContainer {
|
|
|
createLifetime<T>(): ILifetime<T>;
|
|
|
}
|
|
|
|
|
|
export interface ServiceContainer<S extends Configurable<S>> extends
|
|
|
ServiceLocator<ContainerServices<S>>,
|
|
|
IDestroyable {
|
|
|
|
|
|
createChildContainer(): IContainerBuilder<S>;
|
|
|
}
|
|
|
|
|
|
export interface IContainerBuilder<S extends Configurable<S>> {
|
|
|
createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<S, NonNullable<S[K]>, object, keyof S>;
|
|
|
|
|
|
build(): ServiceContainer<S>;
|
|
|
}
|
|
|
|
|
|
|
|
|
export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
|
|
|
|
|
|
/**
|
|
|
* Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
|
|
|
* свой собственный объект `ILifetime`, который создается при первой активации
|
|
|
*/
|
|
|
export interface ILifetime<T> {
|
|
|
/** Проверяет, что уже создан экземпляр объекта */
|
|
|
has(): boolean;
|
|
|
|
|
|
get(): T;
|
|
|
|
|
|
initialize(context: IActivationContext<object>): void;
|
|
|
|
|
|
store(item: T, cleanup?: (item: T) => void): void;
|
|
|
}
|
|
|
|
|
|
export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
|
|
|
|
|
|
|