@navios/di 0.3.1 → 0.4.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 (97) hide show
  1. package/README.md +67 -6
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/clover.xml +2659 -0
  5. package/coverage/coverage-final.json +46 -0
  6. package/coverage/docs/examples/basic-usage.mts.html +376 -0
  7. package/coverage/docs/examples/factory-pattern.mts.html +1039 -0
  8. package/coverage/docs/examples/index.html +176 -0
  9. package/coverage/docs/examples/injection-tokens.mts.html +760 -0
  10. package/coverage/docs/examples/request-scope-example.mts.html +847 -0
  11. package/coverage/docs/examples/service-lifecycle.mts.html +1162 -0
  12. package/coverage/favicon.png +0 -0
  13. package/coverage/index.html +236 -0
  14. package/coverage/lib/_tsup-dts-rollup.d.mts.html +2806 -0
  15. package/coverage/lib/index.d.mts.html +310 -0
  16. package/coverage/lib/index.html +131 -0
  17. package/coverage/prettify.css +1 -0
  18. package/coverage/prettify.js +2 -0
  19. package/coverage/sort-arrow-sprite.png +0 -0
  20. package/coverage/sorter.js +196 -0
  21. package/coverage/src/container.mts.html +586 -0
  22. package/coverage/src/decorators/factory.decorator.mts.html +322 -0
  23. package/coverage/src/decorators/index.html +146 -0
  24. package/coverage/src/decorators/index.mts.html +91 -0
  25. package/coverage/src/decorators/injectable.decorator.mts.html +394 -0
  26. package/coverage/src/enums/index.html +146 -0
  27. package/coverage/src/enums/index.mts.html +91 -0
  28. package/coverage/src/enums/injectable-scope.enum.mts.html +127 -0
  29. package/coverage/src/enums/injectable-type.enum.mts.html +97 -0
  30. package/coverage/src/errors/errors.enum.mts.html +109 -0
  31. package/coverage/src/errors/factory-not-found.mts.html +109 -0
  32. package/coverage/src/errors/factory-token-not-resolved.mts.html +115 -0
  33. package/coverage/src/errors/index.html +221 -0
  34. package/coverage/src/errors/index.mts.html +106 -0
  35. package/coverage/src/errors/instance-destroying.mts.html +109 -0
  36. package/coverage/src/errors/instance-expired.mts.html +109 -0
  37. package/coverage/src/errors/instance-not-found.mts.html +109 -0
  38. package/coverage/src/errors/unknown-error.mts.html +130 -0
  39. package/coverage/src/event-emitter.mts.html +400 -0
  40. package/coverage/src/factory-context.mts.html +109 -0
  41. package/coverage/src/index.html +296 -0
  42. package/coverage/src/index.mts.html +139 -0
  43. package/coverage/src/injection-token.mts.html +571 -0
  44. package/coverage/src/injector.mts.html +133 -0
  45. package/coverage/src/interfaces/factory.interface.mts.html +121 -0
  46. package/coverage/src/interfaces/index.html +161 -0
  47. package/coverage/src/interfaces/index.mts.html +94 -0
  48. package/coverage/src/interfaces/on-service-destroy.interface.mts.html +94 -0
  49. package/coverage/src/interfaces/on-service-init.interface.mts.html +94 -0
  50. package/coverage/src/registry.mts.html +247 -0
  51. package/coverage/src/request-context-holder.mts.html +607 -0
  52. package/coverage/src/service-instantiator.mts.html +559 -0
  53. package/coverage/src/service-locator-event-bus.mts.html +289 -0
  54. package/coverage/src/service-locator-instance-holder.mts.html +307 -0
  55. package/coverage/src/service-locator-manager.mts.html +604 -0
  56. package/coverage/src/service-locator.mts.html +2911 -0
  57. package/coverage/src/symbols/index.html +131 -0
  58. package/coverage/src/symbols/index.mts.html +88 -0
  59. package/coverage/src/symbols/injectable-token.mts.html +88 -0
  60. package/coverage/src/utils/defer.mts.html +304 -0
  61. package/coverage/src/utils/get-injectable-token.mts.html +142 -0
  62. package/coverage/src/utils/get-injectors.mts.html +691 -0
  63. package/coverage/src/utils/index.html +176 -0
  64. package/coverage/src/utils/index.mts.html +97 -0
  65. package/coverage/src/utils/types.mts.html +241 -0
  66. package/docs/README.md +5 -2
  67. package/docs/api-reference.md +38 -0
  68. package/docs/container.md +75 -0
  69. package/docs/getting-started.md +4 -3
  70. package/docs/injectable.md +4 -3
  71. package/docs/migration.md +177 -0
  72. package/docs/request-contexts.md +364 -0
  73. package/lib/_tsup-dts-rollup.d.mts +182 -41
  74. package/lib/_tsup-dts-rollup.d.ts +182 -41
  75. package/lib/index.d.mts +1 -0
  76. package/lib/index.d.ts +1 -0
  77. package/lib/index.js +480 -294
  78. package/lib/index.js.map +1 -1
  79. package/lib/index.mjs +480 -295
  80. package/lib/index.mjs.map +1 -1
  81. package/package.json +1 -1
  82. package/src/__tests__/defer.spec.mts +166 -0
  83. package/src/__tests__/errors.spec.mts +61 -0
  84. package/src/__tests__/event-emitter.spec.mts +163 -0
  85. package/src/__tests__/get-injectors.spec.mts +70 -0
  86. package/src/__tests__/registry.spec.mts +335 -0
  87. package/src/__tests__/request-scope.spec.mts +34 -35
  88. package/src/__tests__/service-instantiator.spec.mts +408 -0
  89. package/src/__tests__/service-locator-event-bus.spec.mts +242 -0
  90. package/src/__tests__/service-locator-manager.spec.mts +370 -0
  91. package/src/__tests__/unified-api.spec.mts +130 -0
  92. package/src/base-instance-holder-manager.mts +175 -0
  93. package/src/event-emitter.mts +5 -5
  94. package/src/index.mts +1 -0
  95. package/src/request-context-holder.mts +73 -44
  96. package/src/service-locator-manager.mts +12 -70
  97. package/src/service-locator.mts +421 -226
