@navios/di 0.4.2 → 0.5.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/README.md +211 -1
- package/coverage/clover.xml +1912 -1277
- package/coverage/coverage-final.json +37 -28
- package/coverage/docs/examples/basic-usage.mts.html +1 -1
- package/coverage/docs/examples/factory-pattern.mts.html +1 -1
- package/coverage/docs/examples/index.html +1 -1
- package/coverage/docs/examples/injection-tokens.mts.html +1 -1
- package/coverage/docs/examples/request-scope-example.mts.html +1 -1
- package/coverage/docs/examples/service-lifecycle.mts.html +1 -1
- package/coverage/index.html +71 -41
- package/coverage/lib/_tsup-dts-rollup.d.mts.html +682 -43
- package/coverage/lib/index.d.mts.html +7 -4
- package/coverage/lib/index.html +5 -5
- package/coverage/lib/testing/index.d.mts.html +91 -0
- package/coverage/lib/testing/index.html +116 -0
- package/coverage/src/base-instance-holder-manager.mts.html +589 -0
- package/coverage/src/container.mts.html +257 -74
- package/coverage/src/decorators/factory.decorator.mts.html +1 -1
- package/coverage/src/decorators/index.html +1 -1
- package/coverage/src/decorators/index.mts.html +1 -1
- package/coverage/src/decorators/injectable.decorator.mts.html +20 -20
- package/coverage/src/enums/index.html +1 -1
- package/coverage/src/enums/index.mts.html +1 -1
- package/coverage/src/enums/injectable-scope.enum.mts.html +1 -1
- package/coverage/src/enums/injectable-type.enum.mts.html +1 -1
- package/coverage/src/errors/di-error.mts.html +292 -0
- package/coverage/src/errors/errors.enum.mts.html +30 -21
- package/coverage/src/errors/factory-not-found.mts.html +31 -22
- package/coverage/src/errors/factory-token-not-resolved.mts.html +29 -26
- package/coverage/src/errors/index.html +56 -41
- package/coverage/src/errors/index.mts.html +15 -9
- package/coverage/src/errors/instance-destroying.mts.html +31 -22
- package/coverage/src/errors/instance-expired.mts.html +31 -22
- package/coverage/src/errors/instance-not-found.mts.html +31 -22
- package/coverage/src/errors/unknown-error.mts.html +31 -43
- package/coverage/src/event-emitter.mts.html +14 -14
- package/coverage/src/factory-context.mts.html +1 -1
- package/coverage/src/index.html +121 -46
- package/coverage/src/index.mts.html +7 -4
- package/coverage/src/injection-token.mts.html +28 -28
- package/coverage/src/injector.mts.html +1 -1
- package/coverage/src/instance-resolver.mts.html +1762 -0
- package/coverage/src/interfaces/factory.interface.mts.html +1 -1
- package/coverage/src/interfaces/index.html +1 -1
- package/coverage/src/interfaces/index.mts.html +1 -1
- package/coverage/src/interfaces/on-service-destroy.interface.mts.html +1 -1
- package/coverage/src/interfaces/on-service-init.interface.mts.html +1 -1
- package/coverage/src/registry.mts.html +28 -28
- package/coverage/src/request-context-holder.mts.html +183 -102
- package/coverage/src/request-context-manager.mts.html +532 -0
- package/coverage/src/service-instantiator.mts.html +49 -49
- package/coverage/src/service-invalidator.mts.html +1372 -0
- package/coverage/src/service-locator-event-bus.mts.html +48 -48
- package/coverage/src/service-locator-instance-holder.mts.html +2 -14
- package/coverage/src/service-locator-manager.mts.html +71 -335
- package/coverage/src/service-locator.mts.html +240 -2328
- package/coverage/src/symbols/index.html +1 -1
- package/coverage/src/symbols/index.mts.html +1 -1
- package/coverage/src/symbols/injectable-token.mts.html +1 -1
- package/coverage/src/testing/index.html +131 -0
- package/coverage/src/testing/index.mts.html +88 -0
- package/coverage/src/testing/test-container.mts.html +445 -0
- package/coverage/src/token-processor.mts.html +607 -0
- package/coverage/src/utils/defer.mts.html +28 -214
- package/coverage/src/utils/get-injectable-token.mts.html +7 -7
- package/coverage/src/utils/get-injectors.mts.html +99 -99
- package/coverage/src/utils/index.html +15 -15
- package/coverage/src/utils/index.mts.html +4 -7
- package/coverage/src/utils/types.mts.html +1 -1
- package/docs/injectable.md +51 -11
- package/docs/scopes.md +63 -29
- package/lib/_tsup-dts-rollup.d.mts +376 -213
- package/lib/_tsup-dts-rollup.d.ts +376 -213
- package/lib/{chunk-3NLYPYBY.mjs → chunk-2M576LCC.mjs} +1024 -608
- package/lib/chunk-2M576LCC.mjs.map +1 -0
- package/lib/index.d.mts +6 -4
- package/lib/index.d.ts +6 -4
- package/lib/index.js +1189 -773
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +2 -2
- package/lib/testing/index.js +1261 -843
- package/lib/testing/index.js.map +1 -1
- package/lib/testing/index.mjs +1 -1
- package/package.json +4 -4
- package/src/__tests__/container.spec.mts +47 -13
- package/src/__tests__/errors.spec.mts +53 -27
- package/src/__tests__/injectable.spec.mts +73 -0
- package/src/__tests__/request-scope.spec.mts +0 -2
- package/src/__tests__/service-instantiator.spec.mts +1 -0
- package/src/__tests__/service-locator-manager.spec.mts +12 -82
- package/src/__tests__/service-locator.spec.mts +1009 -1
- package/src/__type-tests__/inject.spec-d.mts +30 -7
- package/src/__type-tests__/injectable.spec-d.mts +76 -37
- package/src/base-instance-holder-manager.mts +2 -9
- package/src/container.mts +61 -9
- package/src/decorators/injectable.decorator.mts +29 -5
- package/src/errors/di-error.mts +69 -0
- package/src/errors/index.mts +9 -7
- package/src/injection-token.mts +1 -0
- package/src/injector.mts +2 -0
- package/src/instance-resolver.mts +559 -0
- package/src/request-context-holder.mts +0 -2
- package/src/request-context-manager.mts +149 -0
- package/src/service-invalidator.mts +429 -0
- package/src/service-locator-event-bus.mts +1 -1
- package/src/service-locator-instance-holder.mts +0 -4
- package/src/service-locator-manager.mts +10 -40
- package/src/service-locator.mts +86 -782
- package/src/token-processor.mts +174 -0
- package/src/utils/get-injectors.mts +161 -24
- package/src/utils/index.mts +0 -1
- package/src/utils/types.mts +12 -8
- package/lib/chunk-3NLYPYBY.mjs.map +0 -1
- package/src/__tests__/defer.spec.mts +0 -166
- package/src/errors/errors.enum.mts +0 -8
- package/src/errors/factory-not-found.mts +0 -8
- package/src/errors/factory-token-not-resolved.mts +0 -10
- package/src/errors/instance-destroying.mts +0 -8
- package/src/errors/instance-expired.mts +0 -8
- package/src/errors/instance-not-found.mts +0 -8
- package/src/errors/unknown-error.mts +0 -15
- package/src/utils/defer.mts +0 -73
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var __create = Object.create;
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
4
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
5
5
|
var __typeError = (msg) => {
|
|
6
6
|
throw TypeError(msg);
|
|
7
7
|
};
|
|
@@ -172,10 +172,11 @@ var Registry = class {
|
|
|
172
172
|
var globalRegistry = new Registry();
|
|
173
173
|
|
|
174
174
|
// src/symbols/injectable-token.mts
|
|
175
|
-
var InjectableTokenMeta = Symbol.for("InjectableTokenMeta");
|
|
175
|
+
var InjectableTokenMeta = /* @__PURE__ */ Symbol.for("InjectableTokenMeta");
|
|
176
176
|
function Injectable({
|
|
177
177
|
scope = "Singleton" /* Singleton */,
|
|
178
178
|
token,
|
|
179
|
+
schema,
|
|
179
180
|
registry = globalRegistry
|
|
180
181
|
} = {}) {
|
|
181
182
|
return (target, context) => {
|
|
@@ -184,79 +185,73 @@ function Injectable({
|
|
|
184
185
|
"[ServiceLocator] @Injectable decorator can only be used on classes."
|
|
185
186
|
);
|
|
186
187
|
}
|
|
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);
|
|
188
194
|
registry.set(injectableToken, scope, target, "Class" /* Class */);
|
|
189
195
|
target[InjectableTokenMeta] = injectableToken;
|
|
190
196
|
return target;
|
|
191
197
|
};
|
|
192
198
|
}
|
|
193
199
|
|
|
194
|
-
// src/errors/
|
|
195
|
-
var
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
+
);
|
|
219
225
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
this.name = name;
|
|
226
|
+
static factoryTokenNotResolved(token) {
|
|
227
|
+
return new _DIError(
|
|
228
|
+
"FactoryTokenNotResolved" /* FactoryTokenNotResolved */,
|
|
229
|
+
`Factory token not resolved: ${token?.toString() ?? "unknown"}`,
|
|
230
|
+
{ token }
|
|
231
|
+
);
|
|
227
232
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
super(`Instance ${name} expired`);
|
|
235
|
-
this.name = name;
|
|
233
|
+
static instanceNotFound(name) {
|
|
234
|
+
return new _DIError(
|
|
235
|
+
"InstanceNotFound" /* InstanceNotFound */,
|
|
236
|
+
`Instance ${name} not found`,
|
|
237
|
+
{ name }
|
|
238
|
+
);
|
|
236
239
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
super(`Instance ${name} not found`);
|
|
244
|
-
this.name = name;
|
|
240
|
+
static instanceDestroying(name) {
|
|
241
|
+
return new _DIError(
|
|
242
|
+
"InstanceDestroying" /* InstanceDestroying */,
|
|
243
|
+
`Instance ${name} destroying`,
|
|
244
|
+
{ name }
|
|
245
|
+
);
|
|
245
246
|
}
|
|
246
|
-
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// src/errors/unknown-error.mts
|
|
250
|
-
var UnknownError = class extends Error {
|
|
251
|
-
code = "UnknownError" /* UnknownError */;
|
|
252
|
-
parent;
|
|
253
|
-
constructor(message) {
|
|
247
|
+
static unknown(message, context) {
|
|
254
248
|
if (message instanceof Error) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
249
|
+
return new _DIError("UnknownError" /* UnknownError */, message.message, {
|
|
250
|
+
...context,
|
|
251
|
+
parent: message
|
|
252
|
+
});
|
|
258
253
|
}
|
|
259
|
-
|
|
254
|
+
return new _DIError("UnknownError" /* UnknownError */, message, context);
|
|
260
255
|
}
|
|
261
256
|
};
|
|
262
257
|
|
|
@@ -278,29 +273,58 @@ function getInjectors() {
|
|
|
278
273
|
}
|
|
279
274
|
let promiseCollector = null;
|
|
280
275
|
let injectState = null;
|
|
281
|
-
function
|
|
276
|
+
function getRequest(token, args) {
|
|
282
277
|
if (!injectState) {
|
|
283
278
|
throw new Error(
|
|
284
|
-
"[Injector] Trying to
|
|
279
|
+
"[Injector] Trying to make a request outside of a injectable context"
|
|
285
280
|
);
|
|
286
281
|
}
|
|
287
282
|
if (injectState.isFrozen) {
|
|
288
283
|
const idx = injectState.currentIndex++;
|
|
289
|
-
const
|
|
290
|
-
if (
|
|
284
|
+
const request2 = injectState.requests[idx];
|
|
285
|
+
if (request2.token !== token) {
|
|
291
286
|
throw new Error(
|
|
292
|
-
`[Injector] Wrong token order. Expected ${
|
|
287
|
+
`[Injector] Wrong token order. Expected ${request2.token.toString()} but got ${token.toString()}`
|
|
293
288
|
);
|
|
294
289
|
}
|
|
295
|
-
return
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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;
|
|
301
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);
|
|
302
311
|
injectState.currentIndex++;
|
|
303
|
-
return
|
|
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
|
+
});
|
|
304
328
|
}
|
|
305
329
|
function wrapSyncInit2(cb) {
|
|
306
330
|
return (previousState) => {
|
|
@@ -330,23 +354,31 @@ function getInjectors() {
|
|
|
330
354
|
}
|
|
331
355
|
function inject2(token, args) {
|
|
332
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
|
+
}
|
|
333
362
|
const instance = getFactoryContext().locator.getSyncInstance(
|
|
334
363
|
realToken,
|
|
335
364
|
args
|
|
336
365
|
);
|
|
337
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
|
+
}
|
|
338
373
|
if (promiseCollector) {
|
|
339
|
-
|
|
340
|
-
promiseCollector(promise);
|
|
341
|
-
} else {
|
|
342
|
-
throw new Error(`[Injector] Cannot initiate ${realToken.toString()}`);
|
|
374
|
+
promiseCollector(request.promise);
|
|
343
375
|
}
|
|
344
376
|
return new Proxy(
|
|
345
377
|
{},
|
|
346
378
|
{
|
|
347
379
|
get() {
|
|
348
380
|
throw new Error(
|
|
349
|
-
`[Injector] Trying to access ${realToken.toString()} before it's initialized, please
|
|
381
|
+
`[Injector] Trying to access ${realToken.toString()} before it's initialized, please move the code to a onServiceInit method`
|
|
350
382
|
);
|
|
351
383
|
}
|
|
352
384
|
}
|
|
@@ -354,9 +386,17 @@ function getInjectors() {
|
|
|
354
386
|
}
|
|
355
387
|
return instance;
|
|
356
388
|
}
|
|
389
|
+
function optional2(token, args) {
|
|
390
|
+
try {
|
|
391
|
+
return inject2(token, args);
|
|
392
|
+
} catch {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
357
396
|
const injectors = {
|
|
358
397
|
asyncInject: asyncInject2,
|
|
359
398
|
inject: inject2,
|
|
399
|
+
optional: optional2,
|
|
360
400
|
wrapSyncInit: wrapSyncInit2,
|
|
361
401
|
provideFactoryContext: provideFactoryContext2
|
|
362
402
|
};
|
|
@@ -374,73 +414,13 @@ function getInjectableToken(target) {
|
|
|
374
414
|
return token;
|
|
375
415
|
}
|
|
376
416
|
|
|
377
|
-
// src/utils/defer.mts
|
|
378
|
-
var Deferred = class {
|
|
379
|
-
promise;
|
|
380
|
-
_resolve;
|
|
381
|
-
_reject;
|
|
382
|
-
_isResolved = false;
|
|
383
|
-
_isRejected = false;
|
|
384
|
-
constructor() {
|
|
385
|
-
this.promise = new Promise((resolve, reject) => {
|
|
386
|
-
this._resolve = resolve;
|
|
387
|
-
this._reject = reject;
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Resolves the deferred promise with the given value.
|
|
392
|
-
* @param value The value to resolve with
|
|
393
|
-
* @throws Error if the promise has already been resolved or rejected
|
|
394
|
-
*/
|
|
395
|
-
resolve(value) {
|
|
396
|
-
if (this._isResolved || this._isRejected) {
|
|
397
|
-
throw new Error("Deferred promise has already been resolved or rejected");
|
|
398
|
-
}
|
|
399
|
-
this._isResolved = true;
|
|
400
|
-
this._resolve(value);
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Rejects the deferred promise with the given reason.
|
|
404
|
-
* @param reason The reason for rejection
|
|
405
|
-
* @throws Error if the promise has already been resolved or rejected
|
|
406
|
-
*/
|
|
407
|
-
reject(reason) {
|
|
408
|
-
if (this._isResolved || this._isRejected) {
|
|
409
|
-
throw new Error("Deferred promise has already been resolved or rejected");
|
|
410
|
-
}
|
|
411
|
-
this._isRejected = true;
|
|
412
|
-
this._reject(reason);
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Returns true if the promise has been resolved.
|
|
416
|
-
*/
|
|
417
|
-
get isResolved() {
|
|
418
|
-
return this._isResolved;
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Returns true if the promise has been rejected.
|
|
422
|
-
*/
|
|
423
|
-
get isRejected() {
|
|
424
|
-
return this._isRejected;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Returns true if the promise has been settled (resolved or rejected).
|
|
428
|
-
*/
|
|
429
|
-
get isSettled() {
|
|
430
|
-
return this._isResolved || this._isRejected;
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
function createDeferred() {
|
|
434
|
-
return new Deferred();
|
|
435
|
-
}
|
|
436
|
-
|
|
437
417
|
// src/service-locator-instance-holder.mts
|
|
438
|
-
var ServiceLocatorInstanceHolderStatus = /* @__PURE__ */ ((
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
return
|
|
418
|
+
var ServiceLocatorInstanceHolderStatus = /* @__PURE__ */ ((ServiceLocatorInstanceHolderStatus2) => {
|
|
419
|
+
ServiceLocatorInstanceHolderStatus2["Created"] = "created";
|
|
420
|
+
ServiceLocatorInstanceHolderStatus2["Creating"] = "creating";
|
|
421
|
+
ServiceLocatorInstanceHolderStatus2["Destroying"] = "destroying";
|
|
422
|
+
ServiceLocatorInstanceHolderStatus2["Error"] = "error";
|
|
423
|
+
return ServiceLocatorInstanceHolderStatus2;
|
|
444
424
|
})(ServiceLocatorInstanceHolderStatus || {});
|
|
445
425
|
|
|
446
426
|
// src/base-instance-holder-manager.mts
|
|
@@ -493,11 +473,10 @@ var BaseInstanceHolderManager = class {
|
|
|
493
473
|
* @param type The injectable type
|
|
494
474
|
* @param scope The injectable scope
|
|
495
475
|
* @param deps Optional set of dependencies
|
|
496
|
-
* @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
|
|
497
476
|
* @returns A tuple containing the deferred promise and the holder
|
|
498
477
|
*/
|
|
499
|
-
createCreatingHolder(name, type, scope, deps = /* @__PURE__ */ new Set()
|
|
500
|
-
const deferred =
|
|
478
|
+
createCreatingHolder(name, type, scope, deps = /* @__PURE__ */ new Set()) {
|
|
479
|
+
const deferred = Promise.withResolvers();
|
|
501
480
|
const holder = {
|
|
502
481
|
status: "creating" /* Creating */,
|
|
503
482
|
name,
|
|
@@ -508,8 +487,7 @@ var BaseInstanceHolderManager = class {
|
|
|
508
487
|
scope,
|
|
509
488
|
deps,
|
|
510
489
|
destroyListeners: [],
|
|
511
|
-
createdAt: Date.now()
|
|
512
|
-
ttl
|
|
490
|
+
createdAt: Date.now()
|
|
513
491
|
};
|
|
514
492
|
return [deferred, holder];
|
|
515
493
|
}
|
|
@@ -521,10 +499,9 @@ var BaseInstanceHolderManager = class {
|
|
|
521
499
|
* @param type The injectable type
|
|
522
500
|
* @param scope The injectable scope
|
|
523
501
|
* @param deps Optional set of dependencies
|
|
524
|
-
* @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
|
|
525
502
|
* @returns The created holder
|
|
526
503
|
*/
|
|
527
|
-
createCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()
|
|
504
|
+
createCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()) {
|
|
528
505
|
const holder = {
|
|
529
506
|
status: "created" /* Created */,
|
|
530
507
|
name,
|
|
@@ -535,8 +512,7 @@ var BaseInstanceHolderManager = class {
|
|
|
535
512
|
scope,
|
|
536
513
|
deps,
|
|
537
514
|
destroyListeners: [],
|
|
538
|
-
createdAt: Date.now()
|
|
539
|
-
ttl
|
|
515
|
+
createdAt: Date.now()
|
|
540
516
|
};
|
|
541
517
|
return holder;
|
|
542
518
|
}
|
|
@@ -564,6 +540,7 @@ var BaseInstanceHolderManager = class {
|
|
|
564
540
|
var defaultInjectors = getInjectors();
|
|
565
541
|
var asyncInject = defaultInjectors.asyncInject;
|
|
566
542
|
var inject = defaultInjectors.inject;
|
|
543
|
+
var optional = defaultInjectors.optional;
|
|
567
544
|
var wrapSyncInit = defaultInjectors.wrapSyncInit;
|
|
568
545
|
var provideFactoryContext = defaultInjectors.provideFactoryContext;
|
|
569
546
|
|
|
@@ -741,8 +718,7 @@ var DefaultRequestContextHolder = class extends BaseInstanceHolderManager {
|
|
|
741
718
|
instance,
|
|
742
719
|
"Class" /* Class */,
|
|
743
720
|
"Singleton" /* Singleton */,
|
|
744
|
-
/* @__PURE__ */ new Set()
|
|
745
|
-
Infinity
|
|
721
|
+
/* @__PURE__ */ new Set()
|
|
746
722
|
);
|
|
747
723
|
this._holders.set(name, createdHolder);
|
|
748
724
|
} else {
|
|
@@ -784,7 +760,7 @@ var ServiceLocatorEventBus = class {
|
|
|
784
760
|
}
|
|
785
761
|
nsEvents.get(event).add(listener);
|
|
786
762
|
return () => {
|
|
787
|
-
nsEvents.get(event)
|
|
763
|
+
nsEvents.get(event)?.delete(listener);
|
|
788
764
|
if (nsEvents.get(event)?.size === 0) {
|
|
789
765
|
nsEvents.delete(event);
|
|
790
766
|
}
|
|
@@ -826,19 +802,11 @@ var ServiceLocatorManager = class extends BaseInstanceHolderManager {
|
|
|
826
802
|
get(name) {
|
|
827
803
|
const holder = this._holders.get(name);
|
|
828
804
|
if (holder) {
|
|
829
|
-
if (holder.
|
|
830
|
-
const now = Date.now();
|
|
831
|
-
if (now - holder.createdAt > holder.ttl) {
|
|
832
|
-
this.logger?.log(
|
|
833
|
-
`[ServiceLocatorManager]#getInstanceHolder() TTL expired for ${holder.name}`
|
|
834
|
-
);
|
|
835
|
-
return [new InstanceExpired(holder.name), holder];
|
|
836
|
-
}
|
|
837
|
-
} else if (holder.status === "destroying" /* Destroying */) {
|
|
805
|
+
if (holder.status === "destroying" /* Destroying */) {
|
|
838
806
|
this.logger?.log(
|
|
839
807
|
`[ServiceLocatorManager]#getInstanceHolder() Instance ${holder.name} is destroying`
|
|
840
808
|
);
|
|
841
|
-
return [
|
|
809
|
+
return [DIError.instanceDestroying(holder.name), holder];
|
|
842
810
|
} else if (holder.status === "error" /* Error */) {
|
|
843
811
|
this.logger?.log(
|
|
844
812
|
`[ServiceLocatorManager]#getInstanceHolder() Instance ${holder.name} is in error state`
|
|
@@ -850,7 +818,7 @@ var ServiceLocatorManager = class extends BaseInstanceHolderManager {
|
|
|
850
818
|
this.logger?.log(
|
|
851
819
|
`[ServiceLocatorManager]#getInstanceHolder() Instance ${name} not found`
|
|
852
820
|
);
|
|
853
|
-
return [
|
|
821
|
+
return [DIError.instanceNotFound(name)];
|
|
854
822
|
}
|
|
855
823
|
}
|
|
856
824
|
set(name, holder) {
|
|
@@ -861,9 +829,7 @@ var ServiceLocatorManager = class extends BaseInstanceHolderManager {
|
|
|
861
829
|
if (!error) {
|
|
862
830
|
return [void 0, true];
|
|
863
831
|
}
|
|
864
|
-
if (
|
|
865
|
-
error.code
|
|
866
|
-
)) {
|
|
832
|
+
if (error.code === "InstanceDestroying" /* InstanceDestroying */) {
|
|
867
833
|
return [error];
|
|
868
834
|
}
|
|
869
835
|
return [void 0, !!holder];
|
|
@@ -878,55 +844,29 @@ var ServiceLocatorManager = class extends BaseInstanceHolderManager {
|
|
|
878
844
|
* @param type The injectable type
|
|
879
845
|
* @param scope The injectable scope
|
|
880
846
|
* @param deps Optional set of dependencies
|
|
881
|
-
* @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
|
|
882
847
|
* @returns The created holder
|
|
883
848
|
*/
|
|
884
|
-
storeCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()
|
|
885
|
-
const holder = this.createCreatedHolder(
|
|
886
|
-
name,
|
|
887
|
-
instance,
|
|
888
|
-
type,
|
|
889
|
-
scope,
|
|
890
|
-
deps,
|
|
891
|
-
ttl
|
|
892
|
-
);
|
|
849
|
+
storeCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()) {
|
|
850
|
+
const holder = this.createCreatedHolder(name, instance, type, scope, deps);
|
|
893
851
|
this._holders.set(name, holder);
|
|
894
852
|
return holder;
|
|
895
853
|
}
|
|
896
854
|
};
|
|
897
855
|
|
|
898
|
-
// src/
|
|
899
|
-
var
|
|
900
|
-
constructor(registry
|
|
856
|
+
// src/instance-resolver.mts
|
|
857
|
+
var InstanceResolver = class {
|
|
858
|
+
constructor(registry, manager, serviceInstantiator, tokenProcessor, logger = null, serviceLocator) {
|
|
901
859
|
this.registry = registry;
|
|
860
|
+
this.manager = manager;
|
|
861
|
+
this.serviceInstantiator = serviceInstantiator;
|
|
862
|
+
this.tokenProcessor = tokenProcessor;
|
|
902
863
|
this.logger = logger;
|
|
903
|
-
this.
|
|
904
|
-
this.eventBus = new ServiceLocatorEventBus(logger);
|
|
905
|
-
this.manager = new ServiceLocatorManager(logger);
|
|
906
|
-
this.serviceInstantiator = new ServiceInstantiator(injectors);
|
|
907
|
-
}
|
|
908
|
-
eventBus;
|
|
909
|
-
manager;
|
|
910
|
-
serviceInstantiator;
|
|
911
|
-
requestContexts = /* @__PURE__ */ new Map();
|
|
912
|
-
currentRequestContext = null;
|
|
913
|
-
// ============================================================================
|
|
914
|
-
// PUBLIC METHODS
|
|
915
|
-
// ============================================================================
|
|
916
|
-
getEventBus() {
|
|
917
|
-
return this.eventBus;
|
|
918
|
-
}
|
|
919
|
-
getManager() {
|
|
920
|
-
return this.manager;
|
|
921
|
-
}
|
|
922
|
-
getInstanceIdentifier(token, args) {
|
|
923
|
-
const [err, { actualToken, validatedArgs }] = this.validateAndResolveTokenArgs(token, args);
|
|
924
|
-
if (err) {
|
|
925
|
-
throw err;
|
|
926
|
-
}
|
|
927
|
-
return this.generateInstanceName(actualToken, validatedArgs);
|
|
864
|
+
this.serviceLocator = serviceLocator;
|
|
928
865
|
}
|
|
929
|
-
|
|
866
|
+
/**
|
|
867
|
+
* Resolves an instance for the given token and arguments.
|
|
868
|
+
*/
|
|
869
|
+
async resolveInstance(token, args, requestContext) {
|
|
930
870
|
const [err, data] = await this.resolveTokenAndPrepareInstanceName(
|
|
931
871
|
token,
|
|
932
872
|
args
|
|
@@ -934,33 +874,36 @@ var ServiceLocator = class {
|
|
|
934
874
|
if (err) {
|
|
935
875
|
return [err];
|
|
936
876
|
}
|
|
937
|
-
const {
|
|
938
|
-
|
|
877
|
+
const {
|
|
878
|
+
instanceName,
|
|
879
|
+
validatedArgs,
|
|
880
|
+
realToken
|
|
881
|
+
} = data;
|
|
939
882
|
const [error, holder] = await this.retrieveOrCreateInstanceByInstanceName(
|
|
940
883
|
instanceName,
|
|
941
884
|
realToken,
|
|
942
|
-
validatedArgs
|
|
885
|
+
validatedArgs,
|
|
886
|
+
requestContext
|
|
943
887
|
);
|
|
944
888
|
if (error) {
|
|
945
889
|
return [error];
|
|
946
890
|
}
|
|
947
891
|
return [void 0, holder.instance];
|
|
948
892
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
}
|
|
954
|
-
return instance;
|
|
955
|
-
}
|
|
956
|
-
getSyncInstance(token, args) {
|
|
957
|
-
const [err, { actualToken, validatedArgs }] = this.validateAndResolveTokenArgs(token, args);
|
|
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);
|
|
958
898
|
if (err) {
|
|
959
899
|
return null;
|
|
960
900
|
}
|
|
961
|
-
const instanceName = this.generateInstanceName(
|
|
962
|
-
|
|
963
|
-
|
|
901
|
+
const instanceName = this.tokenProcessor.generateInstanceName(
|
|
902
|
+
actualToken,
|
|
903
|
+
validatedArgs
|
|
904
|
+
);
|
|
905
|
+
if (currentRequestContext) {
|
|
906
|
+
const requestHolder = currentRequestContext.get(instanceName);
|
|
964
907
|
if (requestHolder) {
|
|
965
908
|
return requestHolder.instance;
|
|
966
909
|
}
|
|
@@ -971,270 +914,96 @@ var ServiceLocator = class {
|
|
|
971
914
|
}
|
|
972
915
|
return holder.instance;
|
|
973
916
|
}
|
|
974
|
-
invalidate(service, round = 1) {
|
|
975
|
-
this.logger?.log(
|
|
976
|
-
`[ServiceLocator] Starting invalidation process for ${service}`
|
|
977
|
-
);
|
|
978
|
-
const toInvalidate = this.manager.filter(
|
|
979
|
-
(holder) => holder.name === service || holder.deps.has(service)
|
|
980
|
-
);
|
|
981
|
-
const promises = [];
|
|
982
|
-
for (const [key, holder] of toInvalidate.entries()) {
|
|
983
|
-
promises.push(this.invalidateHolder(key, holder, round));
|
|
984
|
-
}
|
|
985
|
-
return Promise.all(promises);
|
|
986
|
-
}
|
|
987
917
|
/**
|
|
988
|
-
*
|
|
918
|
+
* Internal method to resolve token args and create instance name.
|
|
919
|
+
* Handles factory token resolution and validation.
|
|
989
920
|
*/
|
|
990
|
-
async
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
await holder.creationPromise;
|
|
1001
|
-
if (round > 3) {
|
|
1002
|
-
this.logger?.error(
|
|
1003
|
-
`[ServiceLocator] ${key} creation triggered too many invalidation rounds`
|
|
1004
|
-
);
|
|
1005
|
-
return;
|
|
1006
|
-
}
|
|
1007
|
-
await this.invalidate(key, round + 1);
|
|
1008
|
-
break;
|
|
1009
|
-
default:
|
|
1010
|
-
await this.destroyHolder(key, holder);
|
|
1011
|
-
break;
|
|
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);
|
|
1012
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 }];
|
|
1013
938
|
}
|
|
1014
939
|
/**
|
|
1015
|
-
*
|
|
940
|
+
* Gets an instance by its instance name, handling all the logic after instance name creation.
|
|
1016
941
|
*/
|
|
1017
|
-
async
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
holder.destroyPromise = Promise.all(
|
|
1023
|
-
holder.destroyListeners.map((listener) => listener())
|
|
1024
|
-
).then(async () => {
|
|
1025
|
-
this.manager.delete(key);
|
|
1026
|
-
await this.emitInstanceEvent(key, "destroy");
|
|
1027
|
-
});
|
|
1028
|
-
await holder.destroyPromise;
|
|
1029
|
-
}
|
|
1030
|
-
async ready() {
|
|
1031
|
-
const holders = Array.from(this.manager.filter(() => true)).map(
|
|
1032
|
-
([, holder]) => holder
|
|
942
|
+
async retrieveOrCreateInstanceByInstanceName(instanceName, realToken, realArgs, requestContext) {
|
|
943
|
+
const existingHolder = await this.tryGetExistingInstance(
|
|
944
|
+
instanceName,
|
|
945
|
+
realToken,
|
|
946
|
+
requestContext
|
|
1033
947
|
);
|
|
1034
|
-
|
|
1035
|
-
|
|
948
|
+
if (existingHolder) {
|
|
949
|
+
return existingHolder;
|
|
950
|
+
}
|
|
951
|
+
const result = await this.createNewInstance(
|
|
952
|
+
instanceName,
|
|
953
|
+
realToken,
|
|
954
|
+
realArgs,
|
|
955
|
+
requestContext
|
|
1036
956
|
);
|
|
957
|
+
if (result[0]) {
|
|
958
|
+
return [result[0]];
|
|
959
|
+
}
|
|
960
|
+
const [, holder] = result;
|
|
961
|
+
return this.waitForInstanceReady(holder);
|
|
1037
962
|
}
|
|
1038
963
|
/**
|
|
1039
|
-
*
|
|
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.
|
|
1040
966
|
*/
|
|
1041
|
-
async
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
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;
|
|
1049
975
|
}
|
|
976
|
+
return this.tryGetSingletonInstance(instanceName);
|
|
1050
977
|
}
|
|
1051
|
-
// ============================================================================
|
|
1052
|
-
// REQUEST CONTEXT MANAGEMENT
|
|
1053
|
-
// ============================================================================
|
|
1054
978
|
/**
|
|
1055
|
-
*
|
|
1056
|
-
* @param requestId Unique identifier for this request
|
|
1057
|
-
* @param metadata Optional metadata for the request
|
|
1058
|
-
* @param priority Priority for resolution (higher = more priority)
|
|
1059
|
-
* @returns The created request context holder
|
|
979
|
+
* Attempts to get a request-scoped instance if applicable.
|
|
1060
980
|
*/
|
|
1061
|
-
|
|
1062
|
-
if (this.
|
|
1063
|
-
|
|
1064
|
-
|
|
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}`
|
|
1065
992
|
);
|
|
993
|
+
return [
|
|
994
|
+
DIError.unknown(
|
|
995
|
+
`No current request context available for request-scoped service ${instanceName}`
|
|
996
|
+
)
|
|
997
|
+
];
|
|
1066
998
|
}
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
);
|
|
1072
|
-
this.requestContexts.set(requestId, contextHolder);
|
|
1073
|
-
this.currentRequestContext = contextHolder;
|
|
1074
|
-
this.logger?.log(`[ServiceLocator] Started request context: ${requestId}`);
|
|
1075
|
-
return contextHolder;
|
|
999
|
+
const requestHolder = requestContext.get(instanceName);
|
|
1000
|
+
if (!requestHolder) {
|
|
1001
|
+
return null;
|
|
1002
|
+
}
|
|
1003
|
+
return this.waitForInstanceReady(requestHolder);
|
|
1076
1004
|
}
|
|
1077
1005
|
/**
|
|
1078
|
-
*
|
|
1079
|
-
* @param requestId The request ID to end
|
|
1080
|
-
*/
|
|
1081
|
-
async endRequest(requestId) {
|
|
1082
|
-
const contextHolder = this.requestContexts.get(requestId);
|
|
1083
|
-
if (!contextHolder) {
|
|
1084
|
-
this.logger?.warn(
|
|
1085
|
-
`[ServiceLocator] Request context ${requestId} not found`
|
|
1086
|
-
);
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
this.logger?.log(`[ServiceLocator] Ending request context: ${requestId}`);
|
|
1090
|
-
const cleanupPromises = [];
|
|
1091
|
-
for (const [, holder] of contextHolder.holders) {
|
|
1092
|
-
if (holder.destroyListeners.length > 0) {
|
|
1093
|
-
cleanupPromises.push(
|
|
1094
|
-
Promise.all(holder.destroyListeners.map((listener) => listener()))
|
|
1095
|
-
);
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
await Promise.all(cleanupPromises);
|
|
1099
|
-
contextHolder.clear();
|
|
1100
|
-
this.requestContexts.delete(requestId);
|
|
1101
|
-
if (this.currentRequestContext === contextHolder) {
|
|
1102
|
-
this.currentRequestContext = Array.from(this.requestContexts.values()).at(-1) ?? null;
|
|
1103
|
-
}
|
|
1104
|
-
this.logger?.log(`[ServiceLocator] Request context ${requestId} ended`);
|
|
1105
|
-
}
|
|
1106
|
-
/**
|
|
1107
|
-
* Gets the current request context.
|
|
1108
|
-
* @returns The current request context holder or null
|
|
1109
|
-
*/
|
|
1110
|
-
getCurrentRequestContext() {
|
|
1111
|
-
return this.currentRequestContext;
|
|
1112
|
-
}
|
|
1113
|
-
/**
|
|
1114
|
-
* Sets the current request context.
|
|
1115
|
-
* @param requestId The request ID to set as current
|
|
1116
|
-
*/
|
|
1117
|
-
setCurrentRequestContext(requestId) {
|
|
1118
|
-
const contextHolder = this.requestContexts.get(requestId);
|
|
1119
|
-
if (!contextHolder) {
|
|
1120
|
-
throw new Error(`[ServiceLocator] Request context ${requestId} not found`);
|
|
1121
|
-
}
|
|
1122
|
-
this.currentRequestContext = contextHolder;
|
|
1123
|
-
}
|
|
1124
|
-
// ============================================================================
|
|
1125
|
-
// PRIVATE METHODS
|
|
1126
|
-
// ============================================================================
|
|
1127
|
-
/**
|
|
1128
|
-
* Validates and resolves token arguments, handling factory token resolution and validation.
|
|
1129
|
-
*/
|
|
1130
|
-
validateAndResolveTokenArgs(token, args) {
|
|
1131
|
-
let actualToken = token;
|
|
1132
|
-
if (typeof token === "function") {
|
|
1133
|
-
actualToken = getInjectableToken(token);
|
|
1134
|
-
}
|
|
1135
|
-
let realArgs = args;
|
|
1136
|
-
if (actualToken instanceof BoundInjectionToken) {
|
|
1137
|
-
realArgs = actualToken.value;
|
|
1138
|
-
} else if (actualToken instanceof FactoryInjectionToken) {
|
|
1139
|
-
if (actualToken.resolved) {
|
|
1140
|
-
realArgs = actualToken.value;
|
|
1141
|
-
} else {
|
|
1142
|
-
return [new FactoryTokenNotResolved(token.name), { actualToken }];
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
if (!actualToken.schema) {
|
|
1146
|
-
return [void 0, { actualToken, validatedArgs: realArgs }];
|
|
1147
|
-
}
|
|
1148
|
-
const validatedArgs = actualToken.schema?.safeParse(realArgs);
|
|
1149
|
-
if (validatedArgs && !validatedArgs.success) {
|
|
1150
|
-
this.logger?.error(
|
|
1151
|
-
`[ServiceLocator]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`,
|
|
1152
|
-
validatedArgs.error
|
|
1153
|
-
);
|
|
1154
|
-
return [new UnknownError(validatedArgs.error), { actualToken }];
|
|
1155
|
-
}
|
|
1156
|
-
return [void 0, { actualToken, validatedArgs: validatedArgs?.data }];
|
|
1157
|
-
}
|
|
1158
|
-
/**
|
|
1159
|
-
* Internal method to resolve token args and create instance name.
|
|
1160
|
-
* Handles factory token resolution and validation.
|
|
1161
|
-
*/
|
|
1162
|
-
async resolveTokenAndPrepareInstanceName(token, args) {
|
|
1163
|
-
const [err, { actualToken, validatedArgs }] = this.validateAndResolveTokenArgs(token, args);
|
|
1164
|
-
if (err instanceof UnknownError) {
|
|
1165
|
-
return [err];
|
|
1166
|
-
} else if (err instanceof FactoryTokenNotResolved && actualToken instanceof FactoryInjectionToken) {
|
|
1167
|
-
this.logger?.log(
|
|
1168
|
-
`[ServiceLocator]#resolveTokenAndPrepareInstanceName() Factory token not resolved, resolving it`
|
|
1169
|
-
);
|
|
1170
|
-
await actualToken.resolve(this.createFactoryContext());
|
|
1171
|
-
return this.resolveTokenAndPrepareInstanceName(token);
|
|
1172
|
-
}
|
|
1173
|
-
const instanceName = this.generateInstanceName(actualToken, validatedArgs);
|
|
1174
|
-
const realToken = actualToken instanceof BoundInjectionToken || actualToken instanceof FactoryInjectionToken ? actualToken.token : actualToken;
|
|
1175
|
-
return [void 0, { instanceName, validatedArgs, actualToken, realToken }];
|
|
1176
|
-
}
|
|
1177
|
-
/**
|
|
1178
|
-
* Gets an instance by its instance name, handling all the logic after instance name creation.
|
|
1179
|
-
*/
|
|
1180
|
-
async retrieveOrCreateInstanceByInstanceName(instanceName, realToken, realArgs) {
|
|
1181
|
-
const existingHolder = await this.tryGetExistingInstance(
|
|
1182
|
-
instanceName,
|
|
1183
|
-
realToken
|
|
1184
|
-
);
|
|
1185
|
-
if (existingHolder) {
|
|
1186
|
-
return existingHolder;
|
|
1187
|
-
}
|
|
1188
|
-
const result = await this.createNewInstance(
|
|
1189
|
-
instanceName,
|
|
1190
|
-
realToken,
|
|
1191
|
-
realArgs
|
|
1192
|
-
);
|
|
1193
|
-
if (result[0]) {
|
|
1194
|
-
return [result[0]];
|
|
1195
|
-
}
|
|
1196
|
-
const [, holder] = result;
|
|
1197
|
-
return this.waitForInstanceReady(holder);
|
|
1198
|
-
}
|
|
1199
|
-
/**
|
|
1200
|
-
* Attempts to retrieve an existing instance, handling request-scoped and singleton instances.
|
|
1201
|
-
* Returns null if no instance exists and a new one should be created.
|
|
1202
|
-
*/
|
|
1203
|
-
async tryGetExistingInstance(instanceName, realToken) {
|
|
1204
|
-
const requestResult = await this.tryGetRequestScopedInstance(
|
|
1205
|
-
instanceName,
|
|
1206
|
-
realToken
|
|
1207
|
-
);
|
|
1208
|
-
if (requestResult) {
|
|
1209
|
-
return requestResult;
|
|
1210
|
-
}
|
|
1211
|
-
return this.tryGetSingletonInstance(instanceName);
|
|
1212
|
-
}
|
|
1213
|
-
/**
|
|
1214
|
-
* Attempts to get a request-scoped instance if applicable.
|
|
1215
|
-
*/
|
|
1216
|
-
async tryGetRequestScopedInstance(instanceName, realToken) {
|
|
1217
|
-
if (!this.registry.has(realToken)) {
|
|
1218
|
-
return null;
|
|
1219
|
-
}
|
|
1220
|
-
const record = this.registry.get(realToken);
|
|
1221
|
-
if (record.scope !== "Request" /* Request */) {
|
|
1222
|
-
return null;
|
|
1223
|
-
}
|
|
1224
|
-
if (!this.currentRequestContext) {
|
|
1225
|
-
this.logger?.log(
|
|
1226
|
-
`[ServiceLocator] No current request context available for request-scoped service ${instanceName}`
|
|
1227
|
-
);
|
|
1228
|
-
return [new UnknownError("InstanceNotFound" /* InstanceNotFound */)];
|
|
1229
|
-
}
|
|
1230
|
-
const requestHolder = this.currentRequestContext.get(instanceName);
|
|
1231
|
-
if (!requestHolder) {
|
|
1232
|
-
return null;
|
|
1233
|
-
}
|
|
1234
|
-
return this.waitForInstanceReady(requestHolder);
|
|
1235
|
-
}
|
|
1236
|
-
/**
|
|
1237
|
-
* Attempts to get a singleton instance from the manager.
|
|
1006
|
+
* Attempts to get a singleton instance from the manager.
|
|
1238
1007
|
*/
|
|
1239
1008
|
async tryGetSingletonInstance(instanceName) {
|
|
1240
1009
|
const [error, holder] = this.manager.get(instanceName);
|
|
@@ -1244,16 +1013,10 @@ var ServiceLocator = class {
|
|
|
1244
1013
|
switch (error.code) {
|
|
1245
1014
|
case "InstanceDestroying" /* InstanceDestroying */:
|
|
1246
1015
|
this.logger?.log(
|
|
1247
|
-
`[
|
|
1016
|
+
`[InstanceResolver] Instance ${instanceName} is being destroyed, waiting...`
|
|
1248
1017
|
);
|
|
1249
1018
|
await holder?.destroyPromise;
|
|
1250
1019
|
return this.tryGetSingletonInstance(instanceName);
|
|
1251
|
-
case "InstanceExpired" /* InstanceExpired */:
|
|
1252
|
-
this.logger?.log(
|
|
1253
|
-
`[ServiceLocator] Instance ${instanceName} expired, invalidating...`
|
|
1254
|
-
);
|
|
1255
|
-
await this.invalidate(instanceName);
|
|
1256
|
-
return this.tryGetSingletonInstance(instanceName);
|
|
1257
1020
|
case "InstanceNotFound" /* InstanceNotFound */:
|
|
1258
1021
|
return null;
|
|
1259
1022
|
// Instance doesn't exist, should create new one
|
|
@@ -1270,59 +1033,48 @@ var ServiceLocator = class {
|
|
|
1270
1033
|
await holder.creationPromise;
|
|
1271
1034
|
return this.waitForInstanceReady(holder);
|
|
1272
1035
|
case "destroying" /* Destroying */:
|
|
1273
|
-
return [
|
|
1036
|
+
return [DIError.instanceDestroying(holder.name)];
|
|
1274
1037
|
case "error" /* Error */:
|
|
1275
1038
|
return [holder.instance];
|
|
1276
1039
|
case "created" /* Created */:
|
|
1277
1040
|
return [void 0, holder];
|
|
1278
1041
|
default:
|
|
1279
|
-
return [
|
|
1042
|
+
return [DIError.instanceNotFound("unknown")];
|
|
1280
1043
|
}
|
|
1281
1044
|
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Emits events to listeners for instance lifecycle events.
|
|
1284
|
-
*/
|
|
1285
|
-
emitInstanceEvent(name, event = "create") {
|
|
1286
|
-
this.logger?.log(
|
|
1287
|
-
`[ServiceLocator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`
|
|
1288
|
-
);
|
|
1289
|
-
return this.eventBus.emit(name, event);
|
|
1290
|
-
}
|
|
1291
1045
|
/**
|
|
1292
1046
|
* Creates a new instance for the given token and arguments.
|
|
1293
1047
|
*/
|
|
1294
|
-
async createNewInstance(instanceName, realToken, args) {
|
|
1048
|
+
async createNewInstance(instanceName, realToken, args, requestContext) {
|
|
1295
1049
|
this.logger?.log(
|
|
1296
|
-
`[
|
|
1050
|
+
`[InstanceResolver]#createNewInstance() Creating instance for ${instanceName}`
|
|
1297
1051
|
);
|
|
1298
1052
|
if (this.registry.has(realToken)) {
|
|
1299
1053
|
return this.instantiateServiceFromRegistry(
|
|
1300
1054
|
instanceName,
|
|
1301
1055
|
realToken,
|
|
1302
|
-
args
|
|
1056
|
+
args,
|
|
1057
|
+
requestContext
|
|
1303
1058
|
);
|
|
1304
1059
|
} else {
|
|
1305
|
-
return [
|
|
1060
|
+
return [DIError.factoryNotFound(realToken.name.toString())];
|
|
1306
1061
|
}
|
|
1307
1062
|
}
|
|
1308
1063
|
/**
|
|
1309
1064
|
* Instantiates a service from the registry using the service instantiator.
|
|
1310
1065
|
*/
|
|
1311
|
-
instantiateServiceFromRegistry(instanceName, token, args) {
|
|
1066
|
+
instantiateServiceFromRegistry(instanceName, token, args, requestContext) {
|
|
1312
1067
|
this.logger?.log(
|
|
1313
|
-
`[
|
|
1314
|
-
);
|
|
1315
|
-
const ctx = this.createFactoryContext(
|
|
1316
|
-
this.currentRequestContext || void 0
|
|
1068
|
+
`[InstanceResolver]#instantiateServiceFromRegistry(): Creating instance for ${instanceName} from abstract factory`
|
|
1317
1069
|
);
|
|
1070
|
+
const ctx = this.createFactoryContext();
|
|
1318
1071
|
let record = this.registry.get(token);
|
|
1319
1072
|
let { scope, type } = record;
|
|
1320
1073
|
const [deferred, holder] = this.manager.createCreatingHolder(
|
|
1321
1074
|
instanceName,
|
|
1322
1075
|
type,
|
|
1323
1076
|
scope,
|
|
1324
|
-
ctx.deps
|
|
1325
|
-
Infinity
|
|
1077
|
+
ctx.deps
|
|
1326
1078
|
);
|
|
1327
1079
|
this.serviceInstantiator.instantiateService(ctx, record, args).then(async ([error, instance]) => {
|
|
1328
1080
|
await this.handleInstantiationResult(
|
|
@@ -1332,7 +1084,8 @@ var ServiceLocator = class {
|
|
|
1332
1084
|
deferred,
|
|
1333
1085
|
scope,
|
|
1334
1086
|
error,
|
|
1335
|
-
instance
|
|
1087
|
+
instance,
|
|
1088
|
+
requestContext
|
|
1336
1089
|
);
|
|
1337
1090
|
}).catch(async (error) => {
|
|
1338
1091
|
await this.handleInstantiationError(
|
|
@@ -1343,13 +1096,13 @@ var ServiceLocator = class {
|
|
|
1343
1096
|
error
|
|
1344
1097
|
);
|
|
1345
1098
|
});
|
|
1346
|
-
this.storeInstanceByScope(scope, instanceName, holder);
|
|
1099
|
+
this.storeInstanceByScope(scope, instanceName, holder, requestContext);
|
|
1347
1100
|
return [void 0, holder];
|
|
1348
1101
|
}
|
|
1349
1102
|
/**
|
|
1350
1103
|
* Handles the result of service instantiation.
|
|
1351
1104
|
*/
|
|
1352
|
-
async handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance) {
|
|
1105
|
+
async handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance, _requestContext) {
|
|
1353
1106
|
holder.destroyListeners = ctx.getDestroyListeners();
|
|
1354
1107
|
holder.creationPromise = null;
|
|
1355
1108
|
if (error) {
|
|
@@ -1379,15 +1132,18 @@ var ServiceLocator = class {
|
|
|
1379
1132
|
if (ctx.deps.size > 0) {
|
|
1380
1133
|
ctx.deps.forEach((dependency) => {
|
|
1381
1134
|
holder.destroyListeners.push(
|
|
1382
|
-
this.
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
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
|
+
})
|
|
1387
1141
|
);
|
|
1388
1142
|
});
|
|
1389
1143
|
}
|
|
1390
|
-
|
|
1144
|
+
this.logger?.log(
|
|
1145
|
+
`[InstanceResolver] Instance ${instanceName} created successfully`
|
|
1146
|
+
);
|
|
1391
1147
|
deferred.resolve([void 0, instance]);
|
|
1392
1148
|
}
|
|
1393
1149
|
/**
|
|
@@ -1395,133 +1151,754 @@ var ServiceLocator = class {
|
|
|
1395
1151
|
*/
|
|
1396
1152
|
async handleInstantiationError(instanceName, holder, deferred, scope, error) {
|
|
1397
1153
|
this.logger?.error(
|
|
1398
|
-
`[
|
|
1154
|
+
`[InstanceResolver] Error creating instance for ${instanceName}`,
|
|
1399
1155
|
error
|
|
1400
1156
|
);
|
|
1401
1157
|
holder.status = "error" /* Error */;
|
|
1402
1158
|
holder.instance = error;
|
|
1403
1159
|
holder.creationPromise = null;
|
|
1404
1160
|
if (scope === "Singleton" /* Singleton */) {
|
|
1405
|
-
|
|
1161
|
+
this.logger?.log(
|
|
1162
|
+
`[InstanceResolver] Singleton ${instanceName} failed, will be invalidated`
|
|
1163
|
+
);
|
|
1164
|
+
this.serviceLocator.getServiceInvalidator().invalidate(instanceName);
|
|
1406
1165
|
}
|
|
1407
1166
|
deferred.reject(error);
|
|
1408
1167
|
}
|
|
1409
1168
|
/**
|
|
1410
1169
|
* Stores an instance holder based on its scope.
|
|
1411
1170
|
*/
|
|
1412
|
-
storeInstanceByScope(scope, instanceName, holder) {
|
|
1171
|
+
storeInstanceByScope(scope, instanceName, holder, requestContext) {
|
|
1413
1172
|
switch (scope) {
|
|
1414
1173
|
case "Singleton" /* Singleton */:
|
|
1415
1174
|
this.logger?.debug(
|
|
1416
|
-
`[
|
|
1175
|
+
`[InstanceResolver] Setting singleton instance for ${instanceName}`
|
|
1417
1176
|
);
|
|
1418
1177
|
this.manager.set(instanceName, holder);
|
|
1419
1178
|
break;
|
|
1420
1179
|
case "Request" /* Request */:
|
|
1421
|
-
if (
|
|
1180
|
+
if (requestContext) {
|
|
1422
1181
|
this.logger?.debug(
|
|
1423
|
-
`[
|
|
1424
|
-
);
|
|
1425
|
-
this.currentRequestContext.addInstance(
|
|
1426
|
-
instanceName,
|
|
1427
|
-
holder.instance,
|
|
1428
|
-
holder
|
|
1182
|
+
`[InstanceResolver] Setting request-scoped instance for ${instanceName}`
|
|
1429
1183
|
);
|
|
1184
|
+
requestContext.addInstance(instanceName, holder.instance, holder);
|
|
1430
1185
|
}
|
|
1431
1186
|
break;
|
|
1432
1187
|
}
|
|
1433
1188
|
}
|
|
1434
|
-
/**
|
|
1435
|
-
* Tries to get a pre-prepared instance from request contexts.
|
|
1436
|
-
*/
|
|
1437
|
-
tryGetPrePreparedInstance(instanceName, contextHolder, deps) {
|
|
1438
|
-
if (contextHolder && contextHolder.priority > 0) {
|
|
1439
|
-
const prePreparedInstance = contextHolder.get(instanceName)?.instance;
|
|
1440
|
-
if (prePreparedInstance !== void 0) {
|
|
1441
|
-
this.logger?.debug(
|
|
1442
|
-
`[ServiceLocator] Using pre-prepared instance ${instanceName} from request context ${contextHolder.requestId}`
|
|
1443
|
-
);
|
|
1444
|
-
deps.add(instanceName);
|
|
1445
|
-
return prePreparedInstance;
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
if (this.currentRequestContext && this.currentRequestContext !== contextHolder) {
|
|
1449
|
-
const prePreparedInstance = this.currentRequestContext.get(instanceName)?.instance;
|
|
1450
|
-
if (prePreparedInstance !== void 0) {
|
|
1451
|
-
this.logger?.debug(
|
|
1452
|
-
`[ServiceLocator] Using pre-prepared instance ${instanceName} from current request context ${this.currentRequestContext.requestId}`
|
|
1453
|
-
);
|
|
1454
|
-
deps.add(instanceName);
|
|
1455
|
-
return prePreparedInstance;
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
return void 0;
|
|
1459
|
-
}
|
|
1460
1189
|
/**
|
|
1461
1190
|
* Creates a factory context for dependency injection during service instantiation.
|
|
1462
|
-
* @param contextHolder Optional request context holder for priority-based resolution
|
|
1463
1191
|
*/
|
|
1464
|
-
createFactoryContext(
|
|
1465
|
-
|
|
1466
|
-
const deps = /* @__PURE__ */ new Set();
|
|
1467
|
-
const self = this;
|
|
1468
|
-
function addDestroyListener(listener) {
|
|
1469
|
-
destroyListeners.add(listener);
|
|
1470
|
-
}
|
|
1471
|
-
function getDestroyListeners() {
|
|
1472
|
-
return Array.from(destroyListeners);
|
|
1473
|
-
}
|
|
1474
|
-
return {
|
|
1475
|
-
// @ts-expect-error This is correct type
|
|
1476
|
-
async inject(token, args) {
|
|
1477
|
-
const instanceName = self.generateInstanceName(token, args);
|
|
1478
|
-
const prePreparedInstance = self.tryGetPrePreparedInstance(
|
|
1479
|
-
instanceName,
|
|
1480
|
-
contextHolder,
|
|
1481
|
-
deps
|
|
1482
|
-
);
|
|
1483
|
-
if (prePreparedInstance !== void 0) {
|
|
1484
|
-
return prePreparedInstance;
|
|
1485
|
-
}
|
|
1486
|
-
const [error, instance] = await self.getInstance(
|
|
1487
|
-
token,
|
|
1488
|
-
args,
|
|
1489
|
-
({ instanceName: instanceName2 }) => {
|
|
1490
|
-
deps.add(instanceName2);
|
|
1491
|
-
}
|
|
1492
|
-
);
|
|
1493
|
-
if (error) {
|
|
1494
|
-
throw error;
|
|
1495
|
-
}
|
|
1496
|
-
return instance;
|
|
1497
|
-
},
|
|
1498
|
-
addDestroyListener,
|
|
1499
|
-
getDestroyListeners,
|
|
1500
|
-
locator: self,
|
|
1501
|
-
deps
|
|
1502
|
-
};
|
|
1192
|
+
createFactoryContext() {
|
|
1193
|
+
return this.tokenProcessor.createFactoryContext(this.serviceLocator);
|
|
1503
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;
|
|
1504
1204
|
/**
|
|
1505
|
-
*
|
|
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
|
|
1506
1210
|
*/
|
|
1507
|
-
|
|
1508
|
-
if (
|
|
1509
|
-
|
|
1211
|
+
beginRequest(requestId, metadata, priority = 100) {
|
|
1212
|
+
if (this.requestContexts.has(requestId)) {
|
|
1213
|
+
throw new Error(
|
|
1214
|
+
`[RequestContextManager] Request context ${requestId} already exists`
|
|
1215
|
+
);
|
|
1510
1216
|
}
|
|
1511
|
-
const
|
|
1512
|
-
|
|
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;
|
|
1513
1228
|
}
|
|
1514
1229
|
/**
|
|
1515
|
-
*
|
|
1230
|
+
* Ends a request context and cleans up all associated instances.
|
|
1231
|
+
* @param requestId The request ID to end
|
|
1516
1232
|
*/
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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;
|
|
1520
1240
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
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
|
+
}
|
|
1523
1251
|
}
|
|
1524
|
-
|
|
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);
|
|
1525
1902
|
}
|
|
1526
1903
|
};
|
|
1527
1904
|
|
|
@@ -1560,14 +1937,53 @@ var _Container = class _Container {
|
|
|
1560
1937
|
* Invalidates a service and its dependencies
|
|
1561
1938
|
*/
|
|
1562
1939
|
async invalidate(service) {
|
|
1563
|
-
const
|
|
1564
|
-
if (holderMap.size === 0) {
|
|
1565
|
-
return;
|
|
1566
|
-
}
|
|
1567
|
-
const holder = holderMap.values().next().value;
|
|
1940
|
+
const holder = this.getHolderByInstance(service);
|
|
1568
1941
|
if (holder) {
|
|
1569
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
|
+
}
|
|
1570
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();
|
|
1571
1987
|
}
|
|
1572
1988
|
/**
|
|
1573
1989
|
* Waits for all pending operations to complete
|
|
@@ -1614,7 +2030,7 @@ var _Container = class _Container {
|
|
|
1614
2030
|
* This is useful for testing or resetting the container state.
|
|
1615
2031
|
*/
|
|
1616
2032
|
clear() {
|
|
1617
|
-
this.serviceLocator.
|
|
2033
|
+
return this.serviceLocator.clearAll();
|
|
1618
2034
|
}
|
|
1619
2035
|
};
|
|
1620
2036
|
_init = __decoratorStart(null);
|
|
@@ -1622,6 +2038,6 @@ _Container = __decorateElement(_init, 0, "Container", _Container_decorators, _Co
|
|
|
1622
2038
|
__runInitializers(_init, 1, _Container);
|
|
1623
2039
|
var Container = _Container;
|
|
1624
2040
|
|
|
1625
|
-
export { BaseInstanceHolderManager, BoundInjectionToken, Container,
|
|
1626
|
-
//# sourceMappingURL=chunk-
|
|
1627
|
-
//# sourceMappingURL=chunk-
|
|
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
|