##// END OF EJS Templates
WIP lifetime services
WIP lifetime services

File last commit:

r12:94f233c23aa4 default
r12:94f233c23aa4 default
Show More
interfaces.ts
264 lines | 11.4 KiB | video/mp2t | TypeScriptLexer
import { key } from "./traits";
export interface IDestroyable {
destroy(): void;
}
/**
* @template S Карта доступных зависимостей
*/
export interface Resolver<S> {
/**
* Функция для разрешения зависимостей, поддерживает создание фабричных методов,
* отложенную активацию и значение по-умолчанию для сервисов
* @template K Ключ сервиса из {@linkcode S}
* @template O Тип параметра {@linkcode opts} используется для выведения типа
* возвращаемого значения.
* @param name Ключ сервиса, который будет разрешен.
* @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
* будет возвращен фабричный метод для получения зависимости. Если не указан,
* то считается `false`.
* @param {any=} opts.default Значение по умолчанию, если в контейнере указанный
* сервис не зарегистрирован
* @returns Либо фабричный метод для получения зависимости, либо значение зависимости
* @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
*/
<K extends keyof S, O extends { lazy: true; }>(name: K, opts?: O): () => NonNullable<S[K]> | InferDefault<O>;
<K extends keyof S, O extends { lazy?: false; }>(name: K, opts?: O): NonNullable<S[K]> | InferDefault<O>;
}
export type DepsMap<S> = {
[k in key]: Refs<S> | keyof S;
};
export type Refs<S> = {
[k in keyof S]: Ref<k, S[k]>;
}[keyof S];
export type Ref<K extends key, D> = {
/** 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<T, L extends boolean> = L extends true ? () => T : T;
/** Возвращает тип свойства `default` в типе {@link T} */
export type InferDefault<T> = T extends { default: infer D } ? D : never;
export type InferLazy<R> = R extends { lazy: infer L } ?
L extends true ? true : false :
false;
export type Resolve<S, R> =
R extends keyof S ? NonNullable<S[R]> :
R extends Ref<infer K, unknown> ?
K extends keyof S ?
Lazy<NonNullable<S[K]> | InferDefault<R>, InferLazy<R>> :
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<S, T, R, U extends keyof S> {
/** Указывает фабрика для создания экземпляра сервиса, фабрика передается
* в виде параметра. При вызове фабрике будет передан объект с зависимостями,
* которые были предварительно указаны вызовами метода `wants(...)`
*
* Вызов данного метода завершает конфигурирование сервиса.
*
* @param f Фабрика для создания экземпляра сервиса.
*/
factory(f: (refs: R) => NonNullable<T>): void;
/**
* Используется для указания зависимостей, которые потребуются фабричному
* методу при создании нового экземпляра сервиса. Данный метод может быть
* вызван несколько раз подряд, при этом вызовы этого метода имеют
* кумулятивный эффект.
*
* @template X Тип объекта с зависимостями, которые требуется получить при
* создании экземпляра фабрики при помощи фабричного метода.
* @param refs Объект с описанием зависимостей
* @returns Возвращает дескриптор сервиса, в котором указаны необходимые
* зависимости
*/
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]>;
}, U>
override<K extends U>(name: K, builder: BuildDescriptorFn<S, S[K], U>): this;
override<X extends ConfigurationMapConstraint<S, U, keyof X>>(services: X): this;
lifetime(lifetime: "singleton", typeId: string | number | object): this;
lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
/** Указывает функцию для освобождения экземпляра сервиса для случаев, когда
* время жизни привязано к контейнеру.
*/
cleanup(cb: (item: T) => void): this;
/**
* Регистрирует в контейнере постоянное значение в качестве реализации сервиса.
*
* @param v Экземпляр реализации сервиса.
*/
value(v: NonNullable<T>): void;
}
export type BuildDescriptorFn<S, T, U extends keyof S> = (d: IDescriptorBuilder<S, T, Record<never, never>, U>) => void;
/**
* Конфигурация контейнера, состоит из набора функций, которые выполняют конфигурацию.
*
* Все параметры конфигурации являются обязательными, если требуется ввести
* необязательные параметры, то нужно ограничить параметр типа {@linkcode K}
*
* @template S Сервисы доступные в контейнере
* @template K Сервисы участвующие в конфигурации
*/
export type ConfigurationMap<S, K extends keyof S, U extends keyof S> = {
[k in K]-?: BuildDescriptorFn<S, S[k], U>
};
export type ConfigurationMapConstraint<S, U extends keyof S, X extends string | number | symbol> = {
[k in X]-?: k extends U ? BuildDescriptorFn<S, S[k], U> : 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<S> = {
[k in keyof S]: k extends ContainerKeys ? never : S[k];
};
export interface Descriptor<S, T> {
/** 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<S>): NonNullable<T>;
}
/** The context used to initialize lifetime instance {@linkcode ILifetime} */
export interface ILifetimeContext {
contextSlot<T>(slotId: string | number): ILifetimeSlot<T>;
containerSlot<T>(slotId: string | number): ILifetimeSlot<T>;
}
export interface IActivationContext<S> extends ILifetimeContext, ServiceLocator<S> {
register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]): void;
fail(error: unknown): never;
}
/**
* 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<S> = {
[k in keyof S]?: Descriptor<S, S[k]>;
};
type ContainerKeys = keyof ContainerProvided<object>;
export type ContainerProvided<S extends ContainerServicesConstraint<S>> = {
container: ServiceLocator<ContainerServices<S>>;
childContainer: IContainerBuilder<ContainerServices<S>, Exclude<keyof S, ContainerKeys>>;
};
/**
* Таблица сервисов, которые предоставляет контейнер.
*
* Сервисы, предоставляемые контейнером не могут быть null или undefined.
*/
export type ContainerServices<S extends ContainerServicesConstraint<S>> = {
[k in keyof S | ContainerKeys]:
k extends ContainerKeys ? ContainerProvided<S>[k] :
k extends keyof S ? S[k] : never
};
/**
* Returns the service declared in the type map {@link S}.
*
*
*/
export interface ServiceLocator<S> {
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 IContainerBuilder<S, U extends keyof S> {
createServiceBuilder<K extends U>(name: K):
IDescriptorBuilder<S, S[K], Record<never, never>, U>;
build(): ServiceLocator<S>;
}
export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
/**
* Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
* свой собственный объект `ILifetime`, который создается при первой активации
*/
export type ILifetime<T> = (context: ILifetimeContext) => ILifetimeSlot<NonNullable<T>>;
export interface ILifetimeSlot<T> {
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<T>(id: string | number): ILifetimeSlot<T>;
}
export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
export type ExtractRequiredKeys<T, K extends keyof T = keyof T> = { [p in K]-?: undefined extends T[p] ? never : p }[K];