##// END OF EJS Templates
working on fluent configuration, di annotations removed
cin -
r134:511bcc634d65 ioc ts support
parent child
Show More
@@ -1,110 +1,109
1 define([
1 define([
2 "dojo/_base/declare",
2 "dojo/_base/declare",
3 "../safe",
3 "../safe",
4 "dojo/when",
4 "dojo/when",
5 "dojo/store/util/QueryResults" ],
5 "dojo/store/util/QueryResults" ],
6
6
7 function(declare, safe, when, QueryResults) {
7 function(declare, safe, when, QueryResults) {
8
8
9 "use strict";
9 "use strict";
10
10
11 /**
11 /**
12 * Обертка вокруг произвольного хранилища, только для чтения. Используется
12 * Обертка вокруг произвольного хранилища, только для чтения. Используется
13 * для преобразования данных, например, отображения в списках элементов
13 * для преобразования данных, например, отображения в списках элементов
14 * пространственных данных.
14 * пространственных данных.
15 */
15 */
16 return declare(null, {
16 return declare(null, {
17 /**
17 /**
18 * @type{String} Свойство, хранящее идентификатор
18 * @type{String} Свойство, хранящее идентификатор
19 */
19 */
20 idProperty : null,
20 idProperty : null,
21
21
22 _store : null,
22 _store : null,
23
23
24 /**
24 /**
25 * @param{String} opts.idProperty Имя свойства, в которое будет записан
25 * @param{String} opts.idProperty Имя свойства, в которое будет записан
26 * идентификатор, если не указан, то идентификатор будет
26 * идентификатор, если не указан, то идентификатор будет
27 * взят из родительского хранилища или использоваться
27 * взят из родительского хранилища или использоваться
28 * строка <code>id</code>
28 * строка <code>id</code>
29 * @param{dojo.store} opts.store Родительское хранилище
29 * @param{dojo.store} opts.store Родительское хранилище
30 */
30 */
31 constructor : function(opts) {
31 constructor : function(opts) {
32 safe.argumentNotNull(opts, "opts");
32 safe.argumentNotNull(opts, "opts");
33 safe.argumentNotNull(opts.store, "opts.store");
33 safe.argumentNotNull(opts.store, "opts.store");
34
34
35 this._store = opts.store;
35 this._store = opts.store;
36 delete opts.store;
36 delete opts.store;
37 declare.safeMixin(this, opts);
37 declare.safeMixin(this, opts);
38 this.idProperty = opts.idProperty || this._store.idProperty || "id";
38 this.idProperty = opts.idProperty || this._store.idProperty || "id";
39 },
39 },
40
40
41 getParentStore : function() {
41 getParentStore : function() {
42 return this._store;
42 return this._store;
43 },
43 },
44
44
45 get : function(id) {
45 get : function(id) {
46 var me = this;
46 var me = this;
47 return when(me._store.get(id), function(x) {
47 return when(me._store.get(id), function(x) {
48 var m = me.mapItem(x);
48 var m = me.mapItem(x);
49 if (!(me.idProperty in m))
49 if (!(me.idProperty in m))
50 m[me.idProperty] = id;
50 m[me.idProperty] = id;
51 return m;
51 return m;
52 });
52 });
53 },
53 },
54
54
55 /**
55 /**
56 * Выполняет запрос в родительском хранилище, для этого используется
56 * Выполняет запрос в родительском хранилище, для этого используется
57 * <code>translateQuery</code> для подготовки запроса, затем,
57 * <code>translateQuery</code> для подготовки запроса, затем,
58 * <code>mapItem</code> для преобразования результатов.
58 * <code>mapItem</code> для преобразования результатов.
59 */
59 */
60 query : function(q, options) {
60 query : function(q, options) {
61 var me = this, store = this._store;
61 var me = this, store = this._store;
62 return when(store.query(me.translateQuery(q), me
62 return when(store.query(me.translateQuery(q), me
63 .translateOptions(options)), function(res) {
63 .translateOptions(options)), function(res) {
64 var total = res.total;
64 var total = res.total;
65 var mapped = res.map(function(x) {
65 var mapped = res.map(function(x) {
66 var m = me.mapItem(x);
66 var m = me.mapItem(x);
67 if (!(me.idProperty in m))
67 if (!(me.idProperty in m))
68 m[me.idProperty] = store.getIdentity &&
68 m[me.idProperty] = store.getIdentity &&
69 store.getIdentity(x);
69 store.getIdentity(x);
70 return m;
70 return m;
71 });
71 });
72 mapped.total = total;
72 mapped.total = total;
73 var results = new QueryResults(mapped);
73 var results = new QueryResults(mapped);
74 console.log(results);
75 return results;
74 return results;
76 });
75 });
77 },
76 },
78
77
79 getIdentity : function(obj) {
78 getIdentity : function(obj) {
80 return obj && obj[this.idProperty];
79 return obj && obj[this.idProperty];
81 },
80 },
82
81
83 /**
82 /**
84 * Преобразование запроса в формат родительского хранилища.
83 * Преобразование запроса в формат родительского хранилища.
85 *
84 *
86 * @param{Object} q Запрос в формате текущего хранилища
85 * @param{Object} q Запрос в формате текущего хранилища
87 * @returns{Object} Запрос в формате родительского хранилища
86 * @returns{Object} Запрос в формате родительского хранилища
88 */
87 */
89 translateQuery : function(q) {
88 translateQuery : function(q) {
90 return q;
89 return q;
91 },
90 },
92
91
93 translateOptions : function(options) {
92 translateOptions : function(options) {
94 return options;
93 return options;
95 },
94 },
96
95
97 /**
96 /**
98 * Преобразование объекта из родительского хранилища. При преобразовании
97 * Преобразование объекта из родительского хранилища. При преобразовании
99 * в объекте можно задать идентификатор, иначе идентификатор будет
98 * в объекте можно задать идентификатор, иначе идентификатор будет
100 * автоматически получен и присвоен из родительского хранилища
99 * автоматически получен и присвоен из родительского хранилища
101 *
100 *
102 * @param{Object} item Объект из родительского хранилища
101 * @param{Object} item Объект из родительского хранилища
103 * @returns{Object} результат преобразования
102 * @returns{Object} результат преобразования
104 */
103 */
105 mapItem : function(item) {
104 mapItem : function(item) {
106 return item;
105 return item;
107 }
106 }
108 });
107 });
109
108
110 }); No newline at end of file
109 });
@@ -1,40 +1,41
1 import { Cancellation } from "../Cancellation";
1 import { Cancellation } from "../Cancellation";
2 import { IAsyncComponent, ICancellation, ICancellable, IDestroyable } from "../interfaces";
2 import { IAsyncComponent, ICancellation, ICancellable, IDestroyable } from "../interfaces";
3 import { destroy } from "../safe";
3 import { destroy } from "../safe";
4
4
5 const noop = () => void (0);
6
5 export class AsyncComponent implements IAsyncComponent, ICancellable {
7 export class AsyncComponent implements IAsyncComponent, ICancellable {
6 _cancel: ((e: any) => void) | undefined;
8 _cancel: ((e: any) => void) = noop;
7
9
8 _completion: Promise<void> = Promise.resolve();
10 _completion: Promise<void> = Promise.resolve();
9
11
10 getCompletion() { return this._completion; }
12 getCompletion() { return this._completion; }
11
13
12 runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) {
14 runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) {
13 // create inner cancellation bound to the passed cancellation token
15 // create inner cancellation bound to the passed cancellation token
14 let h: IDestroyable;
16 let h: IDestroyable;
15 const inner = new Cancellation(cancel => {
17 const inner = new Cancellation(cancel => {
16
18
17 this._cancel = cancel;
19 this._cancel = cancel;
18 h = ct.register(cancel);
20 h = ct.register(cancel);
19 });
21 });
20
22
21 // TODO create cancellation source here
23 // TODO create cancellation source here
22 const guard = async () => {
24 const guard = async () => {
23 try {
25 try {
24 await op(inner);
26 await op(inner);
25 } finally {
27 } finally {
26 // after the operation is complete we need to cleanup the
28 // after the operation is complete we need to cleanup the
27 // resources
29 // resources
28 destroy(h);
30 destroy(h);
29 this._cancel = undefined;
31 this._cancel = noop;
30 }
32 }
31 };
33 };
32
34
33 return this._completion = guard();
35 return this._completion = guard();
34 }
36 }
35
37
36 cancel(reason: any) {
38 cancel(reason: any) {
37 if (this._cancel)
39 this._cancel(reason);
38 this._cancel(reason);
39 }
40 }
40 }
41 }
@@ -1,133 +1,140
1 import { TraceSource } from "../log/TraceSource";
1 import { TraceSource } from "../log/TraceSource";
2 import { argumentNotEmptyString } from "../safe";
2 import { argumentNotEmptyString } from "../safe";
3 import { Descriptor, ContainerServiceMap, ContainerKeys, TypeOfService } from "./interfaces";
3 import { Descriptor, ContainerServiceMap, ContainerKeys, TypeOfService, ILifetime } from "./interfaces";
4 import { Container } from "./Container";
4 import { Container } from "./Container";
5 import { MapOf } from "../interfaces";
5 import { MapOf } from "../interfaces";
6
6
7 const trace = TraceSource.get("@implab/core/di/ActivationContext");
7 const trace = TraceSource.get("@implab/core/di/ActivationContext");
8
8
9 export interface ActivationContextInfo {
9 export interface ActivationContextInfo {
10 name: string;
10 name: string;
11
11
12 service: string;
12 service: string;
13
13
14 }
14 }
15
15
16 let nextId = 1;
17
16 export class ActivationContext<S extends object> {
18 export class ActivationContext<S extends object> {
17 _cache: MapOf<any>;
19 _cache: MapOf<any>;
18
20
19 _services: ContainerServiceMap<S>;
21 _services: ContainerServiceMap<S>;
20
22
21 _visited: MapOf<any>;
23 _visited: MapOf<any>;
22
24
23 _name: string;
25 _name: string;
24
26
25 _service: Descriptor<S, any>;
27 _service: Descriptor<S, any>;
26
28
27 _container: Container<S>;
29 _container: Container<S>;
28
30
29 _parent: ActivationContext<S> | undefined;
31 _parent: ActivationContext<S> | undefined;
30
32
31 constructor(container: Container<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) {
33 constructor(container: Container<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) {
32 this._name = name;
34 this._name = name;
33 this._service = service;
35 this._service = service;
34 this._visited = {};
36 this._visited = {};
35 this._cache = {};
37 this._cache = {};
36 this._services = services;
38 this._services = services;
37 this._container = container;
39 this._container = container;
38 }
40 }
39
41
40 getName() {
42 getName() {
41 return this._name;
43 return this._name;
42 }
44 }
43
45
44 getContainer() {
46 getContainer() {
45 return this._container;
47 return this._container;
46 }
48 }
47
49
48 resolve<K extends ContainerKeys<S>>(name: K): TypeOfService<S, K>;
50 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;
51 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;
52 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 {
53 resolve<K extends ContainerKeys<S>, T>(name: K, def?: T): TypeOfService<S, K> | T | undefined {
52 const d = this._services[name];
54 const d = this._services[name];
53
55
54 if (d !== undefined) {
56 if (d !== undefined) {
55 return this.activate(d, name.toString());
57 return this.activate(d, name.toString());
56 } else {
58 } else {
57 if (arguments.length > 1)
59 if (arguments.length > 1)
58 return def;
60 return def;
59 else
61 else
60 throw new Error(`Service ${name} not found`);
62 throw new Error(`Service ${name} not found`);
61 }
63 }
62 }
64 }
63
65
64 /**
66 /**
65 * registers services local to the the activation context
67 * registers services local to the the activation context
66 *
68 *
67 * @name{string} the name of the service
69 * @name{string} the name of the service
68 * @service{string} the service descriptor to register
70 * @service{string} the service descriptor to register
69 */
71 */
70 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>) {
72 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>) {
71 argumentNotEmptyString(name, "name");
73 argumentNotEmptyString(name, "name");
72
74
73 this._services[name] = service as any;
75 this._services[name] = service as any;
74 }
76 }
75
77
76 has(id: string) {
78 createLifetime(): ILifetime {
77 return id in this._cache;
79 const id = nextId++;
80 const me = this;
81 return {
82 initialize() {
83 },
84 has() {
85 return id in me._cache;
86 },
87 get() {
88 return me._cache[id];
89 },
90 store(item: any) {
91 me._cache[id] = item;
92 }
93 };
78 }
94 }
79
80 get<T>(id: string) {
81 return this._cache[id];
82 }
83
84 store(id: string, value: any) {
85 return (this._cache[id] = value);
86 }
87
88 activate<T>(d: Descriptor<S, T>, name: string) {
95 activate<T>(d: Descriptor<S, T>, name: string) {
89 if (trace.isLogEnabled())
96 if (trace.isLogEnabled())
90 trace.log(`enter ${name} ${d}`);
97 trace.log(`enter ${name} ${d}`);
91
98
92 const ctx = this.enter(d, name);
99 const ctx = this.enter(d, name);
93 const v = d.activate(ctx);
100 const v = d.activate(ctx);
94
101
95 if (trace.isLogEnabled())
102 if (trace.isLogEnabled())
96 trace.log(`leave ${name}`);
103 trace.log(`leave ${name}`);
97
104
98 return v;
105 return v;
99 }
106 }
100
107
101 visit(id: string) {
108 visit(id: string) {
102 const count = this._visited[id] || 0;
109 const count = this._visited[id] || 0;
103 this._visited[id] = count + 1;
110 this._visited[id] = count + 1;
104 return count;
111 return count;
105 }
112 }
106
113
107 getStack(): ActivationContextInfo[] {
114 getStack(): ActivationContextInfo[] {
108 const stack = [{
115 const stack = [{
109 name: this._name,
116 name: this._name,
110 service: this._service.toString()
117 service: this._service.toString()
111 }];
118 }];
112
119
113 return this._parent ?
120 return this._parent ?
114 stack.concat(this._parent.getStack()) :
121 stack.concat(this._parent.getStack()) :
115 stack;
122 stack;
116 }
123 }
117
124
118 private enter(service: Descriptor<S, any>, name: string): this {
125 private enter(service: Descriptor<S, any>, name: string): this {
119 const clone = Object.create(this);
126 const clone = Object.create(this);
120 clone._name = name;
127 clone._name = name;
121 clone._services = Object.create(this._services);
128 clone._services = Object.create(this._services);
122 clone._parent = this;
129 clone._parent = this;
123 clone._service = service;
130 clone._service = service;
124 return clone;
131 return clone;
125 }
132 }
126
133
127 /** Creates a clone for the current context, used to protect it from modifications */
134 /** Creates a clone for the current context, used to protect it from modifications */
128 clone(): this {
135 clone(): this {
129 const clone = Object.create(this);
136 const clone = Object.create(this);
130 clone._services = Object.create(this._services);
137 clone._services = Object.create(this._services);
131 return clone;
138 return clone;
132 }
139 }
133 }
140 }
@@ -1,446 +1,450
1 import {
1 import {
2 PartialServiceMap,
2 PartialServiceMap,
3 ActivationType,
3 ActivationType,
4 ContainerKeys,
4 ContainerKeys,
5 TypeOfService,
5 TypeOfService,
6 ILifetimeManager
6 ILifetime
7 } from "./interfaces";
7 } from "./interfaces";
8
8
9 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
9 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
11 import { ValueDescriptor } from "./ValueDescriptor";
11 import { ValueDescriptor } from "./ValueDescriptor";
12 import { Container } from "./Container";
12 import { Container } from "./Container";
13 import { ReferenceDescriptor } from "./ReferenceDescriptor";
13 import { ReferenceDescriptor } from "./ReferenceDescriptor";
14 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
14 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
15 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
15 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
16 import { TraceSource } from "../log/TraceSource";
16 import { TraceSource } from "../log/TraceSource";
17 import { ConfigError } from "./ConfigError";
17 import { ConfigError } from "./ConfigError";
18 import { Cancellation } from "../Cancellation";
18 import { Cancellation } from "../Cancellation";
19 import { makeResolver } from "./ResolverHelper";
19 import { makeResolver } from "./ResolverHelper";
20 import { ICancellation } from "../interfaces";
20 import { ICancellation } from "../interfaces";
21 import { isDescriptor } from "./traits";
21 import { isDescriptor } from "./traits";
22 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
22 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
23 import { LifetimeManager } from "./LifetimeManager";
23 import { LifetimeManager } from "./LifetimeManager";
24
24
25 export interface RegistrationScope<S extends object> {
25 export interface RegistrationScope<S extends object> {
26
26
27 /** сервисы, которые регистрируются в контексте активации и таким образом
27 /** сервисы, которые регистрируются в контексте активации и таким образом
28 * могут переопределять ранее зарегистрированные сервисы. за это свойство
28 * могут переопределять ранее зарегистрированные сервисы. за это свойство
29 * нужно платить, кроме того порядок активации будет влиять на результат
29 * нужно платить, кроме того порядок активации будет влиять на результат
30 * разрешения зависимостей.
30 * разрешения зависимостей.
31 */
31 */
32 services?: RegistrationMap<S>;
32 services?: RegistrationMap<S>;
33 }
33 }
34
34
35 /**
35 /**
36 * Базовый интерфейс конфигурации сервисов
36 * Базовый интерфейс конфигурации сервисов
37 */
37 */
38 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
38 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
39
39
40 activation?: ActivationType;
40 activation?: ActivationType;
41
41
42 params?: any;
42 params?: any;
43
43
44 /** Специальный идентификатор используется при активации singleton, если
44 /** Специальный идентификатор используется при активации singleton, если
45 * не указан для TypeRegistration вычисляется как oid($type)
45 * не указан для TypeRegistration вычисляется как oid($type)
46 */
46 */
47 typeId?: string;
47 typeId?: string;
48
48
49 inject?: object | object[];
49 inject?: object | object[];
50
50
51 cleanup?: ((instance: T) => void) | string;
51 cleanup?: ((instance: T) => void) | string;
52 }
52 }
53
53
54 export interface TypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
54 export interface TypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
55 $type: string | C;
55 $type: string | C;
56 params?: Registration<ConstructorParameters<C>, S>;
56 params?: Registration<ConstructorParameters<C>, S>;
57 }
57 }
58
58
59 export interface StrictTypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
59 export interface StrictTypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
60 $type: C;
60 $type: C;
61 params?: Registration<ConstructorParameters<C>, S>;
61 params?: Registration<ConstructorParameters<C>, S>;
62 }
62 }
63
63
64 export interface FactoryRegistration<F extends (...args: any[]) => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
64 export interface FactoryRegistration<F extends (...args: any[]) => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
65 $factory: string | F;
65 $factory: string | F;
66 }
66 }
67
67
68 export interface ValueRegistration<T> {
68 export interface ValueRegistration<T> {
69 $value: T;
69 $value: T;
70 parse?: boolean;
70 parse?: boolean;
71 }
71 }
72
72
73 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
73 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
74 $dependency: K;
74 $dependency: K;
75 lazy?: boolean;
75 lazy?: boolean;
76 optional?: boolean;
76 optional?: boolean;
77 default?: TypeOfService<S, K>;
77 default?: TypeOfService<S, K>;
78 }
78 }
79
79
80 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> {
81 lazy: true;
81 lazy: true;
82 }
82 }
83
83
84 export type Registration<T, S extends object> = T extends primitive ? T :
84 export type Registration<T, S extends object> = T extends primitive ? T :
85 (
85 (
86 T |
86 T |
87 { [k in keyof T]: Registration<T[k], S> } |
87 { [k in keyof T]: Registration<T[k], S> } |
88 TypeRegistration<new (...args: any[]) => T, S> |
88 TypeRegistration<new (...args: any[]) => T, S> |
89 FactoryRegistration<(...args: any[]) => T, S> |
89 FactoryRegistration<(...args: any[]) => T, S> |
90 ValueRegistration<any> |
90 ValueRegistration<any> |
91 DependencyRegistration<S, keyof S>
91 DependencyRegistration<S, keyof S>
92 );
92 );
93
93
94 export type RegistrationMap<S extends object> = {
94 export type RegistrationMap<S extends object> = {
95 [k in keyof S]?: Registration<S[k], S>;
95 [k in keyof S]?: Registration<S[k], S>;
96 };
96 };
97
97
98 const _activationTypes: { [k in ActivationType]: number; } = {
98 const _activationTypes: { [k in ActivationType]: number; } = {
99 singleton: 1,
99 singleton: 1,
100 container: 2,
100 container: 2,
101 hierarchy: 3,
101 hierarchy: 3,
102 context: 4,
102 context: 4,
103 call: 5
103 call: 5
104 };
104 };
105
105
106 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
106 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
107 return (!isPrimitive(x)) && ("$type" in x);
107 return (!isPrimitive(x)) && ("$type" in x);
108 }
108 }
109
109
110 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
110 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
111 return (!isPrimitive(x)) && ("$factory" in x);
111 return (!isPrimitive(x)) && ("$factory" in x);
112 }
112 }
113
113
114 export function isValueRegistration(x: any): x is ValueRegistration<any> {
114 export function isValueRegistration(x: any): x is ValueRegistration<any> {
115 return (!isPrimitive(x)) && ("$value" in x);
115 return (!isPrimitive(x)) && ("$value" in x);
116 }
116 }
117
117
118 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
118 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
119 return (!isPrimitive(x)) && ("$dependency" in x);
119 return (!isPrimitive(x)) && ("$dependency" in x);
120 }
120 }
121
121
122 export function isActivationType(x: string): x is ActivationType {
122 export function isActivationType(x: string): x is ActivationType {
123 return typeof x === "string" && x in _activationTypes;
123 return typeof x === "string" && x in _activationTypes;
124 }
124 }
125
125
126 const trace = TraceSource.get("@implab/core/di/Configuration");
126 const trace = TraceSource.get("@implab/core/di/Configuration");
127 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
127 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
128 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
128 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
129 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
129 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
130 if (data instanceof Array) {
130 if (data instanceof Array) {
131 return Promise.all(map ? data.map(map) : data);
131 return Promise.all(map ? data.map(map) : data);
132 } else {
132 } else {
133 const keys = Object.keys(data);
133 const keys = Object.keys(data);
134
134
135 const o: any = {};
135 const o: any = {};
136
136
137 await Promise.all(keys.map(async k => {
137 await Promise.all(keys.map(async k => {
138 const v = map ? map(data[k], k) : data[k];
138 const v = map ? map(data[k], k) : data[k];
139 o[k] = isPromise(v) ? await v : v;
139 o[k] = isPromise(v) ? await v : v;
140 }));
140 }));
141
141
142 return o;
142 return o;
143 }
143 }
144 }
144 }
145
145
146 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
146 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
147
147
148 export class Configuration<S extends object> {
148 export class Configuration<S extends object> {
149
149
150 _hasInnerDescriptors = false;
150 _hasInnerDescriptors = false;
151
151
152 readonly _container: Container<S>;
152 readonly _container: Container<S>;
153
153
154 _path: Array<string>;
154 _path: Array<string>;
155
155
156 _configName: string | undefined;
156 _configName: string | undefined;
157
157
158 _require: ModuleResolver | undefined;
158 _require: ModuleResolver | undefined;
159
159
160 constructor(container: Container<S>) {
160 constructor(container: Container<S>) {
161 argumentNotNull(container, "container");
161 argumentNotNull(container, "container");
162 this._container = container;
162 this._container = container;
163 this._path = [];
163 this._path = [];
164 }
164 }
165
165
166 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
166 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
167 argumentNotEmptyString(moduleName, "moduleName");
167 argumentNotEmptyString(moduleName, "moduleName");
168
168
169 trace.log(
169 trace.log(
170 "loadConfiguration moduleName={0}, contextRequire={1}",
170 "loadConfiguration moduleName={0}, contextRequire={1}",
171 moduleName,
171 moduleName,
172 contextRequire ? typeof (contextRequire) : "<nil>"
172 contextRequire ? typeof (contextRequire) : "<nil>"
173 );
173 );
174
174
175 this._configName = moduleName;
175 this._configName = moduleName;
176
176
177 const r = await makeResolver(undefined, contextRequire);
177 const r = await makeResolver(undefined, contextRequire);
178
178
179 const config = await r(moduleName, ct);
179 const config = await r(moduleName, ct);
180
180
181 await this._applyConfiguration(
181 await this._applyConfiguration(
182 config,
182 config,
183 await makeResolver(moduleName, contextRequire),
183 await makeResolver(moduleName, contextRequire),
184 ct
184 ct
185 );
185 );
186 }
186 }
187
187
188 async applyConfiguration(data: RegistrationMap<S>, contextRequire?: any, ct = Cancellation.none) {
188 async applyConfiguration(data: RegistrationMap<S>, contextRequire?: any, ct = Cancellation.none) {
189 argumentNotNull(data, "data");
189 argumentNotNull(data, "data");
190
190
191 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
191 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
192 }
192 }
193
193
194 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
194 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
195 trace.log("applyConfiguration");
195 trace.log("applyConfiguration");
196
196
197 this._configName = "$";
197 this._configName = "$";
198
198
199 if (resolver)
199 if (resolver)
200 this._require = resolver;
200 this._require = resolver;
201
201
202 let services: PartialServiceMap<S>;
202 let services: PartialServiceMap<S>;
203
203
204 try {
204 try {
205 services = await this._visitRegistrations(data, "$");
205 services = await this._visitRegistrations(data, "$");
206 } catch (e) {
206 } catch (e) {
207 throw this._makeError(e);
207 throw this._makeError(e);
208 }
208 }
209
209
210 this._container.register(services);
210 this._container.register(services);
211 }
211 }
212
212
213 _makeError(inner: any) {
213 _makeError(inner: any) {
214 const e = new ConfigError("Failed to load configuration", inner);
214 const e = new ConfigError("Failed to load configuration", inner);
215 e.configName = this._configName || "<inline>";
215 e.configName = this._configName || "<inline>";
216 e.path = this._makePath();
216 e.path = this._makePath();
217 return e;
217 return e;
218 }
218 }
219
219
220 _makePath() {
220 _makePath() {
221 return this._path
221 return this._path
222 .reduce(
222 .reduce(
223 (prev, cur) => typeof cur === "number" ?
223 (prev, cur) => typeof cur === "number" ?
224 `${prev}[${cur}]` :
224 `${prev}[${cur}]` :
225 `${prev}.${cur}`
225 `${prev}.${cur}`
226 )
226 )
227 .toString();
227 .toString();
228 }
228 }
229
229
230 async _resolveType(moduleName: string, localName: string) {
230 async _resolveType(moduleName: string, localName: string) {
231 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
231 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
232 try {
232 try {
233 const m = await this._loadModule(moduleName);
233 const m = await this._loadModule(moduleName);
234 if (localName) {
234 if (localName) {
235 return get(localName, m);
235 return get(localName, m);
236 } else {
236 } else {
237 if (m instanceof Function)
237 if (m instanceof Function)
238 return m;
238 return m;
239 if ("default" in m)
239 if ("default" in m)
240 return m.default;
240 return m.default;
241 return m;
241 return m;
242 }
242 }
243 } catch (e) {
243 } catch (e) {
244 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);
245 throw e;
245 throw e;
246 }
246 }
247 }
247 }
248
248
249 _loadModule(moduleName: string) {
249 _loadModule(moduleName: string) {
250 trace.debug("loadModule {0}", moduleName);
250 trace.debug("loadModule {0}", moduleName);
251 if (!this._require)
251 if (!this._require)
252 throw new Error("Module loader isn't specified");
252 throw new Error("Module loader isn't specified");
253
253
254 return this._require(moduleName);
254 return this._require(moduleName);
255 }
255 }
256
256
257 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
257 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
258 this._enter(name);
258 this._enter(name);
259
259
260 if (data.constructor &&
260 if (data.constructor &&
261 data.constructor.prototype !== Object.prototype)
261 data.constructor.prototype !== Object.prototype)
262 throw new Error("Configuration must be a simple object");
262 throw new Error("Configuration must be a simple object");
263
263
264 const services = await mapAll(data, async (v, k) => {
264 const services = await mapAll(data, async (v, k) => {
265 const d = await this._visit(v, k.toString());
265 const d = await this._visit(v, k.toString());
266 return isDescriptor(d) ? d : new AggregateDescriptor(d);
266 return isDescriptor(d) ? d : new AggregateDescriptor(d);
267 }) as PartialServiceMap<S>;
267 }) as PartialServiceMap<S>;
268
268
269 this._leave();
269 this._leave();
270
270
271 return services;
271 return services;
272 }
272 }
273
273
274 _enter(name: string) {
274 _enter(name: string) {
275 this._path.push(name.toString());
275 this._path.push(name.toString());
276 trace.debug(">{0}", name);
276 trace.debug(">{0}", name);
277 }
277 }
278
278
279 _leave() {
279 _leave() {
280 const name = this._path.pop();
280 const name = this._path.pop();
281 trace.debug("<{0}", name);
281 trace.debug("<{0}", name);
282 }
282 }
283
283
284 async _visit(data: any, name: string): Promise<any> {
284 _visit(data: any, name: string): Promise<any> {
285 if (isPrimitive(data) || isDescriptor(data))
285 if (isPrimitive(data))
286 return data;
286 return Promise.resolve(new ValueDescriptor(data));
287 if (isDescriptor(data))
288 return Promise.resolve(data);
287
289
288 if (isDependencyRegistration<S>(data)) {
290 if (isDependencyRegistration<S>(data)) {
289 return this._visitDependencyRegistration(data, name);
291 return this._visitDependencyRegistration(data, name);
290 } else if (isValueRegistration(data)) {
292 } else if (isValueRegistration(data)) {
291 return this._visitValueRegistration(data, name);
293 return this._visitValueRegistration(data, name);
292 } else if (isTypeRegistration(data)) {
294 } else if (isTypeRegistration(data)) {
293 return this._visitTypeRegistration(data, name);
295 return this._visitTypeRegistration(data, name);
294 } else if (isFactoryRegistration(data)) {
296 } else if (isFactoryRegistration(data)) {
295 return this._visitFactoryRegistration(data, name);
297 return this._visitFactoryRegistration(data, name);
296 } else if (data instanceof Array) {
298 } else if (data instanceof Array) {
297 return this._visitArray(data, name);
299 return this._visitArray(data, name);
298 }
300 }
299
301
300 return this._visitObject(data, name);
302 return this._visitObject(data, name);
301 }
303 }
302
304
303 async _visitObject(data: any, name: string) {
305 async _visitObject(data: any, name: string) {
304 if (data.constructor &&
306 if (data.constructor &&
305 data.constructor.prototype !== Object.prototype)
307 data.constructor.prototype !== Object.prototype)
306 return new ValueDescriptor(data);
308 return new ValueDescriptor(data);
307
309
308 this._enter(name);
310 this._enter(name);
309
311
310 const v = await mapAll(data, delegate(this, "_visit"));
312 const v = await mapAll(data, delegate(this, "_visit"));
311
313
312 // TODO: handle inline descriptors properly
314 // TODO: handle inline descriptors properly
313 // const ex = {
315 // const ex = {
314 // activate(ctx) {
316 // activate(ctx) {
315 // const value = ctx.activate(this.prop, "prop");
317 // const value = ctx.activate(this.prop, "prop");
316 // // some code
318 // // some code
317 // },
319 // },
318 // // will be turned to ReferenceDescriptor
320 // // will be turned to ReferenceDescriptor
319 // prop: { $dependency: "depName" }
321 // prop: { $dependency: "depName" }
320 // };
322 // };
321
323
322 this._leave();
324 this._leave();
323 return v;
325 return v;
324 }
326 }
325
327
326 async _visitArray(data: any[], name: string) {
328 async _visitArray(data: any[], name: string) {
327 if (data.constructor &&
329 if (data.constructor &&
328 data.constructor.prototype !== Array.prototype)
330 data.constructor.prototype !== Array.prototype)
329 return new ValueDescriptor(data);
331 return new ValueDescriptor(data);
330
332
331 this._enter(name);
333 this._enter(name);
332
334
333 const v = await mapAll(data, delegate(this, "_visit"));
335 const v = await mapAll(data, delegate(this, "_visit"));
334 this._leave();
336 this._leave();
335
337
336 return v;
338 return v;
337 }
339 }
338
340
339 _makeServiceParams(data: ServiceRegistration<any, S>) {
341 _makeServiceParams(data: ServiceRegistration<any, S>) {
340 const opts: any = {
342 const opts: any = {
341 };
343 };
342 if (data.services)
344 if (data.services)
343 opts.services = this._visitRegistrations(data.services, "services");
345 opts.services = this._visitRegistrations(data.services, "services");
344
346
345 if (data.inject) {
347 if (data.inject) {
346 this._enter("inject");
348 this._enter("inject");
347 opts.inject = mapAll(
349 opts.inject = mapAll(
348 data.inject instanceof Array ?
350 data.inject instanceof Array ?
349 data.inject :
351 data.inject :
350 [data.inject],
352 [data.inject],
351 delegate(this, "_visitObject")
353 delegate(this, "_visitObject")
352 );
354 );
353 this._leave();
355 this._leave();
354 }
356 }
355
357
356 if ("params" in data)
358 if ("params" in data)
357 opts.params = data.params instanceof Array ?
359 opts.params = data.params instanceof Array ?
358 this._visitArray(data.params, "params") :
360 this._visitArray(data.params, "params") :
359 this._visit(data.params, "params");
361 this._visit(data.params, "params");
360
362
361 if (data.activation) {
363 if (data.activation) {
362 opts.activation = this._getLifetimeManager(data.activation, data.typeId);
364 opts.activation = this._getLifetimeManager(data.activation, data.typeId);
363 }
365 }
364
366
365 if (data.cleanup)
367 if (data.cleanup)
366 opts.cleanup = data.cleanup;
368 opts.cleanup = data.cleanup;
367
369
368 return opts;
370 return opts;
369 }
371 }
370
372
371 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
373 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
372 this._enter(name);
374 this._enter(name);
373 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
375 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
374 this._leave();
376 this._leave();
375 return d;
377 return d;
376 }
378 }
377
379
378 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
380 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
379 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
381 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
380 this._enter(name);
382 this._enter(name);
381 const options = {
383 const options = {
382 name: data.$dependency,
384 name: data.$dependency,
383 optional: data.optional,
385 optional: data.optional,
384 default: data.default,
386 default: data.default,
385 services: data.services && await this._visitRegistrations(data.services, "services")
387 services: data.services && await this._visitRegistrations(data.services, "services")
386 };
388 };
387 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
389 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
388 this._leave();
390 this._leave();
389 return d;
391 return d;
390 }
392 }
391
393
392 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
394 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
393 argumentNotNull(data.$type, "data.$type");
395 argumentNotNull(data.$type, "data.$type");
394 this._enter(name);
396 this._enter(name);
395
397
396 const opts = this._makeServiceParams(data);
398 const opts = this._makeServiceParams(data);
397 if (data.$type instanceof Function) {
399 if (data.$type instanceof Function) {
398 opts.type = data.$type;
400 opts.type = data.$type;
399 } else {
401 } else {
400 const [moduleName, typeName] = data.$type.split(":", 2);
402 const [moduleName, typeName] = data.$type.split(":", 2);
401 const t = opts.type = this._resolveType(moduleName, typeName);
403 opts.type = this._resolveType(moduleName, typeName).then(t => {
402 if (!(t instanceof Function))
404 if (!(t instanceof Function))
403 throw Error("$type (" + data.$type + ") is not a constructable");
405 throw Error("$type (" + data.$type + ") is not a constructable");
406 return t;
407 });
404 }
408 }
405
409
406 const d = new TypeServiceDescriptor<S, any, any[]>(
410 const d = new TypeServiceDescriptor<S, any, any[]>(
407 await mapAll(opts)
411 await mapAll(opts)
408 );
412 );
409
413
410 this._leave();
414 this._leave();
411
415
412 return d;
416 return d;
413 }
417 }
414
418
415 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
419 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
416 argumentOfType(data.$factory, Function, "data.$factory");
420 argumentOfType(data.$factory, Function, "data.$factory");
417 this._enter(name);
421 this._enter(name);
418
422
419 const opts = this._makeServiceParams(data);
423 const opts = this._makeServiceParams(data);
420 opts.factory = data.$factory;
424 opts.factory = data.$factory;
421
425
422 const d = new FactoryServiceDescriptor<S, any, any[]>(
426 const d = new FactoryServiceDescriptor<S, any, any[]>(
423 await mapAll(opts)
427 await mapAll(opts)
424 );
428 );
425
429
426 this._leave();
430 this._leave();
427 return d;
431 return d;
428 }
432 }
429
433
430 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetimeManager {
434 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetime {
431 switch (activation) {
435 switch (activation) {
432 case "container":
436 case "container":
433 return this._container.getLifetimeManager();
437 return LifetimeManager.containerLifetime(this._container);
434 case "hierarchy":
438 case "hierarchy":
435 return LifetimeManager.hierarchyLifetime;
439 return LifetimeManager.hierarchyLifetime();
436 case "context":
440 case "context":
437 return LifetimeManager.contextLifetime;
441 return LifetimeManager.contextLifetime();
438 case "singleton":
442 case "singleton":
439 if (typeId === undefined)
443 if (typeId === undefined)
440 throw Error("The singleton activation requires a typeId");
444 throw Error("The singleton activation requires a typeId");
441 return LifetimeManager.singletonLifetime(typeId);
445 return LifetimeManager.singletonLifetime(typeId);
442 default:
446 default:
443 return LifetimeManager.empty;
447 return LifetimeManager.empty();
444 }
448 }
445 }
449 }
446 }
450 }
@@ -1,83 +1,83
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, TypeOfService, 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?: TypeOfService<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>) => TypeOfService<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: TypeOfService<S, K> | undefined;
20 _default: TypeOfService<S, K> | undefined;
21
21
22 _services: PartialServiceMap<S>;
22 _services: PartialServiceMap<S>;
23
23
24 constructor(opts: ReferenceDescriptorParams<S, K>) {
24 constructor(opts: ReferenceDescriptorParams<S, K>) {
25 argumentNotEmptyString(opts && opts.name, "opts.name");
25 argumentNotEmptyString(opts && opts.name, "opts.name");
26 this._name = opts.name;
26 this._name = opts.name;
27 this._optional = !!opts.optional;
27 this._optional = !!opts.optional;
28 this._default = opts.default;
28 this._default = opts.default;
29
29
30 this._services = (opts.services || {}) as PartialServiceMap<S>;
30 this._services = (opts.services || {}) as PartialServiceMap<S>;
31 }
31 }
32
32
33 activate(context: ActivationContext<S>) {
33 activate(context: ActivationContext<S>) {
34 // добавляем сервисы
34 // добавляем сервисы
35 if (this._services) {
35 if (this._services) {
36 each(this._services, (v, k) => context.register(k, v));
36 each(this._services, (v, k) => context.register(k, v));
37 }
37 }
38
38
39 const saved = context.clone();
39 const saved = context.clone();
40
40
41 return (cfg?: PartialServiceMap<S>) => {
41 return (cfg?: PartialServiceMap<S>): any => {
42 // защищаем контекст на случай исключения в процессе
42 // защищаем контекст на случай исключения в процессе
43 // активации
43 // активации
44 const ct = cfg ? saved.clone() : saved;
44 const ct = cfg ? saved.clone() : saved;
45 try {
45 try {
46 if (cfg) {
46 if (cfg) {
47 each(cfg, (v, k) => ct.register(k, v));
47 each(cfg, (v, k) => ct.register(k, v));
48 }
48 }
49
49
50 return this._optional ? ct.resolve(this._name, this._default) : ct
50 return this._optional ? ct.resolve(this._name, this._default) : ct
51 .resolve(this._name);
51 .resolve(this._name);
52 } catch (error) {
52 } catch (error) {
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 }
56 }
57
57
58 toString() {
58 toString() {
59 const opts = [];
59 const opts = [];
60 if (this._optional)
60 if (this._optional)
61 opts.push("optional");
61 opts.push("optional");
62
62
63 opts.push("lazy");
63 opts.push("lazy");
64
64
65 const parts = [
65 const parts = [
66 "@ref "
66 "@ref "
67 ];
67 ];
68 if (opts.length) {
68 if (opts.length) {
69 parts.push("{");
69 parts.push("{");
70 parts.push(opts.join());
70 parts.push(opts.join());
71 parts.push("} ");
71 parts.push("} ");
72 }
72 }
73
73
74 parts.push(this._name.toString());
74 parts.push(this._name.toString());
75
75
76 if (this._default !== undefined && this._default !== null) {
76 if (this._default !== undefined && this._default !== null) {
77 parts.push(" = ");
77 parts.push(" = ");
78 parts.push(String(this._default));
78 parts.push(String(this._default));
79 }
79 }
80
80
81 return parts.join("");
81 return parts.join("");
82 }
82 }
83 }
83 }
@@ -1,132 +1,176
1 import { IDestroyable, MapOf } from "../interfaces";
1 import { IDestroyable, MapOf } from "../interfaces";
2 import { argumentNotNull, isDestroyable } from "../safe";
2 import { argumentNotNull, isDestroyable } from "../safe";
3 import { ILifetimeManager, ILifetime } from "./interfaces";
3 import { ILifetimeManager, ILifetime } from "./interfaces";
4 import { ActivationContext } from "./ActivationContext";
4 import { ActivationContext } from "./ActivationContext";
5 import { Container } from "./Container";
5
6
6 function safeCall(item: () => void) {
7 function safeCall(item: () => void) {
7 try {
8 try {
8 item();
9 item();
9 } catch {
10 } catch {
10 // silence!
11 // silence!
11 }
12 }
12 }
13 }
13
14
14 const emptyLifetime: ILifetime = {
15 const emptyLifetime: ILifetime = {
15 has() {
16 has() {
16 return false;
17 return false;
17 },
18 },
18
19
19 enter() {
20 initialize() {
20
21
21 },
22 },
22
23
23 get() {
24 get() {
24 throw new Error("The specified item isn't registered with this lifetime manager");
25 throw new Error("The specified item isn't registered with this lifetime manager");
25 },
26 },
26
27
27 store() {
28 store() {
28 // does nothing
29 // does nothing
29 }
30 }
30
31
31 };
32 };
32
33
34 const unknownLifetime: ILifetime = {
35 has() {
36 throw new Error("The lifetime is unknown");
37 },
38 initialize() {
39 throw new Error("Can't call initialize on the unknown lifetime object");
40 },
41 get() {
42 throw new Error("The lifetime object isn't initialized");
43 },
44 store() {
45 throw new Error("Can't store a value in the unknown lifetime object");
46 }
47 }
48
33 let nextId = 0;
49 let nextId = 0;
34
50
35 export class LifetimeManager implements IDestroyable, ILifetimeManager {
51 export class LifetimeManager implements IDestroyable, ILifetimeManager {
36 private _cleanup: (() => void)[] = [];
52 private _cleanup: (() => void)[] = [];
37 private _cache: MapOf<any> = {};
53 private _cache: MapOf<any> = {};
38 private _destroyed = false;
54 private _destroyed = false;
39
55
40 private _pending: MapOf<boolean> = {};
56 private _pending: MapOf<boolean> = {};
41
57
42 initialize(): ILifetime {
58 create(): ILifetime {
43 const self = this;
59 const self = this;
44 const id = ++nextId;
60 const id = ++nextId;
45 return {
61 return {
46 has() {
62 has() {
47 return (id in self._cache);
63 return (id in self._cache);
48 },
64 },
49
65
50 get() {
66 get() {
51 const t = self._cache[id];
67 const t = self._cache[id];
52 if (t === undefined)
68 if (t === undefined)
53 throw new Error(`The item with with the key ${id} isn't found`);
69 throw new Error(`The item with with the key ${id} isn't found`);
54 return t;
70 return t;
55 },
71 },
56
72
57 enter() {
73 initialize() {
58 if (self._pending[id])
74 if (self._pending[id])
59 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
75 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
60 self._pending[id] = true;
76 self._pending[id] = true;
61 },
77 },
62
78
63 store(item: any, cleanup?: (item: any) => void) {
79 store(item: any, cleanup?: (item: any) => void) {
64 argumentNotNull(id, "id");
80 argumentNotNull(id, "id");
65 argumentNotNull(item, "item");
81 argumentNotNull(item, "item");
66
82
67 if (this.has())
83 if (this.has())
68 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
84 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
69 delete self._pending[id];
85 delete self._pending[id];
70
86
71 self._cache[id] = item;
87 self._cache[id] = item;
72
88
73 if (self._destroyed)
89 if (self._destroyed)
74 throw new Error("Lifetime manager is destroyed");
90 throw new Error("Lifetime manager is destroyed");
75 if (cleanup) {
91 if (cleanup) {
76 self._cleanup.push(() => cleanup(item));
92 self._cleanup.push(() => cleanup(item));
77 } else if (isDestroyable(item)) {
93 } else if (isDestroyable(item)) {
78 self._cleanup.push(() => item.destroy());
94 self._cleanup.push(() => item.destroy());
79 }
95 }
80 }
96 }
81 };
97 };
82 }
98 }
83
99
84 destroy() {
100 destroy() {
85 if (!this._destroyed) {
101 if (!this._destroyed) {
86 this._destroyed = true;
102 this._destroyed = true;
87 this._cleanup.forEach(safeCall);
103 this._cleanup.forEach(safeCall);
88 this._cleanup.length = 0;
104 this._cleanup.length = 0;
89 }
105 }
90 }
106 }
91
107
92 static readonly empty: ILifetimeManager = {
108 static empty(): ILifetime {
93 initialize(): ILifetime {
109 return emptyLifetime;
94 return emptyLifetime;
110 }
95 }
111
96 };
112 static hierarchyLifetime(): ILifetime {
113 let _lifetime = unknownLifetime;
114 return {
115 initialize(context: ActivationContext<any>) {
116 if (_lifetime !== unknownLifetime)
117 throw new Error("Cyclic reference activation detected");
97
118
98 static readonly hierarchyLifetime: ILifetimeManager = {
119 _lifetime = context.getContainer().getLifetimeManager().create(context);
99 initialize(context: ActivationContext<any>): ILifetime {
120 },
100 return context.getContainer().getLifetimeManager().initialize(context);
121 get() {
101 }
122 return _lifetime.get();
102 };
123 },
124 has() {
125 return _lifetime.has();
126 },
127 store(item: any, cleanup?: (item: any) => void) {
128 return _lifetime.store(item, cleanup);
129 }
130 };
131 }
103
132
104 static readonly contextLifetime: ILifetimeManager = {
133 static contextLifetime(): ILifetime {
105 initialize(context: ActivationContext<any>): ILifetime {
134 let _lifetime = unknownLifetime;
106 const id = String(++nextId);
135 return {
107 return {
136 initialize(context: ActivationContext<any>) {
108 enter() {
137 if (_lifetime !== unknownLifetime)
109 if (context.visit(id))
138 throw new Error("Cyclic reference detected");
110 throw new Error("Cyclic reference detected");
139 _lifetime = context.createLifetime();
111 },
140 },
112 get() {
141 get() {
113 return context.get(id);
142 return _lifetime.get();
114 },
143 },
115 has() {
144 has() {
116 return context.has(id);
145 return _lifetime.has();
117 },
146 },
118 store(item: any) {
147 store(item: any) {
119 context.store(id, item);
148 _lifetime.store(item);
120 }
149 }
121 };
150 };
122 }
151 }
123 };
124
152
125 static singletonLifetime(typeId: string): ILifetimeManager {
153 static singletonLifetime(typeId: string): ILifetime {
154 return emptyLifetime;
155 }
156
157 static containerLifetime(container: Container<any>) {
158 let _lifetime = unknownLifetime;
126 return {
159 return {
127 initialize() {
160 initialize(context: ActivationContext<any>) {
128 return emptyLifetime;
161 if (_lifetime !== unknownLifetime)
162 throw new Error("Cyclic reference detected");
163 _lifetime = container.getLifetimeManager().create(context);
164 },
165 get() {
166 return _lifetime.get();
167 },
168 has() {
169 return _lifetime.has();
170 },
171 store(item: any) {
172 _lifetime.store(item);
129 }
173 }
130 };
174 };
131 }
175 }
132 }
176 }
@@ -1,68 +1,88
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, TypeOfService, 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 /**
7 * The name of the descriptor
8 */
6 name: K;
9 name: K;
10
11 /**
12 * The flag that indicates that the referenced service isn't required to exist.
13 * If the reference is optional and the referenced service doesn't exist,
14 * the undefined or a default value will be returned.
15 */
7 optional?: boolean;
16 optional?: boolean;
17
18 /**
19 * a default value for the reference when the referenced service doesn't exist.
20 */
8 default?: TypeOfService<S, K>;
21 default?: TypeOfService<S, K>;
22
23 /**
24 * The service overrides
25 */
9 services?: PartialServiceMap<S>;
26 services?: PartialServiceMap<S>;
10 }
27 }
11
28
12 export class ReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
29 export class ReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
13 implements Descriptor<S, TypeOfService<S, K>> {
30 implements Descriptor<S, TypeOfService<S, K>> {
14
31
15 _name: K;
32 _name: K;
16
33
17 _optional = false;
34 _optional = false;
18
35
19 _default: TypeOfService<S, K> | undefined;
36 _default: TypeOfService<S, K> | undefined;
20
37
21 _services: PartialServiceMap<S>;
38 _services: PartialServiceMap<S>;
22
39
23 constructor(opts: ReferenceDescriptorParams<S, K>) {
40 constructor(opts: ReferenceDescriptorParams<S, K>) {
24 argumentNotEmptyString(opts && opts.name, "opts.name");
41 argumentNotEmptyString(opts && opts.name, "opts.name");
25 this._name = opts.name;
42 this._name = opts.name;
26 this._optional = !!opts.optional;
43 this._optional = !!opts.optional;
27 this._default = opts.default;
44 this._default = opts.default;
28
45
29 this._services = (opts.services || {}) as PartialServiceMap<S>;
46 this._services = (opts.services || {}) as PartialServiceMap<S>;
30 }
47 }
31
48
32 activate(context: ActivationContext<S>) {
49 /** This method activates the referenced service if one exists
50 * @param context activation context which is used during current activation
51 */
52 activate(context: ActivationContext<S>): any {
33 // добавляем сервисы
53 // добавляем сервисы
34 if (this._services) {
54 if (this._services) {
35 each(this._services, (v, k) => context.register(k, v));
55 each(this._services, (v, k) => context.register(k, v));
36 }
56 }
37
57
38 const res = this._optional ?
58 const res = this._optional ?
39 context.resolve(this._name, this._default) :
59 context.resolve(this._name, this._default) :
40 context.resolve(this._name);
60 context.resolve(this._name);
41
61
42 return res;
62 return res;
43 }
63 }
44
64
45 toString() {
65 toString() {
46 const opts = [];
66 const opts = [];
47 if (this._optional)
67 if (this._optional)
48 opts.push("optional");
68 opts.push("optional");
49
69
50 const parts = [
70 const parts = [
51 "@ref "
71 "@ref "
52 ];
72 ];
53 if (opts.length) {
73 if (opts.length) {
54 parts.push("{");
74 parts.push("{");
55 parts.push(opts.join());
75 parts.push(opts.join());
56 parts.push("} ");
76 parts.push("} ");
57 }
77 }
58
78
59 parts.push(this._name.toString());
79 parts.push(this._name.toString());
60
80
61 if (this._default !== undefined && this._default !== null) {
81 if (this._default !== undefined && this._default !== null) {
62 parts.push(" = ");
82 parts.push(" = ");
63 parts.push(String(this._default));
83 parts.push(String(this._default));
64 }
84 }
65
85
66 return parts.join("");
86 return parts.join("");
67 }
87 }
68 }
88 }
@@ -1,157 +1,154
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ServiceMap, PartialServiceMap, ILifetimeManager, ILifetime } from "./interfaces";
2 import { Descriptor, ServiceMap, PartialServiceMap, ILifetimeManager, ILifetime } from "./interfaces";
3 import { 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
8
9 const trace = TraceSource.get("@implab/core/di/ActivationContext");
9 const trace = TraceSource.get("@implab/core/di/ActivationContext");
10
10
11 function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) {
11 function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) {
12
12
13 const m = target[method];
13 const m = target[method];
14 if (!m || typeof m !== "function")
14 if (!m || typeof m !== "function")
15 throw new Error("Method '" + method + "' not found");
15 throw new Error("Method '" + method + "' not found");
16
16
17 if (args instanceof Array)
17 if (args instanceof Array)
18 return m.apply(target, _parse(args, context, "." + method));
18 return m.apply(target, _parse(args, context, "." + method));
19 else
19 else
20 return m.call(target, _parse(args, context, "." + method));
20 return m.call(target, _parse(args, context, "." + method));
21 }
21 }
22
22
23 function makeCleanupCallback<T>(method: Cleaner<T>) {
23 function makeCleanupCallback<T>(method: Cleaner<T>) {
24 if (typeof (method) === "function") {
24 if (typeof (method) === "function") {
25 return (target: T) => {
25 return (target: T) => {
26 method(target);
26 method(target);
27 };
27 };
28 } else {
28 } else {
29 return (target: T) => {
29 return (target: T) => {
30 const m = target[method] as any;
30 const m = target[method] as any;
31 m.apply(target);
31 m.apply(target);
32 };
32 };
33 }
33 }
34 }
34 }
35
35
36 function _parse(value: any, context: ActivationContext<any>, path: string): any {
36 function _parse(value: any, context: ActivationContext<any>, path: string): any {
37 if (isPrimitive(value))
37 if (isPrimitive(value))
38 return value as any;
38 return value as any;
39
39
40 trace.debug("parse {0}", path);
40 trace.debug("parse {0}", path);
41
41
42 if (isDescriptor(value))
42 if (isDescriptor(value))
43 return context.activate(value, path);
43 return context.activate(value, path);
44
44
45 if (value instanceof Array)
45 if (value instanceof Array)
46 return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any;
46 return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any;
47
47
48 const t: any = {};
48 const t: any = {};
49
49
50 keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`));
50 keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`));
51
51
52 return t;
52 return t;
53 }
53 }
54
54
55 export type Cleaner<T> = ((x: T) => void) | MatchingMemberKeys<() => void, T>;
55 export type Cleaner<T> = ((x: T) => void) | MatchingMemberKeys<() => void, T>;
56
56
57 export type InjectionSpec<T> = {
57 export type InjectionSpec<T> = {
58 [m in keyof T]?: any;
58 [m in keyof T]?: any;
59 };
59 };
60
60
61 export interface ServiceDescriptorParams<S extends object, T, P extends any[]> {
61 export interface ServiceDescriptorParams<S extends object, T, P extends any[]> {
62 lifetime?: ILifetimeManager;
62 lifetime?: ILifetime;
63
63
64 params?: P;
64 params?: P;
65
65
66 inject?: InjectionSpec<T>[];
66 inject?: InjectionSpec<T>[];
67
67
68 services?: PartialServiceMap<S>;
68 services?: PartialServiceMap<S>;
69
69
70 cleanup?: Cleaner<T>;
70 cleanup?: Cleaner<T>;
71 }
71 }
72
72
73 export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> {
73 export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> {
74 _services: ServiceMap<S>;
74 _services: ServiceMap<S>;
75
75
76 _params: P | undefined;
76 _params: P | undefined;
77
77
78 _inject: InjectionSpec<T>[];
78 _inject: InjectionSpec<T>[];
79
79
80 _cleanup: ((item: T) => void) | undefined;
80 _cleanup: ((item: T) => void) | undefined;
81
81
82 _lifetimeManager = LifetimeManager.empty;
82 _lifetime = LifetimeManager.empty();
83
83
84 _objectLifetime: ILifetime | undefined;
84 _objectLifetime: ILifetime | undefined;
85
85
86 constructor(opts: ServiceDescriptorParams<S, T, P>) {
86 constructor(opts: ServiceDescriptorParams<S, T, P>) {
87
87
88 if (opts.lifetime)
88 if (opts.lifetime)
89 this._lifetimeManager = opts.lifetime;
89 this._lifetime = opts.lifetime;
90
90
91 if (!isNull(opts.params))
91 if (!isNull(opts.params))
92 this._params = opts.params;
92 this._params = opts.params;
93
93
94 this._inject = opts.inject || [];
94 this._inject = opts.inject || [];
95
95
96 this._services = (opts.services || {}) as ServiceMap<S>;
96 this._services = (opts.services || {}) as ServiceMap<S>;
97
97
98 if (opts.cleanup) {
98 if (opts.cleanup) {
99 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
99 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
100 throw new Error(
100 throw new Error(
101 "The cleanup parameter must be either a function or a function name");
101 "The cleanup parameter must be either a function or a function name");
102
102
103 this._cleanup = makeCleanupCallback(opts.cleanup);
103 this._cleanup = makeCleanupCallback(opts.cleanup);
104 }
104 }
105 }
105 }
106
106
107 activate(context: ActivationContext<S>) {
107 activate(context: ActivationContext<S>) {
108 if (!this._objectLifetime)
108 const lifetime = this._lifetime;
109 this._objectLifetime = this._lifetimeManager.initialize(context);
110
111 const lifetime = this._objectLifetime;
112
109
113 if (lifetime.has()) {
110 if (lifetime.has()) {
114 return lifetime.get();
111 return lifetime.get();
115 } else {
112 } else {
116 lifetime.enter();
113 lifetime.initialize(context);
117 const instance = this._create(context);
114 const instance = this._create(context);
118 lifetime.store(instance, this._cleanup);
115 lifetime.store(instance, this._cleanup);
119 return instance;
116 return instance;
120 }
117 }
121 }
118 }
122
119
123 _factory(...params: any[]): T {
120 _factory(...params: any[]): T {
124 throw Error("Not implemented");
121 throw Error("Not implemented");
125 }
122 }
126
123
127 _create(context: ActivationContext<S>) {
124 _create(context: ActivationContext<S>) {
128 trace.debug(`constructing ${context._name}`);
125 trace.debug(`constructing ${context._name}`);
129
126
130 if (this._services) {
127 if (this._services) {
131 keys(this._services).forEach(p => context.register(p, this._services[p]));
128 keys(this._services).forEach(p => context.register(p, this._services[p]));
132 }
129 }
133
130
134 let instance: T;
131 let instance: T;
135
132
136 if (this._params === undefined) {
133 if (this._params === undefined) {
137 instance = this._factory();
134 instance = this._factory();
138 } else if (this._params instanceof Array) {
135 } else if (this._params instanceof Array) {
139 instance = this._factory.apply(this, _parse(this._params, context, "args"));
136 instance = this._factory.apply(this, _parse(this._params, context, "args"));
140 } else {
137 } else {
141 instance = this._factory(_parse(this._params, context, "args"));
138 instance = this._factory(_parse(this._params, context, "args"));
142 }
139 }
143
140
144 if (this._inject) {
141 if (this._inject) {
145 this._inject.forEach(spec => {
142 this._inject.forEach(spec => {
146 for (const m in spec)
143 for (const m in spec)
147 injectMethod(instance, m, context, spec[m]);
144 injectMethod(instance, m, context, spec[m]);
148 });
145 });
149 }
146 }
150 return instance;
147 return instance;
151 }
148 }
152
149
153 clone() {
150 clone() {
154 return Object.create(this);
151 return Object.create(this);
155 }
152 }
156
153
157 }
154 }
@@ -1,58 +1,54
1 import { Resolver, ServiceModule, LazyDependencyOptions, DependencyOptions } from "./interfaces";
1 import { Resolver, LazyDependencyOptions, DependencyOptions } from "./interfaces";
2 import { AnnotationBuilder } from "../Annotations";
3 import { Container } from "../Container";
2 import { Container } from "../Container";
4 import { Descriptor, ILifetime, ContainerKeys } from "../interfaces";
3 import { Descriptor, ILifetime, ContainerKeys } from "../interfaces";
5 import { ActivationContext } from "../ActivationContext";
4 import { ActivationContext } from "../ActivationContext";
6
5
7 export class DescriptorBuilder<T, S extends object> {
6 export class DescriptorBuilder<T, S extends object> {
8 readonly _container: Container<S>;
7 readonly _container: Container<S>;
9 readonly _cb: (d: Descriptor<S, T>) => void;
8 readonly _cb: (d: Descriptor<S, T>) => void;
10
9
11 constructor(container: Container<S>, cb: (d: Descriptor<S, T>) => void) {
10 constructor(container: Container<S>, cb: (d: Descriptor<S, T>) => void) {
12 this._container = container;
11 this._container = container;
13 this._cb = cb;
12 this._cb = cb;
14 }
13 }
15 service(service: AnnotationBuilder<T, S> | ServiceModule<T, S>) {
16
17 }
18
14
19 factory(f: (resolve: Resolver<S>, activate: (lifetime: ILifetime, factory: () => any, cleanup?: (item: any) => void) => any) => T): void {
15 factory(f: (resolve: Resolver<S>, activate: (lifetime: ILifetime, factory: () => any, cleanup?: (item: any) => void) => any) => T): void {
20 this._cb({
16 this._cb({
21 activate(context: ActivationContext<S>) {
17 activate(context: ActivationContext<S>) {
22 const resolve = (name: ContainerKeys<S>, opts?: DependencyOptions | LazyDependencyOptions) => {
18 const resolve = (name: ContainerKeys<S>, opts?: DependencyOptions | LazyDependencyOptions) => {
23 if (opts && "lazy" in opts && opts.lazy) {
19 if (opts && "lazy" in opts && opts.lazy) {
24 const c2 = context.clone();
20 const c2 = context.clone();
25 return () => {
21 return () => {
26 return opts.optional ? c2.resolve(name, opts.default) : c2.resolve(name);
22 return opts.optional ? c2.resolve(name, opts.default) : c2.resolve(name);
27 };
23 };
28 } else {
24 } else {
29 return opts && opts.optional ? context.resolve(name, opts.default) : context.resolve(name);
25 return opts && opts.optional ? context.resolve(name, opts.default) : context.resolve(name);
30 }
26 }
31 };
27 };
32
28
33 const activate = (lifetime: ILifetime, factory: () => any, cleanup?: (item: any) => void) => {
29 const activate = (lifetime: ILifetime, factory: () => any, cleanup?: (item: any) => void) => {
34 if (lifetime.has()) {
30 if (lifetime.has()) {
35 return lifetime.get();
31 return lifetime.get();
36 } else {
32 } else {
37 lifetime.enter();
33 lifetime.initialize(context);
38 const instance = factory();
34 const instance = factory();
39 lifetime.store(instance, cleanup);
35 lifetime.store(instance, cleanup);
40 return instance;
36 return instance;
41 }
37 }
42
38
43 };
39 };
44
40
45 return f(resolve, activate);
41 return f(resolve, activate);
46 }
42 }
47 });
43 });
48 }
44 }
49
45
50 value(v: T): void {
46 value(v: T): void {
51 this._cb({
47 this._cb({
52 activate() {
48 activate() {
53 return v;
49 return v;
54 }
50 }
55 });
51 });
56 }
52 }
57
53
58 }
54 }
@@ -1,48 +1,48
1 import { primitive } from "../../safe";
1 import { primitive } from "../../safe";
2 import { AnnotationBuilder } from "../Annotations";
2 import { AnnotationBuilder } from "../Annotations";
3 import { ILifetime, TypeOfService, ContainerKeys } from "../interfaces";
3 import { ILifetime, TypeOfService, ContainerKeys } from "../interfaces";
4
4
5 export interface DependencyOptions {
5 export interface DependencyOptions {
6 optional?: boolean;
6 optional?: boolean;
7 default?: any;
7 default?: any;
8 }
8 }
9
9
10 export interface LazyDependencyOptions extends DependencyOptions {
10 export interface LazyDependencyOptions extends DependencyOptions {
11 lazy: true;
11 lazy: true;
12 }
12 }
13
13
14 export type ExtractService<K, S> = K extends keyof S ? S[K] : never;
14 export type ExtractService<K, S> = K extends keyof S ? S[K] : never;
15
15
16 export type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
16 export type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
17 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
17 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
18 D extends { $type: new (...args: any[]) => infer I } ? I :
18 D extends { $type: new (...args: any[]) => infer I } ? I :
19 D extends { $factory: (...args: any[]) => infer R } ? R :
19 D extends { $factory: (...args: any[]) => infer R } ? R :
20 WalkDependencies<D, S>;
20 WalkDependencies<D, S>;
21
21
22 export type WalkDependencies<D, S> = D extends primitive ? D :
22 export type WalkDependencies<D, S> = D extends primitive ? D :
23 { [K in keyof D]: ExtractDependency<D[K], S> };
23 { [K in keyof D]: ExtractDependency<D[K], S> };
24
24
25 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"> = {
26 [m in M]: AnnotationBuilder<T, S>;
26 [m in M]: AnnotationBuilder<T, S>;
27 };
27 };
28
28
29 export type InferReferenceType<S extends object, K extends keyof ContainerKeys<S>, O> = O extends { default: infer X } ? (TypeOfService<S, K> | X) :
29 export type InferReferenceType<S extends object, K extends ContainerKeys<S>, O> = O extends { default: infer X } ? (TypeOfService<S, K> | X) :
30 O extends { optional: true } ? (TypeOfService<S, K> | undefined) :
30 O extends { optional: true } ? (TypeOfService<S, K> | undefined) :
31 TypeOfService<S, K>;
31 TypeOfService<S, K>;
32
32
33 export interface Resolver<S extends object> {
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>;
34 <K extends 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>;
35 <K extends ContainerKeys<S>, O extends DependencyOptions>(this: void, name: K, opts?: O): InferReferenceType<S, K, O>;
36 }
36 }
37
37
38 export interface DescriptorBuilder<T, S extends object> {
38 export interface DescriptorBuilder<T, S extends object> {
39 service(service: AnnotationBuilder<T, S> | ServiceModule<T, S>): void;
39 service(service: AnnotationBuilder<T, S> | ServiceModule<T, S>): void;
40
40
41 factory(f: (resolve: Resolver<S>, activate: <T2>(lifetime: ILifetime, factory: () => T2, cleanup?: (item: T2) => void) => T2) => T): void;
41 factory(f: (resolve: Resolver<S>, activate: <T2>(lifetime: ILifetime, factory: () => T2, cleanup?: (item: T2) => void) => T2) => T): void;
42
42
43 value(v: T): void;
43 value(v: T): void;
44 }
44 }
45
45
46 export interface Configuration<S extends object, Y extends keyof S = keyof S> {
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>>;
47 register<K extends Y>(name: K, builder: (d: DescriptorBuilder<S[K], S>) => void): Configuration<S, Exclude<Y, K>>;
48 }
48 }
@@ -1,57 +1,57
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2
2
3 export interface Descriptor<S extends object = any, T = any> {
3 export interface Descriptor<S extends object = any, T = any> {
4 activate(context: ActivationContext<S>): T;
4 activate(context: ActivationContext<S>): T;
5 }
5 }
6
6
7 export type ServiceMap<S extends object> = {
7 export type ServiceMap<S extends object> = {
8 [k in keyof S]: Descriptor<S, S[k]>;
8 [k in keyof S]: Descriptor<S, S[k]>;
9 };
9 };
10
10
11 export type ContainerKeys<S extends object> = keyof S | keyof ContainerProvided<S>;
11 export type ContainerKeys<S extends object> = keyof S | keyof ContainerProvided<S>;
12
12
13 export type TypeOfService<S extends object, K> =
13 export type TypeOfService<S extends object, K> =
14 K extends keyof ContainerProvided<S> ? ContainerProvided<S>[K] :
14 K extends keyof ContainerProvided<S> ? ContainerProvided<S>[K] :
15 K extends keyof S ? S[K] : never;
15 K extends keyof S ? S[K] : never;
16
16
17 export type ContainerServiceMap<S extends object> = {
17 export type ContainerServiceMap<S extends object> = {
18 [K in ContainerKeys<S>]: Descriptor<S, TypeOfService<S, K>>;
18 [K in ContainerKeys<S>]: Descriptor<S, TypeOfService<S, K>>;
19 };
19 };
20
20
21 export type PartialServiceMap<S extends object> = {
21 export type PartialServiceMap<S extends object> = {
22 [k in keyof S]?: Descriptor<S, S[k]>;
22 [k in keyof S]?: Descriptor<S, S[k]>;
23 };
23 };
24
24
25 export interface ServiceLocator<S extends object> {
25 export interface ServiceLocator<S extends object> {
26 resolve<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>): TypeOfService<S, K>;
26 resolve<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>): TypeOfService<S, K>;
27 }
27 }
28
28
29 export interface ContainerProvided<S extends object> {
29 export interface ContainerProvided<S extends object> {
30 container: ServiceLocator<S>;
30 container: ServiceLocator<S>;
31 }
31 }
32
32
33 export type ContainerRegistered<S extends object> = /*{
33 export type ContainerRegistered<S extends object> = /*{
34 [K in Exclude<keyof S, keyof ContainerProvided<S>>]: S[K];
34 [K in Exclude<keyof S, keyof ContainerProvided<S>>]: S[K];
35 };*/
35 };*/
36 Exclude<S, ContainerProvided<S>>;
36 Exclude<S, ContainerProvided<S>>;
37
37
38 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
38 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
39
39
40 export interface ILifetimeManager {
40 export interface ILifetimeManager {
41 initialize(context: ActivationContext<any>): ILifetime;
41 create(context: ActivationContext<any>): ILifetime;
42 }
42 }
43
43
44 /**
44 /**
45 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
45 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
46 * свой собственный объект `ILifetime`, который создается при первой активации
46 * свой собственный объект `ILifetime`, который создается при первой активации
47 */
47 */
48 export interface ILifetime {
48 export interface ILifetime {
49 /** Проверяет, что уже создан экземпляр объекта */
49 /** Проверяет, что уже создан экземпляр объекта */
50 has(): boolean;
50 has(): boolean;
51
51
52 get(): any;
52 get(): any;
53
53
54 enter(): void;
54 initialize(context: ActivationContext<any>): void;
55
55
56 store(item: any, cleanup?: (item: any) => void): void;
56 store(item: any, cleanup?: (item: any) => void): void;
57 }
57 }
@@ -1,23 +1,12
1 import { isPrimitive } from "../safe";
1 import { isPrimitive } from "../safe";
2 import { Descriptor } from "./interfaces";
2 import { Descriptor } from "./interfaces";
3 import { AnnotationBuilder } from "./Annotations";
4 import { Configuration } from "./fluent/Configuration";
3 import { Configuration } from "./fluent/Configuration";
5
4
6 export function isDescriptor(x: any): x is Descriptor {
5 export function isDescriptor(x: any): x is Descriptor {
7 return (!isPrimitive(x)) &&
6 return (!isPrimitive(x)) &&
8 (x.activate instanceof Function);
7 (x.activate instanceof Function);
9 }
8 }
10
9
11 export function declare<S extends object>() {
10 export function configure<S extends object>() {
12 return {
11 return new Configuration<S>();
13 annotate<T>() {
14 return new AnnotationBuilder<T, S>();
15 },
16 configure(): Configuration<S> {
17 throw new Error();
18 },
19 dependency() {
20 throw new Error();
21 }
22 };
23 }
12 }
@@ -1,505 +1,500
1 import { ICancellable, Constructor, IDestroyable } from "./interfaces";
1 import { ICancellable, Constructor, IDestroyable } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
2 import { Cancellation } from "./Cancellation";
3
3
4 let _nextOid = 0;
4 let _nextOid = 0;
5 const _oid = typeof Symbol === "function" ?
5 const _oid = typeof Symbol === "function" ?
6 Symbol("__implab__oid__") :
6 Symbol("__implab__oid__") :
7 "__implab__oid__";
7 "__implab__oid__";
8
8
9 export function oid(instance: any): string | undefined {
9 export function oid(instance: any): string | undefined {
10 if (isNull(instance))
10 if (isNull(instance))
11 return undefined;
11 return undefined;
12
12
13 if (_oid in instance)
13 if (_oid in instance)
14 return instance[_oid];
14 return instance[_oid];
15 else
15 else
16 return (instance[_oid] = "oid_" + (++_nextOid));
16 return (instance[_oid] = "oid_" + (++_nextOid));
17 }
17 }
18
18
19 export function keys<T>(arg: T): (Extract<keyof T, string>)[] {
19 export function keys<T>(arg: T): (Extract<keyof T, string>)[] {
20 return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : [];
20 return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : [];
21 }
21 }
22
22
23 export function isKeyof<T>(k: string, target: T): k is Extract<keyof T, string> {
23 export function isKeyof<T>(k: string, target: T): k is Extract<keyof T, string> {
24 return target && typeof target === "object" && k in target;
24 return target && typeof target === "object" && k in target;
25 }
25 }
26
26
27 export function argumentNotNull(arg: any, name: string) {
27 export function argumentNotNull(arg: any, name: string) {
28 if (arg === null || arg === undefined)
28 if (arg === null || arg === undefined)
29 throw new Error("The argument " + name + " can't be null or undefined");
29 throw new Error("The argument " + name + " can't be null or undefined");
30 }
30 }
31
31
32 export function argumentNotEmptyString(arg: any, name: string) {
32 export function argumentNotEmptyString(arg: any, name: string) {
33 if (typeof (arg) !== "string" || !arg.length)
33 if (typeof (arg) !== "string" || !arg.length)
34 throw new Error("The argument '" + name + "' must be a not empty string");
34 throw new Error("The argument '" + name + "' must be a not empty string");
35 }
35 }
36
36
37 export function argumentNotEmptyArray(arg: any, name: string) {
37 export function argumentNotEmptyArray(arg: any, name: string) {
38 if (!(arg instanceof Array) || !arg.length)
38 if (!(arg instanceof Array) || !arg.length)
39 throw new Error("The argument '" + name + "' must be a not empty array");
39 throw new Error("The argument '" + name + "' must be a not empty array");
40 }
40 }
41
41
42 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
42 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
43 if (!(arg instanceof type))
43 if (!(arg instanceof type))
44 throw new Error("The argument '" + name + "' type doesn't match");
44 throw new Error("The argument '" + name + "' type doesn't match");
45 }
45 }
46
46
47 export function isObject(val: any): val is object {
47 export function isObject(val: any): val is object {
48 return typeof val === "object";
48 return typeof val === "object";
49 }
49 }
50
50
51 export function isNull(val: any): val is null | undefined {
51 export function isNull(val: any): val is null | undefined {
52 return (val === null || val === undefined);
52 return (val === null || val === undefined);
53 }
53 }
54
54
55 export type primitive = symbol | string | number | boolean | undefined | null;
55 export type primitive = symbol | string | number | boolean | undefined | null;
56
56
57 export function isPrimitive(val: any): val is primitive {
57 export function isPrimitive(val: any): val is primitive {
58 return (val === null || val === undefined || typeof (val) === "string" ||
58 return (val === null || val === undefined || typeof (val) === "string" ||
59 typeof (val) === "number" || typeof (val) === "boolean");
59 typeof (val) === "number" || typeof (val) === "boolean");
60 }
60 }
61
61
62 export function isInteger(val: any): val is number {
62 export function isInteger(val: any): val is number {
63 return parseInt(val, 10) === val;
63 return parseInt(val, 10) === val;
64 }
64 }
65
65
66 export function isNumber(val: any): val is number {
66 export function isNumber(val: any): val is number {
67 return parseFloat(val) === val;
67 return parseFloat(val) === val;
68 }
68 }
69
69
70 export function isString(val: any): val is string {
70 export function isString(val: any): val is string {
71 return typeof (val) === "string" || val instanceof String;
71 return typeof (val) === "string" || val instanceof String;
72 }
72 }
73
73
74 export function isPromise<T = any>(val: any): val is PromiseLike<T> {
74 export function isPromise<T = any>(val: any): val is PromiseLike<T> {
75 return val && typeof val.then === "function";
75 return val && typeof val.then === "function";
76 }
76 }
77
77
78 export function isCancellable(val: any): val is ICancellable {
78 export function isCancellable(val: any): val is ICancellable {
79 return val && typeof val.cancel === "function";
79 return val && typeof val.cancel === "function";
80 }
80 }
81
81
82 export function isNullOrEmptyString(val: any): val is ("" | null | undefined) {
82 export function isNullOrEmptyString(val: any): val is ("" | null | undefined) {
83 return (val === null || val === undefined ||
83 return (val === null || val === undefined ||
84 ((typeof (val) === "string" || val instanceof String) && val.length === 0));
84 ((typeof (val) === "string" || val instanceof String) && val.length === 0));
85 }
85 }
86
86
87 export function isNotEmptyArray<T = any>(arg: any): arg is T[] {
87 export function isNotEmptyArray<T = any>(arg: any): arg is T[] {
88 return (arg instanceof Array && arg.length > 0);
88 return (arg instanceof Array && arg.length > 0);
89 }
89 }
90
90
91 function _isStrictMode(this: any) {
91 function _isStrictMode(this: any) {
92 return !this;
92 return !this;
93 }
93 }
94
94
95 function _getNonStrictGlobal(this: any) {
95 function _getNonStrictGlobal(this: any) {
96 return this;
96 return this;
97 }
97 }
98
98
99 export function getGlobal() {
99 export function getGlobal() {
100 // in es3 we can't use indirect call to eval, since it will
100 // in es3 we can't use indirect call to eval, since it will
101 // be executed in the current call context.
101 // be executed in the current call context.
102 if (!_isStrictMode()) {
102 if (!_isStrictMode()) {
103 return _getNonStrictGlobal();
103 return _getNonStrictGlobal();
104 } else {
104 } else {
105 // tslint:disable-next-line:no-eval
105 // tslint:disable-next-line:no-eval
106 return eval.call(null, "this");
106 return eval.call(null, "this");
107 }
107 }
108 }
108 }
109
109
110 export function get(member: string, context?: object) {
110 export function get(member: string, context?: object) {
111 argumentNotEmptyString(member, "member");
111 argumentNotEmptyString(member, "member");
112 let that = context || getGlobal();
112 let that = context || getGlobal();
113 const parts = member.split(".");
113 const parts = member.split(".");
114 for (const m of parts) {
114 for (const m of parts) {
115 if (!m)
115 if (!m)
116 continue;
116 continue;
117 if (isNull(that = that[m]))
117 if (isNull(that = that[m]))
118 break;
118 break;
119 }
119 }
120 return that;
120 return that;
121 }
121 }
122
122
123 /**
123 /**
124 * Выполняет метод для каждого элемента массива, останавливается, когда
124 * Выполняет метод для каждого элемента массива, останавливается, когда
125 * либо достигнут конец массива, либо функция <c>cb</c> вернула
125 * либо достигнут конец массива, либо функция <c>cb</c> вернула
126 * значение.
126 * значение.
127 *
127 *
128 * @param {Array | Object} obj массив элементов для просмотра
128 * @param {Array | Object} obj массив элементов для просмотра
129 * @param {Function} cb функция, вызываемая для каждого элемента
129 * @param {Function} cb функция, вызываемая для каждого элемента
130 * @param {Object} thisArg значение, которое будет передано в качестве
130 * @param {Object} thisArg значение, которое будет передано в качестве
131 * <c>this</c> в <c>cb</c>.
131 * <c>this</c> в <c>cb</c>.
132 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
132 * @returns {void}
133 * если достигнут конец массива.
134 */
133 */
135 export function each<T>(obj: T, cb: <X extends keyof T>(v: NonNullable<T[X]>, k: X) => void): void;
134 export function each<T>(obj: T, cb: <X extends keyof T>(v: NonNullable<T[X]>, k: X) => void): void;
136 export function each<T>(array: T[], cb: (v: T, i: number) => void): void;
135 export function each<T>(array: T[], cb: (v: T, i: number) => void): void;
137 export function each(obj: any, cb: any, thisArg?: any): any;
136 export function each(obj: any, cb: any, thisArg?: any): any;
138 export function each(obj: any, cb: any, thisArg?: any) {
137 export function each(obj: any, cb: any, thisArg?: any) {
139 argumentNotNull(cb, "cb");
138 argumentNotNull(cb, "cb");
140 if (obj instanceof Array) {
139 if (obj instanceof Array) {
140 let v: any;
141 for (let i = 0; i < obj.length; i++) {
141 for (let i = 0; i < obj.length; i++) {
142 const x = cb.call(thisArg, obj[i], i);
142 v = obj[i];
143 if (x !== undefined)
143 if (v !== undefined)
144 return x;
144 cb.call(thisArg, v, i);
145 }
145 }
146 } else {
146 } else {
147 const _keys = Object.keys(obj);
147 Object.keys(obj).forEach(k => obj[k] !== undefined && cb.call(thisArg, obj[k], k));
148 for (const k of _keys) {
149 const x = cb.call(thisArg, obj[k], k);
150 if (x !== undefined)
151 return x;
152 }
153 }
148 }
154 }
149 }
155
150
156 /** Copies property values from a source object to the destination and returns
151 /** Copies property values from a source object to the destination and returns
157 * the destination onject.
152 * the destination onject.
158 *
153 *
159 * @param dest The destination object into which properties from the source
154 * @param dest The destination object into which properties from the source
160 * object will be copied.
155 * object will be copied.
161 * @param source The source of values which will be copied to the destination
156 * @param source The source of values which will be copied to the destination
162 * object.
157 * object.
163 * @param template An optional parameter specifies which properties should be
158 * @param template An optional parameter specifies which properties should be
164 * copied from the source and how to map them to the destination. If the
159 * copied from the source and how to map them to the destination. If the
165 * template is an array it contains the list of property names to copy from the
160 * template is an array it contains the list of property names to copy from the
166 * source to the destination. In case of object the templates contains the map
161 * source to the destination. In case of object the templates contains the map
167 * where keys are property names in the source and the values are property
162 * where keys are property names in the source and the values are property
168 * names in the destination object. If the template isn't specified then the
163 * names in the destination object. If the template isn't specified then the
169 * own properties of the source are entirely copied to the destination.
164 * own properties of the source are entirely copied to the destination.
170 *
165 *
171 */
166 */
172 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: keyof S[]): T & S;
167 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: keyof S[]): T & S;
173 export function mixin<T extends object, S extends object, R extends object = T>(dest: T, source: S, template: { [p in keyof S]?: keyof R; }): T & R;
168 export function mixin<T extends object, S extends object, R extends object = T>(dest: T, source: S, template: { [p in keyof S]?: keyof R; }): T & R;
174 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: any): any {
169 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: any): any {
175 argumentNotNull(dest, "dest");
170 argumentNotNull(dest, "dest");
176 const _res: any = dest as any;
171 const _res: any = dest as any;
177
172
178 if (isPrimitive(source))
173 if (isPrimitive(source))
179 return _res;
174 return _res;
180
175
181 if (template instanceof Array) {
176 if (template instanceof Array) {
182 template.forEach(p => {
177 template.forEach(p => {
183 if (isKeyof(p, source))
178 if (isKeyof(p, source))
184 _res[p] = source[p];
179 _res[p] = source[p];
185 });
180 });
186 } else if (template) {
181 } else if (template) {
187 keys(source).forEach(p => {
182 keys(source).forEach(p => {
188 if (isKeyof(p, template))
183 if (isKeyof(p, template))
189 _res[template[p]] = source[p];
184 _res[template[p]] = source[p];
190 });
185 });
191 } else {
186 } else {
192 keys(source).forEach(p => _res[p] = source[p]);
187 keys(source).forEach(p => _res[p] = source[p]);
193 }
188 }
194
189
195 return _res;
190 return _res;
196 }
191 }
197
192
198 /** Wraps the specified function to emulate an asynchronous execution.
193 /** Wraps the specified function to emulate an asynchronous execution.
199 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
194 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
200 * @param{Function|String} fn [Required] Function wich will be wrapped.
195 * @param{Function|String} fn [Required] Function wich will be wrapped.
201 */
196 */
202 export function async<T, F extends (...args: any[]) => T | PromiseLike<T>>(
197 export function async<T, F extends (...args: any[]) => T | PromiseLike<T>>(
203 fn: F,
198 fn: F,
204 thisArg?: ThisParameterType<F>
199 thisArg?: ThisParameterType<F>
205 ): (...args: Parameters<F>) => PromiseLike<T>;
200 ): (...args: Parameters<F>) => PromiseLike<T>;
206 export function async<T, M extends string, O extends { [m in M]?: (...args: any[]) => T | PromiseLike<T> }>(
201 export function async<T, M extends string, O extends { [m in M]?: (...args: any[]) => T | PromiseLike<T> }>(
207 fn: M,
202 fn: M,
208 thisArg: O
203 thisArg: O
209 ): (...args: Parameters<NonNullable<O[M]>>) => PromiseLike<T>;
204 ): (...args: Parameters<NonNullable<O[M]>>) => PromiseLike<T>;
210 export function async(_fn: any, thisArg: any): (...args: any[]) => PromiseLike<any> {
205 export function async(_fn: any, thisArg: any): (...args: any[]) => PromiseLike<any> {
211 let fn = _fn;
206 let fn = _fn;
212
207
213 if (arguments.length === 2 && !(fn instanceof Function))
208 if (arguments.length === 2 && !(fn instanceof Function))
214 fn = thisArg[fn];
209 fn = thisArg[fn];
215
210
216 if (fn == null)
211 if (fn == null)
217 throw new Error("The function must be specified");
212 throw new Error("The function must be specified");
218
213
219 function wrapresult(x: any, e?: any): PromiseLike<any> {
214 function wrapresult(x: any, e?: any): PromiseLike<any> {
220 if (e) {
215 if (e) {
221 return {
216 return {
222 then(cb, eb) {
217 then(cb, eb) {
223 try {
218 try {
224 return eb ? wrapresult(eb(e)) : this;
219 return eb ? wrapresult(eb(e)) : this;
225 } catch (e2) {
220 } catch (e2) {
226 return wrapresult(null, e2);
221 return wrapresult(null, e2);
227 }
222 }
228 }
223 }
229 };
224 };
230 } else {
225 } else {
231 if (x && x.then)
226 if (x && x.then)
232 return x;
227 return x;
233 return {
228 return {
234 then(cb) {
229 then(cb) {
235 try {
230 try {
236 return cb ? wrapresult(cb(x)) : this;
231 return cb ? wrapresult(cb(x)) : this;
237 } catch (e2) {
232 } catch (e2) {
238 return wrapresult(e2);
233 return wrapresult(e2);
239 }
234 }
240 }
235 }
241 };
236 };
242 }
237 }
243 }
238 }
244
239
245 return (...args) => {
240 return (...args) => {
246 try {
241 try {
247 return wrapresult(fn.apply(thisArg, args));
242 return wrapresult(fn.apply(thisArg, args));
248 } catch (e) {
243 } catch (e) {
249 return wrapresult(null, e);
244 return wrapresult(null, e);
250 }
245 }
251 };
246 };
252 }
247 }
253
248
254 export function delegate<T extends object, F extends (this: T, ...args: any[]) => any>(
249 export function delegate<T extends object, F extends (this: T, ...args: any[]) => any>(
255 target: T,
250 target: T,
256 method: F
251 method: F
257 ): OmitThisParameter<F>;
252 ): OmitThisParameter<F>;
258 export function delegate<M extends string, T extends { [m in M]?: (...args: any[]) => any; }>(
253 export function delegate<M extends string, T extends { [m in M]?: (...args: any[]) => any; }>(
259 target: T,
254 target: T,
260 method: M
255 method: M
261 ): OmitThisParameter<T[M]>;
256 ): OmitThisParameter<T[M]>;
262 export function delegate(target: any, _method: any): (...args: any[]) => any {
257 export function delegate(target: any, _method: any): (...args: any[]) => any {
263 let method: any;
258 let method: any;
264 if (!(_method instanceof Function)) {
259 if (!(_method instanceof Function)) {
265 argumentNotNull(target, "target");
260 argumentNotNull(target, "target");
266 method = target[_method];
261 method = target[_method];
267 if (!(method instanceof Function))
262 if (!(method instanceof Function))
268 throw new Error("'method' argument must be a Function or a method name");
263 throw new Error("'method' argument must be a Function or a method name");
269 } else {
264 } else {
270 method = _method;
265 method = _method;
271 }
266 }
272
267
273 return (...args) => {
268 return (...args) => {
274 return method.apply(target, args);
269 return method.apply(target, args);
275 };
270 };
276 }
271 }
277
272
278 export function delay(timeMs: number, ct = Cancellation.none) {
273 export function delay(timeMs: number, ct = Cancellation.none) {
279 ct.throwIfRequested();
274 ct.throwIfRequested();
280 return new Promise((resolve, reject) => {
275 return new Promise((resolve, reject) => {
281 const h = ct.register(e => {
276 const h = ct.register(e => {
282 clearTimeout(id);
277 clearTimeout(id);
283 reject(e);
278 reject(e);
284 // we don't nedd to unregister h, since ct is already disposed
279 // we don't nedd to unregister h, since ct is already disposed
285 });
280 });
286 const id = setTimeout(() => {
281 const id = setTimeout(() => {
287 h.destroy();
282 h.destroy();
288 resolve();
283 resolve();
289 }, timeMs);
284 }, timeMs);
290
285
291 });
286 });
292 }
287 }
293
288
294 /** Returns resolved promise, awaiting this method will cause the asynchronous
289 /** Returns resolved promise, awaiting this method will cause the asynchronous
295 * completion of the rest of the code.
290 * completion of the rest of the code.
296 */
291 */
297 export function fork() {
292 export function fork() {
298 return Promise.resolve();
293 return Promise.resolve();
299 }
294 }
300
295
301 /** Always throws Error, can be used as a stub for the methods which should be
296 /** Always throws Error, can be used as a stub for the methods which should be
302 * assigned later and are required to be not null.
297 * assigned later and are required to be not null.
303 */
298 */
304 export function notImplemented(): never {
299 export function notImplemented(): never {
305 throw new Error("Not implemeted");
300 throw new Error("Not implemeted");
306 }
301 }
307 /**
302 /**
308 * Iterates over the specified array of items and calls the callback `cb`, if
303 * Iterates over the specified array of items and calls the callback `cb`, if
309 * the result of the callback is a promise the next item from the array will be
304 * the result of the callback is a promise the next item from the array will be
310 * proceeded after the promise is resolved.
305 * proceeded after the promise is resolved.
311 *
306 *
312 */
307 */
313 export function pmap<T, T2>(
308 export function pmap<T, T2>(
314 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
309 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
315 cb: (item: T, i: number) => T2 | PromiseLike<T2>
310 cb: (item: T, i: number) => T2 | PromiseLike<T2>
316 ): T2[] | PromiseLike<T2[]> {
311 ): T2[] | PromiseLike<T2[]> {
317 argumentNotNull(cb, "cb");
312 argumentNotNull(cb, "cb");
318
313
319 if (isPromise(items)) {
314 if (isPromise(items)) {
320 return items.then(data => pmap(data, cb));
315 return items.then(data => pmap(data, cb));
321 } else {
316 } else {
322
317
323 if (isNull(items) || !items.length)
318 if (isNull(items) || !items.length)
324 return [];
319 return [];
325
320
326 let i = 0;
321 let i = 0;
327 const result = new Array<T2>();
322 const result = new Array<T2>();
328
323
329 const next = (): any => {
324 const next = (): any => {
330 while (i < items.length) {
325 while (i < items.length) {
331 const r = cb(items[i], i);
326 const r = cb(items[i], i);
332 const ri = i;
327 const ri = i;
333 i++;
328 i++;
334 if (isPromise(r)) {
329 if (isPromise(r)) {
335 return r.then(x => {
330 return r.then(x => {
336 result[ri] = x;
331 result[ri] = x;
337 return next();
332 return next();
338 });
333 });
339 } else {
334 } else {
340 result[ri] = r;
335 result[ri] = r;
341 }
336 }
342 }
337 }
343 return result;
338 return result;
344 };
339 };
345
340
346 return next();
341 return next();
347 }
342 }
348 }
343 }
349
344
350 export function pfor<T>(
345 export function pfor<T>(
351 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
346 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
352 cb: (item: T, i: number) => any
347 cb: (item: T, i: number) => any
353 ): void | PromiseLike<void> {
348 ): void | PromiseLike<void> {
354 argumentNotNull(cb, "cb");
349 argumentNotNull(cb, "cb");
355
350
356 if (isPromise(items)) {
351 if (isPromise(items)) {
357 return items.then(data => pfor(data, cb));
352 return items.then(data => pfor(data, cb));
358 } else {
353 } else {
359 if (isNull(items) || !items.length)
354 if (isNull(items) || !items.length)
360 return;
355 return;
361
356
362 let i = 0;
357 let i = 0;
363
358
364 const next = (): any => {
359 const next = (): any => {
365 while (i < items.length) {
360 while (i < items.length) {
366 const r = cb(items[i], i);
361 const r = cb(items[i], i);
367 i++;
362 i++;
368 if (isPromise(r))
363 if (isPromise(r))
369 return r.then(next);
364 return r.then(next);
370 }
365 }
371 };
366 };
372
367
373 return next();
368 return next();
374 }
369 }
375 }
370 }
376
371
377 export function first<T>(sequence: ArrayLike<T>): T;
372 export function first<T>(sequence: ArrayLike<T>): T;
378 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
373 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
379 export function first<T>(
374 export function first<T>(
380 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
375 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
381 cb?: (x: T) => void,
376 cb?: (x: T) => void,
382 err?: (x: Error) => void
377 err?: (x: Error) => void
383 ): void;
378 ): void;
384 /**
379 /**
385 * Выбирает первый элемент из последовательности, или обещания, если в
380 * Выбирает первый элемент из последовательности, или обещания, если в
386 * качестве параметра используется обещание, оно должно вернуть массив.
381 * качестве параметра используется обещание, оно должно вернуть массив.
387 *
382 *
388 * @param {Function} cb обработчик результата, ему будет передан первый
383 * @param {Function} cb обработчик результата, ему будет передан первый
389 * элемент последовательности в случае успеха
384 * элемент последовательности в случае успеха
390 * @param {Function} err обработчик исключения, если массив пустой, либо
385 * @param {Function} err обработчик исключения, если массив пустой, либо
391 * не массив
386 * не массив
392 *
387 *
393 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
388 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
394 * обещание, либо первый элемент.
389 * обещание, либо первый элемент.
395 * @async
390 * @async
396 */
391 */
397 export function first<T>(
392 export function first<T>(
398 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
393 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
399 cb?: (x: T) => void,
394 cb?: (x: T) => void,
400 err?: (x: Error) => void
395 err?: (x: Error) => void
401 ) {
396 ) {
402 if (isPromise(sequence)) {
397 if (isPromise(sequence)) {
403 return sequence.then(res => first(res, cb as any /* force to pass undefined cb */, err));
398 return sequence.then(res => first(res, cb as any /* force to pass undefined cb */, err));
404 } else if (sequence && "length" in sequence) {
399 } else if (sequence && "length" in sequence) {
405 if (sequence.length === 0) {
400 if (sequence.length === 0) {
406 if (err)
401 if (err)
407 return err(new Error("The sequence is empty"));
402 return err(new Error("The sequence is empty"));
408 else
403 else
409 throw new Error("The sequence is empty");
404 throw new Error("The sequence is empty");
410 } else if (cb) {
405 } else if (cb) {
411 return cb(sequence[0]);
406 return cb(sequence[0]);
412 } else {
407 } else {
413 return sequence[0];
408 return sequence[0];
414 }
409 }
415 } else {
410 } else {
416 if (err)
411 if (err)
417 return err(new Error("The sequence is required"));
412 return err(new Error("The sequence is required"));
418 else
413 else
419 throw new Error("The sequence is required");
414 throw new Error("The sequence is required");
420 }
415 }
421 }
416 }
422
417
423 export function firstWhere<T>(
418 export function firstWhere<T>(
424 sequence: ArrayLike<T>,
419 sequence: ArrayLike<T>,
425 predicate: (x: T) => boolean
420 predicate: (x: T) => boolean
426 ): T;
421 ): T;
427 export function firstWhere<T>(
422 export function firstWhere<T>(
428 sequence: PromiseLike<ArrayLike<T>>,
423 sequence: PromiseLike<ArrayLike<T>>,
429 predicate: (x: T) => boolean
424 predicate: (x: T) => boolean
430 ): PromiseLike<T>;
425 ): PromiseLike<T>;
431 export function firstWhere<T>(
426 export function firstWhere<T>(
432 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
427 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
433 predicate: (x: T) => boolean,
428 predicate: (x: T) => boolean,
434 cb: (x: T) => void,
429 cb: (x: T) => void,
435 err?: (x: Error) => void
430 err?: (x: Error) => void
436 ): void;
431 ): void;
437
432
438 export function firstWhere<T>(
433 export function firstWhere<T>(
439 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
434 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
440 predicate?: (x: T) => boolean,
435 predicate?: (x: T) => boolean,
441 cb?: (x: T) => any,
436 cb?: (x: T) => any,
442 err?: (x: Error) => any
437 err?: (x: Error) => any
443 ) {
438 ) {
444 if (isPromise(sequence)) {
439 if (isPromise(sequence)) {
445 return sequence.then(res => firstWhere(
440 return sequence.then(res => firstWhere(
446 res,
441 res,
447 predicate as any /* force to pass undefined predicate */,
442 predicate as any /* force to pass undefined predicate */,
448 cb as any /* force to pass undefined cb */,
443 cb as any /* force to pass undefined cb */,
449 err)
444 err)
450 );
445 );
451 } else if (sequence && "length" in sequence) {
446 } else if (sequence && "length" in sequence) {
452 if (sequence.length === 0) {
447 if (sequence.length === 0) {
453 if (err)
448 if (err)
454 err(new Error("The sequence is empty"));
449 err(new Error("The sequence is empty"));
455 else
450 else
456 throw new Error("The sequence is empty");
451 throw new Error("The sequence is empty");
457 } else {
452 } else {
458 if (!predicate) {
453 if (!predicate) {
459 return cb ? cb(sequence[0]) && void (0) : sequence[0];
454 return cb ? cb(sequence[0]) && void (0) : sequence[0];
460 } else {
455 } else {
461 for (let i = 0; i < sequence.length; i++) {
456 for (let i = 0; i < sequence.length; i++) {
462 const v = sequence[i];
457 const v = sequence[i];
463 if (predicate(v))
458 if (predicate(v))
464 return cb ? cb(v) : v;
459 return cb ? cb(v) : v;
465 }
460 }
466 if (err)
461 if (err)
467 err(new Error("The sequence doesn't contain matching items"));
462 err(new Error("The sequence doesn't contain matching items"));
468 else
463 else
469 throw new Error("The sequence doesn't contain matching items");
464 throw new Error("The sequence doesn't contain matching items");
470 }
465 }
471 }
466 }
472 } else {
467 } else {
473 if (err)
468 if (err)
474 err(new Error("The sequence is required"));
469 err(new Error("The sequence is required"));
475 else
470 else
476 throw new Error("The sequence is required");
471 throw new Error("The sequence is required");
477 }
472 }
478 }
473 }
479
474
480 export function isDestroyable(d: any): d is IDestroyable {
475 export function isDestroyable(d: any): d is IDestroyable {
481 if (d && "destroy" in d && typeof(destroy) === "function")
476 if (d && "destroy" in d && typeof (destroy) === "function")
482 return true;
477 return true;
483 return false;
478 return false;
484 }
479 }
485
480
486 export function destroy(d: any) {
481 export function destroy(d: any) {
487 if (d && "destroy" in d)
482 if (d && "destroy" in d)
488 d.destroy();
483 d.destroy();
489 }
484 }
490
485
491 /**
486 /**
492 * Used to mark that the async operation isn't awaited intentionally.
487 * Used to mark that the async operation isn't awaited intentionally.
493 * @param p The promise which represents the async operation.
488 * @param p The promise which represents the async operation.
494 */
489 */
495 export function nowait(p: Promise<any>) {
490 export function nowait(p: Promise<any>) {
496 }
491 }
497
492
498 /** represents already destroyed object.
493 /** represents already destroyed object.
499 */
494 */
500 export const destroyed = {
495 export const destroyed = {
501 /** Calling to this method doesn't affect anything, noop.
496 /** Calling to this method doesn't affect anything, noop.
502 */
497 */
503 destroy() {
498 destroy() {
504 }
499 }
505 };
500 };
@@ -1,178 +1,178
1 import { isPrimitive, isNull, each, isKeyof, get } from "../safe";
1 import { isPrimitive, isNull, isKeyof, get } from "../safe";
2 import { MapOf } from "../interfaces";
2 import { MapOf } from "../interfaces";
3
3
4 type SubstFn = (name: string, format?: string) => string;
4 type SubstFn = (name: string, format?: string) => string;
5 type TemplateFn = (subst: SubstFn) => string | undefined;
5 type TemplateFn = (subst: SubstFn) => string | undefined;
6 type ConvertFn = (value: any, format?: string) => string;
6 type ConvertFn = (value: any, format?: string) => string;
7
7
8 const map = {
8 const map = {
9 "\\{": "&curlopen;",
9 "\\{": "&curlopen;",
10 "\\}": "&curlclose;",
10 "\\}": "&curlclose;",
11 "&": "&amp;",
11 "&": "&amp;",
12 "\\:": "&colon;"
12 "\\:": "&colon;"
13 };
13 };
14
14
15 const rev = {
15 const rev = {
16 curlopen: "{",
16 curlopen: "{",
17 curlclose: "}",
17 curlclose: "}",
18 amp: "&",
18 amp: "&",
19 colon: ":"
19 colon: ":"
20 };
20 };
21
21
22 function espaceString(s: string) {
22 function espaceString(s: string) {
23 if (!s)
23 if (!s)
24 return s;
24 return s;
25 return "'" + s.replace(/('|\\)/g, "\\$1").replace("\n", "\\n") + "'";
25 return "'" + s.replace(/('|\\)/g, "\\$1").replace("\n", "\\n") + "'";
26 }
26 }
27
27
28 function encode(s: string) {
28 function encode(s: string) {
29 if (!s)
29 if (!s)
30 return s;
30 return s;
31 return s.replace(/\\{|\\}|&|\\:|\n/g, m => isKeyof(m, map) ? map[m] : m);
31 return s.replace(/\\{|\\}|&|\\:|\n/g, m => isKeyof(m, map) ? map[m] : m);
32 }
32 }
33
33
34 function decode(s: string) {
34 function decode(s: string) {
35 if (!s)
35 if (!s)
36 return s;
36 return s;
37 return s.replace(/&(\w+);/g, (m, $1) => isKeyof($1, rev) ? rev[$1] : m);
37 return s.replace(/&(\w+);/g, (m, $1) => isKeyof($1, rev) ? rev[$1] : m);
38 }
38 }
39
39
40 function subst(s: string) {
40 function subst(s: string) {
41 const i = s.indexOf(":");
41 const i = s.indexOf(":");
42 let name: string;
42 let name: string;
43 let pattern: string | undefined;
43 let pattern: string | undefined;
44 if (i >= 0) {
44 if (i >= 0) {
45 name = s.substr(0, i);
45 name = s.substr(0, i);
46 pattern = s.substr(i + 1);
46 pattern = s.substr(i + 1);
47 } else {
47 } else {
48 name = s;
48 name = s;
49 }
49 }
50
50
51 if (pattern)
51 if (pattern)
52 return [
52 return [
53 espaceString(decode(name)),
53 espaceString(decode(name)),
54 espaceString(decode(pattern))
54 espaceString(decode(pattern))
55 ];
55 ];
56 else
56 else
57 return [espaceString(decode(name))];
57 return [espaceString(decode(name))];
58 }
58 }
59
59
60 function _compile(str: string) {
60 function _compile(str: string) {
61 if (!str)
61 if (!str)
62 return () => void 0;
62 return () => void 0;
63
63
64 const chunks = encode(str).split("{");
64 const chunks = encode(str).split("{");
65 let chunk: string;
65 let chunk: string;
66
66
67 const code = ["var result=[];"];
67 const code = ["var result=[];"];
68
68
69 for (let i = 0; i < chunks.length; i++) {
69 for (let i = 0; i < chunks.length; i++) {
70 chunk = chunks[i];
70 chunk = chunks[i];
71
71
72 if (i === 0) {
72 if (i === 0) {
73 if (chunk)
73 if (chunk)
74 code.push("result.push(" + espaceString(decode(chunk)) +
74 code.push("result.push(" + espaceString(decode(chunk)) +
75 ");");
75 ");");
76 } else {
76 } else {
77 const len = chunk.indexOf("}");
77 const len = chunk.indexOf("}");
78 if (len < 0)
78 if (len < 0)
79 throw new Error("Unbalanced substitution #" + i);
79 throw new Error("Unbalanced substitution #" + i);
80
80
81 code.push("result.push(subst(" +
81 code.push("result.push(subst(" +
82 subst(chunk.substr(0, len)).join(",") + "));");
82 subst(chunk.substr(0, len)).join(",") + "));");
83 if (chunk.length > len + 1)
83 if (chunk.length > len + 1)
84 code.push("result.push(" +
84 code.push("result.push(" +
85 espaceString(decode(chunk.substr(len + 1))) + ");");
85 espaceString(decode(chunk.substr(len + 1))) + ");");
86 }
86 }
87 }
87 }
88
88
89 code.push("return result.join('');");
89 code.push("return result.join('');");
90
90
91 // the code for this function is generated from the template
91 // the code for this function is generated from the template
92 // tslint:disable-next-line:function-constructor
92 // tslint:disable-next-line:function-constructor
93 return new Function("subst", code.join("\n")) as TemplateFn;
93 return new Function("subst", code.join("\n")) as TemplateFn;
94 }
94 }
95
95
96 const cache: MapOf<TemplateFn> = {};
96 const cache: MapOf<TemplateFn> = {};
97
97
98 export function compile(template: string) {
98 export function compile(template: string) {
99 let compiled = cache[template];
99 let compiled = cache[template];
100 if (!compiled) {
100 if (!compiled) {
101 compiled = _compile(template);
101 compiled = _compile(template);
102 cache[template] = compiled;
102 cache[template] = compiled;
103 }
103 }
104 return compiled;
104 return compiled;
105 }
105 }
106
106
107 function defaultConverter(value: any, pattern?: string) {
107 function defaultConverter(value: any, pattern?: string) {
108 if (pattern && pattern.toLocaleLowerCase() === "json") {
108 if (pattern && pattern.toLocaleLowerCase() === "json") {
109 const seen: any = [];
109 const seen: any = [];
110 return JSON.stringify(value, (k, v) => {
110 return JSON.stringify(value, (k, v) => {
111 if (!isPrimitive(v)) {
111 if (!isPrimitive(v)) {
112 const id = seen.indexOf(v);
112 const id = seen.indexOf(v);
113 if (id >= 0)
113 if (id >= 0)
114 return "@ref-" + id;
114 return "@ref-" + id;
115 else {
115 else {
116 seen.push(v);
116 seen.push(v);
117 return v.toString() as string;
117 return v.toString() as string;
118 }
118 }
119 } else {
119 } else {
120 return isNull(v) ? "" : v.toString();
120 return isNull(v) ? "" : v.toString();
121 }
121 }
122 }, 2);
122 }, 2);
123 } else if (isNull(value)) {
123 } else if (isNull(value)) {
124 return "";
124 return "";
125 } else if (value instanceof Date) {
125 } else if (value instanceof Date) {
126 return value.toISOString();
126 return value.toISOString();
127 } else {
127 } else {
128 return value.toString() as string;
128 return value.toString() as string;
129 }
129 }
130 }
130 }
131
131
132 export class Formatter {
132 export class Formatter {
133 _converters: ConvertFn[];
133 _converters: ConvertFn[];
134
134
135 constructor(converters?: ConvertFn[]) {
135 constructor(converters?: ConvertFn[]) {
136 this._converters = converters || [];
136 this._converters = converters || [];
137 this._converters.push(defaultConverter);
137 this._converters.push(defaultConverter);
138 }
138 }
139
139
140 convert(value: any, pattern?: string) {
140 convert(value: any, pattern?: string) {
141 for (const c of this._converters) {
141 for (const c of this._converters) {
142 const res = c(value, pattern);
142 const res = c(value, pattern);
143 if (!isNull(res))
143 if (!isNull(res))
144 return res;
144 return res;
145 }
145 }
146 return "";
146 return "";
147 }
147 }
148
148
149 format(msg: string, ...args: any[]) {
149 format(msg: string, ...args: any[]) {
150 const template = compile(msg);
150 const template = compile(msg);
151
151
152 return template((name, pattern) => {
152 return template((name, pattern) => {
153 const value = get(name, args);
153 const value = get(name, args);
154 return !isNull(value) ? this.convert(value, pattern) : "";
154 return !isNull(value) ? this.convert(value, pattern) : "";
155 });
155 });
156
156
157 }
157 }
158
158
159 compile(msg: string) {
159 compile(msg: string) {
160 const template = compile(msg);
160 const template = compile(msg);
161 return (...args: any[]) => {
161 return (...args: any[]) => {
162 return template((name, pattern) => {
162 return template((name, pattern) => {
163 const value = get(name, args);
163 const value = get(name, args);
164 return !isNull(value) ? this.convert(value, pattern) : "";
164 return !isNull(value) ? this.convert(value, pattern) : "";
165 });
165 });
166 };
166 };
167 }
167 }
168 }
168 }
169
169
170 const _default = new Formatter();
170 const _default = new Formatter();
171
171
172 export function format(msg: string, ...args: any[]) {
172 export function format(msg: string, ...args: any[]) {
173 return _default.format(msg, ...args);
173 return _default.format(msg, ...args);
174 }
174 }
175
175
176 export function convert(value: any, pattern: string) {
176 export function convert(value: any, pattern: string) {
177 return _default.format(value, pattern);
177 return _default.format(value, pattern);
178 }
178 }
@@ -1,39 +1,43
1 import { Foo } from "./Foo";
1 import { Foo } from "./Foo";
2 import { annotate, dependency } from "./services";
3
2
4 export const service = annotate<Bar>();
3 /* export const service = annotate<Bar>();
5
4
6 @service.wire({
5 @service.wire({
7 foo: dependency("foo"),
6 foo: dependency("foo"),
8 nested: {
7 nested: {
9 lazy: dependency("foo", { lazy: true })
8 lazy: dependency("foo", { lazy: true })
10 },
9 },
11 host: dependency("host")
10 host: dependency("host")
12 }, "")
11 }, "") */
13 export class Bar {
12 export class Bar {
14 barName = "Twister";
13 barName = "Twister";
15
14
16 _v: Foo | undefined;
15 _v: Foo | undefined;
17
16
18 constructor(_opts: {
17 constructor(
19 foo?: Foo;
18 _opts: {
20 nested?: {
19 foo?: Foo;
21 lazy: () => Foo
20 nested?: {
21 lazy: () => Foo
22 },
23 host: string
22 },
24 },
23 host: string
25 s: string
24 }, s: string) {
26 ) {
25
27
26 if (_opts && _opts.foo)
28 if (_opts && _opts.foo)
27 this._v = _opts.foo;
29 this._v = _opts.foo;
30 if (s)
31 this.barName = s;
28 }
32 }
29
33
30 setName(name: string) {
34 setName(name: string) {
31
35
32 }
36 }
33
37
34 getFoo() {
38 getFoo() {
35 if (this._v === undefined)
39 if (this._v === undefined)
36 throw new Error("The foo isn't set");
40 throw new Error("The foo isn't set");
37 return this._v;
41 return this._v;
38 }
42 }
39 }
43 }
@@ -1,29 +1,28
1 import { Bar } from "./Bar";
1 import { Bar } from "./Bar";
2 import { annotate, dependency } from "./services";
3
2
4 // export service descriptor
3 // export service descriptor
5 // через service передается информация о типе зависимости
4 // через service передается информация о типе зависимости
6 // даже если это шаблон.
5 // даже если это шаблон.
7 export const service = annotate<Box<Bar>>();
6 // export const service = annotate<Box<Bar>>();
8
7
9 @service.wire()
8 // @service.wire()
10 export class Box<T> {
9 export class Box<T> {
11 private _value: T | undefined;
10 private _value: T | undefined;
12
11
13 constructor(value?: T) {
12 constructor(value?: T) {
14 this._value = value;
13 this._value = value;
15 }
14 }
16
15
17 @service.inject(dependency("bar"))
16 // @service.inject(dependency("bar"))
18 setValue(value: T) {
17 setValue(value: T) {
19 this._value = value;
18 this._value = value;
20 return value;
19 return value;
21 }
20 }
22
21
23 getValue() {
22 getValue() {
24 if (this._value === undefined)
23 if (this._value === undefined)
25 throw new Error("Trying to get a value from the empty box");
24 throw new Error("Trying to get a value from the empty box");
26
25
27 return this._value;
26 return this._value;
28 }
27 }
29 }
28 }
@@ -1,20 +1,23
1 import { configure } from "./services";
1 import { Services } from "./services";
2 import { configure } from "../di/traits";
3 import { LifetimeManager } from "../di/LifetimeManager";
2
4
3 export const config = configure()
5 export const config = configure<Services>()
4 .register("host", s => s.value("example.com"))
6 .register("host", s => s.value("example.com"))
5 .register("bar2", bar2 => Promise.all([import("./Foo"), import("./Bar")])
7 .register("bar2", bar2 => Promise.all([import("./Foo"), import("./Bar")])
6 .then(([{ Foo }, { Bar }]) => {
8 .then(([{ Foo }, { Bar }]) => {
7 const lifetime: any = undefined; // new HierarchyLifetime()
9 const lifetime = LifetimeManager.hierarchyLifetime();
10
8 bar2.factory((resolve, activate) => {
11 bar2.factory((resolve, activate) => {
9 const bar = new Bar({
12 const bar = new Bar({
10 foo: activate(lifetime, () => new Foo()),
13 foo: activate(lifetime, () => new Foo()),
11 nested: {
14 nested: {
12 lazy: resolve("foo", { lazy: true })
15 lazy: resolve("foo", { lazy: true })
13 },
16 },
14 host: resolve("host")
17 host: resolve("host")
15 }, "some text");
18 }, "some text");
16 bar.setName(resolve("host"));
19 bar.setName(resolve("host"));
17 return bar;
20 return bar;
18 });
21 });
19 })
22 })
20 );
23 );
@@ -1,25 +1,19
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/traits";
5
4
6 /**
5 /**
7 * Сервисы доступные внутри контейнера
6 * Сервисы доступные внутри контейнера
8 */
7 */
9 export interface Services {
8 export interface Services {
10 foo: Foo;
9 foo: Foo;
11
10
12 bar: Bar;
11 bar: Bar;
13
12
14 bar2: Bar;
13 bar2: Bar;
15
14
16 box: Box<Bar>;
15 box: Box<Bar>;
17
16
18 host: string;
17 host: string;
19
18
20 }
19 }
21
22 /**
23 * Экспортируем вспомогательные функции для описания сервисов и кинфогурации
24 */
25 export const { dependency, annotate, configure } = declare<Services>();
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