@navios/di 0.4.1 → 0.5.0

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