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