##// END OF EJS Templates
working on IoC configuration
cin -
r113:22cf333c0b34 ioc ts support
parent child
Show More
@@ -1,36 +1,36
1 import { ActivationContextInfo } from "./ActivationContext";
1 import { ActivationContextInfo } from "./ActivationContext";
2
2
3 export class ActivationError {
3 export class ActivationError {
4 activationStack: ActivationContextInfo[];
4 activationStack: ActivationContextInfo[];
5
5
6 service: string;
6 service: string;
7
7
8 innerException: any;
8 innerException: any;
9
9
10 message: string;
10 message: string;
11
11
12 constructor(service: string, activationStack: ActivationContextInfo[], innerException) {
12 constructor(service: string, activationStack: ActivationContextInfo[], innerException: any) {
13 this.message = "Failed to activate the service";
13 this.message = "Failed to activate the service";
14 this.activationStack = activationStack;
14 this.activationStack = activationStack;
15 this.service = service;
15 this.service = service;
16 this.innerException = innerException;
16 this.innerException = innerException;
17 }
17 }
18
18
19 toString() {
19 toString() {
20 const parts = [this.message];
20 const parts = [this.message];
21 if (this.service)
21 if (this.service)
22 parts.push("when activating: " + this.service.toString());
22 parts.push("when activating: " + this.service.toString());
23
23
24 if (this.innerException)
24 if (this.innerException)
25 parts.push("caused by: " + this.innerException.toString());
25 parts.push("caused by: " + this.innerException.toString());
26
26
27 if (this.activationStack) {
27 if (this.activationStack) {
28 parts.push("at");
28 parts.push("at");
29 this.activationStack
29 this.activationStack
30 .forEach(x => parts.push(` ${x.name} ${x.service}`));
30 .forEach(x => parts.push(` ${x.name} ${x.service}`));
31
31
32 }
32 }
33
33
34 return parts.join("\n");
34 return parts.join("\n");
35 }
35 }
36 }
36 }
@@ -1,37 +1,37
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 implements Descriptor {
5 export class AggregateDescriptor<T> implements Descriptor<T> {
6 _value: object;
6 _value: T;
7
7
8 constructor(value: object) {
8 constructor(value: T) {
9 this._value = value;
9 this._value = value;
10 }
10 }
11
11
12 activate(context: ActivationContext) {
12 activate(context: ActivationContext) {
13 return this._parse(this._value, context, "$value");
13 return this._parse(this._value, context, "$value");
14 }
14 }
15
15
16 // TODO: make async
16 // TODO: make async
17 _parse(value, context: ActivationContext, path: string) {
17 _parse(value: T, context: ActivationContext, path: string) {
18 if (isPrimitive(value))
18 if (isPrimitive(value))
19 return value;
19 return value;
20
20
21 if (isDescriptor(value))
21 if (isDescriptor(value))
22 return context.activate(value, path);
22 return context.activate(value, path);
23
23
24 if (value instanceof Array)
24 if (value instanceof Array)
25 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`));
25 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`));
26
26
27 const t = {};
27 const t = {};
28 for (const p of Object.keys(value))
28 for (const p of Object.keys(value))
29 t[p] = this._parse(value[p], context, `${path}.${p}`);
29 t[p] = this._parse(value[p], context, `${path}.${p}`);
30 return t;
30 return t;
31
31
32 }
32 }
33
33
34 toString() {
34 toString() {
35 return "@walk";
35 return "@walk";
36 }
36 }
37 }
37 }
@@ -1,348 +1,350
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: object | any[], map?: (v, k) => any): Promise<any> {
31 async function mapAll(data: any | any[], map?: (v: any, k: keyof any) => 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 type _key = string | number;
51
51
52 export class Configuration {
52 export class Configuration {
53
53
54 _hasInnerDescriptors = false;
54 _hasInnerDescriptors = false;
55
55
56 _container: Container;
56 _container: Container;
57
57
58 _path: Array<_key>;
58 _path: Array<_key>;
59
59
60 _configName: string;
60 _configName: string | undefined;
61
61
62 _require: ModuleResolver;
62 _require: ModuleResolver | undefined;
63
63
64 constructor(container: Container) {
64 constructor(container: Container) {
65 argumentNotNull(container, "container");
65 argumentNotNull(container, "container");
66 this._container = container;
66 this._container = container;
67 this._path = [];
67 this._path = [];
68 }
68 }
69
69
70 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
70 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
71 argumentNotEmptyString(moduleName, "moduleName");
71 argumentNotEmptyString(moduleName, "moduleName");
72
72
73 trace.log(
73 trace.log(
74 "loadConfiguration moduleName={0}, contextRequire={1}",
74 "loadConfiguration moduleName={0}, contextRequire={1}",
75 moduleName,
75 moduleName,
76 contextRequire ? typeof (contextRequire) : "<nil>"
76 contextRequire ? typeof (contextRequire) : "<nil>"
77 );
77 );
78
78
79 this._configName = moduleName;
79 this._configName = moduleName;
80
80
81 const r = await makeResolver(null, contextRequire);
81 const r = await makeResolver(undefined, contextRequire);
82
82
83 const config = await r(moduleName, ct);
83 const config = await r(moduleName, ct);
84
84
85 await this._applyConfiguration(
85 await this._applyConfiguration(
86 config,
86 config,
87 await makeResolver(moduleName, contextRequire),
87 await makeResolver(moduleName, contextRequire),
88 ct
88 ct
89 );
89 );
90 }
90 }
91
91
92 async applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
92 async applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
93 argumentNotNull(data, "data");
93 argumentNotNull(data, "data");
94
94
95 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
95 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
96 }
96 }
97
97
98 async _applyConfiguration(data: object, resolver?: ModuleResolver, ct = Cancellation.none) {
98 async _applyConfiguration(data: object, resolver?: ModuleResolver, ct = Cancellation.none) {
99 trace.log("applyConfiguration");
99 trace.log("applyConfiguration");
100
100
101 this._configName = "$";
101 this._configName = "$";
102
102
103 if (resolver)
103 if (resolver)
104 this._require = resolver;
104 this._require = resolver;
105
105
106 let services: ServiceMap;
106 let services: ServiceMap;
107
107
108 try {
108 try {
109 services = await this._visitRegistrations(data, "$");
109 services = await this._visitRegistrations(data, "$");
110 } catch (e) {
110 } catch (e) {
111 throw this._makeError(e);
111 throw this._makeError(e);
112 }
112 }
113
113
114 this._container.register(services);
114 this._container.register(services);
115 }
115 }
116
116
117 _makeError(inner) {
117 _makeError(inner: any) {
118 const e = new ConfigError("Failed to load configuration", inner);
118 const e = new ConfigError("Failed to load configuration", inner);
119 e.configName = this._configName;
119 e.configName = this._configName || "<inline>";
120 e.path = this._makePath();
120 e.path = this._makePath();
121 return e;
121 return e;
122 }
122 }
123
123
124 _makePath() {
124 _makePath() {
125 return this._path
125 return this._path
126 .reduce(
126 .reduce(
127 (prev, cur) => typeof cur === "number" ?
127 (prev, cur) => typeof cur === "number" ?
128 `${prev}[${cur}]` :
128 `${prev}[${cur}]` :
129 `${prev}.${cur}`
129 `${prev}.${cur}`
130 )
130 )
131 .toString();
131 .toString();
132 }
132 }
133
133
134 async _resolveType(moduleName: string, localName: string) {
134 async _resolveType(moduleName: string, localName: string) {
135 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
135 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
136 try {
136 try {
137 const m = await this._loadModule(moduleName);
137 const m = await this._loadModule(moduleName);
138 return localName ? get(localName, m) : m;
138 return localName ? get(localName, m) : m;
139 } catch (e) {
139 } catch (e) {
140 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
140 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
141 throw e;
141 throw e;
142 }
142 }
143 }
143 }
144
144
145 _loadModule(moduleName: string) {
145 _loadModule(moduleName: string) {
146 trace.debug("loadModule {0}", moduleName);
146 trace.debug("loadModule {0}", moduleName);
147 if (!this._require)
148 throw new Error("Module loader isn't specified");
147
149
148 return this._require(moduleName);
150 return this._require(moduleName);
149 }
151 }
150
152
151 async _visitRegistrations(data, name: _key) {
153 async _visitRegistrations(data: any, name: _key) {
152 this._enter(name);
154 this._enter(name);
153
155
154 if (data.constructor &&
156 if (data.constructor &&
155 data.constructor.prototype !== Object.prototype)
157 data.constructor.prototype !== Object.prototype)
156 throw new Error("Configuration must be a simple object");
158 throw new Error("Configuration must be a simple object");
157
159
158 const o: ServiceMap = {};
160 const o: ServiceMap = {};
159 const keys = Object.keys(data);
161 const keys = Object.keys(data);
160
162
161 const services = await mapAll(data, async (v, k) => {
163 const services = await mapAll(data, async (v, k) => {
162 const d = await this._visit(v, k);
164 const d = await this._visit(v, k);
163 return isDescriptor(d) ? d : new AggregateDescriptor(d);
165 return isDescriptor(d) ? d : new AggregateDescriptor(d);
164 }) as ServiceMap;
166 }) as ServiceMap;
165
167
166 this._leave();
168 this._leave();
167
169
168 return services;
170 return services;
169 }
171 }
170
172
171 _enter(name: _key) {
173 _enter(name: keyof any) {
172 this._path.push(name);
174 this._path.push(name.toString());
173 trace.debug(">{0}", name);
175 trace.debug(">{0}", name);
174 }
176 }
175
177
176 _leave() {
178 _leave() {
177 const name = this._path.pop();
179 const name = this._path.pop();
178 trace.debug("<{0}", name);
180 trace.debug("<{0}", name);
179 }
181 }
180
182
181 async _visit(data, name: string) {
183 async _visit<T extends object>(data: T, name: keyof T) {
182 if (isPrimitive(data) || isDescriptor(data))
184 if (isPrimitive(data) || isDescriptor(data))
183 return data;
185 return data;
184
186
185 if (isDependencyRegistration(data)) {
187 if (isDependencyRegistration(data)) {
186 return this._visitDependencyRegistration(data, name);
188 return this._visitDependencyRegistration(data, name);
187 } else if (isValueRegistration(data)) {
189 } else if (isValueRegistration(data)) {
188 return this._visitValueRegistration(data, name);
190 return this._visitValueRegistration(data, name);
189 } else if (isTypeRegistration(data)) {
191 } else if (isTypeRegistration(data)) {
190 return this._visitTypeRegistration(data, name);
192 return this._visitTypeRegistration(data, name);
191 } else if (isFactoryRegistration(data)) {
193 } else if (isFactoryRegistration(data)) {
192 return this._visitFactoryRegistration(data, name);
194 return this._visitFactoryRegistration(data, name);
193 } else if (data instanceof Array) {
195 } else if (data instanceof Array) {
194 return this._visitArray(data, name);
196 return this._visitArray(data, name);
195 }
197 }
196
198
197 return this._visitObject(data, name);
199 return this._visitObject(data, name);
198 }
200 }
199
201
200 async _visitObject(data: object, name: _key) {
202 async _visitObject(data: object, name: _key) {
201 if (data.constructor &&
203 if (data.constructor &&
202 data.constructor.prototype !== Object.prototype)
204 data.constructor.prototype !== Object.prototype)
203 return new ValueDescriptor(data);
205 return new ValueDescriptor(data);
204
206
205 this._enter(name);
207 this._enter(name);
206
208
207 const v = await mapAll(data, delegate(this, "_visit"));
209 const v = await mapAll(data, delegate(this, "_visit"));
208
210
209 // TODO: handle inline descriptors properly
211 // TODO: handle inline descriptors properly
210 // const ex = {
212 // const ex = {
211 // activate(ctx) {
213 // activate(ctx) {
212 // const value = ctx.activate(this.prop, "prop");
214 // const value = ctx.activate(this.prop, "prop");
213 // // some code
215 // // some code
214 // },
216 // },
215 // // will be turned to ReferenceDescriptor
217 // // will be turned to ReferenceDescriptor
216 // prop: { $dependency: "depName" }
218 // prop: { $dependency: "depName" }
217 // };
219 // };
218
220
219 this._leave();
221 this._leave();
220 return v;
222 return v;
221 }
223 }
222
224
223 async _visitArray(data: any[], name: _key) {
225 async _visitArray(data: any[], name: _key) {
224 if (data.constructor &&
226 if (data.constructor &&
225 data.constructor.prototype !== Array.prototype)
227 data.constructor.prototype !== Array.prototype)
226 return new ValueDescriptor(data);
228 return new ValueDescriptor(data);
227
229
228 this._enter(name);
230 this._enter(name);
229
231
230 const v = await mapAll(data, delegate(this, "_visit"));
232 const v = await mapAll(data, delegate(this, "_visit"));
231 this._leave();
233 this._leave();
232
234
233 return v;
235 return v;
234 }
236 }
235
237
236 _makeServiceParams(data: ServiceRegistration) {
238 _makeServiceParams<T, P, S>(data: ServiceRegistration<T, P, S>) {
237 const opts: any = {
239 const opts: any = {
238 owner: this._container
240 owner: this._container
239 };
241 };
240 if (data.services)
242 if (data.services)
241 opts.services = this._visitRegistrations(data.services, "services");
243 opts.services = this._visitRegistrations(data.services, "services");
242
244
243 if (data.inject) {
245 if (data.inject) {
244 this._enter("inject");
246 this._enter("inject");
245 opts.inject = mapAll(
247 opts.inject = mapAll(
246 data.inject instanceof Array ?
248 data.inject instanceof Array ?
247 data.inject :
249 data.inject :
248 [data.inject],
250 [data.inject],
249 delegate(this, "_visitObject")
251 delegate(this, "_visitObject")
250 );
252 );
251 this._leave();
253 this._leave();
252 }
254 }
253
255
254 if ("params" in data)
256 if ("params" in data)
255 opts.params = data.params instanceof Array ?
257 opts.params = data.params instanceof Array ?
256 this._visitArray(data.params, "params") :
258 this._visitArray(data.params, "params") :
257 this._visit(data.params, "params");
259 this._visit(data.params, "params");
258
260
259 if (data.activation) {
261 if (data.activation) {
260 if (typeof (data.activation) === "string") {
262 if (typeof (data.activation) === "string") {
261 switch (data.activation.toLowerCase()) {
263 switch (data.activation.toLowerCase()) {
262 case "singleton":
264 case "singleton":
263 opts.activation = ActivationType.Singleton;
265 opts.activation = ActivationType.Singleton;
264 break;
266 break;
265 case "container":
267 case "container":
266 opts.activation = ActivationType.Container;
268 opts.activation = ActivationType.Container;
267 break;
269 break;
268 case "hierarchy":
270 case "hierarchy":
269 opts.activation = ActivationType.Hierarchy;
271 opts.activation = ActivationType.Hierarchy;
270 break;
272 break;
271 case "context":
273 case "context":
272 opts.activation = ActivationType.Context;
274 opts.activation = ActivationType.Context;
273 break;
275 break;
274 case "call":
276 case "call":
275 opts.activation = ActivationType.Call;
277 opts.activation = ActivationType.Call;
276 break;
278 break;
277 default:
279 default:
278 throw new Error("Unknown activation type: " +
280 throw new Error("Unknown activation type: " +
279 data.activation);
281 data.activation);
280 }
282 }
281 } else {
283 } else {
282 opts.activation = Number(data.activation);
284 opts.activation = Number(data.activation);
283 }
285 }
284 }
286 }
285
287
286 if (data.cleanup)
288 if (data.cleanup)
287 opts.cleanup = data.cleanup;
289 opts.cleanup = data.cleanup;
288
290
289 return opts;
291 return opts;
290 }
292 }
291
293
292 async _visitValueRegistration(data: ValueRegistration, name: _key) {
294 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: _key) {
293 this._enter(name);
295 this._enter(name);
294 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
296 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
295 this._leave();
297 this._leave();
296 return d;
298 return d;
297 }
299 }
298
300
299 async _visitDependencyRegistration(data: DependencyRegistration, name: _key) {
301 async _visitDependencyRegistration<S, K extends keyof S>(data: DependencyRegistration<S, K>, name: keyof S) {
300 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
302 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
301 this._enter(name);
303 this._enter(name);
302 const d = new ReferenceDescriptor({
304 const d = new ReferenceDescriptor<S, K>({
303 name: data.$dependency,
305 name: data.$dependency,
304 lazy: data.lazy,
306 lazy: data.lazy,
305 optional: data.optional,
307 optional: data.optional,
306 default: data.default,
308 default: data.default,
307 services: data.services && await this._visitRegistrations(data.services, "services")
309 services: data.services && await this._visitRegistrations(data.services, "services")
308 });
310 });
309 this._leave();
311 this._leave();
310 return d;
312 return d;
311 }
313 }
312
314
313 async _visitTypeRegistration(data: TypeRegistration, name: _key) {
315 async _visitTypeRegistration<T, P, S>(data: TypeRegistration<T, P, S>, name: _key) {
314 argumentNotNull(data.$type, "data.$type");
316 argumentNotNull(data.$type, "data.$type");
315 this._enter(name);
317 this._enter(name);
316
318
317 const opts = this._makeServiceParams(data);
319 const opts = this._makeServiceParams(data);
318 if (data.$type instanceof Function) {
320 if (data.$type instanceof Function) {
319 opts.type = data.$type;
321 opts.type = data.$type;
320 } else {
322 } else {
321 const [moduleName, typeName] = data.$type.split(":", 2);
323 const [moduleName, typeName] = data.$type.split(":", 2);
322 opts.type = this._resolveType(moduleName, typeName);
324 opts.type = this._resolveType(moduleName, typeName);
323 }
325 }
324
326
325 const d = new TypeServiceDescriptor(
327 const d = new TypeServiceDescriptor(
326 await mapAll(opts)
328 await mapAll(opts)
327 );
329 );
328
330
329 this._leave();
331 this._leave();
330
332
331 return d;
333 return d;
332 }
334 }
333
335
334 async _visitFactoryRegistration(data: FactoryRegistration, name: _key) {
336 async _visitFactoryRegistration<T, P, S>(data: FactoryRegistration<T, P, S>, name: _key) {
335 argumentOfType(data.$factory, Function, "data.$factory");
337 argumentOfType(data.$factory, Function, "data.$factory");
336 this._enter(name);
338 this._enter(name);
337
339
338 const opts = this._makeServiceParams(data);
340 const opts = this._makeServiceParams(data);
339 opts.factory = data.$factory;
341 opts.factory = data.$factory;
340
342
341 const d = new FactoryServiceDescriptor(
343 const d = new FactoryServiceDescriptor(
342 await mapAll(opts)
344 await mapAll(opts)
343 );
345 );
344
346
345 this._leave();
347 this._leave();
346 return d;
348 return d;
347 }
349 }
348 }
350 }
@@ -1,100 +1,101
1 import { isNull, argumentNotEmptyString, each } from "../safe";
1 import { isNull, argumentNotEmptyString, each } 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 {
6 export interface ReferenceDescriptorParams<S, K extends keyof S> {
7 name: string;
7 name: K;
8 lazy?: boolean;
8 lazy?: boolean;
9 optional?: boolean;
9 optional?: boolean;
10 default?;
10 default?: S[K];
11 services?: ServiceMap;
11 services?: ServiceMap;
12 }
12 }
13
13
14 export class ReferenceDescriptor implements Descriptor {
14 export class ReferenceDescriptor<S, K extends keyof S> implements Descriptor<S[K]> {
15 _name: string;
15 _name: K;
16
16
17 _lazy = false;
17 _lazy = false;
18
18
19 _optional = false;
19 _optional = false;
20
20
21 _default: any;
21 _default: any;
22
22
23 _services: ServiceMap;
23 _services: ServiceMap;
24
24
25 constructor(opts: ReferenceDescriptorParams) {
25 constructor(opts: ReferenceDescriptorParams<S, K>) {
26 argumentNotEmptyString(opts && opts.name, "opts.name");
26 argumentNotEmptyString(opts && opts.name, "opts.name");
27 this._name = opts.name;
27 this._name = opts.name;
28 this._lazy = !!opts.lazy;
28 this._lazy = !!opts.lazy;
29 this._optional = !!opts.optional;
29 this._optional = !!opts.optional;
30 this._default = opts.default;
30 this._default = opts.default;
31 this._services = opts.services;
31
32 this._services = opts.services || {};
32 }
33 }
33
34
34 activate(context: ActivationContext, name: string) {
35 activate(context: ActivationContext, name: string) {
35 // добавляСм сСрвисы
36 // добавляСм сСрвисы
36 if (this._services) {
37 if (this._services) {
37 for (const p of Object.keys(this._services))
38 for (const p of Object.keys(this._services))
38 context.register(p, this._services[p]);
39 context.register(p, this._services[p]);
39 }
40 }
40
41
41 if (this._lazy) {
42 if (this._lazy) {
42 const saved = context.clone();
43 const saved = context.clone();
43
44
44 return (cfg: ServiceMap) => {
45 return (cfg: ServiceMap) => {
45 // Π·Π°Ρ‰ΠΈΡ‰Π°Π΅ΠΌ контСкст Π½Π° случай ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ Π² процСссС
46 // Π·Π°Ρ‰ΠΈΡ‰Π°Π΅ΠΌ контСкст Π½Π° случай ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ Π² процСссС
46 // Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ
47 // Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ
47 const ct = saved.clone();
48 const ct = saved.clone();
48 try {
49 try {
49 if (cfg) {
50 if (cfg) {
50 for (const k in cfg)
51 for (const k in cfg)
51 ct.register(k, cfg[k]);
52 ct.register(k, cfg[k]);
52 }
53 }
53
54
54 return this._optional ? ct.resolve(this._name, this._default) : ct
55 return this._optional ? ct.resolve(this._name, this._default) : ct
55 .resolve(this._name);
56 .resolve(this._name);
56 } catch (error) {
57 } catch (error) {
57 throw new ActivationError(this._name, ct.getStack(), error);
58 throw new ActivationError(this._name.toString(), ct.getStack(), error);
58 }
59 }
59 };
60 };
60 } else {
61 } else {
61 // добавляСм сСрвисы
62 // добавляСм сСрвисы
62 if (this._services) {
63 if (this._services) {
63 for (const p of Object.keys(this._services))
64 for (const p of Object.keys(this._services))
64 context.register(p, this._services[p]);
65 context.register(p, this._services[p]);
65 }
66 }
66
67
67 const v = this._optional ?
68 const v = this._optional ?
68 context.resolve(this._name, this._default) :
69 context.resolve(this._name, this._default) :
69 context.resolve(this._name);
70 context.resolve(this._name);
70
71
71 return v;
72 return v;
72 }
73 }
73 }
74 }
74
75
75 toString() {
76 toString() {
76 const opts = [];
77 const opts = [];
77 if (this._optional)
78 if (this._optional)
78 opts.push("optional");
79 opts.push("optional");
79 if (this._lazy)
80 if (this._lazy)
80 opts.push("lazy");
81 opts.push("lazy");
81
82
82 const parts = [
83 const parts = [
83 "@ref "
84 "@ref "
84 ];
85 ];
85 if (opts.length) {
86 if (opts.length) {
86 parts.push("{");
87 parts.push("{");
87 parts.push(opts.join());
88 parts.push(opts.join());
88 parts.push("} ");
89 parts.push("} ");
89 }
90 }
90
91
91 parts.push(this._name);
92 parts.push(this._name.toString());
92
93
93 if (!isNull(this._default)) {
94 if (!isNull(this._default)) {
94 parts.push(" = ");
95 parts.push(" = ");
95 parts.push(this._default);
96 parts.push(this._default);
96 }
97 }
97
98
98 return parts.join("");
99 return parts.join("");
99 }
100 }
100 }
101 }
@@ -1,17 +1,17
1 import { Descriptor } from "./interfaces";
1 import { Descriptor } from "./interfaces";
2
2
3 export class ValueDescriptor implements Descriptor {
3 export class ValueDescriptor<T> implements Descriptor<T> {
4 _value;
4 _value: T;
5
5
6 constructor(value) {
6 constructor(value: T) {
7 this._value = value;
7 this._value = value;
8 }
8 }
9
9
10 activate() {
10 activate() {
11 return this._value;
11 return this._value;
12 }
12 }
13
13
14 toString() {
14 toString() {
15 return `@type=${typeof this._value}`;
15 return `@type=${typeof this._value}`;
16 }
16 }
17 }
17 }
@@ -1,75 +1,75
1 import { isNull, 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 {
5 export interface Descriptor<T = any> {
6 activate(context: ActivationContext, name?: string);
6 activate(context: ActivationContext, name?: string): T;
7 }
7 }
8
8
9 export function isDescriptor(x): 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 interface ServiceMap {
14 export type ServiceMap<S = any> = {
15 [s: string]: Descriptor;
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 {
26 export interface RegistrationWithServices<S> {
27 services?: object;
27 services?: ServiceMap<S>;
28 }
28 }
29
29
30 export interface ServiceRegistration extends RegistrationWithServices {
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?;
34 params?: P;
35
35
36 inject?: object | object[];
36 inject?: object | object[];
37
37
38 cleanup?: (instance) => void | string;
38 cleanup?: ((instance: T) => void) | string;
39 }
39 }
40
40
41 export interface TypeRegistration extends ServiceRegistration {
41 export interface TypeRegistration<T, P, S> extends ServiceRegistration<T, P, S> {
42 $type: string | Constructor;
42 $type: string | Constructor<T>;
43 }
43 }
44
44
45 export interface FactoryRegistration extends ServiceRegistration {
45 export interface FactoryRegistration<T, P, S> extends ServiceRegistration<T, P, S> {
46 $factory: string | Factory;
46 $factory: string | Factory<T>;
47 }
47 }
48
48
49 export interface ValueRegistration {
49 export interface ValueRegistration<T> {
50 $value;
50 $value: T;
51 parse?: boolean;
51 parse?: boolean;
52 }
52 }
53
53
54 export interface DependencyRegistration extends RegistrationWithServices {
54 export interface DependencyRegistration<S, K extends keyof S> extends RegistrationWithServices<S> {
55 $dependency: string;
55 $dependency: K;
56 lazy?: boolean;
56 lazy?: boolean;
57 optional?: boolean;
57 optional?: boolean;
58 default?;
58 default?: S[K];
59 }
59 }
60
60
61 export function isTypeRegistration(x): x is TypeRegistration {
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): x is FactoryRegistration {
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): x is ValueRegistration {
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): x is DependencyRegistration {
73 export function isDependencyRegistration(x: any): x is DependencyRegistration<any, string | number | symbol> {
74 return (!isPrimitive(x)) && ("$dependency" in x);
74 return (!isPrimitive(x)) && ("$dependency" in x);
75 }
75 }
General Comments 0
You need to be logged in to leave comments. Login now