@navios/di 0.8.0 → 0.9.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 (264) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +117 -17
  3. package/lib/browser/container/abstract-container.d.mts +112 -0
  4. package/lib/browser/container/abstract-container.d.mts.map +1 -0
  5. package/lib/browser/container/abstract-container.mjs +100 -0
  6. package/lib/browser/container/abstract-container.mjs.map +1 -0
  7. package/lib/browser/container/container.d.mts +100 -0
  8. package/lib/browser/container/container.d.mts.map +1 -0
  9. package/lib/browser/container/container.mjs +424 -0
  10. package/lib/browser/container/container.mjs.map +1 -0
  11. package/lib/browser/container/scoped-container.d.mts +93 -0
  12. package/lib/browser/container/scoped-container.d.mts.map +1 -0
  13. package/lib/browser/container/scoped-container.mjs +119 -0
  14. package/lib/browser/container/scoped-container.mjs.map +1 -0
  15. package/lib/browser/decorators/factory.decorator.d.mts +26 -0
  16. package/lib/browser/decorators/factory.decorator.d.mts.map +1 -0
  17. package/lib/browser/decorators/factory.decorator.mjs +20 -0
  18. package/lib/browser/decorators/factory.decorator.mjs.map +1 -0
  19. package/lib/browser/decorators/injectable.decorator.d.mts +38 -0
  20. package/lib/browser/decorators/injectable.decorator.d.mts.map +1 -0
  21. package/lib/browser/decorators/injectable.decorator.mjs +21 -0
  22. package/lib/browser/decorators/injectable.decorator.mjs.map +1 -0
  23. package/lib/browser/enums/injectable-scope.enum.d.mts +18 -0
  24. package/lib/browser/enums/injectable-scope.enum.d.mts.map +1 -0
  25. package/lib/browser/enums/injectable-scope.enum.mjs +20 -0
  26. package/lib/browser/enums/injectable-scope.enum.mjs.map +1 -0
  27. package/lib/browser/enums/injectable-type.enum.d.mts +8 -0
  28. package/lib/browser/enums/injectable-type.enum.d.mts.map +1 -0
  29. package/lib/browser/enums/injectable-type.enum.mjs +10 -0
  30. package/lib/browser/enums/injectable-type.enum.mjs.map +1 -0
  31. package/lib/browser/errors/di-error.d.mts +43 -0
  32. package/lib/browser/errors/di-error.d.mts.map +1 -0
  33. package/lib/browser/errors/di-error.mjs +98 -0
  34. package/lib/browser/errors/di-error.mjs.map +1 -0
  35. package/lib/browser/event-emitter.d.mts +16 -0
  36. package/lib/browser/event-emitter.d.mts.map +1 -0
  37. package/lib/browser/event-emitter.mjs +320 -0
  38. package/lib/browser/event-emitter.mjs.map +1 -0
  39. package/lib/browser/index.d.mts +37 -1558
  40. package/lib/browser/index.mjs +29 -2749
  41. package/lib/browser/interfaces/container.interface.d.mts +59 -0
  42. package/lib/browser/interfaces/container.interface.d.mts.map +1 -0
  43. package/lib/browser/interfaces/factory.interface.d.mts +14 -0
  44. package/lib/browser/interfaces/factory.interface.d.mts.map +1 -0
  45. package/lib/browser/interfaces/on-service-destroy.interface.d.mts +7 -0
  46. package/lib/browser/interfaces/on-service-destroy.interface.d.mts.map +1 -0
  47. package/lib/browser/interfaces/on-service-init.interface.d.mts +7 -0
  48. package/lib/browser/interfaces/on-service-init.interface.d.mts.map +1 -0
  49. package/lib/browser/internal/context/async-local-storage.browser.mjs +20 -0
  50. package/lib/browser/internal/context/async-local-storage.browser.mjs.map +1 -0
  51. package/lib/browser/internal/context/async-local-storage.d.mts +9 -0
  52. package/lib/browser/internal/context/async-local-storage.d.mts.map +1 -0
  53. package/lib/browser/internal/context/async-local-storage.types.d.mts +11 -0
  54. package/lib/browser/internal/context/async-local-storage.types.d.mts.map +1 -0
  55. package/lib/browser/internal/context/factory-context.d.mts +23 -0
  56. package/lib/browser/internal/context/factory-context.d.mts.map +1 -0
  57. package/lib/browser/internal/context/resolution-context.d.mts +43 -0
  58. package/lib/browser/internal/context/resolution-context.d.mts.map +1 -0
  59. package/lib/browser/internal/context/resolution-context.mjs +56 -0
  60. package/lib/browser/internal/context/resolution-context.mjs.map +1 -0
  61. package/lib/browser/internal/context/service-initialization-context.d.mts +48 -0
  62. package/lib/browser/internal/context/service-initialization-context.d.mts.map +1 -0
  63. package/lib/browser/internal/context/sync-local-storage.mjs +53 -0
  64. package/lib/browser/internal/context/sync-local-storage.mjs.map +1 -0
  65. package/lib/browser/internal/core/instance-resolver.d.mts +119 -0
  66. package/lib/browser/internal/core/instance-resolver.d.mts.map +1 -0
  67. package/lib/browser/internal/core/instance-resolver.mjs +306 -0
  68. package/lib/browser/internal/core/instance-resolver.mjs.map +1 -0
  69. package/lib/browser/internal/core/name-resolver.d.mts +52 -0
  70. package/lib/browser/internal/core/name-resolver.d.mts.map +1 -0
  71. package/lib/browser/internal/core/name-resolver.mjs +118 -0
  72. package/lib/browser/internal/core/name-resolver.mjs.map +1 -0
  73. package/lib/browser/internal/core/scope-tracker.d.mts +65 -0
  74. package/lib/browser/internal/core/scope-tracker.d.mts.map +1 -0
  75. package/lib/browser/internal/core/scope-tracker.mjs +120 -0
  76. package/lib/browser/internal/core/scope-tracker.mjs.map +1 -0
  77. package/lib/browser/internal/core/service-initializer.d.mts +44 -0
  78. package/lib/browser/internal/core/service-initializer.d.mts.map +1 -0
  79. package/lib/browser/internal/core/service-initializer.mjs +109 -0
  80. package/lib/browser/internal/core/service-initializer.mjs.map +1 -0
  81. package/lib/browser/internal/core/service-invalidator.d.mts +81 -0
  82. package/lib/browser/internal/core/service-invalidator.d.mts.map +1 -0
  83. package/lib/browser/internal/core/service-invalidator.mjs +142 -0
  84. package/lib/browser/internal/core/service-invalidator.mjs.map +1 -0
  85. package/lib/browser/internal/core/token-resolver.d.mts +54 -0
  86. package/lib/browser/internal/core/token-resolver.d.mts.map +1 -0
  87. package/lib/browser/internal/core/token-resolver.mjs +77 -0
  88. package/lib/browser/internal/core/token-resolver.mjs.map +1 -0
  89. package/lib/browser/internal/holder/holder-storage.interface.d.mts +99 -0
  90. package/lib/browser/internal/holder/holder-storage.interface.d.mts.map +1 -0
  91. package/lib/browser/internal/holder/instance-holder.d.mts +101 -0
  92. package/lib/browser/internal/holder/instance-holder.d.mts.map +1 -0
  93. package/lib/browser/internal/holder/instance-holder.mjs +19 -0
  94. package/lib/browser/internal/holder/instance-holder.mjs.map +1 -0
  95. package/lib/browser/internal/holder/unified-storage.d.mts +53 -0
  96. package/lib/browser/internal/holder/unified-storage.d.mts.map +1 -0
  97. package/lib/browser/internal/holder/unified-storage.mjs +144 -0
  98. package/lib/browser/internal/holder/unified-storage.mjs.map +1 -0
  99. package/lib/browser/internal/lifecycle/circular-detector.d.mts +39 -0
  100. package/lib/browser/internal/lifecycle/circular-detector.d.mts.map +1 -0
  101. package/lib/browser/internal/lifecycle/circular-detector.mjs +55 -0
  102. package/lib/browser/internal/lifecycle/circular-detector.mjs.map +1 -0
  103. package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts +18 -0
  104. package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts.map +1 -0
  105. package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs +43 -0
  106. package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs.map +1 -0
  107. package/lib/browser/internal/stub-factory-class.d.mts +14 -0
  108. package/lib/browser/internal/stub-factory-class.d.mts.map +1 -0
  109. package/lib/browser/internal/stub-factory-class.mjs +18 -0
  110. package/lib/browser/internal/stub-factory-class.mjs.map +1 -0
  111. package/lib/browser/symbols/injectable-token.d.mts +5 -0
  112. package/lib/browser/symbols/injectable-token.d.mts.map +1 -0
  113. package/lib/browser/symbols/injectable-token.mjs +6 -0
  114. package/lib/browser/symbols/injectable-token.mjs.map +1 -0
  115. package/lib/browser/token/injection-token.d.mts +55 -0
  116. package/lib/browser/token/injection-token.d.mts.map +1 -0
  117. package/lib/browser/token/injection-token.mjs +100 -0
  118. package/lib/browser/token/injection-token.mjs.map +1 -0
  119. package/lib/browser/token/registry.d.mts +37 -0
  120. package/lib/browser/token/registry.d.mts.map +1 -0
  121. package/lib/browser/token/registry.mjs +86 -0
  122. package/lib/browser/token/registry.mjs.map +1 -0
  123. package/lib/browser/utils/default-injectors.d.mts +12 -0
  124. package/lib/browser/utils/default-injectors.d.mts.map +1 -0
  125. package/lib/browser/utils/default-injectors.mjs +13 -0
  126. package/lib/browser/utils/default-injectors.mjs.map +1 -0
  127. package/lib/browser/utils/get-injectable-token.d.mts +9 -0
  128. package/lib/browser/utils/get-injectable-token.d.mts.map +1 -0
  129. package/lib/browser/utils/get-injectable-token.mjs +13 -0
  130. package/lib/browser/utils/get-injectable-token.mjs.map +1 -0
  131. package/lib/browser/utils/get-injectors.d.mts +55 -0
  132. package/lib/browser/utils/get-injectors.d.mts.map +1 -0
  133. package/lib/browser/utils/get-injectors.mjs +121 -0
  134. package/lib/browser/utils/get-injectors.mjs.map +1 -0
  135. package/lib/browser/utils/types.d.mts +23 -0
  136. package/lib/browser/utils/types.d.mts.map +1 -0
  137. package/lib/{container-DAKOvAgr.mjs → container-8-z89TyQ.mjs} +1325 -1462
  138. package/lib/container-8-z89TyQ.mjs.map +1 -0
  139. package/lib/{container-Bp1W-pWJ.d.mts → container-CNiqesCL.d.mts} +598 -617
  140. package/lib/container-CNiqesCL.d.mts.map +1 -0
  141. package/lib/{container-DENMeJ87.cjs → container-CaY2fDuk.cjs} +1369 -1512
  142. package/lib/container-CaY2fDuk.cjs.map +1 -0
  143. package/lib/{container-YPwvmlK2.d.cts → container-D-0Ho3qL.d.cts} +598 -612
  144. package/lib/container-D-0Ho3qL.d.cts.map +1 -0
  145. package/lib/index.cjs +13 -15
  146. package/lib/index.cjs.map +1 -1
  147. package/lib/index.d.cts +58 -223
  148. package/lib/index.d.cts.map +1 -1
  149. package/lib/index.d.mts +62 -222
  150. package/lib/index.d.mts.map +1 -1
  151. package/lib/index.mjs +5 -6
  152. package/lib/index.mjs.map +1 -1
  153. package/lib/testing/index.cjs +569 -311
  154. package/lib/testing/index.cjs.map +1 -1
  155. package/lib/testing/index.d.cts +370 -41
  156. package/lib/testing/index.d.cts.map +1 -1
  157. package/lib/testing/index.d.mts +370 -41
  158. package/lib/testing/index.d.mts.map +1 -1
  159. package/lib/testing/index.mjs +568 -305
  160. package/lib/testing/index.mjs.map +1 -1
  161. package/package.json +2 -1
  162. package/src/__tests__/circular-detector.spec.mts +193 -0
  163. package/src/__tests__/concurrent.spec.mts +368 -0
  164. package/src/__tests__/container.spec.mts +32 -30
  165. package/src/__tests__/di-error.spec.mts +351 -0
  166. package/src/__tests__/e2e.browser.spec.mts +0 -4
  167. package/src/__tests__/e2e.spec.mts +10 -19
  168. package/src/__tests__/event-emitter.spec.mts +232 -109
  169. package/src/__tests__/get-injectors.spec.mts +250 -39
  170. package/src/__tests__/injection-token.spec.mts +293 -349
  171. package/src/__tests__/library-findings.spec.mts +8 -8
  172. package/src/__tests__/registry.spec.mts +358 -210
  173. package/src/__tests__/resolution-context.spec.mts +255 -0
  174. package/src/__tests__/scope-tracker.spec.mts +598 -0
  175. package/src/__tests__/scope-upgrade.spec.mts +808 -0
  176. package/src/__tests__/scoped-container.spec.mts +595 -0
  177. package/src/__tests__/test-container.spec.mts +293 -0
  178. package/src/__tests__/token-resolver.spec.mts +207 -0
  179. package/src/__tests__/unified-storage.spec.mts +535 -0
  180. package/src/__tests__/unit-test-container.spec.mts +405 -0
  181. package/src/__type-tests__/container.spec-d.mts +180 -0
  182. package/src/__type-tests__/factory.spec-d.mts +15 -3
  183. package/src/__type-tests__/inject.spec-d.mts +115 -20
  184. package/src/__type-tests__/injectable.spec-d.mts +69 -52
  185. package/src/__type-tests__/injection-token.spec-d.mts +176 -0
  186. package/src/__type-tests__/scoped-container.spec-d.mts +212 -0
  187. package/src/container/abstract-container.mts +327 -0
  188. package/src/container/container.mts +142 -170
  189. package/src/container/scoped-container.mts +126 -208
  190. package/src/decorators/factory.decorator.mts +16 -11
  191. package/src/decorators/injectable.decorator.mts +20 -16
  192. package/src/enums/index.mts +2 -2
  193. package/src/enums/injectable-scope.enum.mts +1 -0
  194. package/src/enums/injectable-type.enum.mts +1 -0
  195. package/src/errors/di-error.mts +96 -0
  196. package/src/event-emitter.mts +3 -27
  197. package/src/index.mts +6 -153
  198. package/src/interfaces/container.interface.mts +13 -0
  199. package/src/interfaces/factory.interface.mts +1 -1
  200. package/src/interfaces/index.mts +1 -1
  201. package/src/internal/context/async-local-storage.mts +3 -2
  202. package/src/internal/context/async-local-storage.types.mts +1 -0
  203. package/src/internal/context/factory-context.mts +1 -0
  204. package/src/internal/context/index.mts +3 -1
  205. package/src/internal/context/resolution-context.mts +1 -0
  206. package/src/internal/context/service-initialization-context.mts +43 -0
  207. package/src/internal/core/index.mts +5 -4
  208. package/src/internal/core/instance-resolver.mts +460 -302
  209. package/src/internal/core/name-resolver.mts +196 -0
  210. package/src/internal/core/scope-tracker.mts +242 -0
  211. package/src/internal/core/{instantiator.mts → service-initializer.mts} +51 -29
  212. package/src/internal/core/service-invalidator.mts +290 -0
  213. package/src/internal/core/token-resolver.mts +122 -0
  214. package/src/internal/holder/holder-storage.interface.mts +11 -5
  215. package/src/internal/holder/index.mts +2 -5
  216. package/src/internal/holder/instance-holder.mts +1 -3
  217. package/src/internal/holder/unified-storage.mts +245 -0
  218. package/src/internal/index.mts +2 -1
  219. package/src/internal/lifecycle/circular-detector.mts +1 -0
  220. package/src/internal/lifecycle/index.mts +1 -1
  221. package/src/internal/lifecycle/lifecycle-event-bus.mts +1 -0
  222. package/src/internal/stub-factory-class.mts +16 -0
  223. package/src/symbols/injectable-token.mts +3 -1
  224. package/src/testing/index.mts +2 -0
  225. package/src/testing/test-container.mts +546 -85
  226. package/src/testing/types.mts +117 -0
  227. package/src/testing/unit-test-container.mts +509 -0
  228. package/src/token/injection-token.mts +41 -4
  229. package/src/token/registry.mts +75 -9
  230. package/src/utils/default-injectors.mts +16 -0
  231. package/src/utils/get-injectable-token.mts +2 -3
  232. package/src/utils/get-injectors.mts +26 -15
  233. package/src/utils/index.mts +3 -1
  234. package/src/utils/types.mts +1 -0
  235. package/tsdown.config.mts +11 -1
  236. package/lib/browser/index.d.mts.map +0 -1
  237. package/lib/browser/index.mjs.map +0 -1
  238. package/lib/container-Bp1W-pWJ.d.mts.map +0 -1
  239. package/lib/container-DAKOvAgr.mjs.map +0 -1
  240. package/lib/container-DENMeJ87.cjs.map +0 -1
  241. package/lib/container-YPwvmlK2.d.cts.map +0 -1
  242. package/src/__tests__/async-local-storage.browser.spec.mts +0 -166
  243. package/src/__tests__/async-local-storage.spec.mts +0 -333
  244. package/src/__tests__/errors.spec.mts +0 -87
  245. package/src/__tests__/factory.spec.mts +0 -137
  246. package/src/__tests__/injectable.spec.mts +0 -246
  247. package/src/__tests__/request-scope.spec.mts +0 -416
  248. package/src/__tests__/service-instantiator.spec.mts +0 -410
  249. package/src/__tests__/service-locator-event-bus.spec.mts +0 -242
  250. package/src/__tests__/service-locator-manager.spec.mts +0 -300
  251. package/src/__tests__/service-locator.spec.mts +0 -966
  252. package/src/__tests__/unified-api.spec.mts +0 -130
  253. package/src/browser.mts +0 -11
  254. package/src/injectors.mts +0 -18
  255. package/src/internal/context/request-context.mts +0 -225
  256. package/src/internal/core/invalidator.mts +0 -437
  257. package/src/internal/core/service-locator.mts +0 -202
  258. package/src/internal/core/token-processor.mts +0 -252
  259. package/src/internal/holder/base-holder-manager.mts +0 -334
  260. package/src/internal/holder/holder-manager.mts +0 -85
  261. package/src/internal/holder/request-storage.mts +0 -127
  262. package/src/internal/holder/singleton-storage.mts +0 -92
  263. package/src/testing/README.md +0 -80
  264. package/src/testing/__tests__/test-container.spec.mts +0 -173