package/lib/index.js CHANGED
@@ -33,18 +33,18 @@ var __decorateElement = (array, flags, name, decorators, target, extra) => {
33
33
  };
34
34
 
35
35
  // src/enums/injectable-scope.enum.mts
36
- var InjectableScope = /* @__PURE__ */ ((InjectableScope4) => {
37
- InjectableScope4["Singleton"] = "Singleton";
38
- InjectableScope4["Transient"] = "Transient";
39
- InjectableScope4["Request"] = "Request";
40
- return InjectableScope4;
36
+ var InjectableScope = /* @__PURE__ */ ((InjectableScope5) => {
37
+ InjectableScope5["Singleton"] = "Singleton";
38
+ InjectableScope5["Transient"] = "Transient";
39
+ InjectableScope5["Request"] = "Request";
40
+ return InjectableScope5;
41
41
  })(InjectableScope || {});
42
42
 
43
43
  // src/enums/injectable-type.enum.mts
44
- var InjectableType = /* @__PURE__ */ ((InjectableType4) => {
45
- InjectableType4["Class"] = "Class";
46
- InjectableType4["Factory"] = "Factory";
47
- return InjectableType4;
44
+ var InjectableType = /* @__PURE__ */ ((InjectableType5) => {
45
+ InjectableType5["Class"] = "Class";
46
+ InjectableType5["Factory"] = "Factory";
47
+ return InjectableType5;
48
48
  })(InjectableType || {});
49
49
 
50
50
  // src/injection-token.mts
@@ -439,6 +439,132 @@ function createDeferred() {
439
439
  return new Deferred();
440
440
  }
441
441
 
442
+ // 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;
449
+ })(ServiceLocatorInstanceHolderStatus || {});
450
+
451
+ // src/base-instance-holder-manager.mts
452
+ var BaseInstanceHolderManager = class {
453
+ constructor(logger = null) {
454
+ this.logger = logger;
455
+ this._holders = /* @__PURE__ */ new Map();
456
+ }
457
+ _holders;
458
+ /**
459
+ * Protected getter for accessing the holders map from subclasses.
460
+ */
461
+ get holders() {
462
+ return this._holders;
463
+ }
464
+ /**
465
+ * Deletes a holder by name.
466
+ * @param name The name of the holder to delete
467
+ * @returns true if the holder was deleted, false if it didn't exist
468
+ */
469
+ delete(name) {
470
+ return this._holders.delete(name);
471
+ }
472
+ /**
473
+ * Filters holders based on a predicate function.
474
+ * @param predicate Function to test each holder
475
+ * @returns A new Map containing only the holders that match the predicate
476
+ */
477
+ filter(predicate) {
478
+ return new Map(
479
+ [...this._holders].filter(([key, value]) => predicate(value, key))
480
+ );
481
+ }
482
+ /**
483
+ * Clears all holders from this manager.
484
+ */
485
+ clear() {
486
+ this._holders.clear();
487
+ }
488
+ /**
489
+ * Gets the number of holders currently managed.
490
+ */
491
+ size() {
492
+ return this._holders.size;
493
+ }
494
+ /**
495
+ * Creates a new holder with Creating status and a deferred creation promise.
496
+ * This is useful for creating placeholder holders that can be fulfilled later.
497
+ * @param name The name of the instance
498
+ * @param type The injectable type
499
+ * @param scope The injectable scope
500
+ * @param deps Optional set of dependencies
501
+ * @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
502
+ * @returns A tuple containing the deferred promise and the holder
503
+ */
504
+ createCreatingHolder(name, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
505
+ const deferred = createDeferred();
506
+ const holder = {
507
+ status: "creating" /* Creating */,
508
+ name,
509
+ instance: null,
510
+ creationPromise: deferred.promise,
511
+ destroyPromise: null,
512
+ type,
513
+ scope,
514
+ deps,
515
+ destroyListeners: [],
516
+ createdAt: Date.now(),
517
+ ttl
518
+ };
519
+ return [deferred, holder];
520
+ }
521
+ /**
522
+ * Creates a new holder with Created status and an actual instance.
523
+ * This is useful for creating holders that already have their instance ready.
524
+ * @param name The name of the instance
525
+ * @param instance The actual instance to store
526
+ * @param type The injectable type
527
+ * @param scope The injectable scope
528
+ * @param deps Optional set of dependencies
529
+ * @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
530
+ * @returns The created holder
531
+ */
532
+ createCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
533
+ const holder = {
534
+ status: "created" /* Created */,
535
+ name,
536
+ instance,
537
+ creationPromise: null,
538
+ destroyPromise: null,
539
+ type,
540
+ scope,
541
+ deps,
542
+ destroyListeners: [],
543
+ createdAt: Date.now(),
544
+ ttl
545
+ };
546
+ return holder;
547
+ }
548
+ /**
549
+ * Gets all holder names currently managed.
550
+ */
551
+ getAllNames() {
552
+ return Array.from(this._holders.keys());
553
+ }
554
+ /**
555
+ * Gets all holders currently managed.
556
+ */
557
+ getAllHolders() {
558
+ return Array.from(this._holders.values());
559
+ }
560
+ /**
561
+ * Checks if this manager has any holders.
562
+ */
563
+ isEmpty() {
564
+ return this._holders.size === 0;
565
+ }
566
+ };
567
+
442
568
  // src/event-emitter.mts
443
569
  var _EventEmitter_decorators, _init;
444
570
  _EventEmitter_decorators = [Injectable({ scope: "Transient" /* Transient */ })];
@@ -619,18 +745,10 @@ var ServiceInstantiator = class {
619
745
  }
620
746
  };
