@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,335 +1,483 @@
1
- import { beforeEach, describe, expect, it } from 'vitest'
1
+ import { describe, expect, it, beforeEach } from 'vitest'
2
2
 
3
3
  import { InjectableScope, InjectableType } from '../enums/index.mjs'
4
4
  import { InjectionToken } from '../token/injection-token.mjs'
5
- import { globalRegistry, Registry } from '../token/registry.mjs'
6
-
7
- class TestService {}
8
- class AnotherService {}
5
+ import { Registry, globalRegistry } from '../token/registry.mjs'
9
6
 
10
7
  describe('Registry', () => {
11
8
  let registry: Registry
12
- let token1: InjectionToken<TestService, undefined>
13
- let token2: InjectionToken<AnotherService, undefined>
14
9
 
15
10
  beforeEach(() => {
16
11
  registry = new Registry()
17
- token1 = new InjectionToken<TestService, undefined>(
18
- 'TestService',
19
- undefined,
20
- )
21
- token2 = new InjectionToken<AnotherService, undefined>(
22
- 'AnotherService',
23
- undefined,
24
- )
25
12
  })
26
13
 
27
- describe('constructor', () => {
28
- it('should create registry without parent', () => {
29
- const reg = new Registry()
30
- expect(reg).toBeDefined()
14
+ describe('has', () => {
15
+ it('should return false for non-existent token', () => {
16
+ const token = InjectionToken.create<string>('test')
17
+ expect(registry.has(token)).toBe(false)
18
+ })
19
+
20
+ it('should return true for registered token', () => {
21
+ const token = InjectionToken.create<string>('test')
22
+ registry.set(token, InjectableScope.Singleton, class Test {}, InjectableType.Class)
23
+
24
+ expect(registry.has(token)).toBe(true)
31
25
  })
32
26
 
33
- it('should create registry with parent', () => {
27
+ it('should check parent registry', () => {
34
28
  const parentRegistry = new Registry()
35
29
  const childRegistry = new Registry(parentRegistry)
36
- expect(childRegistry).toBeDefined()
37
- })
38
- })
30
+ const token = InjectionToken.create<string>('test')
39
31
 
40
- describe('set', () => {
41
- it('should set factory record for token', () => {
42
- registry.set(
43
- token1,
32
+ parentRegistry.set(
33
+ token,
44
34
  InjectableScope.Singleton,
45
- TestService,
35
+ class Test {},
46
36
  InjectableType.Class,
47
37
  )
48
38
 
49
- expect(registry.has(token1)).toBe(true)
39
+ expect(childRegistry.has(token)).toBe(true)
50
40
  })
41
+ })
42
+
43
+ describe('get', () => {
44
+ it('should return factory record for registered token', () => {
45
+ const token = InjectionToken.create<string>('test')
46
+ class TestClass {}
51
47
 
52
- it('should set multiple factory records', () => {
53
48
  registry.set(
54
- token1,
49
+ token,
55
50
  InjectableScope.Singleton,
56
- TestService,
51
+ TestClass,
57
52
  InjectableType.Class,
53
+ 10,
58
54
  )
59
- registry.set(
60
- token2,
61
- InjectableScope.Transient,
62
- AnotherService,
63
- InjectableType.Factory,
64
- )
65
55
 
66
- expect(registry.has(token1)).toBe(true)
67
- expect(registry.has(token2)).toBe(true)
56
+ const record = registry.get(token)
57
+
58
+ expect(record.scope).toBe(InjectableScope.Singleton)
59
+ expect(record.target).toBe(TestClass)
60
+ expect(record.type).toBe(InjectableType.Class)
61
+ expect(record.priority).toBe(10)
62
+ expect(record.originalToken).toBe(token)
68
63
  })
69
64
 
70
- it('should overwrite existing factory record', () => {
71
- registry.set(
72
- token1,
73
- InjectableScope.Singleton,
74
- TestService,
75
- InjectableType.Class,
76
- )
77
- registry.set(
78
- token1,
79
- InjectableScope.Transient,
80
- TestService,
81
- InjectableType.Factory,
82
- )
65
+ it('should throw for non-existent token', () => {
66
+ const token = InjectionToken.create<string>('test')
83
67
 
84
- const record = registry.get(token1)
85
- expect(record.scope).toBe(InjectableScope.Transient)
86
- expect(record.type).toBe(InjectableType.Factory)
68
+ expect(() => registry.get(token)).toThrow()
87
69
  })
88
- })
89
70
 
90
- describe('has', () => {
91
- it('should return false for non-existent token', () => {
92
- expect(registry.has(token1)).toBe(false)
93
- })
71
+ it('should get from parent registry', () => {
72
+ const parentRegistry = new Registry()
73
+ const childRegistry = new Registry(parentRegistry)
74
+ const token = InjectionToken.create<string>('test')
75
+ class TestClass {}
94
76
 
95
- it('should return true for existing token', () => {
96
- registry.set(
97
- token1,
77
+ parentRegistry.set(
78
+ token,
98
79
  InjectableScope.Singleton,
99
- TestService,
80
+ TestClass,
100
81
  InjectableType.Class,
101
82
  )
102
- expect(registry.has(token1)).toBe(true)
83
+
84
+ const record = childRegistry.get(token)
85
+ expect(record.target).toBe(TestClass)
103
86
  })
104
87
 
105
- it('should check parent registry when token not found locally', () => {
88
+ it('should prefer child registry over parent', () => {
106
89
  const parentRegistry = new Registry()
107
90
  const childRegistry = new Registry(parentRegistry)
91
+ const token = InjectionToken.create<string>('test')
92
+ class ParentClass {}
93
+ class ChildClass {}
108
94
 
109
95
  parentRegistry.set(
110
- token1,
96
+ token,
97
+ InjectableScope.Singleton,
98
+ ParentClass,
99
+ InjectableType.Class,
100
+ )
101
+ childRegistry.set(
102
+ token,
111
103
  InjectableScope.Singleton,
112
- TestService,
104
+ ChildClass,
113
105
  InjectableType.Class,
106
+ 1, // Higher priority
114
107
  )
115
108
 
116
- expect(childRegistry.has(token1)).toBe(true)
109
+ const record = childRegistry.get(token)
110
+ expect(record.target).toBe(ChildClass)
117
111
  })
112
+ })
118
113
 
119
- it('should return false when token not found in parent chain', () => {
120
- const parentRegistry = new Registry()
121
- const childRegistry = new Registry(parentRegistry)
114
+ describe('getAll', () => {
115
+ it('should return empty array for non-existent token', () => {
116
+ const token = InjectionToken.create<string>('test')
117
+
118
+ const records = registry.getAll(token)
119
+ expect(records).toEqual([])
120
+ })
122
121
 
123
- expect(childRegistry.has(token1)).toBe(false)
122
+ it('should return all records sorted by priority (highest first)', () => {
123
+ const token = InjectionToken.create<string>('test')
124
+ class ClassA {}
125
+ class ClassB {}
126
+ class ClassC {}
127
+
128
+ registry.set(token, InjectableScope.Singleton, ClassA, InjectableType.Class, 5)
129
+ registry.set(token, InjectableScope.Singleton, ClassB, InjectableType.Class, 10)
130
+ registry.set(token, InjectableScope.Singleton, ClassC, InjectableType.Class, 1)
131
+
132
+ const records = registry.getAll(token)
133
+
134
+ expect(records).toHaveLength(3)
135
+ expect(records[0].target).toBe(ClassB) // priority 10
136
+ expect(records[1].target).toBe(ClassA) // priority 5
137
+ expect(records[2].target).toBe(ClassC) // priority 1
124
138
  })
125
139
 
126
- it('should prioritize local registry over parent', () => {
140
+ it('should get from parent if not in child', () => {
127
141
  const parentRegistry = new Registry()
128
142
  const childRegistry = new Registry(parentRegistry)
143
+ const token = InjectionToken.create<string>('test')
144
+ class TestClass {}
129
145
 
130
146
  parentRegistry.set(
131
- token1,
147
+ token,
132
148
  InjectableScope.Singleton,
133
- TestService,
149
+ TestClass,
134
150
  InjectableType.Class,
135
151
  )
136
- childRegistry.set(
137
- token1,
138
- InjectableScope.Transient,
139
- TestService,
140
- InjectableType.Factory,
141
- )
142
152
 
143
- expect(childRegistry.has(token1)).toBe(true)
144
- const record = childRegistry.get(token1)
145
- expect(record.scope).toBe(InjectableScope.Transient) // From child registry
153
+ const records = childRegistry.getAll(token)
154
+ expect(records).toHaveLength(1)
155
+ expect(records[0].target).toBe(TestClass)
146
156
  })
147
157
  })
148
158
 
149
- describe('get', () => {
150
- it('should return factory record for existing token', () => {
159
+ describe('set', () => {
160
+ it('should register factory with default priority 0', () => {
161
+ const token = InjectionToken.create<string>('test')
162
+ class TestClass {}
163
+
164
+ registry.set(token, InjectableScope.Singleton, TestClass, InjectableType.Class)
165
+
166
+ const record = registry.get(token)
167
+ expect(record.priority).toBe(0)
168
+ })
169
+
170
+ it('should register factory with custom priority', () => {
171
+ const token = InjectionToken.create<string>('test')
172
+ class TestClass {}
173
+
151
174
  registry.set(
152
- token1,
175
+ token,
153
176
  InjectableScope.Singleton,
154
- TestService,
177
+ TestClass,
155
178
  InjectableType.Class,
179
+ 100,
156
180
  )
157
181
 
158
- const record = registry.get(token1)
159
- expect(record.scope).toBe(InjectableScope.Singleton)
160
- expect(record.target).toBe(TestService)
161
- expect(record.type).toBe(InjectableType.Class)
162
- expect(record.originalToken).toBe(token1)
182
+ const record = registry.get(token)
183
+ expect(record.priority).toBe(100)
184
+ })
185
+
186
+ it('should support multiple registrations for same token', () => {
187
+ const token = InjectionToken.create<string>('test')
188
+ class ClassA {}
189
+ class ClassB {}
190
+
191
+ registry.set(token, InjectableScope.Singleton, ClassA, InjectableType.Class, 0)
192
+ registry.set(token, InjectableScope.Singleton, ClassB, InjectableType.Class, 1)
193
+
194
+ const records = registry.getAll(token)
195
+ expect(records).toHaveLength(2)
196
+ })
197
+
198
+ it('should update highest priority cache when new registration is higher', () => {
199
+ const token = InjectionToken.create<string>('test')
200
+ class ClassA {}
201
+ class ClassB {}
202
+
203
+ registry.set(token, InjectableScope.Singleton, ClassA, InjectableType.Class, 5)
204
+ expect(registry.get(token).target).toBe(ClassA)
205
+
206
+ registry.set(token, InjectableScope.Singleton, ClassB, InjectableType.Class, 10)
207
+ expect(registry.get(token).target).toBe(ClassB)
208
+ })
209
+
210
+ it('should not update highest priority cache when new registration is lower', () => {
211
+ const token = InjectionToken.create<string>('test')
212
+ class ClassA {}
213
+ class ClassB {}
214
+
215
+ registry.set(token, InjectableScope.Singleton, ClassA, InjectableType.Class, 10)
216
+ registry.set(token, InjectableScope.Singleton, ClassB, InjectableType.Class, 5)
217
+
218
+ expect(registry.get(token).target).toBe(ClassA)
219
+ })
220
+ })
221
+
222
+ describe('delete', () => {
223
+ it('should remove token registration', () => {
224
+ const token = InjectionToken.create<string>('test')
225
+ class TestClass {}
226
+
227
+ registry.set(token, InjectableScope.Singleton, TestClass, InjectableType.Class)
228
+ expect(registry.has(token)).toBe(true)
229
+
230
+ registry.delete(token)
231
+ expect(registry.has(token)).toBe(false)
232
+ })
233
+
234
+ it('should handle deleting non-existent token', () => {
235
+ const token = InjectionToken.create<string>('test')
236
+
237
+ // Should not throw
238
+ registry.delete(token)
239
+ })
240
+
241
+ it('should remove highest priority and keep lower priority registrations', () => {
242
+ const token = InjectionToken.create<string>('test')
243
+ class ClassA {}
244
+ class ClassB {}
245
+
246
+ registry.set(token, InjectableScope.Singleton, ClassA, InjectableType.Class, 5)
247
+ registry.set(token, InjectableScope.Singleton, ClassB, InjectableType.Class, 10)
248
+
249
+ expect(registry.get(token).target).toBe(ClassB)
250
+ expect(registry.getAll(token)).toHaveLength(2)
251
+
252
+ // Delete removes highest priority and recalculates
253
+ registry.delete(token)
254
+
255
+ // Lower priority registration (ClassA) should remain
256
+ expect(registry.getAll(token)).toHaveLength(1)
257
+ expect(registry.get(token).target).toBe(ClassA)
258
+ })
259
+ })
260
+
261
+ describe('updateScope', () => {
262
+ it('should update scope of registered token', () => {
263
+ const token = InjectionToken.create<string>('test')
264
+ class TestClass {}
265
+
266
+ registry.set(token, InjectableScope.Singleton, TestClass, InjectableType.Class)
267
+ expect(registry.get(token).scope).toBe(InjectableScope.Singleton)
268
+
269
+ const result = registry.updateScope(token, InjectableScope.Request)
270
+
271
+ expect(result).toBe(true)
272
+ expect(registry.get(token).scope).toBe(InjectableScope.Request)
273
+ })
274
+
275
+ it('should return false for non-existent token', () => {
276
+ const token = InjectionToken.create<string>('test')
277
+
278
+ const result = registry.updateScope(token, InjectableScope.Request)
279
+
280
+ expect(result).toBe(false)
163
281
  })
164
282
 
165
- it('should throw error for non-existent token', () => {
166
- expect(() => {
167
- registry.get(token1)
168
- }).toThrow(`[Registry] No factory found for ${token1.toString()}`)
283
+ it('should update all records for the token', () => {
284
+ const token = InjectionToken.create<string>('test')
285
+ class ClassA {}
286
+ class ClassB {}
287
+
288
+ registry.set(token, InjectableScope.Singleton, ClassA, InjectableType.Class, 0)
289
+ registry.set(token, InjectableScope.Singleton, ClassB, InjectableType.Class, 1)
290
+
291
+ registry.updateScope(token, InjectableScope.Request)
292
+
293
+ const records = registry.getAll(token)
294
+ expect(records[0].scope).toBe(InjectableScope.Request)
295
+ expect(records[1].scope).toBe(InjectableScope.Request)
169
296
  })
170
297
 
171
- it('should get from parent registry when not found locally', () => {
298
+ it('should delegate to parent if not found in child', () => {
172
299
  const parentRegistry = new Registry()
173
300
  const childRegistry = new Registry(parentRegistry)
301
+ const token = InjectionToken.create<string>('test')
302
+ class TestClass {}
174
303
 
175
304
  parentRegistry.set(
176
- token1,
305
+ token,
177
306
  InjectableScope.Singleton,
178
- TestService,
307
+ TestClass,
179
308
  InjectableType.Class,
180
309
  )
181
310
 
182
- const record = childRegistry.get(token1)
183
- expect(record.scope).toBe(InjectableScope.Singleton)
184
- expect(record.target).toBe(TestService)
185
- })
186
-
187
- it('should throw error when token not found in parent chain', () => {
188
- const parentRegistry = new Registry()
189
- const childRegistry = new Registry(parentRegistry)
311
+ const result = childRegistry.updateScope(token, InjectableScope.Transient)
190
312
 
191
- expect(() => {
192
- childRegistry.get(token1)
193
- }).toThrow(`[Registry] No factory found for ${token1.toString()}`)
313
+ expect(result).toBe(true)
314
+ expect(parentRegistry.get(token).scope).toBe(InjectableScope.Transient)
194
315
  })
316
+ })
195
317
 
196
- it('should prioritize local registry over parent', () => {
197
- const parentRegistry = new Registry()
198
- const childRegistry = new Registry(parentRegistry)
318
+ describe('priority system', () => {
319
+ it('should respect priority when getting the active registration', () => {
320
+ const token = InjectionToken.create<string>('test')
321
+ class DefaultImpl {}
322
+ class OverrideImpl {}
323
+ class HighPriorityImpl {}
199
324
 
200
- parentRegistry.set(
201
- token1,
325
+ registry.set(
326
+ token,
202
327
  InjectableScope.Singleton,
203
- TestService,
328
+ DefaultImpl,
204
329
  InjectableType.Class,
330
+ 0,
205
331
  )
206
- childRegistry.set(
207
- token1,
208
- InjectableScope.Transient,
209
- AnotherService,
210
- InjectableType.Factory,
332
+ registry.set(
333
+ token,
334
+ InjectableScope.Singleton,
335
+ OverrideImpl,
336
+ InjectableType.Class,
337
+ 1,
338
+ )
339
+ registry.set(
340
+ token,
341
+ InjectableScope.Singleton,
342
+ HighPriorityImpl,
343
+ InjectableType.Class,
344
+ 100,
211
345
  )
212
346
 
213
- const record = childRegistry.get(token1)
214
- expect(record.scope).toBe(InjectableScope.Transient)
215
- expect(record.target).toBe(AnotherService)
216
- expect(record.type).toBe(InjectableType.Factory)
347
+ expect(registry.get(token).target).toBe(HighPriorityImpl)
217
348
  })
218
- })
219
349
 
220
- describe('delete', () => {
221
- it('should delete existing token', () => {
350
+ it('should use first registration if priorities are equal', () => {
351
+ const token = InjectionToken.create<string>('test')
352
+ class FirstImpl {}
353
+ class SecondImpl {}
354
+
355
+ registry.set(
356
+ token,
357
+ InjectableScope.Singleton,
358
+ FirstImpl,
359
+ InjectableType.Class,
360
+ 5,
361
+ )
222
362
  registry.set(
223
- token1,
363
+ token,
224
364
  InjectableScope.Singleton,
225
- TestService,
365
+ SecondImpl,
226
366
  InjectableType.Class,
367
+ 5,
227
368
  )
228
- expect(registry.has(token1)).toBe(true)
229
369
 
230
- registry.delete(token1)
231
- expect(registry.has(token1)).toBe(false)
370
+ // With equal priority, the first one stays as highest
371
+ expect(registry.get(token).target).toBe(FirstImpl)
232
372
  })
233
373
 
234
- it('should not affect parent registry when deleting from child', () => {
235
- const parentRegistry = new Registry()
236
- const childRegistry = new Registry(parentRegistry)
374
+ it('should support negative priorities', () => {
375
+ const token = InjectionToken.create<string>('test')
376
+ class LowPriority {}
377
+ class DefaultPriority {}
237
378
 
238
- parentRegistry.set(
239
- token1,
379
+ registry.set(
380
+ token,
240
381
  InjectableScope.Singleton,
241
- TestService,
382
+ LowPriority,
242
383
  InjectableType.Class,
384
+ -10,
243
385
  )
244
- childRegistry.set(
245
- token1,
246
- InjectableScope.Transient,
247
- AnotherService,
386
+ registry.set(
387
+ token,
388
+ InjectableScope.Singleton,
389
+ DefaultPriority,
390
+ InjectableType.Class,
391
+ 0,
392
+ )
393
+
394
+ expect(registry.get(token).target).toBe(DefaultPriority)
395
+ })
396
+ })
397
+
398
+ describe('factory types', () => {
399
+ it('should support Class type factories', () => {
400
+ const token = InjectionToken.create<string>('test')
401
+ class TestClass {}
402
+
403
+ registry.set(token, InjectableScope.Singleton, TestClass, InjectableType.Class)
404
+
405
+ expect(registry.get(token).type).toBe(InjectableType.Class)
406
+ })
407
+
408
+ it('should support Factory type factories', () => {
409
+ const token = InjectionToken.create<string>('test')
410
+ class TestFactory {
411
+ create() {
412
+ return 'test'
413
+ }
414
+ }
415
+
416
+ registry.set(
417
+ token,
418
+ InjectableScope.Singleton,
419
+ TestFactory,
248
420
  InjectableType.Factory,
249
421
  )
250
422
 
251
- childRegistry.delete(token1)
423
+ expect(registry.get(token).type).toBe(InjectableType.Factory)
424
+ })
425
+ })
252
426
 
253
- // Child should no longer have it
254
- expect(childRegistry.has(token1)).toBe(true) // Still true because it exists in parent
255
- // But getting it should return parent's version
256
- const record = childRegistry.get(token1)
257
- expect(record.scope).toBe(InjectableScope.Singleton) // From parent
427
+ describe('globalRegistry', () => {
428
+ it('should be a singleton Registry instance', () => {
429
+ expect(globalRegistry).toBeInstanceOf(Registry)
258
430
  })
259
431
 
260
- it('should handle delete for non-existent token gracefully', () => {
261
- expect(() => {
262
- registry.delete(token1)
263
- }).not.toThrow()
432
+ it('should not have a parent', () => {
433
+ // We can verify this indirectly by checking that non-existent tokens
434
+ // throw without delegating to a parent
435
+ const token = InjectionToken.create<string>('non-existent-global-test')
436
+ expect(() => globalRegistry.get(token)).toThrow()
264
437
  })
265
438
  })
266
439
 
267
- describe('complex parent-child scenarios', () => {
268
- it('should handle deep hierarchy', () => {
269
- const grandParent = new Registry()
270
- const parent = new Registry(grandParent)
440
+ describe('parent-child relationship', () => {
441
+ it('should allow overriding parent registrations in child', () => {
442
+ const parent = new Registry()
271
443
  const child = new Registry(parent)
272
-
273
- grandParent.set(
274
- token1,
275
- InjectableScope.Singleton,
276
- TestService,
444
+ const token = InjectionToken.create<string>('test')
445
+ class ParentImpl {}
446
+ class ChildImpl {}
447
+
448
+ parent.set(token, InjectableScope.Singleton, ParentImpl, InjectableType.Class)
449
+ child.set(
450
+ token,
451
+ InjectableScope.Request,
452
+ ChildImpl,
277
453
  InjectableType.Class,
454
+ 1,
278
455
  )
279
456
 
280
- expect(child.has(token1)).toBe(true)
281
- const record = child.get(token1)
282
- expect(record.scope).toBe(InjectableScope.Singleton)
457
+ // Child should return its own registration
458
+ expect(child.get(token).target).toBe(ChildImpl)
459
+ expect(child.get(token).scope).toBe(InjectableScope.Request)
460
+
461
+ // Parent should still have its original registration
462
+ expect(parent.get(token).target).toBe(ParentImpl)
283
463
  })
284
464
 
285
- it('should handle overriding at different levels', () => {
286
- const grandParent = new Registry()
287
- const parent = new Registry(grandParent)
465
+ it('should support multi-level hierarchy', () => {
466
+ const grandparent = new Registry()
467
+ const parent = new Registry(grandparent)
288
468
  const child = new Registry(parent)
469
+ const token = InjectionToken.create<string>('test')
470
+ class TestClass {}
289
471
 
290
- grandParent.set(
291
- token1,
472
+ grandparent.set(
473
+ token,
292
474
  InjectableScope.Singleton,
293
- TestService,
475
+ TestClass,
294
476
  InjectableType.Class,
295
477
  )
296
- parent.set(
297
- token1,
298
- InjectableScope.Transient,
299
- AnotherService,
300
- InjectableType.Factory,
301
- )
302
478
 
303
- const record = child.get(token1)
304
- expect(record.scope).toBe(InjectableScope.Transient) // From parent, not grandparent
305
- expect(record.target).toBe(AnotherService)
479
+ expect(child.has(token)).toBe(true)
480
+ expect(child.get(token).target).toBe(TestClass)
306
481
  })
307
482
  })
308
483
  })
309
-
310
- describe('globalRegistry', () => {
311
- it('should be a Registry instance', () => {
312
- expect(globalRegistry).toBeInstanceOf(Registry)
313
- })
314
-
315
- it('should be able to store and retrieve tokens', () => {
316
- const token = new InjectionToken<TestService, undefined>(
317
- 'GlobalTest',
318
- undefined,
319
- )
320
-
321
- globalRegistry.set(
322
- token,
323
- InjectableScope.Singleton,
324
- TestService,
325
- InjectableType.Class,
326
- )
327
- expect(globalRegistry.has(token)).toBe(true)
328
-
329
- const record = globalRegistry.get(token)
330
- expect(record.target).toBe(TestService)
331
-
332
- // Clean up
333
- globalRegistry.delete(token)
334
- })
335
- })