@@ -0,0 +1,290 @@
1
+ import type { IHolderStorage } from '../holder/holder-storage.interface.mjs'
2
+ import type { InstanceHolder } from '../holder/instance-holder.mjs'
3
+ import type { LifecycleEventBus } from '../lifecycle/lifecycle-event-bus.mjs'
4
+
5
+ import { InstanceStatus } from '../holder/instance-holder.mjs'
6
+
7
+ export interface ClearAllOptions {
8
+ /** Whether to wait for all services to settle before starting (default: true) */
9
+ waitForSettlement?: boolean
10
+ }
11
+
12
+ export interface InvalidationOptions {
13
+ /** Whether to emit events after invalidation (default: true) */
14
+ emitEvents?: boolean
15
+ /** Custom event emitter function */
16
+ onInvalidated?: (instanceName: string) => Promise<void>
17
+ /** Whether to cascade invalidation to dependents (default: false - events handle it) */
18
+ cascade?: boolean
19
+ }
20
+
21
+ /**
22
+ * Manages graceful service cleanup with event-based invalidation.
23
+ *
24
+ * Uses event subscriptions instead of manual dependent finding.
25
+ * When a service is created, it subscribes to destroy events of its dependencies.
26
+ * When a dependency is destroyed, the event automatically invalidates dependents.
27
+ */
28
+ export class ServiceInvalidator {
29
+ constructor(
30
+ private readonly eventBus: LifecycleEventBus | null,
31
+ private readonly logger: Console | null = null,
32
+ ) {}
33
+
34
+ /**
35
+ * Invalidates a service using a specific storage.
36
+ * Event-based invalidation means dependents are automatically invalidated
37
+ * via destroy event subscriptions - no need to manually find dependents.
38
+ *
39
+ * @param service The instance name to invalidate
40
+ * @param storage The storage to use for this invalidation
41
+ * @param options Additional options for invalidation behavior
42
+ */
43
+ async invalidateWithStorage(
44
+ service: string,
45
+ storage: IHolderStorage,
46
+ options: InvalidationOptions = {},
47
+ ): Promise<void> {
48
+ const { emitEvents = true, onInvalidated } = options
49
+
50
+ this.logger?.log(
51
+ `[ServiceInvalidator] Starting invalidation process for ${service}`,
52
+ )
53
+
54
+ const result = storage.get(service)
55
+ if (result === null) {
56
+ return
57
+ }
58
+
59
+ const [, holder] = result
60
+ if (holder) {
61
+ await this.invalidateHolderWithStorage(
62
+ service,
63
+ holder,
64
+ storage,
65
+ emitEvents,
66
+ onInvalidated,
67
+ )
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Sets up destroy event subscriptions for a service's dependencies.
73
+ * Called when a service is successfully instantiated.
74
+ *
75
+ * @param serviceName The name of the service
76
+ * @param dependencies The set of dependency names
77
+ * @param storage The storage to use for invalidation
78
+ * @param holder The holder for the service (to add unsubscribe to destroy listeners)
79
+ */
80
+ setupDependencySubscriptions(
81
+ serviceName: string,
82
+ dependencies: Set<string>,
83
+ storage: IHolderStorage,
84
+ holder: InstanceHolder,
85
+ ): void {
86
+ if (!this.eventBus) {
87
+ return
88
+ }
89
+
90
+ for (const dependencyName of dependencies) {
91
+ // Subscribe to the dependency's destroy event
92
+ const unsubscribe = this.eventBus.on(dependencyName, 'destroy', () => {
93
+ this.logger?.log(
94
+ `[ServiceInvalidator] Dependency ${dependencyName} destroyed, invalidating ${serviceName}`,
95
+ )
96
+ // Automatically invalidate this service when dependency is destroyed
97
+ this.invalidateWithStorage(serviceName, storage).catch((error) => {
98
+ this.logger?.error(
99
+ `[ServiceInvalidator] Error invalidating ${serviceName} after dependency ${dependencyName} destroyed:`,
100
+ error,
101
+ )
102
+ })
103
+ })
104
+
105
+ // Store unsubscribe function in the service's destroy listeners
106
+ // so it's cleaned up when the service is destroyed
107
+ holder.destroyListeners.push(unsubscribe)
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Gracefully clears all services in a specific storage.
113
+ * This allows clearing request-scoped services using a RequestStorage.
114
+ */
115
+ async clearAllWithStorage(
116
+ storage: IHolderStorage,
117
+ options: ClearAllOptions = {},
118
+ ): Promise<void> {
119
+ const { waitForSettlement = true } = options
120
+
121
+ this.logger?.log(
122
+ '[ServiceInvalidator] Starting graceful clearing of all services',
123
+ )
124
+
125
+ // Wait for all services to settle if requested
126
+ if (waitForSettlement) {
127
+ this.logger?.log(
128
+ '[ServiceInvalidator] Waiting for all services to settle...',
129
+ )
130
+ await this.readyWithStorage(storage)
131
+ }
132
+
133
+ // Get all service names that need to be cleared
134
+ const allServiceNames = storage.getAllNames()
135
+
136
+ if (allServiceNames.length === 0) {
137
+ this.logger?.log('[ServiceInvalidator] No services to clear')
138
+ } else {
139
+ this.logger?.log(
140
+ `[ServiceInvalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(', ')}`,
141
+ )
142
+
143
+ // Clear services - events will handle dependent invalidation
144
+ const clearPromises = allServiceNames.map((serviceName) =>
145
+ this.invalidateWithStorage(serviceName, storage),
146
+ )
147
+
148
+ await Promise.all(clearPromises)
149
+ }
150
+
151
+ this.logger?.log('[ServiceInvalidator] Graceful clearing completed')
152
+ }
153
+
154
+ /**
155
+ * Waits for all services in a specific storage to settle.
156
+ */
157
+ async readyWithStorage(storage: IHolderStorage): Promise<void> {
158
+ const holders: InstanceHolder<any>[] = []
159
+ storage.forEach((_: string, holder: InstanceHolder) => holders.push(holder))
160
+ await Promise.all(
161
+ holders.map((holder) => this.waitForHolderToSettle(holder)),
162
+ )
163
+ }
164
+
165
+ // ============================================================================
166
+ // INTERNAL INVALIDATION HELPERS
167
+ // ============================================================================
168
+
169
+ /**
170
+ * Invalidates a single holder using a specific storage.
171
+ */
172
+ private async invalidateHolderWithStorage(
173
+ key: string,
174
+ holder: InstanceHolder<any>,
175
+ storage: IHolderStorage,
176
+ emitEvents: boolean,
177
+ onInvalidated?: (instanceName: string) => Promise<void>,
178
+ ): Promise<void> {
179
+ await this.invalidateHolderByStatus(holder, {
180
+ context: key,
181
+ onDestroy: () =>
182
+ this.destroyHolderWithStorage(
183
+ key,
184
+ holder,
185
+ storage,
186
+ emitEvents,
187
+ onInvalidated,
188
+ ),
189
+ })
190
+ }
191
+
192
+ /**
193
+ * Common invalidation logic for holders based on their status.
194
+ */
195
+ private async invalidateHolderByStatus(
196
+ holder: InstanceHolder<any>,
197
+ options: {
198
+ context: string
199
+ onDestroy: () => Promise<void>
200
+ },
201
+ ): Promise<void> {
202
+ switch (holder.status) {
203
+ case InstanceStatus.Destroying:
204
+ await holder.destroyPromise
205
+ break
206
+
207
+ case InstanceStatus.Creating:
208
+ // Wait for creation to complete before destroying
209
+ await holder.creationPromise
210
+ await options.onDestroy()
211
+ break
212
+
213
+ default:
214
+ await options.onDestroy()
215
+ break
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Destroys a holder using a specific storage.
221
+ */
222
+ private async destroyHolderWithStorage(
223
+ key: string,
224
+ holder: InstanceHolder<any>,
225
+ storage: IHolderStorage,
226
+ emitEvents: boolean,
227
+ onInvalidated?: (instanceName: string) => Promise<void>,
228
+ ): Promise<void> {
229
+ holder.status = InstanceStatus.Destroying
230
+ this.logger?.log(
231
+ `[ServiceInvalidator] Invalidating ${key} and notifying listeners`,
232
+ )
233
+
234
+ holder.destroyPromise = Promise.all(
235
+ holder.destroyListeners.map((listener) => listener()),
236
+ ).then(async () => {
237
+ holder.destroyListeners = []
238
+ holder.deps.clear()
239
+ storage.delete(key)
240
+
241
+ // Emit events if enabled and event bus exists
242
+ if (emitEvents && this.eventBus) {
243
+ await this.emitInstanceEvent(key, 'destroy')
244
+ }
245
+
246
+ // Call custom callback if provided
247
+ if (onInvalidated) {
248
+ await onInvalidated(key)
249
+ }
250
+ })
251
+
252
+ await holder.destroyPromise
253
+ }
254
+
255
+ /**
256
+ * Waits for a holder to settle (either created, destroyed, or error state).
257
+ */
258
+ private async waitForHolderToSettle(
259
+ holder: InstanceHolder<any>,
260
+ ): Promise<void> {
261
+ switch (holder.status) {
262
+ case InstanceStatus.Creating:
263
+ await holder.creationPromise
264
+ break
265
+ case InstanceStatus.Destroying:
266
+ await holder.destroyPromise
267
+ break
268
+ // Already settled states
269
+ case InstanceStatus.Created:
270
+ case InstanceStatus.Error:
271
+ break
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Emits events to listeners for instance lifecycle events.
277
+ */
278
+ private emitInstanceEvent(
279
+ name: string,
280
+ event: 'create' | 'destroy' = 'create',
281
+ ) {
282
+ if (!this.eventBus) {
283
+ return Promise.resolve()
284
+ }
285
+ this.logger?.log(
286
+ `[ServiceInvalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`,
287
+ )
288
+ return this.eventBus.emit(name, event)
289
+ }
290
+ }
@@ -0,0 +1,122 @@
1
+ import type {
2
+ AnyInjectableType,
3
+ InjectionTokenType,
4
+ } from '../../token/injection-token.mjs'
5
+
6
+ import { DIError } from '../../errors/index.mjs'
7
+ import {
8
+ BoundInjectionToken,
9
+ FactoryInjectionToken,
10
+ InjectionToken,
11
+ } from '../../token/injection-token.mjs'
12
+ import { getInjectableToken } from '../../utils/index.mjs'
13
+
14
+ /**
15
+ * Handles token validation and resolution.
16
+ *
17
+ * Focuses on token validation, normalization, and argument validation.
18
+ * Name generation is handled by NameResolver.
19
+ */
20
+ export class TokenResolver {
21
+ constructor(private readonly logger: Console | null = null) {}
22
+
23
+ // ============================================================================
24
+ // TOKEN NORMALIZATION
25
+ // ============================================================================
26
+
27
+ /**
28
+ * Normalizes a token to an InjectionToken.
29
+ * Handles class constructors by getting their injectable token.
30
+ *
31
+ * @param token A class constructor, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
32
+ * @returns The normalized InjectionTokenType
33
+ */
34
+ normalizeToken(token: AnyInjectableType): InjectionTokenType {
35
+ if (typeof token === 'function') {
36
+ return getInjectableToken(token)
37
+ }
38
+ return token as InjectionTokenType
39
+ }
40
+
41
+ /**
42
+ * Gets the underlying "real" token from wrapped tokens.
43
+ * For BoundInjectionToken and FactoryInjectionToken, returns the wrapped token.
44
+ * For other tokens, returns the token itself.
45
+ *
46
+ * @param token The token to unwrap
47
+ * @returns The underlying InjectionToken
48
+ */
49
+ getRealToken<T = unknown>(token: InjectionTokenType): InjectionToken<T> {
50
+ if (
51
+ token instanceof BoundInjectionToken ||
52
+ token instanceof FactoryInjectionToken
53
+ ) {
54
+ return token.token as InjectionToken<T>
55
+ }
56
+ return token as InjectionToken<T>
57
+ }
58
+
59
+ /**
60
+ * Convenience method that normalizes a token and then gets the real token.
61
+ * Useful for checking registry entries where you need the actual registered token.
62
+ *
63
+ * @param token Any injectable type
64
+ * @returns The underlying InjectionToken
65
+ */
66
+ getRegistryToken<T = unknown>(token: AnyInjectableType): InjectionToken<T> {
67
+ return this.getRealToken(this.normalizeToken(token))
68
+ }
69
+
70
+ // ============================================================================
71
+ // TOKEN VALIDATION
72
+ // ============================================================================
73
+
74
+ /**
75
+ * Validates and resolves token arguments, handling factory token resolution and validation.
76
+ *
77
+ * @param token The token to validate
78
+ * @param args Optional arguments
79
+ * @returns [error, { actualToken, validatedArgs }]
80
+ */
81
+ validateAndResolveTokenArgs(
82
+ token: AnyInjectableType,
83
+ args?: any,
84
+ ): [
85
+ DIError | undefined,
86
+ { actualToken: InjectionTokenType; validatedArgs?: any },
87
+ ] {
88
+ let actualToken = token as InjectionToken<any, any>
89
+ if (typeof token === 'function') {
90
+ actualToken = getInjectableToken(token)
91
+ }
92
+ let realArgs = args
93
+ if (actualToken instanceof BoundInjectionToken) {
94
+ realArgs = actualToken.value
95
+ } else if (actualToken instanceof FactoryInjectionToken) {
96
+ if (actualToken.resolved) {
97
+ realArgs = actualToken.value
98
+ } else {
99
+ return [DIError.factoryTokenNotResolved(token.name), { actualToken }]
100
+ }
101
+ }
102
+ if (!actualToken.schema) {
103
+ return [undefined, { actualToken, validatedArgs: realArgs }]
104
+ }
105
+ const validatedArgs = actualToken.schema?.safeParse(realArgs)
106
+ if (validatedArgs && !validatedArgs.success) {
107
+ this.logger?.error(
108
+ `[TokenResolver]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`,
109
+ validatedArgs.error,
110
+ )
111
+ return [
112
+ DIError.tokenValidationError(
113
+ `Validation failed for ${actualToken.name.toString()}`,
114
+ actualToken.schema,
115
+ realArgs,
116
+ ),
117
+ { actualToken },
118
+ ]
119
+ }
120
+ return [undefined, { actualToken, validatedArgs: validatedArgs?.data }]
121
+ }
122
+ }
@@ -17,8 +17,7 @@ export type HolderGetResult<T = unknown> =
17
17
  * Interface for abstracting holder storage operations.
18
18
  *
19
19
  * Enables unified instance resolution logic regardless of where
20
- * holders are stored (singleton manager, request context, etc.).
21
- * This is the key abstraction for the Storage Strategy pattern.
20
+ * holders are stored. This is the key abstraction for the unified storage pattern.
22
21
  */
23
22
  export interface IHolderStorage {
24
23
  /**
@@ -94,9 +93,7 @@ export interface IHolderStorage {
94
93
  *
95
94
  * @param callback Function called for each holder with (name, holder)
96
95
  */
97
- forEach(
98
- callback: (name: string, holder: InstanceHolder) => void,
99
- ): void
96
+ forEach(callback: (name: string, holder: InstanceHolder) => void): void
100
97
 
101
98
  /**
102
99
  * Finds a holder by its instance value (reverse lookup).
@@ -113,4 +110,13 @@ export interface IHolderStorage {
113
110
  * @returns Array of instance names that have this instance as a dependency
114
111
  */
115
112
  findDependents(instanceName: string): string[]
113
+
114
+ /**
115
+ * Updates dependency references when instance names change.
116
+ * Used during scope upgrades when instance names are regenerated with requestId.
117
+ *
118
+ * @param oldName The old instance name
119
+ * @param newName The new instance name
120
+ */
121
+ updateDependencyReference(oldName: string, newName: string): void
116
122
  }
@@ -1,6 +1,3 @@
1
- export * from './instance-holder.mjs'
2
- export * from './base-holder-manager.mjs'
3
- export * from './holder-manager.mjs'
4
1
  export * from './holder-storage.interface.mjs'
5
- export * from './singleton-storage.mjs'
6
- export * from './request-storage.mjs'
2
+ export * from './instance-holder.mjs'
3
+ export * from './unified-storage.mjs'
@@ -14,9 +14,6 @@ export enum InstanceStatus {
14
14
  Error = 'error',
15
15
  }
16
16
 
17
- /** Callback function for instance effects */
18
- export type InstanceEffect = () => void
19
-
20
17
  /** Callback function for instance destruction listeners */
21
18
  export type InstanceDestroyListener = () => void | Promise<void>
22
19
 
@@ -107,3 +104,4 @@ export type InstanceHolder<Instance = unknown> =
107
104
  | InstanceHolderCreated<Instance>
108
105
  | InstanceHolderDestroying<Instance>
109
106
  | InstanceHolderError
107
+