621
747
 
622
- // src/service-locator-instance-holder.mts
623
- var ServiceLocatorInstanceHolderStatus = /* @__PURE__ */ ((ServiceLocatorInstanceHolderStatus2) => {
624
- ServiceLocatorInstanceHolderStatus2["Created"] = "created";
625
- ServiceLocatorInstanceHolderStatus2["Creating"] = "creating";
626
- ServiceLocatorInstanceHolderStatus2["Destroying"] = "destroying";
627
- ServiceLocatorInstanceHolderStatus2["Error"] = "error";
628
- return ServiceLocatorInstanceHolderStatus2;
629
- })(ServiceLocatorInstanceHolderStatus || {});
630
-
631
748
  // src/request-context-holder.mts
632
- var DefaultRequestContextHolder = class {
749
+ var DefaultRequestContextHolder = class extends BaseInstanceHolderManager {
633
750
  constructor(requestId, priority = 100, initialMetadata) {
751
+ super(null);
634
752
  this.requestId = requestId;
635
753
  this.priority = priority;
636
754
  if (initialMetadata) {
@@ -639,46 +757,53 @@ var DefaultRequestContextHolder = class {
639
757
  });
640
758
  }
641
759
  }
642
- instances = /* @__PURE__ */ new Map();
643
- holders = /* @__PURE__ */ new Map();
644
760
  metadata = /* @__PURE__ */ new Map();
645
761
  createdAt = Date.now();
762
+ /**
763
+ * Public getter for holders to maintain interface compatibility.
764
+ */
765
+ get holders() {
766
+ return this._holders;
767
+ }
768
+ /**
769
+ * Gets a holder by name. For RequestContextHolder, this is a simple lookup.
770
+ */
771
+ get(name) {
772
+ return this._holders.get(name);
773
+ }
774
+ /**
775
+ * Sets a holder by name.
776
+ */
777
+ set(name, holder) {
778
+ this._holders.set(name, holder);
779
+ }
780
+ /**
781
+ * Checks if a holder exists by name.
782
+ */
783
+ has(name) {
784
+ return this._holders.has(name);
785
+ }
646
786
  addInstance(instanceName, instance, holder) {
647
787
  if (instanceName instanceof InjectionToken) {
648
- this.instances.set(instanceName.toString(), instance);
649
- this.holders.set(instanceName.toString(), {
788
+ const name = instanceName.toString();
789
+ const createdHolder = this.createCreatedHolder(
790
+ name,
650
791
  instance,
651
- status: "created" /* Created */,
652
- creationPromise: null,
653
- destroyPromise: null,
654
- destroyListeners: [],
655
- deps: /* @__PURE__ */ new Set(),
656
- name: instanceName.toString(),
657
- type: "Class" /* Class */,
658
- scope: "Singleton" /* Singleton */,
659
- createdAt: Date.now(),
660
- ttl: Infinity
661
- });
792
+ "Class" /* Class */,
793
+ "Singleton" /* Singleton */,
794
+ /* @__PURE__ */ new Set(),
795
+ Infinity
796
+ );
797
+ this._holders.set(name, createdHolder);
662
798
  } else {
663
799
  if (!holder) {
664
800
  throw new Error("Holder is required when adding an instance by name");
665
801
  }
666
- this.instances.set(instanceName, instance);
667
- this.holders.set(instanceName, holder);
802
+ this._holders.set(instanceName, holder);
668
803
  }
669
804
  }
670
- getInstance(instanceName) {
671
- return this.instances.get(instanceName);
672
- }
673
- getHolder(instanceName) {
674
- return this.holders.get(instanceName);
675
- }
676
- hasInstance(instanceName) {
677
- return this.instances.has(instanceName);
678
- }
679
805
  clear() {
680
- this.instances.clear();
681
- this.holders.clear();
806
+ super.clear();
682
807
  this.metadata.clear();
683
808
  }
684
809
  getMetadata(key) {
@@ -744,13 +869,12 @@ var ServiceLocatorEventBus = class {
744
869
  };
745
870
 
746
871
  // src/service-locator-manager.mts
747
- var ServiceLocatorManager = class {
872
+ var ServiceLocatorManager = class extends BaseInstanceHolderManager {
748
873
  constructor(logger = null) {
749
- this.logger = logger;
874
+ super(logger);
750
875
  }
751
- instancesHolders = /* @__PURE__ */ new Map();
752
876
  get(name) {
753
- const holder = this.instancesHolders.get(name);
877
+ const holder = this._holders.get(name);
754
878
  if (holder) {
755
879
  if (holder.ttl !== Infinity) {
756
880
  const now = Date.now();
@@ -780,7 +904,7 @@ var ServiceLocatorManager = class {
780
904
  }
781
905
  }
782
906
  set(name, holder) {
783
- this.instancesHolders.set(name, holder);
907
+ this._holders.set(name, holder);
784
908
  }
785
909
  has(name) {
786
910
  const [error, holder] = this.get(name);
@@ -794,43 +918,8 @@ var ServiceLocatorManager = class {
794
918
  }
795
919
  return [void 0, !!holder];
796
920
  }
797
- delete(name) {
798
- return this.instancesHolders.delete(name);
799
- }
800
- filter(predicate) {
801
- return new Map(
802
- [...this.instancesHolders].filter(
803
- ([key, value]) => predicate(value, key)
804
- )
805
- );
806
- }
807
- /**
808
- * Creates a new holder with Creating status and a deferred creation promise.
809
- * This is useful for creating placeholder holders that can be fulfilled later.
810
- * @param name The name of the instance
811
- * @param type The injectable type
812
- * @param scope The injectable scope
813
- * @param deps Optional set of dependencies
814
- * @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
815
- * @returns A tuple containing the deferred promise and the holder
816
- */
817
- createCreatingHolder(name, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
818
- const deferred = createDeferred();
819
- const holder = {
820
- status: "creating" /* Creating */,
821
- name,
822
- instance: null,
823
- creationPromise: deferred.promise,
824
- destroyPromise: null,
825
- type,
826
- scope,
827
- deps,
828
- destroyListeners: [],
829
- createdAt: Date.now(),
830
- ttl
831
- };
832
- return [deferred, holder];
833
- }
921
+ // delete and filter methods are inherited from BaseInstanceHolderManager
922
+ // createCreatingHolder method is inherited from BaseInstanceHolderManager
834
923
  /**
835
924
  * Creates a new holder with Created status and an actual instance.
836
925
  * This is useful for creating holders that already have their instance ready.
@@ -843,20 +932,15 @@ var ServiceLocatorManager = class {
843
932
  * @returns The created holder
844
933
  */
845
934
  storeCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
846
- const holder = {
847
- status: "created" /* Created */,
935
+ const holder = this.createCreatedHolder(
848
936
  name,
849
937
  instance,
850
- creationPromise: null,
851
- destroyPromise: null,
852
938
  type,
853
939
  scope,
854
940
  deps,
855
- destroyListeners: [],
856
- createdAt: Date.now(),
857
941
  ttl
858
- };
859
- this.instancesHolders.set(name, holder);
942
+ );
943
+ this._holders.set(name, holder);
860
944
  return holder;
861
945
  }
862
946
  };
@@ -901,9 +985,7 @@ var ServiceLocator = class {
901
985
  return [err];
902
986
  }
903
987
  const { instanceName, validatedArgs, actualToken, realToken } = data;
904
- if (onPrepare) {
905
- onPrepare({ instanceName, actualToken, validatedArgs });
906
- }
988
+ onPrepare?.({ instanceName, actualToken, validatedArgs });
907
989
  const [error, holder] = await this.retrieveOrCreateInstanceByInstanceName(
908
990
  instanceName,
909
991
  realToken,
@@ -928,7 +1010,7 @@ var ServiceLocator = class {
928
1010
  }
929
1011
  const instanceName = this.generateInstanceName(actualToken, validatedArgs);
930
1012
  if (this.currentRequestContext) {
931
- const requestHolder = this.currentRequestContext.getHolder(instanceName);
1013
+ const requestHolder = this.currentRequestContext.get(instanceName);
932
1014
  if (requestHolder) {
933
1015
  return requestHolder.instance;
934
1016
  }
@@ -941,63 +1023,80 @@ var ServiceLocator = class {
941
1023
  }
942
1024
  invalidate(service, round = 1) {
943
1025
  this.logger?.log(
944
- `[ServiceLocator]#invalidate(): Starting Invalidating process of ${service}`
1026
+ `[ServiceLocator] Starting invalidation process for ${service}`
945
1027
  );
946
1028
  const toInvalidate = this.manager.filter(
947
1029
  (holder) => holder.name === service || holder.deps.has(service)
948
1030
  );
949
1031
  const promises = [];
950
1032
  for (const [key, holder] of toInvalidate.entries()) {
951
- if (holder.status === "destroying" /* Destroying */) {
952
- this.logger?.trace(
953
- `[ServiceLocator]#invalidate(): ${key} is already being destroyed`
954
- );
955
- promises.push(holder.destroyPromise);
956
- continue;
957
- }
958
- if (holder.status === "creating" /* Creating */) {
1033
+ promises.push(this.invalidateHolder(key, holder, round));
1034
+ }
1035
+ return Promise.all(promises);
1036
+ }
1037
+ /**
1038
+ * Invalidates a single holder based on its current status.
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 */:
959
1047
  this.logger?.trace(
960
- `[ServiceLocator]#invalidate(): ${key} is being created, waiting for creation to finish`
961
- );
962
- promises.push(
963
- holder.creationPromise?.then(() => {
964
- if (round > 3) {
965
- this.logger?.error(
966
- `[ServiceLocator]#invalidate(): ${key} creation is triggering a new invalidation round, but it is still not created`
967
- );
968
- return;
969
- }
970
- return this.invalidate(key, round + 1);
971
- })
1048
+ `[ServiceLocator] ${key} is being created, waiting...`
972
1049
  );
973
- continue;
974
- }
975
- holder.status = "destroying" /* Destroying */;
976
- this.logger?.log(
977
- `[ServiceLocator]#invalidate(): Invalidating ${key} and notifying listeners`
978
- );
979
- holder.destroyPromise = Promise.all(
980
- holder.destroyListeners.map((listener) => listener())
981
- ).then(async () => {
982
- this.manager.delete(key);
983
- await this.emitInstanceEvent(key, "destroy");
984
- });
985
- promises.push(holder.destroyPromise);
1050
+ await holder.creationPromise;
1051
+ if (round > 3) {
1052
+ this.logger?.error(
1053
+ `[ServiceLocator] ${key} creation triggered too many invalidation rounds`
1054
+ );
1055
+ return;
1056
+ }
1057
+ await this.invalidate(key, round + 1);
1058
+ break;
1059
+ default:
1060
+ await this.destroyHolder(key, holder);
1061
+ break;
986
1062
  }
987
- return Promise.all(promises);
1063
+ }
1064
+ /**
1065
+ * Destroys a holder and cleans up its resources.
1066
+ */
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;
988
1079
  }
989
1080
  async ready() {
990
- return Promise.all(
991
- Array.from(this.manager.filter(() => true)).map(([, holder]) => {
992
- if (holder.status === "creating" /* Creating */) {
993
- return holder.creationPromise?.then(() => null);
994
- }
995
- if (holder.status === "destroying" /* Destroying */) {
996
- return holder.destroyPromise.then(() => null);
997
- }
998
- return Promise.resolve(null);
999
- })
1000
- ).then(() => null);
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
+ );
1087
+ }
1088
+ /**
1089
+ * Waits for a holder to settle (either created, destroyed, or error state).
1090
+ */
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;
1099
+ }
1001
1100
  }
