@navios/di 0.7.0 → 0.8.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 (35) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/lib/browser/index.d.mts +62 -2
  3. package/lib/browser/index.d.mts.map +1 -1
  4. package/lib/browser/index.mjs +132 -15
  5. package/lib/browser/index.mjs.map +1 -1
  6. package/lib/{container-CXDYDJSM.d.mts → container-Bp1W-pWJ.d.mts} +63 -3
  7. package/lib/container-Bp1W-pWJ.d.mts.map +1 -0
  8. package/lib/{container-Bv6PZZLJ.mjs → container-DAKOvAgr.mjs} +137 -18
  9. package/lib/container-DAKOvAgr.mjs.map +1 -0
  10. package/lib/{container-BCv3XS6m.cjs → container-DENMeJ87.cjs} +137 -18
  11. package/lib/container-DENMeJ87.cjs.map +1 -0
  12. package/lib/{container-b6mDUdGq.d.cts → container-YPwvmlK2.d.cts} +63 -3
  13. package/lib/container-YPwvmlK2.d.cts.map +1 -0
  14. package/lib/index.cjs +1 -1
  15. package/lib/index.d.cts +1 -1
  16. package/lib/index.d.cts.map +1 -1
  17. package/lib/index.d.mts +1 -1
  18. package/lib/index.d.mts.map +1 -1
  19. package/lib/index.mjs +1 -1
  20. package/lib/testing/index.cjs +1 -1
  21. package/lib/testing/index.d.cts +1 -1
  22. package/lib/testing/index.d.mts +1 -1
  23. package/lib/testing/index.mjs +1 -1
  24. package/package.json +1 -1
  25. package/src/internal/context/request-context.mts +11 -0
  26. package/src/internal/core/instance-resolver.mts +11 -0
  27. package/src/internal/core/token-processor.mts +60 -1
  28. package/src/internal/holder/base-holder-manager.mts +106 -10
  29. package/src/internal/holder/request-storage.mts +7 -14
  30. package/src/internal/holder/singleton-storage.mts +3 -16
  31. package/src/token/registry.mts +21 -0
  32. package/lib/container-BCv3XS6m.cjs.map +0 -1
  33. package/lib/container-Bv6PZZLJ.mjs.map +0 -1
  34. package/lib/container-CXDYDJSM.d.mts.map +0 -1
  35. package/lib/container-b6mDUdGq.d.cts.map +0 -1
