@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,598 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+
3
+ import { InjectableScope, InjectableType } from '../enums/index.mjs'
4
+ import { NameResolver } from '../internal/core/name-resolver.mjs'
5
+ import { ScopeTracker } from '../internal/core/scope-tracker.mjs'
6
+ import { InstanceStatus } from '../internal/holder/instance-holder.mjs'
7
+ import { UnifiedStorage } from '../internal/holder/unified-storage.mjs'
8
+ import { InjectionToken } from '../token/injection-token.mjs'
9
+ import { Registry } from '../token/registry.mjs'
10
+
11
+ describe('ScopeTracker', () => {
12
+ let registry: Registry
13
+ let nameResolver: NameResolver
14
+ let scopeTracker: ScopeTracker
15
+ let singletonStorage: UnifiedStorage
16
+ let requestStorage: UnifiedStorage
17
+
18
+ beforeEach(() => {
19
+ registry = new Registry()
20
+ nameResolver = new NameResolver()
21
+ scopeTracker = new ScopeTracker(registry, nameResolver)
22
+ singletonStorage = new UnifiedStorage(InjectableScope.Singleton)
23
+ requestStorage = new UnifiedStorage(InjectableScope.Request)
24
+ })
25
+
26
+ describe('checkAndUpgradeScope', () => {
27
+ describe('when conditions are NOT met for upgrade', () => {
28
+ it('should return [false] when current service is NOT Singleton', () => {
29
+ const token = InjectionToken.create<any>('TestService')
30
+ registry.set(
31
+ token,
32
+ InjectableScope.Request,
33
+ class {},
34
+ InjectableType.Class,
35
+ )
36
+
37
+ const result = scopeTracker.checkAndUpgradeScope(
38
+ 'TestService',
39
+ InjectableScope.Request, // Not Singleton
40
+ 'DependencyService',
41
+ InjectableScope.Request,
42
+ token,
43
+ singletonStorage,
44
+ requestStorage,
45
+ 'request-1',
46
+ )
47
+
48
+ expect(result).toEqual([false])
49
+ })
50
+
51
+ it('should return [false] when dependency is NOT Request-scoped', () => {
52
+ const token = InjectionToken.create<any>('TestService')
53
+ registry.set(
54
+ token,
55
+ InjectableScope.Singleton,
56
+ class {},
57
+ InjectableType.Class,
58
+ )
59
+
60
+ const result = scopeTracker.checkAndUpgradeScope(
61
+ 'TestService',
62
+ InjectableScope.Singleton,
63
+ 'DependencyService',
64
+ InjectableScope.Singleton, // Not Request
65
+ token,
66
+ singletonStorage,
67
+ requestStorage,
68
+ 'request-1',
69
+ )
70
+
71
+ expect(result).toEqual([false])
72
+ })
73
+
74
+ it('should return [false] when dependency is Transient', () => {
75
+ const token = InjectionToken.create<any>('TestService')
76
+ registry.set(
77
+ token,
78
+ InjectableScope.Singleton,
79
+ class {},
80
+ InjectableType.Class,
81
+ )
82
+
83
+ const result = scopeTracker.checkAndUpgradeScope(
84
+ 'TestService',
85
+ InjectableScope.Singleton,
86
+ 'DependencyService',
87
+ InjectableScope.Transient, // Transient, not Request
88
+ token,
89
+ singletonStorage,
90
+ requestStorage,
91
+ 'request-1',
92
+ )
93
+
94
+ expect(result).toEqual([false])
95
+ })
96
+
97
+ it('should return [false] when requestStorage is missing', () => {
98
+ const token = InjectionToken.create<any>('TestService')
99
+ registry.set(
100
+ token,
101
+ InjectableScope.Singleton,
102
+ class {},
103
+ InjectableType.Class,
104
+ )
105
+
106
+ const result = scopeTracker.checkAndUpgradeScope(
107
+ 'TestService',
108
+ InjectableScope.Singleton,
109
+ 'DependencyService',
110
+ InjectableScope.Request,
111
+ token,
112
+ singletonStorage,
113
+ undefined, // No request storage
114
+ 'request-1',
115
+ )
116
+
117
+ expect(result).toEqual([false])
118
+ })
119
+
120
+ it('should return [false] when requestId is missing', () => {
121
+ const token = InjectionToken.create<any>('TestService')
122
+ registry.set(
123
+ token,
124
+ InjectableScope.Singleton,
125
+ class {},
126
+ InjectableType.Class,
127
+ )
128
+
129
+ const result = scopeTracker.checkAndUpgradeScope(
130
+ 'TestService',
131
+ InjectableScope.Singleton,
132
+ 'DependencyService',
133
+ InjectableScope.Request,
134
+ token,
135
+ singletonStorage,
136
+ requestStorage,
137
+ undefined, // No requestId
138
+ )
139
+
140
+ expect(result).toEqual([false])
141
+ })
142
+ })
143
+
144
+ describe('when conditions ARE met for upgrade', () => {
145
+ it('should upgrade scope when Singleton depends on Request-scoped', () => {
146
+ const token = InjectionToken.create<any>('TestService')
147
+ registry.set(
148
+ token,
149
+ InjectableScope.Singleton,
150
+ class {},
151
+ InjectableType.Class,
152
+ )
153
+
154
+ const serviceName = token.id
155
+ const requestId = 'request-1'
156
+
157
+ const result = scopeTracker.checkAndUpgradeScope(
158
+ serviceName,
159
+ InjectableScope.Singleton,
160
+ 'DependencyService',
161
+ InjectableScope.Request,
162
+ token,
163
+ singletonStorage,
164
+ requestStorage,
165
+ requestId,
166
+ )
167
+
168
+ expect(result[0]).toBe(true)
169
+ expect(result[1]).toBeDefined()
170
+ expect(result[1]).toContain(`requestId=${requestId}`)
171
+ })
172
+
173
+ it('should update registry scope to Request', () => {
174
+ const token = InjectionToken.create<any>('TestService')
175
+ registry.set(
176
+ token,
177
+ InjectableScope.Singleton,
178
+ class {},
179
+ InjectableType.Class,
180
+ )
181
+
182
+ expect(registry.get(token).scope).toBe(InjectableScope.Singleton)
183
+
184
+ scopeTracker.checkAndUpgradeScope(
185
+ token.id,
186
+ InjectableScope.Singleton,
187
+ 'DependencyService',
188
+ InjectableScope.Request,
189
+ token,
190
+ singletonStorage,
191
+ requestStorage,
192
+ 'request-1',
193
+ )
194
+
195
+ expect(registry.get(token).scope).toBe(InjectableScope.Request)
196
+ })
197
+
198
+ it('should move existing holder from singleton to request storage', () => {
199
+ const token = InjectionToken.create<any>('TestService')
200
+ registry.set(
201
+ token,
202
+ InjectableScope.Singleton,
203
+ class {},
204
+ InjectableType.Class,
205
+ )
206
+
207
+ const serviceName = token.id
208
+ const requestId = 'request-1'
209
+
210
+ // Create a holder in singleton storage
211
+ const [, holder] = singletonStorage.createHolder(
212
+ serviceName,
213
+ InjectableType.Class,
214
+ new Set(),
215
+ )
216
+ holder.status = InstanceStatus.Created
217
+ holder.instance = { value: 'test' }
218
+ singletonStorage.set(serviceName, holder)
219
+
220
+ // Verify holder is in singleton storage
221
+ expect(singletonStorage.get(serviceName)).not.toBeNull()
222
+
223
+ const [success, newName] = scopeTracker.checkAndUpgradeScope(
224
+ serviceName,
225
+ InjectableScope.Singleton,
226
+ 'DependencyService',
227
+ InjectableScope.Request,
228
+ token,
229
+ singletonStorage,
230
+ requestStorage,
231
+ requestId,
232
+ )
233
+
234
+ expect(success).toBe(true)
235
+ expect(newName).toBeDefined()
236
+
237
+ // Holder should be removed from singleton storage
238
+ expect(singletonStorage.get(serviceName)).toBeNull()
239
+
240
+ // Holder should be in request storage with new name
241
+ const requestResult = requestStorage.get(newName!)
242
+ expect(requestResult).not.toBeNull()
243
+ expect(requestResult![1]?.instance).toEqual({ value: 'test' })
244
+ })
245
+
246
+ it('should preserve holder data during move', () => {
247
+ const token = InjectionToken.create<any>('TestService')
248
+ registry.set(
249
+ token,
250
+ InjectableScope.Singleton,
251
+ class {},
252
+ InjectableType.Class,
253
+ )
254
+
255
+ const serviceName = token.id
256
+ const requestId = 'request-1'
257
+
258
+ // Create a holder with dependencies
259
+ const deps = new Set(['dep1', 'dep2'])
260
+ const [, holder] = singletonStorage.createHolder(
261
+ serviceName,
262
+ InjectableType.Class,
263
+ deps,
264
+ )
265
+ holder.status = InstanceStatus.Created
266
+ holder.instance = { value: 'test' }
267
+ holder.destroyListeners = [() => {}, () => {}]
268
+ singletonStorage.set(serviceName, holder)
269
+
270
+ const [success, newName] = scopeTracker.checkAndUpgradeScope(
271
+ serviceName,
272
+ InjectableScope.Singleton,
273
+ 'DependencyService',
274
+ InjectableScope.Request,
275
+ token,
276
+ singletonStorage,
277
+ requestStorage,
278
+ requestId,
279
+ )
280
+
281
+ expect(success).toBe(true)
282
+
283
+ const requestResult = requestStorage.get(newName!)
284
+ const movedHolder = requestResult![1]!
285
+
286
+ expect(movedHolder.instance).toEqual({ value: 'test' })
287
+ expect(movedHolder.destroyListeners).toHaveLength(2)
288
+ expect(movedHolder.name).toBe(newName)
289
+ })
290
+
291
+ it('should handle holder in Creating state', () => {
292
+ const token = InjectionToken.create<any>('TestService')
293
+ registry.set(
294
+ token,
295
+ InjectableScope.Singleton,
296
+ class {},
297
+ InjectableType.Class,
298
+ )
299
+
300
+ const serviceName = token.id
301
+ const requestId = 'request-1'
302
+
303
+ // Create a holder in Creating state
304
+ const [, holder] = singletonStorage.createHolder(
305
+ serviceName,
306
+ InjectableType.Class,
307
+ new Set(),
308
+ )
309
+ // Leave status as Creating (default)
310
+ singletonStorage.set(serviceName, holder)
311
+
312
+ const [success, newName] = scopeTracker.checkAndUpgradeScope(
313
+ serviceName,
314
+ InjectableScope.Singleton,
315
+ 'DependencyService',
316
+ InjectableScope.Request,
317
+ token,
318
+ singletonStorage,
319
+ requestStorage,
320
+ requestId,
321
+ )
322
+
323
+ expect(success).toBe(true)
324
+ expect(newName).toBeDefined()
325
+
326
+ // Should still move the holder
327
+ expect(singletonStorage.get(serviceName)).toBeNull()
328
+ expect(requestStorage.get(newName!)).not.toBeNull()
329
+ })
330
+ })
331
+
332
+ describe('name generation', () => {
333
+ it('should generate correct name for simple token', () => {
334
+ const token = InjectionToken.create<any>('TestService')
335
+ registry.set(
336
+ token,
337
+ InjectableScope.Singleton,
338
+ class {},
339
+ InjectableType.Class,
340
+ )
341
+
342
+ const serviceName = token.id
343
+ const requestId = 'request-1'
344
+
345
+ const [success, newName] = scopeTracker.checkAndUpgradeScope(
346
+ serviceName,
347
+ InjectableScope.Singleton,
348
+ 'DependencyService',
349
+ InjectableScope.Request,
350
+ token,
351
+ singletonStorage,
352
+ requestStorage,
353
+ requestId,
354
+ )
355
+
356
+ expect(success).toBe(true)
357
+ expect(newName).toBe(`${serviceName}:requestId=${requestId}`)
358
+ })
359
+
360
+ it('should preserve args hash in name when upgrading', () => {
361
+ const token = InjectionToken.create<any>('TestService')
362
+ registry.set(
363
+ token,
364
+ InjectableScope.Singleton,
365
+ class {},
366
+ InjectableType.Class,
367
+ )
368
+
369
+ const serviceName = `${token.id}:abc123` // Token with args hash
370
+ const requestId = 'request-1'
371
+
372
+ const [success, newName] = scopeTracker.checkAndUpgradeScope(
373
+ serviceName,
374
+ InjectableScope.Singleton,
375
+ 'DependencyService',
376
+ InjectableScope.Request,
377
+ token,
378
+ singletonStorage,
379
+ requestStorage,
380
+ requestId,
381
+ )
382
+
383
+ expect(success).toBe(true)
384
+ expect(newName).toBe(`${token.id}:requestId=${requestId}:abc123`)
385
+ })
386
+ })
387
+ })
388
+
389
+ describe('upgradeScopeToRequest (async)', () => {
390
+ it('should return success and new name on successful upgrade', async () => {
391
+ const token = InjectionToken.create<any>('TestService')
392
+ registry.set(
393
+ token,
394
+ InjectableScope.Singleton,
395
+ class {},
396
+ InjectableType.Class,
397
+ )
398
+
399
+ const serviceName = token.id
400
+ const requestId = 'request-1'
401
+
402
+ const [success, newName, error] =
403
+ await scopeTracker.upgradeScopeToRequest(
404
+ serviceName,
405
+ token,
406
+ singletonStorage,
407
+ requestStorage,
408
+ requestId,
409
+ )
410
+
411
+ expect(success).toBe(true)
412
+ expect(newName).toBeDefined()
413
+ expect(error).toBeUndefined()
414
+ })
415
+
416
+ it('should return error when registry update fails', async () => {
417
+ const token = InjectionToken.create<any>('NonExistentService')
418
+ // Don't register the token
419
+
420
+ const [success, newName, error] =
421
+ await scopeTracker.upgradeScopeToRequest(
422
+ 'NonExistentService',
423
+ token,
424
+ singletonStorage,
425
+ requestStorage,
426
+ 'request-1',
427
+ )
428
+
429
+ expect(success).toBe(false)
430
+ expect(newName).toBeUndefined()
431
+ expect(error).toBeDefined()
432
+ })
433
+ })
434
+
435
+ describe('updateParentDependencies', () => {
436
+ it('should update dependency references in singleton storage', () => {
437
+ const oldName = 'OldServiceName'
438
+ const newName = 'NewServiceName:requestId=req-1'
439
+
440
+ // Create a holder that depends on the old name
441
+ const [, parentHolder] = singletonStorage.createHolder(
442
+ 'ParentService',
443
+ InjectableType.Class,
444
+ new Set([oldName]),
445
+ )
446
+ singletonStorage.set('ParentService', parentHolder)
447
+
448
+ scopeTracker.updateParentDependencies(
449
+ oldName,
450
+ newName,
451
+ singletonStorage,
452
+ requestStorage,
453
+ )
454
+
455
+ // Check that parent's deps are updated
456
+ const updatedResult = singletonStorage.get('ParentService')
457
+ expect(updatedResult).not.toBeNull()
458
+ expect(updatedResult![1]!.deps.has(oldName)).toBe(false)
459
+ expect(updatedResult![1]!.deps.has(newName)).toBe(true)
460
+ })
461
+
462
+ it('should update dependency references in request storage', () => {
463
+ const oldName = 'OldServiceName'
464
+ const newName = 'NewServiceName:requestId=req-1'
465
+
466
+ // Create a holder in request storage that depends on the old name
467
+ const [, parentHolder] = requestStorage.createHolder(
468
+ 'ParentService:requestId=req-1',
469
+ InjectableType.Class,
470
+ new Set([oldName]),
471
+ )
472
+ requestStorage.set('ParentService:requestId=req-1', parentHolder)
473
+
474
+ scopeTracker.updateParentDependencies(
475
+ oldName,
476
+ newName,
477
+ singletonStorage,
478
+ requestStorage,
479
+ )
480
+
481
+ // Check that parent's deps are updated
482
+ const updatedResult = requestStorage.get('ParentService:requestId=req-1')
483
+ expect(updatedResult).not.toBeNull()
484
+ expect(updatedResult![1]!.deps.has(oldName)).toBe(false)
485
+ expect(updatedResult![1]!.deps.has(newName)).toBe(true)
486
+ })
487
+
488
+ it('should update dependencies in both storages', () => {
489
+ const oldName = 'OldServiceName'
490
+ const newName = 'NewServiceName:requestId=req-1'
491
+
492
+ // Create holders in both storages
493
+ const [, singletonHolder] = singletonStorage.createHolder(
494
+ 'SingletonParent',
495
+ InjectableType.Class,
496
+ new Set([oldName]),
497
+ )
498
+ singletonStorage.set('SingletonParent', singletonHolder)
499
+
500
+ const [, requestHolder] = requestStorage.createHolder(
501
+ 'RequestParent:requestId=req-1',
502
+ InjectableType.Class,
503
+ new Set([oldName]),
504
+ )
505
+ requestStorage.set('RequestParent:requestId=req-1', requestHolder)
506
+
507
+ scopeTracker.updateParentDependencies(
508
+ oldName,
509
+ newName,
510
+ singletonStorage,
511
+ requestStorage,
512
+ )
513
+
514
+ // Both should be updated
515
+ const singletonResult = singletonStorage.get('SingletonParent')
516
+ expect(singletonResult![1]!.deps.has(newName)).toBe(true)
517
+
518
+ const requestResult = requestStorage.get('RequestParent:requestId=req-1')
519
+ expect(requestResult![1]!.deps.has(newName)).toBe(true)
520
+ })
521
+ })
522
+
523
+ describe('logging', () => {
524
+ it('should log when scope upgrade occurs', () => {
525
+ const mockLogger = {
526
+ log: vi.fn(),
527
+ warn: vi.fn(),
528
+ error: vi.fn(),
529
+ } as unknown as Console
530
+
531
+ const trackerWithLogger = new ScopeTracker(
532
+ registry,
533
+ nameResolver,
534
+ mockLogger,
535
+ )
536
+
537
+ const token = InjectionToken.create<any>('TestService')
538
+ registry.set(
539
+ token,
540
+ InjectableScope.Singleton,
541
+ class {},
542
+ InjectableType.Class,
543
+ )
544
+
545
+ trackerWithLogger.checkAndUpgradeScope(
546
+ token.id,
547
+ InjectableScope.Singleton,
548
+ 'DependencyService',
549
+ InjectableScope.Request,
550
+ token,
551
+ singletonStorage,
552
+ requestStorage,
553
+ 'request-1',
554
+ )
555
+
556
+ expect(mockLogger.log).toHaveBeenCalledWith(
557
+ expect.stringContaining('Upgrading'),
558
+ )
559
+ })
560
+
561
+ it('should warn when requestStorage is missing', () => {
562
+ const mockLogger = {
563
+ log: vi.fn(),
564
+ warn: vi.fn(),
565
+ error: vi.fn(),
566
+ } as unknown as Console
567
+
568
+ const trackerWithLogger = new ScopeTracker(
569
+ registry,
570
+ nameResolver,
571
+ mockLogger,
572
+ )
573
+
574
+ const token = InjectionToken.create<any>('TestService')
575
+ registry.set(
576
+ token,
577
+ InjectableScope.Singleton,
578
+ class {},
579
+ InjectableType.Class,
580
+ )
581
+
582
+ trackerWithLogger.checkAndUpgradeScope(
583
+ token.id,
584
+ InjectableScope.Singleton,
585
+ 'DependencyService',
586
+ InjectableScope.Request,
587
+ token,
588
+ singletonStorage,
589
+ undefined, // Missing
590
+ 'request-1',
591
+ )
592
+
593
+ expect(mockLogger.warn).toHaveBeenCalledWith(
594
+ expect.stringContaining('Cannot upgrade scope'),
595
+ )
596
+ })
597
+ })
598
+ })