@navios/di 0.5.1 → 0.6.1
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 +145 -0
- package/README.md +196 -219
- package/docs/README.md +69 -11
- package/docs/api-reference.md +281 -117
- package/docs/container.md +220 -56
- package/docs/examples/request-scope-example.mts +2 -2
- package/docs/factory.md +3 -8
- package/docs/getting-started.md +37 -8
- package/docs/migration.md +318 -37
- package/docs/request-contexts.md +263 -175
- package/docs/scopes.md +79 -42
- package/lib/browser/index.d.mts +1577 -0
- package/lib/browser/index.d.mts.map +1 -0
- package/lib/browser/index.mjs +3013 -0
- package/lib/browser/index.mjs.map +1 -0
- package/lib/index-7jfWsiG4.d.mts +1211 -0
- package/lib/index-7jfWsiG4.d.mts.map +1 -0
- package/lib/index-DW3K5sOX.d.cts +1206 -0
- package/lib/index-DW3K5sOX.d.cts.map +1 -0
- package/lib/index.cjs +389 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +376 -0
- package/lib/index.d.cts.map +1 -0
- package/lib/index.d.mts +371 -78
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +325 -63
- package/lib/index.mjs.map +1 -1
- package/lib/testing/index.cjs +9 -0
- package/lib/testing/index.d.cts +2 -0
- package/lib/testing/index.d.mts +2 -2
- package/lib/testing/index.mjs +2 -72
- package/lib/testing-BG_fa9TJ.mjs +2656 -0
- package/lib/testing-BG_fa9TJ.mjs.map +1 -0
- package/lib/testing-DIaIRiJz.cjs +2896 -0
- package/lib/testing-DIaIRiJz.cjs.map +1 -0
- package/package.json +29 -7
- package/project.json +2 -2
- package/src/__tests__/async-local-storage.browser.spec.mts +240 -0
- package/src/__tests__/async-local-storage.spec.mts +333 -0
- package/src/__tests__/container.spec.mts +30 -25
- package/src/__tests__/e2e.browser.spec.mts +790 -0
- package/src/__tests__/e2e.spec.mts +1222 -0
- package/src/__tests__/factory.spec.mts +1 -1
- package/src/__tests__/get-injectors.spec.mts +1 -1
- package/src/__tests__/injectable.spec.mts +1 -1
- package/src/__tests__/injection-token.spec.mts +1 -1
- package/src/__tests__/library-findings.spec.mts +563 -0
- package/src/__tests__/registry.spec.mts +2 -2
- package/src/__tests__/request-scope.spec.mts +266 -274
- package/src/__tests__/service-instantiator.spec.mts +18 -17
- package/src/__tests__/service-locator-event-bus.spec.mts +9 -9
- package/src/__tests__/service-locator-manager.spec.mts +15 -15
- package/src/__tests__/service-locator.spec.mts +167 -244
- package/src/__tests__/unified-api.spec.mts +27 -27
- package/src/__type-tests__/factory.spec-d.mts +2 -2
- package/src/__type-tests__/inject.spec-d.mts +2 -2
- package/src/__type-tests__/injectable.spec-d.mts +1 -1
- package/src/browser.mts +16 -0
- package/src/container/container.mts +319 -0
- package/src/container/index.mts +2 -0
- package/src/container/scoped-container.mts +350 -0
- package/src/decorators/factory.decorator.mts +4 -4
- package/src/decorators/injectable.decorator.mts +5 -5
- package/src/errors/di-error.mts +12 -5
- package/src/errors/index.mts +0 -8
- package/src/index.mts +156 -15
- package/src/interfaces/container.interface.mts +82 -0
- package/src/interfaces/factory.interface.mts +2 -2
- package/src/interfaces/index.mts +1 -0
- package/src/internal/context/async-local-storage.mts +120 -0
- package/src/internal/context/factory-context.mts +18 -0
- package/src/internal/context/index.mts +3 -0
- package/src/{request-context-holder.mts → internal/context/request-context.mts} +40 -27
- package/src/internal/context/resolution-context.mts +63 -0
- package/src/internal/context/sync-local-storage.mts +51 -0
- package/src/internal/core/index.mts +5 -0
- package/src/internal/core/instance-resolver.mts +641 -0
- package/src/{service-instantiator.mts → internal/core/instantiator.mts} +31 -27
- package/src/internal/core/invalidator.mts +437 -0
- package/src/internal/core/service-locator.mts +202 -0
- package/src/{token-processor.mts → internal/core/token-processor.mts} +79 -60
- package/src/{base-instance-holder-manager.mts → internal/holder/base-holder-manager.mts} +91 -21
- package/src/internal/holder/holder-manager.mts +85 -0
- package/src/internal/holder/holder-storage.interface.mts +116 -0
- package/src/internal/holder/index.mts +6 -0
- package/src/internal/holder/instance-holder.mts +109 -0
- package/src/internal/holder/request-storage.mts +134 -0
- package/src/internal/holder/singleton-storage.mts +105 -0
- package/src/internal/index.mts +4 -0
- package/src/internal/lifecycle/circular-detector.mts +77 -0
- package/src/internal/lifecycle/index.mts +2 -0
- package/src/{service-locator-event-bus.mts → internal/lifecycle/lifecycle-event-bus.mts} +11 -4
- package/src/testing/__tests__/test-container.spec.mts +2 -2
- package/src/testing/test-container.mts +4 -4
- package/src/token/index.mts +2 -0
- package/src/{injection-token.mts → token/injection-token.mts} +1 -1
- package/src/{registry.mts → token/registry.mts} +1 -1
- package/src/utils/get-injectable-token.mts +1 -1
- package/src/utils/get-injectors.mts +32 -15
- package/src/utils/types.mts +1 -1
- package/tsdown.config.mts +67 -0
- package/lib/_tsup-dts-rollup.d.mts +0 -1283
- package/lib/_tsup-dts-rollup.d.ts +0 -1283
- package/lib/chunk-2M576LCC.mjs +0 -2043
- package/lib/chunk-2M576LCC.mjs.map +0 -1
- package/lib/index.d.ts +0 -78
- package/lib/index.js +0 -2127
- package/lib/index.js.map +0 -1
- package/lib/testing/index.d.ts +0 -2
- package/lib/testing/index.js +0 -2060
- package/lib/testing/index.js.map +0 -1
- package/lib/testing/index.mjs.map +0 -1
- package/src/container.mts +0 -227
- package/src/factory-context.mts +0 -8
- package/src/instance-resolver.mts +0 -559
- package/src/request-context-manager.mts +0 -149
- package/src/service-invalidator.mts +0 -429
- package/src/service-locator-instance-holder.mts +0 -70
- package/src/service-locator-manager.mts +0 -85
- package/src/service-locator.mts +0 -246
- package/tsup.config.mts +0 -12
- /package/src/{injector.mts → injectors.mts} +0 -0
package/lib/chunk-2M576LCC.mjs
DELETED
|
@@ -1,2043 +0,0 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
5
|
-
var __typeError = (msg) => {
|
|
6
|
-
throw TypeError(msg);
|
|
7
|
-
};
|
|
8
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
10
|
-
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
11
|
-
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
12
|
-
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
13
|
-
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
14
|
-
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
15
|
-
var __runInitializers = (array, flags, self, value) => {
|
|
16
|
-
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
17
|
-
return value;
|
|
18
|
-
};
|
|
19
|
-
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
20
|
-
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
21
|
-
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
22
|
-
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
23
|
-
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
|
24
|
-
return __privateGet(this, extra);
|
|
25
|
-
}, set [name](x) {
|
|
26
|
-
return __privateSet(this, extra, x);
|
|
27
|
-
} }, name));
|
|
28
|
-
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
29
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
30
|
-
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
31
|
-
if (k) {
|
|
32
|
-
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
|
33
|
-
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
34
|
-
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
35
|
-
}
|
|
36
|
-
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
|
37
|
-
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
38
|
-
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
|
39
|
-
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
40
|
-
}
|
|
41
|
-
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
42
|
-
};
|
|
43
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
44
|
-
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
45
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
46
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
47
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
48
|
-
|
|
49
|
-
// src/enums/injectable-scope.enum.mts
|
|
50
|
-
var InjectableScope = /* @__PURE__ */ ((InjectableScope5) => {
|
|
51
|
-
InjectableScope5["Singleton"] = "Singleton";
|
|
52
|
-
InjectableScope5["Transient"] = "Transient";
|
|
53
|
-
InjectableScope5["Request"] = "Request";
|
|
54
|
-
return InjectableScope5;
|
|
55
|
-
})(InjectableScope || {});
|
|
56
|
-
|
|
57
|
-
// src/enums/injectable-type.enum.mts
|
|
58
|
-
var InjectableType = /* @__PURE__ */ ((InjectableType5) => {
|
|
59
|
-
InjectableType5["Class"] = "Class";
|
|
60
|
-
InjectableType5["Factory"] = "Factory";
|
|
61
|
-
return InjectableType5;
|
|
62
|
-
})(InjectableType || {});
|
|
63
|
-
|
|
64
|
-
// src/injection-token.mts
|
|
65
|
-
var InjectionToken = class _InjectionToken {
|
|
66
|
-
constructor(name, schema) {
|
|
67
|
-
this.name = name;
|
|
68
|
-
this.schema = schema;
|
|
69
|
-
}
|
|
70
|
-
id = globalThis.crypto.randomUUID();
|
|
71
|
-
formattedName = null;
|
|
72
|
-
static create(name, schema) {
|
|
73
|
-
return new _InjectionToken(name, schema);
|
|
74
|
-
}
|
|
75
|
-
static bound(token, value) {
|
|
76
|
-
return new BoundInjectionToken(token, value);
|
|
77
|
-
}
|
|
78
|
-
static factory(token, factory) {
|
|
79
|
-
return new FactoryInjectionToken(token, factory);
|
|
80
|
-
}
|
|
81
|
-
static refineType(token) {
|
|
82
|
-
return token;
|
|
83
|
-
}
|
|
84
|
-
toString() {
|
|
85
|
-
if (this.formattedName) {
|
|
86
|
-
return this.formattedName;
|
|
87
|
-
}
|
|
88
|
-
const { name } = this;
|
|
89
|
-
if (typeof name === "function") {
|
|
90
|
-
const className = name.name;
|
|
91
|
-
this.formattedName = `${className}(${this.id})`;
|
|
92
|
-
} else if (typeof name === "symbol") {
|
|
93
|
-
this.formattedName = `${name.toString()}(${this.id})`;
|
|
94
|
-
} else {
|
|
95
|
-
this.formattedName = `${name}(${this.id})`;
|
|
96
|
-
}
|
|
97
|
-
return this.formattedName;
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
var BoundInjectionToken = class {
|
|
101
|
-
constructor(token, value) {
|
|
102
|
-
this.token = token;
|
|
103
|
-
this.value = value;
|
|
104
|
-
this.name = token.name;
|
|
105
|
-
this.id = token.id;
|
|
106
|
-
this.schema = token.schema;
|
|
107
|
-
}
|
|
108
|
-
id;
|
|
109
|
-
name;
|
|
110
|
-
schema;
|
|
111
|
-
toString() {
|
|
112
|
-
return this.token.toString();
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
var FactoryInjectionToken = class {
|
|
116
|
-
constructor(token, factory) {
|
|
117
|
-
this.token = token;
|
|
118
|
-
this.factory = factory;
|
|
119
|
-
this.name = token.name;
|
|
120
|
-
this.id = token.id;
|
|
121
|
-
this.schema = token.schema;
|
|
122
|
-
}
|
|
123
|
-
value;
|
|
124
|
-
resolved = false;
|
|
125
|
-
id;
|
|
126
|
-
name;
|
|
127
|
-
schema;
|
|
128
|
-
async resolve(ctx) {
|
|
129
|
-
if (!this.value) {
|
|
130
|
-
this.value = await this.factory(ctx);
|
|
131
|
-
this.resolved = true;
|
|
132
|
-
}
|
|
133
|
-
return this.value;
|
|
134
|
-
}
|
|
135
|
-
toString() {
|
|
136
|
-
return this.token.toString();
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// src/registry.mts
|
|
141
|
-
var Registry = class {
|
|
142
|
-
constructor(parent) {
|
|
143
|
-
this.parent = parent;
|
|
144
|
-
}
|
|
145
|
-
factories = /* @__PURE__ */ new Map();
|
|
146
|
-
has(token) {
|
|
147
|
-
if (this.factories.has(token.id)) {
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
if (this.parent) {
|
|
151
|
-
return this.parent.has(token);
|
|
152
|
-
}
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
get(token) {
|
|
156
|
-
const factory = this.factories.get(token.id);
|
|
157
|
-
if (!factory) {
|
|
158
|
-
if (this.parent) {
|
|
159
|
-
return this.parent.get(token);
|
|
160
|
-
}
|
|
161
|
-
throw new Error(`[Registry] No factory found for ${token.toString()}`);
|
|
162
|
-
}
|
|
163
|
-
return factory;
|
|
164
|
-
}
|
|
165
|
-
set(token, scope, target, type) {
|
|
166
|
-
this.factories.set(token.id, { scope, originalToken: token, target, type });
|
|
167
|
-
}
|
|
168
|
-
delete(token) {
|
|
169
|
-
this.factories.delete(token.id);
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
var globalRegistry = new Registry();
|
|
173
|
-
|
|
174
|
-
// src/symbols/injectable-token.mts
|
|
175
|
-
var InjectableTokenMeta = /* @__PURE__ */ Symbol.for("InjectableTokenMeta");
|
|
176
|
-
function Injectable({
|
|
177
|
-
scope = "Singleton" /* Singleton */,
|
|
178
|
-
token,
|
|
179
|
-
schema,
|
|
180
|
-
registry = globalRegistry
|
|
181
|
-
} = {}) {
|
|
182
|
-
return (target, context) => {
|
|
183
|
-
if (context && context.kind !== "class" || target instanceof Function && !context) {
|
|
184
|
-
throw new Error(
|
|
185
|
-
"[ServiceLocator] @Injectable decorator can only be used on classes."
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
if (schema && token) {
|
|
189
|
-
throw new Error(
|
|
190
|
-
"[ServiceLocator] @Injectable decorator cannot have both a token and a schema"
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
let injectableToken = token ?? InjectionToken.create(target, schema);
|
|
194
|
-
registry.set(injectableToken, scope, target, "Class" /* Class */);
|
|
195
|
-
target[InjectableTokenMeta] = injectableToken;
|
|
196
|
-
return target;
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// src/errors/di-error.mts
|
|
201
|
-
var DIErrorCode = /* @__PURE__ */ ((DIErrorCode2) => {
|
|
202
|
-
DIErrorCode2["FactoryNotFound"] = "FactoryNotFound";
|
|
203
|
-
DIErrorCode2["FactoryTokenNotResolved"] = "FactoryTokenNotResolved";
|
|
204
|
-
DIErrorCode2["InstanceNotFound"] = "InstanceNotFound";
|
|
205
|
-
DIErrorCode2["InstanceDestroying"] = "InstanceDestroying";
|
|
206
|
-
DIErrorCode2["UnknownError"] = "UnknownError";
|
|
207
|
-
return DIErrorCode2;
|
|
208
|
-
})(DIErrorCode || {});
|
|
209
|
-
var DIError = class _DIError extends Error {
|
|
210
|
-
code;
|
|
211
|
-
context;
|
|
212
|
-
constructor(code, message, context) {
|
|
213
|
-
super(message);
|
|
214
|
-
this.name = "DIError";
|
|
215
|
-
this.code = code;
|
|
216
|
-
this.context = context;
|
|
217
|
-
}
|
|
218
|
-
// Static factory methods for common error types
|
|
219
|
-
static factoryNotFound(name) {
|
|
220
|
-
return new _DIError(
|
|
221
|
-
"FactoryNotFound" /* FactoryNotFound */,
|
|
222
|
-
`Factory ${name} not found`,
|
|
223
|
-
{ name }
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
static factoryTokenNotResolved(token) {
|
|
227
|
-
return new _DIError(
|
|
228
|
-
"FactoryTokenNotResolved" /* FactoryTokenNotResolved */,
|
|
229
|
-
`Factory token not resolved: ${token?.toString() ?? "unknown"}`,
|
|
230
|
-
{ token }
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
static instanceNotFound(name) {
|
|
234
|
-
return new _DIError(
|
|
235
|
-
"InstanceNotFound" /* InstanceNotFound */,
|
|
236
|
-
`Instance ${name} not found`,
|
|
237
|
-
{ name }
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
static instanceDestroying(name) {
|
|
241
|
-
return new _DIError(
|
|
242
|
-
"InstanceDestroying" /* InstanceDestroying */,
|
|
243
|
-
`Instance ${name} destroying`,
|
|
244
|
-
{ name }
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
static unknown(message, context) {
|
|
248
|
-
if (message instanceof Error) {
|
|
249
|
-
return new _DIError("UnknownError" /* UnknownError */, message.message, {
|
|
250
|
-
...context,
|
|
251
|
-
parent: message
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
return new _DIError("UnknownError" /* UnknownError */, message, context);
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
// src/utils/get-injectors.mts
|
|
259
|
-
function getInjectors() {
|
|
260
|
-
let currentFactoryContext = null;
|
|
261
|
-
function provideFactoryContext2(context) {
|
|
262
|
-
const original = currentFactoryContext;
|
|
263
|
-
currentFactoryContext = context;
|
|
264
|
-
return original;
|
|
265
|
-
}
|
|
266
|
-
function getFactoryContext() {
|
|
267
|
-
if (!currentFactoryContext) {
|
|
268
|
-
throw new Error(
|
|
269
|
-
"[Injector] Trying to access injection context outside of a injectable context"
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
return currentFactoryContext;
|
|
273
|
-
}
|
|
274
|
-
let promiseCollector = null;
|
|
275
|
-
let injectState = null;
|
|
276
|
-
function getRequest(token, args) {
|
|
277
|
-
if (!injectState) {
|
|
278
|
-
throw new Error(
|
|
279
|
-
"[Injector] Trying to make a request outside of a injectable context"
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
if (injectState.isFrozen) {
|
|
283
|
-
const idx = injectState.currentIndex++;
|
|
284
|
-
const request2 = injectState.requests[idx];
|
|
285
|
-
if (request2.token !== token) {
|
|
286
|
-
throw new Error(
|
|
287
|
-
`[Injector] Wrong token order. Expected ${request2.token.toString()} but got ${token.toString()}`
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
return request2;
|
|
291
|
-
}
|
|
292
|
-
let result = null;
|
|
293
|
-
let error = null;
|
|
294
|
-
const promise = getFactoryContext().inject(token, args).then((r) => {
|
|
295
|
-
result = r;
|
|
296
|
-
return r;
|
|
297
|
-
}).catch((e) => {
|
|
298
|
-
error = e;
|
|
299
|
-
});
|
|
300
|
-
const request = {
|
|
301
|
-
token,
|
|
302
|
-
promise,
|
|
303
|
-
get result() {
|
|
304
|
-
return result;
|
|
305
|
-
},
|
|
306
|
-
get error() {
|
|
307
|
-
return error;
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
injectState.requests.push(request);
|
|
311
|
-
injectState.currentIndex++;
|
|
312
|
-
return request;
|
|
313
|
-
}
|
|
314
|
-
function asyncInject2(token, args) {
|
|
315
|
-
if (!injectState) {
|
|
316
|
-
throw new Error(
|
|
317
|
-
"[Injector] Trying to access inject outside of a injectable context"
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
const realToken = token[InjectableTokenMeta] ?? token;
|
|
321
|
-
const request = getRequest(realToken, args);
|
|
322
|
-
return request.promise.then((result) => {
|
|
323
|
-
if (request.error) {
|
|
324
|
-
throw request.error;
|
|
325
|
-
}
|
|
326
|
-
return result;
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
function wrapSyncInit2(cb) {
|
|
330
|
-
return (previousState) => {
|
|
331
|
-
const promises = [];
|
|
332
|
-
const originalPromiseCollector = promiseCollector;
|
|
333
|
-
const originalInjectState = injectState;
|
|
334
|
-
injectState = previousState ? {
|
|
335
|
-
...previousState,
|
|
336
|
-
currentIndex: 0
|
|
337
|
-
} : {
|
|
338
|
-
currentIndex: 0,
|
|
339
|
-
isFrozen: false,
|
|
340
|
-
requests: []
|
|
341
|
-
};
|
|
342
|
-
promiseCollector = (promise) => {
|
|
343
|
-
promises.push(promise);
|
|
344
|
-
};
|
|
345
|
-
const result = cb();
|
|
346
|
-
promiseCollector = originalPromiseCollector;
|
|
347
|
-
const newInjectState = {
|
|
348
|
-
...injectState,
|
|
349
|
-
isFrozen: true
|
|
350
|
-
};
|
|
351
|
-
injectState = originalInjectState;
|
|
352
|
-
return [result, promises, newInjectState];
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
function inject2(token, args) {
|
|
356
|
-
const realToken = token[InjectableTokenMeta] ?? token;
|
|
357
|
-
if (!injectState) {
|
|
358
|
-
throw new Error(
|
|
359
|
-
"[Injector] Trying to access inject outside of a injectable context"
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
const instance = getFactoryContext().locator.getSyncInstance(
|
|
363
|
-
realToken,
|
|
364
|
-
args
|
|
365
|
-
);
|
|
366
|
-
if (!instance) {
|
|
367
|
-
const request = getRequest(realToken, args);
|
|
368
|
-
if (request.error) {
|
|
369
|
-
throw request.error;
|
|
370
|
-
} else if (request.result) {
|
|
371
|
-
return request.result;
|
|
372
|
-
}
|
|
373
|
-
if (promiseCollector) {
|
|
374
|
-
promiseCollector(request.promise);
|
|
375
|
-
}
|
|
376
|
-
return new Proxy(
|
|
377
|
-
{},
|
|
378
|
-
{
|
|
379
|
-
get() {
|
|
380
|
-
throw new Error(
|
|
381
|
-
`[Injector] Trying to access ${realToken.toString()} before it's initialized, please move the code to a onServiceInit method`
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
return instance;
|
|
388
|
-
}
|
|
389
|
-
function optional2(token, args) {
|
|
390
|
-
try {
|
|
391
|
-
return inject2(token, args);
|
|
392
|
-
} catch {
|
|
393
|
-
return null;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
const injectors = {
|
|
397
|
-
asyncInject: asyncInject2,
|
|
398
|
-
inject: inject2,
|
|
399
|
-
optional: optional2,
|
|
400
|
-
wrapSyncInit: wrapSyncInit2,
|
|
401
|
-
provideFactoryContext: provideFactoryContext2
|
|
402
|
-
};
|
|
403
|
-
return injectors;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// src/utils/get-injectable-token.mts
|
|
407
|
-
function getInjectableToken(target) {
|
|
408
|
-
const token = target[InjectableTokenMeta];
|
|
409
|
-
if (!token) {
|
|
410
|
-
throw new Error(
|
|
411
|
-
`[ServiceLocator] Class ${target.name} is not decorated with @Injectable.`
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
return token;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// src/service-locator-instance-holder.mts
|
|
418
|
-
var ServiceLocatorInstanceHolderStatus = /* @__PURE__ */ ((ServiceLocatorInstanceHolderStatus2) => {
|
|
419
|
-
ServiceLocatorInstanceHolderStatus2["Created"] = "created";
|
|
420
|
-
ServiceLocatorInstanceHolderStatus2["Creating"] = "creating";
|
|
421
|
-
ServiceLocatorInstanceHolderStatus2["Destroying"] = "destroying";
|
|
422
|
-
ServiceLocatorInstanceHolderStatus2["Error"] = "error";
|
|
423
|
-
return ServiceLocatorInstanceHolderStatus2;
|
|
424
|
-
})(ServiceLocatorInstanceHolderStatus || {});
|
|
425
|
-
|
|
426
|
-
// src/base-instance-holder-manager.mts
|
|
427
|
-
var BaseInstanceHolderManager = class {
|
|
428
|
-
constructor(logger = null) {
|
|
429
|
-
this.logger = logger;
|
|
430
|
-
this._holders = /* @__PURE__ */ new Map();
|
|
431
|
-
}
|
|
432
|
-
_holders;
|
|
433
|
-
/**
|
|
434
|
-
* Protected getter for accessing the holders map from subclasses.
|
|
435
|
-
*/
|
|
436
|
-
get holders() {
|
|
437
|
-
return this._holders;
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Deletes a holder by name.
|
|
441
|
-
* @param name The name of the holder to delete
|
|
442
|
-
* @returns true if the holder was deleted, false if it didn't exist
|
|
443
|
-
*/
|
|
444
|
-
delete(name) {
|
|
445
|
-
return this._holders.delete(name);
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Filters holders based on a predicate function.
|
|
449
|
-
* @param predicate Function to test each holder
|
|
450
|
-
* @returns A new Map containing only the holders that match the predicate
|
|
451
|
-
*/
|
|
452
|
-
filter(predicate) {
|
|
453
|
-
return new Map(
|
|
454
|
-
[...this._holders].filter(([key, value]) => predicate(value, key))
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
/**
|
|
458
|
-
* Clears all holders from this manager.
|
|
459
|
-
*/
|
|
460
|
-
clear() {
|
|
461
|
-
this._holders.clear();
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* Gets the number of holders currently managed.
|
|
465
|
-
*/
|
|
466
|
-
size() {
|
|
467
|
-
return this._holders.size;
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Creates a new holder with Creating status and a deferred creation promise.
|
|
471
|
-
* This is useful for creating placeholder holders that can be fulfilled later.
|
|
472
|
-
* @param name The name of the instance
|
|
473
|
-
* @param type The injectable type
|
|
474
|
-
* @param scope The injectable scope
|
|
475
|
-
* @param deps Optional set of dependencies
|
|
476
|
-
* @returns A tuple containing the deferred promise and the holder
|
|
477
|
-
*/
|
|
478
|
-
createCreatingHolder(name, type, scope, deps = /* @__PURE__ */ new Set()) {
|
|
479
|
-
const deferred = Promise.withResolvers();
|
|
480
|
-
const holder = {
|
|
481
|
-
status: "creating" /* Creating */,
|
|
482
|
-
name,
|
|
483
|
-
instance: null,
|
|
484
|
-
creationPromise: deferred.promise,
|
|
485
|
-
destroyPromise: null,
|
|
486
|
-
type,
|
|
487
|
-
scope,
|
|
488
|
-
deps,
|
|
489
|
-
destroyListeners: [],
|
|
490
|
-
createdAt: Date.now()
|
|
491
|
-
};
|
|
492
|
-
return [deferred, holder];
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* Creates a new holder with Created status and an actual instance.
|
|
496
|
-
* This is useful for creating holders that already have their instance ready.
|
|
497
|
-
* @param name The name of the instance
|
|
498
|
-
* @param instance The actual instance to store
|
|
499
|
-
* @param type The injectable type
|
|
500
|
-
* @param scope The injectable scope
|
|
501
|
-
* @param deps Optional set of dependencies
|
|
502
|
-
* @returns The created holder
|
|
503
|
-
*/
|
|
504
|
-
createCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()) {
|
|
505
|
-
const holder = {
|
|
506
|
-
status: "created" /* Created */,
|
|
507
|
-
name,
|
|
508
|
-
instance,
|
|
509
|
-
creationPromise: null,
|
|
510
|
-
destroyPromise: null,
|
|
511
|
-
type,
|
|
512
|
-
scope,
|
|
513
|
-
deps,
|
|
514
|
-
destroyListeners: [],
|
|
515
|
-
createdAt: Date.now()
|
|
516
|
-
};
|
|
517
|
-
return holder;
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* Gets all holder names currently managed.
|
|
521
|
-
*/
|
|
522
|
-
getAllNames() {
|
|
523
|
-
return Array.from(this._holders.keys());
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Gets all holders currently managed.
|
|
527
|
-
*/
|
|
528
|
-
getAllHolders() {
|
|
529
|
-
return Array.from(this._holders.values());
|
|
530
|
-
}
|
|
531
|
-
/**
|
|
532
|
-
* Checks if this manager has any holders.
|
|
533
|
-
*/
|
|
534
|
-
isEmpty() {
|
|
535
|
-
return this._holders.size === 0;
|
|
536
|
-
}
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
// src/injector.mts
|
|
540
|
-
var defaultInjectors = getInjectors();
|
|
541
|
-
var asyncInject = defaultInjectors.asyncInject;
|
|
542
|
-
var inject = defaultInjectors.inject;
|
|
543
|
-
var optional = defaultInjectors.optional;
|
|
544
|
-
var wrapSyncInit = defaultInjectors.wrapSyncInit;
|
|
545
|
-
var provideFactoryContext = defaultInjectors.provideFactoryContext;
|
|
546
|
-
|
|
547
|
-
// src/service-instantiator.mts
|
|
548
|
-
var ServiceInstantiator = class {
|
|
549
|
-
constructor(injectors) {
|
|
550
|
-
this.injectors = injectors;
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Instantiates a service based on its registry record.
|
|
554
|
-
* @param ctx The factory context for dependency injection
|
|
555
|
-
* @param record The factory record from the registry
|
|
556
|
-
* @param args Optional arguments for the service
|
|
557
|
-
* @returns Promise resolving to [undefined, instance] or [error]
|
|
558
|
-
*/
|
|
559
|
-
async instantiateService(ctx, record, args = void 0) {
|
|
560
|
-
try {
|
|
561
|
-
switch (record.type) {
|
|
562
|
-
case "Class" /* Class */:
|
|
563
|
-
return this.instantiateClass(ctx, record, args);
|
|
564
|
-
case "Factory" /* Factory */:
|
|
565
|
-
return this.instantiateFactory(ctx, record, args);
|
|
566
|
-
default:
|
|
567
|
-
throw new Error(
|
|
568
|
-
`[ServiceInstantiator] Unknown service type: ${record.type}`
|
|
569
|
-
);
|
|
570
|
-
}
|
|
571
|
-
} catch (error) {
|
|
572
|
-
return [error instanceof Error ? error : new Error(String(error))];
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Instantiates a class-based service (Injectable decorator).
|
|
577
|
-
* @param ctx The factory context for dependency injection
|
|
578
|
-
* @param record The factory record from the registry
|
|
579
|
-
* @param args Optional arguments for the service constructor
|
|
580
|
-
* @returns Promise resolving to [undefined, instance] or [error]
|
|
581
|
-
*/
|
|
582
|
-
async instantiateClass(ctx, record, args) {
|
|
583
|
-
try {
|
|
584
|
-
const tryLoad = this.injectors.wrapSyncInit(() => {
|
|
585
|
-
const original = this.injectors.provideFactoryContext(ctx);
|
|
586
|
-
let result = new record.target(...args ? [args] : []);
|
|
587
|
-
this.injectors.provideFactoryContext(original);
|
|
588
|
-
return result;
|
|
589
|
-
});
|
|
590
|
-
let [instance, promises, injectState] = tryLoad();
|
|
591
|
-
if (promises.length > 0) {
|
|
592
|
-
const results = await Promise.allSettled(promises);
|
|
593
|
-
if (results.some((result) => result.status === "rejected")) {
|
|
594
|
-
throw new Error(
|
|
595
|
-
`[ServiceInstantiator] Service ${record.target.name} cannot be instantiated.`
|
|
596
|
-
);
|
|
597
|
-
}
|
|
598
|
-
const newRes = tryLoad(injectState);
|
|
599
|
-
instance = newRes[0];
|
|
600
|
-
promises = newRes[1];
|
|
601
|
-
}
|
|
602
|
-
if (promises.length > 0) {
|
|
603
|
-
console.error(`[ServiceInstantiator] ${record.target.name} has problem with it's definition.
|
|
604
|
-
|
|
605
|
-
One or more of the dependencies are registered as a InjectableScope.Instance and are used with inject.
|
|
606
|
-
|
|
607
|
-
Please use inject asyncInject of inject to load those dependencies.`);
|
|
608
|
-
throw new Error(
|
|
609
|
-
`[ServiceInstantiator] Service ${record.target.name} cannot be instantiated.`
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
if ("onServiceInit" in instance) {
|
|
613
|
-
await instance.onServiceInit();
|
|
614
|
-
}
|
|
615
|
-
if ("onServiceDestroy" in instance) {
|
|
616
|
-
ctx.addDestroyListener(async () => {
|
|
617
|
-
await instance.onServiceDestroy();
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
return [void 0, instance];
|
|
621
|
-
} catch (error) {
|
|
622
|
-
return [error instanceof Error ? error : new Error(String(error))];
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
/**
|
|
626
|
-
* Instantiates a factory-based service (Factory decorator).
|
|
627
|
-
* @param ctx The factory context for dependency injection
|
|
628
|
-
* @param record The factory record from the registry
|
|
629
|
-
* @param args Optional arguments for the factory
|
|
630
|
-
* @returns Promise resolving to [undefined, instance] or [error]
|
|
631
|
-
*/
|
|
632
|
-
async instantiateFactory(ctx, record, args) {
|
|
633
|
-
try {
|
|
634
|
-
const tryLoad = this.injectors.wrapSyncInit(() => {
|
|
635
|
-
const original = this.injectors.provideFactoryContext(ctx);
|
|
636
|
-
let result = new record.target();
|
|
637
|
-
this.injectors.provideFactoryContext(original);
|
|
638
|
-
return result;
|
|
639
|
-
});
|
|
640
|
-
let [builder, promises, injectState] = tryLoad();
|
|
641
|
-
if (promises.length > 0) {
|
|
642
|
-
const results = await Promise.allSettled(promises);
|
|
643
|
-
if (results.some((result) => result.status === "rejected")) {
|
|
644
|
-
throw new Error(
|
|
645
|
-
`[ServiceInstantiator] Service ${record.target.name} cannot be instantiated.`
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
const newRes = tryLoad(injectState);
|
|
649
|
-
builder = newRes[0];
|
|
650
|
-
promises = newRes[1];
|
|
651
|
-
}
|
|
652
|
-
if (promises.length > 0) {
|
|
653
|
-
console.error(`[ServiceInstantiator] ${record.target.name} has problem with it's definition.
|
|
654
|
-
|
|
655
|
-
One or more of the dependencies are registered as a InjectableScope.Instance and are used with inject.
|
|
656
|
-
|
|
657
|
-
Please use asyncInject instead of inject to load those dependencies.`);
|
|
658
|
-
throw new Error(
|
|
659
|
-
`[ServiceInstantiator] Service ${record.target.name} cannot be instantiated.`
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
if (typeof builder.create !== "function") {
|
|
663
|
-
throw new Error(
|
|
664
|
-
`[ServiceInstantiator] Factory ${record.target.name} does not implement the create method.`
|
|
665
|
-
);
|
|
666
|
-
}
|
|
667
|
-
const instance = await builder.create(ctx, args);
|
|
668
|
-
return [void 0, instance];
|
|
669
|
-
} catch (error) {
|
|
670
|
-
return [error instanceof Error ? error : new Error(String(error))];
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
};
|
|
674
|
-
|
|
675
|
-
// src/request-context-holder.mts
|
|
676
|
-
var DefaultRequestContextHolder = class extends BaseInstanceHolderManager {
|
|
677
|
-
constructor(requestId, priority = 100, initialMetadata) {
|
|
678
|
-
super(null);
|
|
679
|
-
this.requestId = requestId;
|
|
680
|
-
this.priority = priority;
|
|
681
|
-
if (initialMetadata) {
|
|
682
|
-
Object.entries(initialMetadata).forEach(([key, value]) => {
|
|
683
|
-
this.metadata.set(key, value);
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
metadata = /* @__PURE__ */ new Map();
|
|
688
|
-
createdAt = Date.now();
|
|
689
|
-
/**
|
|
690
|
-
* Public getter for holders to maintain interface compatibility.
|
|
691
|
-
*/
|
|
692
|
-
get holders() {
|
|
693
|
-
return this._holders;
|
|
694
|
-
}
|
|
695
|
-
/**
|
|
696
|
-
* Gets a holder by name. For RequestContextHolder, this is a simple lookup.
|
|
697
|
-
*/
|
|
698
|
-
get(name) {
|
|
699
|
-
return this._holders.get(name);
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Sets a holder by name.
|
|
703
|
-
*/
|
|
704
|
-
set(name, holder) {
|
|
705
|
-
this._holders.set(name, holder);
|
|
706
|
-
}
|
|
707
|
-
/**
|
|
708
|
-
* Checks if a holder exists by name.
|
|
709
|
-
*/
|
|
710
|
-
has(name) {
|
|
711
|
-
return this._holders.has(name);
|
|
712
|
-
}
|
|
713
|
-
addInstance(instanceName, instance, holder) {
|
|
714
|
-
if (instanceName instanceof InjectionToken) {
|
|
715
|
-
const name = instanceName.toString();
|
|
716
|
-
const createdHolder = this.createCreatedHolder(
|
|
717
|
-
name,
|
|
718
|
-
instance,
|
|
719
|
-
"Class" /* Class */,
|
|
720
|
-
"Singleton" /* Singleton */,
|
|
721
|
-
/* @__PURE__ */ new Set()
|
|
722
|
-
);
|
|
723
|
-
this._holders.set(name, createdHolder);
|
|
724
|
-
} else {
|
|
725
|
-
if (!holder) {
|
|
726
|
-
throw new Error("Holder is required when adding an instance by name");
|
|
727
|
-
}
|
|
728
|
-
this._holders.set(instanceName, holder);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
clear() {
|
|
732
|
-
super.clear();
|
|
733
|
-
this.metadata.clear();
|
|
734
|
-
}
|
|
735
|
-
getMetadata(key) {
|
|
736
|
-
return this.metadata.get(key);
|
|
737
|
-
}
|
|
738
|
-
setMetadata(key, value) {
|
|
739
|
-
this.metadata.set(key, value);
|
|
740
|
-
}
|
|
741
|
-
};
|
|
742
|
-
function createRequestContextHolder(requestId, priority = 100, initialMetadata) {
|
|
743
|
-
return new DefaultRequestContextHolder(requestId, priority, initialMetadata);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// src/service-locator-event-bus.mts
|
|
747
|
-
var ServiceLocatorEventBus = class {
|
|
748
|
-
constructor(logger = null) {
|
|
749
|
-
this.logger = logger;
|
|
750
|
-
}
|
|
751
|
-
listeners = /* @__PURE__ */ new Map();
|
|
752
|
-
on(ns, event, listener) {
|
|
753
|
-
this.logger?.debug(`[ServiceLocatorEventBus]#on(): ns:${ns} event:${event}`);
|
|
754
|
-
if (!this.listeners.has(ns)) {
|
|
755
|
-
this.listeners.set(ns, /* @__PURE__ */ new Map());
|
|
756
|
-
}
|
|
757
|
-
const nsEvents = this.listeners.get(ns);
|
|
758
|
-
if (!nsEvents.has(event)) {
|
|
759
|
-
nsEvents.set(event, /* @__PURE__ */ new Set());
|
|
760
|
-
}
|
|
761
|
-
nsEvents.get(event).add(listener);
|
|
762
|
-
return () => {
|
|
763
|
-
nsEvents.get(event)?.delete(listener);
|
|
764
|
-
if (nsEvents.get(event)?.size === 0) {
|
|
765
|
-
nsEvents.delete(event);
|
|
766
|
-
}
|
|
767
|
-
if (nsEvents.size === 0) {
|
|
768
|
-
this.listeners.delete(ns);
|
|
769
|
-
}
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
async emit(key, event) {
|
|
773
|
-
if (!this.listeners.has(key)) {
|
|
774
|
-
return;
|
|
775
|
-
}
|
|
776
|
-
const events = this.listeners.get(key);
|
|
777
|
-
this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${event}`);
|
|
778
|
-
const res = await Promise.allSettled(
|
|
779
|
-
[...events.get(event) ?? []].map((listener) => listener(event))
|
|
780
|
-
).then((results) => {
|
|
781
|
-
const res2 = results.filter((result) => result.status === "rejected").map((result) => {
|
|
782
|
-
this.logger?.warn(
|
|
783
|
-
`[ServiceLocatorEventBus]#emit(): ${key}:${event} rejected with`,
|
|
784
|
-
result.reason
|
|
785
|
-
);
|
|
786
|
-
return result;
|
|
787
|
-
});
|
|
788
|
-
if (res2.length > 0) {
|
|
789
|
-
return Promise.reject(res2);
|
|
790
|
-
}
|
|
791
|
-
return results;
|
|
792
|
-
});
|
|
793
|
-
return res;
|
|
794
|
-
}
|
|
795
|
-
};
|
|
796
|
-
|
|
797
|
-
// src/service-locator-manager.mts
|
|
798
|
-
var ServiceLocatorManager = class extends BaseInstanceHolderManager {
|
|
799
|
-
constructor(logger = null) {
|
|
800
|
-
super(logger);
|
|
801
|
-
}
|
|
802
|
-
get(name) {
|
|
803
|
-
const holder = this._holders.get(name);
|
|
804
|
-
if (holder) {
|
|
805
|
-
if (holder.status === "destroying" /* Destroying */) {
|
|
806
|
-
this.logger?.log(
|
|
807
|
-
`[ServiceLocatorManager]#getInstanceHolder() Instance ${holder.name} is destroying`
|
|
808
|
-
);
|
|
809
|
-
return [DIError.instanceDestroying(holder.name), holder];
|
|
810
|
-
} else if (holder.status === "error" /* Error */) {
|
|
811
|
-
this.logger?.log(
|
|
812
|
-
`[ServiceLocatorManager]#getInstanceHolder() Instance ${holder.name} is in error state`
|
|
813
|
-
);
|
|
814
|
-
return [holder.instance, holder];
|
|
815
|
-
}
|
|
816
|
-
return [void 0, holder];
|
|
817
|
-
} else {
|
|
818
|
-
this.logger?.log(
|
|
819
|
-
`[ServiceLocatorManager]#getInstanceHolder() Instance ${name} not found`
|
|
820
|
-
);
|
|
821
|
-
return [DIError.instanceNotFound(name)];
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
set(name, holder) {
|
|
825
|
-
this._holders.set(name, holder);
|
|
826
|
-
}
|
|
827
|
-
has(name) {
|
|
828
|
-
const [error, holder] = this.get(name);
|
|
829
|
-
if (!error) {
|
|
830
|
-
return [void 0, true];
|
|
831
|
-
}
|
|
832
|
-
if (error.code === "InstanceDestroying" /* InstanceDestroying */) {
|
|
833
|
-
return [error];
|
|
834
|
-
}
|
|
835
|
-
return [void 0, !!holder];
|
|
836
|
-
}
|
|
837
|
-
// delete and filter methods are inherited from BaseInstanceHolderManager
|
|
838
|
-
// createCreatingHolder method is inherited from BaseInstanceHolderManager
|
|
839
|
-
/**
|
|
840
|
-
* Creates a new holder with Created status and an actual instance.
|
|
841
|
-
* This is useful for creating holders that already have their instance ready.
|
|
842
|
-
* @param name The name of the instance
|
|
843
|
-
* @param instance The actual instance to store
|
|
844
|
-
* @param type The injectable type
|
|
845
|
-
* @param scope The injectable scope
|
|
846
|
-
* @param deps Optional set of dependencies
|
|
847
|
-
* @returns The created holder
|
|
848
|
-
*/
|
|
849
|
-
storeCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()) {
|
|
850
|
-
const holder = this.createCreatedHolder(name, instance, type, scope, deps);
|
|
851
|
-
this._holders.set(name, holder);
|
|
852
|
-
return holder;
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
|
|
856
|
-
// src/instance-resolver.mts
|
|
857
|
-
var InstanceResolver = class {
|
|
858
|
-
constructor(registry, manager, serviceInstantiator, tokenProcessor, logger = null, serviceLocator) {
|
|
859
|
-
this.registry = registry;
|
|
860
|
-
this.manager = manager;
|
|
861
|
-
this.serviceInstantiator = serviceInstantiator;
|
|
862
|
-
this.tokenProcessor = tokenProcessor;
|
|
863
|
-
this.logger = logger;
|
|
864
|
-
this.serviceLocator = serviceLocator;
|
|
865
|
-
}
|
|
866
|
-
/**
|
|
867
|
-
* Resolves an instance for the given token and arguments.
|
|
868
|
-
*/
|
|
869
|
-
async resolveInstance(token, args, requestContext) {
|
|
870
|
-
const [err, data] = await this.resolveTokenAndPrepareInstanceName(
|
|
871
|
-
token,
|
|
872
|
-
args
|
|
873
|
-
);
|
|
874
|
-
if (err) {
|
|
875
|
-
return [err];
|
|
876
|
-
}
|
|
877
|
-
const {
|
|
878
|
-
instanceName,
|
|
879
|
-
validatedArgs,
|
|
880
|
-
realToken
|
|
881
|
-
} = data;
|
|
882
|
-
const [error, holder] = await this.retrieveOrCreateInstanceByInstanceName(
|
|
883
|
-
instanceName,
|
|
884
|
-
realToken,
|
|
885
|
-
validatedArgs,
|
|
886
|
-
requestContext
|
|
887
|
-
);
|
|
888
|
-
if (error) {
|
|
889
|
-
return [error];
|
|
890
|
-
}
|
|
891
|
-
return [void 0, holder.instance];
|
|
892
|
-
}
|
|
893
|
-
/**
|
|
894
|
-
* Gets a synchronous instance (for sync operations).
|
|
895
|
-
*/
|
|
896
|
-
getSyncInstance(token, args, currentRequestContext) {
|
|
897
|
-
const [err, { actualToken, validatedArgs }] = this.tokenProcessor.validateAndResolveTokenArgs(token, args);
|
|
898
|
-
if (err) {
|
|
899
|
-
return null;
|
|
900
|
-
}
|
|
901
|
-
const instanceName = this.tokenProcessor.generateInstanceName(
|
|
902
|
-
actualToken,
|
|
903
|
-
validatedArgs
|
|
904
|
-
);
|
|
905
|
-
if (currentRequestContext) {
|
|
906
|
-
const requestHolder = currentRequestContext.get(instanceName);
|
|
907
|
-
if (requestHolder) {
|
|
908
|
-
return requestHolder.instance;
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
const [error, holder] = this.manager.get(instanceName);
|
|
912
|
-
if (error) {
|
|
913
|
-
return null;
|
|
914
|
-
}
|
|
915
|
-
return holder.instance;
|
|
916
|
-
}
|
|
917
|
-
/**
|
|
918
|
-
* Internal method to resolve token args and create instance name.
|
|
919
|
-
* Handles factory token resolution and validation.
|
|
920
|
-
*/
|
|
921
|
-
async resolveTokenAndPrepareInstanceName(token, args) {
|
|
922
|
-
const [err, { actualToken, validatedArgs }] = this.tokenProcessor.validateAndResolveTokenArgs(token, args);
|
|
923
|
-
if (err instanceof DIError && err.code === "UnknownError" /* UnknownError */) {
|
|
924
|
-
return [err];
|
|
925
|
-
} else if (err instanceof DIError && err.code === "FactoryTokenNotResolved" /* FactoryTokenNotResolved */ && actualToken instanceof FactoryInjectionToken) {
|
|
926
|
-
this.logger?.log(
|
|
927
|
-
`[InstanceResolver]#resolveTokenAndPrepareInstanceName() Factory token not resolved, resolving it`
|
|
928
|
-
);
|
|
929
|
-
await actualToken.resolve(this.createFactoryContext());
|
|
930
|
-
return this.resolveTokenAndPrepareInstanceName(token);
|
|
931
|
-
}
|
|
932
|
-
const instanceName = this.tokenProcessor.generateInstanceName(
|
|
933
|
-
actualToken,
|
|
934
|
-
validatedArgs
|
|
935
|
-
);
|
|
936
|
-
const realToken = actualToken instanceof BoundInjectionToken || actualToken instanceof FactoryInjectionToken ? actualToken.token : actualToken;
|
|
937
|
-
return [void 0, { instanceName, validatedArgs, actualToken, realToken }];
|
|
938
|
-
}
|
|
939
|
-
/**
|
|
940
|
-
* Gets an instance by its instance name, handling all the logic after instance name creation.
|
|
941
|
-
*/
|
|
942
|
-
async retrieveOrCreateInstanceByInstanceName(instanceName, realToken, realArgs, requestContext) {
|
|
943
|
-
const existingHolder = await this.tryGetExistingInstance(
|
|
944
|
-
instanceName,
|
|
945
|
-
realToken,
|
|
946
|
-
requestContext
|
|
947
|
-
);
|
|
948
|
-
if (existingHolder) {
|
|
949
|
-
return existingHolder;
|
|
950
|
-
}
|
|
951
|
-
const result = await this.createNewInstance(
|
|
952
|
-
instanceName,
|
|
953
|
-
realToken,
|
|
954
|
-
realArgs,
|
|
955
|
-
requestContext
|
|
956
|
-
);
|
|
957
|
-
if (result[0]) {
|
|
958
|
-
return [result[0]];
|
|
959
|
-
}
|
|
960
|
-
const [, holder] = result;
|
|
961
|
-
return this.waitForInstanceReady(holder);
|
|
962
|
-
}
|
|
963
|
-
/**
|
|
964
|
-
* Attempts to retrieve an existing instance, handling request-scoped and singleton instances.
|
|
965
|
-
* Returns null if no instance exists and a new one should be created.
|
|
966
|
-
*/
|
|
967
|
-
async tryGetExistingInstance(instanceName, realToken, requestContext) {
|
|
968
|
-
const requestResult = await this.tryGetRequestScopedInstance(
|
|
969
|
-
instanceName,
|
|
970
|
-
realToken,
|
|
971
|
-
requestContext
|
|
972
|
-
);
|
|
973
|
-
if (requestResult) {
|
|
974
|
-
return requestResult;
|
|
975
|
-
}
|
|
976
|
-
return this.tryGetSingletonInstance(instanceName);
|
|
977
|
-
}
|
|
978
|
-
/**
|
|
979
|
-
* Attempts to get a request-scoped instance if applicable.
|
|
980
|
-
*/
|
|
981
|
-
async tryGetRequestScopedInstance(instanceName, realToken, requestContext) {
|
|
982
|
-
if (!this.registry.has(realToken)) {
|
|
983
|
-
return null;
|
|
984
|
-
}
|
|
985
|
-
const record = this.registry.get(realToken);
|
|
986
|
-
if (record.scope !== "Request" /* Request */) {
|
|
987
|
-
return null;
|
|
988
|
-
}
|
|
989
|
-
if (!requestContext) {
|
|
990
|
-
this.logger?.log(
|
|
991
|
-
`[InstanceResolver] No current request context available for request-scoped service ${instanceName}`
|
|
992
|
-
);
|
|
993
|
-
return [
|
|
994
|
-
DIError.unknown(
|
|
995
|
-
`No current request context available for request-scoped service ${instanceName}`
|
|
996
|
-
)
|
|
997
|
-
];
|
|
998
|
-
}
|
|
999
|
-
const requestHolder = requestContext.get(instanceName);
|
|
1000
|
-
if (!requestHolder) {
|
|
1001
|
-
return null;
|
|
1002
|
-
}
|
|
1003
|
-
return this.waitForInstanceReady(requestHolder);
|
|
1004
|
-
}
|
|
1005
|
-
/**
|
|
1006
|
-
* Attempts to get a singleton instance from the manager.
|
|
1007
|
-
*/
|
|
1008
|
-
async tryGetSingletonInstance(instanceName) {
|
|
1009
|
-
const [error, holder] = this.manager.get(instanceName);
|
|
1010
|
-
if (!error) {
|
|
1011
|
-
return this.waitForInstanceReady(holder);
|
|
1012
|
-
}
|
|
1013
|
-
switch (error.code) {
|
|
1014
|
-
case "InstanceDestroying" /* InstanceDestroying */:
|
|
1015
|
-
this.logger?.log(
|
|
1016
|
-
`[InstanceResolver] Instance ${instanceName} is being destroyed, waiting...`
|
|
1017
|
-
);
|
|
1018
|
-
await holder?.destroyPromise;
|
|
1019
|
-
return this.tryGetSingletonInstance(instanceName);
|
|
1020
|
-
case "InstanceNotFound" /* InstanceNotFound */:
|
|
1021
|
-
return null;
|
|
1022
|
-
// Instance doesn't exist, should create new one
|
|
1023
|
-
default:
|
|
1024
|
-
return [error];
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
/**
|
|
1028
|
-
* Waits for an instance holder to be ready and returns the appropriate result.
|
|
1029
|
-
*/
|
|
1030
|
-
async waitForInstanceReady(holder) {
|
|
1031
|
-
switch (holder.status) {
|
|
1032
|
-
case "creating" /* Creating */:
|
|
1033
|
-
await holder.creationPromise;
|
|
1034
|
-
return this.waitForInstanceReady(holder);
|
|
1035
|
-
case "destroying" /* Destroying */:
|
|
1036
|
-
return [DIError.instanceDestroying(holder.name)];
|
|
1037
|
-
case "error" /* Error */:
|
|
1038
|
-
return [holder.instance];
|
|
1039
|
-
case "created" /* Created */:
|
|
1040
|
-
return [void 0, holder];
|
|
1041
|
-
default:
|
|
1042
|
-
return [DIError.instanceNotFound("unknown")];
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Creates a new instance for the given token and arguments.
|
|
1047
|
-
*/
|
|
1048
|
-
async createNewInstance(instanceName, realToken, args, requestContext) {
|
|
1049
|
-
this.logger?.log(
|
|
1050
|
-
`[InstanceResolver]#createNewInstance() Creating instance for ${instanceName}`
|
|
1051
|
-
);
|
|
1052
|
-
if (this.registry.has(realToken)) {
|
|
1053
|
-
return this.instantiateServiceFromRegistry(
|
|
1054
|
-
instanceName,
|
|
1055
|
-
realToken,
|
|
1056
|
-
args,
|
|
1057
|
-
requestContext
|
|
1058
|
-
);
|
|
1059
|
-
} else {
|
|
1060
|
-
return [DIError.factoryNotFound(realToken.name.toString())];
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Instantiates a service from the registry using the service instantiator.
|
|
1065
|
-
*/
|
|
1066
|
-
instantiateServiceFromRegistry(instanceName, token, args, requestContext) {
|
|
1067
|
-
this.logger?.log(
|
|
1068
|
-
`[InstanceResolver]#instantiateServiceFromRegistry(): Creating instance for ${instanceName} from abstract factory`
|
|
1069
|
-
);
|
|
1070
|
-
const ctx = this.createFactoryContext();
|
|
1071
|
-
let record = this.registry.get(token);
|
|
1072
|
-
let { scope, type } = record;
|
|
1073
|
-
const [deferred, holder] = this.manager.createCreatingHolder(
|
|
1074
|
-
instanceName,
|
|
1075
|
-
type,
|
|
1076
|
-
scope,
|
|
1077
|
-
ctx.deps
|
|
1078
|
-
);
|
|
1079
|
-
this.serviceInstantiator.instantiateService(ctx, record, args).then(async ([error, instance]) => {
|
|
1080
|
-
await this.handleInstantiationResult(
|
|
1081
|
-
instanceName,
|
|
1082
|
-
holder,
|
|
1083
|
-
ctx,
|
|
1084
|
-
deferred,
|
|
1085
|
-
scope,
|
|
1086
|
-
error,
|
|
1087
|
-
instance,
|
|
1088
|
-
requestContext
|
|
1089
|
-
);
|
|
1090
|
-
}).catch(async (error) => {
|
|
1091
|
-
await this.handleInstantiationError(
|
|
1092
|
-
instanceName,
|
|
1093
|
-
holder,
|
|
1094
|
-
deferred,
|
|
1095
|
-
scope,
|
|
1096
|
-
error
|
|
1097
|
-
);
|
|
1098
|
-
});
|
|
1099
|
-
this.storeInstanceByScope(scope, instanceName, holder, requestContext);
|
|
1100
|
-
return [void 0, holder];
|
|
1101
|
-
}
|
|
1102
|
-
/**
|
|
1103
|
-
* Handles the result of service instantiation.
|
|
1104
|
-
*/
|
|
1105
|
-
async handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance, _requestContext) {
|
|
1106
|
-
holder.destroyListeners = ctx.getDestroyListeners();
|
|
1107
|
-
holder.creationPromise = null;
|
|
1108
|
-
if (error) {
|
|
1109
|
-
await this.handleInstantiationError(
|
|
1110
|
-
instanceName,
|
|
1111
|
-
holder,
|
|
1112
|
-
deferred,
|
|
1113
|
-
scope,
|
|
1114
|
-
error
|
|
1115
|
-
);
|
|
1116
|
-
} else {
|
|
1117
|
-
await this.handleInstantiationSuccess(
|
|
1118
|
-
instanceName,
|
|
1119
|
-
holder,
|
|
1120
|
-
ctx,
|
|
1121
|
-
deferred,
|
|
1122
|
-
instance
|
|
1123
|
-
);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
/**
|
|
1127
|
-
* Handles successful service instantiation.
|
|
1128
|
-
*/
|
|
1129
|
-
async handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance) {
|
|
1130
|
-
holder.instance = instance;
|
|
1131
|
-
holder.status = "created" /* Created */;
|
|
1132
|
-
if (ctx.deps.size > 0) {
|
|
1133
|
-
ctx.deps.forEach((dependency) => {
|
|
1134
|
-
holder.destroyListeners.push(
|
|
1135
|
-
this.serviceLocator.getEventBus().on(dependency, "destroy", () => {
|
|
1136
|
-
this.logger?.log(
|
|
1137
|
-
`[InstanceResolver] Dependency ${dependency} destroyed, invalidating ${instanceName}`
|
|
1138
|
-
);
|
|
1139
|
-
this.serviceLocator.getServiceInvalidator().invalidate(instanceName);
|
|
1140
|
-
})
|
|
1141
|
-
);
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
this.logger?.log(
|
|
1145
|
-
`[InstanceResolver] Instance ${instanceName} created successfully`
|
|
1146
|
-
);
|
|
1147
|
-
deferred.resolve([void 0, instance]);
|
|
1148
|
-
}
|
|
1149
|
-
/**
|
|
1150
|
-
* Handles service instantiation errors.
|
|
1151
|
-
*/
|
|
1152
|
-
async handleInstantiationError(instanceName, holder, deferred, scope, error) {
|
|
1153
|
-
this.logger?.error(
|
|
1154
|
-
`[InstanceResolver] Error creating instance for ${instanceName}`,
|
|
1155
|
-
error
|
|
1156
|
-
);
|
|
1157
|
-
holder.status = "error" /* Error */;
|
|
1158
|
-
holder.instance = error;
|
|
1159
|
-
holder.creationPromise = null;
|
|
1160
|
-
if (scope === "Singleton" /* Singleton */) {
|
|
1161
|
-
this.logger?.log(
|
|
1162
|
-
`[InstanceResolver] Singleton ${instanceName} failed, will be invalidated`
|
|
1163
|
-
);
|
|
1164
|
-
this.serviceLocator.getServiceInvalidator().invalidate(instanceName);
|
|
1165
|
-
}
|
|
1166
|
-
deferred.reject(error);
|
|
1167
|
-
}
|
|
1168
|
-
/**
|
|
1169
|
-
* Stores an instance holder based on its scope.
|
|
1170
|
-
*/
|
|
1171
|
-
storeInstanceByScope(scope, instanceName, holder, requestContext) {
|
|
1172
|
-
switch (scope) {
|
|
1173
|
-
case "Singleton" /* Singleton */:
|
|
1174
|
-
this.logger?.debug(
|
|
1175
|
-
`[InstanceResolver] Setting singleton instance for ${instanceName}`
|
|
1176
|
-
);
|
|
1177
|
-
this.manager.set(instanceName, holder);
|
|
1178
|
-
break;
|
|
1179
|
-
case "Request" /* Request */:
|
|
1180
|
-
if (requestContext) {
|
|
1181
|
-
this.logger?.debug(
|
|
1182
|
-
`[InstanceResolver] Setting request-scoped instance for ${instanceName}`
|
|
1183
|
-
);
|
|
1184
|
-
requestContext.addInstance(instanceName, holder.instance, holder);
|
|
1185
|
-
}
|
|
1186
|
-
break;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
/**
|
|
1190
|
-
* Creates a factory context for dependency injection during service instantiation.
|
|
1191
|
-
*/
|
|
1192
|
-
createFactoryContext() {
|
|
1193
|
-
return this.tokenProcessor.createFactoryContext(this.serviceLocator);
|
|
1194
|
-
}
|
|
1195
|
-
};
|
|
1196
|
-
|
|
1197
|
-
// src/request-context-manager.mts
|
|
1198
|
-
var RequestContextManager = class {
|
|
1199
|
-
constructor(logger = null) {
|
|
1200
|
-
this.logger = logger;
|
|
1201
|
-
}
|
|
1202
|
-
requestContexts = /* @__PURE__ */ new Map();
|
|
1203
|
-
currentRequestContext = null;
|
|
1204
|
-
/**
|
|
1205
|
-
* Begins a new request context with the given parameters.
|
|
1206
|
-
* @param requestId Unique identifier for this request
|
|
1207
|
-
* @param metadata Optional metadata for the request
|
|
1208
|
-
* @param priority Priority for resolution (higher = more priority)
|
|
1209
|
-
* @returns The created request context holder
|
|
1210
|
-
*/
|
|
1211
|
-
beginRequest(requestId, metadata, priority = 100) {
|
|
1212
|
-
if (this.requestContexts.has(requestId)) {
|
|
1213
|
-
throw new Error(
|
|
1214
|
-
`[RequestContextManager] Request context ${requestId} already exists`
|
|
1215
|
-
);
|
|
1216
|
-
}
|
|
1217
|
-
const contextHolder = new DefaultRequestContextHolder(
|
|
1218
|
-
requestId,
|
|
1219
|
-
priority,
|
|
1220
|
-
metadata
|
|
1221
|
-
);
|
|
1222
|
-
this.requestContexts.set(requestId, contextHolder);
|
|
1223
|
-
this.currentRequestContext = contextHolder;
|
|
1224
|
-
this.logger?.log(
|
|
1225
|
-
`[RequestContextManager] Started request context: ${requestId}`
|
|
1226
|
-
);
|
|
1227
|
-
return contextHolder;
|
|
1228
|
-
}
|
|
1229
|
-
/**
|
|
1230
|
-
* Ends a request context and cleans up all associated instances.
|
|
1231
|
-
* @param requestId The request ID to end
|
|
1232
|
-
*/
|
|
1233
|
-
async endRequest(requestId) {
|
|
1234
|
-
const contextHolder = this.requestContexts.get(requestId);
|
|
1235
|
-
if (!contextHolder) {
|
|
1236
|
-
this.logger?.warn(
|
|
1237
|
-
`[RequestContextManager] Request context ${requestId} not found`
|
|
1238
|
-
);
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
this.logger?.log(
|
|
1242
|
-
`[RequestContextManager] Ending request context: ${requestId}`
|
|
1243
|
-
);
|
|
1244
|
-
const cleanupPromises = [];
|
|
1245
|
-
for (const [, holder] of contextHolder.holders) {
|
|
1246
|
-
if (holder.destroyListeners.length > 0) {
|
|
1247
|
-
cleanupPromises.push(
|
|
1248
|
-
Promise.all(holder.destroyListeners.map((listener) => listener()))
|
|
1249
|
-
);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
await Promise.all(cleanupPromises);
|
|
1253
|
-
contextHolder.clear();
|
|
1254
|
-
this.requestContexts.delete(requestId);
|
|
1255
|
-
if (this.currentRequestContext === contextHolder) {
|
|
1256
|
-
this.currentRequestContext = Array.from(this.requestContexts.values()).at(-1) ?? null;
|
|
1257
|
-
}
|
|
1258
|
-
this.logger?.log(
|
|
1259
|
-
`[RequestContextManager] Request context ${requestId} ended`
|
|
1260
|
-
);
|
|
1261
|
-
}
|
|
1262
|
-
/**
|
|
1263
|
-
* Gets the current request context.
|
|
1264
|
-
* @returns The current request context holder or null
|
|
1265
|
-
*/
|
|
1266
|
-
getCurrentRequestContext() {
|
|
1267
|
-
return this.currentRequestContext;
|
|
1268
|
-
}
|
|
1269
|
-
/**
|
|
1270
|
-
* Sets the current request context.
|
|
1271
|
-
* @param requestId The request ID to set as current
|
|
1272
|
-
*/
|
|
1273
|
-
setCurrentRequestContext(requestId) {
|
|
1274
|
-
const contextHolder = this.requestContexts.get(requestId);
|
|
1275
|
-
if (!contextHolder) {
|
|
1276
|
-
throw new Error(
|
|
1277
|
-
`[RequestContextManager] Request context ${requestId} not found`
|
|
1278
|
-
);
|
|
1279
|
-
}
|
|
1280
|
-
this.currentRequestContext = contextHolder;
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Gets all request contexts.
|
|
1284
|
-
* @returns Map of request contexts
|
|
1285
|
-
*/
|
|
1286
|
-
getRequestContexts() {
|
|
1287
|
-
return this.requestContexts;
|
|
1288
|
-
}
|
|
1289
|
-
/**
|
|
1290
|
-
* Clears all request contexts.
|
|
1291
|
-
*/
|
|
1292
|
-
async clearAllRequestContexts() {
|
|
1293
|
-
const requestIds = Array.from(this.requestContexts.keys());
|
|
1294
|
-
if (requestIds.length === 0) {
|
|
1295
|
-
this.logger?.log("[RequestContextManager] No request contexts to clear");
|
|
1296
|
-
return;
|
|
1297
|
-
}
|
|
1298
|
-
this.logger?.log(
|
|
1299
|
-
`[RequestContextManager] Clearing ${requestIds.length} request contexts: ${requestIds.join(", ")}`
|
|
1300
|
-
);
|
|
1301
|
-
for (const requestId of requestIds) {
|
|
1302
|
-
try {
|
|
1303
|
-
await this.endRequest(requestId);
|
|
1304
|
-
} catch (error) {
|
|
1305
|
-
this.logger?.error(
|
|
1306
|
-
`[RequestContextManager] Error clearing request context ${requestId}:`,
|
|
1307
|
-
error
|
|
1308
|
-
);
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
};
|
|
1313
|
-
|
|
1314
|
-
// src/service-invalidator.mts
|
|
1315
|
-
var ServiceInvalidator = class {
|
|
1316
|
-
constructor(manager, requestContextManager, eventBus, logger = null) {
|
|
1317
|
-
this.manager = manager;
|
|
1318
|
-
this.requestContextManager = requestContextManager;
|
|
1319
|
-
this.eventBus = eventBus;
|
|
1320
|
-
this.logger = logger;
|
|
1321
|
-
}
|
|
1322
|
-
/**
|
|
1323
|
-
* Invalidates a service and all its dependencies.
|
|
1324
|
-
*/
|
|
1325
|
-
invalidate(service, round = 1) {
|
|
1326
|
-
this.logger?.log(
|
|
1327
|
-
`[ServiceInvalidator] Starting invalidation process for ${service}`
|
|
1328
|
-
);
|
|
1329
|
-
const [, toInvalidate] = this.manager.get(service);
|
|
1330
|
-
const promises = [];
|
|
1331
|
-
if (toInvalidate) {
|
|
1332
|
-
promises.push(this.invalidateHolder(service, toInvalidate, round));
|
|
1333
|
-
}
|
|
1334
|
-
const requestContexts = this.requestContextManager.getRequestContexts();
|
|
1335
|
-
for (const [requestId, requestContext] of requestContexts.entries()) {
|
|
1336
|
-
const holder = requestContext.get(service);
|
|
1337
|
-
if (holder) {
|
|
1338
|
-
this.logger?.log(
|
|
1339
|
-
`[ServiceInvalidator] Invalidating request-scoped instance ${service} in request ${requestId}`
|
|
1340
|
-
);
|
|
1341
|
-
promises.push(
|
|
1342
|
-
this.invalidateRequestHolder(requestId, service, holder, round)
|
|
1343
|
-
);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
return Promise.all(promises);
|
|
1347
|
-
}
|
|
1348
|
-
/**
|
|
1349
|
-
* Gracefully clears all services in the ServiceLocator using invalidation logic.
|
|
1350
|
-
* This method respects service dependencies and ensures proper cleanup order.
|
|
1351
|
-
* Services that depend on others will be invalidated first, then their dependencies.
|
|
1352
|
-
*/
|
|
1353
|
-
async clearAll(options = {}) {
|
|
1354
|
-
const {
|
|
1355
|
-
clearRequestContexts = true,
|
|
1356
|
-
maxRounds = 10,
|
|
1357
|
-
waitForSettlement = true
|
|
1358
|
-
} = options;
|
|
1359
|
-
this.logger?.log(
|
|
1360
|
-
"[ServiceInvalidator] Starting graceful clearing of all services"
|
|
1361
|
-
);
|
|
1362
|
-
if (waitForSettlement) {
|
|
1363
|
-
this.logger?.log(
|
|
1364
|
-
"[ServiceInvalidator] Waiting for all services to settle..."
|
|
1365
|
-
);
|
|
1366
|
-
await this.ready();
|
|
1367
|
-
}
|
|
1368
|
-
const allServiceNames = this.getAllServiceNames();
|
|
1369
|
-
if (allServiceNames.length === 0) {
|
|
1370
|
-
this.logger?.log("[ServiceInvalidator] No singleton services to clear");
|
|
1371
|
-
} else {
|
|
1372
|
-
this.logger?.log(
|
|
1373
|
-
`[ServiceInvalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(", ")}`
|
|
1374
|
-
);
|
|
1375
|
-
await this.clearServicesWithDependencyAwareness(
|
|
1376
|
-
allServiceNames,
|
|
1377
|
-
maxRounds
|
|
1378
|
-
);
|
|
1379
|
-
}
|
|
1380
|
-
if (clearRequestContexts) {
|
|
1381
|
-
await this.requestContextManager.clearAllRequestContexts();
|
|
1382
|
-
}
|
|
1383
|
-
this.logger?.log("[ServiceInvalidator] Graceful clearing completed");
|
|
1384
|
-
}
|
|
1385
|
-
/**
|
|
1386
|
-
* Waits for all services to settle (either created, destroyed, or error state).
|
|
1387
|
-
*/
|
|
1388
|
-
async ready() {
|
|
1389
|
-
const holders = Array.from(this.manager.filter(() => true)).map(
|
|
1390
|
-
([, holder]) => holder
|
|
1391
|
-
);
|
|
1392
|
-
await Promise.all(
|
|
1393
|
-
holders.map((holder) => this.waitForHolderToSettle(holder))
|
|
1394
|
-
);
|
|
1395
|
-
}
|
|
1396
|
-
/**
|
|
1397
|
-
* Invalidates a single holder based on its current status.
|
|
1398
|
-
*/
|
|
1399
|
-
async invalidateHolder(key, holder, round) {
|
|
1400
|
-
await this.invalidateHolderByStatus(holder, round, {
|
|
1401
|
-
context: key,
|
|
1402
|
-
isRequestScoped: false,
|
|
1403
|
-
onCreationError: () => this.logger?.error(
|
|
1404
|
-
`[ServiceInvalidator] ${key} creation triggered too many invalidation rounds`
|
|
1405
|
-
),
|
|
1406
|
-
onRecursiveInvalidate: () => this.invalidate(key, round + 1),
|
|
1407
|
-
onDestroy: () => this.destroyHolder(key, holder)
|
|
1408
|
-
});
|
|
1409
|
-
}
|
|
1410
|
-
/**
|
|
1411
|
-
* Invalidates a request-scoped holder based on its current status.
|
|
1412
|
-
*/
|
|
1413
|
-
async invalidateRequestHolder(requestId, instanceName, holder, round) {
|
|
1414
|
-
await this.invalidateHolderByStatus(holder, round, {
|
|
1415
|
-
context: `Request-scoped ${instanceName} in ${requestId}`,
|
|
1416
|
-
isRequestScoped: true,
|
|
1417
|
-
onCreationError: () => this.logger?.error(
|
|
1418
|
-
`[ServiceInvalidator] Request-scoped ${instanceName} in ${requestId} creation triggered too many invalidation rounds`
|
|
1419
|
-
),
|
|
1420
|
-
onRecursiveInvalidate: () => this.invalidateRequestHolder(
|
|
1421
|
-
requestId,
|
|
1422
|
-
instanceName,
|
|
1423
|
-
holder,
|
|
1424
|
-
round + 1
|
|
1425
|
-
),
|
|
1426
|
-
onDestroy: () => this.destroyRequestHolder(requestId, instanceName, holder)
|
|
1427
|
-
});
|
|
1428
|
-
}
|
|
1429
|
-
/**
|
|
1430
|
-
* Common invalidation logic for holders based on their status.
|
|
1431
|
-
*/
|
|
1432
|
-
async invalidateHolderByStatus(holder, round, options) {
|
|
1433
|
-
switch (holder.status) {
|
|
1434
|
-
case "destroying" /* Destroying */:
|
|
1435
|
-
await holder.destroyPromise;
|
|
1436
|
-
break;
|
|
1437
|
-
case "creating" /* Creating */:
|
|
1438
|
-
await holder.creationPromise;
|
|
1439
|
-
if (round > 3) {
|
|
1440
|
-
options.onCreationError();
|
|
1441
|
-
return;
|
|
1442
|
-
}
|
|
1443
|
-
await options.onRecursiveInvalidate();
|
|
1444
|
-
break;
|
|
1445
|
-
default:
|
|
1446
|
-
await options.onDestroy();
|
|
1447
|
-
break;
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
/**
|
|
1451
|
-
* Destroys a holder and cleans up its resources.
|
|
1452
|
-
*/
|
|
1453
|
-
async destroyHolder(key, holder) {
|
|
1454
|
-
await this.destroyHolderWithCleanup(holder, {
|
|
1455
|
-
context: key,
|
|
1456
|
-
logMessage: `[ServiceInvalidator] Invalidating ${key} and notifying listeners`,
|
|
1457
|
-
cleanup: () => this.manager.delete(key),
|
|
1458
|
-
eventName: key
|
|
1459
|
-
});
|
|
1460
|
-
}
|
|
1461
|
-
/**
|
|
1462
|
-
* Destroys a request-scoped holder and cleans up its resources.
|
|
1463
|
-
*/
|
|
1464
|
-
async destroyRequestHolder(requestId, instanceName, holder) {
|
|
1465
|
-
await this.destroyHolderWithCleanup(holder, {
|
|
1466
|
-
context: `Request-scoped ${instanceName} in ${requestId}`,
|
|
1467
|
-
logMessage: `[ServiceInvalidator] Invalidating request-scoped ${instanceName} in ${requestId} and notifying listeners`,
|
|
1468
|
-
cleanup: () => {
|
|
1469
|
-
const requestContext = this.requestContextManager.getRequestContexts().get(requestId);
|
|
1470
|
-
if (requestContext) {
|
|
1471
|
-
requestContext.delete(instanceName);
|
|
1472
|
-
}
|
|
1473
|
-
},
|
|
1474
|
-
eventName: instanceName
|
|
1475
|
-
});
|
|
1476
|
-
}
|
|
1477
|
-
/**
|
|
1478
|
-
* Common destroy logic for holders with customizable cleanup.
|
|
1479
|
-
*/
|
|
1480
|
-
async destroyHolderWithCleanup(holder, options) {
|
|
1481
|
-
holder.status = "destroying" /* Destroying */;
|
|
1482
|
-
this.logger?.log(options.logMessage);
|
|
1483
|
-
holder.destroyPromise = Promise.all(
|
|
1484
|
-
holder.destroyListeners.map((listener) => listener())
|
|
1485
|
-
).then(async () => {
|
|
1486
|
-
holder.destroyListeners = [];
|
|
1487
|
-
holder.deps.clear();
|
|
1488
|
-
options.cleanup();
|
|
1489
|
-
await this.emitInstanceEvent(options.eventName, "destroy");
|
|
1490
|
-
});
|
|
1491
|
-
await holder.destroyPromise;
|
|
1492
|
-
}
|
|
1493
|
-
/**
|
|
1494
|
-
* Waits for a holder to settle (either created, destroyed, or error state).
|
|
1495
|
-
*/
|
|
1496
|
-
async waitForHolderToSettle(holder) {
|
|
1497
|
-
switch (holder.status) {
|
|
1498
|
-
case "creating" /* Creating */:
|
|
1499
|
-
await holder.creationPromise;
|
|
1500
|
-
break;
|
|
1501
|
-
case "destroying" /* Destroying */:
|
|
1502
|
-
await holder.destroyPromise;
|
|
1503
|
-
break;
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
/**
|
|
1507
|
-
* Clears services with dependency awareness, ensuring proper cleanup order.
|
|
1508
|
-
* Services with no dependencies are cleared first, then services that depend on them.
|
|
1509
|
-
*/
|
|
1510
|
-
async clearServicesWithDependencyAwareness(serviceNames, maxRounds) {
|
|
1511
|
-
const clearedServices = /* @__PURE__ */ new Set();
|
|
1512
|
-
let round = 1;
|
|
1513
|
-
while (clearedServices.size < serviceNames.length && round <= maxRounds) {
|
|
1514
|
-
this.logger?.log(
|
|
1515
|
-
`[ServiceInvalidator] Clearing round ${round}/${maxRounds}, ${clearedServices.size}/${serviceNames.length} services cleared`
|
|
1516
|
-
);
|
|
1517
|
-
const servicesToClearThisRound = this.findServicesReadyForClearing(
|
|
1518
|
-
serviceNames,
|
|
1519
|
-
clearedServices
|
|
1520
|
-
);
|
|
1521
|
-
if (servicesToClearThisRound.length === 0) {
|
|
1522
|
-
const remainingServices = serviceNames.filter(
|
|
1523
|
-
(name) => !clearedServices.has(name)
|
|
1524
|
-
);
|
|
1525
|
-
if (remainingServices.length > 0) {
|
|
1526
|
-
this.logger?.warn(
|
|
1527
|
-
`[ServiceInvalidator] No services ready for clearing, forcing cleanup of remaining: ${remainingServices.join(", ")}`
|
|
1528
|
-
);
|
|
1529
|
-
await this.forceClearServices(remainingServices);
|
|
1530
|
-
remainingServices.forEach((name) => clearedServices.add(name));
|
|
1531
|
-
}
|
|
1532
|
-
break;
|
|
1533
|
-
}
|
|
1534
|
-
const clearPromises = servicesToClearThisRound.map(
|
|
1535
|
-
async (serviceName) => {
|
|
1536
|
-
try {
|
|
1537
|
-
await this.invalidate(serviceName, round);
|
|
1538
|
-
clearedServices.add(serviceName);
|
|
1539
|
-
this.logger?.log(
|
|
1540
|
-
`[ServiceInvalidator] Successfully cleared service: ${serviceName}`
|
|
1541
|
-
);
|
|
1542
|
-
} catch (error) {
|
|
1543
|
-
this.logger?.error(
|
|
1544
|
-
`[ServiceInvalidator] Error clearing service ${serviceName}:`,
|
|
1545
|
-
error
|
|
1546
|
-
);
|
|
1547
|
-
clearedServices.add(serviceName);
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
);
|
|
1551
|
-
await Promise.all(clearPromises);
|
|
1552
|
-
round++;
|
|
1553
|
-
}
|
|
1554
|
-
if (clearedServices.size < serviceNames.length) {
|
|
1555
|
-
this.logger?.warn(
|
|
1556
|
-
`[ServiceInvalidator] Clearing completed after ${maxRounds} rounds, but ${serviceNames.length - clearedServices.size} services may not have been properly cleared`
|
|
1557
|
-
);
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
/**
|
|
1561
|
-
* Finds services that are ready to be cleared in the current round.
|
|
1562
|
-
* A service is ready if all its dependencies have already been cleared.
|
|
1563
|
-
*/
|
|
1564
|
-
findServicesReadyForClearing(allServiceNames, clearedServices) {
|
|
1565
|
-
return allServiceNames.filter((serviceName) => {
|
|
1566
|
-
if (clearedServices.has(serviceName)) {
|
|
1567
|
-
return false;
|
|
1568
|
-
}
|
|
1569
|
-
const [error, holder] = this.manager.get(serviceName);
|
|
1570
|
-
if (error) {
|
|
1571
|
-
return true;
|
|
1572
|
-
}
|
|
1573
|
-
const hasUnclearedDependencies = Array.from(holder.deps).some(
|
|
1574
|
-
(dep) => !clearedServices.has(dep)
|
|
1575
|
-
);
|
|
1576
|
-
return !hasUnclearedDependencies;
|
|
1577
|
-
});
|
|
1578
|
-
}
|
|
1579
|
-
/**
|
|
1580
|
-
* Force clears services that couldn't be cleared through normal dependency resolution.
|
|
1581
|
-
* This handles edge cases like circular dependencies.
|
|
1582
|
-
*/
|
|
1583
|
-
async forceClearServices(serviceNames) {
|
|
1584
|
-
const promises = serviceNames.map(async (serviceName) => {
|
|
1585
|
-
try {
|
|
1586
|
-
const [error, holder] = this.manager.get(serviceName);
|
|
1587
|
-
if (!error && holder) {
|
|
1588
|
-
await this.destroyHolder(serviceName, holder);
|
|
1589
|
-
}
|
|
1590
|
-
} catch (error) {
|
|
1591
|
-
this.logger?.error(
|
|
1592
|
-
`[ServiceInvalidator] Error force clearing service ${serviceName}:`,
|
|
1593
|
-
error
|
|
1594
|
-
);
|
|
1595
|
-
}
|
|
1596
|
-
});
|
|
1597
|
-
await Promise.all(promises);
|
|
1598
|
-
}
|
|
1599
|
-
/**
|
|
1600
|
-
* Gets all service names currently managed by the ServiceLocator.
|
|
1601
|
-
*/
|
|
1602
|
-
getAllServiceNames() {
|
|
1603
|
-
return this.manager.getAllNames();
|
|
1604
|
-
}
|
|
1605
|
-
/**
|
|
1606
|
-
* Emits events to listeners for instance lifecycle events.
|
|
1607
|
-
*/
|
|
1608
|
-
emitInstanceEvent(name, event = "create") {
|
|
1609
|
-
this.logger?.log(
|
|
1610
|
-
`[ServiceInvalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`
|
|
1611
|
-
);
|
|
1612
|
-
return this.eventBus.emit(name, event);
|
|
1613
|
-
}
|
|
1614
|
-
};
|
|
1615
|
-
|
|
1616
|
-
// src/token-processor.mts
|
|
1617
|
-
var TokenProcessor = class {
|
|
1618
|
-
constructor(logger = null) {
|
|
1619
|
-
this.logger = logger;
|
|
1620
|
-
}
|
|
1621
|
-
/**
|
|
1622
|
-
* Validates and resolves token arguments, handling factory token resolution and validation.
|
|
1623
|
-
*/
|
|
1624
|
-
validateAndResolveTokenArgs(token, args) {
|
|
1625
|
-
let actualToken = token;
|
|
1626
|
-
if (typeof token === "function") {
|
|
1627
|
-
actualToken = getInjectableToken(token);
|
|
1628
|
-
}
|
|
1629
|
-
let realArgs = args;
|
|
1630
|
-
if (actualToken instanceof BoundInjectionToken) {
|
|
1631
|
-
realArgs = actualToken.value;
|
|
1632
|
-
} else if (actualToken instanceof FactoryInjectionToken) {
|
|
1633
|
-
if (actualToken.resolved) {
|
|
1634
|
-
realArgs = actualToken.value;
|
|
1635
|
-
} else {
|
|
1636
|
-
return [DIError.factoryTokenNotResolved(token.name), { actualToken }];
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
if (!actualToken.schema) {
|
|
1640
|
-
return [void 0, { actualToken, validatedArgs: realArgs }];
|
|
1641
|
-
}
|
|
1642
|
-
const validatedArgs = actualToken.schema?.safeParse(realArgs);
|
|
1643
|
-
if (validatedArgs && !validatedArgs.success) {
|
|
1644
|
-
this.logger?.error(
|
|
1645
|
-
`[TokenProcessor]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`,
|
|
1646
|
-
validatedArgs.error
|
|
1647
|
-
);
|
|
1648
|
-
return [DIError.unknown(validatedArgs.error), { actualToken }];
|
|
1649
|
-
}
|
|
1650
|
-
return [void 0, { actualToken, validatedArgs: validatedArgs?.data }];
|
|
1651
|
-
}
|
|
1652
|
-
/**
|
|
1653
|
-
* Generates a unique instance name based on token and arguments.
|
|
1654
|
-
*/
|
|
1655
|
-
generateInstanceName(token, args) {
|
|
1656
|
-
if (!args) {
|
|
1657
|
-
return token.toString();
|
|
1658
|
-
}
|
|
1659
|
-
const formattedArgs = Object.entries(args).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => `${key}=${this.formatArgValue(value)}`).join(",");
|
|
1660
|
-
return `${token.toString()}:${formattedArgs.replaceAll(/"/g, "").replaceAll(/:/g, "=")}`;
|
|
1661
|
-
}
|
|
1662
|
-
/**
|
|
1663
|
-
* Formats a single argument value for instance name generation.
|
|
1664
|
-
*/
|
|
1665
|
-
formatArgValue(value) {
|
|
1666
|
-
if (typeof value === "function") {
|
|
1667
|
-
return `fn_${value.name}(${value.length})`;
|
|
1668
|
-
}
|
|
1669
|
-
if (typeof value === "symbol") {
|
|
1670
|
-
return value.toString();
|
|
1671
|
-
}
|
|
1672
|
-
return JSON.stringify(value).slice(0, 40);
|
|
1673
|
-
}
|
|
1674
|
-
/**
|
|
1675
|
-
* Creates a factory context for dependency injection during service instantiation.
|
|
1676
|
-
* @param serviceLocator Reference to the service locator for dependency resolution
|
|
1677
|
-
*/
|
|
1678
|
-
createFactoryContext(serviceLocator) {
|
|
1679
|
-
const destroyListeners = /* @__PURE__ */ new Set();
|
|
1680
|
-
const deps = /* @__PURE__ */ new Set();
|
|
1681
|
-
function addDestroyListener(listener) {
|
|
1682
|
-
destroyListeners.add(listener);
|
|
1683
|
-
}
|
|
1684
|
-
function getDestroyListeners() {
|
|
1685
|
-
return Array.from(destroyListeners);
|
|
1686
|
-
}
|
|
1687
|
-
return {
|
|
1688
|
-
// @ts-expect-error This is correct type
|
|
1689
|
-
async inject(token, args) {
|
|
1690
|
-
const [error, instance] = await serviceLocator.getInstance(
|
|
1691
|
-
token,
|
|
1692
|
-
args,
|
|
1693
|
-
({ instanceName }) => {
|
|
1694
|
-
deps.add(instanceName);
|
|
1695
|
-
}
|
|
1696
|
-
);
|
|
1697
|
-
if (error) {
|
|
1698
|
-
throw error;
|
|
1699
|
-
}
|
|
1700
|
-
return instance;
|
|
1701
|
-
},
|
|
1702
|
-
addDestroyListener,
|
|
1703
|
-
getDestroyListeners,
|
|
1704
|
-
locator: serviceLocator,
|
|
1705
|
-
deps
|
|
1706
|
-
};
|
|
1707
|
-
}
|
|
1708
|
-
/**
|
|
1709
|
-
* Tries to get a pre-prepared instance from request contexts.
|
|
1710
|
-
*/
|
|
1711
|
-
tryGetPrePreparedInstance(instanceName, contextHolder, deps, currentRequestContext) {
|
|
1712
|
-
if (contextHolder && contextHolder.priority > 0) {
|
|
1713
|
-
const prePreparedInstance = contextHolder.get(instanceName)?.instance;
|
|
1714
|
-
if (prePreparedInstance !== void 0) {
|
|
1715
|
-
this.logger?.debug(
|
|
1716
|
-
`[TokenProcessor] Using pre-prepared instance ${instanceName} from request context ${contextHolder.requestId}`
|
|
1717
|
-
);
|
|
1718
|
-
deps.add(instanceName);
|
|
1719
|
-
return prePreparedInstance;
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
if (currentRequestContext && currentRequestContext !== contextHolder) {
|
|
1723
|
-
const prePreparedInstance = currentRequestContext.get(instanceName)?.instance;
|
|
1724
|
-
if (prePreparedInstance !== void 0) {
|
|
1725
|
-
this.logger?.debug(
|
|
1726
|
-
`[TokenProcessor] Using pre-prepared instance ${instanceName} from current request context ${currentRequestContext.requestId}`
|
|
1727
|
-
);
|
|
1728
|
-
deps.add(instanceName);
|
|
1729
|
-
return prePreparedInstance;
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
return void 0;
|
|
1733
|
-
}
|
|
1734
|
-
};
|
|
1735
|
-
|
|
1736
|
-
// src/service-locator.mts
|
|
1737
|
-
var ServiceLocator = class {
|
|
1738
|
-
constructor(registry = globalRegistry, logger = null, injectors = defaultInjectors) {
|
|
1739
|
-
this.registry = registry;
|
|
1740
|
-
this.logger = logger;
|
|
1741
|
-
this.injectors = injectors;
|
|
1742
|
-
this.eventBus = new ServiceLocatorEventBus(logger);
|
|
1743
|
-
this.manager = new ServiceLocatorManager(logger);
|
|
1744
|
-
this.serviceInstantiator = new ServiceInstantiator(injectors);
|
|
1745
|
-
this.tokenProcessor = new TokenProcessor(logger);
|
|
1746
|
-
this.requestContextManager = new RequestContextManager(logger);
|
|
1747
|
-
this.serviceInvalidator = new ServiceInvalidator(
|
|
1748
|
-
this.manager,
|
|
1749
|
-
this.requestContextManager,
|
|
1750
|
-
this.eventBus,
|
|
1751
|
-
logger
|
|
1752
|
-
);
|
|
1753
|
-
this.instanceResolver = new InstanceResolver(
|
|
1754
|
-
this.registry,
|
|
1755
|
-
this.manager,
|
|
1756
|
-
this.serviceInstantiator,
|
|
1757
|
-
this.tokenProcessor,
|
|
1758
|
-
logger,
|
|
1759
|
-
this
|
|
1760
|
-
);
|
|
1761
|
-
}
|
|
1762
|
-
eventBus;
|
|
1763
|
-
manager;
|
|
1764
|
-
serviceInstantiator;
|
|
1765
|
-
tokenProcessor;
|
|
1766
|
-
requestContextManager;
|
|
1767
|
-
serviceInvalidator;
|
|
1768
|
-
instanceResolver;
|
|
1769
|
-
// ============================================================================
|
|
1770
|
-
// PUBLIC METHODS
|
|
1771
|
-
// ============================================================================
|
|
1772
|
-
getEventBus() {
|
|
1773
|
-
return this.eventBus;
|
|
1774
|
-
}
|
|
1775
|
-
getManager() {
|
|
1776
|
-
return this.manager;
|
|
1777
|
-
}
|
|
1778
|
-
getRequestContexts() {
|
|
1779
|
-
return this.requestContextManager.getRequestContexts();
|
|
1780
|
-
}
|
|
1781
|
-
getRequestContextManager() {
|
|
1782
|
-
return this.requestContextManager;
|
|
1783
|
-
}
|
|
1784
|
-
getServiceInvalidator() {
|
|
1785
|
-
return this.serviceInvalidator;
|
|
1786
|
-
}
|
|
1787
|
-
getInstanceIdentifier(token, args) {
|
|
1788
|
-
const [err, { actualToken, validatedArgs }] = this.tokenProcessor.validateAndResolveTokenArgs(token, args);
|
|
1789
|
-
if (err) {
|
|
1790
|
-
throw err;
|
|
1791
|
-
}
|
|
1792
|
-
return this.tokenProcessor.generateInstanceName(actualToken, validatedArgs);
|
|
1793
|
-
}
|
|
1794
|
-
async getInstance(token, args, onPrepare) {
|
|
1795
|
-
const [err, data] = await this.instanceResolver.resolveInstance(
|
|
1796
|
-
token,
|
|
1797
|
-
args,
|
|
1798
|
-
this.requestContextManager.getCurrentRequestContext() || void 0
|
|
1799
|
-
);
|
|
1800
|
-
if (err) {
|
|
1801
|
-
return [err];
|
|
1802
|
-
}
|
|
1803
|
-
if (onPrepare) {
|
|
1804
|
-
const instanceName = this.getInstanceIdentifier(token, args);
|
|
1805
|
-
const [tokenErr, { actualToken, validatedArgs }] = this.tokenProcessor.validateAndResolveTokenArgs(token, args);
|
|
1806
|
-
if (!tokenErr) {
|
|
1807
|
-
onPrepare({ instanceName, actualToken, validatedArgs });
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
return [void 0, data];
|
|
1811
|
-
}
|
|
1812
|
-
async getOrThrowInstance(token, args) {
|
|
1813
|
-
const [error, instance] = await this.getInstance(token, args);
|
|
1814
|
-
if (error) {
|
|
1815
|
-
throw error;
|
|
1816
|
-
}
|
|
1817
|
-
return instance;
|
|
1818
|
-
}
|
|
1819
|
-
getSyncInstance(token, args) {
|
|
1820
|
-
return this.instanceResolver.getSyncInstance(
|
|
1821
|
-
token,
|
|
1822
|
-
args,
|
|
1823
|
-
this.requestContextManager.getCurrentRequestContext()
|
|
1824
|
-
);
|
|
1825
|
-
}
|
|
1826
|
-
invalidate(service, round = 1) {
|
|
1827
|
-
return this.serviceInvalidator.invalidate(service, round);
|
|
1828
|
-
}
|
|
1829
|
-
/**
|
|
1830
|
-
* Gracefully clears all services in the ServiceLocator using invalidation logic.
|
|
1831
|
-
* This method respects service dependencies and ensures proper cleanup order.
|
|
1832
|
-
* Services that depend on others will be invalidated first, then their dependencies.
|
|
1833
|
-
*
|
|
1834
|
-
* @param options Optional configuration for the clearing process
|
|
1835
|
-
* @returns Promise that resolves when all services have been cleared
|
|
1836
|
-
*/
|
|
1837
|
-
async clearAll(options = {}) {
|
|
1838
|
-
return this.serviceInvalidator.clearAll(options);
|
|
1839
|
-
}
|
|
1840
|
-
// ============================================================================
|
|
1841
|
-
// REQUEST CONTEXT MANAGEMENT
|
|
1842
|
-
// ============================================================================
|
|
1843
|
-
/**
|
|
1844
|
-
* Begins a new request context with the given parameters.
|
|
1845
|
-
* @param requestId Unique identifier for this request
|
|
1846
|
-
* @param metadata Optional metadata for the request
|
|
1847
|
-
* @param priority Priority for resolution (higher = more priority)
|
|
1848
|
-
* @returns The created request context holder
|
|
1849
|
-
*/
|
|
1850
|
-
beginRequest(requestId, metadata, priority = 100) {
|
|
1851
|
-
return this.requestContextManager.beginRequest(
|
|
1852
|
-
requestId,
|
|
1853
|
-
metadata,
|
|
1854
|
-
priority
|
|
1855
|
-
);
|
|
1856
|
-
}
|
|
1857
|
-
/**
|
|
1858
|
-
* Ends a request context and cleans up all associated instances.
|
|
1859
|
-
* @param requestId The request ID to end
|
|
1860
|
-
*/
|
|
1861
|
-
async endRequest(requestId) {
|
|
1862
|
-
return this.requestContextManager.endRequest(requestId);
|
|
1863
|
-
}
|
|
1864
|
-
/**
|
|
1865
|
-
* Gets the current request context.
|
|
1866
|
-
* @returns The current request context holder or null
|
|
1867
|
-
*/
|
|
1868
|
-
getCurrentRequestContext() {
|
|
1869
|
-
return this.requestContextManager.getCurrentRequestContext();
|
|
1870
|
-
}
|
|
1871
|
-
/**
|
|
1872
|
-
* Sets the current request context.
|
|
1873
|
-
* @param requestId The request ID to set as current
|
|
1874
|
-
*/
|
|
1875
|
-
setCurrentRequestContext(requestId) {
|
|
1876
|
-
return this.requestContextManager.setCurrentRequestContext(requestId);
|
|
1877
|
-
}
|
|
1878
|
-
/**
|
|
1879
|
-
* Waits for all services to settle (either created, destroyed, or error state).
|
|
1880
|
-
*/
|
|
1881
|
-
async ready() {
|
|
1882
|
-
return this.serviceInvalidator.ready();
|
|
1883
|
-
}
|
|
1884
|
-
/**
|
|
1885
|
-
* Helper method for TokenProcessor to access pre-prepared instances.
|
|
1886
|
-
* This is needed for the factory context creation.
|
|
1887
|
-
*/
|
|
1888
|
-
tryGetPrePreparedInstance(instanceName, contextHolder, deps) {
|
|
1889
|
-
return this.tokenProcessor.tryGetPrePreparedInstance(
|
|
1890
|
-
instanceName,
|
|
1891
|
-
contextHolder,
|
|
1892
|
-
deps,
|
|
1893
|
-
this.requestContextManager.getCurrentRequestContext()
|
|
1894
|
-
);
|
|
1895
|
-
}
|
|
1896
|
-
/**
|
|
1897
|
-
* Helper method for InstanceResolver to generate instance names.
|
|
1898
|
-
* This is needed for the factory context creation.
|
|
1899
|
-
*/
|
|
1900
|
-
generateInstanceName(token, args) {
|
|
1901
|
-
return this.tokenProcessor.generateInstanceName(token, args);
|
|
1902
|
-
}
|
|
1903
|
-
};
|
|
1904
|
-
|
|
1905
|
-
// src/container.mts
|
|
1906
|
-
var _Container_decorators, _init;
|
|
1907
|
-
_Container_decorators = [Injectable()];
|
|
1908
|
-
var _Container = class _Container {
|
|
1909
|
-
constructor(registry = globalRegistry, logger = null, injectors = defaultInjectors) {
|
|
1910
|
-
this.registry = registry;
|
|
1911
|
-
this.logger = logger;
|
|
1912
|
-
this.injectors = injectors;
|
|
1913
|
-
this.serviceLocator = new ServiceLocator(registry, logger, injectors);
|
|
1914
|
-
this.registerSelf();
|
|
1915
|
-
}
|
|
1916
|
-
serviceLocator;
|
|
1917
|
-
registerSelf() {
|
|
1918
|
-
const token = getInjectableToken(_Container);
|
|
1919
|
-
const instanceName = this.serviceLocator.getInstanceIdentifier(token);
|
|
1920
|
-
this.serviceLocator.getManager().storeCreatedHolder(
|
|
1921
|
-
instanceName,
|
|
1922
|
-
this,
|
|
1923
|
-
"Class" /* Class */,
|
|
1924
|
-
"Singleton" /* Singleton */
|
|
1925
|
-
);
|
|
1926
|
-
}
|
|
1927
|
-
async get(token, args) {
|
|
1928
|
-
return this.serviceLocator.getOrThrowInstance(token, args);
|
|
1929
|
-
}
|
|
1930
|
-
/**
|
|
1931
|
-
* Gets the underlying ServiceLocator instance for advanced usage
|
|
1932
|
-
*/
|
|
1933
|
-
getServiceLocator() {
|
|
1934
|
-
return this.serviceLocator;
|
|
1935
|
-
}
|
|
1936
|
-
/**
|
|
1937
|
-
* Invalidates a service and its dependencies
|
|
1938
|
-
*/
|
|
1939
|
-
async invalidate(service) {
|
|
1940
|
-
const holder = this.getHolderByInstance(service);
|
|
1941
|
-
if (holder) {
|
|
1942
|
-
await this.serviceLocator.invalidate(holder.name);
|
|
1943
|
-
} else {
|
|
1944
|
-
const requestHolder = this.getRequestHolderByInstance(service);
|
|
1945
|
-
if (requestHolder) {
|
|
1946
|
-
await this.serviceLocator.invalidate(requestHolder.name);
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
1950
|
-
/**
|
|
1951
|
-
* Gets a service holder by instance (reverse lookup)
|
|
1952
|
-
*/
|
|
1953
|
-
getHolderByInstance(instance) {
|
|
1954
|
-
const holderMap = Array.from(
|
|
1955
|
-
this.serviceLocator.getManager().filter((holder) => holder.instance === instance).values()
|
|
1956
|
-
);
|
|
1957
|
-
return holderMap.length > 0 ? holderMap[0] : null;
|
|
1958
|
-
}
|
|
1959
|
-
getRequestHolderByInstance(instance) {
|
|
1960
|
-
const requestContexts = this.serviceLocator.getRequestContextManager().getRequestContexts();
|
|
1961
|
-
if (requestContexts) {
|
|
1962
|
-
for (const requestContext of requestContexts.values()) {
|
|
1963
|
-
for (const holder of requestContext.holders.values()) {
|
|
1964
|
-
if (holder.instance === instance) {
|
|
1965
|
-
return holder;
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
return null;
|
|
1971
|
-
}
|
|
1972
|
-
/**
|
|
1973
|
-
* Checks if a service is registered in the container
|
|
1974
|
-
*/
|
|
1975
|
-
isRegistered(token) {
|
|
1976
|
-
try {
|
|
1977
|
-
return this.serviceLocator.getInstanceIdentifier(token) !== null;
|
|
1978
|
-
} catch {
|
|
1979
|
-
return false;
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
1982
|
-
/**
|
|
1983
|
-
* Disposes the container and cleans up all resources
|
|
1984
|
-
*/
|
|
1985
|
-
async dispose() {
|
|
1986
|
-
await this.serviceLocator.clearAll();
|
|
1987
|
-
}
|
|
1988
|
-
/**
|
|
1989
|
-
* Waits for all pending operations to complete
|
|
1990
|
-
*/
|
|
1991
|
-
async ready() {
|
|
1992
|
-
await this.serviceLocator.ready();
|
|
1993
|
-
}
|
|
1994
|
-
// ============================================================================
|
|
1995
|
-
// REQUEST CONTEXT MANAGEMENT
|
|
1996
|
-
// ============================================================================
|
|
1997
|
-
/**
|
|
1998
|
-
* Begins a new request context with the given parameters.
|
|
1999
|
-
* @param requestId Unique identifier for this request
|
|
2000
|
-
* @param metadata Optional metadata for the request
|
|
2001
|
-
* @param priority Priority for resolution (higher = more priority)
|
|
2002
|
-
* @returns The created request context holder
|
|
2003
|
-
*/
|
|
2004
|
-
beginRequest(requestId, metadata, priority = 100) {
|
|
2005
|
-
return this.serviceLocator.beginRequest(requestId, metadata, priority);
|
|
2006
|
-
}
|
|
2007
|
-
/**
|
|
2008
|
-
* Ends a request context and cleans up all associated instances.
|
|
2009
|
-
* @param requestId The request ID to end
|
|
2010
|
-
*/
|
|
2011
|
-
async endRequest(requestId) {
|
|
2012
|
-
await this.serviceLocator.endRequest(requestId);
|
|
2013
|
-
}
|
|
2014
|
-
/**
|
|
2015
|
-
* Gets the current request context.
|
|
2016
|
-
* @returns The current request context holder or null
|
|
2017
|
-
*/
|
|
2018
|
-
getCurrentRequestContext() {
|
|
2019
|
-
return this.serviceLocator.getCurrentRequestContext();
|
|
2020
|
-
}
|
|
2021
|
-
/**
|
|
2022
|
-
* Sets the current request context.
|
|
2023
|
-
* @param requestId The request ID to set as current
|
|
2024
|
-
*/
|
|
2025
|
-
setCurrentRequestContext(requestId) {
|
|
2026
|
-
this.serviceLocator.setCurrentRequestContext(requestId);
|
|
2027
|
-
}
|
|
2028
|
-
/**
|
|
2029
|
-
* Clears all instances and bindings from the container.
|
|
2030
|
-
* This is useful for testing or resetting the container state.
|
|
2031
|
-
*/
|
|
2032
|
-
clear() {
|
|
2033
|
-
return this.serviceLocator.clearAll();
|
|
2034
|
-
}
|
|
2035
|
-
};
|
|
2036
|
-
_init = __decoratorStart(null);
|
|
2037
|
-
_Container = __decorateElement(_init, 0, "Container", _Container_decorators, _Container);
|
|
2038
|
-
__runInitializers(_init, 1, _Container);
|
|
2039
|
-
var Container = _Container;
|
|
2040
|
-
|
|
2041
|
-
export { BaseInstanceHolderManager, BoundInjectionToken, Container, DIError, DIErrorCode, DefaultRequestContextHolder, FactoryInjectionToken, Injectable, InjectableScope, InjectableTokenMeta, InjectableType, InjectionToken, Registry, ServiceInstantiator, ServiceLocator, ServiceLocatorEventBus, ServiceLocatorInstanceHolderStatus, ServiceLocatorManager, __decorateElement, __decoratorStart, __runInitializers, asyncInject, createRequestContextHolder, defaultInjectors, getInjectableToken, getInjectors, globalRegistry, inject, optional, provideFactoryContext, wrapSyncInit };
|
|
2042
|
-
//# sourceMappingURL=chunk-2M576LCC.mjs.map
|
|
2043
|
-
//# sourceMappingURL=chunk-2M576LCC.mjs.map
|