@spinajs/di 2.0.385 → 2.0.387
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/container-cache.d.ts +20 -1
- package/lib/cjs/container-cache.d.ts.map +1 -1
- package/lib/cjs/container-cache.js +135 -0
- package/lib/cjs/container-cache.js.map +1 -1
- package/lib/cjs/container.d.ts +13 -0
- package/lib/cjs/container.d.ts.map +1 -1
- package/lib/cjs/container.js +99 -88
- package/lib/cjs/container.js.map +1 -1
- package/lib/mjs/container-cache.d.ts +20 -1
- package/lib/mjs/container-cache.d.ts.map +1 -1
- package/lib/mjs/container-cache.js +135 -0
- package/lib/mjs/container-cache.js.map +1 -1
- package/lib/mjs/container.d.ts +13 -0
- package/lib/mjs/container.d.ts.map +1 -1
- package/lib/mjs/container.js +100 -89
- package/lib/mjs/container.js.map +1 -1
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { TypedArray } from './array.js';
|
|
2
|
-
import { IContainer } from './interfaces.js';
|
|
2
|
+
import { IContainer, IInjectDescriptor } from './interfaces.js';
|
|
3
3
|
import { Class } from './types.js';
|
|
4
4
|
export declare class ContainerCache {
|
|
5
5
|
private container;
|
|
6
6
|
private cache;
|
|
7
|
+
private resolutionPromises;
|
|
8
|
+
private creatingKeys;
|
|
7
9
|
constructor(container: IContainer);
|
|
8
10
|
[Symbol.iterator](): Generator<{
|
|
9
11
|
key: string;
|
|
@@ -11,8 +13,25 @@ export declare class ContainerCache {
|
|
|
11
13
|
}, void, unknown>;
|
|
12
14
|
remove(key: string | Class<any> | TypedArray<any>, parent?: boolean): void;
|
|
13
15
|
add(key: string | Class<any> | object, instance: any): void;
|
|
16
|
+
/**
|
|
17
|
+
* Add instance to cache only if it doesn't already exist (atomic operation for singletons)
|
|
18
|
+
*/
|
|
19
|
+
addIfNotExists(key: string | Class<any> | object, instance: any): boolean;
|
|
14
20
|
has(key: string | Class<any> | object | TypedArray<any>, parent?: boolean): boolean;
|
|
15
21
|
get(key: string | Class<any> | TypedArray<any>, parent?: boolean): any;
|
|
22
|
+
/**
|
|
23
|
+
* Atomic get-or-create operation for singleton services
|
|
24
|
+
* Prevents concurrent creation of the same singleton instance
|
|
25
|
+
*/
|
|
26
|
+
getOrCreate<T>(sourceType: string | Class<any> | TypedArray<any> | object, targetType: string | Class<any> | TypedArray<any>, factory: () => Promise<T> | T, isSingleton: boolean, descriptor: IInjectDescriptor<unknown>, options?: unknown[]): Promise<T> | T;
|
|
27
|
+
/**
|
|
28
|
+
* Get resolution statistics for monitoring
|
|
29
|
+
*/
|
|
30
|
+
getResolutionStats(): {
|
|
31
|
+
cacheSize: number;
|
|
32
|
+
activeResolutions: number;
|
|
33
|
+
resolutionKeys: string[];
|
|
34
|
+
};
|
|
16
35
|
clear(): void;
|
|
17
36
|
}
|
|
18
37
|
//# sourceMappingURL=container-cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container-cache.d.ts","sourceRoot":"","sources":["../../src/container-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"container-cache.d.ts","sourceRoot":"","sources":["../../src/container-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAkB,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAInC,qBAAa,cAAc;IAOb,OAAO,CAAC,SAAS;IAN7B,OAAO,CAAC,KAAK,CAAqB;IAElC,OAAO,CAAC,kBAAkB,CAAwC;IAElE,OAAO,CAAC,YAAY,CAA0B;gBAE1B,SAAS,EAAE,UAAU;IAQxC,CAAC,MAAM,CAAC,QAAQ,CAAC;;;;IAQX,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI;IAQ1E,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,GAAG;IAY3D;;OAEG;IACI,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,OAAO;IAWzE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO;IAanF,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,GAAG;IAc7E;;;OAGG;IACI,WAAW,CAAC,CAAC,EAClB,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,MAAM,EAC1D,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,EACjD,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC7B,WAAW,EAAE,OAAc,EAC3B,UAAU,EAAE,iBAAiB,CAAC,OAAO,CAAC,EACtC,OAAO,CAAC,EAAE,OAAO,EAAE,GAClB,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IA8GjB;;OAEG;IACI,kBAAkB;;;;;IAQlB,KAAK;CAMb"}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { TypedArray } from './array.js';
|
|
1
2
|
import { getTypeName } from './helpers.js';
|
|
3
|
+
import { ResolveType } from './enums.js';
|
|
4
|
+
import { ResolveException } from './exceptions.js';
|
|
2
5
|
export class ContainerCache {
|
|
3
6
|
constructor(container) {
|
|
4
7
|
this.container = container;
|
|
8
|
+
// Track ongoing resolution promises to prevent concurrent singleton creation
|
|
9
|
+
this.resolutionPromises = new Map();
|
|
10
|
+
// Track keys currently being created synchronously
|
|
11
|
+
this.creatingKeys = new Set();
|
|
5
12
|
this.cache = new Map();
|
|
6
13
|
// add to cache container
|
|
7
14
|
// so we can inject container if needed
|
|
@@ -33,6 +40,19 @@ export class ContainerCache {
|
|
|
33
40
|
this.cache.set(tName, [instance]);
|
|
34
41
|
}
|
|
35
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Add instance to cache only if it doesn't already exist (atomic operation for singletons)
|
|
45
|
+
*/
|
|
46
|
+
addIfNotExists(key, instance) {
|
|
47
|
+
const tName = getTypeName(key);
|
|
48
|
+
if (this.has(key)) {
|
|
49
|
+
return false; // Already exists, don't add
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.cache.set(tName, [instance]);
|
|
53
|
+
return true; // Successfully added
|
|
54
|
+
}
|
|
55
|
+
}
|
|
36
56
|
has(key, parent) {
|
|
37
57
|
if (this.cache.has(getTypeName(key))) {
|
|
38
58
|
return true;
|
|
@@ -52,8 +72,123 @@ export class ContainerCache {
|
|
|
52
72
|
}
|
|
53
73
|
return [];
|
|
54
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Atomic get-or-create operation for singleton services
|
|
77
|
+
* Prevents concurrent creation of the same singleton instance
|
|
78
|
+
*/
|
|
79
|
+
getOrCreate(sourceType, targetType, factory, isSingleton = true, descriptor, options) {
|
|
80
|
+
const keyName = getTypeName(targetType);
|
|
81
|
+
const sourceTypeKey = getTypeName(sourceType);
|
|
82
|
+
// Fast path: return cached instance if it exists
|
|
83
|
+
if (this.has(targetType, descriptor.resolver === ResolveType.PerChildContainer ? false : true)) {
|
|
84
|
+
const cached = this.get(targetType, descriptor.resolver === ResolveType.PerChildContainer ? false : true);
|
|
85
|
+
if (descriptor.resolver === ResolveType.PerInstanceCheck) {
|
|
86
|
+
if (cached) {
|
|
87
|
+
const found = cached.find((x) => {
|
|
88
|
+
if (!x.__checkInstance__) {
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
90
|
+
throw new ResolveException(`service ${x.constructor.name} is marked as PerInstanceCheck resolver, but no __checkInstance__ function is provided`);
|
|
91
|
+
}
|
|
92
|
+
return x.__checkInstance__(options);
|
|
93
|
+
});
|
|
94
|
+
if (found) {
|
|
95
|
+
return found;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return factory();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return Array.isArray(cached) ? cached[0] : cached;
|
|
103
|
+
}
|
|
104
|
+
// For non-singletons, always create new instance
|
|
105
|
+
if (!isSingleton) {
|
|
106
|
+
return factory();
|
|
107
|
+
}
|
|
108
|
+
// Check if creation is already in progress (async)
|
|
109
|
+
if (this.resolutionPromises.has(keyName)) {
|
|
110
|
+
return this.resolutionPromises.get(keyName);
|
|
111
|
+
}
|
|
112
|
+
// Check if creation is already in progress (sync)
|
|
113
|
+
if (this.creatingKeys.has(keyName)) {
|
|
114
|
+
// This indicates a potential circular dependency or re-entrant call
|
|
115
|
+
if (this.has(targetType)) {
|
|
116
|
+
const cached = this.get(targetType);
|
|
117
|
+
return Array.isArray(cached) ? cached[0] : cached;
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`Circular dependency detected for ${keyName}`);
|
|
120
|
+
}
|
|
121
|
+
// Mark this key as being created to prevent re-entrance
|
|
122
|
+
this.creatingKeys.add(keyName);
|
|
123
|
+
let instance;
|
|
124
|
+
try {
|
|
125
|
+
instance = factory();
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
this.creatingKeys.delete(keyName);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
const _checkCache = (resolvedInstance) => {
|
|
132
|
+
if (sourceType instanceof TypedArray) {
|
|
133
|
+
// If sourceType is a TypedArray, we need to ensure we cache it correctly
|
|
134
|
+
if (!this.has(sourceTypeKey)) {
|
|
135
|
+
this.add(sourceTypeKey, resolvedInstance);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// If it already exists, we should merge the new instance into the existing array
|
|
139
|
+
const existingInstances = this.get(sourceTypeKey);
|
|
140
|
+
if (!existingInstances.includes(resolvedInstance)) {
|
|
141
|
+
existingInstances.push(resolvedInstance);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const cached = this.get(sourceTypeKey);
|
|
145
|
+
return sourceType instanceof TypedArray ? cached : cached[0];
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// Cache the instance if not already cached
|
|
149
|
+
if (!this.has(targetType)) {
|
|
150
|
+
this.add(targetType, resolvedInstance);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Return the cached version
|
|
154
|
+
const cached = this.get(targetType);
|
|
155
|
+
return cached[0];
|
|
156
|
+
};
|
|
157
|
+
// If factory returns a Promise, handle async resolution
|
|
158
|
+
if (instance instanceof Promise) {
|
|
159
|
+
const creation = instance.then(resolvedInstance => {
|
|
160
|
+
this.creatingKeys.delete(keyName);
|
|
161
|
+
this.resolutionPromises.delete(keyName);
|
|
162
|
+
return _checkCache(resolvedInstance);
|
|
163
|
+
}).catch(error => {
|
|
164
|
+
this.creatingKeys.delete(keyName);
|
|
165
|
+
this.resolutionPromises.delete(keyName);
|
|
166
|
+
throw error;
|
|
167
|
+
});
|
|
168
|
+
// Store the promise to prevent concurrent resolutions
|
|
169
|
+
this.resolutionPromises.set(keyName, creation);
|
|
170
|
+
return creation;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// Synchronous resolution
|
|
174
|
+
this.creatingKeys.delete(keyName);
|
|
175
|
+
return _checkCache(instance);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get resolution statistics for monitoring
|
|
180
|
+
*/
|
|
181
|
+
getResolutionStats() {
|
|
182
|
+
return {
|
|
183
|
+
cacheSize: this.cache.size,
|
|
184
|
+
activeResolutions: this.resolutionPromises.size,
|
|
185
|
+
resolutionKeys: Array.from(this.resolutionPromises.keys()),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
55
188
|
clear() {
|
|
56
189
|
this.cache.clear();
|
|
190
|
+
this.resolutionPromises.clear();
|
|
191
|
+
this.creatingKeys.clear();
|
|
57
192
|
this.add(this.container, this.container);
|
|
58
193
|
}
|
|
59
194
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container-cache.js","sourceRoot":"","sources":["../../src/container-cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"container-cache.js","sourceRoot":"","sources":["../../src/container-cache.ts"],"names":[],"mappings":"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"}
|
package/lib/mjs/container.d.ts
CHANGED
|
@@ -23,6 +23,10 @@ export declare class Container extends EventEmitter implements IContainer {
|
|
|
23
23
|
* Parent container if avaible
|
|
24
24
|
*/
|
|
25
25
|
private parent;
|
|
26
|
+
/**
|
|
27
|
+
* Child containers created from this container
|
|
28
|
+
*/
|
|
29
|
+
private children;
|
|
26
30
|
/**
|
|
27
31
|
* Returns container cache - map object with resolved classes as singletons
|
|
28
32
|
*/
|
|
@@ -43,6 +47,14 @@ export declare class Container extends EventEmitter implements IContainer {
|
|
|
43
47
|
* Clears container resolved types
|
|
44
48
|
*/
|
|
45
49
|
clearRegistry(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Get cache statistics for memory monitoring
|
|
52
|
+
*/
|
|
53
|
+
getCacheStats(): {
|
|
54
|
+
size: number;
|
|
55
|
+
entries: string[];
|
|
56
|
+
childrenCount: number;
|
|
57
|
+
};
|
|
46
58
|
/**
|
|
47
59
|
* Register class/interface to DI.
|
|
48
60
|
* @param type - interface object to register
|
|
@@ -109,6 +121,7 @@ export declare class Container extends EventEmitter implements IContainer {
|
|
|
109
121
|
resolve<T>(type: TypedArray<T>, options?: unknown[], check?: boolean): T extends AsyncService ? Promise<T[]> : T[];
|
|
110
122
|
resolve<T>(type: TypedArray<T>, check?: boolean): T extends AsyncService ? Promise<T[]> : T[];
|
|
111
123
|
getRegisteredTypes<T>(service: string | Class<T> | TypedArray<T>, parent?: boolean): (Class<unknown> | Factory<unknown>)[];
|
|
124
|
+
private getCurrentType;
|
|
112
125
|
private resolveType;
|
|
113
126
|
protected getNewInstance(typeToCreate: Class<unknown> | Factory<unknown>, a?: IResolvedInjection[], options?: unknown[]): Promise<unknown> | unknown;
|
|
114
127
|
hasRegisteredType<T>(source: Class<T> | string, type: Class<T> | string | TypedArray<T>, parent?: boolean): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/container.ts"],"names":[],"mappings":";AACA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAe,SAAS,EAAE,YAAY,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/container.ts"],"names":[],"mappings":"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"}
|
package/lib/mjs/container.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InvalidArgument } from '@spinajs/exceptions';
|
|
2
2
|
import 'reflect-metadata';
|
|
3
|
+
import { TypedArray } from './array.js';
|
|
3
4
|
import { DI_DESCRIPTION_SYMBOL } from './decorators.js';
|
|
4
5
|
import { ResolveType } from './enums.js';
|
|
5
6
|
import { getTypeName, isAsyncService, isFactory, isTypedArray, isPromise } from './helpers.js';
|
|
@@ -9,7 +10,7 @@ import { Binder } from './binder.js';
|
|
|
9
10
|
import { Registry } from './registry.js';
|
|
10
11
|
import { ContainerCache } from './container-cache.js';
|
|
11
12
|
import _ from 'lodash';
|
|
12
|
-
import {
|
|
13
|
+
import { ServiceNotFound } from './exceptions.js';
|
|
13
14
|
/**
|
|
14
15
|
* Dependency injection container implementation
|
|
15
16
|
*/
|
|
@@ -28,6 +29,10 @@ export class Container extends EventEmitter {
|
|
|
28
29
|
}
|
|
29
30
|
constructor(parent) {
|
|
30
31
|
super();
|
|
32
|
+
/**
|
|
33
|
+
* Child containers created from this container
|
|
34
|
+
*/
|
|
35
|
+
this.children = new Set();
|
|
31
36
|
this.registry = new Registry(this);
|
|
32
37
|
this.cache = new ContainerCache(this);
|
|
33
38
|
this.parent = parent || undefined;
|
|
@@ -40,13 +45,31 @@ export class Container extends EventEmitter {
|
|
|
40
45
|
this.clearRegistry();
|
|
41
46
|
}
|
|
42
47
|
async dispose() {
|
|
48
|
+
const disposalErrors = [];
|
|
49
|
+
// Dispose all children first
|
|
50
|
+
await Promise.all([...this.children].map(child => child.dispose().catch(err => {
|
|
51
|
+
disposalErrors.push(err);
|
|
52
|
+
// Continue with other children even if one fails
|
|
53
|
+
})));
|
|
54
|
+
this.children.clear();
|
|
55
|
+
// Dispose services in this container
|
|
43
56
|
for (const entry of this.cache) {
|
|
44
57
|
if (entry.value instanceof Service) {
|
|
45
|
-
|
|
58
|
+
try {
|
|
59
|
+
await entry.value.dispose();
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
disposalErrors.push(err);
|
|
63
|
+
// Continue with other services even if one fails
|
|
64
|
+
}
|
|
46
65
|
}
|
|
47
66
|
}
|
|
48
67
|
this.clearCache();
|
|
49
68
|
this.emit('di.dispose');
|
|
69
|
+
// Log disposal errors if any occurred
|
|
70
|
+
if (disposalErrors.length > 0) {
|
|
71
|
+
console.warn(`${disposalErrors.length} services failed to dispose properly:`, disposalErrors);
|
|
72
|
+
}
|
|
50
73
|
}
|
|
51
74
|
/**
|
|
52
75
|
* clears container registered types information
|
|
@@ -60,6 +83,22 @@ export class Container extends EventEmitter {
|
|
|
60
83
|
clearRegistry() {
|
|
61
84
|
this.Registry.clear();
|
|
62
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Get cache statistics for memory monitoring
|
|
88
|
+
*/
|
|
89
|
+
getCacheStats() {
|
|
90
|
+
const entries = [];
|
|
91
|
+
let size = 0;
|
|
92
|
+
for (const entry of this.cache) {
|
|
93
|
+
entries.push(entry.key);
|
|
94
|
+
size++;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
size,
|
|
98
|
+
entries,
|
|
99
|
+
childrenCount: this.children.size
|
|
100
|
+
};
|
|
101
|
+
}
|
|
63
102
|
/**
|
|
64
103
|
* Register class/interface to DI.
|
|
65
104
|
* @param type - interface object to register
|
|
@@ -85,14 +124,20 @@ export class Container extends EventEmitter {
|
|
|
85
124
|
*
|
|
86
125
|
*/
|
|
87
126
|
child() {
|
|
88
|
-
|
|
127
|
+
const child = new Container(this);
|
|
128
|
+
this.children.add(child);
|
|
129
|
+
// Remove from parent when child is disposed
|
|
130
|
+
child.once('di.dispose', () => {
|
|
131
|
+
this.children.delete(child);
|
|
132
|
+
});
|
|
133
|
+
return child;
|
|
89
134
|
}
|
|
90
135
|
get(service, parent = true) {
|
|
91
136
|
// get value registered as TypedArray ( mean to return all created instances )
|
|
92
|
-
if (service instanceof Array && service.constructor.name === 'TypedArray') {
|
|
137
|
+
if (service instanceof Array && service.constructor.name === 'TypedArray' || service instanceof TypedArray) {
|
|
93
138
|
return this.cache.get(getTypeName(service.Type));
|
|
94
139
|
}
|
|
95
|
-
const r = this.cache.get(service, parent);
|
|
140
|
+
const r = this.cache.get(this.getCurrentType(service, null, parent ?? true) ?? service, parent);
|
|
96
141
|
return r[r.length - 1];
|
|
97
142
|
}
|
|
98
143
|
hasRegistered(service, parent = true) {
|
|
@@ -107,13 +152,16 @@ export class Container extends EventEmitter {
|
|
|
107
152
|
* @throws {@link InvalidArgument} when service is null or empty
|
|
108
153
|
*/
|
|
109
154
|
isResolved(service, parent = true) {
|
|
110
|
-
|
|
155
|
+
if (service instanceof Array && service.constructor.name === 'TypedArray' || service instanceof TypedArray) {
|
|
156
|
+
return this.Cache.has(this.getCurrentType(service.Type, null, parent ?? true) ?? service, parent);
|
|
157
|
+
}
|
|
158
|
+
return this.Cache.has(this.getCurrentType(service, null, parent ?? true) ?? service, parent);
|
|
111
159
|
}
|
|
112
160
|
/**
|
|
113
161
|
*
|
|
114
162
|
* @param type - type to resolve
|
|
115
163
|
* @param options - options passed to constructor / factory
|
|
116
|
-
* @param check - strict check if serivice is registered in container before resolving. Default behavior is to
|
|
164
|
+
* @param check - strict check if serivice is registered in container before resolving. Default behavior is not to check and resolve
|
|
117
165
|
*/
|
|
118
166
|
resolve(type, options, check, tType) {
|
|
119
167
|
if (!type) {
|
|
@@ -144,30 +192,15 @@ export class Container extends EventEmitter {
|
|
|
144
192
|
}
|
|
145
193
|
const resolved = targetType.map((r) => this.resolveType(type, r, opt));
|
|
146
194
|
if (resolved.some((r) => r instanceof Promise)) {
|
|
147
|
-
return Promise.all(resolved);
|
|
195
|
+
return Promise.all(resolved).then(() => this.get(type, check ?? true));
|
|
148
196
|
}
|
|
149
|
-
return
|
|
197
|
+
return this.get(type, check ?? true);
|
|
150
198
|
}
|
|
151
199
|
else {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
}
|
|
200
|
+
const fType = this.getCurrentType(type, tType, check ?? true);
|
|
201
|
+
if (fType === null) {
|
|
202
|
+
return null;
|
|
164
203
|
}
|
|
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
|
-
// if we have override for target type in registry, resolve it ( last registered ) otherwise resolve target type type itself
|
|
170
|
-
const fType = targetType[targetType.length - 1] ?? tType;
|
|
171
204
|
const rValue = this.resolveType(sourceType, fType, opt);
|
|
172
205
|
return rValue;
|
|
173
206
|
}
|
|
@@ -175,6 +208,28 @@ export class Container extends EventEmitter {
|
|
|
175
208
|
getRegisteredTypes(service, parent) {
|
|
176
209
|
return this.Registry.getTypes(service, parent);
|
|
177
210
|
}
|
|
211
|
+
getCurrentType(type, tType, check) {
|
|
212
|
+
// finaly resolve single type:
|
|
213
|
+
// 1. last registered type OR
|
|
214
|
+
// 2. if non is registered - type itself
|
|
215
|
+
let targetType = this.getRegisteredTypes(type, check ?? true);
|
|
216
|
+
if (!targetType) {
|
|
217
|
+
// if nothing is register under string identifier, then return null
|
|
218
|
+
if (typeof type === 'string') {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
targetType = [type];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// if we have target function callback
|
|
226
|
+
// we can select whitch of targetType to resolve
|
|
227
|
+
//
|
|
228
|
+
// if not, by default last registered type is resolved
|
|
229
|
+
// if we have override for target type in registry, resolve it ( last registered ) otherwise resolve target type type itself
|
|
230
|
+
const fType = targetType[targetType.length - 1] ?? tType;
|
|
231
|
+
return fType;
|
|
232
|
+
}
|
|
178
233
|
resolveType(sourceType, targetType, options) {
|
|
179
234
|
/**
|
|
180
235
|
* If its a factory func, always resolve as new instance
|
|
@@ -192,11 +247,7 @@ export class Container extends EventEmitter {
|
|
|
192
247
|
// resolving strategy per container is treatead as singleton
|
|
193
248
|
// in this particular container
|
|
194
249
|
const isSingletonInChild = descriptor.resolver === ResolveType.PerChildContainer;
|
|
195
|
-
const isSingleton = descriptor.resolver === ResolveType.Singleton;
|
|
196
|
-
const setCache = (target) => {
|
|
197
|
-
this.Cache.add(sourceType, target);
|
|
198
|
-
return target;
|
|
199
|
-
};
|
|
250
|
+
const isSingleton = descriptor.resolver === ResolveType.Singleton || descriptor.resolver === ResolveType.PerInstanceCheck || descriptor.resolver === ResolveType.PerInstance;
|
|
200
251
|
const emit = (target) => {
|
|
201
252
|
const sourceTypeName = getTypeName(sourceType);
|
|
202
253
|
const targetTypeName = getTypeName(targetType);
|
|
@@ -207,31 +258,15 @@ export class Container extends EventEmitter {
|
|
|
207
258
|
this.emit(`di.resolved.${sourceTypeName}`, this, target);
|
|
208
259
|
}
|
|
209
260
|
};
|
|
210
|
-
const getCachedInstance = (e, parent) => {
|
|
211
|
-
if (this.isResolved(e, parent)) {
|
|
212
|
-
const rArray = this.get(e, parent);
|
|
213
|
-
return _.isArray(rArray) ? rArray.find((x) => getTypeName(x) === getTypeName(targetType)) : rArray;
|
|
214
|
-
}
|
|
215
|
-
return null;
|
|
216
|
-
};
|
|
217
|
-
const getCachedInstances = (e, parent) => {
|
|
218
|
-
if (this.isResolved(e, parent)) {
|
|
219
|
-
const rArray = this.get(e, parent);
|
|
220
|
-
return _.isArray(rArray) ? rArray : [rArray];
|
|
221
|
-
}
|
|
222
|
-
return null;
|
|
223
|
-
};
|
|
224
261
|
const createNewInstance = (t, i, options) => {
|
|
225
262
|
const instance = this.getNewInstance(t, i, options);
|
|
226
263
|
if (isPromise(instance)) {
|
|
227
264
|
return instance.then((r) => {
|
|
228
|
-
setCache(r);
|
|
229
265
|
emit(r);
|
|
230
266
|
return r;
|
|
231
267
|
});
|
|
232
268
|
}
|
|
233
269
|
else {
|
|
234
|
-
setCache(instance);
|
|
235
270
|
emit(instance);
|
|
236
271
|
return instance;
|
|
237
272
|
}
|
|
@@ -250,48 +285,27 @@ export class Container extends EventEmitter {
|
|
|
250
285
|
return instance;
|
|
251
286
|
}
|
|
252
287
|
}
|
|
253
|
-
if (d.resolver === ResolveType.PerInstanceCheck) {
|
|
254
|
-
const cashed = getCachedInstances(tType, true);
|
|
255
|
-
if (cashed) {
|
|
256
|
-
const found = cashed.find((x) => {
|
|
257
|
-
if (!x.__checkInstance__) {
|
|
258
|
-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
259
|
-
throw new ResolveException(`service ${x.constructor.name} is marked as PerInstanceCheck resolver, but no __checkInstance__ function is provided`);
|
|
260
|
-
}
|
|
261
|
-
return x.__checkInstance__(options);
|
|
262
|
-
});
|
|
263
|
-
if (found) {
|
|
264
|
-
return found;
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
return createNewInstance(t, i, options);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
288
|
this.Registry.register(sName, t);
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return createNewInstance(t, i, options);
|
|
275
|
-
}
|
|
276
|
-
else {
|
|
277
|
-
return cashed;
|
|
278
|
-
}
|
|
289
|
+
// For singletons, don't check cache here - let getOrCreate handle it atomically
|
|
290
|
+
return createNewInstance(t, i, options);
|
|
279
291
|
};
|
|
280
|
-
//
|
|
292
|
+
// For singletons, use atomic cache operations to prevent concurrent creation
|
|
281
293
|
if (isSingletonInChild || isSingleton) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
+
return this.Cache.getOrCreate(sourceType, tType, () => {
|
|
295
|
+
const deps = this.resolveDependencies(descriptor.inject);
|
|
296
|
+
if (deps instanceof Promise) {
|
|
297
|
+
return deps.then((resolvedDependencies) => {
|
|
298
|
+
return resolve(descriptor, tType, resolvedDependencies);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
const resInstance = resolve(descriptor, tType, deps);
|
|
303
|
+
return resInstance;
|
|
304
|
+
}
|
|
305
|
+
}, true, // isSingleton
|
|
306
|
+
descriptor, options);
|
|
294
307
|
}
|
|
308
|
+
// Non-singleton path - resolve normally without caching
|
|
295
309
|
const deps = this.resolveDependencies(descriptor.inject);
|
|
296
310
|
if (deps instanceof Promise) {
|
|
297
311
|
return deps.then((resolvedDependencies) => {
|
|
@@ -300,9 +314,6 @@ export class Container extends EventEmitter {
|
|
|
300
314
|
}
|
|
301
315
|
else {
|
|
302
316
|
const resInstance = resolve(descriptor, tType, deps);
|
|
303
|
-
if (resInstance instanceof Promise) {
|
|
304
|
-
return resInstance;
|
|
305
|
-
}
|
|
306
317
|
return resInstance;
|
|
307
318
|
}
|
|
308
319
|
}
|