1002
1101
  // ============================================================================
1003
1102
  // REQUEST CONTEXT MANAGEMENT
@@ -1129,86 +1228,106 @@ var ServiceLocator = class {
1129
1228
  * Gets an instance by its instance name, handling all the logic after instance name creation.
1130
1229
  */
1131
1230
  async retrieveOrCreateInstanceByInstanceName(instanceName, realToken, realArgs) {
1132
- if (this.registry.has(realToken)) {
1133
- const record = this.registry.get(realToken);
1134
- if (record.scope === "Request" /* Request */) {
1135
- if (!this.currentRequestContext) {
1136
- this.logger?.log(
1137
- `[ServiceLocator]#retrieveOrCreateInstanceByInstanceName() No current request context available for request-scoped service ${instanceName}`
1138
- );
1139
- return [new UnknownError("InstanceNotFound" /* InstanceNotFound */)];
1140
- }
1141
- const requestHolder = this.currentRequestContext.getHolder(instanceName);
1142
- if (requestHolder) {
1143
- if (requestHolder.status === "creating" /* Creating */) {
1144
- await requestHolder.creationPromise;
1145
- return this.retrieveOrCreateInstanceByInstanceName(
1146
- instanceName,
1147
- realToken,
1148
- realArgs
1149
- );
1150
- } else if (requestHolder.status === "destroying" /* Destroying */) {
1151
- return [new UnknownError("InstanceDestroying" /* InstanceDestroying */)];
1152
- }
1153
- return [void 0, requestHolder];
1154
- }
1155
- }
1231
+ const existingHolder = await this.tryGetExistingInstance(
1232
+ instanceName,
1233
+ realToken
1234
+ );
1235
+ if (existingHolder) {
1236
+ return existingHolder;
1156
1237
  }
1238
+ const result = await this.createNewInstance(
1239
+ instanceName,
1240
+ realToken,
1241
+ realArgs
1242
+ );
1243
+ if (result[0]) {
1244
+ return [result[0]];
1245
+ }
1246
+ const [, holder] = result;
1247
+ return this.waitForInstanceReady(holder);
1248
+ }
1249
+ /**
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.
1252
+ */
1253
+ async tryGetExistingInstance(instanceName, realToken) {
1254
+ const requestResult = await this.tryGetRequestScopedInstance(
1255
+ instanceName,
1256
+ realToken
1257
+ );
1258
+ if (requestResult) {
1259
+ return requestResult;
1260
+ }
1261
+ return this.tryGetSingletonInstance(instanceName);
1262
+ }
1263
+ /**
1264
+ * Attempts to get a request-scoped instance if applicable.
1265
+ */
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) {
1275
+ this.logger?.log(
1276
+ `[ServiceLocator] No current request context available for request-scoped service ${instanceName}`
1277
+ );
1278
+ return [new UnknownError("InstanceNotFound" /* InstanceNotFound */)];
1279
+ }
1280
+ const requestHolder = this.currentRequestContext.get(instanceName);
1281
+ if (!requestHolder) {
1282
+ return null;
1283
+ }
1284
+ return this.waitForInstanceReady(requestHolder);
1285
+ }
1286
+ /**
1287
+ * Attempts to get a singleton instance from the manager.
1288
+ */
1289
+ async tryGetSingletonInstance(instanceName) {
1157
1290
  const [error, holder] = this.manager.get(instanceName);
1158
1291
  if (!error) {
1159
- if (holder.status === "creating" /* Creating */) {
1160
- await holder.creationPromise;
1161
- return this.retrieveOrCreateInstanceByInstanceName(
1162
- instanceName,
1163
- realToken,
1164
- realArgs
1165
- );
1166
- } else if (holder.status === "destroying" /* Destroying */) {
1167
- return [new UnknownError("InstanceDestroying" /* InstanceDestroying */)];
1168
- }
1169
- return [void 0, holder];
1292
+ return this.waitForInstanceReady(holder);
1170
1293
  }
1171
1294
  switch (error.code) {
1172
1295
  case "InstanceDestroying" /* InstanceDestroying */:
1173
1296
  this.logger?.log(
1174
- `[ServiceLocator]#retrieveOrCreateInstanceByInstanceName() TTL expired for ${holder?.name}`
1297
+ `[ServiceLocator] Instance ${instanceName} is being destroyed, waiting...`
1175
1298
  );
1176
1299
  await holder?.destroyPromise;
1177
- return this.retrieveOrCreateInstanceByInstanceName(
1178
- instanceName,
1179
- realToken,
1180
- realArgs
1181
- );
1300
+ return this.tryGetSingletonInstance(instanceName);
1182
1301
  case "InstanceExpired" /* InstanceExpired */:
1183
1302
  this.logger?.log(
1184
- `[ServiceLocator]#retrieveOrCreateInstanceByInstanceName() TTL expired for ${holder?.name}`
1303
+ `[ServiceLocator] Instance ${instanceName} expired, invalidating...`
1185
1304
  );
1186
1305
  await this.invalidate(instanceName);
1187
- return this.retrieveOrCreateInstanceByInstanceName(
1188
- instanceName,
1189
- realToken,
1190
- realArgs
1191
- );
1306
+ return this.tryGetSingletonInstance(instanceName);
1192
1307
  case "InstanceNotFound" /* InstanceNotFound */:
1193
- break;
1308
+ return null;
1309
+ // Instance doesn't exist, should create new one
1194
1310
  default:
1195
1311
  return [error];
1196
1312
  }
1197
- const result = await this.createNewInstance(
1198
- instanceName,
1199
- realToken,
1200
- realArgs
1201
- );
1202
- if (result[0]) {
1203
- return [result[0]];
1204
- }
1205
- if (result[1].status === "creating" /* Creating */) {
1206
- await result[1].creationPromise;
1207
- }
1208
- if (result[1].status === "error" /* Error */) {
1209
- return [result[1].instance];
1313
+ }
1314
+ /**
1315
+ * Waits for an instance holder to be ready and returns the appropriate result.
1316
+ */
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 */)];
1210
1330
  }
