@navios/di 0.7.1 → 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 (263) hide show
  1. package/CHANGELOG.md +110 -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 -1508
  40. package/lib/browser/index.mjs +29 -2650
  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-Pb_Y4Z4x.mjs → container-8-z89TyQ.mjs} +1269 -1305
  138. package/lib/container-8-z89TyQ.mjs.map +1 -0
  139. package/lib/{container-BuAutHGg.d.mts → container-CNiqesCL.d.mts} +600 -569
  140. package/lib/container-CNiqesCL.d.mts.map +1 -0
  141. package/lib/{container-DnzgpfBe.cjs → container-CaY2fDuk.cjs} +1287 -1329
  142. package/lib/container-CaY2fDuk.cjs.map +1 -0
  143. package/lib/{container-oGTgX2iX.d.cts → container-D-0Ho3qL.d.cts} +601 -565
  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 +461 -292
  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-processor.mts → token-resolver.mts} +17 -88
  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-BuAutHGg.d.mts.map +0 -1
  239. package/lib/container-DnzgpfBe.cjs.map +0 -1
  240. package/lib/container-Pb_Y4Z4x.mjs.map +0 -1
  241. package/lib/container-oGTgX2iX.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 -214
  256. package/src/internal/core/invalidator.mts +0 -437
  257. package/src/internal/core/service-locator.mts +0 -202
  258. package/src/internal/holder/base-holder-manager.mts +0 -238
  259. package/src/internal/holder/holder-manager.mts +0 -85
  260. package/src/internal/holder/request-storage.mts +0 -134
  261. package/src/internal/holder/singleton-storage.mts +0 -105
  262. package/src/testing/README.md +0 -80
  263. package/src/testing/__tests__/test-container.spec.mts +0 -173
