@spinajs/di 2.0.180 → 2.0.181

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