1211
- return [void 0, result[1]];
1212
1331
  }
1213
1332
  /**
1214
1333
  * Emits events to listeners for instance lifecycle events.
@@ -1256,65 +1375,137 @@ var ServiceLocator = class {
1256
1375
  Infinity
1257
1376
  );
1258
1377
  this.serviceInstantiator.instantiateService(ctx, record, args).then(async ([error, instance]) => {
1259
- holder.destroyListeners = ctx.getDestroyListeners();
1260
- holder.creationPromise = null;
1261
- if (error) {
1262
- this.logger?.error(
1263
- `[ServiceLocator]#instantiateServiceFromRegistry(): Error creating instance for ${instanceName}`,
1264
- error
1265
- );
1266
- holder.status = "error" /* Error */;
1267
- holder.instance = error;
1268
- if (scope === "Singleton" /* Singleton */) {
1269
- setTimeout(() => this.invalidate(instanceName), 10);
1270
- }
1271
- deferred.reject(error);
1272
- } else {
1273
- holder.instance = instance;
1274
- holder.status = "created" /* Created */;
1275
- if (ctx.deps.size > 0) {
1276
- ctx.deps.forEach((dependency) => {
1277
- holder.destroyListeners.push(
1278
- this.eventBus.on(
1279
- dependency,
1280
- "destroy",
1281
- () => this.invalidate(instanceName)
1282
- )
1283
- );
1284
- });
1285
- }
1286
- await this.emitInstanceEvent(instanceName);
1287
- deferred.resolve([void 0, instance]);
1288
- }
1289
- }).catch((error) => {
1290
- this.logger?.error(
1291
- `[ServiceLocator]#instantiateServiceFromRegistry(): Unexpected error creating instance for ${instanceName}`,
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,
1292
1393
  error
1293
1394
  );
1294
- holder.status = "error" /* Error */;
1295
- holder.instance = error;
1296
- holder.creationPromise = null;
1297
- if (scope === "Singleton" /* Singleton */) {
1298
- setTimeout(() => this.invalidate(instanceName), 10);
1299
- }
1300
- deferred.reject(error);
1301
1395
  });
