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