@@ -1,437 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import type { IHolderStorage } from '../holder/holder-storage.interface.mjs'
3
- import type { LifecycleEventBus } from '../lifecycle/lifecycle-event-bus.mjs'
4
- import type { InstanceHolder } from '../holder/instance-holder.mjs'
5
- import type { HolderManager } from '../holder/holder-manager.mjs'
6
-
7
- import { InstanceStatus } from '../holder/instance-holder.mjs'
8
- import { SingletonStorage } from '../holder/singleton-storage.mjs'
9
-
10
- export interface ClearAllOptions {
11
- /** Maximum number of invalidation rounds to prevent infinite loops (default: 10) */
12
- maxRounds?: number
13
- /** Whether to wait for all services to settle before starting (default: true) */
14
- waitForSettlement?: boolean
15
- }
16
-
17
- export interface InvalidationOptions {
18
- /** Whether to emit events after invalidation (default: true for singletons) */
19
- emitEvents?: boolean
20
- /** Custom event emitter function */
21
- onInvalidated?: (instanceName: string) => Promise<void>
22
- /** Whether to cascade invalidation to dependents (default: true) */
23
- cascade?: boolean
24
- /** Internal: tracks services being invalidated in the current call chain to prevent circular loops */
25
- _invalidating?: Set<string>
26
- }
27
-
28
- /**
29
- * Manages graceful service cleanup with dependency-aware invalidation.
30
- *
31
- * Ensures services are destroyed in the correct order based on their dependencies.
32
- * Works with any IHolderStorage implementation, enabling unified invalidation
33
- * for both singleton and request-scoped services.
34
- */
35
- export class Invalidator {
36
- private readonly storage: IHolderStorage
37
-
38
- constructor(
39
- manager: HolderManager,
40
- private readonly eventBus: LifecycleEventBus | null,
41
- private readonly logger: Console | null = null,
42
- ) {
43
- this.storage = new SingletonStorage(manager)
44
- }
45
-
46
- /**
47
- * Invalidates a service and all its dependencies.
48
- * Works with the configured storage (singleton by default).
49
- */
50
- invalidate(service: string, round = 1): Promise<any> {
51
- return this.invalidateWithStorage(service, this.storage, round)
52
- }
53
-
54
- /**
55
- * Invalidates a service using a specific storage.
56
- * This allows request-scoped invalidation using a RequestStorage.
57
- *
58
- * @param service The instance name to invalidate
59
- * @param storage The storage to use for this invalidation
60
- * @param round Current invalidation round (for recursion limiting)
61
- * @param options Additional options for invalidation behavior
62
- */
63
- async invalidateWithStorage(
64
- service: string,
65
- storage: IHolderStorage,
66
- round = 1,
67
- options: InvalidationOptions = {},
68
- ): Promise<void> {
69
- const { cascade = true, _invalidating = new Set<string>() } = options
70
-
71
- // Prevent infinite recursion from circular dependencies
72
- if (_invalidating.has(service)) {
73
- this.logger?.log(
74
- `[Invalidator] Skipping ${service} - already being invalidated in this chain`,
75
- )
76
- return
77
- }
78
-
79
- this.logger?.log(
80
- `[Invalidator] Starting invalidation process for ${service}`,
81
- )
82
-
83
- const result = storage.get(service)
84
- if (result === null) {
85
- return
86
- }
87
-
88
- // Mark this service as being invalidated
89
- _invalidating.add(service)
90
-
91
- // Pass the tracking set to cascaded invalidations
92
- const optionsWithTracking = { ...options, _invalidating }
93
-
94
- // Cascade invalidation: first invalidate all services that depend on this one
95
- if (cascade) {
96
- const dependents = storage.findDependents(service)
97
- for (const dependentName of dependents) {
98
- await this.invalidateWithStorage(dependentName, storage, round, optionsWithTracking)
99
- }
100
- }
101
-
102
- const [, holder] = result
103
- if (holder) {
104
- await this.invalidateHolderWithStorage(service, holder, storage, round, optionsWithTracking)
105
- }
106
- }
107
-
108
- /**
109
- * Gracefully clears all services using invalidation logic.
110
- * This method respects service dependencies and ensures proper cleanup order.
111
- * Services that depend on others will be invalidated first, then their dependencies.
112
- */
113
- async clearAll(options: ClearAllOptions = {}): Promise<void> {
114
- return this.clearAllWithStorage(this.storage, options)
115
- }
116
-
117
- /**
118
- * Gracefully clears all services in a specific storage.
119
- * This allows clearing request-scoped services using a RequestStorage.
120
- */
121
- async clearAllWithStorage(
122
- storage: IHolderStorage,
123
- options: ClearAllOptions = {},
124
- ): Promise<void> {
125
- const { maxRounds = 10, waitForSettlement = true } = options
126
-
127
- this.logger?.log(
128
- '[Invalidator] Starting graceful clearing of all services',
129
- )
130
-
131
- // Wait for all services to settle if requested
132
- if (waitForSettlement) {
133
- this.logger?.log(
134
- '[Invalidator] Waiting for all services to settle...',
135
- )
136
- await this.readyWithStorage(storage)
137
- }
138
-
139
- // Get all service names that need to be cleared
140
- const allServiceNames = storage.getAllNames()
141
-
142
- if (allServiceNames.length === 0) {
143
- this.logger?.log('[Invalidator] No services to clear')
144
- } else {
145
- this.logger?.log(
146
- `[Invalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(', ')}`,
147
- )
148
-
149
- // Clear services using dependency-aware invalidation
150
- await this.clearServicesWithDependencyAwarenessForStorage(
151
- allServiceNames,
152
- maxRounds,
153
- storage,
154
- )
155
- }
156
-
157
- this.logger?.log('[Invalidator] Graceful clearing completed')
158
- }
159
-
160
- /**
161
- * Waits for all services to settle (either created, destroyed, or error state).
162
- */
163
- async ready(): Promise<void> {
164
- return this.readyWithStorage(this.storage)
165
- }
166
-
167
- /**
168
- * Waits for all services in a specific storage to settle.
169
- */
170
- async readyWithStorage(storage: IHolderStorage): Promise<void> {
171
- const holders: InstanceHolder<any>[] = []
172
- storage.forEach((_: string, holder: InstanceHolder) => holders.push(holder))
173
- await Promise.all(
174
- holders.map((holder) => this.waitForHolderToSettle(holder)),
175
- )
176
- }
177
-
178
- // ============================================================================
179
- // INTERNAL INVALIDATION HELPERS
180
- // ============================================================================
181
-
182
- /**
183
- * Invalidates a single holder using a specific storage.
184
- */
185
- private async invalidateHolderWithStorage(
186
- key: string,
187
- holder: InstanceHolder<any>,
188
- storage: IHolderStorage,
189
- round: number,
190
- options: InvalidationOptions = {},
191
- ): Promise<void> {
192
- const { emitEvents = true, onInvalidated } = options
193
-
194
- await this.invalidateHolderByStatus(holder, round, {
195
- context: key,
196
- onCreationError: () =>
197
- this.logger?.error(
198
- `[Invalidator] ${key} creation triggered too many invalidation rounds`,
199
- ),
200
- onRecursiveInvalidate: () =>
201
- this.invalidateWithStorage(key, storage, round + 1, options),
202
- onDestroy: () =>
203
- this.destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated),
204
- })
205
- }
206
-
207
- /**
208
- * Common invalidation logic for holders based on their status.
209
- */
210
- private async invalidateHolderByStatus(
211
- holder: InstanceHolder<any>,
212
- round: number,
213
- options: {
214
- context: string
215
- onCreationError: () => void
216
- onRecursiveInvalidate: () => Promise<void>
217
- onDestroy: () => Promise<void>
218
- },
219
- ): Promise<void> {
220
- switch (holder.status) {
221
- case InstanceStatus.Destroying:
222
- await holder.destroyPromise
223
- break
224
-
225
- case InstanceStatus.Creating:
226
- await holder.creationPromise
227
- if (round > 3) {
228
- options.onCreationError()
229
- return
230
- }
231
- await options.onRecursiveInvalidate()
232
- break
233
-
234
- default:
235
- await options.onDestroy()
236
- break
237
- }
238
- }
239
-
240
- /**
241
- * Destroys a holder using a specific storage.
242
- */
243
- private async destroyHolderWithStorage(
244
- key: string,
245
- holder: InstanceHolder<any>,
246
- storage: IHolderStorage,
247
- emitEvents: boolean,
248
- onInvalidated?: (instanceName: string) => Promise<void>,
249
- ): Promise<void> {
250
- holder.status = InstanceStatus.Destroying
251
- this.logger?.log(`[Invalidator] Invalidating ${key} and notifying listeners`)
252
-
253
- holder.destroyPromise = Promise.all(
254
- holder.destroyListeners.map((listener) => listener()),
255
- ).then(async () => {
256
- holder.destroyListeners = []
257
- holder.deps.clear()
258
- storage.delete(key)
259
-
260
- // Emit events if enabled and event bus exists
261
- if (emitEvents && this.eventBus) {
262
- await this.emitInstanceEvent(key, 'destroy')
263
- }
264
-
265
- // Call custom callback if provided
266
- if (onInvalidated) {
267
- await onInvalidated(key)
268
- }
269
- })
270
-
271
- await holder.destroyPromise
272
- }
273
-
274
- /**
275
- * Waits for a holder to settle (either created, destroyed, or error state).
276
- */
277
- private async waitForHolderToSettle(
278
- holder: InstanceHolder<any>,
279
- ): Promise<void> {
280
- switch (holder.status) {
281
- case InstanceStatus.Creating:
282
- await holder.creationPromise
283
- break
284
- case InstanceStatus.Destroying:
285
- await holder.destroyPromise
286
- break
287
- // Already settled states
288
- case InstanceStatus.Created:
289
- case InstanceStatus.Error:
290
- break
291
- }
292
- }
293
-
294
- /**
295
- * Clears services with dependency awareness for a specific storage.
296
- */
297
- private async clearServicesWithDependencyAwarenessForStorage(
298
- serviceNames: string[],
299
- maxRounds: number,
300
- storage: IHolderStorage,
301
- ): Promise<void> {
302
- const clearedServices = new Set<string>()
303
- let round = 1
304
-
305
- while (clearedServices.size < serviceNames.length && round <= maxRounds) {
306
- this.logger?.log(
307
- `[Invalidator] Clearing round ${round}/${maxRounds}, ${clearedServices.size}/${serviceNames.length} services cleared`,
308
- )
309
-
310
- // Find services that can be cleared in this round
311
- const servicesToClearThisRound = this.findServicesReadyForClearingInStorage(
312
- serviceNames,
313
- clearedServices,
314
- storage,
315
- )
316
-
317
- if (servicesToClearThisRound.length === 0) {
318
- // If no services can be cleared, try to clear remaining services anyway
319
- // This handles circular dependencies or other edge cases
320
- const remainingServices = serviceNames.filter(
321
- (name) => !clearedServices.has(name),
322
- )
323
-
324
- if (remainingServices.length > 0) {
325
- this.logger?.warn(
326
- `[Invalidator] No services ready for clearing, forcing cleanup of remaining: ${remainingServices.join(', ')}`,
327
- )
328
- await this.forceClearServicesInStorage(remainingServices, storage)
329
- remainingServices.forEach((name) => clearedServices.add(name))
330
- }
331
- break
332
- }
333
-
334
- // Clear services in this round
335
- const clearPromises = servicesToClearThisRound.map(
336
- async (serviceName) => {
337
- try {
338
- await this.invalidateWithStorage(serviceName, storage, round)
339
- clearedServices.add(serviceName)
340
- this.logger?.log(
341
- `[Invalidator] Successfully cleared service: ${serviceName}`,
342
- )
343
- } catch (error) {
344
- this.logger?.error(
345
- `[Invalidator] Error clearing service ${serviceName}:`,
346
- error,
347
- )
348
- // Still mark as cleared to avoid infinite loops
349
- clearedServices.add(serviceName)
350
- }
351
- },
352
- )
353
-
354
- await Promise.all(clearPromises)
355
- round++
356
- }
357
-
358
- if (clearedServices.size < serviceNames.length) {
359
- this.logger?.warn(
360
- `[Invalidator] Clearing completed after ${maxRounds} rounds, but ${serviceNames.length - clearedServices.size} services may not have been properly cleared`,
361
- )
362
- }
363
- }
364
-
365
- /**
366
- * Finds services that are ready to be cleared in the current round.
367
- * A service is ready if all its dependencies have already been cleared.
368
- */
369
- private findServicesReadyForClearingInStorage(
370
- allServiceNames: string[],
371
- clearedServices: Set<string>,
372
- storage: IHolderStorage,
373
- ): string[] {
374
- return allServiceNames.filter((serviceName) => {
375
- if (clearedServices.has(serviceName)) {
376
- return false // Already cleared
377
- }
378
-
379
- // Check if this service has any dependencies that haven't been cleared yet
380
- const result = storage.get(serviceName)
381
- if (result === null || result[0]) {
382
- return true // Service not found or in error state, can be cleared
383
- }
384
-
385
- const [, holder] = result
386
- // Check if all dependencies have been cleared
387
- const hasUnclearedDependencies = Array.from(holder!.deps).some(
388
- (dep) => !clearedServices.has(dep),
389
- )
390
-
391
- return !hasUnclearedDependencies
392
- })
393
- }
394
-
395
- /**
396
- * Force clears services that couldn't be cleared through normal dependency resolution.
397
- * This handles edge cases like circular dependencies.
398
- */
399
- private async forceClearServicesInStorage(
400
- serviceNames: string[],
401
- storage: IHolderStorage,
402
- ): Promise<void> {
403
- const promises = serviceNames.map(async (serviceName) => {
404
- try {
405
- // Directly destroy the holder without going through normal invalidation
406
- const result = storage.get(serviceName)
407
- if (result !== null && !result[0]) {
408
- const [, holder] = result
409
- await this.destroyHolderWithStorage(serviceName, holder!, storage, true)
410
- }
411
- } catch (error) {
412
- this.logger?.error(
413
- `[Invalidator] Error force clearing service ${serviceName}:`,
414
- error,
415
- )
416
- }
417
- })
418
-
419
- await Promise.all(promises)
420
- }
421
-
422
- /**
423
- * Emits events to listeners for instance lifecycle events.
424
- */
425
- private emitInstanceEvent(
426
- name: string,
427
- event: 'create' | 'destroy' = 'create',
428
- ) {
429
- if (!this.eventBus) {
430
- return Promise.resolve()
431
- }
432
- this.logger?.log(
433
- `[Invalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`,
434
- )
435
- return this.eventBus.emit(name, event)
436
- }
437
- }
@@ -1,202 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /* eslint-disable @typescript-eslint/no-empty-object-type */
3
- import type { z, ZodObject, ZodOptional } from 'zod/v4'
4
-
5
- import type {
6
- AnyInjectableType,
7
- InjectionTokenSchemaType,
8
- } from '../../token/injection-token.mjs'
9
- import type { IContainer } from '../../interfaces/container.interface.mjs'
10
- import type { Registry } from '../../token/registry.mjs'
11
- import type { ScopedContainer } from '../../container/scoped-container.mjs'
12
- import type { ClearAllOptions } from './invalidator.mjs'
13
- import type { Injectors } from '../../utils/index.mjs'
14
-
15
- import { defaultInjectors } from '../../injectors.mjs'
16
- import { InstanceResolver } from './instance-resolver.mjs'
17
- import { globalRegistry } from '../../token/registry.mjs'
18
- import { Instantiator } from './instantiator.mjs'
19
- import { Invalidator } from './invalidator.mjs'
20
- import { LifecycleEventBus } from '../lifecycle/lifecycle-event-bus.mjs'
21
- import { HolderManager } from '../holder/holder-manager.mjs'
22
- import { TokenProcessor } from './token-processor.mjs'
23
-
24
- /**
25
- * Core DI engine that coordinates service instantiation, resolution, and lifecycle.
26
- *
27
- * Acts as the central orchestrator for dependency injection operations,
28
- * delegating to specialized components (InstanceResolver, Instantiator, Invalidator)
29
- * for specific tasks.
30
- */
31
- export class ServiceLocator {
32
- private readonly eventBus: LifecycleEventBus
33
- private readonly manager: HolderManager
34
- private readonly instantiator: Instantiator
35
- private readonly tokenProcessor: TokenProcessor
36
- private readonly invalidator: Invalidator
37
- private readonly instanceResolver: InstanceResolver
38
-
39
- constructor(
40
- private readonly registry: Registry = globalRegistry,
41
- private readonly logger: Console | null = null,
42
- private readonly injectors: Injectors = defaultInjectors,
43
- ) {
44
- this.eventBus = new LifecycleEventBus(logger)
45
- this.manager = new HolderManager(logger)
46
- this.instantiator = new Instantiator(injectors)
47
- this.tokenProcessor = new TokenProcessor(logger)
48
- this.invalidator = new Invalidator(
49
- this.manager,
50
- this.eventBus,
51
- logger,
52
- )
53
- this.instanceResolver = new InstanceResolver(
54
- this.registry,
55
- this.manager,
56
- this.instantiator,
57
- this.tokenProcessor,
58
- logger,
59
- this,
60
- )
61
- }
62
-
63
- // ============================================================================
64
- // PUBLIC METHODS
65
- // ============================================================================
66
-
67
- getEventBus() {
68
- return this.eventBus
69
- }
70
-
71
- getManager() {
72
- return this.manager
73
- }
74
-
75
- getInvalidator() {
76
- return this.invalidator
77
- }
78
-
79
- getTokenProcessor() {
80
- return this.tokenProcessor
81
- }
82
-
83
- public getInstanceIdentifier(token: AnyInjectableType, args?: any): string {
84
- const [err, { actualToken, validatedArgs }] =
85
- this.tokenProcessor.validateAndResolveTokenArgs(token, args)
86
- if (err) {
87
- throw err
88
- }
89
- return this.tokenProcessor.generateInstanceName(actualToken, validatedArgs)
90
- }
91
-
92
- /**
93
- * Gets or creates an instance for the given token.
94
- * @param token The injection token
95
- * @param args Optional arguments
96
- * @param contextContainer The container to use for creating FactoryContext
97
- */
98
- public async getInstance(
99
- token: AnyInjectableType,
100
- args: any,
101
- contextContainer: IContainer,
102
- ) {
103
- const [err, data] = await this.instanceResolver.resolveInstance(
104
- token,
105
- args,
106
- contextContainer,
107
- )
108
- if (err) {
109
- return [err]
110
- }
111
-
112
- return [undefined, data]
113
- }
114
-
115
- /**
116
- * Gets or throws an instance for the given token.
117
- * @param token The injection token
118
- * @param args Optional arguments
119
- * @param contextContainer The container to use for creating FactoryContext
120
- */
121
- public async getOrThrowInstance<Instance>(
122
- token: AnyInjectableType,
123
- args: any,
124
- contextContainer: IContainer,
125
- ): Promise<Instance> {
126
- const [error, instance] = await this.getInstance(token, args, contextContainer)
127
- if (error) {
128
- throw error
129
- }
130
- return instance
131
- }
132
-
133
- /**
134
- * Resolves a request-scoped service for a ScopedContainer.
135
- * The service will be stored in the ScopedContainer's request context.
136
- *
137
- * @param token The injection token
138
- * @param args Optional arguments
139
- * @param scopedContainer The ScopedContainer that owns the request context
140
- */
141
- public async resolveRequestScoped(
142
- token: AnyInjectableType,
143
- args: any,
144
- scopedContainer: ScopedContainer,
145
- ): Promise<any> {
146
- const [err, data] = await this.instanceResolver.resolveRequestScopedInstance(
147
- token,
148
- args,
149
- scopedContainer,
150
- )
151
- if (err) {
152
- throw err
153
- }
154
- return data
155
- }
156
-
157
- public getSyncInstance<
158
- Instance,
159
- Schema extends InjectionTokenSchemaType | undefined,
160
- >(
161
- token: AnyInjectableType,
162
- args: Schema extends ZodObject
163
- ? z.input<Schema>
164
- : Schema extends ZodOptional<ZodObject>
165
- ? z.input<Schema> | undefined
166
- : undefined,
167
- contextContainer: IContainer,
168
- ): Instance | null {
169
- return this.instanceResolver.getSyncInstance(token, args as any, contextContainer)
170
- }
171
-
172
- invalidate(service: string, round = 1): Promise<any> {
173
- return this.invalidator.invalidate(service, round)
174
- }
175
-
176
- /**
177
- * Gracefully clears all services in the ServiceLocator using invalidation logic.
178
- * This method respects service dependencies and ensures proper cleanup order.
179
- * Services that depend on others will be invalidated first, then their dependencies.
180
- *
181
- * @param options Optional configuration for the clearing process
182
- * @returns Promise that resolves when all services have been cleared
183
- */
184
- async clearAll(options: ClearAllOptions = {}): Promise<void> {
185
- return this.invalidator.clearAll(options)
186
- }
187
-
188
- /**
189
- * Waits for all services to settle (either created, destroyed, or error state).
190
- */
191
- async ready(): Promise<void> {
192
- return this.invalidator.ready()
193
- }
194
-
195
- /**
196
- * Helper method for InstanceResolver to generate instance names.
197
- * This is needed for the factory context creation.
198
- */
199
- generateInstanceName(token: any, args: any): string {
200
- return this.tokenProcessor.generateInstanceName(token, args)
201
- }
202
- }