##// END OF EJS Templates
Corrected method signatures
cin -
r6:5683fe88e2a5 default
parent child
Show More
@@ -1,46 +1,46
1 1 import { Container } from "./Container";
2 2 import { DescriptorBuilder } from "./DescriptorBuilder";
3 3 import { Configurable, ConfigurableKeys, ContainerServices, Descriptor, IContainerBuilder, IDescriptorBuilder, RegistrationMap, ServiceContainer } from "./interfaces";
4 4 import { LifetimeManager } from "./LifetimeManager";
5 5
6 6 export class ContainerBuilder<S extends Configurable<S>> implements IContainerBuilder<S>{
7 7
8 8 private _pending = 1;
9 9
10 10 private readonly _services: Partial<RegistrationMap<ContainerServices<S>>>;
11 11
12 12 private readonly _lifetimeManager = new LifetimeManager();
13 13
14 14 constructor(parentServices?: object) {
15 15 this._services = Object.create(parentServices ? parentServices : null) as object;
16 16 }
17 17
18 createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<S, NonNullable<S[K]>, object> {
18 createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<ContainerServices<S>, NonNullable<S[K]>, object, keyof S> {
19 19 return new DescriptorBuilder(this._lifetimeManager, this._register(name), this._fail);
20 20 }
21 21
22 22 build(): ServiceContainer<S> {
23 23 this._assertBuilding();
24 24 if(!this._complete())
25 25 throw new Error("The configuration didn't complete.");
26 26 return new Container(this._services, this._lifetimeManager);
27 27 }
28 28
29 29 private readonly _register = <K extends ConfigurableKeys<S>>(name: K) => (descriptor: Descriptor<S, NonNullable<S[K]>>) => {
30 30 this._complete();
31 31 this._services[name] = descriptor;
32 32 };
33 33
34 34 private readonly _fail = (ex: unknown) => {
35 35
36 36 };
37 37
38 38 private _assertBuilding() {
39 39 throw new Error("The descriptor builder is finalized");
40 40 }
41 41
42 42 private _complete() {
43 43 return !(--this._pending);
44 44 }
45 45
46 46 } No newline at end of file
@@ -1,189 +1,189
1 1 import { RegistrationBuilder, LifetimeContainer, ConfigurableKeys, IDescriptorBuilder, Ref, Resolved, DepsMap } from "./interfaces";
2 2 import { Descriptor, ILifetime, ActivationType } from "./interfaces";
3 3 import { DescriptorImpl, RegistrationOverridesMap } from "./DescriptorImpl";
4 4 import { LifetimeManager } from "./LifetimeManager";
5 5 import { each, isKey, isPromise, isString, key, oid } from "./traits";
6 6
7 7 /**
8 8 * @template {S} Карта доступных зависимостей, как правило `ContainerServices`
9 9 * @template {T} Тип сервиса
10 10 */
11 export class DescriptorBuilder<S extends object, T, R extends object = object> implements IDescriptorBuilder<S, T, R> {
11 export class DescriptorBuilder<S extends object, T, R extends object, O extends keyof S> implements IDescriptorBuilder<S, T, R, O> {
12 12 private readonly _lifetimeManager: LifetimeManager;
13 13 private readonly _cb: (d: Descriptor<S, T>) => void;
14 14
15 15 private readonly _eb: (err: unknown) => void;
16 16
17 17 private readonly _refs: DepsMap<key, keyof S>;
18 18
19 19 private _lifetime = LifetimeManager.empty<T>();
20 20
21 21 private _overrides: RegistrationOverridesMap<S>;
22 22
23 23 private _cleanup?: (item: T) => void;
24 24
25 25 private _factory?: (refs: R) => T;
26 26
27 27 private _pending = 1;
28 28
29 29 private _failed = false;
30 30
31 31 private _finalized = false;
32 32
33 33 /**
34 34 * Creates new DescriptorBuilder. Accepts a lifetime container for resolving "container"
35 35 * lifetime.
36 36 *
37 37 * @param lifetimeManager The lifetime container is the container where the service is to be registered.
38 38 * @param cb The callback to receive the built service descriptor
39 39 * @param eb The callback to receive the error due
40 40 */
41 41 constructor(lifetimeManager: LifetimeManager, cb: (d: Descriptor<S, T>) => void, eb: (err: unknown) => void) {
42 42 this._lifetimeManager = lifetimeManager;
43 43 this._cb = cb;
44 44 this._eb = eb;
45 45 this._overrides = {};
46 46 this._refs = {};
47 47 }
48 48 wants<X extends { [k in Exclude<key, keyof R>]: keyof S | Ref<keyof S, boolean, unknown>; }>(refs: X):
49 49 IDescriptorBuilder<S, T, R & {
50 50 [k in keyof X]:
51 51 X[k] extends keyof S ? NonNullable<S[X[k]]> :
52 52 X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> :
53 53 never;
54 }> {
54 }, O> {
55 55
56 56 each(refs, (v, k) => this._refs[k] = v);
57 57
58 58 return this as IDescriptorBuilder<S, T, R & {
59 59 [k in keyof X]:
60 60 X[k] extends keyof S ? NonNullable<S[X[k]]> :
61 61 X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> :
62 62 never;
63 }>;
63 }, O>;
64 64 }
65 65 factory(f: (refs: R) => T): void {
66 66 this._assertBuilding();
67 67 this._factory = f;
68 68 this._finalize();
69 69 this._complete();
70 70 }
71 71
72 72
73 73 private _assertBuilding() {
74 74 if (this._finalized)
75 75 throw new Error("The descriptor builder is finalized");
76 76 }
77 77
78 78 private _finalize() {
79 79 this._finalized = true;
80 80 }
81 81
82 override<K extends ConfigurableKeys<S>>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
83 override<K extends ConfigurableKeys<S>>(services: { [k in K]: RegistrationBuilder<S, NonNullable<S[k]>> }): this;
84 override<K extends ConfigurableKeys<S>>(nameOrServices: K | { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }, builder?: RegistrationBuilder<S, NonNullable<S[K]>>): this {
82 override<K extends O>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
83 override<K extends O>(services: { [k in K]: RegistrationBuilder<S, NonNullable<S[k]>> }): this;
84 override<K extends O>(nameOrServices: K | { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }, builder?: RegistrationBuilder<S, NonNullable<S[K]>>): this {
85 85 this._assertBuilding();
86 86 const guard = (v: void | Promise<void>) => {
87 87 if (isPromise(v))
88 88 v.catch(err => this._fail(err));
89 89 };
90 90
91 91 if (isKey(nameOrServices)) {
92 92 if (builder) {
93 93 this._defer();
94 const d = new DescriptorBuilder<S, NonNullable<S[K]>>(
94 const d = new DescriptorBuilder<S, NonNullable<S[K]>, object, O>(
95 95 this._lifetimeManager,
96 96 result => {
97 97 this._overrides[nameOrServices] = result;
98 98 this._complete();
99 99 },
100 100 err => this._fail(err)
101 101 );
102 102
103 103 try {
104 104 guard(builder(d));
105 105 } catch (err) {
106 106 this._fail(err);
107 107 }
108 108 }
109 109 } else {
110 110 each(nameOrServices, (v, k) => this.override(k, v));
111 111 }
112 112 return this;
113 113 }
114 114
115 115 lifetime(lifetime: "singleton", typeId: string): this;
116 116 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
117 117 lifetime(lifetime: ILifetime<T> | ActivationType, typeId?: string): this {
118 118 this._assertBuilding();
119 119 if (isString(lifetime)) {
120 120 this._lifetime = this._resolveLifetime(lifetime, typeId);
121 121 } else {
122 122 this._lifetime = lifetime;
123 123 }
124 124 return this;
125 125 }
126 126
127 127 cleanup(cb: (item: T) => void): this {
128 128 this._assertBuilding();
129 129 this._cleanup = cb;
130 130 return this;
131 131 }
132 132
133 133 value(v: T): void {
134 134 this._assertBuilding();
135 135 this._cb({
136 136 activate() {
137 137 return v;
138 138 }
139 139 });
140 140 this._finalize();
141 141 }
142 142
143 143 _resolveLifetime<T>(activation: ActivationType, typeId?: string | object): ILifetime<T> {
144 144 switch (activation) {
145 145 case "container":
146 146 return this._lifetimeManager.create();
147 147 case "hierarchy":
148 148 return LifetimeManager.hierarchyLifetime();
149 149 case "context":
150 150 return LifetimeManager.contextLifetime();
151 151 case "singleton": {
152 152 if (!typeId)
153 153 throw Error("The singleton activation requires a typeId");
154 154
155 155 const _oid = isString(typeId) ? typeId : oid(typeId);
156 156
157 157 return LifetimeManager.singletonLifetime(_oid);
158 158 }
159 159 default:
160 160 return LifetimeManager.empty();
161 161 }
162 162 }
163 163
164 164 _defer() {
165 165 this._pending++;
166 166 }
167 167
168 168 _complete() {
169 169 if (--this._pending === 0) {
170 170 if (!this._factory)
171 171 throw new Error("The factory must be specified");
172 172
173 173 this._cb(new DescriptorImpl<S, T>({
174 174 lifetime: this._lifetime,
175 175 factory: this._factory,
176 176 overrides: this._overrides,
177 177 cleanup: this._cleanup
178 178 }));
179 179 }
180 180 }
181 181
182 182 _fail(err: unknown) {
183 183 if (!this._failed) {
184 184 this._failed = true;
185 185 this._eb.call(undefined, err);
186 186 }
187 187 }
188 188
189 189 }
@@ -1,83 +1,82
1 import { Descriptor, ILifetime, ConfigurableKeys, DepsMap, Ref, IActivationContext } from "./interfaces";
2 import { ActivationContext } from "./ActivationContext";
1 import { Descriptor, ILifetime, DepsMap, Ref, IActivationContext } from "./interfaces";
3 2 import { each, isKey, key } from "./traits";
4 3
5 export type RegistrationOverridesMap<S extends object> = { [k in ConfigurableKeys<S>]?: Descriptor<S, NonNullable<S[k]>> };
4 export type RegistrationOverridesMap<S extends object> = { [k in keyof S]?: Descriptor<S, NonNullable<S[k]>> };
6 5
7 6 export interface DescriptorImplArgs<S extends object, T> {
8 7 lifetime: ILifetime<T>;
9 8
10 9 factory: (refs: Record<key, never>) => T;
11 10
12 11 cleanup?: (item: T) => void;
13 12
14 13 overrides?: RegistrationOverridesMap<S>;
15 14
16 15 dependencies?: DepsMap<key, keyof S>;
17 16 }
18 17
19 18
20 19 export class DescriptorImpl<S extends object, T> implements Descriptor<S, T> {
21 20
22 21 private readonly _overrides?: RegistrationOverridesMap<S>;
23 22
24 23 private readonly _lifetime: ILifetime<T>;
25 24
26 25 private readonly _factory: (refs: Record<key, never>) => T;
27 26
28 27 private readonly _cleanup?: (item: T) => void;
29 28
30 29 private readonly _deps?: DepsMap<key, keyof S>;
31 30
32 31 constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs<S, T>) {
33 32 this._lifetime = lifetime;
34 33 this._factory = factory;
35 34 if (cleanup)
36 35 this._cleanup = cleanup;
37 36 if (overrides)
38 37 this._overrides = overrides;
39 38 if (dependencies)
40 39 this._deps = dependencies;
41 40 }
42 41
43 42 activate(context: IActivationContext<S>): T {
44 43
45 44 if (this._lifetime.has())
46 45 return this._lifetime.get();
47 46
48 47 this._lifetime.initialize(context);
49 48
50 49 if (this._overrides)
51 50 each(this._overrides, (v, k) => context.register(k, v));
52 51
53 52 const resolve = <K extends keyof S, L extends boolean, D = never>({ name, lazy, ...opts }: Ref<K, L, D>) => {
54 53 if (lazy) {
55 54 return () => "default" in opts ? context.resolve(name, opts.default) : context.resolve(name);
56 55 } else {
57 56 return "default" in opts ? context.resolve(name, opts.default) : context.resolve(name);
58 57 }
59 58 };
60 59
61 60 const makeRefs = (deps: typeof this._deps) => deps ?
62 61 Object.keys(deps)
63 62 .map(k => {
64 63 const ref = deps[k];
65 64 return isKey(ref) ?
66 65 { [k]: resolve({ name: ref }) } :
67 66 { [k]: resolve(ref) };
68 67 })
69 68 .reduce((a, p) => ({ ...a, ...p }), {} ) as Record<key, never>:
70 69 {} as Record<key, never>;
71 70
72 71 const instance = this._factory.call(undefined, makeRefs(this._deps));
73 72
74 73 this._lifetime.store(instance, this._cleanup);
75 74
76 75 return instance;
77 76 }
78 77
79 78
80 79 toString() {
81 80 return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`;
82 81 }
83 82 }
@@ -1,146 +1,143
1 import { ActivationContext } from "./ActivationContext";
2 1 import { key } from "./traits";
3 2
4 3 export interface IDestroyable {
5 4 destroy(): void;
6 5 }
7 6
8 7 /**
9 8 * @template S Карта доступных зависимостей
10 9 */
11 10 export interface Resolver<S extends object> {
12 11 /**
13 12 * Функция для разрешения зависимостей, поддерживает создание фабричных методов,
14 13 * отложенную активацию и значение по-умолчанию для сервисов
15 14 * @template K Ключ сервиса из {@link S}
16 15 * @template O Тип параметра {@link opts} используется для выведения типа
17 16 * возвращаемого значения.
18 17 * @param name Ключ сервиса, который будет разрешен.
19 18 * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
20 19 * будет возвращен фабричный метод для получения зависимости. Если не указан,
21 20 * то считается `false`.
22 21 * @param {any=} opts.default Значение по умолчанию, если в контейнере указанный
23 22 * сервис не зарегистрирован
24 23 * @returns Либо фабричный метод для получения зависимости, либо значение зависимости
25 24 * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
26 25 */
27 26 <K extends keyof S, O extends { lazy: true; default?: unknown }>(name: K, opts?: O): () => (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
28 27 <K extends keyof S, O extends { lazy?: false; default?: unknown }>(name: K, opts?: O): (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
29 28 }
30 29
31 30 export type DepsMap<K extends key, SK extends key> = { [k in K]: SK | Ref<SK, boolean, unknown> };
32 31
33 32 export type Ref<K extends key, L extends boolean, D> = { name: K, lazy?: L } | { name: K, lazy?: L, default: D };
34 33
35 34 export type Resolved<S, K extends keyof S, L, D> =
36 35 L extends true ? () => NonNullable<S[K]> | (unknown extends D ? never : D) : NonNullable<S[K]> | (unknown extends D ? never : D);
37 36
38 export interface IDescriptorBuilder<S extends object, T, R extends object = object> {
37 export interface IDescriptorBuilder<S extends object, T, R extends object, O extends keyof S> {
39 38
40 39 /**
41 40 *
42 41 * @param f
43 42 */
44 43 factory(f: (refs: R) => T): void;
45 44
46 45 wants<X extends DepsMap<Exclude<key, keyof R>, keyof S>>(refs: X):
47 46 IDescriptorBuilder<S, T, R & {
48 47 [k in keyof X]:
49 48 X[k] extends keyof S ? NonNullable<S[X[k]]> :
50 49 X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> :
51 50 never
52 }>
51 }, O>
53 52
54 override<K extends ConfigurableKeys<S>>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
55 override<K extends ConfigurableKeys<S>>(services: { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }): this;
53 override<K extends O>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
54 override<K extends O>(services: { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }): this;
56 55
57 56 lifetime(lifetime: "singleton", typeId: string | number | object): this;
58 57 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
59 58
60 59 cleanup(cb: (item: T) => void): this;
61 60
62 61 value(v: T): void;
63 62 }
64 63
65 export type RegistrationBuilder<S extends object, T> = (d: IDescriptorBuilder<S, T>) => void;
64 export type RegistrationBuilder<S extends object, T> = (d: IDescriptorBuilder<S, T, object, ConfigurableKeys<S>>) => void;
66 65
67 export type RegistrationBuildersMap<S extends object, K extends ConfigurableKeys<S> = ConfigurableKeys<S>> = {
66 export type RegistrationBuildersMap<S extends Configurable<S>, K extends keyof S = keyof S> = {
68 67 [k in K]-?: RegistrationBuilder<ContainerServices<S>, NonNullable<S[k]>>
69 68 };
70 69
71 70 export interface Descriptor<S extends object, T> {
72 71 activate(context: IActivationContext<S>): T;
73 72 }
74 73
75 74 export interface IActivationContext<S extends object> extends ServiceLocator<S> {
76 75 createLifetime<T>(): ILifetime<T>;
77 76
78 77 createContainerLifetime<T>(): ILifetime<T>;
79 78 }
80 79
81 export type ConfigurableDescriptor<S extends object, K extends ConfigurableKeys<S>> = Descriptor<ContainerServices<S>, S[K]>;
82
83 80 export type RegistrationMap<S extends object, K extends keyof S = keyof S> = {
84 81 [k in K]-?: Descriptor<S, S[k]>;
85 82 };
86 83
87 84 export interface ContainerProvided<S extends Configurable<S>> {
88 85 container: ServiceLocator<ContainerServices<S>>;
89 86
90 87 childContainer: IContainerBuilder<S>;
91 88 }
92 89
93 90 export type Configurable<S> = { [k in keyof S & (keyof ContainerProvided<never>)]: never; };
94 91
95 92 export type ProvidedKeys = keyof ContainerProvided<never>;
96 93
97 94 export type ContainerServices<S extends Configurable<S>> = S & ContainerProvided<S>;
98 95
99 96 export type ConfigurableKeys<S extends object> = Exclude<keyof S, ProvidedKeys>;
100 97
101 98 export type ConfigurableServices<S extends object> = Pick<S, ConfigurableKeys<S>>;
102 99
103 100 export type ContainerKeys<S extends Configurable<S>> = keyof S | keyof ContainerProvided<never>;
104 101
105 102 export interface ServiceLocator<S extends object> {
106 103 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
107 104 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
108 105 }
109 106
110 107 export interface LifetimeContainer {
111 108 createLifetime<T>(): ILifetime<T>;
112 109 }
113 110
114 111 export interface ServiceContainer<S extends Configurable<S>> extends
115 112 ServiceLocator<ContainerServices<S>>,
116 113 IDestroyable {
117 114
118 115 createChildContainer(): IContainerBuilder<S>;
119 116 }
120 117
121 118 export interface IContainerBuilder<S extends Configurable<S>> {
122 createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<S, NonNullable<S[K]>>;
119 createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<S, NonNullable<S[K]>, object, keyof S>;
123 120
124 121 build(): ServiceContainer<S>;
125 122 }
126 123
127 124
128 125 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
129 126
130 127 /**
131 128 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
132 129 * свой собственный объект `ILifetime`, который создается при первой активации
133 130 */
134 131 export interface ILifetime<T> {
135 132 /** Проверяет, что уже создан экземпляр объекта */
136 133 has(): boolean;
137 134
138 135 get(): T;
139 136
140 137 initialize(context: IActivationContext<object>): void;
141 138
142 139 store(item: T, cleanup?: (item: T) => void): void;
143 140 }
144 141
145 142 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
146 143
General Comments 0
You need to be logged in to leave comments. Login now