@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,443 +1,387 @@
1
- import { beforeEach, describe, expect, it } from 'vitest'
2
1
  import { z } from 'zod/v4'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+
4
+ import {
5
+ BoundInjectionToken,
6
+ FactoryInjectionToken,
7
+ InjectionToken,
8
+ } from '../token/injection-token.mjs'
9
+ import type { FactoryContext } from '../internal/context/factory-context.mjs'
10
+
11
+ describe('InjectionToken', () => {
12
+ describe('constructor', () => {
13
+ it('should create token with string name', () => {
14
+ const token = new InjectionToken('TestToken', undefined)
15
+
16
+ expect(token.name).toBe('TestToken')
17
+ expect(token.schema).toBeUndefined()
18
+ })
3
19
 
4
- import type { Factorable, FactorableWithArgs } from '../interfaces/index.mjs'
5
-
6
- import { Factory, Injectable } from '../decorators/index.mjs'
7
- import { Container } from '../index.mjs'
8
- import { InjectionToken } from '../token/injection-token.mjs'
20
+ it('should create token with symbol name', () => {
21
+ const sym = Symbol('TestSymbol')
22
+ const token = new InjectionToken(sym, undefined)
9
23
 
10
- describe('InjectToken', () => {
11
- let container: Container
12
- beforeEach(() => {
13
- container = new Container()
14
- })
15
- it('should work with class', async () => {
16
- const token = InjectionToken.create('Test')
17
- @Injectable({
18
- token,
24
+ expect(token.name).toBe(sym)
19
25
  })
20
- class Test {}
21
26
 
22
- const value = await container.get(Test)
23
- expect(value).toBeInstanceOf(Test)
24
- })
27
+ it('should create token with class name', () => {
28
+ class TestClass {}
29
+ const token = new InjectionToken(TestClass, undefined)
25
30
 
26
- it('should work with class and schema', async () => {
27
- const schema = z.object({
28
- test: z.string(),
31
+ expect(token.name).toBe(TestClass)
29
32
  })
30
- const token = InjectionToken.create('Test', schema)
31
33
 
32
- @Injectable({
33
- token,
34
- })
35
- class Test {
36
- makeFoo() {
37
- return 'foo'
38
- }
39
- }
40
- const value = await container.get(token, {
41
- test: 'test',
34
+ it('should create token with schema', () => {
35
+ const schema = z.object({ name: z.string() })
36
+ const token = new InjectionToken('TestToken', schema)
37
+
38
+ expect(token.schema).toBe(schema)
42
39
  })
43
40
 
44
- expect(value).toBeInstanceOf(Test)
45
- })
41
+ it('should generate deterministic id', () => {
42
+ const token1 = InjectionToken.create<string>('TestToken')
43
+ const token2 = InjectionToken.create<string>('TestToken')
46
44
 
47
- it('should work with factory', async () => {
48
- const token = InjectionToken.create<string>('Test')
49
- @Factory({
50
- token,
45
+ expect(token1.id).toBe(token2.id)
51
46
  })
52
- class Test implements Factorable<string> {
53
- async create() {
54
- return 'foo'
55
- }
56
- }
57
47
 
58
- const value = await container.get(Test)
59
- expect(value).toBe('foo')
60
- })
48
+ it('should generate different ids for different names', () => {
49
+ const token1 = InjectionToken.create<string>('TokenA')
50
+ const token2 = InjectionToken.create<string>('TokenB')
61
51
 
62
- it('should work with factory and schema', async () => {
63
- const schema = z.object({
64
- test: z.string(),
52
+ expect(token1.id).not.toBe(token2.id)
65
53
  })
66
- const token = InjectionToken.create<string, typeof schema>('Test', schema)
67
54
 
68
- @Factory({
69
- token,
70
- })
71
- // oxlint-disable-next-line no-unused-vars
72
- class Test implements FactorableWithArgs<string, typeof schema> {
73
- async create(ctx: any, args: { test: string }) {
74
- return args.test
75
- }
76
- }
77
- const value = await container.get(token, {
78
- test: 'test',
79
- })
55
+ it('should use custom id when provided', () => {
56
+ const token = new InjectionToken('TestToken', undefined, 'custom-id')
80
57
 
81
- expect(value).toBe('test')
58
+ expect(token.id).toBe('custom-id')
59
+ })
82
60
  })
83
61
 
84
- it('should work with bound token', async () => {
85
- const schema = z.object({
86
- test: z.string(),
62
+ describe('static create', () => {
63
+ it('should create token with string name', () => {
64
+ const token = InjectionToken.create<string>('TestToken')
65
+
66
+ expect(token).toBeInstanceOf(InjectionToken)
67
+ expect(token.name).toBe('TestToken')
87
68
  })
88
- const token = InjectionToken.create('Test', schema)
89
- const boundToken = InjectionToken.bound(token, {
90
- test: 'test',
69
+
70
+ it('should create token with symbol name', () => {
71
+ const sym = Symbol('test')
72
+ const token = InjectionToken.create<string>(sym)
73
+
74
+ expect(token.name).toBe(sym)
91
75
  })
92
76
 
93
- @Injectable({
94
- token,
77
+ it('should create token with class', () => {
78
+ class TestService {}
79
+ const token = InjectionToken.create(TestService)
80
+
81
+ expect(token.name).toBe(TestService)
95
82
  })
96
- class Test {
97
- makeFoo() {
98
- return 'foo'
99
- }
100
- }
101
- const value = await container.get(boundToken)
102
83
 
103
- expect(value).toBeInstanceOf(Test)
84
+ it('should create token with schema', () => {
85
+ const schema = z.object({ id: z.number() })
86
+ const token = InjectionToken.create<{ data: string }, typeof schema>(
87
+ 'TestToken',
88
+ schema,
89
+ )
90
+
91
+ expect(token.schema).toBe(schema)
92
+ })
104
93
  })
105
94
 
106
- it('should work with factory token', async () => {
107
- const schema = z.object({
108
- test: z.string(),
95
+ describe('static bound', () => {
96
+ it('should create BoundInjectionToken', () => {
97
+ const schema = z.object({ value: z.string() })
98
+ const token = InjectionToken.create<string, typeof schema>(
99
+ 'TestToken',
100
+ schema,
101
+ )
102
+
103
+ const bound = InjectionToken.bound(token, { value: 'test' })
104
+
105
+ expect(bound).toBeInstanceOf(BoundInjectionToken)
106
+ expect(bound.token).toBe(token)
107
+ expect(bound.value).toEqual({ value: 'test' })
109
108
  })
110
- const token = InjectionToken.create('Test', schema)
111
- const factoryInjectionToken = InjectionToken.factory(token, () =>
112
- Promise.resolve({
113
- test: 'test',
114
- }),
115
- )
109
+ })
110
+
111
+ describe('static factory', () => {
112
+ it('should create FactoryInjectionToken', () => {
113
+ const schema = z.object({ value: z.string() })
114
+ const token = InjectionToken.create<string, typeof schema>(
115
+ 'TestToken',
116
+ schema,
117
+ )
118
+ const factory = async () => ({ value: 'factory' })
119
+
120
+ const factoryToken = InjectionToken.factory(token, factory)
116
121
 
117
- @Injectable({
118
- token,
122
+ expect(factoryToken).toBeInstanceOf(FactoryInjectionToken)
123
+ expect(factoryToken.token).toBe(token)
124
+ expect(factoryToken.factory).toBe(factory)
119
125
  })
120
- class Test {
121
- makeFoo() {
122
- return 'foo'
123
- }
124
- }
125
- const value = await container.get(factoryInjectionToken)
126
+ })
127
+
128
+ describe('static refineType', () => {
129
+ it('should refine bound token type', () => {
130
+ const schema = z.object({ value: z.string() })
131
+ const token = InjectionToken.create<string, typeof schema>(
132
+ 'TestToken',
133
+ schema,
134
+ )
135
+ const bound = new BoundInjectionToken(token, { value: 'test' })
136
+
137
+ const refined = InjectionToken.refineType<number>(bound)
126
138
 
127
- expect(value).toBeInstanceOf(Test)
139
+ expect(refined).toBe(bound)
140
+ })
128
141
  })
129
142
 
130
- describe('Factory Token Resolution', () => {
131
- it('should resolve factory token only once and cache the result', async () => {
132
- let resolveCount = 0
133
- const token = InjectionToken.create<Test>('Test')
134
- const factoryInjectionToken = InjectionToken.factory(token, () => {
135
- resolveCount++
136
- return Promise.resolve({
137
- value: 'cached-value',
138
- })
139
- })
140
-
141
- @Injectable({
142
- token,
143
- })
144
- class Test {
145
- value = 'test-value'
146
- }
143
+ describe('toString', () => {
144
+ it('should format string name with id', () => {
145
+ const token = InjectionToken.create<string>('TestToken')
147
146
 
148
- // First resolution
149
- const value1 = await container.get(factoryInjectionToken)
150
- expect(value1).toBeInstanceOf(Test)
151
- expect(resolveCount).toBe(1)
147
+ const str = token.toString()
152
148
 
153
- // Second resolution should use cached value
154
- const value2 = await container.get(factoryInjectionToken)
155
- expect(value2).toBeInstanceOf(Test)
156
- expect(resolveCount).toBe(1) // Should not increment
149
+ expect(str).toMatch(/TestToken\([^)]+\)/)
157
150
  })
158
151
 
159
- it('should handle async factory functions', async () => {
160
- const token = InjectionToken.create<AsyncTest>('AsyncTest')
161
- const factoryInjectionToken = InjectionToken.factory(token, async () => {
162
- await new Promise((resolve) => setTimeout(resolve, 10))
163
- return {
164
- value: 'async-value',
165
- }
166
- })
167
-
168
- @Injectable({
169
- token,
170
- })
171
- class AsyncTest {
172
- value = 'async-test'
173
- }
152
+ it('should format symbol name with id', () => {
153
+ const sym = Symbol('TestSymbol')
154
+ const token = InjectionToken.create<string>(sym)
174
155
 
175
- const value = await container.get(factoryInjectionToken)
176
- expect(value).toBeInstanceOf(AsyncTest)
156
+ const str = token.toString()
157
+
158
+ expect(str).toMatch(/Symbol\(TestSymbol\)\([^)]+\)/)
177
159
  })
178
160
 
179
- it('should handle factory functions that throw errors', async () => {
180
- const token = InjectionToken.create<ErrorTest>('ErrorTest')
181
- const factoryInjectionToken = InjectionToken.factory(token, () => {
182
- throw new Error('Factory error')
183
- })
184
-
185
- @Injectable({
186
- token,
187
- })
188
- class ErrorTest {
189
- value = 'error-test'
190
- }
161
+ it('should format class name with id', () => {
162
+ class TestClass {}
163
+ const token = InjectionToken.create(TestClass)
191
164
 
192
- await expect(container.get(factoryInjectionToken)).rejects.toThrow(
193
- 'Factory error',
194
- )
165
+ const str = token.toString()
166
+
167
+ expect(str).toMatch(/TestClass\([^)]+\)/)
195
168
  })
196
169
 
197
- it('should handle factory functions that return rejected promises', async () => {
198
- const token = InjectionToken.create<RejectedTest>('RejectedTest')
199
- const factoryInjectionToken = InjectionToken.factory(token, () => {
200
- return Promise.reject(new Error('Promise rejection'))
201
- })
202
-
203
- @Injectable({
204
- token,
205
- })
206
- class RejectedTest {
207
- value = 'rejected-test'
208
- }
170
+ it('should cache formatted name', () => {
171
+ const token = InjectionToken.create<string>('TestToken')
209
172
 
210
- await expect(container.get(factoryInjectionToken)).rejects.toThrow(
211
- 'Promise rejection',
212
- )
173
+ const str1 = token.toString()
174
+ const str2 = token.toString()
175
+
176
+ expect(str1).toBe(str2)
213
177
  })
178
+ })
179
+ })
214
180
 
215
- it('should support inject in factory', async () => {
216
- @Injectable()
217
- class InjectTest2 {
218
- value = 'inject-test'
219
- }
181
+ describe('BoundInjectionToken', () => {
182
+ describe('constructor', () => {
183
+ it('should wrap token with value', () => {
184
+ const schema = z.object({ config: z.string() })
185
+ const token = InjectionToken.create<string, typeof schema>(
186
+ 'TestToken',
187
+ schema,
188
+ )
189
+
190
+ const bound = new BoundInjectionToken(token, { config: 'value' })
220
191
 
221
- const token = InjectionToken.create('InjectTest')
222
- const factoryInjectionToken = InjectionToken.factory(
223
- token,
224
- async (ctx) => {
225
- const injectTest = await ctx.inject(InjectTest2)
226
- return { value: injectTest.value }
227
- },
192
+ expect(bound.token).toBe(token)
193
+ expect(bound.value).toEqual({ config: 'value' })
194
+ })
195
+
196
+ it('should copy properties from wrapped token', () => {
197
+ const schema = z.object({ config: z.string() })
198
+ const token = InjectionToken.create<string, typeof schema>(
199
+ 'TestToken',
200
+ schema,
228
201
  )
229
202
 
230
- @Injectable({
231
- token,
232
- })
233
- class InjectTest {
234
- value = 'inject-test'
235
- }
203
+ const bound = new BoundInjectionToken(token, { config: 'value' })
236
204
 
237
- const value = await container.get(factoryInjectionToken)
238
- expect(value).toBeInstanceOf(InjectTest)
205
+ expect(bound.id).toBe(token.id)
206
+ expect(bound.name).toBe(token.name)
207
+ expect(bound.schema).toBe(token.schema)
239
208
  })
240
209
  })
241
210
 
242
- describe('Factory Token with Different Schema Types', () => {
243
- it('should work with optional schema', async () => {
244
- const schema = z
245
- .object({
246
- optional: z.string().optional(),
247
- })
248
- .optional()
249
- const token = InjectionToken.create('OptionalTest', schema)
250
- const factoryInjectionToken = InjectionToken.factory(token, () =>
251
- Promise.resolve(undefined),
211
+ describe('toString', () => {
212
+ it('should delegate to wrapped token', () => {
213
+ const schema = z.object({ config: z.string() })
214
+ const token = InjectionToken.create<string, typeof schema>(
215
+ 'TestToken',
216
+ schema,
252
217
  )
218
+ const bound = new BoundInjectionToken(token, { config: 'value' })
253
219
 
254
- @Injectable({
255
- token,
256
- })
257
- class OptionalTest {
258
- value = 'optional-test'
259
- }
260
-
261
- const value = await container.get(factoryInjectionToken)
262
- expect(value).toBeInstanceOf(OptionalTest)
220
+ expect(bound.toString()).toBe(token.toString())
263
221
  })
222
+ })
223
+ })
264
224
 
265
- it('should work with record schema', async () => {
266
- const schema = z.record(z.string(), z.number())
267
- const token = InjectionToken.create('RecordTest', schema)
268
- const factoryInjectionToken = InjectionToken.factory(token, () =>
269
- Promise.resolve({ count: 42, score: 100 }),
225
+ describe('FactoryInjectionToken', () => {
226
+ describe('constructor', () => {
227
+ it('should wrap token with factory function', () => {
228
+ const schema = z.object({ config: z.string() })
229
+ const token = InjectionToken.create<string, typeof schema>(
230
+ 'TestToken',
231
+ schema,
270
232
  )
233
+ const factory = async () => ({ config: 'factory' })
271
234
 
272
- @Injectable({
273
- token,
274
- })
275
- class RecordTest {
276
- value = 'record-test'
277
- }
235
+ const factoryToken = new FactoryInjectionToken(token, factory)
278
236
 
279
- const value = await container.get(factoryInjectionToken)
280
- expect(value).toBeInstanceOf(RecordTest)
237
+ expect(factoryToken.token).toBe(token)
238
+ expect(factoryToken.factory).toBe(factory)
239
+ expect(factoryToken.resolved).toBe(false)
240
+ expect(factoryToken.value).toBeUndefined()
281
241
  })
282
242
 
283
- it('should work with complex nested schema', async () => {
284
- const schema = z.object({
285
- user: z.object({
286
- id: z.number(),
287
- name: z.string(),
288
- preferences: z.object({
289
- theme: z.enum(['light', 'dark']),
290
- notifications: z.boolean(),
291
- }),
292
- }),
293
- metadata: z.array(z.string()),
294
- })
295
- const token = InjectionToken.create('ComplexTest', schema)
296
- const factoryInjectionToken = InjectionToken.factory(token, () =>
297
- Promise.resolve({
298
- user: {
299
- id: 1,
300
- name: 'John Doe',
301
- preferences: {
302
- theme: 'dark' as const,
303
- notifications: true,
304
- },
305
- },
306
- metadata: ['tag1', 'tag2'],
307
- }),
243
+ it('should copy properties from wrapped token', () => {
244
+ const schema = z.object({ config: z.string() })
245
+ const token = InjectionToken.create<string, typeof schema>(
246
+ 'TestToken',
247
+ schema,
308
248
  )
249
+ const factory = async () => ({ config: 'factory' })
309
250
 
310
- @Injectable({
311
- token,
312
- })
313
- class ComplexTest {
314
- value = 'complex-test'
315
- }
251
+ const factoryToken = new FactoryInjectionToken(token, factory)
316
252
 
317
- const value = await container.get(factoryInjectionToken)
318
- expect(value).toBeInstanceOf(ComplexTest)
253
+ expect(factoryToken.id).toBe(token.id)
254
+ expect(factoryToken.name).toBe(token.name)
255
+ expect(factoryToken.schema).toBe(token.schema)
319
256
  })
320
257
  })
321
258
 
322
- describe('Factory Token with Multiple Instances', () => {
323
- it('should create separate instances for different factory tokens', async () => {
324
- const token1 = InjectionToken.create<Test1>('Test1')
325
- const token2 = InjectionToken.create<Test2>('Test2')
326
-
327
- const factoryToken1 = InjectionToken.factory(token1, () =>
328
- Promise.resolve({
329
- value: 'value1',
330
- }),
259
+ describe('resolve', () => {
260
+ it('should call factory and store value', async () => {
261
+ const schema = z.object({ config: z.string() })
262
+ const token = InjectionToken.create<string, typeof schema>(
263
+ 'TestToken',
264
+ schema,
331
265
  )
332
- const factoryToken2 = InjectionToken.factory(token2, () =>
333
- Promise.resolve({
334
- value: 'value2',
335
- }),
336
- )
337
-
338
- @Injectable({
339
- token: token1,
340
- })
341
- class Test1 {
342
- value = 'test1'
343
- }
266
+ const factory = vi.fn(async () => ({ config: 'resolved' }))
267
+ const factoryToken = new FactoryInjectionToken(token, factory)
344
268
 
345
- @Injectable({
346
- token: token2,
347
- })
348
- class Test2 {
349
- value = 'test2'
269
+ const mockCtx: FactoryContext = {
270
+ inject: vi.fn(),
271
+ container: {} as any,
272
+ addDestroyListener: vi.fn(),
350
273
  }
351
274
 
352
- const value1 = await container.get(factoryToken1)
353
- const value2 = await container.get(factoryToken2)
275
+ const result = await factoryToken.resolve(mockCtx)
354
276
 
355
- expect(value1).toBeInstanceOf(Test1)
356
- expect(value2).toBeInstanceOf(Test2)
357
- expect(value1).not.toBe(value2)
277
+ expect(result).toEqual({ config: 'resolved' })
278
+ expect(factoryToken.value).toEqual({ config: 'resolved' })
279
+ expect(factoryToken.resolved).toBe(true)
280
+ expect(factory).toHaveBeenCalledWith(mockCtx)
358
281
  })
359
282
 
360
- it('should handle factory tokens with same underlying token but different factories', async () => {
361
- const token = InjectionToken.create<SharedTest>('SharedTest')
362
-
363
- const factoryToken1 = InjectionToken.factory(token, () =>
364
- Promise.resolve({
365
- value: 'factory1',
366
- }),
367
- )
368
- const factoryToken2 = InjectionToken.factory(token, () =>
369
- Promise.resolve({
370
- value: 'factory2',
371
- }),
283
+ it('should only call factory once', async () => {
284
+ const schema = z.object({ config: z.string() })
285
+ const token = InjectionToken.create<string, typeof schema>(
286
+ 'TestToken',
287
+ schema,
372
288
  )
289
+ const factory = vi.fn(async () => ({ config: 'resolved' }))
290
+ const factoryToken = new FactoryInjectionToken(token, factory)
373
291
 
374
- @Injectable({
375
- token,
376
- })
377
- class SharedTest {
378
- value = 'shared-test'
292
+ const mockCtx: FactoryContext = {
293
+ inject: vi.fn(),
294
+ container: {} as any,
295
+ addDestroyListener: vi.fn(),
379
296
  }
380
297
 
381
- const value1 = await container.get(factoryToken1)
382
- const value2 = await container.get(factoryToken2)
298
+ await factoryToken.resolve(mockCtx)
299
+ await factoryToken.resolve(mockCtx)
300
+ await factoryToken.resolve(mockCtx)
383
301
 
384
- expect(value1).toBeInstanceOf(SharedTest)
385
- expect(value2).toBeInstanceOf(SharedTest)
302
+ expect(factory).toHaveBeenCalledTimes(1)
386
303
  })
387
- })
388
304
 
389
- describe('Factory Token Properties', () => {
390
- it('should have correct properties', () => {
391
- const token = InjectionToken.create('PropertyTest')
392
- const factoryInjectionToken = InjectionToken.factory(token, () =>
393
- Promise.resolve({
394
- value: 'test',
395
- }),
305
+ it('should return cached value on subsequent calls', async () => {
306
+ const schema = z.object({ config: z.string() })
307
+ const token = InjectionToken.create<string, typeof schema>(
308
+ 'TestToken',
309
+ schema,
396
310
  )
311
+ let callCount = 0
312
+ const factory = async () => ({ config: `call-${++callCount}` })
313
+ const factoryToken = new FactoryInjectionToken(token, factory)
314
+
315
+ const mockCtx: FactoryContext = {
316
+ inject: vi.fn(),
317
+ container: {} as any,
318
+ addDestroyListener: vi.fn(),
319
+ }
320
+
321
+ const result1 = await factoryToken.resolve(mockCtx)
322
+ const result2 = await factoryToken.resolve(mockCtx)
397
323
 
398
- expect(factoryInjectionToken.id).toBe(token.id)
399
- expect(factoryInjectionToken.name).toBe(token.name)
400
- expect(factoryInjectionToken.schema).toBe(token.schema)
401
- expect(factoryInjectionToken.resolved).toBe(false)
402
- expect(factoryInjectionToken.value).toBeUndefined()
324
+ expect(result1).toEqual({ config: 'call-1' })
325
+ expect(result2).toEqual({ config: 'call-1' })
403
326
  })
404
327
 
405
- it('should update resolved property after resolution', async () => {
406
- const token = InjectionToken.create('ResolvedTest')
407
- const factoryInjectionToken = InjectionToken.factory(token, () =>
408
- Promise.resolve({
409
- value: 'resolved',
410
- }),
328
+ it('should allow factory to use inject', async () => {
329
+ const schema = z.object({ config: z.string() })
330
+ const token = InjectionToken.create<string, typeof schema>(
331
+ 'TestToken',
332
+ schema,
411
333
  )
412
334
 
413
- expect(factoryInjectionToken.resolved).toBe(false)
414
- expect(factoryInjectionToken.value).toBeUndefined()
335
+ const factory = async (ctx: FactoryContext) => {
336
+ await ctx.inject(InjectionToken.create<string>('Dependency'))
337
+ return { config: 'with-dep' }
338
+ }
339
+
340
+ const factoryToken = new FactoryInjectionToken(token, factory)
341
+ const mockInject = vi.fn().mockResolvedValue('dep-value')
342
+ const mockCtx: FactoryContext = {
343
+ inject: mockInject,
344
+ container: {} as any,
345
+ addDestroyListener: vi.fn(),
346
+ }
415
347
 
416
- // @ts-expect-error we are not using the context
417
- await factoryInjectionToken.resolve()
348
+ await factoryToken.resolve(mockCtx)
418
349
 
419
- expect(factoryInjectionToken.resolved).toBe(true)
420
- expect(factoryInjectionToken.value).toMatchObject({ value: 'resolved' })
350
+ expect(mockInject).toHaveBeenCalled()
421
351
  })
352
+ })
422
353
 
423
- it('should return cached value on subsequent resolve calls', async () => {
424
- let resolveCount = 0
425
- const token = InjectionToken.create('CacheTest')
426
- const factoryInjectionToken = InjectionToken.factory(token, () => {
427
- resolveCount++
428
- return Promise.resolve({
429
- value: 'cached',
430
- })
431
- })
432
-
433
- // @ts-expect-error we are not using the context
434
- const result1 = await factoryInjectionToken.resolve()
435
- // @ts-expect-error we are not using the context
436
- const result2 = await factoryInjectionToken.resolve()
437
-
438
- expect(result1).toMatchObject({ value: 'cached' })
439
- expect(result2).toMatchObject({ value: 'cached' })
440
- expect(resolveCount).toBe(1)
354
+ describe('toString', () => {
355
+ it('should delegate to wrapped token', () => {
356
+ const schema = z.object({ config: z.string() })
357
+ const token = InjectionToken.create<string, typeof schema>(
358
+ 'TestToken',
359
+ schema,
360
+ )
361
+ const factory = async () => ({ config: 'factory' })
362
+ const factoryToken = new FactoryInjectionToken(token, factory)
363
+
364
+ expect(factoryToken.toString()).toBe(token.toString())
441
365
  })
442
366
  })
443
367
  })
368
+
369
+ describe('token identity', () => {
370
+ it('should maintain token reference through wrapping', () => {
371
+ const schema = z.object({ value: z.string() })
372
+ const original = InjectionToken.create<string, typeof schema>(
373
+ 'TestToken',
374
+ schema,
375
+ )
376
+
377
+ const bound = new BoundInjectionToken(original, { value: 'test' })
378
+ const factory = new FactoryInjectionToken(original, async () => ({
379
+ value: 'test',
380
+ }))
381
+
382
+ expect(bound.token).toBe(original)
383
+ expect(factory.token).toBe(original)
384
+ expect(bound.id).toBe(original.id)
385
+ expect(factory.id).toBe(original.id)
386
+ })
387
+ })