@@ -104,6 +104,7 @@ var FactoryInjectionToken = class {
104
104
  //#endregion
105
105
  //#region src/token/registry.mts
106
106
  var Registry = class {
107
+ parent;
107
108
  factories = /* @__PURE__ */ new Map();
108
109
  constructor(parent) {
109
110
  this.parent = parent;
@@ -132,6 +133,23 @@ var Registry = class {
132
133
  delete(token) {
133
134
  this.factories.delete(token.id);
134
135
  }
136
+ /**
137
+ * Updates the scope of an already registered factory.
138
+ * This is useful when you need to dynamically change a service's scope
139
+ * (e.g., when a singleton controller has request-scoped dependencies).
140
+ *
141
+ * @param token The injection token to update
142
+ * @param scope The new scope to set
143
+ * @returns true if the scope was updated, false if the token was not found
144
+ */ updateScope(token, scope) {
145
+ const factory = this.factories.get(token.id);
146
+ if (factory) {
147
+ factory.scope = scope;
148
+ return true;
149
+ }
150
+ if (this.parent) return this.parent.updateScope(token, scope);
151
+ return false;
152
+ }
135
153
  };
136
154
  const globalRegistry = new Registry();
137
155
 
@@ -491,9 +509,14 @@ let InstanceStatus = /* @__PURE__ */ function(InstanceStatus$1) {
491
509
  */ var BaseHolderManager = class BaseHolderManager {
492
510
  logger;
493
511
  _holders;
512
+ /**
513
+ * Reverse dependency index: maps a dependency name to the set of holder names that depend on it.
514
+ * This allows O(1) lookup of dependents instead of O(n) iteration.
515
+ */ _dependents;
494
516
  constructor(logger = null) {
495
517
  this.logger = logger;
496
518
  this._holders = /* @__PURE__ */ new Map();
519
+ this._dependents = /* @__PURE__ */ new Map();
497
520
  }
498
521
  /**
499
522
  * Protected getter for accessing the holders map from subclasses.
@@ -501,23 +524,81 @@ let InstanceStatus = /* @__PURE__ */ function(InstanceStatus$1) {
501
524
  return this._holders;
502
525
  }
503
526
  /**
504
- * Deletes a holder by name.
527
+ * Deletes a holder by name and cleans up the reverse dependency index.
505
528
  * @param name The name of the holder to delete
506
529
  * @returns true if the holder was deleted, false if it didn't exist
507
530
  */ delete(name) {
531
+ const holder = this._holders.get(name);
532
+ if (holder) this.removeFromDependentsIndex(name, holder.deps);
508
533
  return this._holders.delete(name);
509
534
  }
510
535
  /**
536
+ * Registers a holder's dependencies in the reverse index.
537
+ * Call this after creating a holder with dependencies.
538
+ * @param holderName The name of the holder that has dependencies
539
+ * @param deps The set of dependency names
540
+ */ registerDependencies(holderName, deps) {
541
+ for (const dep of deps) {
542
+ let dependents = this._dependents.get(dep);
543
+ if (!dependents) {
544
+ dependents = /* @__PURE__ */ new Set();
545
+ this._dependents.set(dep, dependents);
546
+ }
547
+ dependents.add(holderName);
548
+ }
549
+ }
550
+ /**
551
+ * Removes a holder from the reverse dependency index.
552
+ * @param holderName The name of the holder to remove
553
+ * @param deps The set of dependency names to clean up
554
+ */ removeFromDependentsIndex(holderName, deps) {
555
+ for (const dep of deps) {
556
+ const dependents = this._dependents.get(dep);
557
+ if (dependents) {
558
+ dependents.delete(holderName);
559
+ if (dependents.size === 0) this._dependents.delete(dep);
560
+ }
561
+ }
562
+ }
563
+ /**
564
+ * Gets all holder names that depend on the given instance name.
565
+ * O(1) lookup using the reverse dependency index.
566
+ * @param instanceName The instance name to find dependents for
567
+ * @returns Array of holder names that depend on this instance
568
+ */ getDependents(instanceName) {
569
+ const dependents = this._dependents.get(instanceName);
570
+ return dependents ? Array.from(dependents) : [];
571
+ }
572
+ /**
511
573
  * Filters holders based on a predicate function.
512
574
  * @param predicate Function to test each holder
513
575
  * @returns A new Map containing only the holders that match the predicate
576
+ * @deprecated Use forEachHolder() for iteration to avoid allocations
514
577
  */ filter(predicate) {
515
- return new Map([...this._holders].filter(([key, value]) => predicate(value, key)));
578
+ const result = /* @__PURE__ */ new Map();
579
+ for (const [key, value] of this._holders) if (predicate(value, key)) result.set(key, value);
580
+ return result;
581
+ }
582
+ /**
583
+ * Iterates over holders with a callback. More efficient than filter() as it
584
+ * avoids creating intermediate arrays and Maps.
585
+ * @param callback Function called for each holder with (holder, name)
586
+ */ forEachHolder(callback) {
587
+ for (const [name, holder] of this._holders) callback(holder, name);
516
588
  }
517
589
  /**
518
- * Clears all holders from this manager.
590
+ * Finds the first holder matching a predicate. More efficient than filter()
591
+ * when only one result is needed.
592
+ * @param predicate Function to test each holder
593
+ * @returns The first matching holder or undefined
594
+ */ findHolder(predicate) {
595
+ for (const [name, holder] of this._holders) if (predicate(holder, name)) return holder;
596
+ }
597
+ /**
598
+ * Clears all holders from this manager and the reverse dependency index.
519
599
  */ clear() {
520
600
  this._holders.clear();
601
+ this._dependents.clear();
521
602
  }
522
603
  /**
523
604
  * Gets the number of holders currently managed.
@@ -601,12 +682,14 @@ let InstanceStatus = /* @__PURE__ */ function(InstanceStatus$1) {
601
682
  if (waiterHolder && getHolder) {
602
683
  const cycle = CircularDetector.detectCycle(waiterHolder.name, holder.name, getHolder);
603
684
  if (cycle) return [DIError.circularDependency(cycle)];
604
- waiterHolder.waitingFor.add(holder.name);
685
+ if (process.env.NODE_ENV !== "production") waiterHolder.waitingFor.add(holder.name);
605
686
  }
606
687
  try {
607
688
  await holder.creationPromise;
608
689
  } finally {
609
- if (waiterHolder) waiterHolder.waitingFor.delete(holder.name);
690
+ if (process.env.NODE_ENV !== "production") {
691
+ if (waiterHolder) waiterHolder.waitingFor.delete(holder.name);
692
+ }
610
693
  }
611
694
  return BaseHolderManager.waitForHolderReady(holder, waiterHolder, getHolder);
612
695
  case InstanceStatus.Destroying: return [DIError.instanceDestroying(holder.name)];
@@ -733,10 +816,11 @@ var RequestStorage = class {
733
816
  return null;
734
817
  }
735
818
  findDependents(instanceName) {
736
- const dependents = [];
737
- for (const [name, holder] of this.contextHolder.holders) if (holder.deps.has(instanceName)) dependents.push(name);
738
- for (const [name, holder] of this.holderManager.filter(() => true)) if (holder.deps.has(instanceName)) dependents.push(name);
739
- return dependents;
819
+ const requestDependents = this.contextHolder.getDependents(instanceName);
820
+ const singletonDependents = this.holderManager.getDependents(instanceName);
821
+ if (requestDependents.length === 0) return singletonDependents;
822
+ if (singletonDependents.length === 0) return requestDependents;
823
+ return [...requestDependents, ...singletonDependents];
740
824
  }
741
825
  };
742
826
 
@@ -942,16 +1026,13 @@ var SingletonStorage = class {
942
1026
  return this.manager.getAllNames();
943
1027
  }
944
1028
  forEach(callback) {
945
- for (const [name, holder] of this.manager.filter(() => true)) callback(name, holder);
1029
+ this.manager.forEachHolder((holder, name) => callback(name, holder));
946
1030
  }
947
1031
  findByInstance(instance) {
948
- for (const [, holder] of this.manager.filter((h) => h.instance === instance)) return holder;
949
- return null;
1032
+ return this.manager.findHolder((h) => h.instance === instance) ?? null;
950
1033
  }
951
1034
  findDependents(instanceName) {
952
- const dependents = [];
953
- for (const [name, holder] of this.manager.filter(() => true)) if (holder.deps.has(instanceName)) dependents.push(name);
954
- return dependents;
1035
+ return this.manager.getDependents(instanceName);
955
1036
  }
956
1037
  };
957
1038
 
@@ -1179,6 +1260,8 @@ var SingletonStorage = class {
1179
1260
  */ async handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance, scopedContainer) {
1180
1261
  holder.instance = instance;
1181
1262
  holder.status = InstanceStatus.Created;
1263
+ if (ctx.deps.size > 0) if (scopedContainer) scopedContainer.getRequestContextHolder().registerDependencies(instanceName, ctx.deps);
1264
+ else this.manager.registerDependencies(instanceName, ctx.deps);
1182
1265
  if (ctx.deps.size > 0) ctx.deps.forEach((dependency) => {
1183
1266
  holder.destroyListeners.push(this.serviceLocator.getEventBus().on(dependency, "destroy", () => {
1184
1267
  this.logger?.log(`[InstanceResolver] Dependency ${dependency} destroyed, invalidating ${instanceName}`);
@@ -1634,12 +1717,42 @@ var SingletonStorage = class {
1634
1717
  //#endregion
1635
1718
  //#region src/internal/core/token-processor.mts
1636
1719
  /**
1720
+ * Simple LRU cache for instance name generation.
1721
+ * Uses a Map which maintains insertion order for efficient LRU eviction.
1722
+ */ var InstanceNameCache = class {
1723
+ cache = /* @__PURE__ */ new Map();
1724
+ maxSize;
1725
+ constructor(maxSize = 1e3) {
1726
+ this.maxSize = maxSize;
1727
+ }
1728
+ get(key) {
1729
+ const value = this.cache.get(key);
1730
+ if (value !== void 0) {
1731
+ this.cache.delete(key);
1732
+ this.cache.set(key, value);
1733
+ }
1734
+ return value;
1735
+ }
1736
+ set(key, value) {
1737
+ if (this.cache.has(key)) this.cache.delete(key);
1738
+ else if (this.cache.size >= this.maxSize) {
1739
+ const firstKey = this.cache.keys().next().value;
1740
+ if (firstKey !== void 0) this.cache.delete(firstKey);
1741
+ }
1742
+ this.cache.set(key, value);
1743
+ }
1744
+ clear() {
1745
+ this.cache.clear();
1746
+ }
1747
+ };
1748
+ /**
1637
1749
  * Handles token validation, normalization, and instance name generation.
1638
1750
  *
1639
1751
  * Provides utilities for resolving tokens to their underlying InjectionToken,
1640
1752
  * validating arguments against schemas, and generating unique instance identifiers.
1641
1753
  */ var TokenProcessor = class {
1642
1754
  logger;
1755
+ instanceNameCache = new InstanceNameCache();
1643
1756
  constructor(logger = null) {
1644
1757
  this.logger = logger;
1645
1758
  }
@@ -1698,10 +1811,16 @@ var SingletonStorage = class {
1698
1811
  }
1699
1812
  /**
1700
1813
  * Generates a unique instance name based on token and arguments.
1814
+ * Results are cached using an LRU cache for performance.
1701
1815
  */ generateInstanceName(token, args) {
1702
1816
  if (!args) return token.toString();
1703
- const formattedArgs = Object.entries(args).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => `${key}=${this.formatArgValue(value)}`).join(",");
1704
- return `${token.toString()}:${formattedArgs.replaceAll(/"/g, "").replaceAll(/:/g, "=")}`;
1817
+ const tokenStr = token.toString();
1818
+ const cacheKey = `${tokenStr}:${JSON.stringify(args)}`;
1819
+ const cached = this.instanceNameCache.get(cacheKey);
1820
+ if (cached !== void 0) return cached;
1821
+ const result = `${tokenStr}:${Object.entries(args).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => `${key}=${this.formatArgValue(value)}`).join(",").replaceAll(/"/g, "").replaceAll(/:/g, "=")}`;
1822
+ this.instanceNameCache.set(cacheKey, result);
1823
+ return result;
1705
1824
  }
1706
1825
  /**
1707
1826
  * Formats a single argument value for instance name generation.
@@ -2497,4 +2616,4 @@ Object.defineProperty(exports, 'wrapSyncInit', {
2497
2616
  return wrapSyncInit;
2498
2617
  }
2499
2618
  });
2500
- //# sourceMappingURL=container-BCv3XS6m.cjs.map
2619
+ //# sourceMappingURL=container-DENMeJ87.cjs.map