@spinajs/di 2.0.180 → 2.0.182
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/array.d.ts +10 -10
- package/lib/cjs/array.js +13 -13
- package/lib/cjs/binder.d.ts +39 -39
- package/lib/cjs/binder.js +96 -96
- package/lib/cjs/container-cache.d.ts +17 -17
- package/lib/cjs/container-cache.js +63 -63
- package/lib/cjs/container.d.ts +133 -133
- package/lib/cjs/container.js +466 -466
- package/lib/cjs/container.js.map +1 -1
- package/lib/cjs/decorators.d.ts +141 -141
- package/lib/cjs/decorators.js +303 -303
- package/lib/cjs/enums.d.ts +31 -31
- package/lib/cjs/enums.js +35 -35
- package/lib/cjs/enums.js.map +1 -1
- package/lib/cjs/exceptions.d.ts +17 -17
- package/lib/cjs/exceptions.js +25 -25
- package/lib/cjs/helpers.d.ts +35 -35
- package/lib/cjs/helpers.js +86 -86
- package/lib/cjs/index.d.ts +12 -12
- package/lib/cjs/index.js +41 -41
- package/lib/cjs/interfaces.d.ts +172 -172
- package/lib/cjs/interfaces.js +53 -53
- package/lib/cjs/registry.d.ts +14 -14
- package/lib/cjs/registry.js +80 -80
- package/lib/cjs/root.d.ts +108 -108
- package/lib/cjs/root.js +216 -217
- package/lib/cjs/root.js.map +1 -1
- package/lib/cjs/types.d.ts +13 -13
- package/lib/cjs/types.js +2 -2
- package/lib/mjs/array.d.ts +10 -10
- package/lib/mjs/array.js +9 -9
- package/lib/mjs/binder.d.ts +39 -39
- package/lib/mjs/binder.js +92 -92
- package/lib/mjs/container-cache.d.ts +17 -17
- package/lib/mjs/container-cache.js +59 -59
- package/lib/mjs/container.d.ts +133 -133
- package/lib/mjs/container.js +459 -459
- package/lib/mjs/container.js.map +1 -1
- package/lib/mjs/decorators.d.ts +141 -141
- package/lib/mjs/decorators.js +266 -266
- package/lib/mjs/enums.d.ts +31 -31
- package/lib/mjs/enums.js +32 -32
- package/lib/mjs/enums.js.map +1 -1
- package/lib/mjs/exceptions.d.ts +17 -17
- package/lib/mjs/exceptions.js +19 -19
- package/lib/mjs/helpers.d.ts +35 -35
- package/lib/mjs/helpers.js +72 -72
- package/lib/mjs/index.d.ts +12 -12
- package/lib/mjs/index.js +12 -12
- package/lib/mjs/interfaces.d.ts +172 -172
- package/lib/mjs/interfaces.js +45 -45
- package/lib/mjs/registry.d.ts +14 -14
- package/lib/mjs/registry.js +76 -76
- package/lib/mjs/root.d.ts +108 -108
- package/lib/mjs/root.js +159 -159
- package/lib/mjs/types.d.ts +13 -13
- package/lib/mjs/types.js +1 -1
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +2 -2
package/lib/cjs/container.js
CHANGED
|
@@ -1,467 +1,467 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Container = void 0;
|
|
7
|
-
const exceptions_1 = require("@spinajs/exceptions");
|
|
8
|
-
require("reflect-metadata");
|
|
9
|
-
const decorators_js_1 = require("./decorators.js");
|
|
10
|
-
const enums_js_1 = require("./enums.js");
|
|
11
|
-
const helpers_js_1 = require("./helpers.js");
|
|
12
|
-
const interfaces_js_1 = require("./interfaces.js");
|
|
13
|
-
const events_1 = require("events");
|
|
14
|
-
const binder_js_1 = require("./binder.js");
|
|
15
|
-
const registry_js_1 = require("./registry.js");
|
|
16
|
-
const container_cache_js_1 = require("./container-cache.js");
|
|
17
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
18
|
-
const exceptions_js_1 = require("./exceptions.js");
|
|
19
|
-
/**
|
|
20
|
-
* Dependency injection container implementation
|
|
21
|
-
*/
|
|
22
|
-
class Container extends events_1.EventEmitter {
|
|
23
|
-
/**
|
|
24
|
-
* Returns container cache - map object with resolved classes as singletons
|
|
25
|
-
*/
|
|
26
|
-
get Cache() {
|
|
27
|
-
return this.cache;
|
|
28
|
-
}
|
|
29
|
-
get Registry() {
|
|
30
|
-
return this.registry;
|
|
31
|
-
}
|
|
32
|
-
get Parent() {
|
|
33
|
-
return this.parent;
|
|
34
|
-
}
|
|
35
|
-
constructor(parent) {
|
|
36
|
-
super();
|
|
37
|
-
this.registry = new registry_js_1.Registry(this);
|
|
38
|
-
this.cache = new container_cache_js_1.ContainerCache(this);
|
|
39
|
-
this.parent = parent || undefined;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Clears container registry and cache. shorthand for container.clearCache() && container.clearRegistry()
|
|
43
|
-
*/
|
|
44
|
-
clear() {
|
|
45
|
-
this.clearCache();
|
|
46
|
-
this.clearRegistry();
|
|
47
|
-
}
|
|
48
|
-
async dispose() {
|
|
49
|
-
for (const entry of this.cache) {
|
|
50
|
-
if (entry.value instanceof interfaces_js_1.Service) {
|
|
51
|
-
await entry.value.dispose();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
this.clearCache();
|
|
55
|
-
this.emit('di.dispose');
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* clears container registered types information
|
|
59
|
-
*/
|
|
60
|
-
clearCache() {
|
|
61
|
-
this.cache.clear();
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Clears container resolved types
|
|
65
|
-
*/
|
|
66
|
-
clearRegistry() {
|
|
67
|
-
this.Registry.clear();
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Register class/interface to DI.
|
|
71
|
-
* @param type - interface object to register
|
|
72
|
-
* @throws {@link InvalidArgument} if type is null or undefined
|
|
73
|
-
*/
|
|
74
|
-
register(implementation) {
|
|
75
|
-
if (!implementation) {
|
|
76
|
-
throw new exceptions_1.InvalidArgument('argument `type` cannot be null or undefined');
|
|
77
|
-
}
|
|
78
|
-
return new binder_js_1.Binder(implementation, this);
|
|
79
|
-
}
|
|
80
|
-
unregister(implementation) {
|
|
81
|
-
if (!implementation) {
|
|
82
|
-
throw new exceptions_1.InvalidArgument('argument `type` cannot be null or undefined');
|
|
83
|
-
}
|
|
84
|
-
this.Registry.unregister(implementation);
|
|
85
|
-
}
|
|
86
|
-
uncache(implementation, parent) {
|
|
87
|
-
this.Cache.remove(implementation, parent);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Creates child DI container.
|
|
91
|
-
*
|
|
92
|
-
*/
|
|
93
|
-
child() {
|
|
94
|
-
return new Container(this);
|
|
95
|
-
}
|
|
96
|
-
get(service, parent = true) {
|
|
97
|
-
// get value registered as TypedArray ( mean to return all created instances )
|
|
98
|
-
if (service instanceof Array && service.constructor.name === 'TypedArray') {
|
|
99
|
-
return this.cache.get((0, helpers_js_1.getTypeName)(service.Type));
|
|
100
|
-
}
|
|
101
|
-
const r = this.cache.get(service, parent);
|
|
102
|
-
return r[r.length - 1];
|
|
103
|
-
}
|
|
104
|
-
hasRegistered(service, parent = true) {
|
|
105
|
-
return this.Registry.hasRegistered(service, parent);
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Checks if service is already resolved and exists in container cache.
|
|
109
|
-
* NOTE: check is only valid for classes that are singletons.
|
|
110
|
-
*
|
|
111
|
-
* @param service - service name or class to check
|
|
112
|
-
* @returns true if service instance already exists, otherwise false.
|
|
113
|
-
* @throws {@link InvalidArgument} when service is null or empty
|
|
114
|
-
*/
|
|
115
|
-
isResolved(service, parent = true) {
|
|
116
|
-
return this.Cache.has(service, parent);
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
*
|
|
120
|
-
* @param type - type to resolve
|
|
121
|
-
* @param options - options passed to constructor / factory
|
|
122
|
-
* @param check - strict check if serivice is registered in container before resolving. Default behavior is to not check and resolve
|
|
123
|
-
*/
|
|
124
|
-
resolve(type, options, check, tType) {
|
|
125
|
-
if (!type) {
|
|
126
|
-
throw new exceptions_1.InvalidArgument('argument `type` cannot be null or undefined');
|
|
127
|
-
}
|
|
128
|
-
// UGLY HACK ?
|
|
129
|
-
// on electron instanceof TypedArray not working ?
|
|
130
|
-
const sourceType = type instanceof Array && type.constructor.name === 'TypedArray' ? type.Type : type;
|
|
131
|
-
const sourceName = (0, helpers_js_1.getTypeName)(type);
|
|
132
|
-
const opt = typeof options === 'boolean' ? null : options;
|
|
133
|
-
if (options === true || check === true) {
|
|
134
|
-
if (!this.hasRegistered(sourceType)) {
|
|
135
|
-
throw new Error(`Type ${sourceName} is not registered at container`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if ((0, helpers_js_1.isTypedArray)(type)) {
|
|
139
|
-
// special case for arrays
|
|
140
|
-
// if we have in cache, retunr all we got
|
|
141
|
-
// TODO: fix this and every time check if theres is any
|
|
142
|
-
// new registerd type
|
|
143
|
-
if (this.Cache.has(type)) {
|
|
144
|
-
return this.Cache.get(type);
|
|
145
|
-
}
|
|
146
|
-
// if its array type, resolve all registered types or throw exception
|
|
147
|
-
const targetType = this.getRegisteredTypes(type);
|
|
148
|
-
if (!targetType) {
|
|
149
|
-
return [];
|
|
150
|
-
}
|
|
151
|
-
const resolved = targetType.map((r) => this.resolveType(type, r, opt));
|
|
152
|
-
if (resolved.some((r) => r instanceof Promise)) {
|
|
153
|
-
return Promise.all(resolved);
|
|
154
|
-
}
|
|
155
|
-
return resolved;
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
// finaly resolve single type:
|
|
159
|
-
// 1. last registered type OR
|
|
160
|
-
// 2. if non is registered - type itself
|
|
161
|
-
let targetType = this.getRegisteredTypes(type);
|
|
162
|
-
if (!targetType) {
|
|
163
|
-
// if nothing is register under string identifier, then return null
|
|
164
|
-
if (typeof type === 'string') {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
targetType = [type];
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// if we have target function callback
|
|
172
|
-
// we can select whitch of targetType to resolve
|
|
173
|
-
//
|
|
174
|
-
// if not, by default last registered type is resolved
|
|
175
|
-
const fType = tType ?? targetType[targetType.length - 1];
|
|
176
|
-
const rValue = this.resolveType(sourceType, fType, opt);
|
|
177
|
-
return rValue;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
getRegisteredTypes(service, parent) {
|
|
181
|
-
return this.Registry.getTypes(service, parent);
|
|
182
|
-
}
|
|
183
|
-
resolveType(sourceType, targetType, options) {
|
|
184
|
-
/**
|
|
185
|
-
* If its a factory func, always resolve as new instance
|
|
186
|
-
*/
|
|
187
|
-
if ((0, helpers_js_1.isFactory)(targetType)) {
|
|
188
|
-
return this.getNewInstance(targetType, null, options);
|
|
189
|
-
}
|
|
190
|
-
// we now know its not factory func
|
|
191
|
-
// but typescript complains about this
|
|
192
|
-
// becouse isFactory is custom type check
|
|
193
|
-
const tType = targetType;
|
|
194
|
-
const sName = (0, helpers_js_1.getTypeName)(sourceType);
|
|
195
|
-
const descriptor = this.extractDescriptor(tType);
|
|
196
|
-
// check if is singleton,
|
|
197
|
-
// resolving strategy per container is treatead as singleton
|
|
198
|
-
// in this particular container
|
|
199
|
-
const isSingletonInChild = descriptor.resolver === enums_js_1.ResolveType.PerChildContainer;
|
|
200
|
-
const isSingleton = descriptor.resolver === enums_js_1.ResolveType.Singleton;
|
|
201
|
-
const setCache = (target) => {
|
|
202
|
-
this.Cache.add(sourceType, target);
|
|
203
|
-
return target;
|
|
204
|
-
};
|
|
205
|
-
const emit = (target) => {
|
|
206
|
-
const sourceTypeName = (0, helpers_js_1.getTypeName)(sourceType);
|
|
207
|
-
const targetTypeName = (0, helpers_js_1.getTypeName)(targetType);
|
|
208
|
-
// firs event to emit that particular type was resolved
|
|
209
|
-
this.emit(`di.resolved.${targetTypeName}`, this, target);
|
|
210
|
-
// emit that source type was resolved
|
|
211
|
-
if (targetTypeName !== sourceTypeName) {
|
|
212
|
-
this.emit(`di.resolved.${sourceTypeName}`, this, target);
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
const getCachedInstance = (e, parent) => {
|
|
216
|
-
if (this.isResolved(e, parent)) {
|
|
217
|
-
const rArray = this.get(e, parent);
|
|
218
|
-
return lodash_1.default.isArray(rArray) ? rArray.find((x) => (0, helpers_js_1.getTypeName)(x) === (0, helpers_js_1.getTypeName)(targetType)) : rArray;
|
|
219
|
-
}
|
|
220
|
-
return null;
|
|
221
|
-
};
|
|
222
|
-
const getCachedInstances = (e, parent) => {
|
|
223
|
-
if (this.isResolved(e, parent)) {
|
|
224
|
-
const rArray = this.get(e, parent);
|
|
225
|
-
return lodash_1.default.isArray(rArray) ? rArray : [rArray];
|
|
226
|
-
}
|
|
227
|
-
return null;
|
|
228
|
-
};
|
|
229
|
-
const createNewInstance = (t, i, options) => {
|
|
230
|
-
const instance = this.getNewInstance(t, i, options);
|
|
231
|
-
if ((0, helpers_js_1.isPromise)(instance)) {
|
|
232
|
-
return instance.then((r) => {
|
|
233
|
-
setCache(r);
|
|
234
|
-
emit(r);
|
|
235
|
-
return r;
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
setCache(instance);
|
|
240
|
-
emit(instance);
|
|
241
|
-
return instance;
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
const resolve = (d, t, i) => {
|
|
245
|
-
if (d.resolver === enums_js_1.ResolveType.NewInstance) {
|
|
246
|
-
const instance = this.getNewInstance(t, i, options);
|
|
247
|
-
if ((0, helpers_js_1.isPromise)(instance)) {
|
|
248
|
-
return instance.then((r) => {
|
|
249
|
-
emit(r);
|
|
250
|
-
return r;
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
emit(instance);
|
|
255
|
-
return instance;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (d.resolver === enums_js_1.ResolveType.PerInstanceCheck) {
|
|
259
|
-
const cashed = getCachedInstances(tType, true);
|
|
260
|
-
if (cashed) {
|
|
261
|
-
const found = cashed.find((x) => {
|
|
262
|
-
if (!x.__checkInstance__) {
|
|
263
|
-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
264
|
-
throw new exceptions_js_1.ResolveException(`service ${x.constructor.name} is marked as PerInstanceCheck resolver, but no __checkInstance__ function is provided`);
|
|
265
|
-
}
|
|
266
|
-
return x.__checkInstance__(options);
|
|
267
|
-
});
|
|
268
|
-
if (found) {
|
|
269
|
-
return found;
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
return createNewInstance(t, i, options);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
this.Registry.register(sName, t);
|
|
277
|
-
const cashed = getCachedInstance(tType, d.resolver === enums_js_1.ResolveType.Singleton ? true : false);
|
|
278
|
-
if (!cashed) {
|
|
279
|
-
return createNewInstance(t, i, options);
|
|
280
|
-
}
|
|
281
|
-
else {
|
|
282
|
-
return cashed;
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
// check cache if needed
|
|
286
|
-
if (isSingletonInChild || isSingleton) {
|
|
287
|
-
// if its singleton ( not per child container )
|
|
288
|
-
// check also in parent containers
|
|
289
|
-
// ------- IMPORTANT ------------
|
|
290
|
-
// TODO: in future allow to check in runtime if target type is cashed,
|
|
291
|
-
// now, if for example we resolve array of some type,
|
|
292
|
-
// when we later register another type of base class used in typed array
|
|
293
|
-
// we will not resolve it, becaouse contaienr will not check
|
|
294
|
-
// if in cache this new type exists ( only check if type in array exists )
|
|
295
|
-
const cached = getCachedInstance(sourceType, isSingleton);
|
|
296
|
-
if (cached) {
|
|
297
|
-
return cached;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
const deps = this.resolveDependencies(descriptor.inject);
|
|
301
|
-
if (deps instanceof Promise) {
|
|
302
|
-
return deps.then((resolvedDependencies) => {
|
|
303
|
-
return resolve(descriptor, tType, resolvedDependencies);
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
const resInstance = resolve(descriptor, tType, deps);
|
|
308
|
-
if (resInstance instanceof Promise) {
|
|
309
|
-
return resInstance;
|
|
310
|
-
}
|
|
311
|
-
return resInstance;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
getNewInstance(typeToCreate, a, options) {
|
|
315
|
-
let args = [null];
|
|
316
|
-
let newInstance = null;
|
|
317
|
-
/**
|
|
318
|
-
* If type is not Constructable, we assume its factory function,
|
|
319
|
-
* just call it with `this` container.
|
|
320
|
-
*/
|
|
321
|
-
if ((0, helpers_js_1.isFactory)(typeToCreate)) {
|
|
322
|
-
newInstance = typeToCreate(this, ...(options ?? []));
|
|
323
|
-
}
|
|
324
|
-
else {
|
|
325
|
-
if (a.constructor.name === 'Array') {
|
|
326
|
-
args = args.concat(a.filter((i) => !i.autoinject).map((i) => i.instance));
|
|
327
|
-
}
|
|
328
|
-
if (options && options.length !== 0) {
|
|
329
|
-
args = args.concat(options);
|
|
330
|
-
}
|
|
331
|
-
/* eslint-disable */
|
|
332
|
-
newInstance = new (Function.prototype.bind.apply(typeToCreate, args))();
|
|
333
|
-
for (const ai of a.filter((i) => i.autoinject)) {
|
|
334
|
-
// TYPE HACK to tell typescript we dont care type
|
|
335
|
-
/* eslint-disable */
|
|
336
|
-
newInstance[`${ai.autoinjectKey}`] = ai.instance;
|
|
337
|
-
}
|
|
338
|
-
if ((0, helpers_js_1.isAsyncService)(newInstance)) {
|
|
339
|
-
return new Promise((res, rej) => {
|
|
340
|
-
newInstance
|
|
341
|
-
.resolve()
|
|
342
|
-
.then(() => {
|
|
343
|
-
res(newInstance);
|
|
344
|
-
})
|
|
345
|
-
.catch((err) => rej(err));
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
if (newInstance instanceof interfaces_js_1.SyncService) {
|
|
350
|
-
newInstance.resolve();
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return newInstance;
|
|
355
|
-
}
|
|
356
|
-
hasRegisteredType(source, type, parent) {
|
|
357
|
-
return this.Registry.hasRegisteredType(source, type, parent);
|
|
358
|
-
}
|
|
359
|
-
resolveDependencies(toInject) {
|
|
360
|
-
const dependencies = toInject.map((t) => {
|
|
361
|
-
let tInject = null;
|
|
362
|
-
// if we have service func, retrieve target
|
|
363
|
-
// we can have multiple implementation of same interface
|
|
364
|
-
// and service can request to inject specific one
|
|
365
|
-
// ( not just last one registered )
|
|
366
|
-
// if serviceFunc returns array,
|
|
367
|
-
// all services will be resolved and mapped
|
|
368
|
-
if (t.serviceFunc) {
|
|
369
|
-
const services = t.serviceFunc(t.data, this);
|
|
370
|
-
const types = this.getRegisteredTypes(t.inject);
|
|
371
|
-
if (!types || types.length === 0) {
|
|
372
|
-
throw new exceptions_js_1.ServiceNotFound(`Service ${t.inject.name} is not registered in DI container`);
|
|
373
|
-
}
|
|
374
|
-
if (lodash_1.default.isArray(services)) {
|
|
375
|
-
tInject = services.map((x) => {
|
|
376
|
-
return {
|
|
377
|
-
type: types.find((t) => t.name === x.service),
|
|
378
|
-
options: x.options,
|
|
379
|
-
};
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
else {
|
|
383
|
-
tInject = {
|
|
384
|
-
type: types.find((t) => t.name === services.service),
|
|
385
|
-
options: services.options,
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
let promiseOrVal = null;
|
|
390
|
-
if (lodash_1.default.isArray(tInject)) {
|
|
391
|
-
const pVals = tInject.map((x) => this.resolve(x.type, [t.options ?? x.options], false, x.type));
|
|
392
|
-
if (pVals.some((x) => (0, helpers_js_1.isPromise)(x))) {
|
|
393
|
-
promiseOrVal = Promise.all(pVals);
|
|
394
|
-
}
|
|
395
|
-
else {
|
|
396
|
-
promiseOrVal = pVals;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
promiseOrVal = this.resolve(tInject?.type ?? t.inject, [t.options ?? tInject?.options], false, tInject?.type);
|
|
401
|
-
}
|
|
402
|
-
if (promiseOrVal instanceof Promise) {
|
|
403
|
-
return promiseOrVal.then((val) => {
|
|
404
|
-
return {
|
|
405
|
-
autoinject: t.autoinject,
|
|
406
|
-
autoinjectKey: t.autoinjectKey,
|
|
407
|
-
instance: valOrMap(val, t),
|
|
408
|
-
};
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
return {
|
|
412
|
-
autoinject: t.autoinject,
|
|
413
|
-
autoinjectKey: t.autoinjectKey,
|
|
414
|
-
instance: valOrMap(promiseOrVal, t),
|
|
415
|
-
};
|
|
416
|
-
});
|
|
417
|
-
if (dependencies.some((p) => p instanceof Promise)) {
|
|
418
|
-
return Promise.all(dependencies);
|
|
419
|
-
}
|
|
420
|
-
return dependencies;
|
|
421
|
-
function valOrMap(val, t) {
|
|
422
|
-
let instance = val;
|
|
423
|
-
if (lodash_1.default.isArray(val) && t.mapFunc) {
|
|
424
|
-
instance = new Map();
|
|
425
|
-
for (const i of val) {
|
|
426
|
-
instance.set(t.mapFunc(i), i);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return instance;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
extractDescriptor(type) {
|
|
433
|
-
const descriptor = {
|
|
434
|
-
inject: [],
|
|
435
|
-
resolver: enums_js_1.ResolveType.Singleton,
|
|
436
|
-
};
|
|
437
|
-
const rootMeta = Reflect.getMetadata(decorators_js_1.DI_DESCRIPTION_SYMBOL, type);
|
|
438
|
-
if (rootMeta) {
|
|
439
|
-
descriptor.resolver = rootMeta.resolver;
|
|
440
|
-
}
|
|
441
|
-
function geAllTypes(clz) {
|
|
442
|
-
if (!clz)
|
|
443
|
-
return undefined;
|
|
444
|
-
const toInject = Reflect.getMetadata(decorators_js_1.DI_DESCRIPTION_SYMBOL, clz);
|
|
445
|
-
if (toInject) {
|
|
446
|
-
toInject.inject.forEach((x) => {
|
|
447
|
-
const xTypeName = (0, helpers_js_1.getTypeName)(x.inject);
|
|
448
|
-
// if we do it by autoinject, skip filtering injection props
|
|
449
|
-
// autoinject can have multiple fields of same type and its identified by prop key
|
|
450
|
-
// we cannot override injection props in derived class
|
|
451
|
-
if (x.autoinject === true) {
|
|
452
|
-
descriptor.inject.push(x);
|
|
453
|
-
}
|
|
454
|
-
else if (descriptor.inject.find(i => (0, helpers_js_1.getTypeName)(i.inject) === xTypeName) === undefined) {
|
|
455
|
-
descriptor.inject.push(x);
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
// get `__proto__` and (recursively) all parent classes
|
|
460
|
-
geAllTypes(Object.getPrototypeOf(clz));
|
|
461
|
-
}
|
|
462
|
-
geAllTypes(type);
|
|
463
|
-
return descriptor;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
exports.Container = Container;
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Container = void 0;
|
|
7
|
+
const exceptions_1 = require("@spinajs/exceptions");
|
|
8
|
+
require("reflect-metadata");
|
|
9
|
+
const decorators_js_1 = require("./decorators.js");
|
|
10
|
+
const enums_js_1 = require("./enums.js");
|
|
11
|
+
const helpers_js_1 = require("./helpers.js");
|
|
12
|
+
const interfaces_js_1 = require("./interfaces.js");
|
|
13
|
+
const events_1 = require("events");
|
|
14
|
+
const binder_js_1 = require("./binder.js");
|
|
15
|
+
const registry_js_1 = require("./registry.js");
|
|
16
|
+
const container_cache_js_1 = require("./container-cache.js");
|
|
17
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
18
|
+
const exceptions_js_1 = require("./exceptions.js");
|
|
19
|
+
/**
|
|
20
|
+
* Dependency injection container implementation
|
|
21
|
+
*/
|
|
22
|
+
class Container extends events_1.EventEmitter {
|
|
23
|
+
/**
|
|
24
|
+
* Returns container cache - map object with resolved classes as singletons
|
|
25
|
+
*/
|
|
26
|
+
get Cache() {
|
|
27
|
+
return this.cache;
|
|
28
|
+
}
|
|
29
|
+
get Registry() {
|
|
30
|
+
return this.registry;
|
|
31
|
+
}
|
|
32
|
+
get Parent() {
|
|
33
|
+
return this.parent;
|
|
34
|
+
}
|
|
35
|
+
constructor(parent) {
|
|
36
|
+
super();
|
|
37
|
+
this.registry = new registry_js_1.Registry(this);
|
|
38
|
+
this.cache = new container_cache_js_1.ContainerCache(this);
|
|
39
|
+
this.parent = parent || undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Clears container registry and cache. shorthand for container.clearCache() && container.clearRegistry()
|
|
43
|
+
*/
|
|
44
|
+
clear() {
|
|
45
|
+
this.clearCache();
|
|
46
|
+
this.clearRegistry();
|
|
47
|
+
}
|
|
48
|
+
async dispose() {
|
|
49
|
+
for (const entry of this.cache) {
|
|
50
|
+
if (entry.value instanceof interfaces_js_1.Service) {
|
|
51
|
+
await entry.value.dispose();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
this.clearCache();
|
|
55
|
+
this.emit('di.dispose');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* clears container registered types information
|
|
59
|
+
*/
|
|
60
|
+
clearCache() {
|
|
61
|
+
this.cache.clear();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Clears container resolved types
|
|
65
|
+
*/
|
|
66
|
+
clearRegistry() {
|
|
67
|
+
this.Registry.clear();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Register class/interface to DI.
|
|
71
|
+
* @param type - interface object to register
|
|
72
|
+
* @throws {@link InvalidArgument} if type is null or undefined
|
|
73
|
+
*/
|
|
74
|
+
register(implementation) {
|
|
75
|
+
if (!implementation) {
|
|
76
|
+
throw new exceptions_1.InvalidArgument('argument `type` cannot be null or undefined');
|
|
77
|
+
}
|
|
78
|
+
return new binder_js_1.Binder(implementation, this);
|
|
79
|
+
}
|
|
80
|
+
unregister(implementation) {
|
|
81
|
+
if (!implementation) {
|
|
82
|
+
throw new exceptions_1.InvalidArgument('argument `type` cannot be null or undefined');
|
|
83
|
+
}
|
|
84
|
+
this.Registry.unregister(implementation);
|
|
85
|
+
}
|
|
86
|
+
uncache(implementation, parent) {
|
|
87
|
+
this.Cache.remove(implementation, parent);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Creates child DI container.
|
|
91
|
+
*
|
|
92
|
+
*/
|
|
93
|
+
child() {
|
|
94
|
+
return new Container(this);
|
|
95
|
+
}
|
|
96
|
+
get(service, parent = true) {
|
|
97
|
+
// get value registered as TypedArray ( mean to return all created instances )
|
|
98
|
+
if (service instanceof Array && service.constructor.name === 'TypedArray') {
|
|
99
|
+
return this.cache.get((0, helpers_js_1.getTypeName)(service.Type));
|
|
100
|
+
}
|
|
101
|
+
const r = this.cache.get(service, parent);
|
|
102
|
+
return r[r.length - 1];
|
|
103
|
+
}
|
|
104
|
+
hasRegistered(service, parent = true) {
|
|
105
|
+
return this.Registry.hasRegistered(service, parent);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Checks if service is already resolved and exists in container cache.
|
|
109
|
+
* NOTE: check is only valid for classes that are singletons.
|
|
110
|
+
*
|
|
111
|
+
* @param service - service name or class to check
|
|
112
|
+
* @returns true if service instance already exists, otherwise false.
|
|
113
|
+
* @throws {@link InvalidArgument} when service is null or empty
|
|
114
|
+
*/
|
|
115
|
+
isResolved(service, parent = true) {
|
|
116
|
+
return this.Cache.has(service, parent);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
*
|
|
120
|
+
* @param type - type to resolve
|
|
121
|
+
* @param options - options passed to constructor / factory
|
|
122
|
+
* @param check - strict check if serivice is registered in container before resolving. Default behavior is to not check and resolve
|
|
123
|
+
*/
|
|
124
|
+
resolve(type, options, check, tType) {
|
|
125
|
+
if (!type) {
|
|
126
|
+
throw new exceptions_1.InvalidArgument('argument `type` cannot be null or undefined');
|
|
127
|
+
}
|
|
128
|
+
// UGLY HACK ?
|
|
129
|
+
// on electron instanceof TypedArray not working ?
|
|
130
|
+
const sourceType = type instanceof Array && type.constructor.name === 'TypedArray' ? type.Type : type;
|
|
131
|
+
const sourceName = (0, helpers_js_1.getTypeName)(type);
|
|
132
|
+
const opt = typeof options === 'boolean' ? null : options;
|
|
133
|
+
if (options === true || check === true) {
|
|
134
|
+
if (!this.hasRegistered(sourceType)) {
|
|
135
|
+
throw new Error(`Type ${sourceName} is not registered at container`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if ((0, helpers_js_1.isTypedArray)(type)) {
|
|
139
|
+
// special case for arrays
|
|
140
|
+
// if we have in cache, retunr all we got
|
|
141
|
+
// TODO: fix this and every time check if theres is any
|
|
142
|
+
// new registerd type
|
|
143
|
+
if (this.Cache.has(type)) {
|
|
144
|
+
return this.Cache.get(type);
|
|
145
|
+
}
|
|
146
|
+
// if its array type, resolve all registered types or throw exception
|
|
147
|
+
const targetType = this.getRegisteredTypes(type);
|
|
148
|
+
if (!targetType) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
const resolved = targetType.map((r) => this.resolveType(type, r, opt));
|
|
152
|
+
if (resolved.some((r) => r instanceof Promise)) {
|
|
153
|
+
return Promise.all(resolved);
|
|
154
|
+
}
|
|
155
|
+
return resolved;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// finaly resolve single type:
|
|
159
|
+
// 1. last registered type OR
|
|
160
|
+
// 2. if non is registered - type itself
|
|
161
|
+
let targetType = this.getRegisteredTypes(type);
|
|
162
|
+
if (!targetType) {
|
|
163
|
+
// if nothing is register under string identifier, then return null
|
|
164
|
+
if (typeof type === 'string') {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
targetType = [type];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// if we have target function callback
|
|
172
|
+
// we can select whitch of targetType to resolve
|
|
173
|
+
//
|
|
174
|
+
// if not, by default last registered type is resolved
|
|
175
|
+
const fType = tType ?? targetType[targetType.length - 1];
|
|
176
|
+
const rValue = this.resolveType(sourceType, fType, opt);
|
|
177
|
+
return rValue;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
getRegisteredTypes(service, parent) {
|
|
181
|
+
return this.Registry.getTypes(service, parent);
|
|
182
|
+
}
|
|
183
|
+
resolveType(sourceType, targetType, options) {
|
|
184
|
+
/**
|
|
185
|
+
* If its a factory func, always resolve as new instance
|
|
186
|
+
*/
|
|
187
|
+
if ((0, helpers_js_1.isFactory)(targetType)) {
|
|
188
|
+
return this.getNewInstance(targetType, null, options);
|
|
189
|
+
}
|
|
190
|
+
// we now know its not factory func
|
|
191
|
+
// but typescript complains about this
|
|
192
|
+
// becouse isFactory is custom type check
|
|
193
|
+
const tType = targetType;
|
|
194
|
+
const sName = (0, helpers_js_1.getTypeName)(sourceType);
|
|
195
|
+
const descriptor = this.extractDescriptor(tType);
|
|
196
|
+
// check if is singleton,
|
|
197
|
+
// resolving strategy per container is treatead as singleton
|
|
198
|
+
// in this particular container
|
|
199
|
+
const isSingletonInChild = descriptor.resolver === enums_js_1.ResolveType.PerChildContainer;
|
|
200
|
+
const isSingleton = descriptor.resolver === enums_js_1.ResolveType.Singleton;
|
|
201
|
+
const setCache = (target) => {
|
|
202
|
+
this.Cache.add(sourceType, target);
|
|
203
|
+
return target;
|
|
204
|
+
};
|
|
205
|
+
const emit = (target) => {
|
|
206
|
+
const sourceTypeName = (0, helpers_js_1.getTypeName)(sourceType);
|
|
207
|
+
const targetTypeName = (0, helpers_js_1.getTypeName)(targetType);
|
|
208
|
+
// firs event to emit that particular type was resolved
|
|
209
|
+
this.emit(`di.resolved.${targetTypeName}`, this, target);
|
|
210
|
+
// emit that source type was resolved
|
|
211
|
+
if (targetTypeName !== sourceTypeName) {
|
|
212
|
+
this.emit(`di.resolved.${sourceTypeName}`, this, target);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const getCachedInstance = (e, parent) => {
|
|
216
|
+
if (this.isResolved(e, parent)) {
|
|
217
|
+
const rArray = this.get(e, parent);
|
|
218
|
+
return lodash_1.default.isArray(rArray) ? rArray.find((x) => (0, helpers_js_1.getTypeName)(x) === (0, helpers_js_1.getTypeName)(targetType)) : rArray;
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
};
|
|
222
|
+
const getCachedInstances = (e, parent) => {
|
|
223
|
+
if (this.isResolved(e, parent)) {
|
|
224
|
+
const rArray = this.get(e, parent);
|
|
225
|
+
return lodash_1.default.isArray(rArray) ? rArray : [rArray];
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
};
|
|
229
|
+
const createNewInstance = (t, i, options) => {
|
|
230
|
+
const instance = this.getNewInstance(t, i, options);
|
|
231
|
+
if ((0, helpers_js_1.isPromise)(instance)) {
|
|
232
|
+
return instance.then((r) => {
|
|
233
|
+
setCache(r);
|
|
234
|
+
emit(r);
|
|
235
|
+
return r;
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
setCache(instance);
|
|
240
|
+
emit(instance);
|
|
241
|
+
return instance;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const resolve = (d, t, i) => {
|
|
245
|
+
if (d.resolver === enums_js_1.ResolveType.NewInstance) {
|
|
246
|
+
const instance = this.getNewInstance(t, i, options);
|
|
247
|
+
if ((0, helpers_js_1.isPromise)(instance)) {
|
|
248
|
+
return instance.then((r) => {
|
|
249
|
+
emit(r);
|
|
250
|
+
return r;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
emit(instance);
|
|
255
|
+
return instance;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (d.resolver === enums_js_1.ResolveType.PerInstanceCheck) {
|
|
259
|
+
const cashed = getCachedInstances(tType, true);
|
|
260
|
+
if (cashed) {
|
|
261
|
+
const found = cashed.find((x) => {
|
|
262
|
+
if (!x.__checkInstance__) {
|
|
263
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
264
|
+
throw new exceptions_js_1.ResolveException(`service ${x.constructor.name} is marked as PerInstanceCheck resolver, but no __checkInstance__ function is provided`);
|
|
265
|
+
}
|
|
266
|
+
return x.__checkInstance__(options);
|
|
267
|
+
});
|
|
268
|
+
if (found) {
|
|
269
|
+
return found;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
return createNewInstance(t, i, options);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
this.Registry.register(sName, t);
|
|
277
|
+
const cashed = getCachedInstance(tType, d.resolver === enums_js_1.ResolveType.Singleton ? true : false);
|
|
278
|
+
if (!cashed) {
|
|
279
|
+
return createNewInstance(t, i, options);
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
return cashed;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
// check cache if needed
|
|
286
|
+
if (isSingletonInChild || isSingleton) {
|
|
287
|
+
// if its singleton ( not per child container )
|
|
288
|
+
// check also in parent containers
|
|
289
|
+
// ------- IMPORTANT ------------
|
|
290
|
+
// TODO: in future allow to check in runtime if target type is cashed,
|
|
291
|
+
// now, if for example we resolve array of some type,
|
|
292
|
+
// when we later register another type of base class used in typed array
|
|
293
|
+
// we will not resolve it, becaouse contaienr will not check
|
|
294
|
+
// if in cache this new type exists ( only check if type in array exists )
|
|
295
|
+
const cached = getCachedInstance(sourceType, isSingleton);
|
|
296
|
+
if (cached) {
|
|
297
|
+
return cached;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const deps = this.resolveDependencies(descriptor.inject);
|
|
301
|
+
if (deps instanceof Promise) {
|
|
302
|
+
return deps.then((resolvedDependencies) => {
|
|
303
|
+
return resolve(descriptor, tType, resolvedDependencies);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
const resInstance = resolve(descriptor, tType, deps);
|
|
308
|
+
if (resInstance instanceof Promise) {
|
|
309
|
+
return resInstance;
|
|
310
|
+
}
|
|
311
|
+
return resInstance;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
getNewInstance(typeToCreate, a, options) {
|
|
315
|
+
let args = [null];
|
|
316
|
+
let newInstance = null;
|
|
317
|
+
/**
|
|
318
|
+
* If type is not Constructable, we assume its factory function,
|
|
319
|
+
* just call it with `this` container.
|
|
320
|
+
*/
|
|
321
|
+
if ((0, helpers_js_1.isFactory)(typeToCreate)) {
|
|
322
|
+
newInstance = typeToCreate(this, ...(options ?? []));
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
if (a.constructor.name === 'Array') {
|
|
326
|
+
args = args.concat(a.filter((i) => !i.autoinject).map((i) => i.instance));
|
|
327
|
+
}
|
|
328
|
+
if (options && options.length !== 0) {
|
|
329
|
+
args = args.concat(options);
|
|
330
|
+
}
|
|
331
|
+
/* eslint-disable */
|
|
332
|
+
newInstance = new (Function.prototype.bind.apply(typeToCreate, args))();
|
|
333
|
+
for (const ai of a.filter((i) => i.autoinject)) {
|
|
334
|
+
// TYPE HACK to tell typescript we dont care type
|
|
335
|
+
/* eslint-disable */
|
|
336
|
+
newInstance[`${ai.autoinjectKey}`] = ai.instance;
|
|
337
|
+
}
|
|
338
|
+
if ((0, helpers_js_1.isAsyncService)(newInstance)) {
|
|
339
|
+
return new Promise((res, rej) => {
|
|
340
|
+
newInstance
|
|
341
|
+
.resolve()
|
|
342
|
+
.then(() => {
|
|
343
|
+
res(newInstance);
|
|
344
|
+
})
|
|
345
|
+
.catch((err) => rej(err));
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
if (newInstance instanceof interfaces_js_1.SyncService) {
|
|
350
|
+
newInstance.resolve();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return newInstance;
|
|
355
|
+
}
|
|
356
|
+
hasRegisteredType(source, type, parent) {
|
|
357
|
+
return this.Registry.hasRegisteredType(source, type, parent);
|
|
358
|
+
}
|
|
359
|
+
resolveDependencies(toInject) {
|
|
360
|
+
const dependencies = toInject.map((t) => {
|
|
361
|
+
let tInject = null;
|
|
362
|
+
// if we have service func, retrieve target
|
|
363
|
+
// we can have multiple implementation of same interface
|
|
364
|
+
// and service can request to inject specific one
|
|
365
|
+
// ( not just last one registered )
|
|
366
|
+
// if serviceFunc returns array,
|
|
367
|
+
// all services will be resolved and mapped
|
|
368
|
+
if (t.serviceFunc) {
|
|
369
|
+
const services = t.serviceFunc(t.data, this);
|
|
370
|
+
const types = this.getRegisteredTypes(t.inject);
|
|
371
|
+
if (!types || types.length === 0) {
|
|
372
|
+
throw new exceptions_js_1.ServiceNotFound(`Service ${t.inject.name} is not registered in DI container`);
|
|
373
|
+
}
|
|
374
|
+
if (lodash_1.default.isArray(services)) {
|
|
375
|
+
tInject = services.map((x) => {
|
|
376
|
+
return {
|
|
377
|
+
type: types.find((t) => t.name === x.service),
|
|
378
|
+
options: x.options,
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
tInject = {
|
|
384
|
+
type: types.find((t) => t.name === services.service),
|
|
385
|
+
options: services.options,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
let promiseOrVal = null;
|
|
390
|
+
if (lodash_1.default.isArray(tInject)) {
|
|
391
|
+
const pVals = tInject.map((x) => this.resolve(x.type, [t.options ?? x.options], false, x.type));
|
|
392
|
+
if (pVals.some((x) => (0, helpers_js_1.isPromise)(x))) {
|
|
393
|
+
promiseOrVal = Promise.all(pVals);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
promiseOrVal = pVals;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
promiseOrVal = this.resolve(tInject?.type ?? t.inject, [t.options ?? tInject?.options], false, tInject?.type);
|
|
401
|
+
}
|
|
402
|
+
if (promiseOrVal instanceof Promise) {
|
|
403
|
+
return promiseOrVal.then((val) => {
|
|
404
|
+
return {
|
|
405
|
+
autoinject: t.autoinject,
|
|
406
|
+
autoinjectKey: t.autoinjectKey,
|
|
407
|
+
instance: valOrMap(val, t),
|
|
408
|
+
};
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
autoinject: t.autoinject,
|
|
413
|
+
autoinjectKey: t.autoinjectKey,
|
|
414
|
+
instance: valOrMap(promiseOrVal, t),
|
|
415
|
+
};
|
|
416
|
+
});
|
|
417
|
+
if (dependencies.some((p) => p instanceof Promise)) {
|
|
418
|
+
return Promise.all(dependencies);
|
|
419
|
+
}
|
|
420
|
+
return dependencies;
|
|
421
|
+
function valOrMap(val, t) {
|
|
422
|
+
let instance = val;
|
|
423
|
+
if (lodash_1.default.isArray(val) && t.mapFunc) {
|
|
424
|
+
instance = new Map();
|
|
425
|
+
for (const i of val) {
|
|
426
|
+
instance.set(t.mapFunc(i), i);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return instance;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
extractDescriptor(type) {
|
|
433
|
+
const descriptor = {
|
|
434
|
+
inject: [],
|
|
435
|
+
resolver: enums_js_1.ResolveType.Singleton,
|
|
436
|
+
};
|
|
437
|
+
const rootMeta = Reflect.getMetadata(decorators_js_1.DI_DESCRIPTION_SYMBOL, type);
|
|
438
|
+
if (rootMeta) {
|
|
439
|
+
descriptor.resolver = rootMeta.resolver;
|
|
440
|
+
}
|
|
441
|
+
function geAllTypes(clz) {
|
|
442
|
+
if (!clz)
|
|
443
|
+
return undefined;
|
|
444
|
+
const toInject = Reflect.getMetadata(decorators_js_1.DI_DESCRIPTION_SYMBOL, clz);
|
|
445
|
+
if (toInject) {
|
|
446
|
+
toInject.inject.forEach((x) => {
|
|
447
|
+
const xTypeName = (0, helpers_js_1.getTypeName)(x.inject);
|
|
448
|
+
// if we do it by autoinject, skip filtering injection props
|
|
449
|
+
// autoinject can have multiple fields of same type and its identified by prop key
|
|
450
|
+
// we cannot override injection props in derived class
|
|
451
|
+
if (x.autoinject === true) {
|
|
452
|
+
descriptor.inject.push(x);
|
|
453
|
+
}
|
|
454
|
+
else if (descriptor.inject.find(i => (0, helpers_js_1.getTypeName)(i.inject) === xTypeName) === undefined) {
|
|
455
|
+
descriptor.inject.push(x);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
// get `__proto__` and (recursively) all parent classes
|
|
460
|
+
geAllTypes(Object.getPrototypeOf(clz));
|
|
461
|
+
}
|
|
462
|
+
geAllTypes(type);
|
|
463
|
+
return descriptor;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
exports.Container = Container;
|
|
467
467
|
//# sourceMappingURL=container.js.map
|