1302
- if (scope === "Singleton" /* Singleton */) {
1303
- this.logger?.debug(
1304
- `[ServiceLocator]#instantiateServiceFromRegistry(): Setting instance for ${instanceName}`
1305
- );
1306
- this.manager.set(instanceName, holder);
1307
- } else if (scope === "Request" /* Request */ && this.currentRequestContext) {
1308
- this.logger?.debug(
1309
- `[ServiceLocator]#instantiateServiceFromRegistry(): Setting request-scoped instance for ${instanceName}`
1396
+ this.storeInstanceByScope(scope, instanceName, holder);
1397
+ return [void 0, holder];
1398
+ }
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
1310
1412
  );
1311
- this.currentRequestContext.addInstance(
1413
+ } else {
1414
+ await this.handleInstantiationSuccess(
1312
1415
  instanceName,
1313
- holder.instance,
1314
- holder
1416
+ holder,
1417
+ ctx,
1418
+ deferred,
1419
+ instance
1315
1420
  );
1316
1421
  }
1317
- return [void 0, holder];
1422
+ }
1423
+ /**
1424
+ * Handles successful service instantiation.
1425
+ */
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
+ });
1439
+ }
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);
1456
+ }
1457
+ deferred.reject(error);
1458
+ }
1459
+ /**
1460
+ * Stores an instance holder based on its scope.
1461
+ */
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;
1482
+ }
1483
+ }
1484
+ /**
1485
+ * Tries to get a pre-prepared instance from request contexts.
1486
+ */
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
+ }
1497
+ }
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
+ }
1507
+ }
1508
+ return void 0;
1318
1509
  }
