@navios/di 0.4.2 → 0.5.1

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