##// END OF EJS Templates
tests
cin -
r136:17c7de5d346c ioc ts support
parent child
Show More
@@ -1,17 +1,28
1 <?xml version="1.0" encoding="UTF-8"?>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <projectDescription>
2 <projectDescription>
3 <name>core</name>
3 <name>core</name>
4 <comment>Project implabjs-core created by Buildship.</comment>
4 <comment>Project implabjs-core created by Buildship.</comment>
5 <projects>
5 <projects>
6 </projects>
6 </projects>
7 <buildSpec>
7 <buildSpec>
8 <buildCommand>
8 <buildCommand>
9 <name>org.eclipse.buildship.core.gradleprojectbuilder</name>
9 <name>org.eclipse.buildship.core.gradleprojectbuilder</name>
10 <arguments>
10 <arguments>
11 </arguments>
11 </arguments>
12 </buildCommand>
12 </buildCommand>
13 </buildSpec>
13 </buildSpec>
14 <natures>
14 <natures>
15 <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
15 <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
16 </natures>
16 </natures>
17 <filteredResources>
18 <filter>
19 <id>1599549685358</id>
20 <name></name>
21 <type>30</type>
22 <matcher>
23 <id>org.eclipse.core.resources.regexFilterMatcher</id>
24 <arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
25 </matcher>
26 </filter>
27 </filteredResources>
17 </projectDescription>
28 </projectDescription>
@@ -1,450 +1,451
1 import {
1 import {
2 PartialServiceMap,
2 PartialServiceMap,
3 ActivationType,
3 ActivationType,
4 ContainerKeys,
4 ContainerKeys,
5 TypeOfService,
5 TypeOfService,
6 ILifetime
6 ILifetime
7 } from "./interfaces";
7 } from "./interfaces";
8
8
9 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
9 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
11 import { ValueDescriptor } from "./ValueDescriptor";
11 import { ValueDescriptor } from "./ValueDescriptor";
12 import { Container } from "./Container";
12 import { Container } from "./Container";
13 import { ReferenceDescriptor } from "./ReferenceDescriptor";
13 import { ReferenceDescriptor } from "./ReferenceDescriptor";
14 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
14 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
15 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
15 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
16 import { TraceSource } from "../log/TraceSource";
16 import { TraceSource } from "../log/TraceSource";
17 import { ConfigError } from "./ConfigError";
17 import { ConfigError } from "./ConfigError";
18 import { Cancellation } from "../Cancellation";
18 import { Cancellation } from "../Cancellation";
19 import { makeResolver } from "./ResolverHelper";
19 import { makeResolver } from "./ResolverHelper";
20 import { ICancellation } from "../interfaces";
20 import { ICancellation } from "../interfaces";
21 import { isDescriptor } from "./traits";
21 import { isDescriptor } from "./traits";
22 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
22 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
23 import { LifetimeManager } from "./LifetimeManager";
23 import { LifetimeManager } from "./LifetimeManager";
24
24
25 export interface RegistrationScope<S extends object> {
25 export interface RegistrationScope<S extends object> {
26
26
27 /** сервисы, которые регистрируются в контексте активации и таким образом
27 /** сервисы, которые регистрируются в контексте активации и таким образом
28 * могут переопределять ранее зарегистрированные сервисы. за это свойство
28 * могут переопределять ранее зарегистрированные сервисы. за это свойство
29 * нужно платить, кроме того порядок активации будет влиять на результат
29 * нужно платить, кроме того порядок активации будет влиять на результат
30 * разрешения зависимостей.
30 * разрешения зависимостей.
31 */
31 */
32 services?: RegistrationMap<S>;
32 services?: RegistrationMap<S>;
33 }
33 }
34
34
35 /**
35 /**
36 * Базовый интерфейс конфигурации сервисов
36 * Базовый интерфейс конфигурации сервисов
37 */
37 */
38 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
38 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
39
39
40 activation?: ActivationType;
40 activation?: ActivationType;
41
41
42 params?: any;
42 params?: any;
43
43
44 /** Специальный идентификатор используется при активации singleton, если
44 /** Специальный идентификатор используется при активации singleton, если
45 * не указан для TypeRegistration вычисляется как oid($type)
45 * не указан для TypeRegistration вычисляется как oid($type)
46 */
46 */
47 typeId?: string;
47 typeId?: string;
48
48
49 inject?: object | object[];
49 inject?: object | object[];
50
50
51 cleanup?: ((instance: T) => void) | string;
51 cleanup?: ((instance: T) => void) | string;
52 }
52 }
53
53
54 export interface TypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
54 export interface TypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
55 $type: string | C;
55 $type: string | C;
56 params?: Registration<ConstructorParameters<C>, S>;
56 params?: Registration<ConstructorParameters<C>, S>;
57 }
57 }
58
58
59 export interface StrictTypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
59 export interface StrictTypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
60 $type: C;
60 $type: C;
61 params?: Registration<ConstructorParameters<C>, S>;
61 params?: Registration<ConstructorParameters<C>, S>;
62 }
62 }
63
63
64 export interface FactoryRegistration<F extends (...args: any[]) => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
64 export interface FactoryRegistration<F extends (...args: any[]) => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
65 $factory: string | F;
65 $factory: string | F;
66 }
66 }
67
67
68 export interface ValueRegistration<T> {
68 export interface ValueRegistration<T> {
69 $value: T;
69 $value: T;
70 parse?: boolean;
70 parse?: boolean;
71 }
71 }
72
72
73 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
73 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
74 $dependency: K;
74 $dependency: K;
75 lazy?: boolean;
75 lazy?: boolean;
76 optional?: boolean;
76 optional?: boolean;
77 default?: TypeOfService<S, K>;
77 default?: TypeOfService<S, K>;
78 }
78 }
79
79
80 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
80 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
81 lazy: true;
81 lazy: true;
82 }
82 }
83
83
84 export type Registration<T, S extends object> = T extends primitive ? T :
84 export type Registration<T, S extends object> = T extends primitive ? T :
85 (
85 (
86 T |
86 T |
87 { [k in keyof T]: Registration<T[k], S> } |
87 { [k in keyof T]: Registration<T[k], S> } |
88 TypeRegistration<new (...args: any[]) => T, S> |
88 TypeRegistration<new (...args: any[]) => T, S> |
89 FactoryRegistration<(...args: any[]) => T, S> |
89 FactoryRegistration<(...args: any[]) => T, S> |
90 ValueRegistration<any> |
90 ValueRegistration<any> |
91 DependencyRegistration<S, keyof S>
91 DependencyRegistration<S, keyof S>
92 );
92 );
93
93
94 export type RegistrationMap<S extends object> = {
94 export type RegistrationMap<S extends object> = {
95 [k in keyof S]?: Registration<S[k], S>;
95 [k in keyof S]?: Registration<S[k], S>;
96 };
96 };
97
97
98 const _activationTypes: { [k in ActivationType]: number; } = {
98 const _activationTypes: { [k in ActivationType]: number; } = {
99 singleton: 1,
99 singleton: 1,
100 container: 2,
100 container: 2,
101 hierarchy: 3,
101 hierarchy: 3,
102 context: 4,
102 context: 4,
103 call: 5
103 call: 5
104 };
104 };
105
105
106 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
106 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
107 return (!isPrimitive(x)) && ("$type" in x);
107 return (!isPrimitive(x)) && ("$type" in x);
108 }
108 }
109
109
110 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
110 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
111 return (!isPrimitive(x)) && ("$factory" in x);
111 return (!isPrimitive(x)) && ("$factory" in x);
112 }
112 }
113
113
114 export function isValueRegistration(x: any): x is ValueRegistration<any> {
114 export function isValueRegistration(x: any): x is ValueRegistration<any> {
115 return (!isPrimitive(x)) && ("$value" in x);
115 return (!isPrimitive(x)) && ("$value" in x);
116 }
116 }
117
117
118 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
118 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
119 return (!isPrimitive(x)) && ("$dependency" in x);
119 return (!isPrimitive(x)) && ("$dependency" in x);
120 }
120 }
121
121
122 export function isActivationType(x: string): x is ActivationType {
122 export function isActivationType(x: string): x is ActivationType {
123 return typeof x === "string" && x in _activationTypes;
123 return typeof x === "string" && x in _activationTypes;
124 }
124 }
125
125
126 const trace = TraceSource.get("@implab/core/di/Configuration");
126 const trace = TraceSource.get("@implab/core/di/Configuration");
127 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
127 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
128 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
128 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
129 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
129 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
130 if (data instanceof Array) {
130 if (data instanceof Array) {
131 return Promise.all(map ? data.map(map) : data);
131 return Promise.all(map ? data.map(map) : data);
132 } else {
132 } else {
133 const keys = Object.keys(data);
133 const keys = Object.keys(data);
134
134
135 const o: any = {};
135 const o: any = {};
136
136
137 await Promise.all(keys.map(async k => {
137 await Promise.all(keys.map(async k => {
138 const v = map ? map(data[k], k) : data[k];
138 const v = map ? map(data[k], k) : data[k];
139 o[k] = isPromise(v) ? await v : v;
139 o[k] = isPromise(v) ? await v : v;
140 }));
140 }));
141
141
142 return o;
142 return o;
143 }
143 }
144 }
144 }
145
145
146 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
146 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
147
147
148 export class Configuration<S extends object> {
148 export class Configuration<S extends object> {
149
149
150 _hasInnerDescriptors = false;
150 _hasInnerDescriptors = false;
151
151
152 readonly _container: Container<S>;
152 readonly _container: Container<S>;
153
153
154 _path: Array<string>;
154 _path: Array<string>;
155
155
156 _configName: string | undefined;
156 _configName: string | undefined;
157
157
158 _require: ModuleResolver | undefined;
158 _require: ModuleResolver | undefined;
159
159
160 constructor(container: Container<S>) {
160 constructor(container: Container<S>) {
161 argumentNotNull(container, "container");
161 argumentNotNull(container, "container");
162 this._container = container;
162 this._container = container;
163 this._path = [];
163 this._path = [];
164 }
164 }
165
165
166 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
166 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
167 argumentNotEmptyString(moduleName, "moduleName");
167 argumentNotEmptyString(moduleName, "moduleName");
168
168
169 trace.log(
169 trace.log(
170 "loadConfiguration moduleName={0}, contextRequire={1}",
170 "loadConfiguration moduleName={0}, contextRequire={1}",
171 moduleName,
171 moduleName,
172 contextRequire ? typeof (contextRequire) : "<nil>"
172 contextRequire ? typeof (contextRequire) : "<nil>"
173 );
173 );
174
174
175 this._configName = moduleName;
175 this._configName = moduleName;
176
176
177 const r = await makeResolver(undefined, contextRequire);
177 const r = await makeResolver(undefined, contextRequire);
178
178
179 const config = await r(moduleName, ct);
179 const config = await r(moduleName, ct);
180
180
181 await this._applyConfiguration(
181 await this._applyConfiguration(
182 config,
182 config,
183 await makeResolver(moduleName, contextRequire),
183 await makeResolver(moduleName, contextRequire),
184 ct
184 ct
185 );
185 );
186 }
186 }
187
187
188 async applyConfiguration(data: RegistrationMap<S>, contextRequire?: any, ct = Cancellation.none) {
188 async applyConfiguration(data: RegistrationMap<S>, opts: { contextRequire?: any; baseModule?: string }, ct = Cancellation.none) {
189 argumentNotNull(data, "data");
189 argumentNotNull(data, "data");
190 const _opts = opts || {};
190
191
191 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
192 await this._applyConfiguration(data, await makeResolver(_opts.baseModule, _opts.contextRequire), ct);
192 }
193 }
193
194
194 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
195 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
195 trace.log("applyConfiguration");
196 trace.log("applyConfiguration");
196
197
197 this._configName = "$";
198 this._configName = "$";
198
199
199 if (resolver)
200 if (resolver)
200 this._require = resolver;
201 this._require = resolver;
201
202
202 let services: PartialServiceMap<S>;
203 let services: PartialServiceMap<S>;
203
204
204 try {
205 try {
205 services = await this._visitRegistrations(data, "$");
206 services = await this._visitRegistrations(data, "$");
206 } catch (e) {
207 } catch (e) {
207 throw this._makeError(e);
208 throw this._makeError(e);
208 }
209 }
209
210
210 this._container.register(services);
211 this._container.register(services);
211 }
212 }
212
213
213 _makeError(inner: any) {
214 _makeError(inner: any) {
214 const e = new ConfigError("Failed to load configuration", inner);
215 const e = new ConfigError("Failed to load configuration", inner);
215 e.configName = this._configName || "<inline>";
216 e.configName = this._configName || "<inline>";
216 e.path = this._makePath();
217 e.path = this._makePath();
217 return e;
218 return e;
218 }
219 }
219
220
220 _makePath() {
221 _makePath() {
221 return this._path
222 return this._path
222 .reduce(
223 .reduce(
223 (prev, cur) => typeof cur === "number" ?
224 (prev, cur) => typeof cur === "number" ?
224 `${prev}[${cur}]` :
225 `${prev}[${cur}]` :
225 `${prev}.${cur}`
226 `${prev}.${cur}`
226 )
227 )
227 .toString();
228 .toString();
228 }
229 }
229
230
230 async _resolveType(moduleName: string, localName: string) {
231 async _resolveType(moduleName: string, localName: string) {
231 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
232 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
232 try {
233 try {
233 const m = await this._loadModule(moduleName);
234 const m = await this._loadModule(moduleName);
234 if (localName) {
235 if (localName) {
235 return get(localName, m);
236 return get(localName, m);
236 } else {
237 } else {
237 if (m instanceof Function)
238 if (m instanceof Function)
238 return m;
239 return m;
239 if ("default" in m)
240 if ("default" in m)
240 return m.default;
241 return m.default;
241 return m;
242 return m;
242 }
243 }
243 } catch (e) {
244 } catch (e) {
244 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
245 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
245 throw e;
246 throw e;
246 }
247 }
247 }
248 }
248
249
249 _loadModule(moduleName: string) {
250 _loadModule(moduleName: string) {
250 trace.debug("loadModule {0}", moduleName);
251 trace.debug("loadModule {0}", moduleName);
251 if (!this._require)
252 if (!this._require)
252 throw new Error("Module loader isn't specified");
253 throw new Error("Module loader isn't specified");
253
254
254 return this._require(moduleName);
255 return this._require(moduleName);
255 }
256 }
256
257
257 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
258 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
258 this._enter(name);
259 this._enter(name);
259
260
260 if (data.constructor &&
261 if (data.constructor &&
261 data.constructor.prototype !== Object.prototype)
262 data.constructor.prototype !== Object.prototype)
262 throw new Error("Configuration must be a simple object");
263 throw new Error("Configuration must be a simple object");
263
264
264 const services = await mapAll(data, async (v, k) => {
265 const services = await mapAll(data, async (v, k) => {
265 const d = await this._visit(v, k.toString());
266 const d = await this._visit(v, k.toString());
266 return isDescriptor(d) ? d : new AggregateDescriptor(d);
267 return isDescriptor(d) ? d : new AggregateDescriptor(d);
267 }) as PartialServiceMap<S>;
268 }) as PartialServiceMap<S>;
268
269
269 this._leave();
270 this._leave();
270
271
271 return services;
272 return services;
272 }
273 }
273
274
274 _enter(name: string) {
275 _enter(name: string) {
275 this._path.push(name.toString());
276 this._path.push(name.toString());
276 trace.debug(">{0}", name);
277 trace.debug(">{0}", name);
277 }
278 }
278
279
279 _leave() {
280 _leave() {
280 const name = this._path.pop();
281 const name = this._path.pop();
281 trace.debug("<{0}", name);
282 trace.debug("<{0}", name);
282 }
283 }
283
284
284 _visit(data: any, name: string): Promise<any> {
285 _visit(data: any, name: string): Promise<any> {
285 if (isPrimitive(data))
286 if (isPrimitive(data))
286 return Promise.resolve(new ValueDescriptor(data));
287 return Promise.resolve(new ValueDescriptor(data));
287 if (isDescriptor(data))
288 if (isDescriptor(data))
288 return Promise.resolve(data);
289 return Promise.resolve(data);
289
290
290 if (isDependencyRegistration<S>(data)) {
291 if (isDependencyRegistration<S>(data)) {
291 return this._visitDependencyRegistration(data, name);
292 return this._visitDependencyRegistration(data, name);
292 } else if (isValueRegistration(data)) {
293 } else if (isValueRegistration(data)) {
293 return this._visitValueRegistration(data, name);
294 return this._visitValueRegistration(data, name);
294 } else if (isTypeRegistration(data)) {
295 } else if (isTypeRegistration(data)) {
295 return this._visitTypeRegistration(data, name);
296 return this._visitTypeRegistration(data, name);
296 } else if (isFactoryRegistration(data)) {
297 } else if (isFactoryRegistration(data)) {
297 return this._visitFactoryRegistration(data, name);
298 return this._visitFactoryRegistration(data, name);
298 } else if (data instanceof Array) {
299 } else if (data instanceof Array) {
299 return this._visitArray(data, name);
300 return this._visitArray(data, name);
300 }
301 }
301
302
302 return this._visitObject(data, name);
303 return this._visitObject(data, name);
303 }
304 }
304
305
305 async _visitObject(data: any, name: string) {
306 async _visitObject(data: any, name: string) {
306 if (data.constructor &&
307 if (data.constructor &&
307 data.constructor.prototype !== Object.prototype)
308 data.constructor.prototype !== Object.prototype)
308 return new ValueDescriptor(data);
309 return new ValueDescriptor(data);
309
310
310 this._enter(name);
311 this._enter(name);
311
312
312 const v = await mapAll(data, delegate(this, "_visit"));
313 const v = await mapAll(data, delegate(this, "_visit"));
313
314
314 // TODO: handle inline descriptors properly
315 // TODO: handle inline descriptors properly
315 // const ex = {
316 // const ex = {
316 // activate(ctx) {
317 // activate(ctx) {
317 // const value = ctx.activate(this.prop, "prop");
318 // const value = ctx.activate(this.prop, "prop");
318 // // some code
319 // // some code
319 // },
320 // },
320 // // will be turned to ReferenceDescriptor
321 // // will be turned to ReferenceDescriptor
321 // prop: { $dependency: "depName" }
322 // prop: { $dependency: "depName" }
322 // };
323 // };
323
324
324 this._leave();
325 this._leave();
325 return v;
326 return v;
326 }
327 }
327
328
328 async _visitArray(data: any[], name: string) {
329 async _visitArray(data: any[], name: string) {
329 if (data.constructor &&
330 if (data.constructor &&
330 data.constructor.prototype !== Array.prototype)
331 data.constructor.prototype !== Array.prototype)
331 return new ValueDescriptor(data);
332 return new ValueDescriptor(data);
332
333
333 this._enter(name);
334 this._enter(name);
334
335
335 const v = await mapAll(data, delegate(this, "_visit"));
336 const v = await mapAll(data, delegate(this, "_visit"));
336 this._leave();
337 this._leave();
337
338
338 return v;
339 return v;
339 }
340 }
340
341
341 _makeServiceParams(data: ServiceRegistration<any, S>) {
342 _makeServiceParams(data: ServiceRegistration<any, S>) {
342 const opts: any = {
343 const opts: any = {
343 };
344 };
344 if (data.services)
345 if (data.services)
345 opts.services = this._visitRegistrations(data.services, "services");
346 opts.services = this._visitRegistrations(data.services, "services");
346
347
347 if (data.inject) {
348 if (data.inject) {
348 this._enter("inject");
349 this._enter("inject");
349 opts.inject = mapAll(
350 opts.inject = mapAll(
350 data.inject instanceof Array ?
351 data.inject instanceof Array ?
351 data.inject :
352 data.inject :
352 [data.inject],
353 [data.inject],
353 delegate(this, "_visitObject")
354 delegate(this, "_visitObject")
354 );
355 );
355 this._leave();
356 this._leave();
356 }
357 }
357
358
358 if ("params" in data)
359 if ("params" in data)
359 opts.params = data.params instanceof Array ?
360 opts.params = data.params instanceof Array ?
360 this._visitArray(data.params, "params") :
361 this._visitArray(data.params, "params") :
361 this._visit(data.params, "params");
362 this._visit(data.params, "params");
362
363
363 if (data.activation) {
364 if (data.activation) {
364 opts.activation = this._getLifetimeManager(data.activation, data.typeId);
365 opts.activation = this._getLifetimeManager(data.activation, data.typeId);
365 }
366 }
366
367
367 if (data.cleanup)
368 if (data.cleanup)
368 opts.cleanup = data.cleanup;
369 opts.cleanup = data.cleanup;
369
370
370 return opts;
371 return opts;
371 }
372 }
372
373
373 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
374 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
374 this._enter(name);
375 this._enter(name);
375 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
376 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
376 this._leave();
377 this._leave();
377 return d;
378 return d;
378 }
379 }
379
380
380 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
381 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
381 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
382 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
382 this._enter(name);
383 this._enter(name);
383 const options = {
384 const options = {
384 name: data.$dependency,
385 name: data.$dependency,
385 optional: data.optional,
386 optional: data.optional,
386 default: data.default,
387 default: data.default,
387 services: data.services && await this._visitRegistrations(data.services, "services")
388 services: data.services && await this._visitRegistrations(data.services, "services")
388 };
389 };
389 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
390 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
390 this._leave();
391 this._leave();
391 return d;
392 return d;
392 }
393 }
393
394
394 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
395 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
395 argumentNotNull(data.$type, "data.$type");
396 argumentNotNull(data.$type, "data.$type");
396 this._enter(name);
397 this._enter(name);
397
398
398 const opts = this._makeServiceParams(data);
399 const opts = this._makeServiceParams(data);
399 if (data.$type instanceof Function) {
400 if (data.$type instanceof Function) {
400 opts.type = data.$type;
401 opts.type = data.$type;
401 } else {
402 } else {
402 const [moduleName, typeName] = data.$type.split(":", 2);
403 const [moduleName, typeName] = data.$type.split(":", 2);
403 opts.type = this._resolveType(moduleName, typeName).then(t => {
404 opts.type = this._resolveType(moduleName, typeName).then(t => {
404 if (!(t instanceof Function))
405 if (!(t instanceof Function))
405 throw Error("$type (" + data.$type + ") is not a constructable");
406 throw Error("$type (" + data.$type + ") is not a constructable");
406 return t;
407 return t;
407 });
408 });
408 }
409 }
409
410
410 const d = new TypeServiceDescriptor<S, any, any[]>(
411 const d = new TypeServiceDescriptor<S, any, any[]>(
411 await mapAll(opts)
412 await mapAll(opts)
412 );
413 );
413
414
414 this._leave();
415 this._leave();
415
416
416 return d;
417 return d;
417 }
418 }
418
419
419 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
420 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
420 argumentOfType(data.$factory, Function, "data.$factory");
421 argumentOfType(data.$factory, Function, "data.$factory");
421 this._enter(name);
422 this._enter(name);
422
423
423 const opts = this._makeServiceParams(data);
424 const opts = this._makeServiceParams(data);
424 opts.factory = data.$factory;
425 opts.factory = data.$factory;
425
426
426 const d = new FactoryServiceDescriptor<S, any, any[]>(
427 const d = new FactoryServiceDescriptor<S, any, any[]>(
427 await mapAll(opts)
428 await mapAll(opts)
428 );
429 );
429
430
430 this._leave();
431 this._leave();
431 return d;
432 return d;
432 }
433 }
433
434
434 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetime {
435 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetime {
435 switch (activation) {
436 switch (activation) {
436 case "container":
437 case "container":
437 return LifetimeManager.containerLifetime(this._container);
438 return LifetimeManager.containerLifetime(this._container);
438 case "hierarchy":
439 case "hierarchy":
439 return LifetimeManager.hierarchyLifetime();
440 return LifetimeManager.hierarchyLifetime();
440 case "context":
441 case "context":
441 return LifetimeManager.contextLifetime();
442 return LifetimeManager.contextLifetime();
442 case "singleton":
443 case "singleton":
443 if (typeId === undefined)
444 if (typeId === undefined)
444 throw Error("The singleton activation requires a typeId");
445 throw Error("The singleton activation requires a typeId");
445 return LifetimeManager.singletonLifetime(typeId);
446 return LifetimeManager.singletonLifetime(typeId);
446 default:
447 default:
447 return LifetimeManager.empty();
448 return LifetimeManager.empty();
448 }
449 }
449 }
450 }
450 }
451 }
@@ -1,139 +1,149
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { ValueDescriptor } from "./ValueDescriptor";
2 import { ValueDescriptor } from "./ValueDescriptor";
3 import { ActivationError } from "./ActivationError";
3 import { ActivationError } from "./ActivationError";
4 import { ServiceMap, Descriptor, PartialServiceMap, ServiceLocator, ContainerServiceMap, ContainerKeys, TypeOfService } from "./interfaces";
4 import { ServiceMap, Descriptor, PartialServiceMap, ServiceLocator, ContainerServiceMap, ContainerKeys, TypeOfService } from "./interfaces";
5 import { TraceSource } from "../log/TraceSource";
5 import { TraceSource } from "../log/TraceSource";
6 import { Configuration, RegistrationMap } from "./Configuration";
6 import { Configuration, RegistrationMap } from "./Configuration";
7 import { Cancellation } from "../Cancellation";
7 import { Cancellation } from "../Cancellation";
8 import { MapOf, IDestroyable } from "../interfaces";
8 import { MapOf, IDestroyable } from "../interfaces";
9 import { isDescriptor } from "./traits";
9 import { isDescriptor } from "./traits";
10 import { LifetimeManager } from "./LifetimeManager";
10 import { LifetimeManager } from "./LifetimeManager";
11 import { each } from "../safe";
11 import { each } from "../safe";
12 import { FluentRegistrations } from "./fluent/interfaces";
12 import { FluentRegistrations } from "./fluent/interfaces";
13 import { FluentConfiguration } from "./fluent/FluentConfiguration";
13 import { FluentConfiguration } from "./fluent/FluentConfiguration";
14
14
15 const trace = TraceSource.get("@implab/core/di/ActivationContext");
15 const trace = TraceSource.get("@implab/core/di/ActivationContext");
16
16
17 export class Container<S extends object = any> implements ServiceLocator<S>, IDestroyable {
17 export class Container<S extends object = any> implements ServiceLocator<S>, IDestroyable {
18 readonly _services: ContainerServiceMap<S>;
18 readonly _services: ContainerServiceMap<S>;
19
19
20 readonly _lifetimeManager: LifetimeManager;
20 readonly _lifetimeManager: LifetimeManager;
21
21
22 readonly _cleanup: (() => void)[];
22 readonly _cleanup: (() => void)[];
23
23
24 readonly _root: Container<S>;
24 readonly _root: Container<S>;
25
25
26 readonly _parent?: Container<S>;
26 readonly _parent?: Container<S>;
27
27
28 _disposed: boolean;
28 _disposed: boolean;
29
29
30 constructor(parent?: Container<S>) {
30 constructor(parent?: Container<S>) {
31 this._parent = parent;
31 this._parent = parent;
32 this._services = parent ? Object.create(parent._services) : {};
32 this._services = parent ? Object.create(parent._services) : {};
33 this._cleanup = [];
33 this._cleanup = [];
34 this._root = parent ? parent.getRootContainer() : this;
34 this._root = parent ? parent.getRootContainer() : this;
35 this._services.container = new ValueDescriptor(this) as any;
35 this._services.container = new ValueDescriptor(this) as any;
36 this._disposed = false;
36 this._disposed = false;
37 this._lifetimeManager = new LifetimeManager();
37 this._lifetimeManager = new LifetimeManager();
38 }
38 }
39
39
40 getRootContainer() {
40 getRootContainer() {
41 return this._root;
41 return this._root;
42 }
42 }
43
43
44 getParent() {
44 getParent() {
45 return this._parent;
45 return this._parent;
46 }
46 }
47
47
48 getLifetimeManager() {
48 getLifetimeManager() {
49 return this._lifetimeManager;
49 return this._lifetimeManager;
50 }
50 }
51
51
52 resolve<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>): TypeOfService<S, K> {
52 resolve<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>): TypeOfService<S, K> {
53 trace.debug("resolve {0}", name);
53 trace.debug("resolve {0}", name);
54 const d = this._services[name];
54 const d = this._services[name];
55 if (d === undefined) {
55 if (d === undefined) {
56 if (def !== undefined)
56 if (def !== undefined)
57 return def;
57 return def;
58 else
58 else
59 throw new Error("Service '" + name + "' isn't found");
59 throw new Error("Service '" + name + "' isn't found");
60 } else {
60 } else {
61
61
62 const context = new ActivationContext<S>(this, this._services, String(name), d);
62 const context = new ActivationContext<S>(this, this._services, String(name), d);
63 try {
63 try {
64 return d.activate(context);
64 return d.activate(context);
65 } catch (error) {
65 } catch (error) {
66 throw new ActivationError(name.toString(), context.getStack(), error);
66 throw new ActivationError(name.toString(), context.getStack(), error);
67 }
67 }
68 }
68 }
69 }
69 }
70
70
71 /**
71 /**
72 * @deprecated use resolve() method
72 * @deprecated use resolve() method
73 */
73 */
74 getService<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>) {
74 getService<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>) {
75 return this.resolve(name, def);
75 return this.resolve(name, def);
76 }
76 }
77
77
78 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>): this;
78 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>): this;
79 register(services: PartialServiceMap<S>): this;
79 register(services: PartialServiceMap<S>): this;
80 register<K extends keyof S>(nameOrCollection: K | ServiceMap<S>, service?: Descriptor<S, S[K]>) {
80 register<K extends keyof S>(nameOrCollection: K | ServiceMap<S>, service?: Descriptor<S, S[K]>) {
81 if (arguments.length === 1) {
81 if (arguments.length === 1) {
82 const data = nameOrCollection as ServiceMap<S>;
82 const data = nameOrCollection as ServiceMap<S>;
83
83
84 each(data, (v, k) => this.register(k, v));
84 each(data, (v, k) => this.register(k, v));
85 } else {
85 } else {
86 if (!isDescriptor(service))
86 if (!isDescriptor(service))
87 throw new Error("The service parameter must be a descriptor");
87 throw new Error("The service parameter must be a descriptor");
88
88
89 this._services[nameOrCollection as K] = service as any;
89 this._services[nameOrCollection as K] = service as any;
90 }
90 }
91 return this;
91 return this;
92 }
92 }
93
93
94 onDispose(callback: () => void) {
94 onDispose(callback: () => void) {
95 if (!(callback instanceof Function))
95 if (!(callback instanceof Function))
96 throw new Error("The callback must be a function");
96 throw new Error("The callback must be a function");
97 this._cleanup.push(callback);
97 this._cleanup.push(callback);
98 }
98 }
99
99
100 destroy() {
100 destroy() {
101 return this.dispose();
101 return this.dispose();
102 }
102 }
103 dispose() {
103 dispose() {
104 if (this._disposed)
104 if (this._disposed)
105 return;
105 return;
106 this._disposed = true;
106 this._disposed = true;
107 for (const f of this._cleanup)
107 for (const f of this._cleanup)
108 f();
108 f();
109 }
109 }
110
110
111 /**
111 /**
112 * @param{String|Object} config
112 * @param{String|Object} config
113 * The configuration of the contaier. Can be either a string or an object,
113 * The configuration of the container. Can be either a string or an object,
114 * if the configuration is an object it's treated as a collection of
114 * if the configuration is an object it's treated as a collection of
115 * services which will be registed in the contaier.
115 * services which will be registered in the container.
116 *
116 *
117 * @param{Function} opts.contextRequire
117 * @param{Function} opts.contextRequire
118 * The function which will be used to load a configuration or types for services.
118 * The function which will be used to load a configuration or types for services.
119 *
119 *
120 */
120 */
121 async configure(config: string | RegistrationMap<S>, opts?: any, ct = Cancellation.none) {
121 async configure(config: string | RegistrationMap<S>, opts?: { contextRequire: any; baseModule?: string }, ct = Cancellation.none) {
122 const c = new Configuration<S>(this);
122 const _opts = Object.create(opts || null);
123
123
124 if (typeof (config) === "string") {
124 if (typeof (config) === "string") {
125 return c.loadConfiguration(config, opts && opts.contextRequire, ct);
125 _opts.baseModule = config;
126
127 const module = await import(config);
128 if (module && module.default && typeof (module.default.apply) === "function")
129 return module.default.apply(this);
130 else
131 return this._applyLegacyConfig(module, _opts, ct);
126 } else {
132 } else {
127 return c.applyConfiguration(config, opts && opts.contextRequire, ct);
133 return this._applyLegacyConfig(config, _opts, ct);
134 }
128 }
135 }
136
137 async _applyLegacyConfig(config: RegistrationMap<S>, opts: { contextRequire: any; baseModule?: string }, ct = Cancellation.none) {
138 return new Configuration<S>(this).applyConfiguration(config, opts);
129 }
139 }
130
140
131 async fluent<K extends keyof S>(config: FluentRegistrations<K, S>, ct = Cancellation.none): Promise<this> {
141 async fluent<K extends keyof S>(config: FluentRegistrations<K, S>, ct = Cancellation.none): Promise<this> {
132 await new FluentConfiguration<S>().register(config).apply(this, ct);
142 await new FluentConfiguration<S>().register(config).apply(this, ct);
133 return this;
143 return this;
134 }
144 }
135
145
136 createChildContainer<S2 extends object = S>(): Container<S & S2> {
146 createChildContainer<S2 extends object = S>(): Container<S & S2> {
137 return new Container<S & S2>(this as any);
147 return new Container<S & S2>(this as any);
138 }
148 }
139 }
149 }
@@ -1,58 +1,60
1 import { Container } from "../Container";
1 import { Container } from "../Container";
2 import { argumentNotNull, each, isPrimitive, isPromise } from "../../safe";
2 import { argumentNotNull, each, isPrimitive, isPromise } from "../../safe";
3 import { DescriptorBuilder } from "./DescriptorBuilder";
3 import { DescriptorBuilder } from "./DescriptorBuilder";
4 import { RegistrationBuilder, FluentRegistrations } from "./interfaces";
4 import { RegistrationBuilder, FluentRegistrations } from "./interfaces";
5 import { Cancellation } from "../../Cancellation";
5 import { Cancellation } from "../../Cancellation";
6
6
7 export class FluentConfiguration<S extends object, Y extends keyof S = keyof S> {
7 export class FluentConfiguration<S extends object, Y extends keyof S = keyof S> {
8
8
9 _builders: { [k in keyof S]?: RegistrationBuilder<S, S[k]> } = {};
9 _builders: { [k in keyof S]?: RegistrationBuilder<S, S[k]> } = {};
10
10
11 register<K extends Y>(name: K, builder: RegistrationBuilder<S, S[K]>): FluentConfiguration<S, Exclude<Y, K>>;
11 register<K extends Y>(name: K, builder: RegistrationBuilder<S, S[K]>): FluentConfiguration<S, Exclude<Y, K>>;
12 register<K extends Y>(config: FluentRegistrations<K, S>): FluentConfiguration<S, Exclude<Y, K>>;
12 register<K extends Y>(config: FluentRegistrations<K, S>): FluentConfiguration<S, Exclude<Y, K>>;
13 register<K extends Y>(nameOrConfig: K | FluentRegistrations<K, S>, builder?: RegistrationBuilder<S, S[K]>): FluentConfiguration<S, Exclude<Y, K>> {
13 register<K extends Y>(nameOrConfig: K | FluentRegistrations<K, S>, builder?: RegistrationBuilder<S, S[K]>): FluentConfiguration<S, Exclude<Y, K>> {
14 if (isPrimitive(nameOrConfig)) {
14 if (isPrimitive(nameOrConfig)) {
15 argumentNotNull(builder, "builder");
15 argumentNotNull(builder, "builder");
16 this._builders[nameOrConfig] = builder;
16 this._builders[nameOrConfig] = builder;
17 } else {
17 } else {
18 each(nameOrConfig, (v, k) => this.register(k, v));
18 each(nameOrConfig, (v, k) => this.register(k, v));
19 }
19 }
20
20
21 return this;
21 return this;
22 }
22 }
23
23
24 apply(target: Container<S>, ct = Cancellation.none) {
24 apply<SC extends object>(target: Container<SC>, ct = Cancellation.none) {
25
25
26 let pending = 1;
26 let pending = 1;
27
27
28 return new Promise((resolve, reject) => {
28 const _t2 = target as unknown as Container<SC & S>;
29
30 return new Promise<Container<SC & S>>((resolve, reject) => {
29 function guard(v: void | Promise<void>) {
31 function guard(v: void | Promise<void>) {
30 if (isPromise(v))
32 if (isPromise(v))
31 v.catch(reject);
33 v.catch(reject);
32 }
34 }
33
35
34 function complete() {
36 function complete() {
35 if (!--pending)
37 if (!--pending)
36 resolve();
38 resolve(_t2);
37 }
39 }
38 each(this._builders, (v, k) => {
40 each(this._builders, (v, k) => {
39 pending++;
41 pending++;
40 const d = new DescriptorBuilder<S, any>(target,
42 const d = new DescriptorBuilder<SC & S, any>(_t2,
41 result => {
43 result => {
42 target.register(k, result);
44 _t2.register(k, result);
43 complete();
45 complete();
44 },
46 },
45 reject
47 reject
46 );
48 );
47
49
48 try {
50 try {
49 guard(v(d, ct));
51 guard(v(d, ct));
50 } catch (e) {
52 } catch (e) {
51 reject(e);
53 reject(e);
52 }
54 }
53 });
55 });
54 complete();
56 complete();
55 });
57 });
56 }
58 }
57
59
58 }
60 }
@@ -1,26 +1,14
1 import { Services } from "./services";
1 import { Services } from "./services";
2 import { fluent } from "../di/traits";
2 import { fluent } from "../di/traits";
3 import { Box } from "./Box";
3
4
4 export default fluent<Services>().register({
5 export default fluent<Services>().register({
5 host: it => it.value("example.com"),
6 host: it => it.value("example.com"),
6
7
7 bar2: it => Promise.all([import("./Foo"), import("./Bar")])
8 foo: it => import("./Foo").then(({ Foo }) => it
8 .then(([{ Foo }, { Bar }]) => it
9 .factory(() => new Foo())
9 .lifetime("container")
10 ),
10 .override({
11
11 host: it2 => it2.value("simple.org"),
12 box: it => it
12 foo: it2 => it2.value(new Foo())
13 .factory($dependency => new Box($dependency("foo")))
13 })
14 .factory(resolve => {
15 const bar = new Bar({
16 foo: new Foo(),
17 nested: {
18 lazy: resolve("foo", { lazy: true })
19 },
20 host: resolve("host")
21 }, "some text");
22 bar.setName(resolve("host"));
23 return bar;
24 })
25 )
26 });
14 });
@@ -1,19 +1,15
1 import { Foo } from "./Foo";
1 import { Foo } from "./Foo";
2 import { Bar } from "./Bar";
2 import { Bar } from "./Bar";
3 import { Box } from "./Box";
3 import { Box } from "./Box";
4
4
5 /**
5 /**
6 * Сервисы доступные внутри контейнера
6 * Сервисы доступные внутри контейнера
7 */
7 */
8 export interface Services {
8 export interface Services {
9 foo: Foo;
9 foo: Foo;
10
10
11 bar: Bar;
11 box: Box<Foo>;
12
13 bar2: Bar;
14
15 box: Box<Bar>;
16
12
17 host: string;
13 host: string;
18
14
19 }
15 }
@@ -1,54 +1,63
1 import { test } from "./TestTraits";
1 import { test } from "./TestTraits";
2 import { fluent } from "../di/traits";
2 import { fluent } from "../di/traits";
3 import { Bar } from "../mock/Bar";
3 import { Bar } from "../mock/Bar";
4 import { Container } from "../di/Container";
4 import { Container } from "../di/Container";
5 import { Foo } from "../mock/Foo";
5 import { Foo } from "../mock/Foo";
6 import { Box } from "../mock/Box";
6 import { Box } from "../mock/Box";
7 import { delay } from "../safe";
7 import { delay } from "../safe";
8 import { Services } from "../mock/services";
8
9
9 test("Simple fluent config", async t => {
10 test("Simple fluent config", async t => {
10 const config = fluent<{ host: string; bar: Bar; foo: Foo }>()
11 const config = fluent<{ host: string; bar: Bar; foo: Foo }>()
11 .register({
12 .register({
12 host: it => it.value("example.com"),
13 host: it => it.value("example.com"),
13 bar: it => it.factory(resolve => new Bar({ host: resolve("host") }, "s-bar")),
14 bar: it => it.factory(resolve => new Bar({ host: resolve("host") }, "s-bar")),
14 foo: it => import("../mock/Foo").then(m => it.lifetime("container").factory(() => new m.Foo()))
15 foo: it => import("../mock/Foo").then(m => it.lifetime("container").factory(() => new m.Foo()))
15 });
16 });
16
17
17 const container = new Container<{ host: string; bar: Bar; foo: Foo; }>();
18 const c1 = new Container<{}>();
18 await config.apply(container);
19 const container = await config.apply(c1);
19
20
20 t.equal(container.resolve("host"), "example.com", "The value should be resolved");
21 t.equal(container.resolve("host"), "example.com", "The value should be resolved");
21 t.assert(container.resolve("bar"), "The service should de activated");
22 t.assert(container.resolve("bar"), "The service should de activated");
22 t.equal(container.resolve("foo"), container.resolve("foo"), "The service should be activated once");
23 t.equal(container.resolve("foo"), container.resolve("foo"), "The service should be activated once");
23 });
24 });
24
25
25 test("Nested async configuration", async t => {
26 test("Nested async configuration", async t => {
26 const container = await new Container<{
27 const container = await new Container<{
27 foo: Foo;
28 foo: Foo;
28 box: Box<Foo>
29 box: Box<Foo>
29 }>().fluent({
30 }>().fluent({
30 foo: it => delay(0).then(() => it.factory(() => new Foo())),
31 foo: it => delay(0).then(() => it.factory(() => new Foo())),
31 box: it => it.lifetime("context").factory($dependency => new Box($dependency("foo")))
32 box: it => it.lifetime("context").factory($dependency => new Box($dependency("foo")))
32 });
33 });
33
34
34 t.assert(container.resolve("box").getValue(), "The dependency should be set");
35 t.assert(container.resolve("box").getValue(), "The dependency should be set");
35 t.equals(container.resolve("box").getValue(), container.resolve("box").getValue(), "The service should be activated once")
36 t.equals(container.resolve("box").getValue(), container.resolve("box").getValue(), "The service should be activated once")
36 });
37 });
37
38
38 test("Bad fluent config", async t => {
39 test("Bad fluent config", async t => {
39 try {
40 try {
40 await new Container<{
41 await new Container<{
41 foo: Foo;
42 foo: Foo;
42 box: Box<Foo>
43 box: Box<Foo>
43 }>().fluent({
44 }>().fluent({
44 foo: it => delay(0).then(() => it.factory(() => new Foo())),
45 foo: it => delay(0).then(() => it.factory(() => new Foo())),
45 box: it => it.lifetime("context")
46 box: it => it.lifetime("context")
46 .override("foo", () => { throw new Error("bad override"); })
47 .override("foo", () => { throw new Error("bad override"); })
47 .factory($dependency => new Box($dependency("foo")))
48 .factory($dependency => new Box($dependency("foo")))
48 });
49 });
49 t.fail("Should throw");
50 t.fail("Should throw");
50 } catch (e) {
51 } catch (e) {
51 t.pass("The configuration should fail");
52 t.pass("The configuration should fail");
52 t.equal(e.message, "bad override", "the error should pass");
53 t.equal(e.message, "bad override", "the error should pass");
53 }
54 }
54 });
55 });
56
57 test("Load fluent config", async t => {
58 const container = new Container<Services>();
59
60 await container.configure("../mock/config", { contextRequire: require });
61
62 t.assert(container.resolve("host"), "Should resolve simple value");
63 });
@@ -1,7 +1,8
1 import "./ActivatableTests";
1 import "./ActivatableTests";
2 import "./TraceSourceTests";
2 import "./TraceSourceTests";
3 import "./CancellationTests";
3 import "./CancellationTests";
4 import "./ObservableTests";
4 import "./ObservableTests";
5 import "./ContainerTests";
5 import "./ContainerTests";
6 import "./SafeTests";
6 import "./SafeTests";
7 import "./TextTests";
7 import "./TextTests";
8 import "./FluentContainerTests";
General Comments 0
You need to be logged in to leave comments. Login now