1319
1510
  /**
1320
1511
  * Creates a factory context for dependency injection during service instantiation.
@@ -1333,33 +1524,20 @@ var ServiceLocator = class {
1333
1524
  return {
1334
1525
  // @ts-expect-error This is correct type
1335
1526
  async inject(token, args) {
1336
- if (contextHolder && contextHolder.priority > 0) {
1337
- const instanceName = self.generateInstanceName(token, args);
1338
- const prePreparedInstance = contextHolder.getInstance(instanceName);
1339
- if (prePreparedInstance !== void 0) {
1340
- self.logger?.debug(
1341
- `[ServiceLocator] Using pre-prepared instance ${instanceName} from request context ${contextHolder.requestId}`
1342
- );
1343
- deps.add(instanceName);
1344
- return prePreparedInstance;
1345
- }
1346
- }
1347
- if (self.currentRequestContext && self.currentRequestContext !== contextHolder) {
1348
- const instanceName = self.generateInstanceName(token, args);
1349
- const prePreparedInstance = self.currentRequestContext.getInstance(instanceName);
1350
- if (prePreparedInstance !== void 0) {
1351
- self.logger?.debug(
1352
- `[ServiceLocator] Using pre-prepared instance ${instanceName} from current request context ${self.currentRequestContext.requestId}`
1353
- );
1354
- deps.add(instanceName);
1355
- return prePreparedInstance;
1356
- }
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;
1357
1535
  }
1358
1536
  const [error, instance] = await self.getInstance(
1359
1537
  token,
1360
1538
  args,
1361
- ({ instanceName }) => {
1362
- deps.add(instanceName);
1539
+ ({ instanceName: instanceName2 }) => {
1540
+ deps.add(instanceName2);
1363
1541
  }
1364
1542
  );
1365
1543
  if (error) {
@@ -1377,16 +1555,23 @@ var ServiceLocator = class {
1377
1555
  * Generates a unique instance name based on token and arguments.
1378
1556
  */
