##// END OF EJS Templates
working on IoC configuration
cin -
r114:475b8ce3e850 ioc ts support
parent child
Show More
@@ -1,132 +1,138
1 import { TraceSource } from "../log/TraceSource";
1 import { TraceSource } from "../log/TraceSource";
2 import { argumentNotNull, argumentNotEmptyString, isPrimitive, each, isNull } from "../safe";
2 import { argumentNotNull, argumentNotEmptyString, isPrimitive, each, isNull } from "../safe";
3 import { Descriptor, ServiceMap } from "./interfaces";
3 import { Descriptor, ServiceMap } from "./interfaces";
4 import { Container } from "./Container";
4 import { Container } from "./Container";
5 import { MapOf } from "../interfaces";
5
6
6 const trace = TraceSource.get("@implab/core/di/ActivationContext");
7 const trace = TraceSource.get("@implab/core/di/ActivationContext");
7
8
8 export interface ActivationContextInfo {
9 export interface ActivationContextInfo<S> {
9 name: string;
10 name: string;
10
11
11 service: string;
12 service: string;
12
13
13 scope: ServiceMap;
14 scope: ServiceMap<S>;
14 }
15 }
15
16
16 export class ActivationContext {
17 export class ActivationContext<S> {
17 _cache: object;
18 _cache: MapOf<any>;
18
19
19 _services: ServiceMap;
20 _services: ServiceMap<S>;
20
21
21 _stack: ActivationContextInfo[];
22 _stack: ActivationContextInfo<S>[];
22
23
23 _visited: object;
24 _visited: MapOf<any>;
24
25
25 _name: string;
26 _name: string;
26
27
27 _localized: boolean;
28 _localized: boolean = false;
28
29
29 container: Container;
30 container: Container<S>;
30
31
31 constructor(container: Container, services: ServiceMap, name?: string, cache?: object, visited?) {
32 constructor(container: Container<S>, services: ServiceMap<S>, name?: string, cache?: object, visited?: MapOf<any>) {
32 argumentNotNull(container, "container");
33 argumentNotNull(container, "container");
33 argumentNotNull(services, "services");
34 argumentNotNull(services, "services");
34
35
35 this._name = name;
36 this._name = name || "<unnamed>";
36 this._visited = visited || {};
37 this._visited = visited || {};
37 this._stack = [];
38 this._stack = [];
38 this._cache = cache || {};
39 this._cache = cache || {};
39 this._services = services;
40 this._services = services;
40 this.container = container;
41 this.container = container;
41 }
42 }
42
43
43 getName() {
44 getName() {
44 return this._name;
45 return this._name;
45 }
46 }
46
47
47 resolve(name, def?): any {
48 resolve<K extends keyof S, T extends S[K]>(name: K, def?: T) {
48 const d = this._services[name];
49 const d = this._services[name];
49
50
50 if (!d)
51 if (d !== undefined) {
51 if (arguments.length > 1)
52 return this.activate(d as Descriptor<T>, name.toString());
53 } else {
54 if (def !== undefined && def !== null)
52 return def;
55 return def;
53 else
56 else
54 throw new Error(`Service ${name} not found`);
57 throw new Error(`Service ${name} not found`);
55
58 }
56 return this.activate(d, name);
57 }
59 }
58
60
59 /**
61 /**
60 * registers services local to the the activation context
62 * registers services local to the the activation context
61 *
63 *
62 * @name{string} the name of the service
64 * @name{string} the name of the service
63 * @service{string} the service descriptor to register
65 * @service{string} the service descriptor to register
64 */
66 */
65 register(name: string, service: Descriptor) {
67 register<K extends keyof S>(name: K, service: Descriptor<S[K]>) {
66 argumentNotEmptyString(name, "name");
68 argumentNotEmptyString(name, "name");
67
69
68 this._services[name] = service;
70 this._services[name] = service;
69 }
71 }
70
72
71 clone() {
73 clone() {
72 return new ActivationContext(
74 return new ActivationContext(
73 this.container,
75 this.container,
74 this._services,
76 this._services,
75 this._name,
77 this._name,
76 this._cache,
78 this._cache,
77 this._visited
79 this._visited
78 );
80 );
79 }
81 }
80
82
81 has(id: string) {
83 has(id: string) {
82 return id in this._cache;
84 return id in this._cache;
83 }
85 }
84
86
85 get(id: string) {
87 get<T>(id: string) {
86 return this._cache[id];
88 return this._cache[id];
87 }
89 }
88
90
89 store(id: string, value) {
91 store(id: string, value: any) {
90 return (this._cache[id] = value);
92 return (this._cache[id] = value);
91 }
93 }
92
94
93 activate(d: Descriptor, name: string) {
95 activate<T>(d: Descriptor<T>, name: string) {
94 if (trace.isLogEnabled())
96 if (trace.isLogEnabled())
95 trace.log(`enter ${name} ${d}`);
97 trace.log(`enter ${name} ${d}`);
96
98
97 this.enter(name, d.toString());
99 this.enter(name, d.toString());
98 const v = d.activate(this);
100 const v = d.activate<S>(this);
99 this.leave();
101 this.leave();
100
102
101 if (trace.isLogEnabled())
103 if (trace.isLogEnabled())
102 trace.log(`leave ${name}`);
104 trace.log(`leave ${name}`);
103
105
104 return v;
106 return v;
105 }
107 }
106
108
107 visit(id: string) {
109 visit(id: string) {
108 const count = this._visited[id] || 0;
110 const count = this._visited[id] || 0;
109 this._visited[id] = count + 1;
111 this._visited[id] = count + 1;
110 return count;
112 return count;
111 }
113 }
112
114
113 getStack() {
115 getStack() {
114 return this._stack.slice().reverse();
116 return this._stack.slice().reverse();
115 }
117 }
116
118
117 private enter(name: string, service: string) {
119 private enter(name: string, service: string) {
118 this._stack.push({
120 this._stack.push({
119 name,
121 name,
120 service,
122 service,
121 scope: this._services
123 scope: this._services
122 });
124 });
123 this._name = name;
125 this._name = name;
124 this._services = Object.create(this._services);
126 this._services = Object.create(this._services);
125 }
127 }
126
128
127 private leave() {
129 private leave() {
128 const ctx = this._stack.pop();
130 const ctx = this._stack.pop();
129 this._services = ctx.scope;
131 if (ctx) {
130 this._name = ctx.name;
132 this._services = ctx.scope;
133 this._name = ctx.name;
134 } else {
135 trace.error("Trying to leave the last activation scope");
136 }
131 }
137 }
132 }
138 }
@@ -1,36 +1,39
1 import { ActivationContextInfo } from "./ActivationContext";
1 export interface ActivationItem {
2 name: string;
3 service: string;
4 }
2
5
3 export class ActivationError {
6 export class ActivationError {
4 activationStack: ActivationContextInfo[];
7 activationStack: ActivationItem[];
5
8
6 service: string;
9 service: string;
7
10
8 innerException: any;
11 innerException: any;
9
12
10 message: string;
13 message: string;
11
14
12 constructor(service: string, activationStack: ActivationContextInfo[], innerException: any) {
15 constructor(service: string, activationStack: ActivationItem[], innerException: any) {
13 this.message = "Failed to activate the service";
16 this.message = "Failed to activate the service";
14 this.activationStack = activationStack;
17 this.activationStack = activationStack;
15 this.service = service;
18 this.service = service;
16 this.innerException = innerException;
19 this.innerException = innerException;
17 }
20 }
18
21
19 toString() {
22 toString() {
20 const parts = [this.message];
23 const parts = [this.message];
21 if (this.service)
24 if (this.service)
22 parts.push("when activating: " + this.service.toString());
25 parts.push("when activating: " + this.service.toString());
23
26
24 if (this.innerException)
27 if (this.innerException)
25 parts.push("caused by: " + this.innerException.toString());
28 parts.push("caused by: " + this.innerException.toString());
26
29
27 if (this.activationStack) {
30 if (this.activationStack) {
28 parts.push("at");
31 parts.push("at");
29 this.activationStack
32 this.activationStack
30 .forEach(x => parts.push(` ${x.name} ${x.service}`));
33 .forEach(x => parts.push(` ${x.name} ${x.service}`));
31
34
32 }
35 }
33
36
34 return parts.join("\n");
37 return parts.join("\n");
35 }
38 }
36 }
39 }
@@ -1,37 +1,39
1 import { Descriptor, isDescriptor } from "./interfaces";
1 import { Descriptor, isDescriptor } from "./interfaces";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { isPrimitive } from "../safe";
3 import { isPrimitive } from "../safe";
4
4
5 export class AggregateDescriptor<T> implements Descriptor<T> {
5 type Parse<T> = T extends Descriptor<infer V> ? V :
6 T extends {} ? { [K in keyof T]: Parse<T[K]> } :
7 T;
8
9 export class AggregateDescriptor<T> implements Descriptor<Parse<T>> {
6 _value: T;
10 _value: T;
7
11
8 constructor(value: T) {
12 constructor(value: T) {
9 this._value = value;
13 this._value = value;
10 }
14 }
11
15
12 activate(context: ActivationContext) {
16 activate<S>(context: ActivationContext<S>) {
13 return this._parse(this._value, context, "$value");
17 return this._parse(this._value, context, "$value");
14 }
18 }
15
19
16 // TODO: make async
20 _parse<S, V>(value: V, context: ActivationContext<S>, path: string): Parse<V> {
17 _parse(value: T, context: ActivationContext, path: string) {
18 if (isPrimitive(value))
21 if (isPrimitive(value))
19 return value;
22 return value as any;
20
23
21 if (isDescriptor(value))
24 if (isDescriptor(value))
22 return context.activate(value, path);
25 return context.activate(value, path);
23
26
24 if (value instanceof Array)
27 if (value instanceof Array)
25 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`));
28 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`)) as any;
26
29
27 const t = {};
30 const t: any = {};
28 for (const p of Object.keys(value))
31 for (const p in value)
29 t[p] = this._parse(value[p], context, `${path}.${p}`);
32 t[p] = this._parse(value[p], context, `${path}.${p}`);
30 return t;
33 return t;
31
32 }
34 }
33
35
34 toString() {
36 toString() {
35 return "@walk";
37 return "@walk";
36 }
38 }
37 }
39 }
@@ -1,41 +1,61
1 import { Constructor } from "../interfaces";
1 import { Constructor } from "../interfaces";
2
2
3 export interface InjectOptions {
3 export interface InjectOptions {
4 lazy?: boolean;
4 lazy?: boolean;
5 }
5 }
6
6
7 interface Dependency<K extends keyof any> {
8 $dependency: K;
9
10 lazy?: boolean;
11 }
12
13 interface Lazy<K extends keyof any> extends Dependency<K> {
14 lazy: true;
15 }
16
7 type Setter<T = any> = (v: T) => void;
17 type Setter<T = any> = (v: T) => void;
8
18
9 type Compatible<T1, T2> = T1 extends T2 ? any : never;
19 type Compatible<T1, T2> = T1 extends T2 ? any : never;
10
20
11 type SetterType<T> = T extends (v: infer V) => void ? V : never;
12
13 type ExtractService<K, S> = K extends keyof S ? S[K] : K;
21 type ExtractService<K, S> = K extends keyof S ? S[K] : K;
14
22
15 type ExtractDependency<D, S> = D extends { $dependency: infer K } ? D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> : VisitDependency<D, S>;
23 type ExtractDependency<D, S> = D extends { $dependency: infer K } ? D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> : VisitDependency<D, S>;
16
24
17 type VisitDependency<D, S> = D extends {} ? { [K in keyof D]: ExtractDependency<D[K], S> } : D;
25 type VisitDependency<D, S> = D extends {} ? { [K in keyof D]: ExtractDependency<D[K], S> } : D;
18
26
27 interface Config<S> {
28 dependency<K extends keyof S>(name: K): Dependency<K>;
29
30 lazy<K extends keyof S>(name: K): Lazy<K>;
31
32 build<T>(): Builder<T, S>;
33 }
34
35 export declare function services<S extends object>(): Config<S>;
36
19 export class Builder<T, S> {
37 export class Builder<T, S> {
20 consume<P extends any[]>(...args: P) {
38 consume<P extends any[]>(...args: P) {
21 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
39 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
22 return constructor as typeof constructor & { service: () => T };
40 return constructor;
23 };
41 };
24 }
42 }
25
43
26 inject<K extends keyof S>(dependency: K) {
44 inject<K extends keyof S>(dependency: K) {
27 // K = "bar"
45 // K = "bar"
28 // M = "setValue"
46 // M = "setValue"
29 // S[K] = Bar
47 // S[K] = Bar
30 // T[M] = (value: string) => void
48 // T[M] = (value: string) => void
31 // P[m] = (value: V) => void
49 // P[m] = (value: V) => void
32 return <P, M extends keyof (T | P)>(target: P, memberName: M, descriptor: TypedPropertyDescriptor<Compatible<T[M], Setter<S[K]>>>) => {
50 return <P, M extends keyof (T | P)>(target: P, memberName: M, descriptor: TypedPropertyDescriptor<Compatible<T[M], Setter<S[K]>>>) => {
33
51
34 };
52 };
35 }
53 }
36
54
37 cast<T2 extends T>(): Builder<T2, S> {
55 cast<T2 extends T>(): Builder<T2, S> {
38 return this as Builder<T2, S>;
56 return this as Builder<T2, S>;
39 }
57 }
40
58
41 }
59 }
60
61
@@ -1,12 +1,12
1 export class ConfigError extends Error {
1 export class ConfigError extends Error {
2 inner: any;
2 inner?: {};
3
3
4 path: string;
4 path?: string;
5
5
6 configName: string;
6 configName?: string;
7
7
8 constructor(message: string, inner?: any) {
8 constructor(message: string, inner?: {}) {
9 super(message);
9 super(message);
10 this.inner = inner;
10 this.inner = inner;
11 }
11 }
12 }
12 }
@@ -1,350 +1,348
1 import {
1 import {
2 ServiceRegistration,
2 ServiceRegistration,
3 TypeRegistration,
3 TypeRegistration,
4 FactoryRegistration,
4 FactoryRegistration,
5 ServiceMap,
5 ServiceMap,
6 isDescriptor,
6 isDescriptor,
7 isDependencyRegistration,
7 isDependencyRegistration,
8 DependencyRegistration,
8 DependencyRegistration,
9 ValueRegistration,
9 ValueRegistration,
10 ActivationType,
10 ActivationType,
11 isValueRegistration,
11 isValueRegistration,
12 isTypeRegistration,
12 isTypeRegistration,
13 isFactoryRegistration
13 isFactoryRegistration
14 } from "./interfaces";
14 } from "./interfaces";
15
15
16 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe";
16 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe";
17 import { AggregateDescriptor } from "./AggregateDescriptor";
17 import { AggregateDescriptor } from "./AggregateDescriptor";
18 import { ValueDescriptor } from "./ValueDescriptor";
18 import { ValueDescriptor } from "./ValueDescriptor";
19 import { Container } from "./Container";
19 import { Container } from "./Container";
20 import { ReferenceDescriptor } from "./ReferenceDescriptor";
20 import { ReferenceDescriptor } from "./ReferenceDescriptor";
21 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
21 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
22 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
22 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
23 import { TraceSource } from "../log/TraceSource";
23 import { TraceSource } from "../log/TraceSource";
24 import { ConfigError } from "./ConfigError";
24 import { ConfigError } from "./ConfigError";
25 import { Cancellation } from "../Cancellation";
25 import { Cancellation } from "../Cancellation";
26 import { makeResolver } from "./ResolverHelper";
26 import { makeResolver } from "./ResolverHelper";
27 import { ICancellation } from "../interfaces";
27 import { ICancellation } from "../interfaces";
28
28
29 const trace = TraceSource.get("@implab/core/di/Configuration");
29 const trace = TraceSource.get("@implab/core/di/Configuration");
30
30
31 async function mapAll(data: any | any[], map?: (v: any, k: keyof any) => any): Promise<any> {
31 async function mapAll(data: any | any[], map?: (v: any, k: number | string) => any): Promise<any> {
32 if (data instanceof Array) {
32 if (data instanceof Array) {
33 return Promise.all(map ? data.map(map) : data);
33 return Promise.all(map ? data.map(map) : data);
34 } else {
34 } else {
35 const keys = Object.keys(data);
35 const keys = Object.keys(data);
36
36
37 const o: any = {};
37 const o: any = {};
38
38
39 await Promise.all(keys.map(async k => {
39 await Promise.all(keys.map(async k => {
40 const v = map ? map(data[k], k) : data[k];
40 const v = map ? map(data[k], k) : data[k];
41 o[k] = isPromise(v) ? await v : v;
41 o[k] = isPromise(v) ? await v : v;
42 }));
42 }));
43
43
44 return o;
44 return o;
45 }
45 }
46 }
46 }
47
47
48 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
48 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
49
49
50 type _key = string | number;
50 export class Configuration<S> {
51
52 export class Configuration {
53
51
54 _hasInnerDescriptors = false;
52 _hasInnerDescriptors = false;
55
53
56 _container: Container;
54 _container: Container<S>;
57
55
58 _path: Array<_key>;
56 _path: Array<string>;
59
57
60 _configName: string | undefined;
58 _configName: string | undefined;
61
59
62 _require: ModuleResolver | undefined;
60 _require: ModuleResolver | undefined;
63
61
64 constructor(container: Container) {
62 constructor(container: Container<S>) {
65 argumentNotNull(container, "container");
63 argumentNotNull(container, "container");
66 this._container = container;
64 this._container = container;
67 this._path = [];
65 this._path = [];
68 }
66 }
69
67
70 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
68 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
71 argumentNotEmptyString(moduleName, "moduleName");
69 argumentNotEmptyString(moduleName, "moduleName");
72
70
73 trace.log(
71 trace.log(
74 "loadConfiguration moduleName={0}, contextRequire={1}",
72 "loadConfiguration moduleName={0}, contextRequire={1}",
75 moduleName,
73 moduleName,
76 contextRequire ? typeof (contextRequire) : "<nil>"
74 contextRequire ? typeof (contextRequire) : "<nil>"
77 );
75 );
78
76
79 this._configName = moduleName;
77 this._configName = moduleName;
80
78
81 const r = await makeResolver(undefined, contextRequire);
79 const r = await makeResolver(undefined, contextRequire);
82
80
83 const config = await r(moduleName, ct);
81 const config = await r(moduleName, ct);
84
82
85 await this._applyConfiguration(
83 await this._applyConfiguration(
86 config,
84 config,
87 await makeResolver(moduleName, contextRequire),
85 await makeResolver(moduleName, contextRequire),
88 ct
86 ct
89 );
87 );
90 }
88 }
91
89
92 async applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
90 async applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
93 argumentNotNull(data, "data");
91 argumentNotNull(data, "data");
94
92
95 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
93 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
96 }
94 }
97
95
98 async _applyConfiguration(data: object, resolver?: ModuleResolver, ct = Cancellation.none) {
96 async _applyConfiguration(data: object, resolver?: ModuleResolver, ct = Cancellation.none) {
99 trace.log("applyConfiguration");
97 trace.log("applyConfiguration");
100
98
101 this._configName = "$";
99 this._configName = "$";
102
100
103 if (resolver)
101 if (resolver)
104 this._require = resolver;
102 this._require = resolver;
105
103
106 let services: ServiceMap;
104 let services: ServiceMap;
107
105
108 try {
106 try {
109 services = await this._visitRegistrations(data, "$");
107 services = await this._visitRegistrations(data, "$");
110 } catch (e) {
108 } catch (e) {
111 throw this._makeError(e);
109 throw this._makeError(e);
112 }
110 }
113
111
114 this._container.register(services);
112 this._container.register(services);
115 }
113 }
116
114
117 _makeError(inner: any) {
115 _makeError(inner: any) {
118 const e = new ConfigError("Failed to load configuration", inner);
116 const e = new ConfigError("Failed to load configuration", inner);
119 e.configName = this._configName || "<inline>";
117 e.configName = this._configName || "<inline>";
120 e.path = this._makePath();
118 e.path = this._makePath();
121 return e;
119 return e;
122 }
120 }
123
121
124 _makePath() {
122 _makePath() {
125 return this._path
123 return this._path
126 .reduce(
124 .reduce(
127 (prev, cur) => typeof cur === "number" ?
125 (prev, cur) => typeof cur === "number" ?
128 `${prev}[${cur}]` :
126 `${prev}[${cur}]` :
129 `${prev}.${cur}`
127 `${prev}.${cur}`
130 )
128 )
131 .toString();
129 .toString();
132 }
130 }
133
131
134 async _resolveType(moduleName: string, localName: string) {
132 async _resolveType(moduleName: string, localName: string) {
135 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
133 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
136 try {
134 try {
137 const m = await this._loadModule(moduleName);
135 const m = await this._loadModule(moduleName);
138 return localName ? get(localName, m) : m;
136 return localName ? get(localName, m) : m;
139 } catch (e) {
137 } catch (e) {
140 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
138 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
141 throw e;
139 throw e;
142 }
140 }
143 }
141 }
144
142
145 _loadModule(moduleName: string) {
143 _loadModule(moduleName: string) {
146 trace.debug("loadModule {0}", moduleName);
144 trace.debug("loadModule {0}", moduleName);
147 if (!this._require)
145 if (!this._require)
148 throw new Error("Module loader isn't specified");
146 throw new Error("Module loader isn't specified");
149
147
150 return this._require(moduleName);
148 return this._require(moduleName);
151 }
149 }
152
150
153 async _visitRegistrations(data: any, name: _key) {
151 async _visitRegistrations(data: any, name: string) {
154 this._enter(name);
152 this._enter(name);
155
153
156 if (data.constructor &&
154 if (data.constructor &&
157 data.constructor.prototype !== Object.prototype)
155 data.constructor.prototype !== Object.prototype)
158 throw new Error("Configuration must be a simple object");
156 throw new Error("Configuration must be a simple object");
159
157
160 const o: ServiceMap = {};
158 const o: ServiceMap = {};
161 const keys = Object.keys(data);
159 const keys = Object.keys(data);
162
160
163 const services = await mapAll(data, async (v, k) => {
161 const services = await mapAll(data, async (v, k) => {
164 const d = await this._visit(v, k);
162 const d = await this._visit(v, k.toString());
165 return isDescriptor(d) ? d : new AggregateDescriptor(d);
163 return isDescriptor(d) ? d : new AggregateDescriptor(d);
166 }) as ServiceMap;
164 }) as ServiceMap;
167
165
168 this._leave();
166 this._leave();
169
167
170 return services;
168 return services;
171 }
169 }
172
170
173 _enter(name: keyof any) {
171 _enter(name: keyof any) {
174 this._path.push(name.toString());
172 this._path.push(name.toString());
175 trace.debug(">{0}", name);
173 trace.debug(">{0}", name);
176 }
174 }
177
175
178 _leave() {
176 _leave() {
179 const name = this._path.pop();
177 const name = this._path.pop();
180 trace.debug("<{0}", name);
178 trace.debug("<{0}", name);
181 }
179 }
182
180
183 async _visit<T extends object>(data: T, name: keyof T) {
181 async _visit<T>(data: T, name: string) {
184 if (isPrimitive(data) || isDescriptor(data))
182 if (isPrimitive(data) || isDescriptor(data))
185 return data;
183 return data;
186
184
187 if (isDependencyRegistration(data)) {
185 if (isDependencyRegistration<S>(data)) {
188 return this._visitDependencyRegistration(data, name);
186 return this._visitDependencyRegistration(data, name);
189 } else if (isValueRegistration(data)) {
187 } else if (isValueRegistration(data)) {
190 return this._visitValueRegistration(data, name);
188 return this._visitValueRegistration(data, name);
191 } else if (isTypeRegistration(data)) {
189 } else if (isTypeRegistration(data)) {
192 return this._visitTypeRegistration(data, name);
190 return this._visitTypeRegistration(data, name);
193 } else if (isFactoryRegistration(data)) {
191 } else if (isFactoryRegistration(data)) {
194 return this._visitFactoryRegistration(data, name);
192 return this._visitFactoryRegistration(data, name);
195 } else if (data instanceof Array) {
193 } else if (data instanceof Array) {
196 return this._visitArray(data, name);
194 return this._visitArray(data, name);
197 }
195 }
198
196
199 return this._visitObject(data, name);
197 return this._visitObject(data as T & object, name);
200 }
198 }
201
199
202 async _visitObject(data: object, name: _key) {
200 async _visitObject<T extends object>(data: T, name: string) {
203 if (data.constructor &&
201 if (data.constructor &&
204 data.constructor.prototype !== Object.prototype)
202 data.constructor.prototype !== Object.prototype)
205 return new ValueDescriptor(data);
203 return new ValueDescriptor(data);
206
204
207 this._enter(name);
205 this._enter(name);
208
206
209 const v = await mapAll(data, delegate(this, "_visit"));
207 const v = await mapAll(data, delegate(this, "_visit"));
210
208
211 // TODO: handle inline descriptors properly
209 // TODO: handle inline descriptors properly
212 // const ex = {
210 // const ex = {
213 // activate(ctx) {
211 // activate(ctx) {
214 // const value = ctx.activate(this.prop, "prop");
212 // const value = ctx.activate(this.prop, "prop");
215 // // some code
213 // // some code
216 // },
214 // },
217 // // will be turned to ReferenceDescriptor
215 // // will be turned to ReferenceDescriptor
218 // prop: { $dependency: "depName" }
216 // prop: { $dependency: "depName" }
219 // };
217 // };
220
218
221 this._leave();
219 this._leave();
222 return v;
220 return v;
223 }
221 }
224
222
225 async _visitArray(data: any[], name: _key) {
223 async _visitArray(data: any[], name: string) {
226 if (data.constructor &&
224 if (data.constructor &&
227 data.constructor.prototype !== Array.prototype)
225 data.constructor.prototype !== Array.prototype)
228 return new ValueDescriptor(data);
226 return new ValueDescriptor(data);
229
227
230 this._enter(name);
228 this._enter(name);
231
229
232 const v = await mapAll(data, delegate(this, "_visit"));
230 const v = await mapAll(data, delegate(this, "_visit"));
233 this._leave();
231 this._leave();
234
232
235 return v;
233 return v;
236 }
234 }
237
235
238 _makeServiceParams<T, P, S>(data: ServiceRegistration<T, P, S>) {
236 _makeServiceParams<T, P>(data: ServiceRegistration<T, P, S>) {
239 const opts: any = {
237 const opts: any = {
240 owner: this._container
238 owner: this._container
241 };
239 };
242 if (data.services)
240 if (data.services)
243 opts.services = this._visitRegistrations(data.services, "services");
241 opts.services = this._visitRegistrations(data.services, "services");
244
242
245 if (data.inject) {
243 if (data.inject) {
246 this._enter("inject");
244 this._enter("inject");
247 opts.inject = mapAll(
245 opts.inject = mapAll(
248 data.inject instanceof Array ?
246 data.inject instanceof Array ?
249 data.inject :
247 data.inject :
250 [data.inject],
248 [data.inject],
251 delegate(this, "_visitObject")
249 delegate(this, "_visitObject")
252 );
250 );
253 this._leave();
251 this._leave();
254 }
252 }
255
253
256 if ("params" in data)
254 if ("params" in data)
257 opts.params = data.params instanceof Array ?
255 opts.params = data.params instanceof Array ?
258 this._visitArray(data.params, "params") :
256 this._visitArray(data.params, "params") :
259 this._visit(data.params, "params");
257 this._visit(data.params, "params");
260
258
261 if (data.activation) {
259 if (data.activation) {
262 if (typeof (data.activation) === "string") {
260 if (typeof (data.activation) === "string") {
263 switch (data.activation.toLowerCase()) {
261 switch (data.activation.toLowerCase()) {
264 case "singleton":
262 case "singleton":
265 opts.activation = ActivationType.Singleton;
263 opts.activation = ActivationType.Singleton;
266 break;
264 break;
267 case "container":
265 case "container":
268 opts.activation = ActivationType.Container;
266 opts.activation = ActivationType.Container;
269 break;
267 break;
270 case "hierarchy":
268 case "hierarchy":
271 opts.activation = ActivationType.Hierarchy;
269 opts.activation = ActivationType.Hierarchy;
272 break;
270 break;
273 case "context":
271 case "context":
274 opts.activation = ActivationType.Context;
272 opts.activation = ActivationType.Context;
275 break;
273 break;
276 case "call":
274 case "call":
277 opts.activation = ActivationType.Call;
275 opts.activation = ActivationType.Call;
278 break;
276 break;
279 default:
277 default:
280 throw new Error("Unknown activation type: " +
278 throw new Error("Unknown activation type: " +
281 data.activation);
279 data.activation);
282 }
280 }
283 } else {
281 } else {
284 opts.activation = Number(data.activation);
282 opts.activation = Number(data.activation);
285 }
283 }
286 }
284 }
287
285
288 if (data.cleanup)
286 if (data.cleanup)
289 opts.cleanup = data.cleanup;
287 opts.cleanup = data.cleanup;
290
288
291 return opts;
289 return opts;
292 }
290 }
293
291
294 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: _key) {
292 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
295 this._enter(name);
293 this._enter(name);
296 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
294 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
297 this._leave();
295 this._leave();
298 return d;
296 return d;
299 }
297 }
300
298
301 async _visitDependencyRegistration<S, K extends keyof S>(data: DependencyRegistration<S, K>, name: keyof S) {
299 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
302 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
300 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
303 this._enter(name);
301 this._enter(name);
304 const d = new ReferenceDescriptor<S, K>({
302 const d = new ReferenceDescriptor<S, K>({
305 name: data.$dependency,
303 name: data.$dependency,
306 lazy: data.lazy,
304 lazy: data.lazy,
307 optional: data.optional,
305 optional: data.optional,
308 default: data.default,
306 default: data.default,
309 services: data.services && await this._visitRegistrations(data.services, "services")
307 services: data.services && await this._visitRegistrations(data.services, "services")
310 });
308 });
311 this._leave();
309 this._leave();
312 return d;
310 return d;
313 }
311 }
314
312
315 async _visitTypeRegistration<T, P, S>(data: TypeRegistration<T, P, S>, name: _key) {
313 async _visitTypeRegistration<T, P>(data: TypeRegistration<T, P, S>, name: string) {
316 argumentNotNull(data.$type, "data.$type");
314 argumentNotNull(data.$type, "data.$type");
317 this._enter(name);
315 this._enter(name);
318
316
319 const opts = this._makeServiceParams(data);
317 const opts = this._makeServiceParams(data);
320 if (data.$type instanceof Function) {
318 if (data.$type instanceof Function) {
321 opts.type = data.$type;
319 opts.type = data.$type;
322 } else {
320 } else {
323 const [moduleName, typeName] = data.$type.split(":", 2);
321 const [moduleName, typeName] = data.$type.split(":", 2);
324 opts.type = this._resolveType(moduleName, typeName);
322 opts.type = this._resolveType(moduleName, typeName);
325 }
323 }
326
324
327 const d = new TypeServiceDescriptor(
325 const d = new TypeServiceDescriptor(
328 await mapAll(opts)
326 await mapAll(opts)
329 );
327 );
330
328
331 this._leave();
329 this._leave();
332
330
333 return d;
331 return d;
334 }
332 }
335
333
336 async _visitFactoryRegistration<T, P, S>(data: FactoryRegistration<T, P, S>, name: _key) {
334 async _visitFactoryRegistration<T, P>(data: FactoryRegistration<T, P, S>, name: string) {
337 argumentOfType(data.$factory, Function, "data.$factory");
335 argumentOfType(data.$factory, Function, "data.$factory");
338 this._enter(name);
336 this._enter(name);
339
337
340 const opts = this._makeServiceParams(data);
338 const opts = this._makeServiceParams(data);
341 opts.factory = data.$factory;
339 opts.factory = data.$factory;
342
340
343 const d = new FactoryServiceDescriptor(
341 const d = new FactoryServiceDescriptor(
344 await mapAll(opts)
342 await mapAll(opts)
345 );
343 );
346
344
347 this._leave();
345 this._leave();
348 return d;
346 return d;
349 }
347 }
350 }
348 }
@@ -1,128 +1,138
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { ValueDescriptor } from "./ValueDescriptor";
2 import { ValueDescriptor } from "./ValueDescriptor";
3 import { ActivationError } from "./ActivationError";
3 import { ActivationError } from "./ActivationError";
4 import { isDescriptor, ServiceMap } from "./interfaces";
4 import { isDescriptor, ServiceMap, Descriptor } from "./interfaces";
5 import { TraceSource } from "../log/TraceSource";
5 import { TraceSource } from "../log/TraceSource";
6 import { Configuration } from "./Configuration";
6 import { Configuration } from "./Configuration";
7 import { Cancellation } from "../Cancellation";
7 import { Cancellation } from "../Cancellation";
8 import { MapOf } from "../interfaces";
8
9
9 const trace = TraceSource.get("@implab/core/di/ActivationContext");
10 const trace = TraceSource.get("@implab/core/di/ActivationContext");
10
11
11 export class Container {
12 export class Container<S extends { container?: Container<S> }> {
12 _services: ServiceMap;
13 readonly _services: ServiceMap<S>;
13
14
14 _cache: object;
15 readonly _cache: MapOf<any>;
16
17 readonly _cleanup: (() => void)[];
15
18
16 _cleanup: (() => void)[];
19 readonly _root: Container<S>;
17
20
18 _root: Container;
21 readonly _parent?: Container<S>;
19
22
20 _parent: Container;
23 _disposed: boolean;
21
24
22 constructor(parent?: Container) {
25 constructor(parent?: Container<S>) {
23 this._parent = parent;
26 this._parent = parent;
24 this._services = parent ? Object.create(parent._services) : {};
27 this._services = parent ? Object.create(parent._services) : {};
25 this._cache = {};
28 this._cache = {};
26 this._cleanup = [];
29 this._cleanup = [];
27 this._root = parent ? parent.getRootContainer() : this;
30 this._root = parent ? parent.getRootContainer() : this;
28 this._services.container = new ValueDescriptor(this);
31 this._services.container = new ValueDescriptor(this);
32 this._disposed = false;
29 }
33 }
30
34
31 getRootContainer() {
35 getRootContainer() {
32 return this._root;
36 return this._root;
33 }
37 }
34
38
35 getParent() {
39 getParent() {
36 return this._parent;
40 return this._parent;
37 }
41 }
38
42
39 resolve(name: string, def?) {
43 resolve<K extends keyof S, T extends S[K] = S[K]>(name: K, def?: T) {
40 trace.debug("resolve {0}", name);
44 trace.debug("resolve {0}", name);
41 const d = this._services[name];
45 const d = this._services[name];
42 if (d === undefined) {
46 if (d === undefined) {
43 if (arguments.length > 1)
47 if (def !== undefined)
44 return def;
48 return def;
45 else
49 else
46 throw new Error("Service '" + name + "' isn't found");
50 throw new Error("Service '" + name + "' isn't found");
47 }
51 } else {
48
52
49 const context = new ActivationContext(this, this._services);
53 const context = new ActivationContext<S>(this, this._services);
50 try {
54 try {
51 return context.activate(d, name);
55 return context.activate(d as Descriptor<T>, name.toString());
52 } catch (error) {
56 } catch (error) {
53 throw new ActivationError(name, context.getStack(), error);
57 throw new ActivationError(name.toString(), context.getStack(), error);
58 }
54 }
59 }
55 }
60 }
56
61
57 /**
62 /**
58 * @deprecated use resolve() method
63 * @deprecated use resolve() method
59 */
64 */
60 getService() {
65 getService<K extends keyof S>(name: K, def?: S[K]) {
61 return this.resolve.apply(this, arguments);
66 return this.resolve(name, def);
62 }
67 }
63
68
64 register(nameOrCollection, service?) {
69 register<K extends keyof S>(name: K, service: Descriptor<S[K]>): this;
70 register(services: ServiceMap<S>): this;
71 register<K extends keyof S>(nameOrCollection: K | ServiceMap<S>, service?: Descriptor<S[K]>) {
65 if (arguments.length === 1) {
72 if (arguments.length === 1) {
66 const data = nameOrCollection;
73 const data = nameOrCollection as ServiceMap<S>;
67 for (const name in data)
74 for (const name in data) {
68 this.register(name, data[name]);
75 if (Object.prototype.hasOwnProperty.call(data, name)) {
76 this.register(name, data[name] as Descriptor<S[keyof S]>);
77 }
78 }
69 } else {
79 } else {
70 if (!isDescriptor(service))
80 if (!isDescriptor(service))
71 throw new Error("The service parameter must be a descriptor");
81 throw new Error("The service parameter must be a descriptor");
72
82
73 this._services[nameOrCollection] = service;
83 this._services[nameOrCollection as K] = service;
74 }
84 }
75 return this;
85 return this;
76 }
86 }
77
87
78 onDispose(callback) {
88 onDispose(callback: () => void) {
79 if (!(callback instanceof Function))
89 if (!(callback instanceof Function))
80 throw new Error("The callback must be a function");
90 throw new Error("The callback must be a function");
81 this._cleanup.push(callback);
91 this._cleanup.push(callback);
82 }
92 }
83
93
84 dispose() {
94 dispose() {
85 if (this._cleanup) {
95 if (this._disposed)
86 for (const f of this._cleanup)
96 return;
87 f();
97 this._disposed = true;
88 this._cleanup = null;
98 for (const f of this._cleanup)
89 }
99 f();
90 }
100 }
91
101
92 /**
102 /**
93 * @param{String|Object} config
103 * @param{String|Object} config
94 * The configuration of the contaier. Can be either a string or an object,
104 * The configuration of the contaier. Can be either a string or an object,
95 * if the configuration is an object it's treated as a collection of
105 * if the configuration is an object it's treated as a collection of
96 * services which will be registed in the contaier.
106 * services which will be registed in the contaier.
97 *
107 *
98 * @param{Function} opts.contextRequire
108 * @param{Function} opts.contextRequire
99 * The function which will be used to load a configuration or types for services.
109 * The function which will be used to load a configuration or types for services.
100 *
110 *
101 */
111 */
102 async configure(config: string | object, opts?: any, ct = Cancellation.none) {
112 async configure(config: string | object, opts?: any, ct = Cancellation.none) {
103 const c = new Configuration(this);
113 const c = new Configuration(this);
104
114
105 if (typeof (config) === "string") {
115 if (typeof (config) === "string") {
106 return c.loadConfiguration(config, opts && opts.contextRequire, ct);
116 return c.loadConfiguration(config, opts && opts.contextRequire, ct);
107 } else {
117 } else {
108 return c.applyConfiguration(config, opts && opts.contextRequire, ct);
118 return c.applyConfiguration(config, opts && opts.contextRequire, ct);
109 }
119 }
110 }
120 }
111
121
112 createChildContainer() {
122 createChildContainer<S2 extends { container?: Container<S & S2> } = S>(): Container<S & S2> {
113 return new Container(this);
123 return new Container<S & S2>(this as any);
114 }
124 }
115
125
116 has(id: string | number) {
126 has(id: string | number) {
117 return id in this._cache;
127 return id in this._cache;
118 }
128 }
119
129
120 get(id: string | number) {
130 get(id: string | number) {
121 return this._cache[id];
131 return this._cache[id];
122 }
132 }
123
133
124 store(id: string | number, value: any) {
134 store(id: string | number, value: any) {
125 return (this._cache[id] = value);
135 return (this._cache[id] = value);
126 }
136 }
127
137
128 }
138 }
@@ -1,101 +1,99
1 import { isNull, argumentNotEmptyString, each } from "../safe";
1 import { isNull, argumentNotEmptyString, each, keys } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { ServiceMap, Descriptor } from "./interfaces";
3 import { ServiceMap, Descriptor } from "./interfaces";
4 import { ActivationError } from "./ActivationError";
4 import { ActivationError } from "./ActivationError";
5
5
6 export interface ReferenceDescriptorParams<S, K extends keyof S> {
6 export interface ReferenceDescriptorParams<S, K extends keyof S> {
7 name: K;
7 name: K;
8 lazy?: boolean;
8 lazy?: boolean;
9 optional?: boolean;
9 optional?: boolean;
10 default?: S[K];
10 default?: S[K];
11 services?: ServiceMap;
11 services?: ServiceMap;
12 }
12 }
13
13
14 function def<T>(v: T) {
15 if (v === undefined)
16 throw Error();
17 return v;
18 }
19
14 export class ReferenceDescriptor<S, K extends keyof S> implements Descriptor<S[K]> {
20 export class ReferenceDescriptor<S, K extends keyof S> implements Descriptor<S[K]> {
15 _name: K;
21 _name: K;
16
22
17 _lazy = false;
23 _lazy = false;
18
24
19 _optional = false;
25 _optional = false;
20
26
21 _default: any;
27 _default: any;
22
28
23 _services: ServiceMap;
29 _services: ServiceMap<S>;
24
30
25 constructor(opts: ReferenceDescriptorParams<S, K>) {
31 constructor(opts: ReferenceDescriptorParams<S, K>) {
26 argumentNotEmptyString(opts && opts.name, "opts.name");
32 argumentNotEmptyString(opts && opts.name, "opts.name");
27 this._name = opts.name;
33 this._name = opts.name;
28 this._lazy = !!opts.lazy;
34 this._lazy = !!opts.lazy;
29 this._optional = !!opts.optional;
35 this._optional = !!opts.optional;
30 this._default = opts.default;
36 this._default = opts.default;
31
37
32 this._services = opts.services || {};
38 this._services = opts.services || {};
33 }
39 }
34
40
35 activate(context: ActivationContext, name: string) {
41 activate(context: ActivationContext<S>) {
36 // добавляСм сСрвисы
42 // добавляСм сСрвисы
37 if (this._services) {
43 if (this._services) {
38 for (const p of Object.keys(this._services))
44 each(this._services, (v, k) => context.register(k, def(v)));
39 context.register(p, this._services[p]);
40 }
45 }
41
46
42 if (this._lazy) {
47 if (this._lazy) {
43 const saved = context.clone();
48 const saved = context.clone();
44
49
45 return (cfg: ServiceMap) => {
50 return (cfg: Partial<ServiceMap<S>>) => {
46 // Π·Π°Ρ‰ΠΈΡ‰Π°Π΅ΠΌ контСкст Π½Π° случай ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ Π² процСссС
51 // Π·Π°Ρ‰ΠΈΡ‰Π°Π΅ΠΌ контСкст Π½Π° случай ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ Π² процСссС
47 // Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ
52 // Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ
48 const ct = saved.clone();
53 const ct = saved.clone();
49 try {
54 try {
50 if (cfg) {
55 if (cfg) {
51 for (const k in cfg)
56 each(cfg, (v, k) => ct.register(k, v || {}));
52 ct.register(k, cfg[k]);
53 }
57 }
54
58
55 return this._optional ? ct.resolve(this._name, this._default) : ct
59 return this._optional ? ct.resolve(this._name, this._default) : ct
56 .resolve(this._name);
60 .resolve(this._name);
57 } catch (error) {
61 } catch (error) {
58 throw new ActivationError(this._name.toString(), ct.getStack(), error);
62 throw new ActivationError(this._name.toString(), ct.getStack(), error);
59 }
63 }
60 };
64 };
61 } else {
65 } else {
62 // добавляСм сСрвисы
63 if (this._services) {
64 for (const p of Object.keys(this._services))
65 context.register(p, this._services[p]);
66 }
67
68 const v = this._optional ?
66 const v = this._optional ?
69 context.resolve(this._name, this._default) :
67 context.resolve(this._name, this._default) :
70 context.resolve(this._name);
68 context.resolve(this._name);
71
69
72 return v;
70 return v;
73 }
71 }
74 }
72 }
75
73
76 toString() {
74 toString() {
77 const opts = [];
75 const opts = [];
78 if (this._optional)
76 if (this._optional)
79 opts.push("optional");
77 opts.push("optional");
80 if (this._lazy)
78 if (this._lazy)
81 opts.push("lazy");
79 opts.push("lazy");
82
80
83 const parts = [
81 const parts = [
84 "@ref "
82 "@ref "
85 ];
83 ];
86 if (opts.length) {
84 if (opts.length) {
87 parts.push("{");
85 parts.push("{");
88 parts.push(opts.join());
86 parts.push(opts.join());
89 parts.push("} ");
87 parts.push("} ");
90 }
88 }
91
89
92 parts.push(this._name.toString());
90 parts.push(this._name.toString());
93
91
94 if (!isNull(this._default)) {
92 if (!isNull(this._default)) {
95 parts.push(" = ");
93 parts.push(" = ");
96 parts.push(this._default);
94 parts.push(this._default);
97 }
95 }
98
96
99 return parts.join("");
97 return parts.join("");
100 }
98 }
101 }
99 }
@@ -1,75 +1,75
1 import { isPrimitive } from "../safe";
1 import { isPrimitive } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { Constructor, Factory } from "../interfaces";
3 import { Constructor, Factory } from "../interfaces";
4
4
5 export interface Descriptor<T = any> {
5 export interface Descriptor<T = any> {
6 activate(context: ActivationContext, name?: string): T;
6 activate<S>(context: ActivationContext<S>): T;
7 }
7 }
8
8
9 export function isDescriptor(x: any): x is Descriptor {
9 export function isDescriptor(x: any): x is Descriptor {
10 return (!isPrimitive(x)) &&
10 return (!isPrimitive(x)) &&
11 (x.activate instanceof Function);
11 (x.activate instanceof Function);
12 }
12 }
13
13
14 export type ServiceMap<S = any> = {
14 export type ServiceMap<S = any> = {
15 [k in keyof S]: Descriptor<S[k]>;
15 [k in keyof S]: Descriptor<S[k]>;
16 }
16 }
17
17
18 export enum ActivationType {
18 export enum ActivationType {
19 Singleton = 1,
19 Singleton = 1,
20 Container,
20 Container,
21 Hierarchy,
21 Hierarchy,
22 Context,
22 Context,
23 Call
23 Call
24 }
24 }
25
25
26 export interface RegistrationWithServices<S> {
26 export interface RegistrationWithServices<S> {
27 services?: ServiceMap<S>;
27 services?: ServiceMap<S>;
28 }
28 }
29
29
30 export interface ServiceRegistration<T, P, S> extends RegistrationWithServices<S> {
30 export interface ServiceRegistration<T, P, S> extends RegistrationWithServices<S> {
31
31
32 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
32 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
33
33
34 params?: P;
34 params?: P;
35
35
36 inject?: object | object[];
36 inject?: object | object[];
37
37
38 cleanup?: ((instance: T) => void) | string;
38 cleanup?: ((instance: T) => void) | string;
39 }
39 }
40
40
41 export interface TypeRegistration<T, P, S> extends ServiceRegistration<T, P, S> {
41 export interface TypeRegistration<T, P, S> extends ServiceRegistration<T, P, S> {
42 $type: string | Constructor<T>;
42 $type: string | Constructor<T>;
43 }
43 }
44
44
45 export interface FactoryRegistration<T, P, S> extends ServiceRegistration<T, P, S> {
45 export interface FactoryRegistration<T, P, S> extends ServiceRegistration<T, P, S> {
46 $factory: string | Factory<T>;
46 $factory: string | Factory<T>;
47 }
47 }
48
48
49 export interface ValueRegistration<T> {
49 export interface ValueRegistration<T> {
50 $value: T;
50 $value: T;
51 parse?: boolean;
51 parse?: boolean;
52 }
52 }
53
53
54 export interface DependencyRegistration<S, K extends keyof S> extends RegistrationWithServices<S> {
54 export interface DependencyRegistration<S, K extends keyof S> extends RegistrationWithServices<S> {
55 $dependency: K;
55 $dependency: K;
56 lazy?: boolean;
56 lazy?: boolean;
57 optional?: boolean;
57 optional?: boolean;
58 default?: S[K];
58 default?: S[K];
59 }
59 }
60
60
61 export function isTypeRegistration(x: any): x is TypeRegistration<any, any, any> {
61 export function isTypeRegistration(x: any): x is TypeRegistration<any, any, any> {
62 return (!isPrimitive(x)) && ("$type" in x);
62 return (!isPrimitive(x)) && ("$type" in x);
63 }
63 }
64
64
65 export function isFactoryRegistration(x: any): x is FactoryRegistration<any, any, any> {
65 export function isFactoryRegistration(x: any): x is FactoryRegistration<any, any, any> {
66 return (!isPrimitive(x)) && ("$factory" in x);
66 return (!isPrimitive(x)) && ("$factory" in x);
67 }
67 }
68
68
69 export function isValueRegistration(x: any): x is ValueRegistration<any> {
69 export function isValueRegistration(x: any): x is ValueRegistration<any> {
70 return (!isPrimitive(x)) && ("$value" in x);
70 return (!isPrimitive(x)) && ("$value" in x);
71 }
71 }
72
72
73 export function isDependencyRegistration(x: any): x is DependencyRegistration<any, string | number | symbol> {
73 export function isDependencyRegistration<S>(x: any): x is DependencyRegistration<S, keyof S> {
74 return (!isPrimitive(x)) && ("$dependency" in x);
74 return (!isPrimitive(x)) && ("$dependency" in x);
75 }
75 }
@@ -1,452 +1,461
1 import { ICancellable, Constructor } from "./interfaces";
1 import { ICancellable, Constructor } 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: object): string {
9 export function oid(instance: any): string | undefined {
10 if (isNull(instance))
10 if (isNull(instance))
11 return null;
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>)[] {
20 return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : [];
21 }
22
19 export function argumentNotNull(arg: any, name: string) {
23 export function argumentNotNull(arg: any, name: string) {
20 if (arg === null || arg === undefined)
24 if (arg === null || arg === undefined)
21 throw new Error("The argument " + name + " can't be null or undefined");
25 throw new Error("The argument " + name + " can't be null or undefined");
22 }
26 }
23
27
24 export function argumentNotEmptyString(arg: any, name: string) {
28 export function argumentNotEmptyString(arg: any, name: string) {
25 if (typeof (arg) !== "string" || !arg.length)
29 if (typeof (arg) !== "string" || !arg.length)
26 throw new Error("The argument '" + name + "' must be a not empty string");
30 throw new Error("The argument '" + name + "' must be a not empty string");
27 }
31 }
28
32
29 export function argumentNotEmptyArray(arg: any, name: string) {
33 export function argumentNotEmptyArray(arg: any, name: string) {
30 if (!(arg instanceof Array) || !arg.length)
34 if (!(arg instanceof Array) || !arg.length)
31 throw new Error("The argument '" + name + "' must be a not empty array");
35 throw new Error("The argument '" + name + "' must be a not empty array");
32 }
36 }
33
37
34 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
38 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
35 if (!(arg instanceof type))
39 if (!(arg instanceof type))
36 throw new Error("The argument '" + name + "' type doesn't match");
40 throw new Error("The argument '" + name + "' type doesn't match");
37 }
41 }
38
42
43 export function isObject(val: any): val is object {
44 return typeof val === "object";
45 }
46
39 export function isNull(val: any) {
47 export function isNull(val: any) {
40 return (val === null || val === undefined);
48 return (val === null || val === undefined);
41 }
49 }
42
50
43 export function isPrimitive(val: any): val is string | number | boolean | undefined | null {
51 export function isPrimitive(val: any): val is string | number | boolean | undefined | null {
44 return (val === null || val === undefined || typeof (val) === "string" ||
52 return (val === null || val === undefined || typeof (val) === "string" ||
45 typeof (val) === "number" || typeof (val) === "boolean");
53 typeof (val) === "number" || typeof (val) === "boolean");
46 }
54 }
47
55
48 export function isInteger(val: any): val is number {
56 export function isInteger(val: any): val is number {
49 return parseInt(val, 10) === val;
57 return parseInt(val, 10) === val;
50 }
58 }
51
59
52 export function isNumber(val: any): val is number {
60 export function isNumber(val: any): val is number {
53 return parseFloat(val) === val;
61 return parseFloat(val) === val;
54 }
62 }
55
63
56 export function isString(val: any): val is string {
64 export function isString(val: any): val is string {
57 return typeof (val) === "string" || val instanceof String;
65 return typeof (val) === "string" || val instanceof String;
58 }
66 }
59
67
60 export function isPromise(val: any): val is PromiseLike<any> {
68 export function isPromise(val: any): val is PromiseLike<any> {
61 return val && typeof val.then === "function";
69 return val && typeof val.then === "function";
62 }
70 }
63
71
64 export function isCancellable(val: any): val is ICancellable {
72 export function isCancellable(val: any): val is ICancellable {
65 return val && typeof val.cancel === "function";
73 return val && typeof val.cancel === "function";
66 }
74 }
67
75
68 export function isNullOrEmptyString(val: any): val is string | null | undefined {
76 export function isNullOrEmptyString(val: any): val is (string | null | undefined) {
69 if (val === null || val === undefined ||
77 return (val === null || val === undefined ||
70 ((typeof (val) === "string" || val instanceof String) && val.length === 0))
78 ((typeof (val) === "string" || val instanceof String) && val.length === 0))
71 return true;
72 }
79 }
73
80
74 export function isNotEmptyArray(arg: any): arg is Array<any> {
81 export function isNotEmptyArray(arg: any): arg is Array<any> {
75 return (arg instanceof Array && arg.length > 0);
82 return (arg instanceof Array && arg.length > 0);
76 }
83 }
77
84
78 function _isStrictMode() {
85 function _isStrictMode(this: any) {
79 return !this;
86 return !this;
80 }
87 }
81
88
82 function _getNonStrictGlobal() {
89 function _getNonStrictGlobal(this: any) {
83 return this;
90 return this;
84 }
91 }
85
92
86 export function getGlobal() {
93 export function getGlobal() {
87 // in es3 we can't use indirect call to eval, since it will
94 // in es3 we can't use indirect call to eval, since it will
88 // be executed in the current call context.
95 // be executed in the current call context.
89 if (!_isStrictMode()) {
96 if (!_isStrictMode()) {
90 return _getNonStrictGlobal();
97 return _getNonStrictGlobal();
91 } else {
98 } else {
92 // tslint:disable-next-line:no-eval
99 // tslint:disable-next-line:no-eval
93 return eval.call(null, "this");
100 return eval.call(null, "this");
94 }
101 }
95 }
102 }
96
103
97 export function get(member: string, context?: object) {
104 export function get(member: string, context?: object) {
98 argumentNotEmptyString(member, "member");
105 argumentNotEmptyString(member, "member");
99 let that = context || getGlobal();
106 let that = context || getGlobal();
100 const parts = member.split(".");
107 const parts = member.split(".");
101 for (const m of parts) {
108 for (const m of parts) {
102 if (!m)
109 if (!m)
103 continue;
110 continue;
104 if (isNull(that = that[m]))
111 if (isNull(that = that[m]))
105 break;
112 break;
106 }
113 }
107 return that;
114 return that;
108 }
115 }
109
116
110 /**
117 /**
111 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
118 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
112 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
119 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
113 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
120 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
114 *
121 *
115 * @param {Array | Object} obj массив элСмСнтов для просмотра
122 * @param {Array | Object} obj массив элСмСнтов для просмотра
116 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
123 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
117 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
124 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
118 * <c>this</c> Π² <c>cb</c>.
125 * <c>this</c> Π² <c>cb</c>.
119 * @returns Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π²Ρ‹Π·ΠΎΠ²Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ <c>cb</c>, Π»ΠΈΠ±ΠΎ <c>undefined</c>
126 * @returns Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π²Ρ‹Π·ΠΎΠ²Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ <c>cb</c>, Π»ΠΈΠ±ΠΎ <c>undefined</c>
120 * Ссли достигнут ΠΊΠΎΠ½Π΅Ρ† массива.
127 * Ссли достигнут ΠΊΠΎΠ½Π΅Ρ† массива.
121 */
128 */
122 export function each(obj, cb, thisArg?) {
129 export function each<T>(obj: T, cb: (v: T[keyof T], k: keyof T) => void): void;
130 export function each(obj: any, cb: any, thisArg?: any): any;
131 export function each(obj: any, cb: any, thisArg?: any) {
123 argumentNotNull(cb, "cb");
132 argumentNotNull(cb, "cb");
124 if (obj instanceof Array) {
133 if (obj instanceof Array) {
125 for (let i = 0; i < obj.length; i++) {
134 for (let i = 0; i < obj.length; i++) {
126 const x = cb.call(thisArg, obj[i], i);
135 const x = cb.call(thisArg, obj[i], i);
127 if (x !== undefined)
136 if (x !== undefined)
128 return x;
137 return x;
129 }
138 }
130 } else {
139 } else {
131 const keys = Object.keys(obj);
140 const _keys = Object.keys(obj);
132 for (const k of keys) {
141 for (const k of _keys) {
133 const x = cb.call(thisArg, obj[k], k);
142 const x = cb.call(thisArg, obj[k], k);
134 if (x !== undefined)
143 if (x !== undefined)
135 return x;
144 return x;
136 }
145 }
137 }
146 }
138 }
147 }
139
148
140 /** Copies property values from a source object to the destination and returns
149 /** Copies property values from a source object to the destination and returns
141 * the destination onject.
150 * the destination onject.
142 *
151 *
143 * @param dest The destination object into which properties from the source
152 * @param dest The destination object into which properties from the source
144 * object will be copied.
153 * object will be copied.
145 * @param source The source of values which will be copied to the destination
154 * @param source The source of values which will be copied to the destination
146 * object.
155 * object.
147 * @param template An optional parameter specifies which properties should be
156 * @param template An optional parameter specifies which properties should be
148 * copied from the source and how to map them to the destination. If the
157 * copied from the source and how to map them to the destination. If the
149 * template is an array it contains the list of property names to copy from the
158 * template is an array it contains the list of property names to copy from the
150 * source to the destination. In case of object the templates contains the map
159 * source to the destination. In case of object the templates contains the map
151 * where keys are property names in the source and the values are property
160 * where keys are property names in the source and the values are property
152 * names in the destination object. If the template isn't specified then the
161 * names in the destination object. If the template isn't specified then the
153 * own properties of the source are entirely copied to the destination.
162 * own properties of the source are entirely copied to the destination.
154 *
163 *
155 */
164 */
156 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: string[] | object): T & S {
165 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: string[] | object): T & S {
157 argumentNotNull(dest, "to");
166 argumentNotNull(dest, "to");
158 const _res = dest as T & S;
167 const _res = dest as T & S;
159
168
160 if (isPrimitive(source))
169 if (isPrimitive(source))
161 return _res;
170 return _res;
162
171
163 if (template instanceof Array) {
172 if (template instanceof Array) {
164 for (const p of template) {
173 for (const p of template) {
165 if (p in source)
174 if (p in source)
166 _res[p] = source[p];
175 _res[p] = source[p];
167 }
176 }
168 } else if (template) {
177 } else if (template) {
169 const keys = Object.keys(source);
178 const _keys = Object.keys(source);
170 for (const p of keys) {
179 for (const p of _keys) {
171 if (p in template)
180 if (p in template)
172 _res[template[p]] = source[p];
181 _res[template[p]] = source[p];
173 }
182 }
174 } else {
183 } else {
175 const keys = Object.keys(source);
184 const keys = Object.keys(source);
176 for (const p of keys)
185 for (const p of keys)
177 _res[p] = source[p];
186 _res[p] = source[p];
178 }
187 }
179
188
180 return _res;
189 return _res;
181 }
190 }
182
191
183 /** Wraps the specified function to emulate an asynchronous execution.
192 /** Wraps the specified function to emulate an asynchronous execution.
184 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
193 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
185 * @param{Function|String} fn [Required] Function wich will be wrapped.
194 * @param{Function|String} fn [Required] Function wich will be wrapped.
186 */
195 */
187 export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike<any> {
196 export function async(_fn: (...args: any[]) => any, thisArg: any): (...args: any[]) => PromiseLike<any> {
188 let fn = _fn;
197 let fn = _fn;
189
198
190 if (arguments.length === 2 && !(fn instanceof Function))
199 if (arguments.length === 2 && !(fn instanceof Function))
191 fn = thisArg[fn];
200 fn = thisArg[fn];
192
201
193 if (fn == null)
202 if (fn == null)
194 throw new Error("The function must be specified");
203 throw new Error("The function must be specified");
195
204
196 function wrapresult(x, e?): PromiseLike<any> {
205 function wrapresult(x, e?): PromiseLike<any> {
197 if (e) {
206 if (e) {
198 return {
207 return {
199 then(cb, eb) {
208 then(cb, eb) {
200 try {
209 try {
201 return eb ? wrapresult(eb(e)) : this;
210 return eb ? wrapresult(eb(e)) : this;
202 } catch (e2) {
211 } catch (e2) {
203 return wrapresult(null, e2);
212 return wrapresult(null, e2);
204 }
213 }
205 }
214 }
206 };
215 };
207 } else {
216 } else {
208 if (x && x.then)
217 if (x && x.then)
209 return x;
218 return x;
210 return {
219 return {
211 then(cb) {
220 then(cb) {
212 try {
221 try {
213 return cb ? wrapresult(cb(x)) : this;
222 return cb ? wrapresult(cb(x)) : this;
214 } catch (e2) {
223 } catch (e2) {
215 return wrapresult(e2);
224 return wrapresult(e2);
216 }
225 }
217 }
226 }
218 };
227 };
219 }
228 }
220 }
229 }
221
230
222 return (...args) => {
231 return (...args) => {
223 try {
232 try {
224 return wrapresult(fn.apply(thisArg, args));
233 return wrapresult(fn.apply(thisArg, args));
225 } catch (e) {
234 } catch (e) {
226 return wrapresult(null, e);
235 return wrapresult(null, e);
227 }
236 }
228 };
237 };
229 }
238 }
230
239
231 type _AnyFn = (...args) => any;
240 type _AnyFn = (...args) => any;
232
241
233 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
242 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
234 let method;
243 let method;
235 if (!(_method instanceof Function)) {
244 if (!(_method instanceof Function)) {
236 argumentNotNull(target, "target");
245 argumentNotNull(target, "target");
237 method = target[_method];
246 method = target[_method];
238 if (!(method instanceof Function))
247 if (!(method instanceof Function))
239 throw new Error("'method' argument must be a Function or a method name");
248 throw new Error("'method' argument must be a Function or a method name");
240 } else {
249 } else {
241 method = _method;
250 method = _method;
242 }
251 }
243
252
244 return (...args) => {
253 return (...args) => {
245 return method.apply(target, args);
254 return method.apply(target, args);
246 };
255 };
247 }
256 }
248
257
249 export function delay(timeMs: number, ct = Cancellation.none) {
258 export function delay(timeMs: number, ct = Cancellation.none) {
250 ct.throwIfRequested();
259 ct.throwIfRequested();
251 return new Promise((resolve, reject) => {
260 return new Promise((resolve, reject) => {
252 const h = ct.register(e => {
261 const h = ct.register(e => {
253 clearTimeout(id);
262 clearTimeout(id);
254 reject(e);
263 reject(e);
255 // we don't nedd to unregister h, since ct is already disposed
264 // we don't nedd to unregister h, since ct is already disposed
256 });
265 });
257 const id = setTimeout(() => {
266 const id = setTimeout(() => {
258 h.destroy();
267 h.destroy();
259 resolve();
268 resolve();
260 }, timeMs);
269 }, timeMs);
261
270
262 });
271 });
263 }
272 }
264
273
265 /**
274 /**
266 * Iterates over the specified array of items and calls the callback `cb`, if
275 * Iterates over the specified array of items and calls the callback `cb`, if
267 * the result of the callback is a promise the next item from the array will be
276 * the result of the callback is a promise the next item from the array will be
268 * proceeded after the promise is resolved.
277 * proceeded after the promise is resolved.
269 *
278 *
270 */
279 */
271 export function pmap<T, T2>(
280 export function pmap<T, T2>(
272 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
281 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
273 cb: (item: T, i: number) => T2 | PromiseLike<T2>
282 cb: (item: T, i: number) => T2 | PromiseLike<T2>
274 ): T2[] | PromiseLike<T2[]> {
283 ): T2[] | PromiseLike<T2[]> {
275 argumentNotNull(cb, "cb");
284 argumentNotNull(cb, "cb");
276
285
277 if (isPromise(items)) {
286 if (isPromise(items)) {
278 return items.then(data => pmap(data, cb));
287 return items.then(data => pmap(data, cb));
279 } else {
288 } else {
280
289
281 if (isNull(items) || !items.length)
290 if (isNull(items) || !items.length)
282 return [];
291 return [];
283
292
284 let i = 0;
293 let i = 0;
285 const result = new Array<T2>();
294 const result = new Array<T2>();
286
295
287 const next = () => {
296 const next = (): any => {
288 while (i < items.length) {
297 while (i < items.length) {
289 const r = cb(items[i], i);
298 const r = cb(items[i], i);
290 const ri = i;
299 const ri = i;
291 i++;
300 i++;
292 if (isPromise(r)) {
301 if (isPromise(r)) {
293 return r.then(x => {
302 return r.then(x => {
294 result[ri] = x;
303 result[ri] = x;
295 return next();
304 return next();
296 });
305 });
297 } else {
306 } else {
298 result[ri] = r;
307 result[ri] = r;
299 }
308 }
300 }
309 }
301 return result;
310 return result;
302 };
311 };
303
312
304 return next();
313 return next();
305 }
314 }
306 }
315 }
307
316
308 export function pfor<T>(
317 export function pfor<T>(
309 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
318 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
310 cb: (item: T, i: number) => any
319 cb: (item: T, i: number) => any
311 ): void | PromiseLike<void> {
320 ): void | PromiseLike<void> {
312 argumentNotNull(cb, "cb");
321 argumentNotNull(cb, "cb");
313
322
314 if (isPromise(items)) {
323 if (isPromise(items)) {
315 return items.then(data => pfor(data, cb));
324 return items.then(data => pfor(data, cb));
316 } else {
325 } else {
317 if (isNull(items) || !items.length)
326 if (isNull(items) || !items.length)
318 return;
327 return;
319
328
320 let i = 0;
329 let i = 0;
321
330
322 const next = () => {
331 const next = (): any => {
323 while (i < items.length) {
332 while (i < items.length) {
324 const r = cb(items[i], i);
333 const r = cb(items[i], i);
325 i++;
334 i++;
326 if (isPromise(r))
335 if (isPromise(r))
327 return r.then(next);
336 return r.then(next);
328 }
337 }
329 };
338 };
330
339
331 return next();
340 return next();
332 }
341 }
333 }
342 }
334
343
335 export function first<T>(sequence: ArrayLike<T>): T;
344 export function first<T>(sequence: ArrayLike<T>): T;
336 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
345 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
337 export function first<T>(
346 export function first<T>(
338 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
347 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
339 cb: (x: T) => void,
348 cb: (x: T) => void,
340 err?: (x: Error) => void
349 err?: (x: Error) => void
341 ): void;
350 ): void;
342 /**
351 /**
343 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
352 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
344 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
353 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
345 *
354 *
346 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
355 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
347 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
356 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
348 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
357 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
349 * нС массив
358 * нС массив
350 *
359 *
351 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
360 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
352 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
361 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
353 * @async
362 * @async
354 */
363 */
355 export function first<T>(
364 export function first<T>(
356 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
365 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
357 cb?: (x: T) => void,
366 cb?: (x: T) => void,
358 err?: (x: Error) => void
367 err?: (x: Error) => void
359 ) {
368 ) {
360 if (isPromise(sequence)) {
369 if (isPromise(sequence)) {
361 return sequence.then(res => first(res, cb, err));
370 return sequence.then(res => first(res, cb, err));
362 } else if (sequence && "length" in sequence) {
371 } else if (sequence && "length" in sequence) {
363 if (sequence.length === 0) {
372 if (sequence.length === 0) {
364 if (err)
373 if (err)
365 return err(new Error("The sequence is empty"));
374 return err(new Error("The sequence is empty"));
366 else
375 else
367 throw new Error("The sequence is empty");
376 throw new Error("The sequence is empty");
368 } else if (cb) {
377 } else if (cb) {
369 return cb(sequence[0]);
378 return cb(sequence[0]);
370 } else {
379 } else {
371 return sequence[0];
380 return sequence[0];
372 }
381 }
373 } else {
382 } else {
374 if (err)
383 if (err)
375 return err(new Error("The sequence is required"));
384 return err(new Error("The sequence is required"));
376 else
385 else
377 throw new Error("The sequence is required");
386 throw new Error("The sequence is required");
378 }
387 }
379 }
388 }
380
389
381 export function firstWhere<T>(
390 export function firstWhere<T>(
382 sequence: ArrayLike<T>,
391 sequence: ArrayLike<T>,
383 predicate: (x: T) => boolean
392 predicate: (x: T) => boolean
384 ): T;
393 ): T;
385 export function firstWhere<T>(
394 export function firstWhere<T>(
386 sequence: PromiseLike<ArrayLike<T>>,
395 sequence: PromiseLike<ArrayLike<T>>,
387 predicate: (x: T) => boolean
396 predicate: (x: T) => boolean
388 ): PromiseLike<T>;
397 ): PromiseLike<T>;
389 export function firstWhere<T>(
398 export function firstWhere<T>(
390 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
399 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
391 predicate: (x: T) => boolean,
400 predicate: (x: T) => boolean,
392 cb: (x: T) => void,
401 cb: (x: T) => void,
393 err?: (x: Error) => void
402 err?: (x: Error) => void
394 ): void;
403 ): void;
395
404
396 export function firstWhere<T>(
405 export function firstWhere<T>(
397 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
406 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
398 predicate?: (x: T) => boolean,
407 predicate?: (x: T) => boolean,
399 cb?: (x: T) => any,
408 cb?: (x: T) => any,
400 err?: (x: Error) => any
409 err?: (x: Error) => any
401 ) {
410 ) {
402 if (isPromise(sequence)) {
411 if (isPromise(sequence)) {
403 return sequence.then(res => firstWhere(res, predicate, cb, err));
412 return sequence.then(res => firstWhere(res, predicate, cb, err));
404 } else if (sequence && "length" in sequence) {
413 } else if (sequence && "length" in sequence) {
405 if (sequence.length === 0) {
414 if (sequence.length === 0) {
406 if (err)
415 if (err)
407 err(new Error("The sequence is empty"));
416 err(new Error("The sequence is empty"));
408 else
417 else
409 throw new Error("The sequence is empty");
418 throw new Error("The sequence is empty");
410 } else {
419 } else {
411 if (!predicate) {
420 if (!predicate) {
412 return cb ? cb(sequence[0]) && void (0) : sequence[0];
421 return cb ? cb(sequence[0]) && void (0) : sequence[0];
413 } else {
422 } else {
414 for (let i = 0; i < sequence.length; i++) {
423 for (let i = 0; i < sequence.length; i++) {
415 const v = sequence[i];
424 const v = sequence[i];
416 if (predicate(v))
425 if (predicate(v))
417 return cb ? cb(v) : v;
426 return cb ? cb(v) : v;
418 }
427 }
419 if (err)
428 if (err)
420 err(new Error("The sequence doesn't contain matching items"));
429 err(new Error("The sequence doesn't contain matching items"));
421 else
430 else
422 throw new Error("The sequence doesn't contain matching items");
431 throw new Error("The sequence doesn't contain matching items");
423 }
432 }
424 }
433 }
425 } else {
434 } else {
426 if (err)
435 if (err)
427 err(new Error("The sequence is required"));
436 err(new Error("The sequence is required"));
428 else
437 else
429 throw new Error("The sequence is required");
438 throw new Error("The sequence is required");
430 }
439 }
431 }
440 }
432
441
433 export function destroy(d: any) {
442 export function destroy(d: any) {
434 if (d && "destroy" in d)
443 if (d && "destroy" in d)
435 d.destroy();
444 d.destroy();
436 }
445 }
437
446
438 /**
447 /**
439 * Used to mark that the async operation isn't awaited intentionally.
448 * Used to mark that the async operation isn't awaited intentionally.
440 * @param p The promise which represents the async operation.
449 * @param p The promise which represents the async operation.
441 */
450 */
442 export function nowait(p: Promise<any>) {
451 export function nowait(p: Promise<any>) {
443 }
452 }
444
453
445 /** represents already destroyed object.
454 /** represents already destroyed object.
446 */
455 */
447 export const destroyed = {
456 export const destroyed = {
448 /** Calling to this method doesn't affect anything, noop.
457 /** Calling to this method doesn't affect anything, noop.
449 */
458 */
450 destroy() {
459 destroy() {
451 }
460 }
452 };
461 };
@@ -1,28 +1,35
1 import { config } from "./config";
1 import { services } from "../di/Annotations";
2 import { Bar } from "./Bar";
2
3
3 const service = config.build("barBox");
4 // declare required dependencies
5 const config = services<{
6 bar: Bar;
7 }>();
8
9 // export service descriptor
10 export const service = config.build<Box<Bar>>();
4
11
5 @service.consume(config.dependency("bar"))
12 @service.consume(config.dependency("bar"))
6 export class Box<T> {
13 export class Box<T> {
7 private _value: T | undefined;
14 private _value: T | undefined;
8
15
9 constructor(value: T) {
16 constructor(value: T) {
10 this._value = value;
17 this._value = value;
11 }
18 }
12
19
13 @service.inject("bar")
20 @service.inject("bar")
14 setValue(value: T) {
21 setValue(value: T) {
15 this._value = value;
22 this._value = value;
16 }
23 }
17
24
18 setObj(value: object) {
25 setObj(value: object) {
19
26
20 }
27 }
21
28
22 getValue() {
29 getValue() {
23 if (this._value === undefined)
30 if (this._value === undefined)
24 throw new Error("Trying to get a value from the empty box");
31 throw new Error("Trying to get a value from the empty box");
25
32
26 return this._value;
33 return this._value;
27 }
34 }
28 } No newline at end of file
35 }
General Comments 0
You need to be logged in to leave comments. Login now