DescriptorBuilder.ts
196 lines
| 6.3 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r4 | import { RegistrationBuilder, LifetimeContainer, ConfigurableKeys, IDescriptorBuilder, Ref, Resolved, DepsMap } from "./interfaces"; | ||
|
|
r1 | import { Descriptor, ILifetime, ActivationType } from "./interfaces"; | ||
| import { DescriptorImpl, RegistrationOverridesMap } from "./DescriptorImpl"; | ||||
|
|
r0 | import { LifetimeManager } from "./LifetimeManager"; | ||
|
|
r4 | import { each, isKey, isPromise, isString, key, oid } from "./traits"; | ||
|
|
r0 | |||
|
|
r1 | /** | ||
| * @template {S} Карта доступных зависимостей, как правило `ContainerServices` | ||||
| * @template {T} Тип сервиса | ||||
| */ | ||||
|
|
r4 | export class DescriptorBuilder<S extends object, T, R extends object = object> implements IDescriptorBuilder<S, T, R> { | ||
|
|
r1 | private readonly _lifetimeContainer: LifetimeContainer; | ||
|
|
r0 | private readonly _cb: (d: Descriptor<S, T>) => void; | ||
|
|
r1 | private readonly _eb: (err: unknown) => void; | ||
|
|
r0 | |||
|
|
r4 | private readonly _refs: DepsMap<key, keyof S>; | ||
|
|
r1 | private _lifetime = LifetimeManager.empty<T>(); | ||
|
|
r0 | |||
|
|
r1 | private _overrides: RegistrationOverridesMap<S>; | ||
|
|
r0 | |||
| private _cleanup?: (item: T) => void; | ||||
|
|
r4 | private _factory?: (refs: R) => T; | ||
|
|
r0 | |||
| private _pending = 1; | ||||
| private _failed = false; | ||||
|
|
r1 | private _finalized = false; | ||
| /** | ||||
| * Creates new DescriptorBuilder. Accepts a lifetime container for resolving "container" | ||||
| * lifetime. | ||||
| * | ||||
| * @param container The lifetime container is the container where the service is to be registered. | ||||
| * @param cb The callback to receive the built service descriptor | ||||
| * @param eb The callback to receive the error due | ||||
| */ | ||||
| constructor(container: LifetimeContainer, cb: (d: Descriptor<S, T>) => void, eb: (err: unknown) => void) { | ||||
| this._lifetimeContainer = container; | ||||
|
|
r0 | this._cb = cb; | ||
| this._eb = eb; | ||||
|
|
r1 | this._overrides = {}; | ||
|
|
r4 | this._refs = {}; | ||
|
|
r0 | } | ||
|
|
r4 | wants<X extends { [k in Exclude<key, keyof R>]: keyof S | Ref<keyof S, boolean, unknown>; }>(refs: X): | ||
| IDescriptorBuilder<S, T, R & { | ||||
| [k in keyof X]: | ||||
| X[k] extends keyof S ? NonNullable<S[X[k]]> : | ||||
| X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> : | ||||
| never; | ||||
| }> { | ||||
| each(refs, (v, k) => this._refs[k] = v); | ||||
| return this as IDescriptorBuilder<S, T, R & { | ||||
| [k in keyof X]: | ||||
| X[k] extends keyof S ? NonNullable<S[X[k]]> : | ||||
| X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> : | ||||
| never; | ||||
| }>; | ||||
| } | ||||
| factory(f: (refs: R) => T): void { | ||||
| this._assertBuilding(); | ||||
| this._factory = f; | ||||
| this._finalize(); | ||||
| this._complete(); | ||||
| } | ||||
|
|
r0 | |||
|
|
r1 | private _assertBuilding() { | ||
| if (this._finalized) | ||||
| throw new Error("The descriptor builder is finalized"); | ||||
|
|
r0 | } | ||
|
|
r1 | private _finalize() { | ||
| this._finalized = true; | ||||
| } | ||||
|
|
r0 | |||
|
|
r1 | override<K extends ConfigurableKeys<S>>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this; | ||
| override<K extends ConfigurableKeys<S>>(services: { [k in K]: RegistrationBuilder<S, NonNullable<S[k]>> }): this; | ||||
| override<K extends ConfigurableKeys<S>>(nameOrServices: K | { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }, builder?: RegistrationBuilder<S, NonNullable<S[K]>>): this { | ||||
| this._assertBuilding(); | ||||
|
|
r0 | const guard = (v: void | Promise<void>) => { | ||
| if (isPromise(v)) | ||||
| v.catch(err => this._fail(err)); | ||||
| }; | ||||
|
|
r1 | if (isKey(nameOrServices)) { | ||
|
|
r0 | if (builder) { | ||
| this._defer(); | ||||
|
|
r1 | const d = new DescriptorBuilder<S, NonNullable<S[K]>>( | ||
| this._lifetimeContainer, | ||||
|
|
r0 | result => { | ||
|
|
r1 | this._overrides[nameOrServices] = result; | ||
|
|
r0 | this._complete(); | ||
| }, | ||||
| err => this._fail(err) | ||||
| ); | ||||
| try { | ||||
| guard(builder(d)); | ||||
| } catch (err) { | ||||
| this._fail(err); | ||||
| } | ||||
| } | ||||
| } else { | ||||
| each(nameOrServices, (v, k) => this.override(k, v)); | ||||
| } | ||||
| return this; | ||||
| } | ||||
| lifetime(lifetime: "singleton", typeId: string): this; | ||||
|
|
r1 | lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this; | ||
| lifetime(lifetime: ILifetime<T> | ActivationType, typeId?: string): this { | ||||
| this._assertBuilding(); | ||||
|
|
r0 | if (isString(lifetime)) { | ||
| this._lifetime = this._resolveLifetime(lifetime, typeId); | ||||
| } else { | ||||
| this._lifetime = lifetime; | ||||
| } | ||||
| return this; | ||||
| } | ||||
| cleanup(cb: (item: T) => void): this { | ||||
|
|
r1 | this._assertBuilding(); | ||
|
|
r0 | this._cleanup = cb; | ||
| return this; | ||||
| } | ||||
|
|
r4 | /*factory(f: (resolve: Resolver<S>) => T): void { | ||
|
|
r1 | this._assertBuilding(); | ||
|
|
r0 | this._factory = f; | ||
|
|
r1 | this._finalize(); | ||
|
|
r0 | this._complete(); | ||
|
|
r4 | }*/ | ||
|
|
r0 | |||
| value(v: T): void { | ||||
|
|
r1 | this._assertBuilding(); | ||
|
|
r0 | this._cb({ | ||
| activate() { | ||||
| return v; | ||||
| } | ||||
| }); | ||||
|
|
r1 | this._finalize(); | ||
|
|
r0 | } | ||
|
|
r1 | _resolveLifetime<T>(activation: ActivationType, typeId?: string | object): ILifetime<T> { | ||
|
|
r0 | switch (activation) { | ||
| case "container": | ||||
|
|
r1 | return LifetimeManager.containerLifetime(this._lifetimeContainer); | ||
|
|
r0 | case "hierarchy": | ||
| return LifetimeManager.hierarchyLifetime(); | ||||
| case "context": | ||||
| return LifetimeManager.contextLifetime(); | ||||
|
|
r1 | case "singleton": { | ||
|
|
r0 | if (!typeId) | ||
| throw Error("The singleton activation requires a typeId"); | ||||
| const _oid = isString(typeId) ? typeId : oid(typeId); | ||||
| return LifetimeManager.singletonLifetime(_oid); | ||||
|
|
r1 | } | ||
|
|
r0 | default: | ||
| return LifetimeManager.empty(); | ||||
| } | ||||
| } | ||||
| _defer() { | ||||
| this._pending++; | ||||
| } | ||||
| _complete() { | ||||
| if (--this._pending === 0) { | ||||
| if (!this._factory) | ||||
| throw new Error("The factory must be specified"); | ||||
| this._cb(new DescriptorImpl<S, T>({ | ||||
| lifetime: this._lifetime, | ||||
| factory: this._factory, | ||||
| overrides: this._overrides, | ||||
| cleanup: this._cleanup | ||||
| })); | ||||
| } | ||||
| } | ||||
|
|
r1 | _fail(err: unknown) { | ||
|
|
r0 | if (!this._failed) { | ||
| this._failed = true; | ||||
| this._eb.call(undefined, err); | ||||
| } | ||||
| } | ||||
| } | ||||
