##// END OF EJS Templates
working on fluent configuration
cin -
r133:09ea4b9e3735 ioc ts support
parent child
Show More
@@ -0,0 +1,59
1 import { Container } from "../Container";
2 import { argumentNotNull, each } from "../../safe";
3 import { DescriptorBuilder } from "./DescriptorBuilder";
4
5 enum State {
6 ready,
7
8 pending
9 }
10
11 export class Configuration<S extends object, Y extends keyof S = keyof S> {
12
13 private _state = State.ready;
14
15 _completion: PromiseLike<void> = Promise.resolve();
16
17 _builders: { [k in keyof S]?: (service: DescriptorBuilder<S[k], S>) => void } = {};
18
19 register<K extends Y>(name: K, builder: (service: DescriptorBuilder<S[K], S>) => void): Configuration<S, Exclude<Y, K>> {
20 argumentNotNull(builder, "builder");
21
22 return this;
23 }
24
25 private _moveStart() {
26 if (this._state !== State.ready)
27 throw new Error("Invalid operation");
28
29 this._state = State.pending;
30 }
31
32 private _moveDone() {
33 this._state = State.ready;
34 }
35
36 apply(target: Container<S>) {
37 this._moveStart();
38
39 let pending = 1;
40
41 this._completion = new Promise((resolve, reject) => {
42 each(this._builders, (v, k) => {
43 pending++;
44 const d = new DescriptorBuilder<any, S>(target, result => {
45 target.register(k, result);
46 if (!--pending)
47 resolve();
48 });
49
50 try {
51 v(d);
52 } catch (e) {
53 reject(e);
54 }
55 });
56 }).then(() => this._moveDone());
57 }
58
59 }
@@ -0,0 +1,58
1 import { Resolver, ServiceModule, LazyDependencyOptions, DependencyOptions } from "./interfaces";
2 import { AnnotationBuilder } from "../Annotations";
3 import { Container } from "../Container";
4 import { Descriptor, ILifetime, ContainerKeys } from "../interfaces";
5 import { ActivationContext } from "../ActivationContext";
6
7 export class DescriptorBuilder<T, S extends object> {
8 readonly _container: Container<S>;
9 readonly _cb: (d: Descriptor<S, T>) => void;
10
11 constructor(container: Container<S>, cb: (d: Descriptor<S, T>) => void) {
12 this._container = container;
13 this._cb = cb;
14 }
15 service(service: AnnotationBuilder<T, S> | ServiceModule<T, S>) {
16
17 }
18
19 factory(f: (resolve: Resolver<S>, activate: (lifetime: ILifetime, factory: () => any, cleanup?: (item: any) => void) => any) => T): void {
20 this._cb({
21 activate(context: ActivationContext<S>) {
22 const resolve = (name: ContainerKeys<S>, opts?: DependencyOptions | LazyDependencyOptions) => {
23 if (opts && "lazy" in opts && opts.lazy) {
24 const c2 = context.clone();
25 return () => {
26 return opts.optional ? c2.resolve(name, opts.default) : c2.resolve(name);
27 };
28 } else {
29 return opts && opts.optional ? context.resolve(name, opts.default) : context.resolve(name);
30 }
31 };
32
33 const activate = (lifetime: ILifetime, factory: () => any, cleanup?: (item: any) => void) => {
34 if (lifetime.has()) {
35 return lifetime.get();
36 } else {
37 lifetime.enter();
38 const instance = factory();
39 lifetime.store(instance, cleanup);
40 return instance;
41 }
42
43 };
44
45 return f(resolve, activate);
46 }
47 });
48 }
49
50 value(v: T): void {
51 this._cb({
52 activate() {
53 return v;
54 }
55 });
56 }
57
58 }
@@ -5,7 +5,7
5 самостоятельных событий, например, связанных с действиями пользователя.
5 самостоятельных событий, например, связанных с действиями пользователя.
6
6
7 Является реализацией классического шаблона наблюдателя с возможность сообщить
7 Является реализацией классического шаблона наблюдателя с возможность сообщить
8 о коце потока событий. Данная реализация не содержит никаких дополнительных
8 о конце потока событий. Данная реализация не содержит никаких дополнительных
9 функций, таких как фильтрация, канал с состоянием, преобразования сообщений и
9 функций, таких как фильтрация, канал с состоянием, преобразования сообщений и
10 т.п. Это сделано специально, чтобы реализация оставалась максимально простой.
10 т.п. Это сделано специально, чтобы реализация оставалась максимально простой.
11
11
@@ -20,7 +20,7 var events = new Observable(async (notif
20 notify(i);
20 notify(i);
21 }
21 }
22 // по окончании последовательности информируем, что событий больше не будет
22 // по окончании последовательности информируем, что событий больше не будет
23 compelte();
23 complete();
24 });
24 });
25
25
26 // создаем окно с отображением хода событий
26 // создаем окно с отображением хода событий
@@ -49,9 +49,9 let firstEvent = await events.next();
49 `Observable` можно создавать из событий другого объекта, например, виджета:
49 `Observable` можно создавать из событий другого объекта, например, виджета:
50
50
51 ```ts
51 ```ts
52 // клсс
52 // класс
53 class Canvas {
53 class Canvas {
54 readonly mouseMove: IObservable<[number,number]>
54 mouseMove: IObservable<[number,number]>;
55
55
56 postCreate() {
56 postCreate() {
57 // превращаем события виджета в Observable
57 // превращаем события виджета в Observable
@@ -98,7 +98,7 class PositionTracker implements IDestro
98 }
98 }
99 ```
99 ```
100
100
101 Существует также несколько варинатов получения сообщений
101 Существует также несколько вариантов получения сообщений
102
102
103 ```ts
103 ```ts
104 // регистрация метода для получений событий
104 // регистрация метода для получений событий
@@ -128,7 +128,7 class Map {
128 let evt = this.viewport.click.next(ct);
128 let evt = this.viewport.click.next(ct);
129
129
130 // преобразуем позицию на экране в координаты карты
130 // преобразуем позицию на экране в координаты карты
131 return this.clientToCoodinates([evt.clientx,evt.clientY]);
131 return this.clientToCoordinates([evt.clientX,evt.clientY]);
132 }
132 }
133 }
133 }
134
134
@@ -142,8 +142,8 let coords = await map.peekCoordinates()
142
142
143 ## Observable и последовательности
143 ## Observable и последовательности
144
144
145 Можно сичтать, что `Observable` это некоторая аналогия итератора только в
145 Можно считать, что `Observable` это некоторая аналогия итератора только в
146 парадигме событийного (или реактивного) программировния. Следует также понимать,
146 парадигме событийного (или реактивного) программирования. Следует также понимать,
147 что при переходе от синхронного процедурного программирования к событийному так
147 что при переходе от синхронного процедурного программирования к событийному так
148 же меняется и направление управления (Inverse Of Control), что означает
148 же меняется и направление управления (Inverse Of Control), что означает
149 следующее:
149 следующее:
@@ -153,7 +153,7 let coords = await map.peekCoordinates()
153 * при работе с `Observable` клиенты вынуждены обрабатывать эти события по мере
153 * при работе с `Observable` клиенты вынуждены обрабатывать эти события по мере
154 их поступления и не могут на это повлиять.
154 их поступления и не могут на это повлиять.
155
155
156 Последний пункт можно изменить применив, например, буффер или канал с
156 Последний пункт можно изменить применив, например, буфер или канал с
157 состоянием, т.е. очередь, но данные механизмы выходят за рамки простого шаблона
157 состоянием, т.е. очередь, но данные механизмы выходят за рамки простого шаблона
158 наблюдателя.
158 наблюдателя.
159
159
@@ -179,4 +179,4 events.on((data) => {
179 // будет вызван для всех сообщений
179 // будет вызван для всех сообщений
180 processEvent(data);
180 processEvent(data);
181 });
181 });
182 ``` No newline at end of file
182 ```
@@ -1,6 +1,6
1 import { IObservable, IDestroyable, ICancellation, IObserver } from "./interfaces";
1 import { IObservable, IDestroyable, ICancellation, IObserver } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
2 import { Cancellation } from "./Cancellation";
3 import { argumentNotNull, destroyed } from "./safe";
3 import { argumentNotNull } from "./safe";
4
4
5 type Handler<T> = (x: T) => void;
5 type Handler<T> = (x: T) => void;
6
6
@@ -8,12 +8,6 type Initializer<T> = (notify: Handler<T
8
8
9 const noop = () => { };
9 const noop = () => { };
10
10
11 const nulObserver: IObserver<any> = Object.freeze({
12 next: noop,
13 error: noop,
14 complete: noop
15 });
16
17 function isObserver(val: any): val is IObserver<any> {
11 function isObserver(val: any): val is IObserver<any> {
18 return val && (typeof val.next === "function");
12 return val && (typeof val.next === "function");
19 }
13 }
@@ -1,6 +1,6
1 import { TraceSource } from "../log/TraceSource";
1 import { TraceSource } from "../log/TraceSource";
2 import { argumentNotNull, argumentNotEmptyString } from "../safe";
2 import { argumentNotEmptyString } from "../safe";
3 import { Descriptor, ContainerServiceMap, ContainerKeys, ContainerResolve } from "./interfaces";
3 import { Descriptor, ContainerServiceMap, ContainerKeys, TypeOfService } from "./interfaces";
4 import { Container } from "./Container";
4 import { Container } from "./Container";
5 import { MapOf } from "../interfaces";
5 import { MapOf } from "../interfaces";
6
6
@@ -45,13 +45,16 export class ActivationContext<S extends
45 return this._container;
45 return this._container;
46 }
46 }
47
47
48 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>) {
48 resolve<K extends ContainerKeys<S>>(name: K): TypeOfService<S, K>;
49 resolve<K extends ContainerKeys<S>, T>(name: K, def: T): TypeOfService<S, K> | T;
50 resolve<K extends ContainerKeys<S>>(name: K, def: undefined): TypeOfService<S, K> | undefined;
51 resolve<K extends ContainerKeys<S>, T>(name: K, def?: T): TypeOfService<S, K> | T | undefined {
49 const d = this._services[name];
52 const d = this._services[name];
50
53
51 if (d !== undefined) {
54 if (d !== undefined) {
52 return this.activate(d, name.toString());
55 return this.activate(d, name.toString());
53 } else {
56 } else {
54 if (def !== undefined && def !== null)
57 if (arguments.length > 1)
55 return def;
58 return def;
56 else
59 else
57 throw new Error(`Service ${name} not found`);
60 throw new Error(`Service ${name} not found`);
@@ -1,8 +1,7
1 import { TypeRegistration } from "./Configuration";
1 import { TypeRegistration } from "./Configuration";
2 import { ExtractDependency } from "./fluent/interfaces";
2 import { ExtractDependency } from "./fluent/interfaces";
3 import { RegistrationBuilder } from "./fluent/RegistrationBuilder";
4
3
5 export class AnnotaionBuilder<T, S extends object> {
4 export class AnnotationBuilder<T, S extends object> {
6 wire<P extends any[]>(...args: P) {
5 wire<P extends any[]>(...args: P) {
7 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
6 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
8
7
@@ -23,8 +22,4 export class AnnotaionBuilder<T, S exten
23 throw new Error();
22 throw new Error();
24 }
23 }
25
24
26 getRegistrationBuilder(): RegistrationBuilder<T, S> {
27 throw new Error();
28 }
25 }
29
30 }
@@ -2,7 +2,8 import {
2 PartialServiceMap,
2 PartialServiceMap,
3 ActivationType,
3 ActivationType,
4 ContainerKeys,
4 ContainerKeys,
5 ContainerResolve
5 TypeOfService,
6 ILifetimeManager
6 } from "./interfaces";
7 } from "./interfaces";
7
8
8 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
9 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
@@ -32,7 +33,7 export interface RegistrationScope<S ext
32 }
33 }
33
34
34 /**
35 /**
35 * Базовый интефейс конфигурации сервисов
36 * Базовый интерфейс конфигурации сервисов
36 */
37 */
37 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
38 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
38
39
@@ -40,6 +41,11 export interface ServiceRegistration<T,
40
41
41 params?: any;
42 params?: any;
42
43
44 /** Специальный идентификатор используется при активации singleton, если
45 * не указан для TypeRegistration вычисляется как oid($type)
46 */
47 typeId?: string;
48
43 inject?: object | object[];
49 inject?: object | object[];
44
50
45 cleanup?: ((instance: T) => void) | string;
51 cleanup?: ((instance: T) => void) | string;
@@ -68,7 +74,7 export interface DependencyRegistration<
68 $dependency: K;
74 $dependency: K;
69 lazy?: boolean;
75 lazy?: boolean;
70 optional?: boolean;
76 optional?: boolean;
71 default?: ContainerResolve<S, K>;
77 default?: TypeOfService<S, K>;
72 }
78 }
73
79
74 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
80 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
@@ -225,7 +231,15 export class Configuration<S extends obj
225 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
231 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
226 try {
232 try {
227 const m = await this._loadModule(moduleName);
233 const m = await this._loadModule(moduleName);
228 return localName ? get(localName, m) : m;
234 if (localName) {
235 return get(localName, m);
236 } else {
237 if (m instanceof Function)
238 return m;
239 if ("default" in m)
240 return m.default;
241 return m;
242 }
229 } catch (e) {
243 } catch (e) {
230 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
244 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
231 throw e;
245 throw e;
@@ -345,7 +359,7 export class Configuration<S extends obj
345 this._visit(data.params, "params");
359 this._visit(data.params, "params");
346
360
347 if (data.activation) {
361 if (data.activation) {
348 opts.activation = this._getLifetimeManager(data.activation);
362 opts.activation = this._getLifetimeManager(data.activation, data.typeId);
349 }
363 }
350
364
351 if (data.cleanup)
365 if (data.cleanup)
@@ -384,7 +398,9 export class Configuration<S extends obj
384 opts.type = data.$type;
398 opts.type = data.$type;
385 } else {
399 } else {
386 const [moduleName, typeName] = data.$type.split(":", 2);
400 const [moduleName, typeName] = data.$type.split(":", 2);
387 opts.type = this._resolveType(moduleName, typeName);
401 const t = opts.type = this._resolveType(moduleName, typeName);
402 if (!(t instanceof Function))
403 throw Error("$type (" + data.$type + ") is not a constructable");
388 }
404 }
389
405
390 const d = new TypeServiceDescriptor<S, any, any[]>(
406 const d = new TypeServiceDescriptor<S, any, any[]>(
@@ -411,7 +427,7 export class Configuration<S extends obj
411 return d;
427 return d;
412 }
428 }
413
429
414 _getLifetimeManager(activation: ActivationType) {
430 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetimeManager {
415 switch (activation) {
431 switch (activation) {
416 case "container":
432 case "container":
417 return this._container.getLifetimeManager();
433 return this._container.getLifetimeManager();
@@ -420,7 +436,9 export class Configuration<S extends obj
420 case "context":
436 case "context":
421 return LifetimeManager.contextLifetime;
437 return LifetimeManager.contextLifetime;
422 case "singleton":
438 case "singleton":
423 return LifetimeManager.singletonLifetime;
439 if (typeId === undefined)
440 throw Error("The singleton activation requires a typeId");
441 return LifetimeManager.singletonLifetime(typeId);
424 default:
442 default:
425 return LifetimeManager.empty;
443 return LifetimeManager.empty;
426 }
444 }
@@ -1,7 +1,7
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { ValueDescriptor } from "./ValueDescriptor";
2 import { ValueDescriptor } from "./ValueDescriptor";
3 import { ActivationError } from "./ActivationError";
3 import { ActivationError } from "./ActivationError";
4 import { ServiceMap, Descriptor, PartialServiceMap, ContainerProvided, Resolver, ContainerServiceMap, ContainerKeys, ContainerResolve, ILifetimeManager } from "./interfaces";
4 import { ServiceMap, Descriptor, PartialServiceMap, ContainerProvided, ServiceLocator, ContainerServiceMap, ContainerKeys, TypeOfService, ILifetimeManager } from "./interfaces";
5 import { TraceSource } from "../log/TraceSource";
5 import { TraceSource } from "../log/TraceSource";
6 import { Configuration, RegistrationMap } from "./Configuration";
6 import { Configuration, RegistrationMap } from "./Configuration";
7 import { Cancellation } from "../Cancellation";
7 import { Cancellation } from "../Cancellation";
@@ -12,11 +12,9 import { each } from "../safe";
12
12
13 const trace = TraceSource.get("@implab/core/di/ActivationContext");
13 const trace = TraceSource.get("@implab/core/di/ActivationContext");
14
14
15 export class Container<S extends object = any> implements Resolver<S>, IDestroyable {
15 export class Container<S extends object = any> implements ServiceLocator<S>, IDestroyable {
16 readonly _services: ContainerServiceMap<S>;
16 readonly _services: ContainerServiceMap<S>;
17
17
18 readonly _cache: MapOf<any>;
19
20 readonly _lifetimeManager: ILifetimeManager;
18 readonly _lifetimeManager: ILifetimeManager;
21
19
22 readonly _cleanup: (() => void)[];
20 readonly _cleanup: (() => void)[];
@@ -30,7 +28,6 export class Container<S extends object
30 constructor(parent?: Container<S>) {
28 constructor(parent?: Container<S>) {
31 this._parent = parent;
29 this._parent = parent;
32 this._services = parent ? Object.create(parent._services) : {};
30 this._services = parent ? Object.create(parent._services) : {};
33 this._cache = {};
34 this._cleanup = [];
31 this._cleanup = [];
35 this._root = parent ? parent.getRootContainer() : this;
32 this._root = parent ? parent.getRootContainer() : this;
36 this._services.container = new ValueDescriptor(this) as any;
33 this._services.container = new ValueDescriptor(this) as any;
@@ -50,7 +47,7 export class Container<S extends object
50 return this._lifetimeManager;
47 return this._lifetimeManager;
51 }
48 }
52
49
53 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>): ContainerResolve<S, K> {
50 resolve<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>): TypeOfService<S, K> {
54 trace.debug("resolve {0}", name);
51 trace.debug("resolve {0}", name);
55 const d = this._services[name];
52 const d = this._services[name];
56 if (d === undefined) {
53 if (d === undefined) {
@@ -72,7 +69,7 export class Container<S extends object
72 /**
69 /**
73 * @deprecated use resolve() method
70 * @deprecated use resolve() method
74 */
71 */
75 getService<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>) {
72 getService<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>) {
76 return this.resolve(name, def);
73 return this.resolve(name, def);
77 }
74 }
78
75
@@ -1,23 +1,23
1 import { argumentNotEmptyString, each } from "../safe";
1 import { argumentNotEmptyString, each } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { Descriptor, PartialServiceMap, ContainerResolve, ContainerKeys } from "./interfaces";
3 import { Descriptor, PartialServiceMap, TypeOfService, ContainerKeys } from "./interfaces";
4 import { ActivationError } from "./ActivationError";
4 import { ActivationError } from "./ActivationError";
5
5
6 export interface ReferenceDescriptorParams<S extends object, K extends ContainerKeys<S>> {
6 export interface ReferenceDescriptorParams<S extends object, K extends ContainerKeys<S>> {
7 name: K;
7 name: K;
8 optional?: boolean;
8 optional?: boolean;
9 default?: ContainerResolve<S, K>;
9 default?: TypeOfService<S, K>;
10 services?: PartialServiceMap<S>;
10 services?: PartialServiceMap<S>;
11 }
11 }
12
12
13 export class LazyReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
13 export class LazyReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
14 implements Descriptor<S, ((args?: PartialServiceMap<S>) => ContainerResolve<S, K>)> {
14 implements Descriptor<S, ((args?: PartialServiceMap<S>) => TypeOfService<S, K>)> {
15
15
16 _name: K;
16 _name: K;
17
17
18 _optional = false;
18 _optional = false;
19
19
20 _default: ContainerResolve<S, K> | undefined;
20 _default: TypeOfService<S, K> | undefined;
21
21
22 _services: PartialServiceMap<S>;
22 _services: PartialServiceMap<S>;
23
23
@@ -53,7 +53,6 export class LazyReferenceDescriptor<S e
53 throw new ActivationError(this._name.toString(), ct.getStack(), error);
53 throw new ActivationError(this._name.toString(), ct.getStack(), error);
54 }
54 }
55 };
55 };
56
57 }
56 }
58
57
59 toString() {
58 toString() {
@@ -30,6 +30,8 const emptyLifetime: ILifetime = {
30
30
31 };
31 };
32
32
33 let nextId = 0;
34
33 export class LifetimeManager implements IDestroyable, ILifetimeManager {
35 export class LifetimeManager implements IDestroyable, ILifetimeManager {
34 private _cleanup: (() => void)[] = [];
36 private _cleanup: (() => void)[] = [];
35 private _cache: MapOf<any> = {};
37 private _cache: MapOf<any> = {};
@@ -37,8 +39,9 export class LifetimeManager implements
37
39
38 private _pending: MapOf<boolean> = {};
40 private _pending: MapOf<boolean> = {};
39
41
40 initialize(id: string): ILifetime {
42 initialize(): ILifetime {
41 const self = this;
43 const self = this;
44 const id = ++nextId;
42 return {
45 return {
43 has() {
46 has() {
44 return (id in self._cache);
47 return (id in self._cache);
@@ -89,33 +92,18 export class LifetimeManager implements
89 static readonly empty: ILifetimeManager = {
92 static readonly empty: ILifetimeManager = {
90 initialize(): ILifetime {
93 initialize(): ILifetime {
91 return emptyLifetime;
94 return emptyLifetime;
92 },
93 destroy() {
94 throw new Error("Trying to destroy empty lifetime manager, this is a bug.");
95 }
95 }
96
97 };
96 };
98
97
99 static readonly hierarchyLifetime: ILifetimeManager = {
98 static readonly hierarchyLifetime: ILifetimeManager = {
100 initialize(id: string, context: ActivationContext<any>): ILifetime {
99 initialize(context: ActivationContext<any>): ILifetime {
101 return context.getContainer().getLifetimeManager().initialize(id, context);
100 return context.getContainer().getLifetimeManager().initialize(context);
102 },
103 destroy() {
104 throw new Error("Trying to destroy hierarchy lifetime manager, this is a bug.");
105 }
106 };
107
108 static readonly singletonLifetime: ILifetimeManager = {
109 initialize(id: string): ILifetime {
110 return singletonLifetimeManager.initialize(id);
111 },
112 destroy() {
113 throw new Error("Trying to destroy singleton lifetime manager, this is a bug.");
114 }
101 }
115 };
102 };
116
103
117 static readonly contextLifetime: ILifetimeManager = {
104 static readonly contextLifetime: ILifetimeManager = {
118 initialize(id: string, context: ActivationContext<any>): ILifetime {
105 initialize(context: ActivationContext<any>): ILifetime {
106 const id = String(++nextId);
119 return {
107 return {
120 enter() {
108 enter() {
121 if (context.visit(id))
109 if (context.visit(id))
@@ -130,13 +118,15 export class LifetimeManager implements
130 store(item: any) {
118 store(item: any) {
131 context.store(id, item);
119 context.store(id, item);
132 }
120 }
133
121 };
122 }
134 };
123 };
135 },
124
136 destroy() {
125 static singletonLifetime(typeId: string): ILifetimeManager {
137 throw new Error("Trying to destroy empty lifetime manager, this is a bug.");
126 return {
127 initialize() {
128 return emptyLifetime;
138 }
129 }
139 };
130 };
140 }
131 }
141
132 }
142 const singletonLifetimeManager = new LifetimeManager();
@@ -1,22 +1,22
1 import { argumentNotEmptyString, each } from "../safe";
1 import { argumentNotEmptyString, each } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { Descriptor, PartialServiceMap, ContainerResolve, ContainerKeys } from "./interfaces";
3 import { Descriptor, PartialServiceMap, TypeOfService, ContainerKeys } from "./interfaces";
4
4
5 export interface ReferenceDescriptorParams<S extends object, K extends ContainerKeys<S>> {
5 export interface ReferenceDescriptorParams<S extends object, K extends ContainerKeys<S>> {
6 name: K;
6 name: K;
7 optional?: boolean;
7 optional?: boolean;
8 default?: ContainerResolve<S, K>;
8 default?: TypeOfService<S, K>;
9 services?: PartialServiceMap<S>;
9 services?: PartialServiceMap<S>;
10 }
10 }
11
11
12 export class ReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
12 export class ReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
13 implements Descriptor<S, ContainerResolve<S, K>> {
13 implements Descriptor<S, TypeOfService<S, K>> {
14
14
15 _name: K;
15 _name: K;
16
16
17 _optional = false;
17 _optional = false;
18
18
19 _default: ContainerResolve<S, K> | undefined;
19 _default: TypeOfService<S, K> | undefined;
20
20
21 _services: PartialServiceMap<S>;
21 _services: PartialServiceMap<S>;
22
22
@@ -1,13 +1,10
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ServiceMap, PartialServiceMap, ILifetimeManager } from "./interfaces";
2 import { Descriptor, ServiceMap, PartialServiceMap, ILifetimeManager, ILifetime } from "./interfaces";
3 import { argumentNotNull, isPrimitive, keys, isNull } from "../safe";
3 import { isPrimitive, keys, isNull } from "../safe";
4 import { TraceSource } from "../log/TraceSource";
4 import { TraceSource } from "../log/TraceSource";
5 import { isDescriptor } from "./traits";
5 import { isDescriptor } from "./traits";
6 import { LifetimeManager } from "./LifetimeManager";
6 import { LifetimeManager } from "./LifetimeManager";
7 import { MatchingMemberKeys } from "../interfaces";
7 import { MatchingMemberKeys } from "../interfaces";
8 import { Container } from "./Container";
9
10 let cacheId = 0;
11
8
12 const trace = TraceSource.get("@implab/core/di/ActivationContext");
9 const trace = TraceSource.get("@implab/core/di/ActivationContext");
13
10
@@ -82,14 +79,14 export class ServiceDescriptor<S extends
82
79
83 _cleanup: ((item: T) => void) | undefined;
80 _cleanup: ((item: T) => void) | undefined;
84
81
85 _cacheId = String(++cacheId);
82 _lifetimeManager = LifetimeManager.empty;
86
83
87 _lifetime = LifetimeManager.empty;
84 _objectLifetime: ILifetime | undefined;
88
85
89 constructor(opts: ServiceDescriptorParams<S, T, P>) {
86 constructor(opts: ServiceDescriptorParams<S, T, P>) {
90
87
91 if (opts.lifetime)
88 if (opts.lifetime)
92 this._lifetime = opts.lifetime;
89 this._lifetimeManager = opts.lifetime;
93
90
94 if (!isNull(opts.params))
91 if (!isNull(opts.params))
95 this._params = opts.params;
92 this._params = opts.params;
@@ -108,14 +105,17 export class ServiceDescriptor<S extends
108 }
105 }
109
106
110 activate(context: ActivationContext<S>) {
107 activate(context: ActivationContext<S>) {
111 const lifetime = this._lifetime.initialize(this._cacheId, context);
108 if (!this._objectLifetime)
109 this._objectLifetime = this._lifetimeManager.initialize(context);
110
111 const lifetime = this._objectLifetime;
112
112
113 if (lifetime.has()) {
113 if (lifetime.has()) {
114 return lifetime.get();
114 return lifetime.get();
115 } else {
115 } else {
116 lifetime.enter();
116 lifetime.enter();
117 const instance = this._create(context);
117 const instance = this._create(context);
118 lifetime.store(this._cacheId, this._cleanup);
118 lifetime.store(instance, this._cleanup);
119 return instance;
119 return instance;
120 }
120 }
121 }
121 }
@@ -1,19 +1,17
1 import { primitive } from "../../safe";
1 import { primitive } from "../../safe";
2 import { ActivationType } from "../interfaces";
2 import { AnnotationBuilder } from "../Annotations";
3 import { AnnotaionBuilder } from "../Annotations";
3 import { ILifetime, TypeOfService, ContainerKeys } from "../interfaces";
4 import { LazyDependencyRegistration, DependencyRegistration } from "../Configuration";
5 import { Container } from "../Container";
6
4
7 export interface DependencyOptions<T> {
5 export interface DependencyOptions {
8 optional?: boolean;
6 optional?: boolean;
9 default?: T;
7 default?: any;
10 }
8 }
11
9
12 export interface LazyDependencyOptions<T> extends DependencyOptions<T> {
10 export interface LazyDependencyOptions extends DependencyOptions {
13 lazy: true;
11 lazy: true;
14 }
12 }
15
13
16 export type ExtractService<K, S> = K extends keyof S ? S[K] : K;
14 export type ExtractService<K, S> = K extends keyof S ? S[K] : never;
17
15
18 export type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
16 export type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
19 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
17 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
@@ -25,47 +23,26 export type WalkDependencies<D, S> = D e
25 { [K in keyof D]: ExtractDependency<D[K], S> };
23 { [K in keyof D]: ExtractDependency<D[K], S> };
26
24
27 export type ServiceModule<T, S extends object, M extends keyof any = "service"> = {
25 export type ServiceModule<T, S extends object, M extends keyof any = "service"> = {
28 [m in M]: AnnotaionBuilder<T, S>;
26 [m in M]: AnnotationBuilder<T, S>;
29 };
27 };
30
28
31 export interface ServiceRecordBuilder<T, S extends object> {
29 export type InferReferenceType<S extends object, K extends keyof ContainerKeys<S>, O> = O extends { default: infer X } ? (TypeOfService<S, K> | X) :
32 type<P extends any[], C extends new (...args: ExtractDependency<P, S>) => T>(
30 O extends { optional: true } ? (TypeOfService<S, K> | undefined) :
33 target: C, ...params: P): ConstructorBuilder<C, S>;
31 TypeOfService<S, K>;
34 factory<P extends any[], F extends (...args: ExtractDependency<P, S>) => T>(
32
35 target: F, ...params: P): FactoryBuilder<F, S>;
33 export interface Resolver<S extends object> {
36 wired<M extends keyof any>(module: ServiceModule<T, S, M>, m: M): RegistrationBuilder<T, S>;
34 <K extends keyof ContainerKeys<S>, O extends LazyDependencyOptions>(this: void, name: K, opts: O): () => InferReferenceType<S, K, O>;
37 wired(module: ServiceModule<T, S>): RegistrationBuilder<T, S>;
35 <K extends keyof ContainerKeys<S>, O extends DependencyOptions>(this: void, name: K, opts?: O): InferReferenceType<S, K, O>;
38 }
36 }
39
37
40 export interface RegistrationVisitor {
38 export interface DescriptorBuilder<T, S extends object> {
41 visitDependency(): void;
39 service(service: AnnotationBuilder<T, S> | ServiceModule<T, S>): void;
42
43 visitObject(): void;
44
45 visitTypeRegistration(): void;
46
40
47 visitFactoryRegistration(): void;
41 factory(f: (resolve: Resolver<S>, activate: <T2>(lifetime: ILifetime, factory: () => T2, cleanup?: (item: T2) => void) => T2) => T): void;
48
42
49 }
43 value(v: T): void;
50
51 export interface ServiceRegistration {
52 visit(visitor: RegistrationVisitor): void;
53 }
44 }
54
45
55 export interface ConfigBuilder<S extends object, Y extends keyof S = keyof S> {
46 export interface Configuration<S extends object, Y extends keyof S = keyof S> {
56 register<K extends Y>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void | Promise<void>): ConfigBuilder<S, Exclude<Y, K>>;
47 register<K extends Y>(name: K, builder: (d: DescriptorBuilder<S[K], S>) => void): Configuration<S, Exclude<Y, K>>;
57 register<K extends Y, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): ConfigBuilder<S, Exclude<Y, K>>;
58 register<K extends Y>(name: K, value: S[K], raw: true): ConfigBuilder<S, Exclude<Y, K>>;
59
60 apply(container: Container<S>): Promise<void>;
61 }
48 }
62
63 export interface ServicesDeclaration<S extends object> {
64 build<T>(this: void): ServiceRecordBuilder<T, S>;
65 annotate<T>(this: void): AnnotaionBuilder<T, S>;
66
67 dependency<K extends keyof S>(this: void, name: K, opts: LazyDependencyOptions<S[K]>): LazyDependencyRegistration<S, K>;
68 dependency<K extends keyof S>(this: void, name: K, opts?: DependencyOptions<S[K]>): DependencyRegistration<S, K>;
69
70 configure(): ConfigBuilder<S>;
71 }
@@ -1,5 +1,4
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { IDestroyable } from "../interfaces";
3
2
4 export interface Descriptor<S extends object = any, T = any> {
3 export interface Descriptor<S extends object = any, T = any> {
5 activate(context: ActivationContext<S>): T;
4 activate(context: ActivationContext<S>): T;
@@ -11,24 +10,24 export type ServiceMap<S extends object>
11
10
12 export type ContainerKeys<S extends object> = keyof S | keyof ContainerProvided<S>;
11 export type ContainerKeys<S extends object> = keyof S | keyof ContainerProvided<S>;
13
12
14 export type ContainerResolve<S extends object, K> =
13 export type TypeOfService<S extends object, K> =
15 K extends keyof ContainerProvided<S> ? ContainerProvided<S>[K] :
14 K extends keyof ContainerProvided<S> ? ContainerProvided<S>[K] :
16 K extends keyof S ? S[K] : never;
15 K extends keyof S ? S[K] : never;
17
16
18 export type ContainerServiceMap<S extends object> = {
17 export type ContainerServiceMap<S extends object> = {
19 [K in ContainerKeys<S>]: Descriptor<S, ContainerResolve<S, K>>;
18 [K in ContainerKeys<S>]: Descriptor<S, TypeOfService<S, K>>;
20 };
19 };
21
20
22 export type PartialServiceMap<S extends object> = {
21 export type PartialServiceMap<S extends object> = {
23 [k in keyof S]?: Descriptor<S, S[k]>;
22 [k in keyof S]?: Descriptor<S, S[k]>;
24 };
23 };
25
24
26 export interface Resolver<S extends object> {
25 export interface ServiceLocator<S extends object> {
27 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>): ContainerResolve<S, K>;
26 resolve<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>): TypeOfService<S, K>;
28 }
27 }
29
28
30 export interface ContainerProvided<S extends object> {
29 export interface ContainerProvided<S extends object> {
31 container: Resolver<S>;
30 container: ServiceLocator<S>;
32 }
31 }
33
32
34 export type ContainerRegistered<S extends object> = /*{
33 export type ContainerRegistered<S extends object> = /*{
@@ -38,11 +37,16 export type ContainerRegistered<S extend
38
37
39 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
38 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
40
39
41 export interface ILifetimeManager extends IDestroyable {
40 export interface ILifetimeManager {
42 initialize(id: string, context: ActivationContext<any>): ILifetime;
41 initialize(context: ActivationContext<any>): ILifetime;
43 }
42 }
44
43
44 /**
45 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
46 * свой собственный объект `ILifetime`, который создается при первой активации
47 */
45 export interface ILifetime {
48 export interface ILifetime {
49 /** Проверяет, что уже создан экземпляр объекта */
46 has(): boolean;
50 has(): boolean;
47
51
48 get(): any;
52 get(): any;
@@ -1,46 +1,19
1 import { isPrimitive } from "../safe";
1 import { isPrimitive } from "../safe";
2 import { Descriptor } from "./interfaces";
2 import { Descriptor } from "./interfaces";
3 import { ServicesDeclaration, ServiceRecordBuilder, ServiceModule, RegistrationBuilder, ExtractDependency } from "./fluent/interfaces";
3 import { AnnotationBuilder } from "./Annotations";
4 import { AnnotaionBuilder } from "./Annotations";
4 import { Configuration } from "./fluent/Configuration";
5 import { FactoryBuilder } from "./fluent/FactoryBuilder";
6 import { ConstructorBuilder } from "./fluent/ConstructorBuiler";
7
5
8 export function isDescriptor(x: any): x is Descriptor {
6 export function isDescriptor(x: any): x is Descriptor {
9 return (!isPrimitive(x)) &&
7 return (!isPrimitive(x)) &&
10 (x.activate instanceof Function);
8 (x.activate instanceof Function);
11 }
9 }
12
10
13 export function declare<S extends object>(): ServicesDeclaration<S> {
11 export function declare<S extends object>() {
14 return {
12 return {
15 annotate<T>() {
13 annotate<T>() {
16 return new AnnotaionBuilder<T, S>();
14 return new AnnotationBuilder<T, S>();
17 },
18 build<T>(): ServiceRecordBuilder<T, S> {
19 return {
20 factory<P extends any[], F extends (...args: ExtractDependency<P, S>) => T>(
21 target: F,
22 ...params: P
23 ): FactoryBuilder<F, S> {
24 return new FactoryBuilder(target, params);
25 },
15 },
26
16 configure(): Configuration<S> {
27 type<P extends any[], C extends new (...args: ExtractDependency<P, S>) => T>(
28 target: C, ...params: P
29 ): ConstructorBuilder<C, S> {
30 return new ConstructorBuilder(target, params);
31 },
32
33 wired<M extends keyof any>(module: ServiceModule<T, S, M>, m?: M): RegistrationBuilder<T, S> {
34 const service = m ?
35 module[m] :
36 (module as ServiceModule<T, S>).service;
37 if (!service)
38 throw new Error("The specified module doen's provides a service annotation");
39 return service.getRegistrationBuilder();
40 }
41 };
42 },
43 configure() {
44 throw new Error();
17 throw new Error();
45 },
18 },
46 dependency() {
19 dependency() {
@@ -1,17 +1,20
1 import { configure, dependency, build } from "./services";
1 import { configure } from "./services";
2
2
3 export const config = configure()
3 export const config = configure()
4 .register("bar", async s => s.wired(await import("./Bar"), "service"))
4 .register("host", s => s.value("example.com"))
5 .register("box", s => import("./Box").then(m => s.wired(m)))
5 .register("bar2", bar2 => Promise.all([import("./Foo"), import("./Bar")])
6 .register("host", "example.com")
6 .then(([{ Foo }, { Bar }]) => {
7 // .registerType("bar2", Bar, [{ foo: dependency("foo"), host: "" }]);
7 const lifetime: any = undefined; // new HierarchyLifetime()
8 .register("bar2", async s => s.type((await import("./Bar")).Bar,
8 bar2.factory((resolve, activate) => {
9 {
9 const bar = new Bar({
10 foo: build().type((await import("./Foo")).Foo)
10 foo: activate(lifetime, () => new Foo()),
11 .activate("context"),
11 nested: {
12 nested: { lazy: dependency("foo", { lazy: true }) },
12 lazy: resolve("foo", { lazy: true })
13 host: dependency("host")
14 },
13 },
15 "")
14 host: resolve("host")
16 .inject("setName", dependency("host"))
15 }, "some text");
16 bar.setName(resolve("host"));
17 return bar;
18 });
19 })
17 );
20 );
@@ -1,7 +1,7
1 import { Foo } from "./Foo";
1 import { Foo } from "./Foo";
2 import { Bar } from "./Bar";
2 import { Bar } from "./Bar";
3 import { Box } from "./Box";
3 import { Box } from "./Box";
4 import { declare } from "../di/fluent/interfaces";
4 import { declare } from "../di/traits";
5
5
6 /**
6 /**
7 * Сервисы доступные внутри контейнера
7 * Сервисы доступные внутри контейнера
@@ -22,4 +22,4 export interface Services {
22 /**
22 /**
23 * Экспортируем вспомогательные функции для описания сервисов и кинфогурации
23 * Экспортируем вспомогательные функции для описания сервисов и кинфогурации
24 */
24 */
25 export const { dependency, build, annotate, configure } = declare<Services>();
25 export const { dependency, annotate, configure } = declare<Services>();
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now