@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.mjs CHANGED
@@ -31,18 +31,18 @@ var __decorateElement = (array, flags, name, decorators, target, extra) => {
31
31
  };
32
32
 
33
33
  // src/enums/injectable-scope.enum.mts
34
- var InjectableScope = /* @__PURE__ */ ((InjectableScope4) => {
35
- InjectableScope4["Singleton"] = "Singleton";
36
- InjectableScope4["Transient"] = "Transient";
37
- InjectableScope4["Request"] = "Request";
38
- return InjectableScope4;
34
+ var InjectableScope = /* @__PURE__ */ ((InjectableScope5) => {
35
+ InjectableScope5["Singleton"] = "Singleton";
36
+ InjectableScope5["Transient"] = "Transient";
37
+ InjectableScope5["Request"] = "Request";
38
+ return InjectableScope5;
39
39
  })(InjectableScope || {});
40
40
 
41
41
  // src/enums/injectable-type.enum.mts
42
- var InjectableType = /* @__PURE__ */ ((InjectableType4) => {
43
- InjectableType4["Class"] = "Class";
44
- InjectableType4["Factory"] = "Factory";
45
- return InjectableType4;
42
+ var InjectableType = /* @__PURE__ */ ((InjectableType5) => {
43
+ InjectableType5["Class"] = "Class";
44
+ InjectableType5["Factory"] = "Factory";
45
+ return InjectableType5;
46
46
  })(InjectableType || {});
47
47
 
48
48
  // src/injection-token.mts
@@ -437,6 +437,132 @@ function createDeferred() {
437
437
  return new Deferred();
438
438
  }
439
439
 
440
+ // src/service-locator-instance-holder.mts
441
+ var ServiceLocatorInstanceHolderStatus = /* @__PURE__ */ ((ServiceLocatorInstanceHolderStatus3) => {
442
+ ServiceLocatorInstanceHolderStatus3["Created"] = "created";
443
+ ServiceLocatorInstanceHolderStatus3["Creating"] = "creating";
444
+ ServiceLocatorInstanceHolderStatus3["Destroying"] = "destroying";
445
+ ServiceLocatorInstanceHolderStatus3["Error"] = "error";
446
+ return ServiceLocatorInstanceHolderStatus3;
447
+ })(ServiceLocatorInstanceHolderStatus || {});
448
+
449
+ // src/base-instance-holder-manager.mts
450
+ var BaseInstanceHolderManager = class {
451
+ constructor(logger = null) {
452
+ this.logger = logger;
453
+ this._holders = /* @__PURE__ */ new Map();
454
+ }
455
+ _holders;
456
+ /**
457
+ * Protected getter for accessing the holders map from subclasses.
458
+ */
459
+ get holders() {
460
+ return this._holders;
461
+ }
462
+ /**
463
+ * Deletes a holder by name.
464
+ * @param name The name of the holder to delete
465
+ * @returns true if the holder was deleted, false if it didn't exist
466
+ */
467
+ delete(name) {
468
+ return this._holders.delete(name);
469
+ }
470
+ /**
471
+ * Filters holders based on a predicate function.
472
+ * @param predicate Function to test each holder
473
+ * @returns A new Map containing only the holders that match the predicate
474
+ */
475
+ filter(predicate) {
476
+ return new Map(
477
+ [...this._holders].filter(([key, value]) => predicate(value, key))
478
+ );
479
+ }
480
+ /**
481
+ * Clears all holders from this manager.
482
+ */
483
+ clear() {
484
+ this._holders.clear();
485
+ }
486
+ /**
487
+ * Gets the number of holders currently managed.
488
+ */
489
+ size() {
490
+ return this._holders.size;
491
+ }
492
+ /**
493
+ * Creates a new holder with Creating status and a deferred creation promise.
494
+ * This is useful for creating placeholder holders that can be fulfilled later.
495
+ * @param name The name of the instance
496
+ * @param type The injectable type
497
+ * @param scope The injectable scope
498
+ * @param deps Optional set of dependencies
499
+ * @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
500
+ * @returns A tuple containing the deferred promise and the holder
501
+ */
502
+ createCreatingHolder(name, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
503
+ const deferred = createDeferred();
504
+ const holder = {
505
+ status: "creating" /* Creating */,
506
+ name,
507
+ instance: null,
508
+ creationPromise: deferred.promise,
509
+ destroyPromise: null,
510
+ type,
511
+ scope,
512
+ deps,
513
+ destroyListeners: [],
514
+ createdAt: Date.now(),
515
+ ttl
516
+ };
517
+ return [deferred, holder];
518
+ }
519
+ /**
520
+ * Creates a new holder with Created status and an actual instance.
521
+ * This is useful for creating holders that already have their instance ready.
522
+ * @param name The name of the instance
523
+ * @param instance The actual instance to store
524
+ * @param type The injectable type
525
+ * @param scope The injectable scope
526
+ * @param deps Optional set of dependencies
527
+ * @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
528
+ * @returns The created holder
529
+ */
530
+ createCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
531
+ const holder = {
532
+ status: "created" /* Created */,
533
+ name,
534
+ instance,
535
+ creationPromise: null,
536
+ destroyPromise: null,
537
+ type,
538
+ scope,
539
+ deps,
540
+ destroyListeners: [],
541
+ createdAt: Date.now(),
542
+ ttl
543
+ };
544
+ return holder;
545
+ }
546
+ /**
547
+ * Gets all holder names currently managed.
548
+ */
549
+ getAllNames() {
550
+ return Array.from(this._holders.keys());
551
+ }
552
+ /**
553
+ * Gets all holders currently managed.
554
+ */
555
+ getAllHolders() {
556
+ return Array.from(this._holders.values());
557
+ }
558
+ /**
559
+ * Checks if this manager has any holders.
560
+ */
561
+ isEmpty() {
562
+ return this._holders.size === 0;
563
+ }
564
+ };
565
+
440
566
  // src/event-emitter.mts
441
567
  var _EventEmitter_decorators, _init;
442
568
  _EventEmitter_decorators = [Injectable({ scope: "Transient" /* Transient */ })];
@@ -617,18 +743,10 @@ var ServiceInstantiator = class {
617
743
  }
618
744
  };
619
745
 
620
- // src/service-locator-instance-holder.mts
621
- var ServiceLocatorInstanceHolderStatus = /* @__PURE__ */ ((ServiceLocatorInstanceHolderStatus2) => {
622
- ServiceLocatorInstanceHolderStatus2["Created"] = "created";
623
- ServiceLocatorInstanceHolderStatus2["Creating"] = "creating";
624
- ServiceLocatorInstanceHolderStatus2["Destroying"] = "destroying";
625
- ServiceLocatorInstanceHolderStatus2["Error"] = "error";
626
- return ServiceLocatorInstanceHolderStatus2;
627
- })(ServiceLocatorInstanceHolderStatus || {});
628
-
629
746
  // src/request-context-holder.mts
630
- var DefaultRequestContextHolder = class {
747
+ var DefaultRequestContextHolder = class extends BaseInstanceHolderManager {
631
748
  constructor(requestId, priority = 100, initialMetadata) {
749
+ super(null);
632
750
  this.requestId = requestId;
633
751
  this.priority = priority;
634
752
  if (initialMetadata) {
@@ -637,46 +755,53 @@ var DefaultRequestContextHolder = class {
637
755
  });
638
756
  }
639
757
  }
640
- instances = /* @__PURE__ */ new Map();
641
- holders = /* @__PURE__ */ new Map();
642
758
  metadata = /* @__PURE__ */ new Map();
643
759
  createdAt = Date.now();
760
+ /**
761
+ * Public getter for holders to maintain interface compatibility.
762
+ */
763
+ get holders() {
764
+ return this._holders;
765
+ }
766
+ /**
767
+ * Gets a holder by name. For RequestContextHolder, this is a simple lookup.
768
+ */
769
+ get(name) {
770
+ return this._holders.get(name);
771
+ }
772
+ /**
773
+ * Sets a holder by name.
774
+ */
775
+ set(name, holder) {
776
+ this._holders.set(name, holder);
777
+ }
778
+ /**
779
+ * Checks if a holder exists by name.
780
+ */
781
+ has(name) {
782
+ return this._holders.has(name);
783
+ }
644
784
  addInstance(instanceName, instance, holder) {
645
785
  if (instanceName instanceof InjectionToken) {
646
- this.instances.set(instanceName.toString(), instance);
647
- this.holders.set(instanceName.toString(), {
786
+ const name = instanceName.toString();
787
+ const createdHolder = this.createCreatedHolder(
788
+ name,
648
789
  instance,
649
- status: "created" /* Created */,
650
- creationPromise: null,
651
- destroyPromise: null,
652
- destroyListeners: [],
653
- deps: /* @__PURE__ */ new Set(),
654
- name: instanceName.toString(),
655
- type: "Class" /* Class */,
656
- scope: "Singleton" /* Singleton */,
657
- createdAt: Date.now(),
658
- ttl: Infinity
659
- });
790
+ "Class" /* Class */,
791
+ "Singleton" /* Singleton */,
792
+ /* @__PURE__ */ new Set(),
793
+ Infinity
794
+ );
795
+ this._holders.set(name, createdHolder);
660
796
  } else {
661
797
  if (!holder) {
662
798
  throw new Error("Holder is required when adding an instance by name");
663
799
  }
664
- this.instances.set(instanceName, instance);
665
- this.holders.set(instanceName, holder);
800
+ this._holders.set(instanceName, holder);
666
801
  }
667
802
  }
668
- getInstance(instanceName) {
669
- return this.instances.get(instanceName);
670
- }
671
- getHolder(instanceName) {
672
- return this.holders.get(instanceName);
673
- }
674
- hasInstance(instanceName) {
675
- return this.instances.has(instanceName);
676
- }
677
803
  clear() {
678
- this.instances.clear();
679
- this.holders.clear();
804
+ super.clear();
680
805
  this.metadata.clear();
681
806
  }
682
807
  getMetadata(key) {
@@ -742,13 +867,12 @@ var ServiceLocatorEventBus = class {
742
867
  };
743
868
 
744
869
  // src/service-locator-manager.mts
745
- var ServiceLocatorManager = class {
870
+ var ServiceLocatorManager = class extends BaseInstanceHolderManager {
746
871
  constructor(logger = null) {
747
- this.logger = logger;
872
+ super(logger);
748
873
  }
749
- instancesHolders = /* @__PURE__ */ new Map();
750
874
  get(name) {
751
- const holder = this.instancesHolders.get(name);
875
+ const holder = this._holders.get(name);
752
876
  if (holder) {
753
877
  if (holder.ttl !== Infinity) {
754
878
  const now = Date.now();
@@ -778,7 +902,7 @@ var ServiceLocatorManager = class {
778
902
  }
779
903
  }
780
904
  set(name, holder) {
781
- this.instancesHolders.set(name, holder);
905
+ this._holders.set(name, holder);
782
906
  }
783
907
  has(name) {
784
908
  const [error, holder] = this.get(name);
@@ -792,43 +916,8 @@ var ServiceLocatorManager = class {
792
916
  }
793
917
  return [void 0, !!holder];
794
918
  }
795
- delete(name) {
796
- return this.instancesHolders.delete(name);
797
- }
798
- filter(predicate) {
799
- return new Map(
800
- [...this.instancesHolders].filter(
801
- ([key, value]) => predicate(value, key)
802
- )
803
- );
804
- }
805
- /**
806
- * Creates a new holder with Creating status and a deferred creation promise.
807
- * This is useful for creating placeholder holders that can be fulfilled later.
808
- * @param name The name of the instance
809
- * @param type The injectable type
810
- * @param scope The injectable scope
811
- * @param deps Optional set of dependencies
812
- * @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
813
- * @returns A tuple containing the deferred promise and the holder
814
- */
815
- createCreatingHolder(name, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
816
- const deferred = createDeferred();
817
- const holder = {
818
- status: "creating" /* Creating */,
819
- name,
820
- instance: null,
821
- creationPromise: deferred.promise,
822
- destroyPromise: null,
823
- type,
824
- scope,
825
- deps,
826
- destroyListeners: [],
827
- createdAt: Date.now(),
828
- ttl
829
- };
830
- return [deferred, holder];
831
- }
919
+ // delete and filter methods are inherited from BaseInstanceHolderManager
920
+ // createCreatingHolder method is inherited from BaseInstanceHolderManager
832
921
  /**
833
922
  * Creates a new holder with Created status and an actual instance.
834
923
  * This is useful for creating holders that already have their instance ready.
@@ -841,20 +930,15 @@ var ServiceLocatorManager = class {
841
930
  * @returns The created holder
842
931
  */
843
932
  storeCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
844
- const holder = {
845
- status: "created" /* Created */,
933
+ const holder = this.createCreatedHolder(
846
934
  name,
847
935
  instance,
848
- creationPromise: null,
849
- destroyPromise: null,
850
936
  type,
851
937
  scope,
852
938
  deps,
853
- destroyListeners: [],
854
- createdAt: Date.now(),
855
939
  ttl
856
- };
857
- this.instancesHolders.set(name, holder);
940
+ );
941
+ this._holders.set(name, holder);
858
942
  return holder;
859
943
  }
860
944
  };
@@ -899,9 +983,7 @@ var ServiceLocator = class {
899
983
  return [err];
900
984
  }
901
985
  const { instanceName, validatedArgs, actualToken, realToken } = data;
902
- if (onPrepare) {
903
- onPrepare({ instanceName, actualToken, validatedArgs });
904
- }
986
+ onPrepare?.({ instanceName, actualToken, validatedArgs });
905
987
  const [error, holder] = await this.retrieveOrCreateInstanceByInstanceName(
906
988
  instanceName,
907
989
  realToken,
@@ -926,7 +1008,7 @@ var ServiceLocator = class {
926
1008
  }
927
1009
  const instanceName = this.generateInstanceName(actualToken, validatedArgs);
928
1010
  if (this.currentRequestContext) {
929
- const requestHolder = this.currentRequestContext.getHolder(instanceName);
1011
+ const requestHolder = this.currentRequestContext.get(instanceName);
930
1012
  if (requestHolder) {
931
1013
  return requestHolder.instance;
932
1014
  }
@@ -939,63 +1021,80 @@ var ServiceLocator = class {
939
1021
  }
940
1022
  invalidate(service, round = 1) {
941
1023
  this.logger?.log(
942
- `[ServiceLocator]#invalidate(): Starting Invalidating process of ${service}`
1024
+ `[ServiceLocator] Starting invalidation process for ${service}`
943
1025
  );
944
1026
  const toInvalidate = this.manager.filter(
945
1027
  (holder) => holder.name === service || holder.deps.has(service)
946
1028
  );
947
1029
  const promises = [];
948
1030
  for (const [key, holder] of toInvalidate.entries()) {
949
- if (holder.status === "destroying" /* Destroying */) {
950
- this.logger?.trace(
951
- `[ServiceLocator]#invalidate(): ${key} is already being destroyed`
952
- );
953
- promises.push(holder.destroyPromise);
954
- continue;
955
- }
956
- if (holder.status === "creating" /* Creating */) {
1031
+ promises.push(this.invalidateHolder(key, holder, round));
1032
+ }
1033
+ return Promise.all(promises);
1034
+ }
1035
+ /**
1036
+ * Invalidates a single holder based on its current status.
1037
+ */
1038
+ async invalidateHolder(key, holder, round) {
1039
+ switch (holder.status) {
1040
+ case "destroying" /* Destroying */:
1041
+ this.logger?.trace(`[ServiceLocator] ${key} is already being destroyed`);
1042
+ await holder.destroyPromise;
1043
+ break;
1044
+ case "creating" /* Creating */:
957
1045
  this.logger?.trace(
958
- `[ServiceLocator]#invalidate(): ${key} is being created, waiting for creation to finish`
959
- );
960
- promises.push(
961
- holder.creationPromise?.then(() => {
962
- if (round > 3) {
963
- this.logger?.error(
964
- `[ServiceLocator]#invalidate(): ${key} creation is triggering a new invalidation round, but it is still not created`
965
- );
966
- return;
967
- }
968
- return this.invalidate(key, round + 1);
969
- })
1046
+ `[ServiceLocator] ${key} is being created, waiting...`
970
1047
  );
971
- continue;
972
- }
973
- holder.status = "destroying" /* Destroying */;
974
- this.logger?.log(
975
- `[ServiceLocator]#invalidate(): Invalidating ${key} and notifying listeners`
976
- );
977
- holder.destroyPromise = Promise.all(
978
- holder.destroyListeners.map((listener) => listener())
979
- ).then(async () => {
980
- this.manager.delete(key);
981
- await this.emitInstanceEvent(key, "destroy");
982
- });
983
- promises.push(holder.destroyPromise);
1048
+ await holder.creationPromise;
1049
+ if (round > 3) {
1050
+ this.logger?.error(
1051
+ `[ServiceLocator] ${key} creation triggered too many invalidation rounds`
1052
+ );
1053
+ return;
1054
+ }
1055
+ await this.invalidate(key, round + 1);
1056
+ break;
1057
+ default:
1058
+ await this.destroyHolder(key, holder);
1059
+ break;
984
1060
  }
985
- return Promise.all(promises);
1061
+ }
1062
+ /**
1063
+ * Destroys a holder and cleans up its resources.
1064
+ */
1065
+ async destroyHolder(key, holder) {
1066
+ holder.status = "destroying" /* Destroying */;
1067
+ this.logger?.log(
1068
+ `[ServiceLocator] Invalidating ${key} and notifying listeners`
1069
+ );
1070
+ holder.destroyPromise = Promise.all(
1071
+ holder.destroyListeners.map((listener) => listener())
1072
+ ).then(async () => {
1073
+ this.manager.delete(key);
1074
+ await this.emitInstanceEvent(key, "destroy");
1075
+ });
1076
+ await holder.destroyPromise;
986
1077
  }
987
1078
  async ready() {
988
- return Promise.all(
989
- Array.from(this.manager.filter(() => true)).map(([, holder]) => {
990
- if (holder.status === "creating" /* Creating */) {
991
- return holder.creationPromise?.then(() => null);
992
- }
993
- if (holder.status === "destroying" /* Destroying */) {
994
- return holder.destroyPromise.then(() => null);
995
- }
996
- return Promise.resolve(null);
997
- })
998
- ).then(() => null);
1079
+ const holders = Array.from(this.manager.filter(() => true)).map(
1080
+ ([, holder]) => holder
1081
+ );
1082
+ await Promise.all(
1083
+ holders.map((holder) => this.waitForHolderToSettle(holder))
1084
+ );
1085
+ }
1086
+ /**
1087
+ * Waits for a holder to settle (either created, destroyed, or error state).
1088
+ */
1089
+ async waitForHolderToSettle(holder) {
1090
+ switch (holder.status) {
1091
+ case "creating" /* Creating */:
1092
+ await holder.creationPromise;
1093
+ break;
1094
+ case "destroying" /* Destroying */:
1095
+ await holder.destroyPromise;
1096
+ break;
1097
+ }
999
1098
  }
1000
1099
  // ============================================================================
1001
1100
  // REQUEST CONTEXT MANAGEMENT
@@ -1127,86 +1226,106 @@ var ServiceLocator = class {
1127
1226
  * Gets an instance by its instance name, handling all the logic after instance name creation.
1128
1227
  */
1129
1228
  async retrieveOrCreateInstanceByInstanceName(instanceName, realToken, realArgs) {
1130
- if (this.registry.has(realToken)) {
1131
- const record = this.registry.get(realToken);
1132
- if (record.scope === "Request" /* Request */) {
1133
- if (!this.currentRequestContext) {
1134
- this.logger?.log(
1135
- `[ServiceLocator]#retrieveOrCreateInstanceByInstanceName() No current request context available for request-scoped service ${instanceName}`
1136
- );
1137
- return [new UnknownError("InstanceNotFound" /* InstanceNotFound */)];
1138
- }
1139
- const requestHolder = this.currentRequestContext.getHolder(instanceName);
1140
- if (requestHolder) {
1141
- if (requestHolder.status === "creating" /* Creating */) {
1142
- await requestHolder.creationPromise;
1143
- return this.retrieveOrCreateInstanceByInstanceName(
1144
- instanceName,
1145
- realToken,
1146
- realArgs
1147
- );
1148
- } else if (requestHolder.status === "destroying" /* Destroying */) {
1149
- return [new UnknownError("InstanceDestroying" /* InstanceDestroying */)];
1150
- }
1151
- return [void 0, requestHolder];
1152
- }
1153
- }
1229
+ const existingHolder = await this.tryGetExistingInstance(
1230
+ instanceName,
1231
+ realToken
1232
+ );
1233
+ if (existingHolder) {
1234
+ return existingHolder;
1154
1235
  }
1236
+ const result = await this.createNewInstance(
1237
+ instanceName,
1238
+ realToken,
1239
+ realArgs
1240
+ );
1241
+ if (result[0]) {
1242
+ return [result[0]];
1243
+ }
1244
+ const [, holder] = result;
1245
+ return this.waitForInstanceReady(holder);
1246
+ }
1247
+ /**
1248
+ * Attempts to retrieve an existing instance, handling request-scoped and singleton instances.
1249
+ * Returns null if no instance exists and a new one should be created.
1250
+ */
1251
+ async tryGetExistingInstance(instanceName, realToken) {
1252
+ const requestResult = await this.tryGetRequestScopedInstance(
1253
+ instanceName,
1254
+ realToken
1255
+ );
1256
+ if (requestResult) {
1257
+ return requestResult;
1258
+ }
1259
+ return this.tryGetSingletonInstance(instanceName);
1260
+ }
1261
+ /**
1262
+ * Attempts to get a request-scoped instance if applicable.
1263
+ */
1264
+ async tryGetRequestScopedInstance(instanceName, realToken) {
1265
+ if (!this.registry.has(realToken)) {
1266
+ return null;
1267
+ }
1268
+ const record = this.registry.get(realToken);
1269
+ if (record.scope !== "Request" /* Request */) {
1270
+ return null;
1271
+ }
1272
+ if (!this.currentRequestContext) {
1273
+ this.logger?.log(
1274
+ `[ServiceLocator] No current request context available for request-scoped service ${instanceName}`
1275
+ );
1276
+ return [new UnknownError("InstanceNotFound" /* InstanceNotFound */)];
1277
+ }
1278
+ const requestHolder = this.currentRequestContext.get(instanceName);
1279
+ if (!requestHolder) {
1280
+ return null;
1281
+ }
1282
+ return this.waitForInstanceReady(requestHolder);
1283
+ }
1284
+ /**
1285
+ * Attempts to get a singleton instance from the manager.
1286
+ */
1287
+ async tryGetSingletonInstance(instanceName) {
1155
1288
  const [error, holder] = this.manager.get(instanceName);
1156
1289
  if (!error) {
1157
- if (holder.status === "creating" /* Creating */) {
1158
- await holder.creationPromise;
1159
- return this.retrieveOrCreateInstanceByInstanceName(
1160
- instanceName,
1161
- realToken,
1162
- realArgs
1163
- );
1164
- } else if (holder.status === "destroying" /* Destroying */) {
1165
- return [new UnknownError("InstanceDestroying" /* InstanceDestroying */)];
1166
- }
1167
- return [void 0, holder];
1290
+ return this.waitForInstanceReady(holder);
1168
1291
  }
1169
1292
  switch (error.code) {
1170
1293
  case "InstanceDestroying" /* InstanceDestroying */:
1171
1294
  this.logger?.log(
1172
- `[ServiceLocator]#retrieveOrCreateInstanceByInstanceName() TTL expired for ${holder?.name}`
1295
+ `[ServiceLocator] Instance ${instanceName} is being destroyed, waiting...`
1173
1296
  );
1174
1297
  await holder?.destroyPromise;
1175
- return this.retrieveOrCreateInstanceByInstanceName(
1176
- instanceName,
1177
- realToken,
1178
- realArgs
1179
- );
1298
+ return this.tryGetSingletonInstance(instanceName);
1180
1299
  case "InstanceExpired" /* InstanceExpired */:
1181
1300
  this.logger?.log(
1182
- `[ServiceLocator]#retrieveOrCreateInstanceByInstanceName() TTL expired for ${holder?.name}`
1301
+ `[ServiceLocator] Instance ${instanceName} expired, invalidating...`
1183
1302
  );
1184
1303
  await this.invalidate(instanceName);
1185
- return this.retrieveOrCreateInstanceByInstanceName(
1186
- instanceName,
1187
- realToken,
1188
- realArgs
1189
- );
1304
+ return this.tryGetSingletonInstance(instanceName);
1190
1305
  case "InstanceNotFound" /* InstanceNotFound */:
1191
- break;
1306
+ return null;
1307
+ // Instance doesn't exist, should create new one
1192
1308
  default:
1193
1309
  return [error];
1194
1310
  }
1195
- const result = await this.createNewInstance(
1196
- instanceName,
1197
- realToken,
1198
- realArgs
1199
- );
1200
- if (result[0]) {
1201
- return [result[0]];
1202
- }
1203
- if (result[1].status === "creating" /* Creating */) {
1204
- await result[1].creationPromise;
1205
- }
1206
- if (result[1].status === "error" /* Error */) {
1207
- return [result[1].instance];
1311
+ }
1312
+ /**
1313
+ * Waits for an instance holder to be ready and returns the appropriate result.
1314
+ */
1315
+ async waitForInstanceReady(holder) {
1316
+ switch (holder.status) {
1317
+ case "creating" /* Creating */:
1318
+ await holder.creationPromise;
1319
+ return this.waitForInstanceReady(holder);
1320
+ case "destroying" /* Destroying */:
1321
+ return [new UnknownError("InstanceDestroying" /* InstanceDestroying */)];
1322
+ case "error" /* Error */:
1323
+ return [holder.instance];
1324
+ case "created" /* Created */:
1325
+ return [void 0, holder];
1326
+ default:
1327
+ return [new UnknownError("InstanceNotFound" /* InstanceNotFound */)];
1208
1328
  }
1209
- return [void 0, result[1]];
1210
1329
  }
1211
1330
  /**
1212
1331
  * Emits events to listeners for instance lifecycle events.
@@ -1254,65 +1373,137 @@ var ServiceLocator = class {
1254
1373
  Infinity
1255
1374
  );
1256
1375
  this.serviceInstantiator.instantiateService(ctx, record, args).then(async ([error, instance]) => {
1257
- holder.destroyListeners = ctx.getDestroyListeners();
1258
- holder.creationPromise = null;
1259
- if (error) {
1260
- this.logger?.error(
1261
- `[ServiceLocator]#instantiateServiceFromRegistry(): Error creating instance for ${instanceName}`,
1262
- error
1263
- );
1264
- holder.status = "error" /* Error */;
1265
- holder.instance = error;
1266
- if (scope === "Singleton" /* Singleton */) {
1267
- setTimeout(() => this.invalidate(instanceName), 10);
1268
- }
1269
- deferred.reject(error);
1270
- } else {
1271
- holder.instance = instance;
1272
- holder.status = "created" /* Created */;
1273
- if (ctx.deps.size > 0) {
1274
- ctx.deps.forEach((dependency) => {
1275
- holder.destroyListeners.push(
1276
- this.eventBus.on(
1277
- dependency,
1278
- "destroy",
1279
- () => this.invalidate(instanceName)
1280
- )
1281
- );
1282
- });
1283
- }
1284
- await this.emitInstanceEvent(instanceName);
1285
- deferred.resolve([void 0, instance]);
1286
- }
1287
- }).catch((error) => {
1288
- this.logger?.error(
1289
- `[ServiceLocator]#instantiateServiceFromRegistry(): Unexpected error creating instance for ${instanceName}`,
1376
+ await this.handleInstantiationResult(
1377
+ instanceName,
1378
+ holder,
1379
+ ctx,
1380
+ deferred,
1381
+ scope,
1382
+ error,
1383
+ instance
1384
+ );
1385
+ }).catch(async (error) => {
1386
+ await this.handleInstantiationError(
1387
+ instanceName,
1388
+ holder,
1389
+ deferred,
1390
+ scope,
1290
1391
  error
1291
1392
  );
1292
- holder.status = "error" /* Error */;
1293
- holder.instance = error;
1294
- holder.creationPromise = null;
1295
- if (scope === "Singleton" /* Singleton */) {
1296
- setTimeout(() => this.invalidate(instanceName), 10);
1297
- }
1298
- deferred.reject(error);
1299
1393
  });
1300
- if (scope === "Singleton" /* Singleton */) {
1301
- this.logger?.debug(
1302
- `[ServiceLocator]#instantiateServiceFromRegistry(): Setting instance for ${instanceName}`
1303
- );
1304
- this.manager.set(instanceName, holder);
1305
- } else if (scope === "Request" /* Request */ && this.currentRequestContext) {
1306
- this.logger?.debug(
1307
- `[ServiceLocator]#instantiateServiceFromRegistry(): Setting request-scoped instance for ${instanceName}`
1394
+ this.storeInstanceByScope(scope, instanceName, holder);
1395
+ return [void 0, holder];
1396
+ }
1397
+ /**
1398
+ * Handles the result of service instantiation.
1399
+ */
1400
+ async handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance) {
1401
+ holder.destroyListeners = ctx.getDestroyListeners();
1402
+ holder.creationPromise = null;
1403
+ if (error) {
1404
+ await this.handleInstantiationError(
1405
+ instanceName,
1406
+ holder,
1407
+ deferred,
1408
+ scope,
1409
+ error
1308
1410
  );
1309
- this.currentRequestContext.addInstance(
1411
+ } else {
1412
+ await this.handleInstantiationSuccess(
1310
1413
  instanceName,
1311
- holder.instance,
1312
- holder
1414
+ holder,
1415
+ ctx,
1416
+ deferred,
1417
+ instance
1313
1418
  );
1314
1419
  }
1315
- return [void 0, holder];
1420
+ }
1421
+ /**
1422
+ * Handles successful service instantiation.
1423
+ */
1424
+ async handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance) {
1425
+ holder.instance = instance;
1426
+ holder.status = "created" /* Created */;
1427
+ if (ctx.deps.size > 0) {
1428
+ ctx.deps.forEach((dependency) => {
1429
+ holder.destroyListeners.push(
1430
+ this.eventBus.on(
1431
+ dependency,
1432
+ "destroy",
1433
+ () => this.invalidate(instanceName)
1434
+ )
1435
+ );
1436
+ });
1437
+ }
1438
+ await this.emitInstanceEvent(instanceName);
1439
+ deferred.resolve([void 0, instance]);
1440
+ }
1441
+ /**
1442
+ * Handles service instantiation errors.
1443
+ */
1444
+ async handleInstantiationError(instanceName, holder, deferred, scope, error) {
1445
+ this.logger?.error(
1446
+ `[ServiceLocator] Error creating instance for ${instanceName}`,
1447
+ error
1448
+ );
1449
+ holder.status = "error" /* Error */;
1450
+ holder.instance = error;
1451
+ holder.creationPromise = null;
1452
+ if (scope === "Singleton" /* Singleton */) {
1453
+ setTimeout(() => this.invalidate(instanceName), 10);
1454
+ }
1455
+ deferred.reject(error);
1456
+ }
1457
+ /**
1458
+ * Stores an instance holder based on its scope.
1459
+ */
1460
+ storeInstanceByScope(scope, instanceName, holder) {
1461
+ switch (scope) {
1462
+ case "Singleton" /* Singleton */:
1463
+ this.logger?.debug(
1464
+ `[ServiceLocator] Setting singleton instance for ${instanceName}`
1465
+ );
1466
+ this.manager.set(instanceName, holder);
1467
+ break;
1468
+ case "Request" /* Request */:
1469
+ if (this.currentRequestContext) {
1470
+ this.logger?.debug(
1471
+ `[ServiceLocator] Setting request-scoped instance for ${instanceName}`
1472
+ );
1473
+ this.currentRequestContext.addInstance(
1474
+ instanceName,
1475
+ holder.instance,
1476
+ holder
1477
+ );
1478
+ }
1479
+ break;
1480
+ }
1481
+ }
1482
+ /**
1483
+ * Tries to get a pre-prepared instance from request contexts.
1484
+ */
1485
+ tryGetPrePreparedInstance(instanceName, contextHolder, deps) {
1486
+ if (contextHolder && contextHolder.priority > 0) {
1487
+ const prePreparedInstance = contextHolder.get(instanceName)?.instance;
1488
+ if (prePreparedInstance !== void 0) {
1489
+ this.logger?.debug(
1490
+ `[ServiceLocator] Using pre-prepared instance ${instanceName} from request context ${contextHolder.requestId}`
1491
+ );
1492
+ deps.add(instanceName);
1493
+ return prePreparedInstance;
1494
+ }
1495
+ }
1496
+ if (this.currentRequestContext && this.currentRequestContext !== contextHolder) {
1497
+ const prePreparedInstance = this.currentRequestContext.get(instanceName)?.instance;
1498
+ if (prePreparedInstance !== void 0) {
1499
+ this.logger?.debug(
1500
+ `[ServiceLocator] Using pre-prepared instance ${instanceName} from current request context ${this.currentRequestContext.requestId}`
1501
+ );
1502
+ deps.add(instanceName);
1503
+ return prePreparedInstance;
1504
+ }
1505
+ }
1506
+ return void 0;
1316
1507
  }
1317
1508
  /**
1318
1509
  * Creates a factory context for dependency injection during service instantiation.
@@ -1331,33 +1522,20 @@ var ServiceLocator = class {
1331
1522
  return {
1332
1523
  // @ts-expect-error This is correct type
1333
1524
  async inject(token, args) {
1334
- if (contextHolder && contextHolder.priority > 0) {
1335
- const instanceName = self.generateInstanceName(token, args);
1336
- const prePreparedInstance = contextHolder.getInstance(instanceName);
1337
- if (prePreparedInstance !== void 0) {
1338
- self.logger?.debug(
1339
- `[ServiceLocator] Using pre-prepared instance ${instanceName} from request context ${contextHolder.requestId}`
1340
- );
1341
- deps.add(instanceName);
1342
- return prePreparedInstance;
1343
- }
1344
- }
1345
- if (self.currentRequestContext && self.currentRequestContext !== contextHolder) {
1346
- const instanceName = self.generateInstanceName(token, args);
1347
- const prePreparedInstance = self.currentRequestContext.getInstance(instanceName);
1348
- if (prePreparedInstance !== void 0) {
1349
- self.logger?.debug(
1350
- `[ServiceLocator] Using pre-prepared instance ${instanceName} from current request context ${self.currentRequestContext.requestId}`
1351
- );
1352
- deps.add(instanceName);
1353
- return prePreparedInstance;
1354
- }
1525
+ const instanceName = self.generateInstanceName(token, args);
1526
+ const prePreparedInstance = self.tryGetPrePreparedInstance(
1527
+ instanceName,
1528
+ contextHolder,
1529
+ deps
1530
+ );
1531
+ if (prePreparedInstance !== void 0) {
1532
+ return prePreparedInstance;
1355
1533
  }
1356
1534
  const [error, instance] = await self.getInstance(
1357
1535
  token,
1358
1536
  args,
1359
- ({ instanceName }) => {
1360
- deps.add(instanceName);
1537
+ ({ instanceName: instanceName2 }) => {
1538
+ deps.add(instanceName2);
1361
1539
  }
1362
1540
  );
1363
1541
  if (error) {
@@ -1375,16 +1553,23 @@ var ServiceLocator = class {
1375
1553
  * Generates a unique instance name based on token and arguments.
1376
1554
  */
1377
1555
  generateInstanceName(token, args) {
1378
- const formattedArgs = args ? ":" + Object.entries(args ?? {}).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => {
1379
- if (typeof value === "function") {
1380
- return `${key}=fn_${value.name}(${value.length})`;
1381
- }
1382
- if (typeof value === "symbol") {
1383
- return `${key}=${value.toString()}`;
1384
- }
1385
- return `${key}=${JSON.stringify(value).slice(0, 40)}`;
1386
- }).join(",").replaceAll(/"/g, "").replaceAll(/:/g, "=") : "";
1387
- return `${token.toString()}${formattedArgs}`;
1556
+ if (!args) {
1557
+ return token.toString();
1558
+ }
1559
+ const formattedArgs = Object.entries(args).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => `${key}=${this.formatArgValue(value)}`).join(",");
1560
+ return `${token.toString()}:${formattedArgs.replaceAll(/"/g, "").replaceAll(/:/g, "=")}`;
1561
+ }
1562
+ /**
1563
+ * Formats a single argument value for instance name generation.
1564
+ */
1565
+ formatArgValue(value) {
1566
+ if (typeof value === "function") {
1567
+ return `fn_${value.name}(${value.length})`;
1568
+ }
1569
+ if (typeof value === "symbol") {
1570
+ return value.toString();
1571
+ }
1572
+ return JSON.stringify(value).slice(0, 40);
1388
1573
  }
1389
1574
  };
1390
1575
 
@@ -1475,6 +1660,6 @@ _Container = __decorateElement(_init2, 0, "Container", _Container_decorators, _C
1475
1660
  __runInitializers(_init2, 1, _Container);
1476
1661
  var Container = _Container;
1477
1662
 
1478
- export { BoundInjectionToken, Container, DefaultRequestContextHolder, Deferred, ErrorsEnum, EventEmitter, Factory, FactoryInjectionToken, FactoryNotFound, FactoryTokenNotResolved, Injectable, InjectableScope, InjectableTokenMeta, InjectableType, InjectionToken, InstanceDestroying, InstanceExpired, InstanceNotFound, Registry, ServiceInstantiator, ServiceLocator, ServiceLocatorEventBus, ServiceLocatorInstanceHolderStatus, ServiceLocatorManager, UnknownError, asyncInject, createDeferred, createRequestContextHolder, defaultInjectors, getInjectableToken, getInjectors, globalRegistry, inject, provideFactoryContext, wrapSyncInit };
1663
+ export { BaseInstanceHolderManager, BoundInjectionToken, Container, DefaultRequestContextHolder, Deferred, ErrorsEnum, EventEmitter, Factory, FactoryInjectionToken, FactoryNotFound, FactoryTokenNotResolved, Injectable, InjectableScope, InjectableTokenMeta, InjectableType, InjectionToken, InstanceDestroying, InstanceExpired, InstanceNotFound, Registry, ServiceInstantiator, ServiceLocator, ServiceLocatorEventBus, ServiceLocatorInstanceHolderStatus, ServiceLocatorManager, UnknownError, asyncInject, createDeferred, createRequestContextHolder, defaultInjectors, getInjectableToken, getInjectors, globalRegistry, inject, provideFactoryContext, wrapSyncInit };
1479
1664
  //# sourceMappingURL=index.mjs.map
1480
1665
  //# sourceMappingURL=index.mjs.map