@navios/di 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) 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 +180 -35
  74. package/lib/_tsup-dts-rollup.d.ts +180 -35
  75. package/lib/index.d.mts +1 -0
  76. package/lib/index.d.ts +1 -0
  77. package/lib/index.js +485 -279
  78. package/lib/index.js.map +1 -1
  79. package/lib/index.mjs +485 -280
  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 +167 -4
  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/index.mts +1 -0
  94. package/src/request-context-holder.mts +85 -27
  95. package/src/service-locator-manager.mts +12 -70
  96. 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 */ })];
@@ -618,8 +744,9 @@ var ServiceInstantiator = class {
618
744
  };
619
745
 
620
746
  // src/request-context-holder.mts
621
- var DefaultRequestContextHolder = class {
747
+ var DefaultRequestContextHolder = class extends BaseInstanceHolderManager {
622
748
  constructor(requestId, priority = 100, initialMetadata) {
749
+ super(null);
623
750
  this.requestId = requestId;
624
751
  this.priority = priority;
625
752
  if (initialMetadata) {
@@ -628,26 +755,53 @@ var DefaultRequestContextHolder = class {
628
755
  });
629
756
  }
630
757
  }
631
- instances = /* @__PURE__ */ new Map();
632
- holders = /* @__PURE__ */ new Map();
633
758
  metadata = /* @__PURE__ */ new Map();
634
759
  createdAt = Date.now();
635
- addInstance(instanceName, instance, holder) {
636
- this.instances.set(instanceName, instance);
637
- this.holders.set(instanceName, holder);
760
+ /**
761
+ * Public getter for holders to maintain interface compatibility.
762
+ */
763
+ get holders() {
764
+ return this._holders;
638
765
  }
639
- getInstance(instanceName) {
640
- return this.instances.get(instanceName);
766
+ /**
767
+ * Gets a holder by name. For RequestContextHolder, this is a simple lookup.
768
+ */
769
+ get(name) {
770
+ return this._holders.get(name);
641
771
  }
642
- getHolder(instanceName) {
643
- return this.holders.get(instanceName);
772
+ /**
773
+ * Sets a holder by name.
774
+ */
775
+ set(name, holder) {
776
+ this._holders.set(name, holder);
644
777
  }
645
- hasInstance(instanceName) {
646
- return this.instances.has(instanceName);
778
+ /**
779
+ * Checks if a holder exists by name.
780
+ */
781
+ has(name) {
782
+ return this._holders.has(name);
783
+ }
784
+ addInstance(instanceName, instance, holder) {
785
+ if (instanceName instanceof InjectionToken) {
786
+ const name = instanceName.toString();
787
+ const createdHolder = this.createCreatedHolder(
788
+ name,
789
+ instance,
790
+ "Class" /* Class */,
791
+ "Singleton" /* Singleton */,
792
+ /* @__PURE__ */ new Set(),
793
+ Infinity
794
+ );
795
+ this._holders.set(name, createdHolder);
796
+ } else {
797
+ if (!holder) {
798
+ throw new Error("Holder is required when adding an instance by name");
799
+ }
800
+ this._holders.set(instanceName, holder);
801
+ }
647
802
  }
648
803
  clear() {
649
- this.instances.clear();
650
- this.holders.clear();
804
+ super.clear();
651
805
  this.metadata.clear();
652
806
  }
653
807
  getMetadata(key) {
@@ -712,23 +866,13 @@ var ServiceLocatorEventBus = class {
712
866
  }
713
867
  };
714
868
 
715
- // src/service-locator-instance-holder.mts
716
- var ServiceLocatorInstanceHolderStatus = /* @__PURE__ */ ((ServiceLocatorInstanceHolderStatus2) => {
717
- ServiceLocatorInstanceHolderStatus2["Created"] = "created";
718
- ServiceLocatorInstanceHolderStatus2["Creating"] = "creating";
719
- ServiceLocatorInstanceHolderStatus2["Destroying"] = "destroying";
720
- ServiceLocatorInstanceHolderStatus2["Error"] = "error";
721
- return ServiceLocatorInstanceHolderStatus2;
722
- })(ServiceLocatorInstanceHolderStatus || {});
723
-
724
869
  // src/service-locator-manager.mts
725
- var ServiceLocatorManager = class {
870
+ var ServiceLocatorManager = class extends BaseInstanceHolderManager {
726
871
  constructor(logger = null) {
727
- this.logger = logger;
872
+ super(logger);
728
873
  }
729
- instancesHolders = /* @__PURE__ */ new Map();
730
874
  get(name) {
731
- const holder = this.instancesHolders.get(name);
875
+ const holder = this._holders.get(name);
732
876
  if (holder) {
733
877
  if (holder.ttl !== Infinity) {
734
878
  const now = Date.now();
@@ -758,7 +902,7 @@ var ServiceLocatorManager = class {
758
902
  }
759
903
  }
760
904
  set(name, holder) {
761
- this.instancesHolders.set(name, holder);
905
+ this._holders.set(name, holder);
762
906
  }
763
907
  has(name) {
764
908
  const [error, holder] = this.get(name);
@@ -772,43 +916,8 @@ var ServiceLocatorManager = class {
772
916
  }
773
917
  return [void 0, !!holder];
774
918
  }
775
- delete(name) {
776
- return this.instancesHolders.delete(name);
777
- }
778
- filter(predicate) {
779
- return new Map(
780
- [...this.instancesHolders].filter(
781
- ([key, value]) => predicate(value, key)
782
- )
783
- );
784
- }
785
- /**
786
- * Creates a new holder with Creating status and a deferred creation promise.
787
- * This is useful for creating placeholder holders that can be fulfilled later.
788
- * @param name The name of the instance
789
- * @param type The injectable type
790
- * @param scope The injectable scope
791
- * @param deps Optional set of dependencies
792
- * @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
793
- * @returns A tuple containing the deferred promise and the holder
794
- */
795
- createCreatingHolder(name, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
796
- const deferred = createDeferred();
797
- const holder = {
798
- status: "creating" /* Creating */,
799
- name,
800
- instance: null,
801
- creationPromise: deferred.promise,
802
- destroyPromise: null,
803
- type,
804
- scope,
805
- deps,
806
- destroyListeners: [],
807
- createdAt: Date.now(),
808
- ttl
809
- };
810
- return [deferred, holder];
811
- }
919
+ // delete and filter methods are inherited from BaseInstanceHolderManager
920
+ // createCreatingHolder method is inherited from BaseInstanceHolderManager
812
921
  /**
813
922
  * Creates a new holder with Created status and an actual instance.
814
923
  * This is useful for creating holders that already have their instance ready.
@@ -821,20 +930,15 @@ var ServiceLocatorManager = class {
821
930
  * @returns The created holder
822
931
  */
823
932
  storeCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set(), ttl = Infinity) {
824
- const holder = {
825
- status: "created" /* Created */,
933
+ const holder = this.createCreatedHolder(
826
934
  name,
827
935
  instance,
828
- creationPromise: null,
829
- destroyPromise: null,
830
936
  type,
831
937
  scope,
832
938
  deps,
833
- destroyListeners: [],
834
- createdAt: Date.now(),
835
939
  ttl
836
- };
837
- this.instancesHolders.set(name, holder);
940
+ );
941
+ this._holders.set(name, holder);
838
942
  return holder;
839
943
  }
840
944
  };
@@ -879,9 +983,7 @@ var ServiceLocator = class {
879
983
  return [err];
880
984
  }
881
985
  const { instanceName, validatedArgs, actualToken, realToken } = data;
882
- if (onPrepare) {
883
- onPrepare({ instanceName, actualToken, validatedArgs });
884
- }
986
+ onPrepare?.({ instanceName, actualToken, validatedArgs });
885
987
  const [error, holder] = await this.retrieveOrCreateInstanceByInstanceName(
886
988
  instanceName,
887
989
  realToken,
@@ -906,7 +1008,7 @@ var ServiceLocator = class {
906
1008
  }
907
1009
  const instanceName = this.generateInstanceName(actualToken, validatedArgs);
908
1010
  if (this.currentRequestContext) {
909
- const requestHolder = this.currentRequestContext.getHolder(instanceName);
1011
+ const requestHolder = this.currentRequestContext.get(instanceName);
910
1012
  if (requestHolder) {
911
1013
  return requestHolder.instance;
912
1014
  }
@@ -919,63 +1021,80 @@ var ServiceLocator = class {
919
1021
  }
920
1022
  invalidate(service, round = 1) {
921
1023
  this.logger?.log(
922
- `[ServiceLocator]#invalidate(): Starting Invalidating process of ${service}`
1024
+ `[ServiceLocator] Starting invalidation process for ${service}`
923
1025
  );
924
1026
  const toInvalidate = this.manager.filter(
925
1027
  (holder) => holder.name === service || holder.deps.has(service)
926
1028
  );
927
1029
  const promises = [];
928
1030
  for (const [key, holder] of toInvalidate.entries()) {
929
- if (holder.status === "destroying" /* Destroying */) {
930
- this.logger?.trace(
931
- `[ServiceLocator]#invalidate(): ${key} is already being destroyed`
932
- );
933
- promises.push(holder.destroyPromise);
934
- continue;
935
- }
936
- 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 */:
937
1045
  this.logger?.trace(
938
- `[ServiceLocator]#invalidate(): ${key} is being created, waiting for creation to finish`
1046
+ `[ServiceLocator] ${key} is being created, waiting...`
939
1047
  );
940
- promises.push(
941
- holder.creationPromise?.then(() => {
942
- if (round > 3) {
943
- this.logger?.error(
944
- `[ServiceLocator]#invalidate(): ${key} creation is triggering a new invalidation round, but it is still not created`
945
- );
946
- return;
947
- }
948
- return this.invalidate(key, round + 1);
949
- })
950
- );
951
- continue;
952
- }
953
- holder.status = "destroying" /* Destroying */;
954
- this.logger?.log(
955
- `[ServiceLocator]#invalidate(): Invalidating ${key} and notifying listeners`
956
- );
957
- holder.destroyPromise = Promise.all(
958
- holder.destroyListeners.map((listener) => listener())
959
- ).then(async () => {
960
- this.manager.delete(key);
961
- await this.emitInstanceEvent(key, "destroy");
962
- });
963
- 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;
964
1060
  }
965
- 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;
966
1077
  }
967
1078
  async ready() {
968
- return Promise.all(
969
- Array.from(this.manager.filter(() => true)).map(([, holder]) => {
970
- if (holder.status === "creating" /* Creating */) {
971
- return holder.creationPromise?.then(() => null);
972
- }
973
- if (holder.status === "destroying" /* Destroying */) {
974
- return holder.destroyPromise.then(() => null);
975
- }
976
- return Promise.resolve(null);
977
- })
978
- ).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
+ }
979
1098
  }
980
1099
  // ============================================================================
981
1100
  // REQUEST CONTEXT MANAGEMENT
@@ -1107,86 +1226,106 @@ var ServiceLocator = class {
1107
1226
  * Gets an instance by its instance name, handling all the logic after instance name creation.
1108
1227
  */
1109
1228
  async retrieveOrCreateInstanceByInstanceName(instanceName, realToken, realArgs) {
1110
- if (this.registry.has(realToken)) {
1111
- const record = this.registry.get(realToken);
1112
- if (record.scope === "Request" /* Request */) {
1113
- if (!this.currentRequestContext) {
1114
- this.logger?.log(
1115
- `[ServiceLocator]#retrieveOrCreateInstanceByInstanceName() No current request context available for request-scoped service ${instanceName}`
1116
- );
1117
- return [new UnknownError("InstanceNotFound" /* InstanceNotFound */)];
1118
- }
1119
- const requestHolder = this.currentRequestContext.getHolder(instanceName);
1120
- if (requestHolder) {
1121
- if (requestHolder.status === "creating" /* Creating */) {
1122
- await requestHolder.creationPromise;
1123
- return this.retrieveOrCreateInstanceByInstanceName(
1124
- instanceName,
1125
- realToken,
1126
- realArgs
1127
- );
1128
- } else if (requestHolder.status === "destroying" /* Destroying */) {
1129
- return [new UnknownError("InstanceDestroying" /* InstanceDestroying */)];
1130
- }
1131
- return [void 0, requestHolder];
1132
- }
1133
- }
1229
+ const existingHolder = await this.tryGetExistingInstance(
1230
+ instanceName,
1231
+ realToken
1232
+ );
1233
+ if (existingHolder) {
1234
+ return existingHolder;
1235
+ }
1236
+ const result = await this.createNewInstance(
1237
+ instanceName,
1238
+ realToken,
1239
+ realArgs
1240
+ );
1241
+ if (result[0]) {
1242
+ return [result[0]];
1134
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) {
1135
1288
  const [error, holder] = this.manager.get(instanceName);
1136
1289
  if (!error) {
1137
- if (holder.status === "creating" /* Creating */) {
1138
- await holder.creationPromise;
1139
- return this.retrieveOrCreateInstanceByInstanceName(
1140
- instanceName,
1141
- realToken,
1142
- realArgs
1143
- );
1144
- } else if (holder.status === "destroying" /* Destroying */) {
1145
- return [new UnknownError("InstanceDestroying" /* InstanceDestroying */)];
1146
- }
1147
- return [void 0, holder];
1290
+ return this.waitForInstanceReady(holder);
1148
1291
  }
1149
1292
  switch (error.code) {
1150
1293
  case "InstanceDestroying" /* InstanceDestroying */:
1151
1294
  this.logger?.log(
1152
- `[ServiceLocator]#retrieveOrCreateInstanceByInstanceName() TTL expired for ${holder?.name}`
1295
+ `[ServiceLocator] Instance ${instanceName} is being destroyed, waiting...`
1153
1296
  );
1154
1297
  await holder?.destroyPromise;
1155
- return this.retrieveOrCreateInstanceByInstanceName(
1156
- instanceName,
1157
- realToken,
1158
- realArgs
1159
- );
1298
+ return this.tryGetSingletonInstance(instanceName);
1160
1299
  case "InstanceExpired" /* InstanceExpired */:
1161
1300
  this.logger?.log(
1162
- `[ServiceLocator]#retrieveOrCreateInstanceByInstanceName() TTL expired for ${holder?.name}`
1301
+ `[ServiceLocator] Instance ${instanceName} expired, invalidating...`
1163
1302
  );
1164
1303
  await this.invalidate(instanceName);
1165
- return this.retrieveOrCreateInstanceByInstanceName(
1166
- instanceName,
1167
- realToken,
1168
- realArgs
1169
- );
1304
+ return this.tryGetSingletonInstance(instanceName);
1170
1305
  case "InstanceNotFound" /* InstanceNotFound */:
1171
- break;
1306
+ return null;
1307
+ // Instance doesn't exist, should create new one
1172
1308
  default:
1173
1309
  return [error];
1174
1310
  }
1175
- const result = await this.createNewInstance(
1176
- instanceName,
1177
- realToken,
1178
- realArgs
1179
- );
1180
- if (result[0]) {
1181
- return [result[0]];
1182
- }
1183
- if (result[1].status === "creating" /* Creating */) {
1184
- await result[1].creationPromise;
1185
- }
1186
- if (result[1].status === "error" /* Error */) {
1187
- 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 */)];
1188
1328
  }
1189
- return [void 0, result[1]];
1190
1329
  }
1191
1330
  /**
1192
1331
  * Emits events to listeners for instance lifecycle events.
@@ -1234,65 +1373,137 @@ var ServiceLocator = class {
1234
1373
  Infinity
1235
1374
  );
1236
1375
  this.serviceInstantiator.instantiateService(ctx, record, args).then(async ([error, instance]) => {
1237
- holder.destroyListeners = ctx.getDestroyListeners();
1238
- holder.creationPromise = null;
1239
- if (error) {
1240
- this.logger?.error(
1241
- `[ServiceLocator]#instantiateServiceFromRegistry(): Error creating instance for ${instanceName}`,
1242
- error
1243
- );
1244
- holder.status = "error" /* Error */;
1245
- holder.instance = error;
1246
- if (scope === "Singleton" /* Singleton */) {
1247
- setTimeout(() => this.invalidate(instanceName), 10);
1248
- }
1249
- deferred.reject(error);
1250
- } else {
1251
- holder.instance = instance;
1252
- holder.status = "created" /* Created */;
1253
- if (ctx.deps.size > 0) {
1254
- ctx.deps.forEach((dependency) => {
1255
- holder.destroyListeners.push(
1256
- this.eventBus.on(
1257
- dependency,
1258
- "destroy",
1259
- () => this.invalidate(instanceName)
1260
- )
1261
- );
1262
- });
1263
- }
1264
- await this.emitInstanceEvent(instanceName);
1265
- deferred.resolve([void 0, instance]);
1266
- }
1267
- }).catch((error) => {
1268
- this.logger?.error(
1269
- `[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,
1270
1391
  error
1271
1392
  );
1272
- holder.status = "error" /* Error */;
1273
- holder.instance = error;
1274
- holder.creationPromise = null;
1275
- if (scope === "Singleton" /* Singleton */) {
1276
- setTimeout(() => this.invalidate(instanceName), 10);
1277
- }
1278
- deferred.reject(error);
1279
1393
  });
1280
- if (scope === "Singleton" /* Singleton */) {
1281
- this.logger?.debug(
1282
- `[ServiceLocator]#instantiateServiceFromRegistry(): Setting instance for ${instanceName}`
1283
- );
1284
- this.manager.set(instanceName, holder);
1285
- } else if (scope === "Request" /* Request */ && this.currentRequestContext) {
1286
- this.logger?.debug(
1287
- `[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
1288
1410
  );
1289
- this.currentRequestContext.addInstance(
1411
+ } else {
1412
+ await this.handleInstantiationSuccess(
1290
1413
  instanceName,
1291
- holder.instance,
1292
- holder
1414
+ holder,
1415
+ ctx,
1416
+ deferred,
1417
+ instance
1293
1418
  );
1294
1419
  }
1295
- 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;
1296
1507
  }
1297
1508
  /**
1298
1509
  * Creates a factory context for dependency injection during service instantiation.
@@ -1311,33 +1522,20 @@ var ServiceLocator = class {
1311
1522
  return {
1312
1523
  // @ts-expect-error This is correct type
1313
1524
  async inject(token, args) {
1314
- if (contextHolder && contextHolder.priority > 0) {
1315
- const instanceName = self.generateInstanceName(token, args);
1316
- const prePreparedInstance = contextHolder.getInstance(instanceName);
1317
- if (prePreparedInstance !== void 0) {
1318
- self.logger?.debug(
1319
- `[ServiceLocator] Using pre-prepared instance ${instanceName} from request context ${contextHolder.requestId}`
1320
- );
1321
- deps.add(instanceName);
1322
- return prePreparedInstance;
1323
- }
1324
- }
1325
- if (self.currentRequestContext && self.currentRequestContext !== contextHolder) {
1326
- const instanceName = self.generateInstanceName(token, args);
1327
- const prePreparedInstance = self.currentRequestContext.getInstance(instanceName);
1328
- if (prePreparedInstance !== void 0) {
1329
- self.logger?.debug(
1330
- `[ServiceLocator] Using pre-prepared instance ${instanceName} from current request context ${self.currentRequestContext.requestId}`
1331
- );
1332
- deps.add(instanceName);
1333
- return prePreparedInstance;
1334
- }
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;
1335
1533
  }
1336
1534
  const [error, instance] = await self.getInstance(
1337
1535
  token,
1338
1536
  args,
1339
- ({ instanceName }) => {
1340
- deps.add(instanceName);
1537
+ ({ instanceName: instanceName2 }) => {
1538
+ deps.add(instanceName2);
1341
1539
  }
1342
1540
  );
1343
1541
  if (error) {
@@ -1355,16 +1553,23 @@ var ServiceLocator = class {
1355
1553
  * Generates a unique instance name based on token and arguments.
1356
1554
  */
1357
1555
  generateInstanceName(token, args) {
1358
- const formattedArgs = args ? ":" + Object.entries(args ?? {}).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => {
1359
- if (typeof value === "function") {
1360
- return `${key}=fn_${value.name}(${value.length})`;
1361
- }
1362
- if (typeof value === "symbol") {
1363
- return `${key}=${value.toString()}`;
1364
- }
1365
- return `${key}=${JSON.stringify(value).slice(0, 40)}`;
1366
- }).join(",").replaceAll(/"/g, "").replaceAll(/:/g, "=") : "";
1367
- 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);
1368
1573
  }
1369
1574
  };
1370
1575
 
@@ -1455,6 +1660,6 @@ _Container = __decorateElement(_init2, 0, "Container", _Container_decorators, _C
1455
1660
  __runInitializers(_init2, 1, _Container);
1456
1661
  var Container = _Container;
1457
1662
 
1458
- 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 };
1459
1664
  //# sourceMappingURL=index.mjs.map
1460
1665
  //# sourceMappingURL=index.mjs.map