@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
@@ -1,252 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /* eslint-disable @typescript-eslint/no-empty-object-type */
3
-
4
- import type { FactoryContext } from '../context/factory-context.mjs'
5
- import type {
6
- AnyInjectableType,
7
- InjectionTokenType,
8
- } from '../../token/injection-token.mjs'
9
- import type { IContainer } from '../../interfaces/container.interface.mjs'
10
-
11
- import { DIError } from '../../errors/index.mjs'
12
- import {
13
- BoundInjectionToken,
14
- FactoryInjectionToken,
15
- InjectionToken,
16
- } from '../../token/injection-token.mjs'
17
- import { getInjectableToken } from '../../utils/index.mjs'
18
-
19
- /**
20
- * Simple LRU cache for instance name generation.
21
- * Uses a Map which maintains insertion order for efficient LRU eviction.
22
- */
23
- class InstanceNameCache {
24
- private readonly cache = new Map<string, string>()
25
- private readonly maxSize: number
26
-
27
- constructor(maxSize = 1000) {
28
- this.maxSize = maxSize
29
- }
30
-
31
- get(key: string): string | undefined {
32
- const value = this.cache.get(key)
33
- if (value !== undefined) {
34
- // Move to end (most recently used)
35
- this.cache.delete(key)
36
- this.cache.set(key, value)
37
- }
38
- return value
39
- }
40
-
41
- set(key: string, value: string): void {
42
- if (this.cache.has(key)) {
43
- this.cache.delete(key)
44
- } else if (this.cache.size >= this.maxSize) {
45
- // Remove least recently used (first item)
46
- const firstKey = this.cache.keys().next().value
47
- if (firstKey !== undefined) {
48
- this.cache.delete(firstKey)
49
- }
50
- }
51
- this.cache.set(key, value)
52
- }
53
-
54
- clear(): void {
55
- this.cache.clear()
56
- }
57
- }
58
-
59
- /**
60
- * Handles token validation, normalization, and instance name generation.
61
- *
62
- * Provides utilities for resolving tokens to their underlying InjectionToken,
63
- * validating arguments against schemas, and generating unique instance identifiers.
64
- */
65
- export class TokenProcessor {
66
- private readonly instanceNameCache = new InstanceNameCache()
67
-
68
- constructor(private readonly logger: Console | null = null) {}
69
-
70
- // ============================================================================
71
- // TOKEN NORMALIZATION
72
- // ============================================================================
73
-
74
- /**
75
- * Normalizes a token to an InjectionToken.
76
- * Handles class constructors by getting their injectable token.
77
- *
78
- * @param token A class constructor, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
79
- * @returns The normalized InjectionTokenType
80
- */
81
- normalizeToken(token: AnyInjectableType): InjectionTokenType {
82
- if (typeof token === 'function') {
83
- return getInjectableToken(token)
84
- }
85
- return token as InjectionTokenType
86
- }
87
-
88
- /**
89
- * Gets the underlying "real" token from wrapped tokens.
90
- * For BoundInjectionToken and FactoryInjectionToken, returns the wrapped token.
91
- * For other tokens, returns the token itself.
92
- *
93
- * @param token The token to unwrap
94
- * @returns The underlying InjectionToken
95
- */
96
- getRealToken<T = unknown>(token: InjectionTokenType): InjectionToken<T> {
97
- if (
98
- token instanceof BoundInjectionToken ||
99
- token instanceof FactoryInjectionToken
100
- ) {
101
- return token.token as InjectionToken<T>
102
- }
103
- return token as InjectionToken<T>
104
- }
105
-
106
- /**
107
- * Convenience method that normalizes a token and then gets the real token.
108
- * Useful for checking registry entries where you need the actual registered token.
109
- *
110
- * @param token Any injectable type
111
- * @returns The underlying InjectionToken
112
- */
113
- getRegistryToken<T = unknown>(token: AnyInjectableType): InjectionToken<T> {
114
- return this.getRealToken(this.normalizeToken(token))
115
- }
116
-
117
- // ============================================================================
118
- // TOKEN VALIDATION
119
- // ============================================================================
120
-
121
- /**
122
- * Validates and resolves token arguments, handling factory token resolution and validation.
123
- */
124
- validateAndResolveTokenArgs(
125
- token: AnyInjectableType,
126
- args?: any,
127
- ): [
128
- DIError | undefined,
129
- { actualToken: InjectionTokenType; validatedArgs?: any },
130
- ] {
131
- let actualToken = token as InjectionToken<any, any>
132
- if (typeof token === 'function') {
133
- actualToken = getInjectableToken(token)
134
- }
135
- let realArgs = args
136
- if (actualToken instanceof BoundInjectionToken) {
137
- realArgs = actualToken.value
138
- } else if (actualToken instanceof FactoryInjectionToken) {
139
- if (actualToken.resolved) {
140
- realArgs = actualToken.value
141
- } else {
142
- return [DIError.factoryTokenNotResolved(token.name), { actualToken }]
143
- }
144
- }
145
- if (!actualToken.schema) {
146
- return [undefined, { actualToken, validatedArgs: realArgs }]
147
- }
148
- const validatedArgs = actualToken.schema?.safeParse(realArgs)
149
- if (validatedArgs && !validatedArgs.success) {
150
- this.logger?.error(
151
- `[TokenProcessor]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`,
152
- validatedArgs.error,
153
- )
154
- return [DIError.unknown(validatedArgs.error), { actualToken }]
155
- }
156
- return [undefined, { actualToken, validatedArgs: validatedArgs?.data }]
157
- }
158
-
159
- /**
160
- * Generates a unique instance name based on token and arguments.
161
- * Results are cached using an LRU cache for performance.
162
- */
163
- generateInstanceName(token: InjectionTokenType, args: any): string {
164
- if (!args) {
165
- return token.toString()
166
- }
167
-
168
- // Create a cache key from token id and args
169
- const tokenStr = token.toString()
170
- const cacheKey = `${tokenStr}:${JSON.stringify(args)}`
171
-
172
- // Check cache first
173
- const cached = this.instanceNameCache.get(cacheKey)
174
- if (cached !== undefined) {
175
- return cached
176
- }
177
-
178
- // Generate the instance name
179
- const formattedArgs = Object.entries(args)
180
- .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
181
- .map(([key, value]) => `${key}=${this.formatArgValue(value)}`)
182
- .join(',')
183
-
184
- const result = `${tokenStr}:${formattedArgs.replaceAll(/"/g, '').replaceAll(/:/g, '=')}`
185
-
186
- // Cache the result
187
- this.instanceNameCache.set(cacheKey, result)
188
-
189
- return result
190
- }
191
-
192
- /**
193
- * Formats a single argument value for instance name generation.
194
- */
195
- formatArgValue(value: any): string {
196
- if (typeof value === 'function') {
197
- return `fn_${value.name}(${value.length})`
198
- }
199
- if (typeof value === 'symbol') {
200
- return value.toString()
201
- }
202
- return JSON.stringify(value).slice(0, 40)
203
- }
204
-
205
- /**
206
- * Creates a factory context for dependency injection during service instantiation.
207
- * @param container The container instance (Container or ScopedContainer) for dependency resolution
208
- * @param onDependencyResolved Callback when a dependency is resolved, receives the instance name
209
- */
210
- createFactoryContext(
211
- container: IContainer,
212
- onDependencyResolved?: (instanceName: string) => void,
213
- ): FactoryContext & {
214
- getDestroyListeners: () => (() => void)[]
215
- deps: Set<string>
216
- } {
217
- const destroyListeners = new Set<() => void>()
218
- const deps = new Set<string>()
219
-
220
- function addDestroyListener(listener: () => void) {
221
- destroyListeners.add(listener)
222
- }
223
-
224
- function getDestroyListeners() {
225
- return Array.from(destroyListeners)
226
- }
227
-
228
- const self = this
229
-
230
- return {
231
- // @ts-expect-error This is correct type
232
- async inject(token, args) {
233
- // Get the instance name for dependency tracking
234
- const actualToken =
235
- typeof token === 'function' ? getInjectableToken(token) : token
236
- const instanceName = self.generateInstanceName(actualToken, args)
237
- deps.add(instanceName)
238
-
239
- if (onDependencyResolved) {
240
- onDependencyResolved(instanceName)
241
- }
242
-
243
- // Use the container's get method for resolution
244
- return container.get(token, args)
245
- },
246
- addDestroyListener,
247
- getDestroyListeners,
248
- container,
249
- deps,
250
- }
251
- }
252
- }
@@ -1,334 +0,0 @@
1
- import type { InstanceHolder } from './instance-holder.mjs'
2
-
3
- import { InjectableScope, InjectableType } from '../../enums/index.mjs'
4
- import { DIError } from '../../errors/index.mjs'
5
- import { CircularDetector } from '../lifecycle/circular-detector.mjs'
6
- import { InstanceStatus } from './instance-holder.mjs'
7
-
8
- /**
9
- * Result type for waitForHolderReady.
10
- * Returns either [undefined, holder] on success or [error] on failure.
11
- */
12
- export type HolderReadyResult<T> = [undefined, InstanceHolder<T>] | [DIError]
13
-
14
- /**
15
- * Abstract base class providing common functionality for managing InstanceHolder objects.
16
- *
17
- * Provides shared patterns for holder storage, creation, and lifecycle management
18
- * used by both singleton (HolderManager) and request-scoped (RequestContext) managers.
19
- */
20
- export abstract class BaseHolderManager {
21
- protected readonly _holders: Map<string, InstanceHolder>
22
- /**
23
- * Reverse dependency index: maps a dependency name to the set of holder names that depend on it.
24
- * This allows O(1) lookup of dependents instead of O(n) iteration.
25
- */
26
- protected readonly _dependents: Map<string, Set<string>>
27
-
28
- constructor(protected readonly logger: Console | null = null) {
29
- this._holders = new Map()
30
- this._dependents = new Map()
31
- }
32
-
33
- /**
34
- * Protected getter for accessing the holders map from subclasses.
35
- */
36
- protected get holders(): Map<string, InstanceHolder> {
37
- return this._holders
38
- }
39
-
40
- /**
41
- * Abstract method to get a holder by name. Each implementation defines its own return type
42
- * based on their specific error handling and validation needs.
43
- */
44
- abstract get(name: string): any
45
-
46
- /**
47
- * Abstract method to set a holder by name. Each implementation may have different validation logic.
48
- */
49
- abstract set(name: string, holder: InstanceHolder): void
50
-
51
- /**
52
- * Abstract method to check if a holder exists. Each implementation may have different validation logic.
53
- */
54
- abstract has(name: string): any
55
-
56
- /**
57
- * Deletes a holder by name and cleans up the reverse dependency index.
58
- * @param name The name of the holder to delete
59
- * @returns true if the holder was deleted, false if it didn't exist
60
- */
61
- delete(name: string): boolean {
62
- const holder = this._holders.get(name)
63
- if (holder) {
64
- // Remove this holder from the reverse index for all its dependencies
65
- this.removeFromDependentsIndex(name, holder.deps)
66
- }
67
- return this._holders.delete(name)
68
- }
69
-
70
- /**
71
- * Registers a holder's dependencies in the reverse index.
72
- * Call this after creating a holder with dependencies.
73
- * @param holderName The name of the holder that has dependencies
74
- * @param deps The set of dependency names
75
- */
76
- registerDependencies(holderName: string, deps: Set<string>): void {
77
- for (const dep of deps) {
78
- let dependents = this._dependents.get(dep)
79
- if (!dependents) {
80
- dependents = new Set()
81
- this._dependents.set(dep, dependents)
82
- }
83
- dependents.add(holderName)
84
- }
85
- }
86
-
87
- /**
88
- * Removes a holder from the reverse dependency index.
89
- * @param holderName The name of the holder to remove
90
- * @param deps The set of dependency names to clean up
91
- */
92
- protected removeFromDependentsIndex(holderName: string, deps: Set<string>): void {
93
- for (const dep of deps) {
94
- const dependents = this._dependents.get(dep)
95
- if (dependents) {
96
- dependents.delete(holderName)
97
- if (dependents.size === 0) {
98
- this._dependents.delete(dep)
99
- }
100
- }
101
- }
102
- }
103
-
104
- /**
105
- * Gets all holder names that depend on the given instance name.
106
- * O(1) lookup using the reverse dependency index.
107
- * @param instanceName The instance name to find dependents for
108
- * @returns Array of holder names that depend on this instance
109
- */
110
- getDependents(instanceName: string): string[] {
111
- const dependents = this._dependents.get(instanceName)
112
- return dependents ? Array.from(dependents) : []
113
- }
114
-
115
- /**
116
- * Filters holders based on a predicate function.
117
- * @param predicate Function to test each holder
118
- * @returns A new Map containing only the holders that match the predicate
119
- * @deprecated Use forEachHolder() for iteration to avoid allocations
120
- */
121
- filter(
122
- predicate: (value: InstanceHolder<any>, key: string) => boolean,
123
- ): Map<string, InstanceHolder> {
124
- const result = new Map<string, InstanceHolder>()
125
- for (const [key, value] of this._holders) {
126
- if (predicate(value, key)) {
127
- result.set(key, value)
128
- }
129
- }
130
- return result
131
- }
132
-
133
- /**
134
- * Iterates over holders with a callback. More efficient than filter() as it
135
- * avoids creating intermediate arrays and Maps.
136
- * @param callback Function called for each holder with (holder, name)
137
- */
138
- forEachHolder(
139
- callback: (holder: InstanceHolder<any>, name: string) => void,
140
- ): void {
141
- for (const [name, holder] of this._holders) {
142
- callback(holder, name)
143
- }
144
- }
145
-
146
- /**
147
- * Finds the first holder matching a predicate. More efficient than filter()
148
- * when only one result is needed.
149
- * @param predicate Function to test each holder
150
- * @returns The first matching holder or undefined
151
- */
152
- findHolder(
153
- predicate: (holder: InstanceHolder<any>, name: string) => boolean,
154
- ): InstanceHolder | undefined {
155
- for (const [name, holder] of this._holders) {
156
- if (predicate(holder, name)) {
157
- return holder
158
- }
159
- }
160
- return undefined
161
- }
162
-
163
- /**
164
- * Clears all holders from this manager and the reverse dependency index.
165
- */
166
- clear(): void {
167
- this._holders.clear()
168
- this._dependents.clear()
169
- }
170
-
171
- /**
172
- * Gets the number of holders currently managed.
173
- */
174
- size(): number {
175
- return this._holders.size
176
- }
177
-
178
- /**
179
- * Creates a new holder with Creating status and a deferred creation promise.
180
- * This is useful for creating placeholder holders that can be fulfilled later.
181
- * @param name The name of the instance
182
- * @param type The injectable type
183
- * @param scope The injectable scope
184
- * @param deps Optional set of dependencies
185
- * @returns A tuple containing the deferred promise and the holder
186
- */
187
- createCreatingHolder<Instance>(
188
- name: string,
189
- type: InjectableType,
190
- scope: InjectableScope,
191
- deps: Set<string> = new Set(),
192
- ): [
193
- ReturnType<typeof Promise.withResolvers<[undefined, Instance]>>,
194
- InstanceHolder<Instance>,
195
- ] {
196
- const deferred = Promise.withResolvers<[undefined, Instance]>()
197
-
198
- const holder: InstanceHolder<Instance> = {
199
- status: InstanceStatus.Creating,
200
- name,
201
- instance: null,
202
- creationPromise: deferred.promise,
203
- destroyPromise: null,
204
- type,
205
- scope,
206
- deps,
207
- destroyListeners: [],
208
- createdAt: Date.now(),
209
- waitingFor: new Set(),
210
- }
211
-
212
- return [deferred, holder]
213
- }
214
-
215
- /**
216
- * Creates a new holder with Created status and an actual instance.
217
- * This is useful for creating holders that already have their instance ready.
218
- * @param name The name of the instance
219
- * @param instance The actual instance to store
220
- * @param type The injectable type
221
- * @param scope The injectable scope
222
- * @param deps Optional set of dependencies
223
- * @returns The created holder
224
- */
225
- protected createCreatedHolder<Instance>(
226
- name: string,
227
- instance: Instance,
228
- type: InjectableType,
229
- scope: InjectableScope,
230
- deps: Set<string> = new Set(),
231
- ): InstanceHolder<Instance> {
232
- const holder: InstanceHolder<Instance> = {
233
- status: InstanceStatus.Created,
234
- name,
235
- instance,
236
- creationPromise: null,
237
- destroyPromise: null,
238
- type,
239
- scope,
240
- deps,
241
- destroyListeners: [],
242
- createdAt: Date.now(),
243
- waitingFor: new Set(),
244
- }
245
-
246
- return holder
247
- }
248
-
249
- /**
250
- * Gets all holder names currently managed.
251
- */
252
- getAllNames(): string[] {
253
- return Array.from(this._holders.keys())
254
- }
255
-
256
- /**
257
- * Gets all holders currently managed.
258
- */
259
- getAllHolders(): InstanceHolder[] {
260
- return Array.from(this._holders.values())
261
- }
262
-
263
- /**
264
- * Checks if this manager has any holders.
265
- */
266
- isEmpty(): boolean {
267
- return this._holders.size === 0
268
- }
269
-
270
- /**
271
- * Waits for a holder to be ready and returns the appropriate result.
272
- * This is a shared utility used by both singleton and request-scoped resolution.
273
- *
274
- * @param holder The holder to wait for
275
- * @param waiterHolder Optional holder that is doing the waiting (for circular dependency detection)
276
- * @param getHolder Optional function to retrieve holders by name (required if waiterHolder is provided)
277
- * @returns A promise that resolves with [undefined, holder] on success or [DIError] on failure
278
- */
279
- static async waitForHolderReady<T>(
280
- holder: InstanceHolder<T>,
281
- waiterHolder?: InstanceHolder,
282
- getHolder?: (name: string) => InstanceHolder | undefined,
283
- ): Promise<HolderReadyResult<T>> {
284
- switch (holder.status) {
285
- case InstanceStatus.Creating: {
286
- // Check for circular dependency before waiting
287
- if (waiterHolder && getHolder) {
288
- const cycle = CircularDetector.detectCycle(
289
- waiterHolder.name,
290
- holder.name,
291
- getHolder,
292
- )
293
- if (cycle) {
294
- return [DIError.circularDependency(cycle)]
295
- }
296
-
297
- if (process.env.NODE_ENV !== 'production') {
298
- // Track the waiting relationship
299
- waiterHolder.waitingFor.add(holder.name)
300
- }
301
- }
302
-
303
- try {
304
- await holder.creationPromise
305
- } finally {
306
- if (process.env.NODE_ENV !== 'production') {
307
- // Clean up the waiting relationship
308
- if (waiterHolder) {
309
- waiterHolder.waitingFor.delete(holder.name)
310
- }
311
- }
312
- }
313
-
314
- return BaseHolderManager.waitForHolderReady(
315
- holder,
316
- waiterHolder,
317
- getHolder,
318
- )
319
- }
320
-
321
- case InstanceStatus.Destroying:
322
- return [DIError.instanceDestroying(holder.name)]
323
-
324
- case InstanceStatus.Error:
325
- return [holder.instance as unknown as DIError]
326
-
327
- case InstanceStatus.Created:
328
- return [undefined, holder]
329
-
330
- default:
331
- return [DIError.instanceNotFound('unknown')]
332
- }
333
- }
334
- }
@@ -1,85 +0,0 @@
1
- import type { InstanceHolder } from './instance-holder.mjs'
2
-
3
- import { InjectableScope, InjectableType } from '../../enums/index.mjs'
4
- import { DIError, DIErrorCode } from '../../errors/index.mjs'
5
- import { BaseHolderManager } from './base-holder-manager.mjs'
6
- import { InstanceStatus } from './instance-holder.mjs'
7
-
8
- /**
9
- * Manages the storage and retrieval of singleton instance holders.
10
- *
11
- * Provides CRUD operations and filtering for the holder map.
12
- * Handles holder state validation (destroying, error states) on retrieval.
13
- */
14
- export class HolderManager extends BaseHolderManager {
15
- constructor(logger: Console | null = null) {
16
- super(logger)
17
- }
18
-
19
- get(
20
- name: string,
21
- ): [DIError, InstanceHolder] | [DIError] | [undefined, InstanceHolder] {
22
- const holder = this._holders.get(name)
23
- if (holder) {
24
- if (holder.status === InstanceStatus.Destroying) {
25
- this.logger?.log(
26
- `[HolderManager]#get() Instance ${holder.name} is destroying`,
27
- )
28
- return [DIError.instanceDestroying(holder.name), holder]
29
- } else if (holder.status === InstanceStatus.Error) {
30
- this.logger?.log(
31
- `[HolderManager]#get() Instance ${holder.name} is in error state`,
32
- )
33
- return [holder.instance as unknown as DIError, holder]
34
- }
35
-
36
- return [undefined, holder]
37
- } else {
38
- this.logger?.log(`[HolderManager]#get() Instance ${name} not found`)
39
- return [DIError.instanceNotFound(name)]
40
- }
41
- }
42
-
43
- set(name: string, holder: InstanceHolder): void {
44
- this._holders.set(name, holder)
45
- }
46
-
47
- has(name: string): [DIError] | [undefined, boolean] {
48
- const [error, holder] = this.get(name)
49
- if (!error) {
50
- return [undefined, true]
51
- }
52
- if (error.code === DIErrorCode.InstanceDestroying) {
53
- return [error]
54
- }
55
- return [undefined, !!holder]
56
- }
57
-
58
- // delete and filter methods are inherited from BaseHolderManager
59
-
60
- // createCreatingHolder method is inherited from BaseHolderManager
61
-
62
- /**
63
- * Creates a new holder with Created status and stores it.
64
- * This is useful for creating holders that already have their instance ready.
65
- * @param name The name of the instance
66
- * @param instance The actual instance to store
67
- * @param type The injectable type
68
- * @param scope The injectable scope
69
- * @param deps Optional set of dependencies
70
- * @returns The created holder
71
- */
72
- storeCreatedHolder<Instance>(
73
- name: string,
74
- instance: Instance,
75
- type: InjectableType,
76
- scope: InjectableScope,
77
- deps: Set<string> = new Set(),
78
- ): InstanceHolder<Instance> {
79
- const holder = this.createCreatedHolder(name, instance, type, scope, deps)
80
-
81
- this._holders.set(name, holder)
82
-
83
- return holder
84
- }
85
- }