@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,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