@navios/di 0.8.0 → 0.9.0
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/CHANGELOG.md +87 -0
- package/README.md +117 -17
- package/lib/browser/container/abstract-container.d.mts +112 -0
- package/lib/browser/container/abstract-container.d.mts.map +1 -0
- package/lib/browser/container/abstract-container.mjs +100 -0
- package/lib/browser/container/abstract-container.mjs.map +1 -0
- package/lib/browser/container/container.d.mts +100 -0
- package/lib/browser/container/container.d.mts.map +1 -0
- package/lib/browser/container/container.mjs +424 -0
- package/lib/browser/container/container.mjs.map +1 -0
- package/lib/browser/container/scoped-container.d.mts +93 -0
- package/lib/browser/container/scoped-container.d.mts.map +1 -0
- package/lib/browser/container/scoped-container.mjs +119 -0
- package/lib/browser/container/scoped-container.mjs.map +1 -0
- package/lib/browser/decorators/factory.decorator.d.mts +26 -0
- package/lib/browser/decorators/factory.decorator.d.mts.map +1 -0
- package/lib/browser/decorators/factory.decorator.mjs +20 -0
- package/lib/browser/decorators/factory.decorator.mjs.map +1 -0
- package/lib/browser/decorators/injectable.decorator.d.mts +38 -0
- package/lib/browser/decorators/injectable.decorator.d.mts.map +1 -0
- package/lib/browser/decorators/injectable.decorator.mjs +21 -0
- package/lib/browser/decorators/injectable.decorator.mjs.map +1 -0
- package/lib/browser/enums/injectable-scope.enum.d.mts +18 -0
- package/lib/browser/enums/injectable-scope.enum.d.mts.map +1 -0
- package/lib/browser/enums/injectable-scope.enum.mjs +20 -0
- package/lib/browser/enums/injectable-scope.enum.mjs.map +1 -0
- package/lib/browser/enums/injectable-type.enum.d.mts +8 -0
- package/lib/browser/enums/injectable-type.enum.d.mts.map +1 -0
- package/lib/browser/enums/injectable-type.enum.mjs +10 -0
- package/lib/browser/enums/injectable-type.enum.mjs.map +1 -0
- package/lib/browser/errors/di-error.d.mts +43 -0
- package/lib/browser/errors/di-error.d.mts.map +1 -0
- package/lib/browser/errors/di-error.mjs +98 -0
- package/lib/browser/errors/di-error.mjs.map +1 -0
- package/lib/browser/event-emitter.d.mts +16 -0
- package/lib/browser/event-emitter.d.mts.map +1 -0
- package/lib/browser/event-emitter.mjs +320 -0
- package/lib/browser/event-emitter.mjs.map +1 -0
- package/lib/browser/index.d.mts +37 -1558
- package/lib/browser/index.mjs +29 -2749
- package/lib/browser/interfaces/container.interface.d.mts +59 -0
- package/lib/browser/interfaces/container.interface.d.mts.map +1 -0
- package/lib/browser/interfaces/factory.interface.d.mts +14 -0
- package/lib/browser/interfaces/factory.interface.d.mts.map +1 -0
- package/lib/browser/interfaces/on-service-destroy.interface.d.mts +7 -0
- package/lib/browser/interfaces/on-service-destroy.interface.d.mts.map +1 -0
- package/lib/browser/interfaces/on-service-init.interface.d.mts +7 -0
- package/lib/browser/interfaces/on-service-init.interface.d.mts.map +1 -0
- package/lib/browser/internal/context/async-local-storage.browser.mjs +20 -0
- package/lib/browser/internal/context/async-local-storage.browser.mjs.map +1 -0
- package/lib/browser/internal/context/async-local-storage.d.mts +9 -0
- package/lib/browser/internal/context/async-local-storage.d.mts.map +1 -0
- package/lib/browser/internal/context/async-local-storage.types.d.mts +11 -0
- package/lib/browser/internal/context/async-local-storage.types.d.mts.map +1 -0
- package/lib/browser/internal/context/factory-context.d.mts +23 -0
- package/lib/browser/internal/context/factory-context.d.mts.map +1 -0
- package/lib/browser/internal/context/resolution-context.d.mts +43 -0
- package/lib/browser/internal/context/resolution-context.d.mts.map +1 -0
- package/lib/browser/internal/context/resolution-context.mjs +56 -0
- package/lib/browser/internal/context/resolution-context.mjs.map +1 -0
- package/lib/browser/internal/context/service-initialization-context.d.mts +48 -0
- package/lib/browser/internal/context/service-initialization-context.d.mts.map +1 -0
- package/lib/browser/internal/context/sync-local-storage.mjs +53 -0
- package/lib/browser/internal/context/sync-local-storage.mjs.map +1 -0
- package/lib/browser/internal/core/instance-resolver.d.mts +119 -0
- package/lib/browser/internal/core/instance-resolver.d.mts.map +1 -0
- package/lib/browser/internal/core/instance-resolver.mjs +306 -0
- package/lib/browser/internal/core/instance-resolver.mjs.map +1 -0
- package/lib/browser/internal/core/name-resolver.d.mts +52 -0
- package/lib/browser/internal/core/name-resolver.d.mts.map +1 -0
- package/lib/browser/internal/core/name-resolver.mjs +118 -0
- package/lib/browser/internal/core/name-resolver.mjs.map +1 -0
- package/lib/browser/internal/core/scope-tracker.d.mts +65 -0
- package/lib/browser/internal/core/scope-tracker.d.mts.map +1 -0
- package/lib/browser/internal/core/scope-tracker.mjs +120 -0
- package/lib/browser/internal/core/scope-tracker.mjs.map +1 -0
- package/lib/browser/internal/core/service-initializer.d.mts +44 -0
- package/lib/browser/internal/core/service-initializer.d.mts.map +1 -0
- package/lib/browser/internal/core/service-initializer.mjs +109 -0
- package/lib/browser/internal/core/service-initializer.mjs.map +1 -0
- package/lib/browser/internal/core/service-invalidator.d.mts +81 -0
- package/lib/browser/internal/core/service-invalidator.d.mts.map +1 -0
- package/lib/browser/internal/core/service-invalidator.mjs +142 -0
- package/lib/browser/internal/core/service-invalidator.mjs.map +1 -0
- package/lib/browser/internal/core/token-resolver.d.mts +54 -0
- package/lib/browser/internal/core/token-resolver.d.mts.map +1 -0
- package/lib/browser/internal/core/token-resolver.mjs +77 -0
- package/lib/browser/internal/core/token-resolver.mjs.map +1 -0
- package/lib/browser/internal/holder/holder-storage.interface.d.mts +99 -0
- package/lib/browser/internal/holder/holder-storage.interface.d.mts.map +1 -0
- package/lib/browser/internal/holder/instance-holder.d.mts +101 -0
- package/lib/browser/internal/holder/instance-holder.d.mts.map +1 -0
- package/lib/browser/internal/holder/instance-holder.mjs +19 -0
- package/lib/browser/internal/holder/instance-holder.mjs.map +1 -0
- package/lib/browser/internal/holder/unified-storage.d.mts +53 -0
- package/lib/browser/internal/holder/unified-storage.d.mts.map +1 -0
- package/lib/browser/internal/holder/unified-storage.mjs +144 -0
- package/lib/browser/internal/holder/unified-storage.mjs.map +1 -0
- package/lib/browser/internal/lifecycle/circular-detector.d.mts +39 -0
- package/lib/browser/internal/lifecycle/circular-detector.d.mts.map +1 -0
- package/lib/browser/internal/lifecycle/circular-detector.mjs +55 -0
- package/lib/browser/internal/lifecycle/circular-detector.mjs.map +1 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts +18 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts.map +1 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs +43 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs.map +1 -0
- package/lib/browser/internal/stub-factory-class.d.mts +14 -0
- package/lib/browser/internal/stub-factory-class.d.mts.map +1 -0
- package/lib/browser/internal/stub-factory-class.mjs +18 -0
- package/lib/browser/internal/stub-factory-class.mjs.map +1 -0
- package/lib/browser/symbols/injectable-token.d.mts +5 -0
- package/lib/browser/symbols/injectable-token.d.mts.map +1 -0
- package/lib/browser/symbols/injectable-token.mjs +6 -0
- package/lib/browser/symbols/injectable-token.mjs.map +1 -0
- package/lib/browser/token/injection-token.d.mts +55 -0
- package/lib/browser/token/injection-token.d.mts.map +1 -0
- package/lib/browser/token/injection-token.mjs +100 -0
- package/lib/browser/token/injection-token.mjs.map +1 -0
- package/lib/browser/token/registry.d.mts +37 -0
- package/lib/browser/token/registry.d.mts.map +1 -0
- package/lib/browser/token/registry.mjs +86 -0
- package/lib/browser/token/registry.mjs.map +1 -0
- package/lib/browser/utils/default-injectors.d.mts +12 -0
- package/lib/browser/utils/default-injectors.d.mts.map +1 -0
- package/lib/browser/utils/default-injectors.mjs +13 -0
- package/lib/browser/utils/default-injectors.mjs.map +1 -0
- package/lib/browser/utils/get-injectable-token.d.mts +9 -0
- package/lib/browser/utils/get-injectable-token.d.mts.map +1 -0
- package/lib/browser/utils/get-injectable-token.mjs +13 -0
- package/lib/browser/utils/get-injectable-token.mjs.map +1 -0
- package/lib/browser/utils/get-injectors.d.mts +55 -0
- package/lib/browser/utils/get-injectors.d.mts.map +1 -0
- package/lib/browser/utils/get-injectors.mjs +121 -0
- package/lib/browser/utils/get-injectors.mjs.map +1 -0
- package/lib/browser/utils/types.d.mts +23 -0
- package/lib/browser/utils/types.d.mts.map +1 -0
- package/lib/{container-DAKOvAgr.mjs → container-8-z89TyQ.mjs} +1325 -1462
- package/lib/container-8-z89TyQ.mjs.map +1 -0
- package/lib/{container-Bp1W-pWJ.d.mts → container-CNiqesCL.d.mts} +598 -617
- package/lib/container-CNiqesCL.d.mts.map +1 -0
- package/lib/{container-DENMeJ87.cjs → container-CaY2fDuk.cjs} +1369 -1512
- package/lib/container-CaY2fDuk.cjs.map +1 -0
- package/lib/{container-YPwvmlK2.d.cts → container-D-0Ho3qL.d.cts} +598 -612
- package/lib/container-D-0Ho3qL.d.cts.map +1 -0
- package/lib/index.cjs +13 -15
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +58 -223
- package/lib/index.d.cts.map +1 -1
- package/lib/index.d.mts +62 -222
- package/lib/index.d.mts.map +1 -1
- package/lib/index.mjs +5 -6
- package/lib/index.mjs.map +1 -1
- package/lib/testing/index.cjs +569 -311
- package/lib/testing/index.cjs.map +1 -1
- package/lib/testing/index.d.cts +370 -41
- package/lib/testing/index.d.cts.map +1 -1
- package/lib/testing/index.d.mts +370 -41
- package/lib/testing/index.d.mts.map +1 -1
- package/lib/testing/index.mjs +568 -305
- package/lib/testing/index.mjs.map +1 -1
- package/package.json +2 -1
- package/src/__tests__/circular-detector.spec.mts +193 -0
- package/src/__tests__/concurrent.spec.mts +368 -0
- package/src/__tests__/container.spec.mts +32 -30
- package/src/__tests__/di-error.spec.mts +351 -0
- package/src/__tests__/e2e.browser.spec.mts +0 -4
- package/src/__tests__/e2e.spec.mts +10 -19
- package/src/__tests__/event-emitter.spec.mts +232 -109
- package/src/__tests__/get-injectors.spec.mts +250 -39
- package/src/__tests__/injection-token.spec.mts +293 -349
- package/src/__tests__/library-findings.spec.mts +8 -8
- package/src/__tests__/registry.spec.mts +358 -210
- package/src/__tests__/resolution-context.spec.mts +255 -0
- package/src/__tests__/scope-tracker.spec.mts +598 -0
- package/src/__tests__/scope-upgrade.spec.mts +808 -0
- package/src/__tests__/scoped-container.spec.mts +595 -0
- package/src/__tests__/test-container.spec.mts +293 -0
- package/src/__tests__/token-resolver.spec.mts +207 -0
- package/src/__tests__/unified-storage.spec.mts +535 -0
- package/src/__tests__/unit-test-container.spec.mts +405 -0
- package/src/__type-tests__/container.spec-d.mts +180 -0
- package/src/__type-tests__/factory.spec-d.mts +15 -3
- package/src/__type-tests__/inject.spec-d.mts +115 -20
- package/src/__type-tests__/injectable.spec-d.mts +69 -52
- package/src/__type-tests__/injection-token.spec-d.mts +176 -0
- package/src/__type-tests__/scoped-container.spec-d.mts +212 -0
- package/src/container/abstract-container.mts +327 -0
- package/src/container/container.mts +142 -170
- package/src/container/scoped-container.mts +126 -208
- package/src/decorators/factory.decorator.mts +16 -11
- package/src/decorators/injectable.decorator.mts +20 -16
- package/src/enums/index.mts +2 -2
- package/src/enums/injectable-scope.enum.mts +1 -0
- package/src/enums/injectable-type.enum.mts +1 -0
- package/src/errors/di-error.mts +96 -0
- package/src/event-emitter.mts +3 -27
- package/src/index.mts +6 -153
- package/src/interfaces/container.interface.mts +13 -0
- package/src/interfaces/factory.interface.mts +1 -1
- package/src/interfaces/index.mts +1 -1
- package/src/internal/context/async-local-storage.mts +3 -2
- package/src/internal/context/async-local-storage.types.mts +1 -0
- package/src/internal/context/factory-context.mts +1 -0
- package/src/internal/context/index.mts +3 -1
- package/src/internal/context/resolution-context.mts +1 -0
- package/src/internal/context/service-initialization-context.mts +43 -0
- package/src/internal/core/index.mts +5 -4
- package/src/internal/core/instance-resolver.mts +460 -302
- package/src/internal/core/name-resolver.mts +196 -0
- package/src/internal/core/scope-tracker.mts +242 -0
- package/src/internal/core/{instantiator.mts → service-initializer.mts} +51 -29
- package/src/internal/core/service-invalidator.mts +290 -0
- package/src/internal/core/token-resolver.mts +122 -0
- package/src/internal/holder/holder-storage.interface.mts +11 -5
- package/src/internal/holder/index.mts +2 -5
- package/src/internal/holder/instance-holder.mts +1 -3
- package/src/internal/holder/unified-storage.mts +245 -0
- package/src/internal/index.mts +2 -1
- package/src/internal/lifecycle/circular-detector.mts +1 -0
- package/src/internal/lifecycle/index.mts +1 -1
- package/src/internal/lifecycle/lifecycle-event-bus.mts +1 -0
- package/src/internal/stub-factory-class.mts +16 -0
- package/src/symbols/injectable-token.mts +3 -1
- package/src/testing/index.mts +2 -0
- package/src/testing/test-container.mts +546 -85
- package/src/testing/types.mts +117 -0
- package/src/testing/unit-test-container.mts +509 -0
- package/src/token/injection-token.mts +41 -4
- package/src/token/registry.mts +75 -9
- package/src/utils/default-injectors.mts +16 -0
- package/src/utils/get-injectable-token.mts +2 -3
- package/src/utils/get-injectors.mts +26 -15
- package/src/utils/index.mts +3 -1
- package/src/utils/types.mts +1 -0
- package/tsdown.config.mts +11 -1
- package/lib/browser/index.d.mts.map +0 -1
- package/lib/browser/index.mjs.map +0 -1
- package/lib/container-Bp1W-pWJ.d.mts.map +0 -1
- package/lib/container-DAKOvAgr.mjs.map +0 -1
- package/lib/container-DENMeJ87.cjs.map +0 -1
- package/lib/container-YPwvmlK2.d.cts.map +0 -1
- package/src/__tests__/async-local-storage.browser.spec.mts +0 -166
- package/src/__tests__/async-local-storage.spec.mts +0 -333
- package/src/__tests__/errors.spec.mts +0 -87
- package/src/__tests__/factory.spec.mts +0 -137
- package/src/__tests__/injectable.spec.mts +0 -246
- package/src/__tests__/request-scope.spec.mts +0 -416
- package/src/__tests__/service-instantiator.spec.mts +0 -410
- package/src/__tests__/service-locator-event-bus.spec.mts +0 -242
- package/src/__tests__/service-locator-manager.spec.mts +0 -300
- package/src/__tests__/service-locator.spec.mts +0 -966
- package/src/__tests__/unified-api.spec.mts +0 -130
- package/src/browser.mts +0 -11
- package/src/injectors.mts +0 -18
- package/src/internal/context/request-context.mts +0 -225
- package/src/internal/core/invalidator.mts +0 -437
- package/src/internal/core/service-locator.mts +0 -202
- package/src/internal/core/token-processor.mts +0 -252
- package/src/internal/holder/base-holder-manager.mts +0 -334
- package/src/internal/holder/holder-manager.mts +0 -85
- package/src/internal/holder/request-storage.mts +0 -127
- package/src/internal/holder/singleton-storage.mts +0 -92
- package/src/testing/README.md +0 -80
- package/src/testing/__tests__/test-container.spec.mts +0 -173
|
@@ -25,19 +25,45 @@ let InjectableType = /* @__PURE__ */ function(InjectableType$1) {
|
|
|
25
25
|
return InjectableType$1;
|
|
26
26
|
}({});
|
|
27
27
|
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/symbols/injectable-token.mts
|
|
30
|
+
const InjectableTokenMeta = /* @__PURE__ */ Symbol.for("InjectableTokenMeta");
|
|
31
|
+
|
|
28
32
|
//#endregion
|
|
29
33
|
//#region src/token/injection-token.mts
|
|
34
|
+
/**
|
|
35
|
+
* Simple hash function for deterministic ID generation
|
|
36
|
+
*/ function simpleHash(str) {
|
|
37
|
+
let hash = 0;
|
|
38
|
+
for (let i = 0; i < str.length; i++) {
|
|
39
|
+
const char = str.charCodeAt(i);
|
|
40
|
+
hash = (hash << 5) - hash + char;
|
|
41
|
+
hash = hash & hash;
|
|
42
|
+
}
|
|
43
|
+
return Math.abs(hash).toString(36);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Generate deterministic ID from token name
|
|
47
|
+
*/ function generateTokenId(name, customId) {
|
|
48
|
+
if (customId) return customId;
|
|
49
|
+
let base;
|
|
50
|
+
if (typeof name === "function") base = `${name.name}_${name.toString()}`;
|
|
51
|
+
else if (typeof name === "symbol") base = `symbol_${name.toString()}`;
|
|
52
|
+
else base = `token_${name}`;
|
|
53
|
+
return `${base.split("_")[0]}_${simpleHash(base)}`;
|
|
54
|
+
}
|
|
30
55
|
var InjectionToken = class InjectionToken {
|
|
31
56
|
name;
|
|
32
57
|
schema;
|
|
33
|
-
id
|
|
58
|
+
id;
|
|
34
59
|
formattedName = null;
|
|
35
|
-
constructor(name, schema) {
|
|
60
|
+
constructor(name, schema, customId) {
|
|
36
61
|
this.name = name;
|
|
37
62
|
this.schema = schema;
|
|
63
|
+
this.id = generateTokenId(name, customId);
|
|
38
64
|
}
|
|
39
|
-
static create(name, schema) {
|
|
40
|
-
return new InjectionToken(name, schema);
|
|
65
|
+
static create(name, schema, customId) {
|
|
66
|
+
return new InjectionToken(name, schema, customId);
|
|
41
67
|
}
|
|
42
68
|
static bound(token, value) {
|
|
43
69
|
return new BoundInjectionToken(token, value);
|
|
@@ -106,6 +132,7 @@ var FactoryInjectionToken = class {
|
|
|
106
132
|
var Registry = class {
|
|
107
133
|
parent;
|
|
108
134
|
factories = /* @__PURE__ */ new Map();
|
|
135
|
+
highestPriority = /* @__PURE__ */ new Map();
|
|
109
136
|
constructor(parent) {
|
|
110
137
|
this.parent = parent;
|
|
111
138
|
}
|
|
@@ -115,23 +142,50 @@ var Registry = class {
|
|
|
115
142
|
return false;
|
|
116
143
|
}
|
|
117
144
|
get(token) {
|
|
118
|
-
const factory = this.
|
|
145
|
+
const factory = this.highestPriority.get(token.id);
|
|
119
146
|
if (!factory) {
|
|
120
147
|
if (this.parent) return this.parent.get(token);
|
|
121
148
|
throw new Error(`[Registry] No factory found for ${token.toString()}`);
|
|
122
149
|
}
|
|
123
150
|
return factory;
|
|
124
151
|
}
|
|
125
|
-
|
|
126
|
-
this.factories.
|
|
152
|
+
getAll(token) {
|
|
153
|
+
const records = this.factories.get(token.id);
|
|
154
|
+
if (!records || records.length === 0) {
|
|
155
|
+
if (this.parent) return this.parent.getAll(token);
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
return [...records].sort((a, b) => b.priority - a.priority);
|
|
159
|
+
}
|
|
160
|
+
set(token, scope, target, type, priority = 0) {
|
|
161
|
+
const record = {
|
|
127
162
|
scope,
|
|
128
163
|
originalToken: token,
|
|
129
164
|
target,
|
|
130
|
-
type
|
|
131
|
-
|
|
165
|
+
type,
|
|
166
|
+
priority
|
|
167
|
+
};
|
|
168
|
+
const existing = this.factories.get(token.id) || [];
|
|
169
|
+
existing.push(record);
|
|
170
|
+
this.factories.set(token.id, existing);
|
|
171
|
+
const currentHighest = this.highestPriority.get(token.id);
|
|
172
|
+
if (!currentHighest || priority > currentHighest.priority) this.highestPriority.set(token.id, record);
|
|
132
173
|
}
|
|
133
174
|
delete(token) {
|
|
134
|
-
this.factories.
|
|
175
|
+
const records = this.factories.get(token.id);
|
|
176
|
+
if (records) {
|
|
177
|
+
const deletedHighest = this.highestPriority.get(token.id);
|
|
178
|
+
this.factories.delete(token.id);
|
|
179
|
+
this.highestPriority.delete(token.id);
|
|
180
|
+
if (deletedHighest && records.length > 1) {
|
|
181
|
+
const remaining = records.filter((r) => r.originalToken.id !== deletedHighest.originalToken.id || r.priority !== deletedHighest.priority);
|
|
182
|
+
if (remaining.length > 0) {
|
|
183
|
+
const newHighest = remaining.reduce((max, current) => current.priority > max.priority ? current : max);
|
|
184
|
+
this.highestPriority.set(token.id, newHighest);
|
|
185
|
+
this.factories.set(token.id, remaining);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
135
189
|
}
|
|
136
190
|
/**
|
|
137
191
|
* Updates the scope of an already registered factory.
|
|
@@ -142,29 +196,29 @@ var Registry = class {
|
|
|
142
196
|
* @param scope The new scope to set
|
|
143
197
|
* @returns true if the scope was updated, false if the token was not found
|
|
144
198
|
*/ updateScope(token, scope) {
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
147
|
-
|
|
199
|
+
const records = this.factories.get(token.id);
|
|
200
|
+
if (records && records.length > 0) {
|
|
201
|
+
records.forEach((record) => {
|
|
202
|
+
record.scope = scope;
|
|
203
|
+
});
|
|
204
|
+
const highest = this.highestPriority.get(token.id);
|
|
205
|
+
if (highest) highest.scope = scope;
|
|
148
206
|
return true;
|
|
149
207
|
}
|
|
150
208
|
if (this.parent) return this.parent.updateScope(token, scope);
|
|
151
209
|
return false;
|
|
152
210
|
}
|
|
153
211
|
};
|
|
154
|
-
const globalRegistry = new Registry();
|
|
155
|
-
|
|
156
|
-
//#endregion
|
|
157
|
-
//#region src/symbols/injectable-token.mts
|
|
158
|
-
const InjectableTokenMeta = Symbol.for("InjectableTokenMeta");
|
|
212
|
+
const globalRegistry = /* @__PURE__ */ new Registry();
|
|
159
213
|
|
|
160
214
|
//#endregion
|
|
161
215
|
//#region src/decorators/injectable.decorator.mts
|
|
162
|
-
function Injectable({ scope = InjectableScope.Singleton, token, schema, registry = globalRegistry } = {}) {
|
|
216
|
+
function Injectable({ scope = InjectableScope.Singleton, token, schema, registry = globalRegistry, priority = 0 } = {}) {
|
|
163
217
|
return (target, context) => {
|
|
164
|
-
if (context && context.kind !== "class" || target instanceof Function && !context) throw new Error("[
|
|
165
|
-
if (schema && token) throw new Error("[
|
|
218
|
+
if (context && context.kind !== "class" || target instanceof Function && !context) throw new Error("[DI] @Injectable decorator can only be used on classes.");
|
|
219
|
+
if (schema && token) throw new Error("[DI] @Injectable decorator cannot have both a token and a schema");
|
|
166
220
|
let injectableToken = token ?? InjectionToken.create(target, schema);
|
|
167
|
-
registry.set(injectableToken, scope, target, InjectableType.Class);
|
|
221
|
+
registry.set(injectableToken, scope, target, InjectableType.Class, priority);
|
|
168
222
|
target[InjectableTokenMeta] = injectableToken;
|
|
169
223
|
return target;
|
|
170
224
|
};
|
|
@@ -172,45 +226,97 @@ function Injectable({ scope = InjectableScope.Singleton, token, schema, registry
|
|
|
172
226
|
|
|
173
227
|
//#endregion
|
|
174
228
|
//#region src/errors/di-error.mts
|
|
175
|
-
|
|
229
|
+
var DIErrorCode = /* @__PURE__ */ function(DIErrorCode$1) {
|
|
176
230
|
DIErrorCode$1["FactoryNotFound"] = "FactoryNotFound";
|
|
177
231
|
DIErrorCode$1["FactoryTokenNotResolved"] = "FactoryTokenNotResolved";
|
|
178
232
|
DIErrorCode$1["InstanceNotFound"] = "InstanceNotFound";
|
|
179
233
|
DIErrorCode$1["InstanceDestroying"] = "InstanceDestroying";
|
|
180
234
|
DIErrorCode$1["CircularDependency"] = "CircularDependency";
|
|
235
|
+
DIErrorCode$1["TokenValidationError"] = "TokenValidationError";
|
|
236
|
+
DIErrorCode$1["TokenSchemaRequiredError"] = "TokenSchemaRequiredError";
|
|
237
|
+
DIErrorCode$1["ClassNotInjectable"] = "ClassNotInjectable";
|
|
238
|
+
DIErrorCode$1["ScopeMismatchError"] = "ScopeMismatchError";
|
|
239
|
+
DIErrorCode$1["PriorityConflictError"] = "PriorityConflictError";
|
|
240
|
+
DIErrorCode$1["StorageError"] = "StorageError";
|
|
241
|
+
DIErrorCode$1["InitializationError"] = "InitializationError";
|
|
242
|
+
DIErrorCode$1["DependencyResolutionError"] = "DependencyResolutionError";
|
|
181
243
|
DIErrorCode$1["UnknownError"] = "UnknownError";
|
|
182
244
|
return DIErrorCode$1;
|
|
183
245
|
}({});
|
|
184
246
|
var DIError = class DIError extends Error {
|
|
247
|
+
code;
|
|
248
|
+
message;
|
|
185
249
|
context;
|
|
186
250
|
constructor(code, message, context) {
|
|
187
|
-
super(message);
|
|
188
|
-
this.code = code;
|
|
189
|
-
this.message = message;
|
|
251
|
+
super(message), this.code = code, this.message = message;
|
|
190
252
|
this.context = context;
|
|
253
|
+
this.name = "DIError";
|
|
191
254
|
}
|
|
192
255
|
static factoryNotFound(name) {
|
|
193
|
-
return new DIError(
|
|
256
|
+
return new DIError("FactoryNotFound", `Factory ${name} not found`, { name });
|
|
194
257
|
}
|
|
195
258
|
static factoryTokenNotResolved(token) {
|
|
196
|
-
return new DIError(
|
|
259
|
+
return new DIError("FactoryTokenNotResolved", `Factory token not resolved: ${token?.toString() ?? "unknown"}`, { token });
|
|
197
260
|
}
|
|
198
261
|
static instanceNotFound(name) {
|
|
199
|
-
return new DIError(
|
|
262
|
+
return new DIError("InstanceNotFound", `Instance ${name} not found`, { name });
|
|
200
263
|
}
|
|
201
264
|
static instanceDestroying(name) {
|
|
202
|
-
return new DIError(
|
|
265
|
+
return new DIError("InstanceDestroying", `Instance ${name} destroying`, { name });
|
|
203
266
|
}
|
|
204
267
|
static unknown(message, context) {
|
|
205
|
-
if (message instanceof Error) return new DIError(
|
|
268
|
+
if (message instanceof Error) return new DIError("UnknownError", message.message, {
|
|
206
269
|
...context,
|
|
207
270
|
parent: message
|
|
208
271
|
});
|
|
209
|
-
return new DIError(
|
|
272
|
+
return new DIError("UnknownError", message, context);
|
|
210
273
|
}
|
|
211
274
|
static circularDependency(cycle) {
|
|
212
|
-
|
|
213
|
-
|
|
275
|
+
return new DIError("CircularDependency", `Circular dependency detected: ${cycle.join(" -> ")}`, { cycle });
|
|
276
|
+
}
|
|
277
|
+
static tokenValidationError(message, schema, value) {
|
|
278
|
+
return new DIError("TokenValidationError", message, {
|
|
279
|
+
schema,
|
|
280
|
+
value
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
static tokenSchemaRequiredError(token) {
|
|
284
|
+
return new DIError("TokenSchemaRequiredError", `Token ${token?.toString() ?? "unknown"} requires schema arguments and cannot be used with addInstance. Use BoundInjectionToken or provide arguments when resolving.`, { token });
|
|
285
|
+
}
|
|
286
|
+
static classNotInjectable(className) {
|
|
287
|
+
return new DIError("ClassNotInjectable", `Class ${className} is not decorated with @Injectable.`, { className });
|
|
288
|
+
}
|
|
289
|
+
static scopeMismatchError(token, expectedScope, actualScope) {
|
|
290
|
+
return new DIError("ScopeMismatchError", `Scope mismatch for ${token?.toString() ?? "unknown"}: expected ${expectedScope}, got ${actualScope}`, {
|
|
291
|
+
token,
|
|
292
|
+
expectedScope,
|
|
293
|
+
actualScope
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
static priorityConflictError(token, records) {
|
|
297
|
+
return new DIError("PriorityConflictError", `Priority conflict for ${token?.toString() ?? "unknown"}: multiple bindings with same priority`, {
|
|
298
|
+
token,
|
|
299
|
+
records
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
static storageError(message, operation, instanceName) {
|
|
303
|
+
return new DIError("StorageError", `Storage error: ${message}`, {
|
|
304
|
+
operation,
|
|
305
|
+
instanceName
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
static initializationError(serviceName, error) {
|
|
309
|
+
return new DIError("InitializationError", `Service ${serviceName} initialization failed: ${error instanceof Error ? error.message : error}`, {
|
|
310
|
+
serviceName,
|
|
311
|
+
error
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
static dependencyResolutionError(serviceName, dependencyName, error) {
|
|
315
|
+
return new DIError("DependencyResolutionError", `Failed to resolve dependency ${dependencyName} for service ${serviceName}: ${error instanceof Error ? error.message : error}`, {
|
|
316
|
+
serviceName,
|
|
317
|
+
dependencyName,
|
|
318
|
+
error
|
|
319
|
+
});
|
|
214
320
|
}
|
|
215
321
|
};
|
|
216
322
|
|
|
@@ -240,6 +346,9 @@ function getModule() {
|
|
|
240
346
|
function createAsyncLocalStorage() {
|
|
241
347
|
return getModule().createAsyncLocalStorage();
|
|
242
348
|
}
|
|
349
|
+
function isUsingNativeAsyncLocalStorage() {
|
|
350
|
+
return getModule().isUsingNativeAsyncLocalStorage();
|
|
351
|
+
}
|
|
243
352
|
|
|
244
353
|
//#endregion
|
|
245
354
|
//#region src/internal/context/resolution-context.mts
|
|
@@ -294,135 +403,21 @@ function getResolutionContext() {
|
|
|
294
403
|
}
|
|
295
404
|
|
|
296
405
|
//#endregion
|
|
297
|
-
//#region src/
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (!injectState) throw new Error("[Injector] Trying to make a request outside of a injectable context");
|
|
313
|
-
if (injectState.isFrozen) {
|
|
314
|
-
const idx = injectState.currentIndex++;
|
|
315
|
-
const request$1 = injectState.requests[idx];
|
|
316
|
-
if (request$1.token !== token) throw new Error(`[Injector] Wrong token order. Expected ${request$1.token.toString()} but got ${token.toString()}`);
|
|
317
|
-
return request$1;
|
|
318
|
-
}
|
|
319
|
-
let result = null;
|
|
320
|
-
let error = null;
|
|
321
|
-
const doInject = () => getFactoryContext().inject(token, args).then((r) => {
|
|
322
|
-
result = r;
|
|
323
|
-
return r;
|
|
324
|
-
}).catch((e) => {
|
|
325
|
-
error = e;
|
|
326
|
-
});
|
|
327
|
-
const request = {
|
|
328
|
-
token,
|
|
329
|
-
promise: skipCycleTracking ? withoutResolutionContext(doInject) : doInject(),
|
|
330
|
-
get result() {
|
|
331
|
-
return result;
|
|
332
|
-
},
|
|
333
|
-
get error() {
|
|
334
|
-
return error;
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
|
-
injectState.requests.push(request);
|
|
338
|
-
injectState.currentIndex++;
|
|
339
|
-
return request;
|
|
340
|
-
}
|
|
341
|
-
function asyncInject$1(token, args) {
|
|
342
|
-
if (!injectState) throw new Error("[Injector] Trying to access inject outside of a injectable context");
|
|
343
|
-
const request = getRequest(token[InjectableTokenMeta] ?? token, args, true);
|
|
344
|
-
return request.promise.then((result) => {
|
|
345
|
-
if (request.error) throw request.error;
|
|
346
|
-
return result;
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
function wrapSyncInit$1(cb) {
|
|
350
|
-
return (previousState) => {
|
|
351
|
-
const promises = [];
|
|
352
|
-
const originalPromiseCollector = promiseCollector;
|
|
353
|
-
const originalInjectState = injectState;
|
|
354
|
-
injectState = previousState ? {
|
|
355
|
-
...previousState,
|
|
356
|
-
currentIndex: 0
|
|
357
|
-
} : {
|
|
358
|
-
currentIndex: 0,
|
|
359
|
-
isFrozen: false,
|
|
360
|
-
requests: []
|
|
361
|
-
};
|
|
362
|
-
promiseCollector = (promise) => {
|
|
363
|
-
promises.push(promise);
|
|
364
|
-
};
|
|
365
|
-
const result = cb();
|
|
366
|
-
promiseCollector = originalPromiseCollector;
|
|
367
|
-
const newInjectState = {
|
|
368
|
-
...injectState,
|
|
369
|
-
isFrozen: true
|
|
370
|
-
};
|
|
371
|
-
injectState = originalInjectState;
|
|
372
|
-
return [
|
|
373
|
-
result,
|
|
374
|
-
promises,
|
|
375
|
-
newInjectState
|
|
376
|
-
];
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
function inject$1(token, args) {
|
|
380
|
-
const realToken = token[InjectableTokenMeta] ?? token;
|
|
381
|
-
if (!injectState) throw new Error("[Injector] Trying to access inject outside of a injectable context");
|
|
382
|
-
const instance = getFactoryContext().container.tryGetSync(realToken, args);
|
|
383
|
-
if (!instance) {
|
|
384
|
-
const request = getRequest(realToken, args);
|
|
385
|
-
if (request.error) throw request.error;
|
|
386
|
-
else if (request.result) return request.result;
|
|
387
|
-
if (promiseCollector) promiseCollector(request.promise);
|
|
388
|
-
return new Proxy({}, { get() {
|
|
389
|
-
throw new Error(`[Injector] Trying to access ${realToken.toString()} before it's initialized, please move the code to a onServiceInit method`);
|
|
390
|
-
} });
|
|
391
|
-
}
|
|
392
|
-
return instance;
|
|
393
|
-
}
|
|
394
|
-
function optional$1(token, args) {
|
|
395
|
-
try {
|
|
396
|
-
return inject$1(token, args);
|
|
397
|
-
} catch {
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return {
|
|
402
|
-
asyncInject: asyncInject$1,
|
|
403
|
-
inject: inject$1,
|
|
404
|
-
optional: optional$1,
|
|
405
|
-
wrapSyncInit: wrapSyncInit$1,
|
|
406
|
-
provideFactoryContext: provideFactoryContext$1
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
//#endregion
|
|
411
|
-
//#region src/utils/get-injectable-token.mts
|
|
412
|
-
function getInjectableToken(target) {
|
|
413
|
-
const token = target[InjectableTokenMeta];
|
|
414
|
-
if (!token) throw new Error(`[ServiceLocator] Class ${target.name} is not decorated with @Injectable.`);
|
|
415
|
-
return token;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
//#endregion
|
|
419
|
-
//#region src/injectors.mts
|
|
420
|
-
const defaultInjectors = getInjectors();
|
|
421
|
-
const asyncInject = defaultInjectors.asyncInject;
|
|
422
|
-
const inject = defaultInjectors.inject;
|
|
423
|
-
const optional = defaultInjectors.optional;
|
|
424
|
-
const wrapSyncInit = defaultInjectors.wrapSyncInit;
|
|
425
|
-
const provideFactoryContext = defaultInjectors.provideFactoryContext;
|
|
406
|
+
//#region src/internal/holder/instance-holder.mts
|
|
407
|
+
/**
|
|
408
|
+
* Represents the lifecycle status of an instance holder.
|
|
409
|
+
*/
|
|
410
|
+
let InstanceStatus = /* @__PURE__ */ function(InstanceStatus$1) {
|
|
411
|
+
/** Instance has been successfully created and is ready for use */
|
|
412
|
+
InstanceStatus$1["Created"] = "created";
|
|
413
|
+
/** Instance is currently being created (async initialization in progress) */
|
|
414
|
+
InstanceStatus$1["Creating"] = "creating";
|
|
415
|
+
/** Instance is being destroyed (cleanup in progress) */
|
|
416
|
+
InstanceStatus$1["Destroying"] = "destroying";
|
|
417
|
+
/** Instance creation failed with an error */
|
|
418
|
+
InstanceStatus$1["Error"] = "error";
|
|
419
|
+
return InstanceStatus$1;
|
|
420
|
+
}({});
|
|
426
421
|
|
|
427
422
|
//#endregion
|
|
428
423
|
//#region src/internal/lifecycle/circular-detector.mts
|
|
@@ -483,200 +478,249 @@ const provideFactoryContext = defaultInjectors.provideFactoryContext;
|
|
|
483
478
|
};
|
|
484
479
|
|
|
485
480
|
//#endregion
|
|
486
|
-
//#region src/internal/
|
|
487
|
-
/**
|
|
488
|
-
* Represents the lifecycle status of an instance holder.
|
|
489
|
-
*/
|
|
490
|
-
let InstanceStatus = /* @__PURE__ */ function(InstanceStatus$1) {
|
|
491
|
-
/** Instance has been successfully created and is ready for use */
|
|
492
|
-
InstanceStatus$1["Created"] = "created";
|
|
493
|
-
/** Instance is currently being created (async initialization in progress) */
|
|
494
|
-
InstanceStatus$1["Creating"] = "creating";
|
|
495
|
-
/** Instance is being destroyed (cleanup in progress) */
|
|
496
|
-
InstanceStatus$1["Destroying"] = "destroying";
|
|
497
|
-
/** Instance creation failed with an error */
|
|
498
|
-
InstanceStatus$1["Error"] = "error";
|
|
499
|
-
return InstanceStatus$1;
|
|
500
|
-
}({});
|
|
501
|
-
|
|
502
|
-
//#endregion
|
|
503
|
-
//#region src/internal/holder/base-holder-manager.mts
|
|
481
|
+
//#region src/internal/core/instance-resolver.mts
|
|
504
482
|
/**
|
|
505
|
-
*
|
|
483
|
+
* Resolves instances from tokens, handling caching, creation, and scope rules.
|
|
506
484
|
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
|
|
485
|
+
* Uses unified storage for both singleton and request-scoped services.
|
|
486
|
+
* Coordinates with ServiceInitializer for actual service creation.
|
|
487
|
+
* Integrates ScopeTracker for automatic scope upgrades.
|
|
488
|
+
*/ var InstanceResolver = class {
|
|
489
|
+
registry;
|
|
490
|
+
storage;
|
|
491
|
+
serviceInitializer;
|
|
492
|
+
tokenResolver;
|
|
493
|
+
nameResolver;
|
|
494
|
+
scopeTracker;
|
|
495
|
+
serviceInvalidator;
|
|
496
|
+
eventBus;
|
|
510
497
|
logger;
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
498
|
+
constructor(registry, storage, serviceInitializer, tokenResolver, nameResolver, scopeTracker, serviceInvalidator, eventBus, logger = null) {
|
|
499
|
+
this.registry = registry;
|
|
500
|
+
this.storage = storage;
|
|
501
|
+
this.serviceInitializer = serviceInitializer;
|
|
502
|
+
this.tokenResolver = tokenResolver;
|
|
503
|
+
this.nameResolver = nameResolver;
|
|
504
|
+
this.scopeTracker = scopeTracker;
|
|
505
|
+
this.serviceInvalidator = serviceInvalidator;
|
|
506
|
+
this.eventBus = eventBus;
|
|
517
507
|
this.logger = logger;
|
|
518
|
-
this._holders = /* @__PURE__ */ new Map();
|
|
519
|
-
this._dependents = /* @__PURE__ */ new Map();
|
|
520
508
|
}
|
|
521
509
|
/**
|
|
522
|
-
*
|
|
523
|
-
|
|
524
|
-
|
|
510
|
+
* Resolves an instance for the given token and arguments.
|
|
511
|
+
* This method is used for singleton and transient services.
|
|
512
|
+
*
|
|
513
|
+
* @param token The injection token
|
|
514
|
+
* @param args Optional arguments
|
|
515
|
+
* @param contextContainer The container to use for creating context
|
|
516
|
+
* @param requestStorage Optional request storage (for scope upgrades)
|
|
517
|
+
* @param requestId Optional request ID (for scope upgrades)
|
|
518
|
+
*/ async resolveInstance(token, args, contextContainer, requestStorage, requestId) {
|
|
519
|
+
return this.resolveWithStorage(token, args, contextContainer, this.storage, void 0, requestStorage, requestId);
|
|
525
520
|
}
|
|
526
521
|
/**
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
522
|
+
* Resolves a request-scoped instance for a ScopedContainer.
|
|
523
|
+
* The service will be stored in the ScopedContainer's request storage.
|
|
524
|
+
*
|
|
525
|
+
* @param token The injection token
|
|
526
|
+
* @param args Optional arguments
|
|
527
|
+
* @param scopedContainer The ScopedContainer that owns the request context
|
|
528
|
+
*/ async resolveRequestScopedInstance(token, args, scopedContainer) {
|
|
529
|
+
return this.resolveWithStorage(token, args, scopedContainer.getParent(), scopedContainer.getParent().getStorage(), scopedContainer, scopedContainer.getStorage(), scopedContainer.getRequestId());
|
|
534
530
|
}
|
|
535
531
|
/**
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
532
|
+
* Unified resolution method that works with any IHolderStorage.
|
|
533
|
+
* This eliminates duplication between singleton and request-scoped resolution.
|
|
534
|
+
*
|
|
535
|
+
* IMPORTANT: The check-and-store logic is carefully designed to avoid race conditions.
|
|
536
|
+
* The storage check and holder creation must happen synchronously (no awaits between).
|
|
537
|
+
*
|
|
538
|
+
* @param token The injection token
|
|
539
|
+
* @param args Optional arguments
|
|
540
|
+
* @param contextContainer The container for context
|
|
541
|
+
* @param storage The storage strategy to use
|
|
542
|
+
* @param scopedContainer Optional scoped container for request-scoped services
|
|
543
|
+
* @param requestStorage Optional request storage (for scope upgrades)
|
|
544
|
+
* @param requestId Optional request ID (for scope upgrades)
|
|
545
|
+
*/ async resolveWithStorage(token, args, contextContainer, storage, scopedContainer, requestStorage, requestId) {
|
|
546
|
+
const [err, data] = await this.resolveTokenAndPrepareInstanceName(token, args, contextContainer, requestId, scopedContainer);
|
|
547
|
+
if (err) return [err];
|
|
548
|
+
const { instanceName, validatedArgs, realToken, scope } = data;
|
|
549
|
+
const getResult = storage.get(instanceName) ?? requestStorage?.get(instanceName) ?? null;
|
|
550
|
+
const getHolder = (name) => {
|
|
551
|
+
const result = storage.get(name);
|
|
552
|
+
if (result && result[0] === void 0 && result[1]) return result[1];
|
|
553
|
+
if (requestStorage) {
|
|
554
|
+
const reqResult = requestStorage.get(name);
|
|
555
|
+
if (reqResult && reqResult[0] === void 0 && reqResult[1]) return reqResult[1];
|
|
546
556
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
if (dependents.size === 0) this._dependents.delete(dep);
|
|
557
|
+
};
|
|
558
|
+
if (getResult !== null) {
|
|
559
|
+
const [error, holder$1] = getResult;
|
|
560
|
+
if (!error && holder$1) {
|
|
561
|
+
const waiterHolder = getCurrentResolutionContext()?.waiterHolder;
|
|
562
|
+
const readyResult = await this.waitForInstanceReady(holder$1, waiterHolder, getHolder);
|
|
563
|
+
if (readyResult[0]) return [readyResult[0]];
|
|
564
|
+
return [void 0, readyResult[1].instance];
|
|
565
|
+
}
|
|
566
|
+
if (error) {
|
|
567
|
+
const handledResult = await this.handleStorageError(instanceName, error, holder$1, storage);
|
|
568
|
+
if (handledResult) return handledResult;
|
|
560
569
|
}
|
|
561
570
|
}
|
|
571
|
+
const [createError, holder] = await this.createAndStoreInstance(instanceName, realToken, validatedArgs, contextContainer, storage, scopedContainer, requestStorage, requestId, scope);
|
|
572
|
+
if (createError) return [createError];
|
|
573
|
+
return [void 0, holder.instance];
|
|
562
574
|
}
|
|
563
575
|
/**
|
|
564
|
-
*
|
|
565
|
-
*
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Finds the first holder matching a predicate. More efficient than filter()
|
|
591
|
-
* when only one result is needed.
|
|
592
|
-
* @param predicate Function to test each holder
|
|
593
|
-
* @returns The first matching holder or undefined
|
|
594
|
-
*/ findHolder(predicate) {
|
|
595
|
-
for (const [name, holder] of this._holders) if (predicate(holder, name)) return holder;
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* Clears all holders from this manager and the reverse dependency index.
|
|
599
|
-
*/ clear() {
|
|
600
|
-
this._holders.clear();
|
|
601
|
-
this._dependents.clear();
|
|
576
|
+
* Internal method to resolve token args and create instance name.
|
|
577
|
+
* Handles factory token resolution and validation.
|
|
578
|
+
*/ async resolveTokenAndPrepareInstanceName(token, args, contextContainer, requestId, scopedContainer) {
|
|
579
|
+
const [err, { actualToken, validatedArgs }] = this.tokenResolver.validateAndResolveTokenArgs(token, args);
|
|
580
|
+
if (err instanceof DIError && err.code === DIErrorCode.TokenValidationError) return [err];
|
|
581
|
+
else if (err instanceof DIError && err.code === DIErrorCode.FactoryTokenNotResolved && actualToken instanceof FactoryInjectionToken) {
|
|
582
|
+
this.logger?.log(`[InstanceResolver]#resolveTokenAndPrepareInstanceName() Factory token not resolved, resolving it`);
|
|
583
|
+
const factoryCtx = {
|
|
584
|
+
inject: async (t, a) => (scopedContainer ?? contextContainer).get(t, a),
|
|
585
|
+
container: scopedContainer ?? contextContainer,
|
|
586
|
+
addDestroyListener: () => {}
|
|
587
|
+
};
|
|
588
|
+
await actualToken.resolve(factoryCtx);
|
|
589
|
+
return this.resolveTokenAndPrepareInstanceName(token, void 0, contextContainer, requestId, scopedContainer);
|
|
590
|
+
}
|
|
591
|
+
const realToken = this.tokenResolver.getRealToken(actualToken);
|
|
592
|
+
const scope = this.registry.get(realToken).scope;
|
|
593
|
+
return [void 0, {
|
|
594
|
+
instanceName: this.nameResolver.generateInstanceName(actualToken, validatedArgs, requestId, scope),
|
|
595
|
+
validatedArgs,
|
|
596
|
+
actualToken,
|
|
597
|
+
realToken,
|
|
598
|
+
scope
|
|
599
|
+
}];
|
|
602
600
|
}
|
|
603
601
|
/**
|
|
604
|
-
*
|
|
605
|
-
|
|
606
|
-
|
|
602
|
+
* Handles storage error states (destroying, error, etc.).
|
|
603
|
+
* Returns a result if handled, null if should proceed with creation.
|
|
604
|
+
*/ async handleStorageError(instanceName, error, holder, storage) {
|
|
605
|
+
switch (error.code) {
|
|
606
|
+
case DIErrorCode.InstanceDestroying:
|
|
607
|
+
this.logger?.log(`[InstanceResolver] Instance ${instanceName} is being destroyed, waiting...`);
|
|
608
|
+
if (holder?.destroyPromise) await holder.destroyPromise;
|
|
609
|
+
const newResult = storage.get(instanceName);
|
|
610
|
+
if (newResult !== null && !newResult[0]) {
|
|
611
|
+
const getHolder = (name) => {
|
|
612
|
+
const result = storage.get(name);
|
|
613
|
+
return result && result[0] === void 0 && result[1] ? result[1] : void 0;
|
|
614
|
+
};
|
|
615
|
+
const readyResult = await this.waitForInstanceReady(newResult[1], void 0, getHolder);
|
|
616
|
+
if (readyResult[0]) return [readyResult[0]];
|
|
617
|
+
return [void 0, readyResult[1].instance];
|
|
618
|
+
}
|
|
619
|
+
return null;
|
|
620
|
+
default:
|
|
621
|
+
if (holder) {
|
|
622
|
+
this.logger?.log(`[InstanceResolver] Removing failed instance ${instanceName} from storage to allow retry`);
|
|
623
|
+
storage.delete(instanceName);
|
|
624
|
+
}
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
607
627
|
}
|
|
608
628
|
/**
|
|
609
|
-
* Creates a new
|
|
610
|
-
* This
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
629
|
+
* Creates a new instance and stores it using the provided storage strategy.
|
|
630
|
+
* This unified method replaces instantiateServiceFromRegistry and createRequestScopedInstance.
|
|
631
|
+
*
|
|
632
|
+
* For transient services, the instance is created but not stored (no caching).
|
|
633
|
+
*/ async createAndStoreInstance(instanceName, realToken, args, contextContainer, storage, scopedContainer, requestStorage, requestId, scope) {
|
|
634
|
+
this.logger?.log(`[InstanceResolver]#createAndStoreInstance() Creating instance for ${instanceName}`);
|
|
635
|
+
if (!this.registry.has(realToken)) return [DIError.factoryNotFound(realToken.name.toString())];
|
|
636
|
+
const record = this.registry.get(realToken);
|
|
637
|
+
const { type, scope: recordScope } = record;
|
|
638
|
+
const serviceScope = scope || recordScope;
|
|
639
|
+
if (serviceScope === InjectableScope.Transient) return this.createTransientInstance(instanceName, record, args, contextContainer, scopedContainer, requestStorage, requestId);
|
|
640
|
+
if (serviceScope === InjectableScope.Request && !requestStorage) return [DIError.initializationError(`Request storage is required for request-scoped services`, instanceName)];
|
|
641
|
+
let storageToUse;
|
|
642
|
+
if (serviceScope === InjectableScope.Request) storageToUse = requestStorage;
|
|
643
|
+
else storageToUse = storage;
|
|
644
|
+
const [deferred, holder] = storageToUse.createHolder(instanceName, type, /* @__PURE__ */ new Set());
|
|
645
|
+
storageToUse.set(instanceName, holder);
|
|
646
|
+
const ctx = this.createServiceInitializationContext(scopedContainer ?? contextContainer, instanceName, serviceScope, holder.deps, realToken, requestStorage, requestId);
|
|
647
|
+
holder.destroyListeners = ctx.getDestroyListeners();
|
|
648
|
+
const getHolder = (name) => {
|
|
649
|
+
const result = storage.get(name);
|
|
650
|
+
if (result && result[0] === void 0 && result[1]) return result[1];
|
|
651
|
+
if (requestStorage) {
|
|
652
|
+
const reqResult = requestStorage.get(name);
|
|
653
|
+
if (reqResult && reqResult[0] === void 0 && reqResult[1]) return reqResult[1];
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
withResolutionContext(holder, getHolder, () => {
|
|
657
|
+
this.serviceInitializer.instantiateService(ctx, record, args).then(async (result) => {
|
|
658
|
+
const [error, instance] = result.length === 2 ? result : [result[0], void 0];
|
|
659
|
+
const newScope = record.scope;
|
|
660
|
+
const newName = this.nameResolver.generateInstanceName(realToken, args, requestId, newScope);
|
|
661
|
+
await this.handleInstantiationResult(newName, holder, ctx, deferred, newScope, error, instance, scopedContainer, requestStorage, requestId);
|
|
662
|
+
}).catch(async (error) => {
|
|
663
|
+
const newScope = record.scope;
|
|
664
|
+
const newName = this.nameResolver.generateInstanceName(realToken, args, requestId, newScope);
|
|
665
|
+
await this.handleInstantiationError(newName, holder, deferred, newScope, error);
|
|
666
|
+
}).catch(() => {});
|
|
667
|
+
});
|
|
668
|
+
const waiterHolder = getCurrentResolutionContext()?.waiterHolder;
|
|
669
|
+
return this.waitForInstanceReady(holder, waiterHolder, getHolder);
|
|
631
670
|
}
|
|
632
671
|
/**
|
|
633
|
-
* Creates a
|
|
634
|
-
*
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
*/ createCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()) {
|
|
642
|
-
return {
|
|
672
|
+
* Creates a transient instance without storage or locking.
|
|
673
|
+
* Each call creates a new instance.
|
|
674
|
+
*/ async createTransientInstance(instanceName, record, args, contextContainer, scopedContainer, requestStorage, requestId) {
|
|
675
|
+
this.logger?.log(`[InstanceResolver]#createTransientInstance() Creating transient instance for ${instanceName}`);
|
|
676
|
+
const ctx = this.createServiceInitializationContext(scopedContainer ?? contextContainer, instanceName, InjectableScope.Transient, /* @__PURE__ */ new Set(), record.originalToken, requestStorage, requestId);
|
|
677
|
+
const [error, instance] = await this.serviceInitializer.instantiateService(ctx, record, args);
|
|
678
|
+
if (error) return [error];
|
|
679
|
+
return [void 0, {
|
|
643
680
|
status: InstanceStatus.Created,
|
|
644
|
-
name,
|
|
681
|
+
name: instanceName,
|
|
645
682
|
instance,
|
|
646
683
|
creationPromise: null,
|
|
647
684
|
destroyPromise: null,
|
|
648
|
-
type,
|
|
649
|
-
scope,
|
|
650
|
-
deps,
|
|
651
|
-
destroyListeners:
|
|
685
|
+
type: record.type,
|
|
686
|
+
scope: InjectableScope.Transient,
|
|
687
|
+
deps: ctx.dependencies,
|
|
688
|
+
destroyListeners: ctx.getDestroyListeners(),
|
|
652
689
|
createdAt: Date.now(),
|
|
653
690
|
waitingFor: /* @__PURE__ */ new Set()
|
|
654
|
-
};
|
|
691
|
+
}];
|
|
655
692
|
}
|
|
656
693
|
/**
|
|
657
|
-
*
|
|
658
|
-
*/
|
|
659
|
-
|
|
694
|
+
* Handles successful service instantiation.
|
|
695
|
+
*/ async handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance, _scopedContainer, requestStorage, _requestId) {
|
|
696
|
+
holder.instance = instance;
|
|
697
|
+
holder.status = InstanceStatus.Created;
|
|
698
|
+
const storageForSubscriptions = requestStorage || this.storage;
|
|
699
|
+
if (ctx.dependencies.size > 0) this.serviceInvalidator.setupDependencySubscriptions(instanceName, ctx.dependencies, storageForSubscriptions, holder);
|
|
700
|
+
this.logger?.log(`[InstanceResolver] Instance ${instanceName} created successfully`);
|
|
701
|
+
deferred.resolve([void 0, instance]);
|
|
660
702
|
}
|
|
661
703
|
/**
|
|
662
|
-
*
|
|
663
|
-
*/
|
|
664
|
-
|
|
704
|
+
* Handles service instantiation errors.
|
|
705
|
+
*/ async handleInstantiationError(instanceName, holder, deferred, scope, error) {
|
|
706
|
+
holder.status = InstanceStatus.Error;
|
|
707
|
+
holder.instance = error instanceof DIError ? error : DIError.unknown(error);
|
|
708
|
+
this.logger?.error(`[InstanceResolver] Instance ${instanceName} creation failed:`, error);
|
|
709
|
+
deferred.reject(error instanceof DIError ? error : DIError.unknown(error));
|
|
665
710
|
}
|
|
666
711
|
/**
|
|
667
|
-
*
|
|
668
|
-
*/
|
|
669
|
-
|
|
712
|
+
* Handles instantiation result (success or error).
|
|
713
|
+
*/ async handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance, scopedContainer, requestStorage, requestId) {
|
|
714
|
+
if (error) await this.handleInstantiationError(instanceName, holder, deferred, scope, error);
|
|
715
|
+
else await this.handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance, scopedContainer, requestStorage, requestId);
|
|
670
716
|
}
|
|
671
717
|
/**
|
|
672
|
-
* Waits for
|
|
673
|
-
* This is a shared utility used by both singleton and request-scoped resolution.
|
|
718
|
+
* Waits for an instance holder to be ready and returns the appropriate result.
|
|
674
719
|
*
|
|
675
720
|
* @param holder The holder to wait for
|
|
676
721
|
* @param waiterHolder Optional holder that is doing the waiting (for circular dependency detection)
|
|
677
722
|
* @param getHolder Optional function to retrieve holders by name (required if waiterHolder is provided)
|
|
678
|
-
|
|
679
|
-
*/ static async waitForHolderReady(holder, waiterHolder, getHolder) {
|
|
723
|
+
*/ async waitForInstanceReady(holder, waiterHolder, getHolder) {
|
|
680
724
|
switch (holder.status) {
|
|
681
725
|
case InstanceStatus.Creating:
|
|
682
726
|
if (waiterHolder && getHolder) {
|
|
@@ -691,642 +735,303 @@ let InstanceStatus = /* @__PURE__ */ function(InstanceStatus$1) {
|
|
|
691
735
|
if (waiterHolder) waiterHolder.waitingFor.delete(holder.name);
|
|
692
736
|
}
|
|
693
737
|
}
|
|
694
|
-
return
|
|
738
|
+
return this.waitForInstanceReady(holder, waiterHolder, getHolder);
|
|
695
739
|
case InstanceStatus.Destroying: return [DIError.instanceDestroying(holder.name)];
|
|
696
740
|
case InstanceStatus.Error: return [holder.instance];
|
|
697
741
|
case InstanceStatus.Created: return [void 0, holder];
|
|
698
|
-
default: return [DIError.instanceNotFound("unknown")];
|
|
742
|
+
default: return [DIError.instanceNotFound(holder?.name ?? "unknown")];
|
|
699
743
|
}
|
|
700
744
|
}
|
|
745
|
+
/**
|
|
746
|
+
* Creates a ServiceInitializationContext for service instantiation.
|
|
747
|
+
*/ createServiceInitializationContext(container, serviceName, scope, deps, serviceToken, requestStorage, requestId) {
|
|
748
|
+
const destroyListeners = [];
|
|
749
|
+
return {
|
|
750
|
+
inject: async (token, args) => {
|
|
751
|
+
const actualToken = typeof token === "function" ? this.tokenResolver.normalizeToken(token) : token;
|
|
752
|
+
const realToken = this.tokenResolver.getRealToken(actualToken);
|
|
753
|
+
const depScope = this.registry.get(realToken).scope;
|
|
754
|
+
const dependencyRequestId = depScope === InjectableScope.Request ? requestId : void 0;
|
|
755
|
+
const finalDepName = this.nameResolver.generateInstanceName(actualToken, args, dependencyRequestId, depScope);
|
|
756
|
+
if (scope === InjectableScope.Singleton && depScope === InjectableScope.Request && requestStorage && requestId) {
|
|
757
|
+
const [needsUpgrade, newServiceName] = this.scopeTracker.checkAndUpgradeScope(serviceName, scope, finalDepName, depScope, serviceToken, this.storage, requestStorage, requestId);
|
|
758
|
+
if (needsUpgrade && newServiceName) {}
|
|
759
|
+
}
|
|
760
|
+
deps.add(finalDepName);
|
|
761
|
+
return container.get(token, args);
|
|
762
|
+
},
|
|
763
|
+
container,
|
|
764
|
+
addDestroyListener: (listener) => {
|
|
765
|
+
destroyListeners.push(listener);
|
|
766
|
+
},
|
|
767
|
+
getDestroyListeners: () => destroyListeners,
|
|
768
|
+
serviceName,
|
|
769
|
+
dependencies: deps,
|
|
770
|
+
scope,
|
|
771
|
+
trackDependency: (name, depScope) => {
|
|
772
|
+
deps.add(name);
|
|
773
|
+
if (scope === InjectableScope.Singleton && depScope === InjectableScope.Request && requestStorage && requestId) this.scopeTracker.checkAndUpgradeScope(serviceName, scope, name, depScope, serviceToken, this.storage, requestStorage, requestId);
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
}
|
|
701
777
|
};
|
|
702
778
|
|
|
703
779
|
//#endregion
|
|
704
|
-
//#region src/internal/
|
|
780
|
+
//#region src/internal/core/name-resolver.mts
|
|
705
781
|
/**
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
metadata = /* @__PURE__ */ new Map();
|
|
714
|
-
createdAt = Date.now();
|
|
715
|
-
constructor(requestId, priority = 100, initialMetadata) {
|
|
716
|
-
super(null), this.requestId = requestId, this.priority = priority;
|
|
717
|
-
if (initialMetadata) Object.entries(initialMetadata).forEach(([key, value]) => {
|
|
718
|
-
this.metadata.set(key, value);
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
/**
|
|
722
|
-
* Public getter for holders to maintain interface compatibility.
|
|
723
|
-
*/ get holders() {
|
|
724
|
-
return this._holders;
|
|
725
|
-
}
|
|
726
|
-
/**
|
|
727
|
-
* Gets a holder by name. For RequestContext, this is a simple lookup.
|
|
728
|
-
*/ get(name) {
|
|
729
|
-
return this._holders.get(name);
|
|
730
|
-
}
|
|
731
|
-
/**
|
|
732
|
-
* Sets a holder by name.
|
|
733
|
-
*/ set(name, holder) {
|
|
734
|
-
this._holders.set(name, holder);
|
|
782
|
+
* Simple LRU cache for instance name generation.
|
|
783
|
+
* Uses a Map which maintains insertion order for efficient LRU eviction.
|
|
784
|
+
*/ var InstanceNameCache = class {
|
|
785
|
+
cache = /* @__PURE__ */ new Map();
|
|
786
|
+
maxSize;
|
|
787
|
+
constructor(maxSize = 1e3) {
|
|
788
|
+
this.maxSize = maxSize;
|
|
735
789
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
790
|
+
get(key) {
|
|
791
|
+
const value = this.cache.get(key);
|
|
792
|
+
if (value !== void 0) {
|
|
793
|
+
this.cache.delete(key);
|
|
794
|
+
this.cache.set(key, value);
|
|
795
|
+
}
|
|
796
|
+
return value;
|
|
740
797
|
}
|
|
741
|
-
|
|
742
|
-
if (
|
|
743
|
-
|
|
744
|
-
const
|
|
745
|
-
this.
|
|
746
|
-
} else {
|
|
747
|
-
if (!holder) throw new Error("Holder is required when adding an instance by name");
|
|
748
|
-
this._holders.set(instanceName, holder);
|
|
798
|
+
set(key, value) {
|
|
799
|
+
if (this.cache.has(key)) this.cache.delete(key);
|
|
800
|
+
else if (this.cache.size >= this.maxSize) {
|
|
801
|
+
const firstKey = this.cache.keys().next().value;
|
|
802
|
+
if (firstKey !== void 0) this.cache.delete(firstKey);
|
|
749
803
|
}
|
|
804
|
+
this.cache.set(key, value);
|
|
750
805
|
}
|
|
751
806
|
clear() {
|
|
752
|
-
|
|
753
|
-
this.metadata.clear();
|
|
754
|
-
}
|
|
755
|
-
getMetadata(key) {
|
|
756
|
-
return this.metadata.get(key);
|
|
757
|
-
}
|
|
758
|
-
setMetadata(key, value) {
|
|
759
|
-
this.metadata.set(key, value);
|
|
807
|
+
this.cache.clear();
|
|
760
808
|
}
|
|
761
809
|
};
|
|
762
810
|
/**
|
|
763
|
-
*
|
|
764
|
-
*/ function
|
|
765
|
-
|
|
811
|
+
* Simple hash function for deterministic hashing of arguments
|
|
812
|
+
*/ function hashArgs(args) {
|
|
813
|
+
const str = JSON.stringify(args, Object.keys(args || {}).sort());
|
|
814
|
+
let hash = 0;
|
|
815
|
+
for (let i = 0; i < str.length; i++) {
|
|
816
|
+
const char = str.charCodeAt(i);
|
|
817
|
+
hash = (hash << 5) - hash + char;
|
|
818
|
+
hash = hash & hash;
|
|
819
|
+
}
|
|
820
|
+
return Math.abs(hash).toString(36);
|
|
766
821
|
}
|
|
767
|
-
|
|
768
|
-
//#endregion
|
|
769
|
-
//#region src/internal/holder/request-storage.mts
|
|
770
822
|
/**
|
|
771
|
-
*
|
|
823
|
+
* Handles instance name generation with support for requestId and scope.
|
|
772
824
|
*
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
this.contextHolder = contextHolder;
|
|
781
|
-
this.holderManager = holderManager;
|
|
825
|
+
* Generates unique instance identifiers based on token, arguments, and scope.
|
|
826
|
+
* Request-scoped services MUST include requestId in their name for proper isolation.
|
|
827
|
+
*/ var NameResolver = class {
|
|
828
|
+
logger;
|
|
829
|
+
instanceNameCache = new InstanceNameCache();
|
|
830
|
+
constructor(logger = null) {
|
|
831
|
+
this.logger = logger;
|
|
782
832
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
833
|
+
/**
|
|
834
|
+
* Generates a unique instance name based on token, arguments, requestId, and scope.
|
|
835
|
+
*
|
|
836
|
+
* Name formats:
|
|
837
|
+
* - Singleton/Transient without args: `${tokenId}`
|
|
838
|
+
* - Singleton/Transient with args: `${tokenId}:${argsHash}`
|
|
839
|
+
* - Request without args: `${tokenId}:requestId=${requestId}`
|
|
840
|
+
* - Request with args: `${tokenId}:requestId=${requestId}:${argsHash}`
|
|
841
|
+
*
|
|
842
|
+
* @param token The injection token
|
|
843
|
+
* @param args Optional arguments
|
|
844
|
+
* @param requestId Optional request ID (required for request-scoped services)
|
|
845
|
+
* @param scope Optional scope (used to determine if requestId should be included)
|
|
846
|
+
* @returns The generated instance name
|
|
847
|
+
*/ generateInstanceName(token, args, requestId, scope) {
|
|
848
|
+
const tokenStr = token.toString();
|
|
849
|
+
const isRequest = scope === InjectableScope.Request;
|
|
850
|
+
if (isRequest && !requestId) throw new Error(`[NameResolver] requestId is required for request-scoped services`);
|
|
851
|
+
const cacheKey = `${tokenStr}:${scope}:${requestId || ""}:${args ? JSON.stringify(args) : ""}`;
|
|
852
|
+
const cached = this.instanceNameCache.get(cacheKey);
|
|
853
|
+
if (cached !== void 0) return cached;
|
|
854
|
+
let result = tokenStr;
|
|
855
|
+
if (isRequest && requestId) result = `${result}:requestId=${requestId}`;
|
|
856
|
+
if (args) {
|
|
857
|
+
const argsHash = hashArgs(args);
|
|
858
|
+
result = `${result}:${argsHash}`;
|
|
792
859
|
}
|
|
860
|
+
this.instanceNameCache.set(cacheKey, result);
|
|
861
|
+
return result;
|
|
793
862
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
return
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
findByInstance(instance) {
|
|
815
|
-
for (const holder of this.contextHolder.holders.values()) if (holder.instance === instance) return holder;
|
|
816
|
-
return null;
|
|
863
|
+
/**
|
|
864
|
+
* Upgrades an existing instance name to include requestId.
|
|
865
|
+
* Preserves any args hash that might already be in the name.
|
|
866
|
+
*
|
|
867
|
+
* Examples:
|
|
868
|
+
* - `TokenName` → `TokenName:requestId=req-123`
|
|
869
|
+
* - `TokenName:abc123` → `TokenName:requestId=req-123:abc123`
|
|
870
|
+
*
|
|
871
|
+
* @param existingName The existing instance name (without requestId)
|
|
872
|
+
* @param requestId The request ID to add
|
|
873
|
+
* @returns The upgraded instance name with requestId
|
|
874
|
+
*/ upgradeInstanceNameToRequest(existingName, requestId) {
|
|
875
|
+
if (existingName.includes(`:requestId=${requestId}`)) return existingName;
|
|
876
|
+
if (/:requestId=/.test(existingName)) return existingName;
|
|
877
|
+
const colonIndex = existingName.indexOf(":");
|
|
878
|
+
if (colonIndex === -1) return `${existingName}:requestId=${requestId}`;
|
|
879
|
+
const tokenPart = existingName.substring(0, colonIndex);
|
|
880
|
+
const argsPart = existingName.substring(colonIndex + 1);
|
|
881
|
+
if (argsPart.startsWith("requestId=")) return existingName;
|
|
882
|
+
return `${tokenPart}:requestId=${requestId}:${argsPart}`;
|
|
817
883
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
if (
|
|
822
|
-
if (
|
|
823
|
-
return
|
|
884
|
+
/**
|
|
885
|
+
* Formats a single argument value for instance name generation.
|
|
886
|
+
*/ formatArgValue(value) {
|
|
887
|
+
if (typeof value === "function") return `fn_${value.name}(${value.length})`;
|
|
888
|
+
if (typeof value === "symbol") return value.toString();
|
|
889
|
+
return JSON.stringify(value).slice(0, 40);
|
|
824
890
|
}
|
|
825
891
|
};
|
|
826
892
|
|
|
827
893
|
//#endregion
|
|
828
|
-
//#region src/
|
|
894
|
+
//#region src/internal/core/scope-tracker.mts
|
|
829
895
|
/**
|
|
830
|
-
*
|
|
896
|
+
* Component for tracking and handling scope upgrades.
|
|
831
897
|
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
|
|
835
|
-
* when multiple requests are processed concurrently.
|
|
836
|
-
*/ var ScopedContainer = class {
|
|
837
|
-
parent;
|
|
898
|
+
* Detects when a Singleton service needs to be upgraded to Request scope
|
|
899
|
+
* and coordinates the scope upgrade process atomically.
|
|
900
|
+
*/ var ScopeTracker = class {
|
|
838
901
|
registry;
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
disposed = false;
|
|
843
|
-
constructor(parent, registry, requestId, metadata, priority = 100) {
|
|
844
|
-
this.parent = parent;
|
|
902
|
+
nameResolver;
|
|
903
|
+
logger;
|
|
904
|
+
constructor(registry, nameResolver, logger = null) {
|
|
845
905
|
this.registry = registry;
|
|
846
|
-
this.
|
|
847
|
-
this.
|
|
848
|
-
this.holderStorage = new RequestStorage(this.requestContextHolder, this.parent.getServiceLocator().getManager());
|
|
906
|
+
this.nameResolver = nameResolver;
|
|
907
|
+
this.logger = logger;
|
|
849
908
|
}
|
|
850
909
|
/**
|
|
851
|
-
*
|
|
852
|
-
|
|
853
|
-
|
|
910
|
+
* Checks if a dependency requires scope upgrade and performs it if needed.
|
|
911
|
+
* Called during service resolution when a dependency is resolved.
|
|
912
|
+
*
|
|
913
|
+
* @param currentServiceName - Name of the service being created
|
|
914
|
+
* @param currentServiceScope - Current scope of the service being created
|
|
915
|
+
* @param dependencyName - Name of the dependency being resolved
|
|
916
|
+
* @param dependencyScope - Scope of the dependency
|
|
917
|
+
* @param dependencyToken - Token of the dependency
|
|
918
|
+
* @param singletonStorage - Singleton storage instance
|
|
919
|
+
* @param requestStorage - Request storage instance (if in request context)
|
|
920
|
+
* @param requestId - Request ID (if in request context)
|
|
921
|
+
* @returns [needsUpgrade: boolean, newName?: string] - whether upgrade occurred and new name
|
|
922
|
+
*/ checkAndUpgradeScope(currentServiceName, currentServiceScope, dependencyName, dependencyScope, dependencyToken, singletonStorage, requestStorage, requestId) {
|
|
923
|
+
if (currentServiceScope !== InjectableScope.Singleton || dependencyScope !== InjectableScope.Request) return [false];
|
|
924
|
+
if (!requestStorage || !requestId) {
|
|
925
|
+
this.logger?.warn(`[ScopeTracker] Cannot upgrade scope for ${currentServiceName}: missing requestStorage or requestId`);
|
|
926
|
+
return [false];
|
|
927
|
+
}
|
|
928
|
+
this.logger?.log(`[ScopeTracker] Upgrading ${currentServiceName} from Singleton to Request scope`);
|
|
929
|
+
try {
|
|
930
|
+
const [success, newName] = this.upgradeScopeToRequestSync(currentServiceName, dependencyToken, singletonStorage, requestStorage, requestId);
|
|
931
|
+
if (success && newName) return [true, newName];
|
|
932
|
+
} catch (error) {
|
|
933
|
+
this.logger?.error(`[ScopeTracker] Error upgrading scope for ${currentServiceName}:`, error);
|
|
934
|
+
}
|
|
935
|
+
return [false];
|
|
854
936
|
}
|
|
855
937
|
/**
|
|
856
|
-
*
|
|
857
|
-
*
|
|
858
|
-
|
|
859
|
-
|
|
938
|
+
* Performs the actual scope upgrade from Singleton to Request.
|
|
939
|
+
* This is the core migration logic.
|
|
940
|
+
*
|
|
941
|
+
* @param serviceName - Current service name (without requestId)
|
|
942
|
+
* @param token - Service injection token
|
|
943
|
+
* @param singletonStorage - Source storage
|
|
944
|
+
* @param requestStorage - Target storage
|
|
945
|
+
* @param requestId - Request ID to include in new name
|
|
946
|
+
* @returns [success: boolean, newName?: string, error?: DIError]
|
|
947
|
+
*/ async upgradeScopeToRequest(serviceName, token, singletonStorage, requestStorage, requestId) {
|
|
948
|
+
try {
|
|
949
|
+
const [success, newName] = this.upgradeScopeToRequestSync(serviceName, token, singletonStorage, requestStorage, requestId);
|
|
950
|
+
if (success && newName) return [true, newName];
|
|
951
|
+
return [
|
|
952
|
+
false,
|
|
953
|
+
void 0,
|
|
954
|
+
DIError.storageError("Scope upgrade failed", "upgradeScopeToRequest", serviceName)
|
|
955
|
+
];
|
|
956
|
+
} catch (error) {
|
|
957
|
+
return [
|
|
958
|
+
false,
|
|
959
|
+
void 0,
|
|
960
|
+
error instanceof DIError ? error : DIError.unknown(error)
|
|
961
|
+
];
|
|
962
|
+
}
|
|
860
963
|
}
|
|
861
964
|
/**
|
|
862
|
-
*
|
|
863
|
-
|
|
864
|
-
|
|
965
|
+
* Synchronous part of scope upgrade - handles immediate updates.
|
|
966
|
+
* Async operations (like waiting for holder creation) should be done separately.
|
|
967
|
+
*/ upgradeScopeToRequestSync(serviceName, token, singletonStorage, requestStorage, requestId) {
|
|
968
|
+
const newName = this.nameResolver.upgradeInstanceNameToRequest(serviceName, requestId);
|
|
969
|
+
if (!this.registry.updateScope(token, InjectableScope.Request)) {
|
|
970
|
+
this.logger?.warn(`[ScopeTracker] Could not update scope in registry for ${serviceName}`);
|
|
971
|
+
return [false];
|
|
972
|
+
}
|
|
973
|
+
const holderResult = singletonStorage.get(serviceName);
|
|
974
|
+
if (holderResult === null) return [true, newName];
|
|
975
|
+
const [error, holder] = holderResult;
|
|
976
|
+
if (error) {
|
|
977
|
+
this.logger?.warn(`[ScopeTracker] Holder for ${serviceName} is in error state: ${error.message}`);
|
|
978
|
+
return [false];
|
|
979
|
+
}
|
|
980
|
+
if (!holder) return [false];
|
|
981
|
+
if (holder.status === InstanceStatus.Creating) {
|
|
982
|
+
holder.name = newName;
|
|
983
|
+
requestStorage.set(newName, holder);
|
|
984
|
+
singletonStorage.delete(serviceName);
|
|
985
|
+
this.updateParentDependencies(serviceName, newName, singletonStorage, requestStorage);
|
|
986
|
+
return [true, newName];
|
|
987
|
+
}
|
|
988
|
+
holder.name = newName;
|
|
989
|
+
requestStorage.set(newName, holder);
|
|
990
|
+
singletonStorage.delete(serviceName);
|
|
991
|
+
this.updateParentDependencies(serviceName, newName, singletonStorage, requestStorage);
|
|
992
|
+
return [true, newName];
|
|
865
993
|
}
|
|
866
994
|
/**
|
|
867
|
-
*
|
|
868
|
-
|
|
869
|
-
|
|
995
|
+
* Updates all parent dependencies to reference the new service name.
|
|
996
|
+
*
|
|
997
|
+
* @param oldName - Original service name
|
|
998
|
+
* @param newName - New service name with requestId
|
|
999
|
+
* @param singletonStorage - Singleton storage to check
|
|
1000
|
+
* @param requestStorage - Request storage to check
|
|
1001
|
+
*/ updateParentDependencies(oldName, newName, singletonStorage, requestStorage) {
|
|
1002
|
+
singletonStorage.updateDependencyReference(oldName, newName);
|
|
1003
|
+
if (requestStorage) requestStorage.updateDependencyReference(oldName, newName);
|
|
870
1004
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
//#endregion
|
|
1008
|
+
//#region src/internal/core/service-initializer.mts
|
|
1009
|
+
/**
|
|
1010
|
+
* Creates service instances from registry records.
|
|
1011
|
+
*
|
|
1012
|
+
* Handles both class-based (@Injectable) and factory-based (@Factory) services,
|
|
1013
|
+
* managing the instantiation lifecycle including lifecycle hook invocation.
|
|
1014
|
+
*/ var ServiceInitializer = class {
|
|
1015
|
+
injectors;
|
|
1016
|
+
constructor(injectors) {
|
|
1017
|
+
this.injectors = injectors;
|
|
875
1018
|
}
|
|
876
1019
|
/**
|
|
877
|
-
*
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
/**
|
|
893
|
-
* Invalidates a service and its dependencies.
|
|
894
|
-
* For request-scoped services, invalidation is handled within this context.
|
|
895
|
-
*/ async invalidate(service) {
|
|
896
|
-
const holder = this.holderStorage.findByInstance(service);
|
|
897
|
-
if (holder) {
|
|
898
|
-
await this.parent.getServiceLocator().getInvalidator().invalidateWithStorage(holder.name, this.holderStorage, 1, { emitEvents: false });
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
await this.parent.invalidate(service);
|
|
902
|
-
}
|
|
903
|
-
/**
|
|
904
|
-
* Checks if a service is registered.
|
|
905
|
-
*/ isRegistered(token) {
|
|
906
|
-
return this.parent.isRegistered(token);
|
|
907
|
-
}
|
|
908
|
-
/**
|
|
909
|
-
* Disposes this scoped container and cleans up all request-scoped instances.
|
|
910
|
-
* This is an alias for endRequest() for IContainer compatibility.
|
|
911
|
-
*/ async dispose() {
|
|
912
|
-
await this.endRequest();
|
|
913
|
-
}
|
|
914
|
-
/**
|
|
915
|
-
* Ends the request and cleans up all request-scoped instances.
|
|
916
|
-
* Uses the invalidation system to properly cascade to dependent singletons.
|
|
917
|
-
*/ async endRequest() {
|
|
918
|
-
if (this.disposed) return;
|
|
919
|
-
this.disposed = true;
|
|
920
|
-
await this.parent.getServiceLocator().getInvalidator().clearAllWithStorage(this.holderStorage, {
|
|
921
|
-
waitForSettlement: true,
|
|
922
|
-
maxRounds: 10
|
|
923
|
-
});
|
|
924
|
-
this.requestContextHolder.clear();
|
|
925
|
-
this.parent.removeActiveRequest(this.requestId);
|
|
926
|
-
}
|
|
927
|
-
/**
|
|
928
|
-
* Waits for all pending operations to complete.
|
|
929
|
-
*/ async ready() {
|
|
930
|
-
await this.parent.ready();
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* @internal
|
|
934
|
-
* Attempts to get an instance synchronously if it already exists and is ready.
|
|
935
|
-
* For request-scoped services, checks this container's context first.
|
|
936
|
-
* For other services, delegates to the parent container.
|
|
937
|
-
*
|
|
938
|
-
* Returns null if the instance doesn't exist or is not yet ready (still creating).
|
|
939
|
-
*/ tryGetSync(token, args) {
|
|
940
|
-
const actualToken = this.parent.getServiceLocator().getTokenProcessor().normalizeToken(token);
|
|
941
|
-
if (this.isRequestScoped(actualToken)) {
|
|
942
|
-
const instanceName = this.parent.getServiceLocator().getInstanceIdentifier(token, args);
|
|
943
|
-
const holder = this.requestContextHolder.get(instanceName);
|
|
944
|
-
if (holder && holder.status === InstanceStatus.Created) return holder.instance;
|
|
945
|
-
return null;
|
|
946
|
-
}
|
|
947
|
-
return this.parent.tryGetSync(token, args);
|
|
948
|
-
}
|
|
949
|
-
/**
|
|
950
|
-
* Checks if a token is for a request-scoped service.
|
|
951
|
-
*/ isRequestScoped(token) {
|
|
952
|
-
const realToken = this.parent.getServiceLocator().getTokenProcessor().getRealToken(token);
|
|
953
|
-
if (!this.registry.has(realToken)) return false;
|
|
954
|
-
return this.registry.get(realToken).scope === InjectableScope.Request;
|
|
955
|
-
}
|
|
956
|
-
/**
|
|
957
|
-
* Resolves a request-scoped service from this container's context.
|
|
958
|
-
* Uses locking to prevent duplicate initialization during concurrent resolution.
|
|
959
|
-
*/ async resolveRequestScoped(token, args) {
|
|
960
|
-
const instanceName = this.parent.getServiceLocator().getInstanceIdentifier(token, args);
|
|
961
|
-
const existingHolder = this.requestContextHolder.get(instanceName);
|
|
962
|
-
if (existingHolder) if (existingHolder.status === InstanceStatus.Error) this.requestContextHolder.delete(instanceName);
|
|
963
|
-
else {
|
|
964
|
-
const [error, readyHolder] = await BaseHolderManager.waitForHolderReady(existingHolder);
|
|
965
|
-
if (error) throw error;
|
|
966
|
-
return readyHolder.instance;
|
|
967
|
-
}
|
|
968
|
-
return this.parent.resolveForRequest(token, args, this);
|
|
969
|
-
}
|
|
970
|
-
/**
|
|
971
|
-
* Stores an instance in the request context.
|
|
972
|
-
* Called by Container during request-scoped service resolution.
|
|
973
|
-
*/ storeRequestInstance(instanceName, instance, holder) {
|
|
974
|
-
this.requestContextHolder.addInstance(instanceName, instance, holder);
|
|
975
|
-
}
|
|
976
|
-
/**
|
|
977
|
-
* Gets an existing instance from the request context.
|
|
978
|
-
* Called by Container during resolution to check for existing instances.
|
|
979
|
-
*/ getRequestInstance(instanceName) {
|
|
980
|
-
return this.requestContextHolder.get(instanceName);
|
|
981
|
-
}
|
|
982
|
-
/**
|
|
983
|
-
* Generates a prefixed event name for request-scoped services.
|
|
984
|
-
* Format: {requestId}:{instanceName}
|
|
985
|
-
*/ getPrefixedEventName(instanceName) {
|
|
986
|
-
return `${this.requestId}:${instanceName}`;
|
|
987
|
-
}
|
|
988
|
-
};
|
|
989
|
-
|
|
990
|
-
//#endregion
|
|
991
|
-
//#region src/internal/holder/singleton-storage.mts
|
|
992
|
-
/**
|
|
993
|
-
* Storage implementation for Singleton-scoped services.
|
|
994
|
-
*
|
|
995
|
-
* Wraps a HolderManager instance and provides the IHolderStorage interface.
|
|
996
|
-
* This allows the InstanceResolver to work with singleton storage
|
|
997
|
-
* using the same interface as request-scoped storage.
|
|
998
|
-
*/
|
|
999
|
-
var SingletonStorage = class {
|
|
1000
|
-
scope = InjectableScope.Singleton;
|
|
1001
|
-
constructor(manager) {
|
|
1002
|
-
this.manager = manager;
|
|
1003
|
-
}
|
|
1004
|
-
get(instanceName) {
|
|
1005
|
-
const [error, holder] = this.manager.get(instanceName);
|
|
1006
|
-
if (!error) return [void 0, holder];
|
|
1007
|
-
switch (error.code) {
|
|
1008
|
-
case DIErrorCode.InstanceNotFound: return null;
|
|
1009
|
-
case DIErrorCode.InstanceDestroying: return [error, holder];
|
|
1010
|
-
default: return [error];
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
set(instanceName, holder) {
|
|
1014
|
-
this.manager.set(instanceName, holder);
|
|
1015
|
-
}
|
|
1016
|
-
delete(instanceName) {
|
|
1017
|
-
return this.manager.delete(instanceName);
|
|
1018
|
-
}
|
|
1019
|
-
createHolder(instanceName, type, deps) {
|
|
1020
|
-
return this.manager.createCreatingHolder(instanceName, type, this.scope, deps);
|
|
1021
|
-
}
|
|
1022
|
-
handles(scope) {
|
|
1023
|
-
return scope === InjectableScope.Singleton;
|
|
1024
|
-
}
|
|
1025
|
-
getAllNames() {
|
|
1026
|
-
return this.manager.getAllNames();
|
|
1027
|
-
}
|
|
1028
|
-
forEach(callback) {
|
|
1029
|
-
this.manager.forEachHolder((holder, name) => callback(name, holder));
|
|
1030
|
-
}
|
|
1031
|
-
findByInstance(instance) {
|
|
1032
|
-
return this.manager.findHolder((h) => h.instance === instance) ?? null;
|
|
1033
|
-
}
|
|
1034
|
-
findDependents(instanceName) {
|
|
1035
|
-
return this.manager.getDependents(instanceName);
|
|
1036
|
-
}
|
|
1037
|
-
};
|
|
1038
|
-
|
|
1039
|
-
//#endregion
|
|
1040
|
-
//#region src/internal/core/instance-resolver.mts
|
|
1041
|
-
/**
|
|
1042
|
-
* Resolves instances from tokens, handling caching, creation, and scope rules.
|
|
1043
|
-
*
|
|
1044
|
-
* Uses the Storage Strategy pattern for unified singleton/request-scoped handling.
|
|
1045
|
-
* Coordinates with Instantiator for actual service creation.
|
|
1046
|
-
*/ var InstanceResolver = class {
|
|
1047
|
-
registry;
|
|
1048
|
-
manager;
|
|
1049
|
-
instantiator;
|
|
1050
|
-
tokenProcessor;
|
|
1051
|
-
logger;
|
|
1052
|
-
serviceLocator;
|
|
1053
|
-
singletonStorage;
|
|
1054
|
-
constructor(registry, manager, instantiator, tokenProcessor, logger = null, serviceLocator) {
|
|
1055
|
-
this.registry = registry;
|
|
1056
|
-
this.manager = manager;
|
|
1057
|
-
this.instantiator = instantiator;
|
|
1058
|
-
this.tokenProcessor = tokenProcessor;
|
|
1059
|
-
this.logger = logger;
|
|
1060
|
-
this.serviceLocator = serviceLocator;
|
|
1061
|
-
this.singletonStorage = new SingletonStorage(manager);
|
|
1062
|
-
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Resolves an instance for the given token and arguments.
|
|
1065
|
-
* This method is used for singleton and transient services.
|
|
1066
|
-
*
|
|
1067
|
-
* @param token The injection token
|
|
1068
|
-
* @param args Optional arguments
|
|
1069
|
-
* @param contextContainer The container to use for creating FactoryContext
|
|
1070
|
-
*/ async resolveInstance(token, args, contextContainer) {
|
|
1071
|
-
return this.resolveWithStorage(token, args, contextContainer, this.singletonStorage);
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
* Resolves a request-scoped instance for a ScopedContainer.
|
|
1075
|
-
* The service will be stored in the ScopedContainer's request context.
|
|
1076
|
-
*
|
|
1077
|
-
* @param token The injection token
|
|
1078
|
-
* @param args Optional arguments
|
|
1079
|
-
* @param scopedContainer The ScopedContainer that owns the request context
|
|
1080
|
-
*/ async resolveRequestScopedInstance(token, args, scopedContainer) {
|
|
1081
|
-
return this.resolveWithStorage(token, args, scopedContainer, scopedContainer.getHolderStorage(), scopedContainer);
|
|
1082
|
-
}
|
|
1083
|
-
/**
|
|
1084
|
-
* Unified resolution method that works with any IHolderStorage.
|
|
1085
|
-
* This eliminates duplication between singleton and request-scoped resolution.
|
|
1086
|
-
*
|
|
1087
|
-
* IMPORTANT: The check-and-store logic is carefully designed to avoid race conditions.
|
|
1088
|
-
* The storage check and holder creation must happen synchronously (no awaits between).
|
|
1089
|
-
*
|
|
1090
|
-
* @param token The injection token
|
|
1091
|
-
* @param args Optional arguments
|
|
1092
|
-
* @param contextContainer The container for FactoryContext
|
|
1093
|
-
* @param storage The storage strategy to use
|
|
1094
|
-
* @param scopedContainer Optional scoped container for request-scoped services
|
|
1095
|
-
*/ async resolveWithStorage(token, args, contextContainer, storage, scopedContainer) {
|
|
1096
|
-
const [err, data] = await this.resolveTokenAndPrepareInstanceName(token, args, contextContainer);
|
|
1097
|
-
if (err) return [err];
|
|
1098
|
-
const { instanceName, validatedArgs, realToken } = data;
|
|
1099
|
-
const getResult = storage.get(instanceName);
|
|
1100
|
-
if (getResult !== null) {
|
|
1101
|
-
const [error, holder$1] = getResult;
|
|
1102
|
-
if (!error && holder$1) {
|
|
1103
|
-
const readyResult = await this.waitForInstanceReady(holder$1);
|
|
1104
|
-
if (readyResult[0]) return [readyResult[0]];
|
|
1105
|
-
return [void 0, readyResult[1].instance];
|
|
1106
|
-
}
|
|
1107
|
-
if (error) {
|
|
1108
|
-
const handledResult = await this.handleStorageError(instanceName, error, holder$1, storage);
|
|
1109
|
-
if (handledResult) return handledResult;
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
const [createError, holder] = await this.createAndStoreInstance(instanceName, realToken, validatedArgs, contextContainer, storage, scopedContainer);
|
|
1113
|
-
if (createError) return [createError];
|
|
1114
|
-
return [void 0, holder.instance];
|
|
1115
|
-
}
|
|
1116
|
-
/**
|
|
1117
|
-
* Handles storage error states (destroying, error, etc.).
|
|
1118
|
-
* Returns a result if handled, null if should proceed with creation.
|
|
1119
|
-
*/ async handleStorageError(instanceName, error, holder, storage) {
|
|
1120
|
-
switch (error.code) {
|
|
1121
|
-
case DIErrorCode.InstanceDestroying:
|
|
1122
|
-
this.logger?.log(`[InstanceResolver] Instance ${instanceName} is being destroyed, waiting...`);
|
|
1123
|
-
if (holder?.destroyPromise) await holder.destroyPromise;
|
|
1124
|
-
const newResult = storage.get(instanceName);
|
|
1125
|
-
if (newResult !== null && !newResult[0]) {
|
|
1126
|
-
const readyResult = await this.waitForInstanceReady(newResult[1]);
|
|
1127
|
-
if (readyResult[0]) return [readyResult[0]];
|
|
1128
|
-
return [void 0, readyResult[1].instance];
|
|
1129
|
-
}
|
|
1130
|
-
return null;
|
|
1131
|
-
default:
|
|
1132
|
-
if (holder) {
|
|
1133
|
-
this.logger?.log(`[InstanceResolver] Removing failed instance ${instanceName} from storage to allow retry`);
|
|
1134
|
-
storage.delete(instanceName);
|
|
1135
|
-
}
|
|
1136
|
-
return null;
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
/**
|
|
1140
|
-
* Creates a new instance and stores it using the provided storage strategy.
|
|
1141
|
-
* This unified method replaces instantiateServiceFromRegistry and createRequestScopedInstance.
|
|
1142
|
-
*
|
|
1143
|
-
* For transient services, the instance is created but not stored (no caching).
|
|
1144
|
-
*/ async createAndStoreInstance(instanceName, realToken, args, contextContainer, storage, scopedContainer) {
|
|
1145
|
-
this.logger?.log(`[InstanceResolver]#createAndStoreInstance() Creating instance for ${instanceName}`);
|
|
1146
|
-
if (!this.registry.has(realToken)) return [DIError.factoryNotFound(realToken.name.toString())];
|
|
1147
|
-
const ctx = this.createFactoryContext(contextContainer);
|
|
1148
|
-
const record = this.registry.get(realToken);
|
|
1149
|
-
const { scope, type } = record;
|
|
1150
|
-
if (scope === InjectableScope.Transient) return this.createTransientInstance(instanceName, record, args, ctx);
|
|
1151
|
-
const [deferred, holder] = this.manager.createCreatingHolder(instanceName, type, scope, ctx.deps);
|
|
1152
|
-
storage.set(instanceName, holder);
|
|
1153
|
-
const getHolder = (name) => {
|
|
1154
|
-
const storageResult = storage.get(name);
|
|
1155
|
-
if (storageResult !== null) {
|
|
1156
|
-
const [, storageHolder] = storageResult;
|
|
1157
|
-
if (storageHolder) return storageHolder;
|
|
1158
|
-
}
|
|
1159
|
-
const [, managerHolder] = this.manager.get(name);
|
|
1160
|
-
return managerHolder;
|
|
1161
|
-
};
|
|
1162
|
-
withResolutionContext(holder, getHolder, () => {
|
|
1163
|
-
this.instantiator.instantiateService(ctx, record, args).then(async (result) => {
|
|
1164
|
-
const [error, instance] = result.length === 2 ? result : [result[0], void 0];
|
|
1165
|
-
await this.handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance, scopedContainer);
|
|
1166
|
-
}).catch(async (error) => {
|
|
1167
|
-
await this.handleInstantiationError(instanceName, holder, deferred, scope, error);
|
|
1168
|
-
}).catch(() => {});
|
|
1169
|
-
});
|
|
1170
|
-
return this.waitForInstanceReady(holder);
|
|
1171
|
-
}
|
|
1172
|
-
/**
|
|
1173
|
-
* Creates a transient instance without storage or locking.
|
|
1174
|
-
* Each call creates a new instance.
|
|
1175
|
-
*/ async createTransientInstance(instanceName, record, args, ctx) {
|
|
1176
|
-
this.logger?.log(`[InstanceResolver]#createTransientInstance() Creating transient instance for ${instanceName}`);
|
|
1177
|
-
const tempHolder = {
|
|
1178
|
-
status: InstanceStatus.Creating,
|
|
1179
|
-
name: instanceName,
|
|
1180
|
-
instance: null,
|
|
1181
|
-
creationPromise: null,
|
|
1182
|
-
destroyPromise: null,
|
|
1183
|
-
type: record.type,
|
|
1184
|
-
scope: InjectableScope.Transient,
|
|
1185
|
-
deps: ctx.deps,
|
|
1186
|
-
destroyListeners: [],
|
|
1187
|
-
createdAt: Date.now(),
|
|
1188
|
-
waitingFor: /* @__PURE__ */ new Set()
|
|
1189
|
-
};
|
|
1190
|
-
const getHolder = (name) => {
|
|
1191
|
-
const [, managerHolder] = this.manager.get(name);
|
|
1192
|
-
return managerHolder;
|
|
1193
|
-
};
|
|
1194
|
-
const [error, instance] = await withResolutionContext(tempHolder, getHolder, () => this.instantiator.instantiateService(ctx, record, args));
|
|
1195
|
-
if (error) return [error];
|
|
1196
|
-
return [void 0, {
|
|
1197
|
-
status: InstanceStatus.Created,
|
|
1198
|
-
name: instanceName,
|
|
1199
|
-
instance,
|
|
1200
|
-
creationPromise: null,
|
|
1201
|
-
destroyPromise: null,
|
|
1202
|
-
type: record.type,
|
|
1203
|
-
scope: InjectableScope.Transient,
|
|
1204
|
-
deps: ctx.deps,
|
|
1205
|
-
destroyListeners: ctx.getDestroyListeners(),
|
|
1206
|
-
createdAt: Date.now(),
|
|
1207
|
-
waitingFor: /* @__PURE__ */ new Set()
|
|
1208
|
-
}];
|
|
1209
|
-
}
|
|
1210
|
-
/**
|
|
1211
|
-
* Gets a synchronous instance (for sync operations).
|
|
1212
|
-
*/ getSyncInstance(token, args, contextContainer) {
|
|
1213
|
-
const [err, { actualToken, validatedArgs }] = this.tokenProcessor.validateAndResolveTokenArgs(token, args);
|
|
1214
|
-
if (err) return null;
|
|
1215
|
-
const instanceName = this.tokenProcessor.generateInstanceName(actualToken, validatedArgs);
|
|
1216
|
-
if ("getRequestInstance" in contextContainer) {
|
|
1217
|
-
const requestHolder = contextContainer.getRequestInstance(instanceName);
|
|
1218
|
-
if (requestHolder) return requestHolder.instance;
|
|
1219
|
-
}
|
|
1220
|
-
const [error, holder] = this.manager.get(instanceName);
|
|
1221
|
-
if (error) return null;
|
|
1222
|
-
return holder.instance;
|
|
1223
|
-
}
|
|
1224
|
-
/**
|
|
1225
|
-
* Internal method to resolve token args and create instance name.
|
|
1226
|
-
* Handles factory token resolution and validation.
|
|
1227
|
-
*/ async resolveTokenAndPrepareInstanceName(token, args, contextContainer) {
|
|
1228
|
-
const [err, { actualToken, validatedArgs }] = this.tokenProcessor.validateAndResolveTokenArgs(token, args);
|
|
1229
|
-
if (err instanceof DIError && err.code === DIErrorCode.UnknownError) return [err];
|
|
1230
|
-
else if (err instanceof DIError && err.code === DIErrorCode.FactoryTokenNotResolved && actualToken instanceof FactoryInjectionToken) {
|
|
1231
|
-
this.logger?.log(`[InstanceResolver]#resolveTokenAndPrepareInstanceName() Factory token not resolved, resolving it`);
|
|
1232
|
-
await actualToken.resolve(this.createFactoryContext(contextContainer));
|
|
1233
|
-
return this.resolveTokenAndPrepareInstanceName(token, void 0, contextContainer);
|
|
1234
|
-
}
|
|
1235
|
-
return [void 0, {
|
|
1236
|
-
instanceName: this.tokenProcessor.generateInstanceName(actualToken, validatedArgs),
|
|
1237
|
-
validatedArgs,
|
|
1238
|
-
actualToken,
|
|
1239
|
-
realToken: actualToken instanceof BoundInjectionToken || actualToken instanceof FactoryInjectionToken ? actualToken.token : actualToken
|
|
1240
|
-
}];
|
|
1241
|
-
}
|
|
1242
|
-
/**
|
|
1243
|
-
* Waits for an instance holder to be ready and returns the appropriate result.
|
|
1244
|
-
* Uses the shared utility from BaseHolderManager.
|
|
1245
|
-
* Passes the current resolution context for circular dependency detection.
|
|
1246
|
-
*/ waitForInstanceReady(holder) {
|
|
1247
|
-
const ctx = getCurrentResolutionContext();
|
|
1248
|
-
return BaseHolderManager.waitForHolderReady(holder, ctx?.waiterHolder, ctx?.getHolder);
|
|
1249
|
-
}
|
|
1250
|
-
/**
|
|
1251
|
-
* Handles the result of service instantiation.
|
|
1252
|
-
*/ async handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance, scopedContainer) {
|
|
1253
|
-
holder.destroyListeners = ctx.getDestroyListeners();
|
|
1254
|
-
holder.creationPromise = null;
|
|
1255
|
-
if (error) await this.handleInstantiationError(instanceName, holder, deferred, scope, error);
|
|
1256
|
-
else await this.handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance, scopedContainer);
|
|
1257
|
-
}
|
|
1258
|
-
/**
|
|
1259
|
-
* Handles successful service instantiation.
|
|
1260
|
-
*/ async handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance, scopedContainer) {
|
|
1261
|
-
holder.instance = instance;
|
|
1262
|
-
holder.status = InstanceStatus.Created;
|
|
1263
|
-
if (ctx.deps.size > 0) if (scopedContainer) scopedContainer.getRequestContextHolder().registerDependencies(instanceName, ctx.deps);
|
|
1264
|
-
else this.manager.registerDependencies(instanceName, ctx.deps);
|
|
1265
|
-
if (ctx.deps.size > 0) ctx.deps.forEach((dependency) => {
|
|
1266
|
-
holder.destroyListeners.push(this.serviceLocator.getEventBus().on(dependency, "destroy", () => {
|
|
1267
|
-
this.logger?.log(`[InstanceResolver] Dependency ${dependency} destroyed, invalidating ${instanceName}`);
|
|
1268
|
-
this.serviceLocator.getInvalidator().invalidate(instanceName);
|
|
1269
|
-
}));
|
|
1270
|
-
if (scopedContainer) {
|
|
1271
|
-
const prefixedDependency = scopedContainer.getPrefixedEventName(dependency);
|
|
1272
|
-
holder.destroyListeners.push(this.serviceLocator.getEventBus().on(prefixedDependency, "destroy", () => {
|
|
1273
|
-
this.logger?.log(`[InstanceResolver] Request-scoped dependency ${dependency} destroyed, invalidating ${instanceName}`);
|
|
1274
|
-
scopedContainer.invalidate(instance);
|
|
1275
|
-
}));
|
|
1276
|
-
}
|
|
1277
|
-
});
|
|
1278
|
-
this.logger?.log(`[InstanceResolver] Instance ${instanceName} created successfully`);
|
|
1279
|
-
deferred.resolve([void 0, instance]);
|
|
1280
|
-
}
|
|
1281
|
-
/**
|
|
1282
|
-
* Handles service instantiation errors.
|
|
1283
|
-
*/ async handleInstantiationError(instanceName, holder, deferred, scope, error) {
|
|
1284
|
-
this.logger?.error(`[InstanceResolver] Error creating instance for ${instanceName}`, error);
|
|
1285
|
-
holder.status = InstanceStatus.Error;
|
|
1286
|
-
holder.instance = error;
|
|
1287
|
-
holder.creationPromise = null;
|
|
1288
|
-
if (scope === InjectableScope.Singleton) {
|
|
1289
|
-
this.logger?.log(`[InstanceResolver] Singleton ${instanceName} failed, will be invalidated`);
|
|
1290
|
-
this.serviceLocator.getInvalidator().invalidate(instanceName).catch(() => {});
|
|
1291
|
-
}
|
|
1292
|
-
deferred.reject(error);
|
|
1293
|
-
}
|
|
1294
|
-
/**
|
|
1295
|
-
* Creates a factory context for dependency injection during service instantiation.
|
|
1296
|
-
*/ createFactoryContext(contextContainer) {
|
|
1297
|
-
return this.tokenProcessor.createFactoryContext(contextContainer);
|
|
1298
|
-
}
|
|
1299
|
-
};
|
|
1300
|
-
|
|
1301
|
-
//#endregion
|
|
1302
|
-
//#region src/internal/core/instantiator.mts
|
|
1303
|
-
/**
|
|
1304
|
-
* Creates service instances from registry records.
|
|
1305
|
-
*
|
|
1306
|
-
* Handles both class-based (@Injectable) and factory-based (@Factory) services,
|
|
1307
|
-
* managing the instantiation lifecycle including sync initialization retries
|
|
1308
|
-
* and lifecycle hook invocation (onServiceInit, onServiceDestroy).
|
|
1309
|
-
*/ var Instantiator = class {
|
|
1310
|
-
injectors;
|
|
1311
|
-
constructor(injectors) {
|
|
1312
|
-
this.injectors = injectors;
|
|
1313
|
-
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Instantiates a service based on its registry record.
|
|
1316
|
-
* @param ctx The factory context for dependency injection
|
|
1317
|
-
* @param record The factory record from the registry
|
|
1318
|
-
* @param args Optional arguments for the service
|
|
1319
|
-
* @returns Promise resolving to [undefined, instance] or [error]
|
|
1320
|
-
*/ async instantiateService(ctx, record, args = void 0) {
|
|
1321
|
-
try {
|
|
1322
|
-
switch (record.type) {
|
|
1323
|
-
case InjectableType.Class: return this.instantiateClass(ctx, record, args);
|
|
1324
|
-
case InjectableType.Factory: return this.instantiateFactory(ctx, record, args);
|
|
1325
|
-
default: throw DIError.unknown(`[Instantiator] Unknown service type: ${record.type}`);
|
|
1326
|
-
}
|
|
1327
|
-
} catch (error) {
|
|
1328
|
-
return [error instanceof DIError ? error : DIError.unknown(String(error))];
|
|
1329
|
-
}
|
|
1020
|
+
* Instantiates a service based on its registry record.
|
|
1021
|
+
* @param ctx The factory context for dependency injection
|
|
1022
|
+
* @param record The factory record from the registry
|
|
1023
|
+
* @param args Optional arguments for the service
|
|
1024
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
1025
|
+
*/ async instantiateService(ctx, record, args = void 0) {
|
|
1026
|
+
try {
|
|
1027
|
+
switch (record.type) {
|
|
1028
|
+
case InjectableType.Class: return this.instantiateClass(ctx, record, args);
|
|
1029
|
+
case InjectableType.Factory: return this.instantiateFactory(ctx, record, args);
|
|
1030
|
+
default: throw DIError.unknown(`[ServiceInitializer] Unknown service type: ${record.type}`);
|
|
1031
|
+
}
|
|
1032
|
+
} catch (error) {
|
|
1033
|
+
return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
|
|
1034
|
+
}
|
|
1330
1035
|
}
|
|
1331
1036
|
/**
|
|
1332
1037
|
* Instantiates a class-based service (Injectable decorator).
|
|
@@ -1344,18 +1049,18 @@ var SingletonStorage = class {
|
|
|
1344
1049
|
});
|
|
1345
1050
|
let [instance, promises, injectState] = tryLoad();
|
|
1346
1051
|
if (promises.length > 0) {
|
|
1347
|
-
if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.
|
|
1052
|
+
if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
|
|
1348
1053
|
const newRes = tryLoad(injectState);
|
|
1349
1054
|
instance = newRes[0];
|
|
1350
1055
|
promises = newRes[1];
|
|
1351
1056
|
}
|
|
1352
1057
|
if (promises.length > 0) {
|
|
1353
|
-
console.error(`[
|
|
1058
|
+
console.error(`[ServiceInitializer] ${record.target.name} has problem with it's definition.
|
|
1354
1059
|
|
|
1355
|
-
One or more of the dependencies are registered as a InjectableScope.
|
|
1060
|
+
One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.
|
|
1356
1061
|
|
|
1357
|
-
Please use
|
|
1358
|
-
throw DIError.
|
|
1062
|
+
Please use asyncInject instead of inject to load those dependencies.`);
|
|
1063
|
+
throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
|
|
1359
1064
|
}
|
|
1360
1065
|
if ("onServiceInit" in instance) await instance.onServiceInit();
|
|
1361
1066
|
if ("onServiceDestroy" in instance) ctx.addDestroyListener(async () => {
|
|
@@ -1363,7 +1068,7 @@ var SingletonStorage = class {
|
|
|
1363
1068
|
});
|
|
1364
1069
|
return [void 0, instance];
|
|
1365
1070
|
} catch (error) {
|
|
1366
|
-
return [error instanceof DIError ? error : DIError.
|
|
1071
|
+
return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
|
|
1367
1072
|
}
|
|
1368
1073
|
}
|
|
1369
1074
|
/**
|
|
@@ -1382,242 +1087,503 @@ var SingletonStorage = class {
|
|
|
1382
1087
|
});
|
|
1383
1088
|
let [builder, promises, injectState] = tryLoad();
|
|
1384
1089
|
if (promises.length > 0) {
|
|
1385
|
-
if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.
|
|
1090
|
+
if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
|
|
1386
1091
|
const newRes = tryLoad(injectState);
|
|
1387
1092
|
builder = newRes[0];
|
|
1388
1093
|
promises = newRes[1];
|
|
1389
1094
|
}
|
|
1390
1095
|
if (promises.length > 0) {
|
|
1391
|
-
console.error(`[
|
|
1096
|
+
console.error(`[ServiceInitializer] ${record.target.name} has problem with it's definition.
|
|
1392
1097
|
|
|
1393
|
-
One or more of the dependencies are registered as a InjectableScope.
|
|
1098
|
+
One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.
|
|
1394
1099
|
|
|
1395
1100
|
Please use asyncInject instead of inject to load those dependencies.`);
|
|
1396
|
-
throw DIError.
|
|
1101
|
+
throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
|
|
1397
1102
|
}
|
|
1398
|
-
if (typeof builder.create !== "function") throw DIError.
|
|
1103
|
+
if (typeof builder.create !== "function") throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Factory does not implement the create method"));
|
|
1399
1104
|
return [void 0, await builder.create(ctx, args)];
|
|
1400
1105
|
} catch (error) {
|
|
1401
|
-
return [error instanceof DIError ? error : DIError.
|
|
1106
|
+
return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
//#endregion
|
|
1112
|
+
//#region src/internal/core/service-invalidator.mts
|
|
1113
|
+
/**
|
|
1114
|
+
* Manages graceful service cleanup with event-based invalidation.
|
|
1115
|
+
*
|
|
1116
|
+
* Uses event subscriptions instead of manual dependent finding.
|
|
1117
|
+
* When a service is created, it subscribes to destroy events of its dependencies.
|
|
1118
|
+
* When a dependency is destroyed, the event automatically invalidates dependents.
|
|
1119
|
+
*/ var ServiceInvalidator = class {
|
|
1120
|
+
eventBus;
|
|
1121
|
+
logger;
|
|
1122
|
+
constructor(eventBus, logger = null) {
|
|
1123
|
+
this.eventBus = eventBus;
|
|
1124
|
+
this.logger = logger;
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Invalidates a service using a specific storage.
|
|
1128
|
+
* Event-based invalidation means dependents are automatically invalidated
|
|
1129
|
+
* via destroy event subscriptions - no need to manually find dependents.
|
|
1130
|
+
*
|
|
1131
|
+
* @param service The instance name to invalidate
|
|
1132
|
+
* @param storage The storage to use for this invalidation
|
|
1133
|
+
* @param options Additional options for invalidation behavior
|
|
1134
|
+
*/ async invalidateWithStorage(service, storage, options = {}) {
|
|
1135
|
+
const { emitEvents = true, onInvalidated } = options;
|
|
1136
|
+
this.logger?.log(`[ServiceInvalidator] Starting invalidation process for ${service}`);
|
|
1137
|
+
const result = storage.get(service);
|
|
1138
|
+
if (result === null) return;
|
|
1139
|
+
const [, holder] = result;
|
|
1140
|
+
if (holder) await this.invalidateHolderWithStorage(service, holder, storage, emitEvents, onInvalidated);
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Sets up destroy event subscriptions for a service's dependencies.
|
|
1144
|
+
* Called when a service is successfully instantiated.
|
|
1145
|
+
*
|
|
1146
|
+
* @param serviceName The name of the service
|
|
1147
|
+
* @param dependencies The set of dependency names
|
|
1148
|
+
* @param storage The storage to use for invalidation
|
|
1149
|
+
* @param holder The holder for the service (to add unsubscribe to destroy listeners)
|
|
1150
|
+
*/ setupDependencySubscriptions(serviceName, dependencies, storage, holder) {
|
|
1151
|
+
if (!this.eventBus) return;
|
|
1152
|
+
for (const dependencyName of dependencies) {
|
|
1153
|
+
const unsubscribe = this.eventBus.on(dependencyName, "destroy", () => {
|
|
1154
|
+
this.logger?.log(`[ServiceInvalidator] Dependency ${dependencyName} destroyed, invalidating ${serviceName}`);
|
|
1155
|
+
this.invalidateWithStorage(serviceName, storage).catch((error) => {
|
|
1156
|
+
this.logger?.error(`[ServiceInvalidator] Error invalidating ${serviceName} after dependency ${dependencyName} destroyed:`, error);
|
|
1157
|
+
});
|
|
1158
|
+
});
|
|
1159
|
+
holder.destroyListeners.push(unsubscribe);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Gracefully clears all services in a specific storage.
|
|
1164
|
+
* This allows clearing request-scoped services using a RequestStorage.
|
|
1165
|
+
*/ async clearAllWithStorage(storage, options = {}) {
|
|
1166
|
+
const { waitForSettlement = true } = options;
|
|
1167
|
+
this.logger?.log("[ServiceInvalidator] Starting graceful clearing of all services");
|
|
1168
|
+
if (waitForSettlement) {
|
|
1169
|
+
this.logger?.log("[ServiceInvalidator] Waiting for all services to settle...");
|
|
1170
|
+
await this.readyWithStorage(storage);
|
|
1171
|
+
}
|
|
1172
|
+
const allServiceNames = storage.getAllNames();
|
|
1173
|
+
if (allServiceNames.length === 0) this.logger?.log("[ServiceInvalidator] No services to clear");
|
|
1174
|
+
else {
|
|
1175
|
+
this.logger?.log(`[ServiceInvalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(", ")}`);
|
|
1176
|
+
const clearPromises = allServiceNames.map((serviceName) => this.invalidateWithStorage(serviceName, storage));
|
|
1177
|
+
await Promise.all(clearPromises);
|
|
1178
|
+
}
|
|
1179
|
+
this.logger?.log("[ServiceInvalidator] Graceful clearing completed");
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Waits for all services in a specific storage to settle.
|
|
1183
|
+
*/ async readyWithStorage(storage) {
|
|
1184
|
+
const holders = [];
|
|
1185
|
+
storage.forEach((_, holder) => holders.push(holder));
|
|
1186
|
+
await Promise.all(holders.map((holder) => this.waitForHolderToSettle(holder)));
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Invalidates a single holder using a specific storage.
|
|
1190
|
+
*/ async invalidateHolderWithStorage(key, holder, storage, emitEvents, onInvalidated) {
|
|
1191
|
+
await this.invalidateHolderByStatus(holder, {
|
|
1192
|
+
context: key,
|
|
1193
|
+
onDestroy: () => this.destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated)
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Common invalidation logic for holders based on their status.
|
|
1198
|
+
*/ async invalidateHolderByStatus(holder, options) {
|
|
1199
|
+
switch (holder.status) {
|
|
1200
|
+
case InstanceStatus.Destroying:
|
|
1201
|
+
await holder.destroyPromise;
|
|
1202
|
+
break;
|
|
1203
|
+
case InstanceStatus.Creating:
|
|
1204
|
+
await holder.creationPromise;
|
|
1205
|
+
await options.onDestroy();
|
|
1206
|
+
break;
|
|
1207
|
+
default:
|
|
1208
|
+
await options.onDestroy();
|
|
1209
|
+
break;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Destroys a holder using a specific storage.
|
|
1214
|
+
*/ async destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated) {
|
|
1215
|
+
holder.status = InstanceStatus.Destroying;
|
|
1216
|
+
this.logger?.log(`[ServiceInvalidator] Invalidating ${key} and notifying listeners`);
|
|
1217
|
+
holder.destroyPromise = Promise.all(holder.destroyListeners.map((listener) => listener())).then(async () => {
|
|
1218
|
+
holder.destroyListeners = [];
|
|
1219
|
+
holder.deps.clear();
|
|
1220
|
+
storage.delete(key);
|
|
1221
|
+
if (emitEvents && this.eventBus) await this.emitInstanceEvent(key, "destroy");
|
|
1222
|
+
if (onInvalidated) await onInvalidated(key);
|
|
1223
|
+
});
|
|
1224
|
+
await holder.destroyPromise;
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Waits for a holder to settle (either created, destroyed, or error state).
|
|
1228
|
+
*/ async waitForHolderToSettle(holder) {
|
|
1229
|
+
switch (holder.status) {
|
|
1230
|
+
case InstanceStatus.Creating:
|
|
1231
|
+
await holder.creationPromise;
|
|
1232
|
+
break;
|
|
1233
|
+
case InstanceStatus.Destroying:
|
|
1234
|
+
await holder.destroyPromise;
|
|
1235
|
+
break;
|
|
1236
|
+
case InstanceStatus.Created:
|
|
1237
|
+
case InstanceStatus.Error: break;
|
|
1402
1238
|
}
|
|
1403
1239
|
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Emits events to listeners for instance lifecycle events.
|
|
1242
|
+
*/ emitInstanceEvent(name, event = "create") {
|
|
1243
|
+
if (!this.eventBus) return Promise.resolve();
|
|
1244
|
+
this.logger?.log(`[ServiceInvalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`);
|
|
1245
|
+
return this.eventBus.emit(name, event);
|
|
1246
|
+
}
|
|
1404
1247
|
};
|
|
1405
1248
|
|
|
1406
1249
|
//#endregion
|
|
1407
|
-
//#region src/
|
|
1250
|
+
//#region src/utils/get-injectors.mts
|
|
1251
|
+
function getInjectors() {
|
|
1252
|
+
let currentFactoryContext = null;
|
|
1253
|
+
function provideFactoryContext$1(context) {
|
|
1254
|
+
const original = currentFactoryContext;
|
|
1255
|
+
currentFactoryContext = context;
|
|
1256
|
+
return original;
|
|
1257
|
+
}
|
|
1258
|
+
function getFactoryContext() {
|
|
1259
|
+
if (!currentFactoryContext) throw new Error("[Injector] Trying to access injection context outside of a injectable context");
|
|
1260
|
+
return currentFactoryContext;
|
|
1261
|
+
}
|
|
1262
|
+
let promiseCollector = null;
|
|
1263
|
+
let injectState = null;
|
|
1264
|
+
function getRequest(token, args, skipCycleTracking = false) {
|
|
1265
|
+
if (!injectState) throw new Error("[Injector] Trying to make a request outside of a injectable context");
|
|
1266
|
+
if (injectState.isFrozen) {
|
|
1267
|
+
const idx = injectState.currentIndex++;
|
|
1268
|
+
const request$1 = injectState.requests[idx];
|
|
1269
|
+
if (request$1.token !== token) throw new Error(`[Injector] Wrong token order. Expected ${request$1.token.toString()} but got ${token.toString()}`);
|
|
1270
|
+
return request$1;
|
|
1271
|
+
}
|
|
1272
|
+
let result = null;
|
|
1273
|
+
let error = null;
|
|
1274
|
+
const doInject = () => getFactoryContext().inject(token, args).then((r) => {
|
|
1275
|
+
result = r;
|
|
1276
|
+
return r;
|
|
1277
|
+
}).catch((e) => {
|
|
1278
|
+
error = e;
|
|
1279
|
+
});
|
|
1280
|
+
const request = {
|
|
1281
|
+
token,
|
|
1282
|
+
promise: skipCycleTracking ? withoutResolutionContext(doInject) : doInject(),
|
|
1283
|
+
get result() {
|
|
1284
|
+
return result;
|
|
1285
|
+
},
|
|
1286
|
+
get error() {
|
|
1287
|
+
return error;
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
injectState.requests.push(request);
|
|
1291
|
+
injectState.currentIndex++;
|
|
1292
|
+
return request;
|
|
1293
|
+
}
|
|
1294
|
+
function asyncInject$1(token, args) {
|
|
1295
|
+
if (!injectState) throw new Error("[Injector] Trying to access inject outside of a injectable context");
|
|
1296
|
+
const request = getRequest(token[InjectableTokenMeta] ?? token, args, true);
|
|
1297
|
+
return request.promise.then((result) => {
|
|
1298
|
+
if (request.error) throw request.error;
|
|
1299
|
+
return result;
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
function wrapSyncInit$1(cb) {
|
|
1303
|
+
return (previousState) => {
|
|
1304
|
+
const promises = [];
|
|
1305
|
+
const originalPromiseCollector = promiseCollector;
|
|
1306
|
+
const originalInjectState = injectState;
|
|
1307
|
+
injectState = previousState ? {
|
|
1308
|
+
...previousState,
|
|
1309
|
+
currentIndex: 0
|
|
1310
|
+
} : {
|
|
1311
|
+
currentIndex: 0,
|
|
1312
|
+
isFrozen: false,
|
|
1313
|
+
requests: []
|
|
1314
|
+
};
|
|
1315
|
+
promiseCollector = (promise) => {
|
|
1316
|
+
promises.push(promise);
|
|
1317
|
+
};
|
|
1318
|
+
const result = cb();
|
|
1319
|
+
promiseCollector = originalPromiseCollector;
|
|
1320
|
+
const newInjectState = {
|
|
1321
|
+
...injectState,
|
|
1322
|
+
isFrozen: true
|
|
1323
|
+
};
|
|
1324
|
+
injectState = originalInjectState;
|
|
1325
|
+
return [
|
|
1326
|
+
result,
|
|
1327
|
+
promises,
|
|
1328
|
+
newInjectState
|
|
1329
|
+
];
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
function inject$1(token, args) {
|
|
1333
|
+
const realToken = token[InjectableTokenMeta] ?? token;
|
|
1334
|
+
if (!injectState) throw new Error("[Injector] Trying to access inject outside of a injectable context");
|
|
1335
|
+
const ctx = getFactoryContext();
|
|
1336
|
+
const instance = ctx.container.tryGetSync(realToken, args);
|
|
1337
|
+
if (!instance) {
|
|
1338
|
+
const request = getRequest(realToken, args);
|
|
1339
|
+
if (request.error) throw request.error;
|
|
1340
|
+
else if (request.result) return request.result;
|
|
1341
|
+
if (promiseCollector) promiseCollector(request.promise);
|
|
1342
|
+
return new Proxy({}, { get() {
|
|
1343
|
+
throw new Error(`[Injector] Trying to access ${realToken.toString()} before it's initialized, please move the code to a onServiceInit method`);
|
|
1344
|
+
} });
|
|
1345
|
+
}
|
|
1346
|
+
ctx.inject(realToken, args).catch(() => {});
|
|
1347
|
+
return instance;
|
|
1348
|
+
}
|
|
1349
|
+
function optional$1(token, args) {
|
|
1350
|
+
try {
|
|
1351
|
+
return inject$1(token, args);
|
|
1352
|
+
} catch {
|
|
1353
|
+
return null;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
return {
|
|
1357
|
+
asyncInject: asyncInject$1,
|
|
1358
|
+
inject: inject$1,
|
|
1359
|
+
optional: optional$1,
|
|
1360
|
+
wrapSyncInit: wrapSyncInit$1,
|
|
1361
|
+
provideFactoryContext: provideFactoryContext$1
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
//#endregion
|
|
1366
|
+
//#region src/utils/default-injectors.mts
|
|
1367
|
+
const defaultInjectors = /* @__PURE__ */ getInjectors();
|
|
1368
|
+
const inject = defaultInjectors.inject;
|
|
1369
|
+
const optional = defaultInjectors.optional;
|
|
1370
|
+
const asyncInject = defaultInjectors.asyncInject;
|
|
1371
|
+
const wrapSyncInit = defaultInjectors.wrapSyncInit;
|
|
1372
|
+
const provideFactoryContext = defaultInjectors.provideFactoryContext;
|
|
1373
|
+
|
|
1374
|
+
//#endregion
|
|
1375
|
+
//#region src/utils/get-injectable-token.mts
|
|
1376
|
+
function getInjectableToken(target) {
|
|
1377
|
+
const token = target[InjectableTokenMeta];
|
|
1378
|
+
if (!token) throw DIError.classNotInjectable(target.name);
|
|
1379
|
+
return token;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
//#endregion
|
|
1383
|
+
//#region src/internal/core/token-resolver.mts
|
|
1408
1384
|
/**
|
|
1409
|
-
*
|
|
1385
|
+
* Handles token validation and resolution.
|
|
1410
1386
|
*
|
|
1411
|
-
*
|
|
1412
|
-
*
|
|
1413
|
-
|
|
1414
|
-
*/ var Invalidator = class {
|
|
1415
|
-
eventBus;
|
|
1387
|
+
* Focuses on token validation, normalization, and argument validation.
|
|
1388
|
+
* Name generation is handled by NameResolver.
|
|
1389
|
+
*/ var TokenResolver = class {
|
|
1416
1390
|
logger;
|
|
1417
|
-
|
|
1418
|
-
constructor(manager, eventBus, logger = null) {
|
|
1419
|
-
this.eventBus = eventBus;
|
|
1391
|
+
constructor(logger = null) {
|
|
1420
1392
|
this.logger = logger;
|
|
1421
|
-
this.storage = new SingletonStorage(manager);
|
|
1422
1393
|
}
|
|
1423
1394
|
/**
|
|
1424
|
-
*
|
|
1425
|
-
*
|
|
1426
|
-
|
|
1427
|
-
|
|
1395
|
+
* Normalizes a token to an InjectionToken.
|
|
1396
|
+
* Handles class constructors by getting their injectable token.
|
|
1397
|
+
*
|
|
1398
|
+
* @param token A class constructor, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
|
|
1399
|
+
* @returns The normalized InjectionTokenType
|
|
1400
|
+
*/ normalizeToken(token) {
|
|
1401
|
+
if (typeof token === "function") return getInjectableToken(token);
|
|
1402
|
+
return token;
|
|
1428
1403
|
}
|
|
1429
1404
|
/**
|
|
1430
|
-
*
|
|
1431
|
-
*
|
|
1405
|
+
* Gets the underlying "real" token from wrapped tokens.
|
|
1406
|
+
* For BoundInjectionToken and FactoryInjectionToken, returns the wrapped token.
|
|
1407
|
+
* For other tokens, returns the token itself.
|
|
1432
1408
|
*
|
|
1433
|
-
* @param
|
|
1434
|
-
* @
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
const { cascade = true, _invalidating = /* @__PURE__ */ new Set() } = options;
|
|
1439
|
-
if (_invalidating.has(service)) {
|
|
1440
|
-
this.logger?.log(`[Invalidator] Skipping ${service} - already being invalidated in this chain`);
|
|
1441
|
-
return;
|
|
1442
|
-
}
|
|
1443
|
-
this.logger?.log(`[Invalidator] Starting invalidation process for ${service}`);
|
|
1444
|
-
const result = storage.get(service);
|
|
1445
|
-
if (result === null) return;
|
|
1446
|
-
_invalidating.add(service);
|
|
1447
|
-
const optionsWithTracking = {
|
|
1448
|
-
...options,
|
|
1449
|
-
_invalidating
|
|
1450
|
-
};
|
|
1451
|
-
if (cascade) {
|
|
1452
|
-
const dependents = storage.findDependents(service);
|
|
1453
|
-
for (const dependentName of dependents) await this.invalidateWithStorage(dependentName, storage, round, optionsWithTracking);
|
|
1454
|
-
}
|
|
1455
|
-
const [, holder] = result;
|
|
1456
|
-
if (holder) await this.invalidateHolderWithStorage(service, holder, storage, round, optionsWithTracking);
|
|
1409
|
+
* @param token The token to unwrap
|
|
1410
|
+
* @returns The underlying InjectionToken
|
|
1411
|
+
*/ getRealToken(token) {
|
|
1412
|
+
if (token instanceof BoundInjectionToken || token instanceof FactoryInjectionToken) return token.token;
|
|
1413
|
+
return token;
|
|
1457
1414
|
}
|
|
1458
1415
|
/**
|
|
1459
|
-
*
|
|
1460
|
-
*
|
|
1461
|
-
*
|
|
1462
|
-
|
|
1463
|
-
|
|
1416
|
+
* Convenience method that normalizes a token and then gets the real token.
|
|
1417
|
+
* Useful for checking registry entries where you need the actual registered token.
|
|
1418
|
+
*
|
|
1419
|
+
* @param token Any injectable type
|
|
1420
|
+
* @returns The underlying InjectionToken
|
|
1421
|
+
*/ getRegistryToken(token) {
|
|
1422
|
+
return this.getRealToken(this.normalizeToken(token));
|
|
1464
1423
|
}
|
|
1465
1424
|
/**
|
|
1466
|
-
*
|
|
1467
|
-
*
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
if (
|
|
1477
|
-
else {
|
|
1478
|
-
|
|
1479
|
-
|
|
1425
|
+
* Validates and resolves token arguments, handling factory token resolution and validation.
|
|
1426
|
+
*
|
|
1427
|
+
* @param token The token to validate
|
|
1428
|
+
* @param args Optional arguments
|
|
1429
|
+
* @returns [error, { actualToken, validatedArgs }]
|
|
1430
|
+
*/ validateAndResolveTokenArgs(token, args) {
|
|
1431
|
+
let actualToken = token;
|
|
1432
|
+
if (typeof token === "function") actualToken = getInjectableToken(token);
|
|
1433
|
+
let realArgs = args;
|
|
1434
|
+
if (actualToken instanceof BoundInjectionToken) realArgs = actualToken.value;
|
|
1435
|
+
else if (actualToken instanceof FactoryInjectionToken) if (actualToken.resolved) realArgs = actualToken.value;
|
|
1436
|
+
else return [DIError.factoryTokenNotResolved(token.name), { actualToken }];
|
|
1437
|
+
if (!actualToken.schema) return [void 0, {
|
|
1438
|
+
actualToken,
|
|
1439
|
+
validatedArgs: realArgs
|
|
1440
|
+
}];
|
|
1441
|
+
const validatedArgs = actualToken.schema?.safeParse(realArgs);
|
|
1442
|
+
if (validatedArgs && !validatedArgs.success) {
|
|
1443
|
+
this.logger?.error(`[TokenResolver]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`, validatedArgs.error);
|
|
1444
|
+
return [DIError.tokenValidationError(`Validation failed for ${actualToken.name.toString()}`, actualToken.schema, realArgs), { actualToken }];
|
|
1480
1445
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
*/ async ready() {
|
|
1486
|
-
return this.readyWithStorage(this.storage);
|
|
1487
|
-
}
|
|
1488
|
-
/**
|
|
1489
|
-
* Waits for all services in a specific storage to settle.
|
|
1490
|
-
*/ async readyWithStorage(storage) {
|
|
1491
|
-
const holders = [];
|
|
1492
|
-
storage.forEach((_, holder) => holders.push(holder));
|
|
1493
|
-
await Promise.all(holders.map((holder) => this.waitForHolderToSettle(holder)));
|
|
1446
|
+
return [void 0, {
|
|
1447
|
+
actualToken,
|
|
1448
|
+
validatedArgs: validatedArgs?.data
|
|
1449
|
+
}];
|
|
1494
1450
|
}
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1453
|
+
//#endregion
|
|
1454
|
+
//#region src/internal/holder/unified-storage.mts
|
|
1455
|
+
/**
|
|
1456
|
+
* Unified storage implementation that works the same way regardless of scope.
|
|
1457
|
+
* Replaces RequestContext, HolderManager, SingletonStorage, RequestStorage.
|
|
1458
|
+
*
|
|
1459
|
+
* Scope is just metadata - storage operations are identical for all scopes.
|
|
1460
|
+
* Different storage instances are just isolated storage spaces.
|
|
1461
|
+
*/ var UnifiedStorage = class {
|
|
1462
|
+
scope;
|
|
1463
|
+
holders = /* @__PURE__ */ new Map();
|
|
1495
1464
|
/**
|
|
1496
|
-
*
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
onCreationError: () => this.logger?.error(`[Invalidator] ${key} creation triggered too many invalidation rounds`),
|
|
1502
|
-
onRecursiveInvalidate: () => this.invalidateWithStorage(key, storage, round + 1, options),
|
|
1503
|
-
onDestroy: () => this.destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated)
|
|
1504
|
-
});
|
|
1465
|
+
* Reverse dependency index: maps a dependency name to the set of holder names that depend on it.
|
|
1466
|
+
* This allows O(1) lookup of dependents instead of O(n) iteration.
|
|
1467
|
+
*/ dependents = /* @__PURE__ */ new Map();
|
|
1468
|
+
constructor(scope = InjectableScope.Singleton) {
|
|
1469
|
+
this.scope = scope;
|
|
1505
1470
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1471
|
+
get(instanceName) {
|
|
1472
|
+
const holder = this.holders.get(instanceName);
|
|
1473
|
+
if (!holder) return null;
|
|
1509
1474
|
switch (holder.status) {
|
|
1510
|
-
case InstanceStatus.Destroying:
|
|
1511
|
-
|
|
1512
|
-
break;
|
|
1475
|
+
case InstanceStatus.Destroying: return [DIError.instanceDestroying(instanceName), holder];
|
|
1476
|
+
case InstanceStatus.Error: return [holder.instance, holder];
|
|
1513
1477
|
case InstanceStatus.Creating:
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
options.onCreationError();
|
|
1517
|
-
return;
|
|
1518
|
-
}
|
|
1519
|
-
await options.onRecursiveInvalidate();
|
|
1520
|
-
break;
|
|
1521
|
-
default:
|
|
1522
|
-
await options.onDestroy();
|
|
1523
|
-
break;
|
|
1478
|
+
case InstanceStatus.Created: return [void 0, holder];
|
|
1479
|
+
default: return null;
|
|
1524
1480
|
}
|
|
1525
1481
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
holder
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1482
|
+
set(instanceName, holder) {
|
|
1483
|
+
this.holders.set(instanceName, holder);
|
|
1484
|
+
if (holder.deps.size > 0) this.registerDependencies(instanceName, holder.deps);
|
|
1485
|
+
}
|
|
1486
|
+
delete(instanceName) {
|
|
1487
|
+
const holder = this.holders.get(instanceName);
|
|
1488
|
+
if (holder) this.removeFromDependentsIndex(instanceName, holder.deps);
|
|
1489
|
+
return this.holders.delete(instanceName);
|
|
1490
|
+
}
|
|
1491
|
+
createHolder(instanceName, type, deps) {
|
|
1492
|
+
const deferred = Promise.withResolvers();
|
|
1493
|
+
return [deferred, {
|
|
1494
|
+
status: InstanceStatus.Creating,
|
|
1495
|
+
name: instanceName,
|
|
1496
|
+
instance: null,
|
|
1497
|
+
creationPromise: deferred.promise,
|
|
1498
|
+
destroyPromise: null,
|
|
1499
|
+
type,
|
|
1500
|
+
scope: this.scope,
|
|
1501
|
+
deps,
|
|
1502
|
+
destroyListeners: [],
|
|
1503
|
+
createdAt: Date.now(),
|
|
1504
|
+
waitingFor: /* @__PURE__ */ new Set()
|
|
1505
|
+
}];
|
|
1506
|
+
}
|
|
1507
|
+
storeInstance(instanceName, instance) {
|
|
1508
|
+
if (this.holders.get(instanceName)) throw DIError.storageError("Instance already stored", "storeInstance", instanceName);
|
|
1509
|
+
this.set(instanceName, {
|
|
1510
|
+
status: InstanceStatus.Created,
|
|
1511
|
+
name: instanceName,
|
|
1512
|
+
instance,
|
|
1513
|
+
creationPromise: null,
|
|
1514
|
+
destroyPromise: null,
|
|
1515
|
+
type: InjectableType.Class,
|
|
1516
|
+
scope: this.scope,
|
|
1517
|
+
deps: /* @__PURE__ */ new Set(),
|
|
1518
|
+
destroyListeners: typeof instance === "object" && instance !== null && "onServiceDestroy" in instance ? [instance.onServiceDestroy] : [],
|
|
1519
|
+
createdAt: Date.now(),
|
|
1520
|
+
waitingFor: /* @__PURE__ */ new Set()
|
|
1537
1521
|
});
|
|
1538
|
-
|
|
1522
|
+
}
|
|
1523
|
+
handles(scope) {
|
|
1524
|
+
return scope === this.scope;
|
|
1525
|
+
}
|
|
1526
|
+
getAllNames() {
|
|
1527
|
+
return Array.from(this.holders.keys());
|
|
1528
|
+
}
|
|
1529
|
+
forEach(callback) {
|
|
1530
|
+
for (const [name, holder] of this.holders) callback(name, holder);
|
|
1531
|
+
}
|
|
1532
|
+
findByInstance(instance) {
|
|
1533
|
+
for (const holder of this.holders.values()) if (holder.instance === instance) return holder;
|
|
1534
|
+
return null;
|
|
1535
|
+
}
|
|
1536
|
+
findDependents(instanceName) {
|
|
1537
|
+
const dependents = this.dependents.get(instanceName);
|
|
1538
|
+
return dependents ? Array.from(dependents) : [];
|
|
1539
1539
|
}
|
|
1540
1540
|
/**
|
|
1541
|
-
*
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1541
|
+
* Updates dependency references when instance names change.
|
|
1542
|
+
* Used during scope upgrades when instance names are regenerated with requestId.
|
|
1543
|
+
*
|
|
1544
|
+
* @param oldName The old instance name
|
|
1545
|
+
* @param newName The new instance name
|
|
1546
|
+
*/ updateDependencyReference(oldName, newName) {
|
|
1547
|
+
for (const holder of this.holders.values()) if (holder.deps.has(oldName)) {
|
|
1548
|
+
holder.deps.delete(oldName);
|
|
1549
|
+
holder.deps.add(newName);
|
|
1550
|
+
}
|
|
1551
|
+
const oldDependents = this.dependents.get(oldName);
|
|
1552
|
+
if (oldDependents) {
|
|
1553
|
+
const newDependents = this.dependents.get(newName) || /* @__PURE__ */ new Set();
|
|
1554
|
+
for (const dependent of oldDependents) newDependents.add(dependent);
|
|
1555
|
+
this.dependents.set(newName, newDependents);
|
|
1556
|
+
this.dependents.delete(oldName);
|
|
1557
|
+
}
|
|
1558
|
+
for (const [depName, dependents] of this.dependents.entries()) if (depName === oldName) {
|
|
1559
|
+
const newDependents = this.dependents.get(newName) || /* @__PURE__ */ new Set();
|
|
1560
|
+
for (const dependent of dependents) newDependents.add(dependent);
|
|
1561
|
+
this.dependents.set(newName, newDependents);
|
|
1562
|
+
this.dependents.delete(oldName);
|
|
1552
1563
|
}
|
|
1553
1564
|
}
|
|
1554
1565
|
/**
|
|
1555
|
-
*
|
|
1556
|
-
*/
|
|
1557
|
-
const
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
if (servicesToClearThisRound.length === 0) {
|
|
1563
|
-
const remainingServices = serviceNames.filter((name) => !clearedServices.has(name));
|
|
1564
|
-
if (remainingServices.length > 0) {
|
|
1565
|
-
this.logger?.warn(`[Invalidator] No services ready for clearing, forcing cleanup of remaining: ${remainingServices.join(", ")}`);
|
|
1566
|
-
await this.forceClearServicesInStorage(remainingServices, storage);
|
|
1567
|
-
remainingServices.forEach((name) => clearedServices.add(name));
|
|
1568
|
-
}
|
|
1569
|
-
break;
|
|
1566
|
+
* Registers a holder's dependencies in the reverse index.
|
|
1567
|
+
*/ registerDependencies(holderName, deps) {
|
|
1568
|
+
for (const dep of deps) {
|
|
1569
|
+
let dependents = this.dependents.get(dep);
|
|
1570
|
+
if (!dependents) {
|
|
1571
|
+
dependents = /* @__PURE__ */ new Set();
|
|
1572
|
+
this.dependents.set(dep, dependents);
|
|
1570
1573
|
}
|
|
1571
|
-
|
|
1572
|
-
try {
|
|
1573
|
-
await this.invalidateWithStorage(serviceName, storage, round);
|
|
1574
|
-
clearedServices.add(serviceName);
|
|
1575
|
-
this.logger?.log(`[Invalidator] Successfully cleared service: ${serviceName}`);
|
|
1576
|
-
} catch (error) {
|
|
1577
|
-
this.logger?.error(`[Invalidator] Error clearing service ${serviceName}:`, error);
|
|
1578
|
-
clearedServices.add(serviceName);
|
|
1579
|
-
}
|
|
1580
|
-
});
|
|
1581
|
-
await Promise.all(clearPromises);
|
|
1582
|
-
round++;
|
|
1574
|
+
dependents.add(holderName);
|
|
1583
1575
|
}
|
|
1584
|
-
if (clearedServices.size < serviceNames.length) this.logger?.warn(`[Invalidator] Clearing completed after ${maxRounds} rounds, but ${serviceNames.length - clearedServices.size} services may not have been properly cleared`);
|
|
1585
|
-
}
|
|
1586
|
-
/**
|
|
1587
|
-
* Finds services that are ready to be cleared in the current round.
|
|
1588
|
-
* A service is ready if all its dependencies have already been cleared.
|
|
1589
|
-
*/ findServicesReadyForClearingInStorage(allServiceNames, clearedServices, storage) {
|
|
1590
|
-
return allServiceNames.filter((serviceName) => {
|
|
1591
|
-
if (clearedServices.has(serviceName)) return false;
|
|
1592
|
-
const result = storage.get(serviceName);
|
|
1593
|
-
if (result === null || result[0]) return true;
|
|
1594
|
-
const [, holder] = result;
|
|
1595
|
-
return !Array.from(holder.deps).some((dep) => !clearedServices.has(dep));
|
|
1596
|
-
});
|
|
1597
1576
|
}
|
|
1598
1577
|
/**
|
|
1599
|
-
*
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
if (
|
|
1606
|
-
const [, holder] = result;
|
|
1607
|
-
await this.destroyHolderWithStorage(serviceName, holder, storage, true);
|
|
1608
|
-
}
|
|
1609
|
-
} catch (error) {
|
|
1610
|
-
this.logger?.error(`[Invalidator] Error force clearing service ${serviceName}:`, error);
|
|
1578
|
+
* Removes a holder from the reverse dependency index.
|
|
1579
|
+
*/ removeFromDependentsIndex(holderName, deps) {
|
|
1580
|
+
for (const dep of deps) {
|
|
1581
|
+
const dependents = this.dependents.get(dep);
|
|
1582
|
+
if (dependents) {
|
|
1583
|
+
dependents.delete(holderName);
|
|
1584
|
+
if (dependents.size === 0) this.dependents.delete(dep);
|
|
1611
1585
|
}
|
|
1612
|
-
}
|
|
1613
|
-
await Promise.all(promises);
|
|
1614
|
-
}
|
|
1615
|
-
/**
|
|
1616
|
-
* Emits events to listeners for instance lifecycle events.
|
|
1617
|
-
*/ emitInstanceEvent(name, event = "create") {
|
|
1618
|
-
if (!this.eventBus) return Promise.resolve();
|
|
1619
|
-
this.logger?.log(`[Invalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`);
|
|
1620
|
-
return this.eventBus.emit(name, event);
|
|
1586
|
+
}
|
|
1621
1587
|
}
|
|
1622
1588
|
};
|
|
1623
1589
|
|
|
@@ -1641,328 +1607,242 @@ var SingletonStorage = class {
|
|
|
1641
1607
|
const nsEvents = this.listeners.get(ns);
|
|
1642
1608
|
if (!nsEvents.has(event)) nsEvents.set(event, /* @__PURE__ */ new Set());
|
|
1643
1609
|
nsEvents.get(event).add(listener);
|
|
1644
|
-
return () => {
|
|
1645
|
-
nsEvents.get(event)?.delete(listener);
|
|
1646
|
-
if (nsEvents.get(event)?.size === 0) nsEvents.delete(event);
|
|
1647
|
-
if (nsEvents.size === 0) this.listeners.delete(ns);
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
async emit(key, event) {
|
|
1651
|
-
if (!this.listeners.has(key)) return;
|
|
1652
|
-
const events = this.listeners.get(key);
|
|
1653
|
-
this.logger?.debug(`[LifecycleEventBus]#emit(): ${key}:${event}`);
|
|
1654
|
-
return await Promise.allSettled([...events.get(event) ?? []].map((listener) => listener(event))).then((results) => {
|
|
1655
|
-
const res = results.filter((result) => result.status === "rejected").map((result) => {
|
|
1656
|
-
this.logger?.warn(`[LifecycleEventBus]#emit(): ${key}:${event} rejected with`, result.reason);
|
|
1657
|
-
return result;
|
|
1658
|
-
});
|
|
1659
|
-
if (res.length > 0) return Promise.reject(res);
|
|
1660
|
-
return results;
|
|
1661
|
-
});
|
|
1662
|
-
}
|
|
1663
|
-
};
|
|
1664
|
-
|
|
1665
|
-
//#endregion
|
|
1666
|
-
//#region src/internal/holder/holder-manager.mts
|
|
1667
|
-
/**
|
|
1668
|
-
* Manages the storage and retrieval of singleton instance holders.
|
|
1669
|
-
*
|
|
1670
|
-
* Provides CRUD operations and filtering for the holder map.
|
|
1671
|
-
* Handles holder state validation (destroying, error states) on retrieval.
|
|
1672
|
-
*/ var HolderManager = class extends BaseHolderManager {
|
|
1673
|
-
constructor(logger = null) {
|
|
1674
|
-
super(logger);
|
|
1675
|
-
}
|
|
1676
|
-
get(name) {
|
|
1677
|
-
const holder = this._holders.get(name);
|
|
1678
|
-
if (holder) {
|
|
1679
|
-
if (holder.status === InstanceStatus.Destroying) {
|
|
1680
|
-
this.logger?.log(`[HolderManager]#get() Instance ${holder.name} is destroying`);
|
|
1681
|
-
return [DIError.instanceDestroying(holder.name), holder];
|
|
1682
|
-
} else if (holder.status === InstanceStatus.Error) {
|
|
1683
|
-
this.logger?.log(`[HolderManager]#get() Instance ${holder.name} is in error state`);
|
|
1684
|
-
return [holder.instance, holder];
|
|
1685
|
-
}
|
|
1686
|
-
return [void 0, holder];
|
|
1687
|
-
} else {
|
|
1688
|
-
this.logger?.log(`[HolderManager]#get() Instance ${name} not found`);
|
|
1689
|
-
return [DIError.instanceNotFound(name)];
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
set(name, holder) {
|
|
1693
|
-
this._holders.set(name, holder);
|
|
1694
|
-
}
|
|
1695
|
-
has(name) {
|
|
1696
|
-
const [error, holder] = this.get(name);
|
|
1697
|
-
if (!error) return [void 0, true];
|
|
1698
|
-
if (error.code === DIErrorCode.InstanceDestroying) return [error];
|
|
1699
|
-
return [void 0, !!holder];
|
|
1700
|
-
}
|
|
1701
|
-
/**
|
|
1702
|
-
* Creates a new holder with Created status and stores it.
|
|
1703
|
-
* This is useful for creating holders that already have their instance ready.
|
|
1704
|
-
* @param name The name of the instance
|
|
1705
|
-
* @param instance The actual instance to store
|
|
1706
|
-
* @param type The injectable type
|
|
1707
|
-
* @param scope The injectable scope
|
|
1708
|
-
* @param deps Optional set of dependencies
|
|
1709
|
-
* @returns The created holder
|
|
1710
|
-
*/ storeCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()) {
|
|
1711
|
-
const holder = this.createCreatedHolder(name, instance, type, scope, deps);
|
|
1712
|
-
this._holders.set(name, holder);
|
|
1713
|
-
return holder;
|
|
1714
|
-
}
|
|
1715
|
-
};
|
|
1716
|
-
|
|
1717
|
-
//#endregion
|
|
1718
|
-
//#region src/internal/core/token-processor.mts
|
|
1719
|
-
/**
|
|
1720
|
-
* Simple LRU cache for instance name generation.
|
|
1721
|
-
* Uses a Map which maintains insertion order for efficient LRU eviction.
|
|
1722
|
-
*/ var InstanceNameCache = class {
|
|
1723
|
-
cache = /* @__PURE__ */ new Map();
|
|
1724
|
-
maxSize;
|
|
1725
|
-
constructor(maxSize = 1e3) {
|
|
1726
|
-
this.maxSize = maxSize;
|
|
1727
|
-
}
|
|
1728
|
-
get(key) {
|
|
1729
|
-
const value = this.cache.get(key);
|
|
1730
|
-
if (value !== void 0) {
|
|
1731
|
-
this.cache.delete(key);
|
|
1732
|
-
this.cache.set(key, value);
|
|
1733
|
-
}
|
|
1734
|
-
return value;
|
|
1735
|
-
}
|
|
1736
|
-
set(key, value) {
|
|
1737
|
-
if (this.cache.has(key)) this.cache.delete(key);
|
|
1738
|
-
else if (this.cache.size >= this.maxSize) {
|
|
1739
|
-
const firstKey = this.cache.keys().next().value;
|
|
1740
|
-
if (firstKey !== void 0) this.cache.delete(firstKey);
|
|
1741
|
-
}
|
|
1742
|
-
this.cache.set(key, value);
|
|
1610
|
+
return () => {
|
|
1611
|
+
nsEvents.get(event)?.delete(listener);
|
|
1612
|
+
if (nsEvents.get(event)?.size === 0) nsEvents.delete(event);
|
|
1613
|
+
if (nsEvents.size === 0) this.listeners.delete(ns);
|
|
1614
|
+
};
|
|
1743
1615
|
}
|
|
1744
|
-
|
|
1745
|
-
this.
|
|
1616
|
+
async emit(key, event) {
|
|
1617
|
+
if (!this.listeners.has(key)) return;
|
|
1618
|
+
const events = this.listeners.get(key);
|
|
1619
|
+
this.logger?.debug(`[LifecycleEventBus]#emit(): ${key}:${event}`);
|
|
1620
|
+
return await Promise.allSettled([...events.get(event) ?? []].map((listener) => listener(event))).then((results) => {
|
|
1621
|
+
const res = results.filter((result) => result.status === "rejected").map((result) => {
|
|
1622
|
+
this.logger?.warn(`[LifecycleEventBus]#emit(): ${key}:${event} rejected with`, result.reason);
|
|
1623
|
+
return result;
|
|
1624
|
+
});
|
|
1625
|
+
if (res.length > 0) return Promise.reject(res);
|
|
1626
|
+
return results;
|
|
1627
|
+
});
|
|
1746
1628
|
}
|
|
1747
1629
|
};
|
|
1630
|
+
|
|
1631
|
+
//#endregion
|
|
1632
|
+
//#region src/internal/stub-factory-class.mts
|
|
1748
1633
|
/**
|
|
1749
|
-
*
|
|
1634
|
+
* Stub factory class used when registering InjectionTokens without a real class implementation.
|
|
1635
|
+
* This is used in ScopedContainer.addInstance() to allow registering tokens that don't have
|
|
1636
|
+
* a corresponding class, while preventing accidental instantiation.
|
|
1750
1637
|
*
|
|
1751
|
-
*
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
instanceNameCache = new InstanceNameCache();
|
|
1756
|
-
constructor(logger = null) {
|
|
1757
|
-
this.logger = logger;
|
|
1638
|
+
* @internal
|
|
1639
|
+
*/ var StubFactoryClass = class {
|
|
1640
|
+
constructor() {
|
|
1641
|
+
throw DIError.factoryNotFound("Trying to get instance of factory without real implementation");
|
|
1758
1642
|
}
|
|
1643
|
+
};
|
|
1644
|
+
|
|
1645
|
+
//#endregion
|
|
1646
|
+
//#region src/container/abstract-container.mts
|
|
1647
|
+
/**
|
|
1648
|
+
* Abstract base class for dependency injection containers.
|
|
1649
|
+
*
|
|
1650
|
+
* Provides shared implementation for common container operations.
|
|
1651
|
+
* Both Container and ScopedContainer extend this class.
|
|
1652
|
+
*/ var AbstractContainer = class {
|
|
1759
1653
|
/**
|
|
1760
|
-
*
|
|
1761
|
-
* Handles class constructors by getting their injectable token.
|
|
1654
|
+
* Calculates the instance name for a given token and optional arguments.
|
|
1762
1655
|
*
|
|
1763
|
-
* @
|
|
1764
|
-
* @
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1656
|
+
* @internal
|
|
1657
|
+
* @param token The class type, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
|
|
1658
|
+
* @param args Optional arguments (ignored for BoundInjectionToken which uses its bound value)
|
|
1659
|
+
* @returns The calculated instance name string, or null if the token is a FactoryInjectionToken that is not yet resolved
|
|
1660
|
+
*/ calculateInstanceName(token, args) {
|
|
1661
|
+
const [err, { actualToken, validatedArgs }] = this.getTokenResolver().validateAndResolveTokenArgs(token, args);
|
|
1662
|
+
if (err) {
|
|
1663
|
+
if (err instanceof DIError && err.code === DIErrorCode.FactoryTokenNotResolved) return null;
|
|
1664
|
+
if (err instanceof DIError && err.code === DIErrorCode.TokenValidationError) return null;
|
|
1665
|
+
}
|
|
1666
|
+
const realToken = this.getTokenResolver().getRealToken(actualToken);
|
|
1667
|
+
const registry = this.getRegistry();
|
|
1668
|
+
const scope = registry.has(realToken) ? registry.get(realToken).scope : this.defaultScope;
|
|
1669
|
+
return this.getNameResolver().generateInstanceName(actualToken, validatedArgs, scope === InjectableScope.Request ? this.requestId : void 0, scope);
|
|
1768
1670
|
}
|
|
1769
1671
|
/**
|
|
1770
|
-
*
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
* @param token The token to unwrap
|
|
1775
|
-
* @returns The underlying InjectionToken
|
|
1776
|
-
*/ getRealToken(token) {
|
|
1777
|
-
if (token instanceof BoundInjectionToken || token instanceof FactoryInjectionToken) return token.token;
|
|
1778
|
-
return token;
|
|
1672
|
+
* Checks if a service is registered in the container.
|
|
1673
|
+
*/ isRegistered(token) {
|
|
1674
|
+
const realToken = this.getTokenResolver().getRegistryToken(token);
|
|
1675
|
+
return this.getRegistry().has(realToken);
|
|
1779
1676
|
}
|
|
1780
1677
|
/**
|
|
1781
|
-
*
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
* @param token Any injectable type
|
|
1785
|
-
* @returns The underlying InjectionToken
|
|
1786
|
-
*/ getRegistryToken(token) {
|
|
1787
|
-
return this.getRealToken(this.normalizeToken(token));
|
|
1678
|
+
* Waits for all pending operations to complete.
|
|
1679
|
+
*/ async ready() {
|
|
1680
|
+
await this.getServiceInvalidator().readyWithStorage(this.getStorage());
|
|
1788
1681
|
}
|
|
1789
1682
|
/**
|
|
1790
|
-
*
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
let realArgs = args;
|
|
1795
|
-
if (actualToken instanceof BoundInjectionToken) realArgs = actualToken.value;
|
|
1796
|
-
else if (actualToken instanceof FactoryInjectionToken) if (actualToken.resolved) realArgs = actualToken.value;
|
|
1797
|
-
else return [DIError.factoryTokenNotResolved(token.name), { actualToken }];
|
|
1798
|
-
if (!actualToken.schema) return [void 0, {
|
|
1799
|
-
actualToken,
|
|
1800
|
-
validatedArgs: realArgs
|
|
1801
|
-
}];
|
|
1802
|
-
const validatedArgs = actualToken.schema?.safeParse(realArgs);
|
|
1803
|
-
if (validatedArgs && !validatedArgs.success) {
|
|
1804
|
-
this.logger?.error(`[TokenProcessor]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`, validatedArgs.error);
|
|
1805
|
-
return [DIError.unknown(validatedArgs.error), { actualToken }];
|
|
1806
|
-
}
|
|
1807
|
-
return [void 0, {
|
|
1808
|
-
actualToken,
|
|
1809
|
-
validatedArgs: validatedArgs?.data
|
|
1810
|
-
}];
|
|
1683
|
+
* @internal
|
|
1684
|
+
* Attempts to get an instance synchronously if it already exists.
|
|
1685
|
+
*/ tryGetSync(token, args) {
|
|
1686
|
+
return this.tryGetSyncFromStorage(token, args, this.getStorage(), this.requestId);
|
|
1811
1687
|
}
|
|
1812
1688
|
/**
|
|
1813
|
-
*
|
|
1814
|
-
*
|
|
1815
|
-
*/
|
|
1816
|
-
|
|
1817
|
-
const
|
|
1818
|
-
const
|
|
1819
|
-
const
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1689
|
+
* @internal
|
|
1690
|
+
* Internal method for getting instances synchronously with configurable storage.
|
|
1691
|
+
*/ tryGetSyncFromStorage(token, args, storage, requestId) {
|
|
1692
|
+
const tokenResolver = this.getTokenResolver();
|
|
1693
|
+
const realToken = tokenResolver.getRegistryToken(token);
|
|
1694
|
+
const registry = this.getRegistry();
|
|
1695
|
+
const scope = registry.has(realToken) ? registry.get(realToken).scope : InjectableScope.Singleton;
|
|
1696
|
+
try {
|
|
1697
|
+
const instanceName = this.getNameResolver().generateInstanceName(tokenResolver.normalizeToken(token), args, requestId, scope);
|
|
1698
|
+
const result = storage.get(instanceName);
|
|
1699
|
+
if (result && result[0] === void 0 && result[1]) {
|
|
1700
|
+
const holder = result[1];
|
|
1701
|
+
if (holder.status === InstanceStatus.Created) return holder.instance;
|
|
1702
|
+
}
|
|
1703
|
+
} catch {}
|
|
1704
|
+
return null;
|
|
1824
1705
|
}
|
|
1825
1706
|
/**
|
|
1826
|
-
*
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1707
|
+
* Adds an instance to the container.
|
|
1708
|
+
* Accepts class types, InjectionTokens, and BoundInjectionTokens.
|
|
1709
|
+
* Rejects InjectionTokens with required schemas (use BoundInjectionToken instead).
|
|
1710
|
+
*
|
|
1711
|
+
* @param token The class type, InjectionToken, or BoundInjectionToken to register the instance for
|
|
1712
|
+
* @param instance The instance to store
|
|
1713
|
+
*/ addInstance(token, instance) {
|
|
1714
|
+
this.addInstanceToStorage(token, instance, this.getStorage(), this.defaultScope, this.requestId);
|
|
1831
1715
|
}
|
|
1832
1716
|
/**
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
destroyListeners.add(listener);
|
|
1841
|
-
}
|
|
1842
|
-
function getDestroyListeners() {
|
|
1843
|
-
return Array.from(destroyListeners);
|
|
1717
|
+
* @internal
|
|
1718
|
+
* Internal method for adding instances with configurable scope and storage.
|
|
1719
|
+
*/ addInstanceToStorage(token, instance, storage, scope, requestId) {
|
|
1720
|
+
if (token instanceof InjectionToken) {
|
|
1721
|
+
if (token.schema) {
|
|
1722
|
+
if (token.schema?.def?.type !== "optional") throw DIError.tokenSchemaRequiredError(token.name);
|
|
1723
|
+
}
|
|
1844
1724
|
}
|
|
1845
|
-
const
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
},
|
|
1854
|
-
addDestroyListener,
|
|
1855
|
-
getDestroyListeners,
|
|
1856
|
-
container,
|
|
1857
|
-
deps
|
|
1858
|
-
};
|
|
1725
|
+
const tokenResolver = this.getTokenResolver();
|
|
1726
|
+
const registry = this.getRegistry();
|
|
1727
|
+
const normalizedToken = tokenResolver.normalizeToken(token);
|
|
1728
|
+
const realToken = tokenResolver.getRegistryToken(token);
|
|
1729
|
+
if (typeof token === "function" && !registry.has(realToken)) registry.set(realToken, scope, token, InjectableType.Class);
|
|
1730
|
+
else if (!registry.has(realToken)) registry.set(realToken, scope, StubFactoryClass, InjectableType.Class, -1);
|
|
1731
|
+
const instanceName = this.getNameResolver().generateInstanceName(normalizedToken, normalizedToken instanceof BoundInjectionToken ? normalizedToken.value : void 0, requestId, scope);
|
|
1732
|
+
storage.storeInstance(instanceName, instance);
|
|
1859
1733
|
}
|
|
1860
1734
|
};
|
|
1861
1735
|
|
|
1862
1736
|
//#endregion
|
|
1863
|
-
//#region src/
|
|
1737
|
+
//#region src/container/scoped-container.mts
|
|
1864
1738
|
/**
|
|
1865
|
-
*
|
|
1739
|
+
* Request-scoped dependency injection container.
|
|
1866
1740
|
*
|
|
1867
|
-
*
|
|
1868
|
-
* delegating
|
|
1869
|
-
*
|
|
1870
|
-
|
|
1741
|
+
* Wraps a parent Container and provides isolated request-scoped instances
|
|
1742
|
+
* while delegating singleton and transient resolution to the parent.
|
|
1743
|
+
* This design eliminates race conditions that can occur with async operations
|
|
1744
|
+
* when multiple requests are processed concurrently.
|
|
1745
|
+
*/ var ScopedContainer = class extends AbstractContainer {
|
|
1746
|
+
parent;
|
|
1871
1747
|
registry;
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
this.registry = registry;
|
|
1882
|
-
this.logger = logger;
|
|
1883
|
-
this.injectors = injectors;
|
|
1884
|
-
this.eventBus = new LifecycleEventBus(logger);
|
|
1885
|
-
this.manager = new HolderManager(logger);
|
|
1886
|
-
this.instantiator = new Instantiator(injectors);
|
|
1887
|
-
this.tokenProcessor = new TokenProcessor(logger);
|
|
1888
|
-
this.invalidator = new Invalidator(this.manager, this.eventBus, logger);
|
|
1889
|
-
this.instanceResolver = new InstanceResolver(this.registry, this.manager, this.instantiator, this.tokenProcessor, logger, this);
|
|
1748
|
+
requestId;
|
|
1749
|
+
defaultScope = InjectableScope.Request;
|
|
1750
|
+
storage;
|
|
1751
|
+
disposed = false;
|
|
1752
|
+
metadata;
|
|
1753
|
+
constructor(parent, registry, requestId, metadata) {
|
|
1754
|
+
super(), this.parent = parent, this.registry = registry, this.requestId = requestId;
|
|
1755
|
+
this.storage = new UnifiedStorage(InjectableScope.Request);
|
|
1756
|
+
this.metadata = metadata || {};
|
|
1890
1757
|
}
|
|
1891
|
-
|
|
1892
|
-
return this.
|
|
1758
|
+
getStorage() {
|
|
1759
|
+
return this.storage;
|
|
1893
1760
|
}
|
|
1894
|
-
|
|
1895
|
-
return this.
|
|
1761
|
+
getRegistry() {
|
|
1762
|
+
return this.registry;
|
|
1896
1763
|
}
|
|
1897
|
-
|
|
1898
|
-
return this.
|
|
1764
|
+
getTokenResolver() {
|
|
1765
|
+
return this.parent.getTokenResolver();
|
|
1899
1766
|
}
|
|
1900
|
-
|
|
1901
|
-
return this.
|
|
1767
|
+
getNameResolver() {
|
|
1768
|
+
return this.parent.getNameResolver();
|
|
1902
1769
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
if (err) throw err;
|
|
1906
|
-
return this.tokenProcessor.generateInstanceName(actualToken, validatedArgs);
|
|
1770
|
+
getServiceInvalidator() {
|
|
1771
|
+
return this.parent.getServiceInvalidator();
|
|
1907
1772
|
}
|
|
1908
1773
|
/**
|
|
1909
|
-
* Gets
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
* @param contextContainer The container to use for creating FactoryContext
|
|
1913
|
-
*/ async getInstance(token, args, contextContainer) {
|
|
1914
|
-
const [err, data] = await this.instanceResolver.resolveInstance(token, args, contextContainer);
|
|
1915
|
-
if (err) return [err];
|
|
1916
|
-
return [void 0, data];
|
|
1774
|
+
* Gets the request ID for this scoped container.
|
|
1775
|
+
*/ getRequestId() {
|
|
1776
|
+
return this.requestId;
|
|
1917
1777
|
}
|
|
1918
1778
|
/**
|
|
1919
|
-
* Gets
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1779
|
+
* Gets the parent container.
|
|
1780
|
+
*/ getParent() {
|
|
1781
|
+
return this.parent;
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Gets metadata from the request context.
|
|
1785
|
+
*/ getMetadata(key) {
|
|
1786
|
+
return this.metadata[key];
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Sets metadata on the request context.
|
|
1790
|
+
*/ setMetadata(key, value) {
|
|
1791
|
+
this.metadata[key] = value;
|
|
1792
|
+
}
|
|
1793
|
+
async get(token, args) {
|
|
1794
|
+
if (this.disposed) throw new Error("ScopedContainer has been disposed");
|
|
1795
|
+
const realToken = this.getTokenResolver().getRegistryToken(token);
|
|
1796
|
+
if (this.registry.has(realToken)) {
|
|
1797
|
+
if (this.registry.get(realToken).scope === InjectableScope.Request) {
|
|
1798
|
+
const [error$1, instance$1] = await this.parent.getInstanceResolver().resolveRequestScopedInstance(token, args, this);
|
|
1799
|
+
if (error$1) throw error$1;
|
|
1800
|
+
return instance$1;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
const [error, instance] = await this.parent.getInstanceResolver().resolveInstance(token, args, this, this.storage, this.requestId);
|
|
1925
1804
|
if (error) throw error;
|
|
1926
1805
|
return instance;
|
|
1927
1806
|
}
|
|
1928
1807
|
/**
|
|
1929
|
-
*
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
* @param scopedContainer The ScopedContainer that owns the request context
|
|
1935
|
-
*/ async resolveRequestScoped(token, args, scopedContainer) {
|
|
1936
|
-
const [err, data] = await this.instanceResolver.resolveRequestScopedInstance(token, args, scopedContainer);
|
|
1937
|
-
if (err) throw err;
|
|
1938
|
-
return data;
|
|
1939
|
-
}
|
|
1940
|
-
getSyncInstance(token, args, contextContainer) {
|
|
1941
|
-
return this.instanceResolver.getSyncInstance(token, args, contextContainer);
|
|
1808
|
+
* Invalidates a service and its dependencies.
|
|
1809
|
+
*/ async invalidate(service) {
|
|
1810
|
+
const holder = this.storage.findByInstance(service);
|
|
1811
|
+
if (!holder) return this.parent.invalidate(service);
|
|
1812
|
+
await this.getServiceInvalidator().invalidateWithStorage(holder.name, this.storage);
|
|
1942
1813
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1814
|
+
/**
|
|
1815
|
+
* Disposes the container and cleans up all resources.
|
|
1816
|
+
* Alias for endRequest().
|
|
1817
|
+
*/ async dispose() {
|
|
1818
|
+
return this.endRequest();
|
|
1945
1819
|
}
|
|
1946
1820
|
/**
|
|
1947
|
-
*
|
|
1948
|
-
*
|
|
1949
|
-
*
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1821
|
+
* @internal
|
|
1822
|
+
* Attempts to get an instance synchronously if it already exists.
|
|
1823
|
+
* Checks request storage first, then delegates to parent.
|
|
1824
|
+
*/ tryGetSync(token, args) {
|
|
1825
|
+
const realToken = this.getTokenResolver().getRegistryToken(token);
|
|
1826
|
+
if ((this.registry.has(realToken) ? this.registry.get(realToken).scope : InjectableScope.Singleton) === InjectableScope.Request) {
|
|
1827
|
+
const result = this.tryGetSyncFromStorage(token, args, this.storage, this.requestId);
|
|
1828
|
+
if (result !== null) return result;
|
|
1829
|
+
}
|
|
1830
|
+
return this.parent.tryGetSync(token, args, this.requestId);
|
|
1955
1831
|
}
|
|
1956
1832
|
/**
|
|
1957
|
-
*
|
|
1958
|
-
|
|
1959
|
-
|
|
1833
|
+
* Adds an instance to the container.
|
|
1834
|
+
* Overrides base class to check disposed state.
|
|
1835
|
+
*/ addInstance(token, instance) {
|
|
1836
|
+
if (this.disposed) throw new Error("ScopedContainer has been disposed");
|
|
1837
|
+
super.addInstance(token, instance);
|
|
1960
1838
|
}
|
|
1961
1839
|
/**
|
|
1962
|
-
*
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1840
|
+
* Ends the request and cleans up all request-scoped services.
|
|
1841
|
+
*/ async endRequest() {
|
|
1842
|
+
if (this.disposed) return;
|
|
1843
|
+
this.disposed = true;
|
|
1844
|
+
await this.getServiceInvalidator().clearAllWithStorage(this.storage);
|
|
1845
|
+
this.parent.removeRequestId(this.requestId);
|
|
1966
1846
|
}
|
|
1967
1847
|
};
|
|
1968
1848
|
|
|
@@ -2245,142 +2125,125 @@ function applyDecs2203RFactory() {
|
|
|
2245
2125
|
function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
2246
2126
|
return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
|
|
2247
2127
|
}
|
|
2248
|
-
var _dec, _initClass;
|
|
2128
|
+
var _dec, _initClass, _AbstractContainer;
|
|
2249
2129
|
let _Container;
|
|
2250
2130
|
_dec = Injectable();
|
|
2251
|
-
var Container = class {
|
|
2131
|
+
var Container = class extends (_AbstractContainer = AbstractContainer) {
|
|
2252
2132
|
registry;
|
|
2253
2133
|
logger;
|
|
2254
2134
|
injectors;
|
|
2255
2135
|
static {
|
|
2256
|
-
({c: [_Container, _initClass]} = _apply_decs_2203_r(this, [], [_dec]));
|
|
2136
|
+
({c: [_Container, _initClass]} = _apply_decs_2203_r(this, [], [_dec], _AbstractContainer));
|
|
2257
2137
|
}
|
|
2258
2138
|
constructor(registry = globalRegistry, logger = null, injectors = defaultInjectors) {
|
|
2259
|
-
this.registry = registry;
|
|
2260
|
-
this.
|
|
2261
|
-
this.
|
|
2262
|
-
this.
|
|
2139
|
+
super(), this.registry = registry, this.logger = logger, this.injectors = injectors;
|
|
2140
|
+
this.storage = new UnifiedStorage(InjectableScope.Singleton);
|
|
2141
|
+
this.eventBus = new LifecycleEventBus(logger);
|
|
2142
|
+
this.nameResolver = new NameResolver(logger);
|
|
2143
|
+
this.tokenResolver = new TokenResolver(logger);
|
|
2144
|
+
this.scopeTracker = new ScopeTracker(registry, this.nameResolver, logger);
|
|
2145
|
+
this.serviceInitializer = new ServiceInitializer(injectors);
|
|
2146
|
+
this.serviceInvalidator = new ServiceInvalidator(this.eventBus, logger);
|
|
2147
|
+
this.instanceResolver = new InstanceResolver(registry, this.storage, this.serviceInitializer, this.tokenResolver, this.nameResolver, this.scopeTracker, this.serviceInvalidator, this.eventBus, logger);
|
|
2263
2148
|
this.registerSelf();
|
|
2264
2149
|
}
|
|
2265
|
-
|
|
2150
|
+
defaultScope = InjectableScope.Singleton;
|
|
2151
|
+
requestId = void 0;
|
|
2152
|
+
storage;
|
|
2153
|
+
serviceInitializer;
|
|
2154
|
+
serviceInvalidator;
|
|
2155
|
+
tokenResolver;
|
|
2156
|
+
nameResolver;
|
|
2157
|
+
scopeTracker;
|
|
2158
|
+
eventBus;
|
|
2159
|
+
instanceResolver;
|
|
2266
2160
|
activeRequestIds = /* @__PURE__ */ new Set();
|
|
2267
2161
|
registerSelf() {
|
|
2268
2162
|
const token = getInjectableToken(_Container);
|
|
2269
|
-
|
|
2270
|
-
this.
|
|
2163
|
+
this.registry.set(token, InjectableScope.Singleton, _Container, InjectableType.Class);
|
|
2164
|
+
const instanceName = this.nameResolver.generateInstanceName(token, void 0, void 0, InjectableScope.Singleton);
|
|
2165
|
+
this.storage.storeInstance(instanceName, this);
|
|
2271
2166
|
}
|
|
2272
2167
|
async get(token, args) {
|
|
2273
|
-
const realToken = this.
|
|
2168
|
+
const realToken = this.tokenResolver.getRegistryToken(token);
|
|
2274
2169
|
if (this.registry.has(realToken)) {
|
|
2275
|
-
if (this.registry.get(realToken).scope === InjectableScope.Request) throw DIError.
|
|
2170
|
+
if (this.registry.get(realToken).scope === InjectableScope.Request) throw DIError.scopeMismatchError(realToken.name, "ScopedContainer", "Container");
|
|
2276
2171
|
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
* Gets an instance with a specific container context.
|
|
2281
|
-
* Used by ScopedContainer to delegate singleton/transient resolution
|
|
2282
|
-
* while maintaining the correct container context for nested inject() calls.
|
|
2283
|
-
*
|
|
2284
|
-
* @internal
|
|
2285
|
-
*/ async getWithContext(token, args, contextContainer) {
|
|
2286
|
-
return this.serviceLocator.getOrThrowInstance(token, args, contextContainer);
|
|
2287
|
-
}
|
|
2288
|
-
/**
|
|
2289
|
-
* Resolves a request-scoped service for a ScopedContainer.
|
|
2290
|
-
* The service will be stored in the ScopedContainer's request context.
|
|
2291
|
-
*
|
|
2292
|
-
* @internal
|
|
2293
|
-
*/ async resolveForRequest(token, args, scopedContainer) {
|
|
2294
|
-
return this.serviceLocator.resolveRequestScoped(token, args, scopedContainer);
|
|
2295
|
-
}
|
|
2296
|
-
/**
|
|
2297
|
-
* Gets the underlying ServiceLocator instance for advanced usage
|
|
2298
|
-
*/ getServiceLocator() {
|
|
2299
|
-
return this.serviceLocator;
|
|
2300
|
-
}
|
|
2301
|
-
/**
|
|
2302
|
-
* Gets the registry
|
|
2303
|
-
*/ getRegistry() {
|
|
2304
|
-
return this.registry;
|
|
2172
|
+
const [error, instance] = await this.instanceResolver.resolveInstance(token, args, this);
|
|
2173
|
+
if (error) throw error;
|
|
2174
|
+
return instance;
|
|
2305
2175
|
}
|
|
2306
2176
|
/**
|
|
2307
|
-
* Invalidates a service and its dependencies
|
|
2177
|
+
* Invalidates a service and its dependencies.
|
|
2308
2178
|
*/ async invalidate(service) {
|
|
2309
|
-
const holder = this.
|
|
2310
|
-
if (holder)
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
* Gets a service holder by instance (reverse lookup)
|
|
2314
|
-
*/ getHolderByInstance(instance) {
|
|
2315
|
-
const holderMap = Array.from(this.serviceLocator.getManager().filter((holder) => holder.instance === instance).values());
|
|
2316
|
-
return holderMap.length > 0 ? holderMap[0] : null;
|
|
2317
|
-
}
|
|
2318
|
-
/**
|
|
2319
|
-
* Checks if a service is registered in the container
|
|
2320
|
-
*/ isRegistered(token) {
|
|
2321
|
-
try {
|
|
2322
|
-
return this.serviceLocator.getInstanceIdentifier(token) !== null;
|
|
2323
|
-
} catch {
|
|
2324
|
-
return false;
|
|
2179
|
+
const holder = this.storage.findByInstance(service);
|
|
2180
|
+
if (!holder) {
|
|
2181
|
+
this.logger?.warn(`[Container] Service instance not found for invalidation`);
|
|
2182
|
+
return;
|
|
2325
2183
|
}
|
|
2184
|
+
await this.serviceInvalidator.invalidateWithStorage(holder.name, this.storage);
|
|
2326
2185
|
}
|
|
2327
2186
|
/**
|
|
2328
|
-
* Disposes the container and cleans up all resources
|
|
2187
|
+
* Disposes the container and cleans up all resources.
|
|
2329
2188
|
*/ async dispose() {
|
|
2330
|
-
await this.
|
|
2331
|
-
}
|
|
2332
|
-
/**
|
|
2333
|
-
* Waits for all pending operations to complete
|
|
2334
|
-
*/ async ready() {
|
|
2335
|
-
await this.serviceLocator.ready();
|
|
2189
|
+
await this.serviceInvalidator.clearAllWithStorage(this.storage);
|
|
2336
2190
|
}
|
|
2337
2191
|
/**
|
|
2338
2192
|
* @internal
|
|
2339
2193
|
* Attempts to get an instance synchronously if it already exists.
|
|
2340
|
-
*
|
|
2341
|
-
*/ tryGetSync(token, args) {
|
|
2342
|
-
return this.
|
|
2194
|
+
* Overrides base class to support requestId parameter for ScopedContainer compatibility.
|
|
2195
|
+
*/ tryGetSync(token, args, requestId) {
|
|
2196
|
+
return this.tryGetSyncFromStorage(token, args, this.storage, requestId ?? this.requestId);
|
|
2343
2197
|
}
|
|
2344
2198
|
/**
|
|
2345
2199
|
* Begins a new request context and returns a ScopedContainer.
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
* while delegating singleton and transient services to this Container.
|
|
2349
|
-
*
|
|
2350
|
-
* @param requestId Unique identifier for this request
|
|
2351
|
-
* @param metadata Optional metadata for the request
|
|
2352
|
-
* @param priority Priority for resolution (higher = more priority)
|
|
2353
|
-
* @returns A ScopedContainer for this request
|
|
2354
|
-
*/ beginRequest(requestId, metadata, priority = 100) {
|
|
2355
|
-
if (this.activeRequestIds.has(requestId)) throw DIError.unknown(`Request context "${requestId}" already exists. Use a unique request ID.`);
|
|
2200
|
+
*/ beginRequest(requestId, metadata) {
|
|
2201
|
+
if (this.activeRequestIds.has(requestId)) throw new Error(`Request with ID ${requestId} already exists`);
|
|
2356
2202
|
this.activeRequestIds.add(requestId);
|
|
2357
|
-
|
|
2358
|
-
return new ScopedContainer(this, this.registry, requestId, metadata, priority);
|
|
2359
|
-
}
|
|
2360
|
-
/**
|
|
2361
|
-
* Removes a request ID from the active set.
|
|
2362
|
-
* Called by ScopedContainer when the request ends.
|
|
2363
|
-
*
|
|
2364
|
-
* @internal
|
|
2365
|
-
*/ removeActiveRequest(requestId) {
|
|
2366
|
-
this.activeRequestIds.delete(requestId);
|
|
2367
|
-
this.logger?.log(`[Container] Ended request context: ${requestId}`);
|
|
2203
|
+
return new ScopedContainer(this, this.registry, requestId, metadata);
|
|
2368
2204
|
}
|
|
2369
2205
|
/**
|
|
2370
|
-
* Gets
|
|
2206
|
+
* Gets all active request IDs.
|
|
2371
2207
|
*/ getActiveRequestIds() {
|
|
2372
2208
|
return this.activeRequestIds;
|
|
2373
2209
|
}
|
|
2374
2210
|
/**
|
|
2375
|
-
* Checks if a request
|
|
2211
|
+
* Checks if a request is active.
|
|
2376
2212
|
*/ hasActiveRequest(requestId) {
|
|
2377
2213
|
return this.activeRequestIds.has(requestId);
|
|
2378
2214
|
}
|
|
2379
2215
|
/**
|
|
2380
|
-
*
|
|
2381
|
-
*
|
|
2382
|
-
*/
|
|
2383
|
-
|
|
2216
|
+
* Removes a request ID from active requests.
|
|
2217
|
+
* Called by ScopedContainer when request ends.
|
|
2218
|
+
*/ removeRequestId(requestId) {
|
|
2219
|
+
this.activeRequestIds.delete(requestId);
|
|
2220
|
+
}
|
|
2221
|
+
getStorage() {
|
|
2222
|
+
return this.storage;
|
|
2223
|
+
}
|
|
2224
|
+
getServiceInitializer() {
|
|
2225
|
+
return this.serviceInitializer;
|
|
2226
|
+
}
|
|
2227
|
+
getServiceInvalidator() {
|
|
2228
|
+
return this.serviceInvalidator;
|
|
2229
|
+
}
|
|
2230
|
+
getTokenResolver() {
|
|
2231
|
+
return this.tokenResolver;
|
|
2232
|
+
}
|
|
2233
|
+
getNameResolver() {
|
|
2234
|
+
return this.nameResolver;
|
|
2235
|
+
}
|
|
2236
|
+
getScopeTracker() {
|
|
2237
|
+
return this.scopeTracker;
|
|
2238
|
+
}
|
|
2239
|
+
getEventBus() {
|
|
2240
|
+
return this.eventBus;
|
|
2241
|
+
}
|
|
2242
|
+
getRegistry() {
|
|
2243
|
+
return this.registry;
|
|
2244
|
+
}
|
|
2245
|
+
getInstanceResolver() {
|
|
2246
|
+
return this.instanceResolver;
|
|
2384
2247
|
}
|
|
2385
2248
|
static {
|
|
2386
2249
|
_initClass();
|
|
@@ -2388,12 +2251,6 @@ var Container = class {
|
|
|
2388
2251
|
};
|
|
2389
2252
|
|
|
2390
2253
|
//#endregion
|
|
2391
|
-
Object.defineProperty(exports, 'BaseHolderManager', {
|
|
2392
|
-
enumerable: true,
|
|
2393
|
-
get: function () {
|
|
2394
|
-
return BaseHolderManager;
|
|
2395
|
-
}
|
|
2396
|
-
});
|
|
2397
2254
|
Object.defineProperty(exports, 'BoundInjectionToken', {
|
|
2398
2255
|
enumerable: true,
|
|
2399
2256
|
get: function () {
|
|
@@ -2418,24 +2275,12 @@ Object.defineProperty(exports, 'DIErrorCode', {
|
|
|
2418
2275
|
return DIErrorCode;
|
|
2419
2276
|
}
|
|
2420
2277
|
});
|
|
2421
|
-
Object.defineProperty(exports, 'DefaultRequestContext', {
|
|
2422
|
-
enumerable: true,
|
|
2423
|
-
get: function () {
|
|
2424
|
-
return DefaultRequestContext;
|
|
2425
|
-
}
|
|
2426
|
-
});
|
|
2427
2278
|
Object.defineProperty(exports, 'FactoryInjectionToken', {
|
|
2428
2279
|
enumerable: true,
|
|
2429
2280
|
get: function () {
|
|
2430
2281
|
return FactoryInjectionToken;
|
|
2431
2282
|
}
|
|
2432
2283
|
});
|
|
2433
|
-
Object.defineProperty(exports, 'HolderManager', {
|
|
2434
|
-
enumerable: true,
|
|
2435
|
-
get: function () {
|
|
2436
|
-
return HolderManager;
|
|
2437
|
-
}
|
|
2438
|
-
});
|
|
2439
2284
|
Object.defineProperty(exports, 'Injectable', {
|
|
2440
2285
|
enumerable: true,
|
|
2441
2286
|
get: function () {
|
|
@@ -2478,22 +2323,16 @@ Object.defineProperty(exports, 'InstanceStatus', {
|
|
|
2478
2323
|
return InstanceStatus;
|
|
2479
2324
|
}
|
|
2480
2325
|
});
|
|
2481
|
-
Object.defineProperty(exports, '
|
|
2482
|
-
enumerable: true,
|
|
2483
|
-
get: function () {
|
|
2484
|
-
return Instantiator;
|
|
2485
|
-
}
|
|
2486
|
-
});
|
|
2487
|
-
Object.defineProperty(exports, 'Invalidator', {
|
|
2326
|
+
Object.defineProperty(exports, 'LifecycleEventBus', {
|
|
2488
2327
|
enumerable: true,
|
|
2489
2328
|
get: function () {
|
|
2490
|
-
return
|
|
2329
|
+
return LifecycleEventBus;
|
|
2491
2330
|
}
|
|
2492
2331
|
});
|
|
2493
|
-
Object.defineProperty(exports, '
|
|
2332
|
+
Object.defineProperty(exports, 'NameResolver', {
|
|
2494
2333
|
enumerable: true,
|
|
2495
2334
|
get: function () {
|
|
2496
|
-
return
|
|
2335
|
+
return NameResolver;
|
|
2497
2336
|
}
|
|
2498
2337
|
});
|
|
2499
2338
|
Object.defineProperty(exports, 'Registry', {
|
|
@@ -2502,10 +2341,10 @@ Object.defineProperty(exports, 'Registry', {
|
|
|
2502
2341
|
return Registry;
|
|
2503
2342
|
}
|
|
2504
2343
|
});
|
|
2505
|
-
Object.defineProperty(exports, '
|
|
2344
|
+
Object.defineProperty(exports, 'ScopeTracker', {
|
|
2506
2345
|
enumerable: true,
|
|
2507
2346
|
get: function () {
|
|
2508
|
-
return
|
|
2347
|
+
return ScopeTracker;
|
|
2509
2348
|
}
|
|
2510
2349
|
});
|
|
2511
2350
|
Object.defineProperty(exports, 'ScopedContainer', {
|
|
@@ -2514,22 +2353,34 @@ Object.defineProperty(exports, 'ScopedContainer', {
|
|
|
2514
2353
|
return ScopedContainer;
|
|
2515
2354
|
}
|
|
2516
2355
|
});
|
|
2517
|
-
Object.defineProperty(exports, '
|
|
2356
|
+
Object.defineProperty(exports, 'ServiceInitializer', {
|
|
2357
|
+
enumerable: true,
|
|
2358
|
+
get: function () {
|
|
2359
|
+
return ServiceInitializer;
|
|
2360
|
+
}
|
|
2361
|
+
});
|
|
2362
|
+
Object.defineProperty(exports, 'ServiceInvalidator', {
|
|
2518
2363
|
enumerable: true,
|
|
2519
2364
|
get: function () {
|
|
2520
|
-
return
|
|
2365
|
+
return ServiceInvalidator;
|
|
2521
2366
|
}
|
|
2522
2367
|
});
|
|
2523
|
-
Object.defineProperty(exports, '
|
|
2368
|
+
Object.defineProperty(exports, 'StubFactoryClass', {
|
|
2524
2369
|
enumerable: true,
|
|
2525
2370
|
get: function () {
|
|
2526
|
-
return
|
|
2371
|
+
return StubFactoryClass;
|
|
2527
2372
|
}
|
|
2528
2373
|
});
|
|
2529
|
-
Object.defineProperty(exports, '
|
|
2374
|
+
Object.defineProperty(exports, 'TokenResolver', {
|
|
2530
2375
|
enumerable: true,
|
|
2531
2376
|
get: function () {
|
|
2532
|
-
return
|
|
2377
|
+
return TokenResolver;
|
|
2378
|
+
}
|
|
2379
|
+
});
|
|
2380
|
+
Object.defineProperty(exports, 'UnifiedStorage', {
|
|
2381
|
+
enumerable: true,
|
|
2382
|
+
get: function () {
|
|
2383
|
+
return UnifiedStorage;
|
|
2533
2384
|
}
|
|
2534
2385
|
});
|
|
2535
2386
|
Object.defineProperty(exports, '_Container', {
|
|
@@ -2544,10 +2395,10 @@ Object.defineProperty(exports, 'asyncInject', {
|
|
|
2544
2395
|
return asyncInject;
|
|
2545
2396
|
}
|
|
2546
2397
|
});
|
|
2547
|
-
Object.defineProperty(exports, '
|
|
2398
|
+
Object.defineProperty(exports, 'createAsyncLocalStorage', {
|
|
2548
2399
|
enumerable: true,
|
|
2549
2400
|
get: function () {
|
|
2550
|
-
return
|
|
2401
|
+
return createAsyncLocalStorage;
|
|
2551
2402
|
}
|
|
2552
2403
|
});
|
|
2553
2404
|
Object.defineProperty(exports, 'defaultInjectors', {
|
|
@@ -2586,6 +2437,12 @@ Object.defineProperty(exports, 'inject', {
|
|
|
2586
2437
|
return inject;
|
|
2587
2438
|
}
|
|
2588
2439
|
});
|
|
2440
|
+
Object.defineProperty(exports, 'isUsingNativeAsyncLocalStorage', {
|
|
2441
|
+
enumerable: true,
|
|
2442
|
+
get: function () {
|
|
2443
|
+
return isUsingNativeAsyncLocalStorage;
|
|
2444
|
+
}
|
|
2445
|
+
});
|
|
2589
2446
|
Object.defineProperty(exports, 'optional', {
|
|
2590
2447
|
enumerable: true,
|
|
2591
2448
|
get: function () {
|
|
@@ -2616,4 +2473,4 @@ Object.defineProperty(exports, 'wrapSyncInit', {
|
|
|
2616
2473
|
return wrapSyncInit;
|
|
2617
2474
|
}
|
|
2618
2475
|
});
|
|
2619
|
-
//# sourceMappingURL=container-
|
|
2476
|
+
//# sourceMappingURL=container-CaY2fDuk.cjs.map
|