1379
1557
  generateInstanceName(token, args) {
1380
- const formattedArgs = args ? ":" + Object.entries(args ?? {}).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => {
1381
- if (typeof value === "function") {
1382
- return `${key}=fn_${value.name}(${value.length})`;
1383
- }
1384
- if (typeof value === "symbol") {
1385
- return `${key}=${value.toString()}`;
1386
- }
1387
- return `${key}=${JSON.stringify(value).slice(0, 40)}`;
1388
- }).join(",").replaceAll(/"/g, "").replaceAll(/:/g, "=") : "";
1389
- return `${token.toString()}${formattedArgs}`;
1558
+ if (!args) {
1559
+ return token.toString();
1560
+ }
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, "=")}`;
1563
+ }
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})`;
1570
+ }
1571
+ if (typeof value === "symbol") {
1572
+ return value.toString();
1573
+ }
1574
+ return JSON.stringify(value).slice(0, 40);
1390
1575
  }
1391
1576
  };
1392
1577
 
@@ -1477,6 +1662,7 @@ _Container = __decorateElement(_init2, 0, "Container", _Container_decorators, _C
1477
1662
  __runInitializers(_init2, 1, _Container);
1478
1663
  var Container = _Container;
1479
1664
 
1665
+ exports.BaseInstanceHolderManager = BaseInstanceHolderManager;
1480
1666
  exports.BoundInjectionToken = BoundInjectionToken;
1481
1667
  exports.Container = Container;
1482
1668
  exports.DefaultRequestContextHolder = DefaultRequestContextHolder;