@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,28 +1,26 @@
1
1
  import type { z, ZodType } from 'zod/v4'
2
2
 
3
- import type { Container } from './container.mjs'
3
+ import type { Factorable } from '../interfaces/factory.interface.mjs'
4
4
  import type {
5
- BoundInjectionToken,
6
5
  ClassType,
7
6
  ClassTypeWithArgument,
8
7
  FactoryInjectionToken,
9
- InjectionToken,
10
8
  InjectionTokenSchemaType,
11
9
  } from '../token/injection-token.mjs'
12
- import type { IContainer } from '../interfaces/container.interface.mjs'
13
- import type { IHolderStorage } from '../internal/holder/holder-storage.interface.mjs'
14
- import type { Factorable } from '../interfaces/factory.interface.mjs'
15
10
  import type { Registry } from '../token/registry.mjs'
16
- import type { RequestContext } from '../internal/context/request-context.mjs'
17
- import type { InstanceHolder } from '../internal/holder/instance-holder.mjs'
18
11
  import type { Join, UnionToArray } from '../utils/types.mjs'
12
+ import type { NameResolver } from '../internal/core/name-resolver.mjs'
13
+ import type { ServiceInvalidator } from '../internal/core/service-invalidator.mjs'
14
+ import type { TokenResolver } from '../internal/core/token-resolver.mjs'
19
15
 
20
- import { BaseHolderManager } from '../internal/holder/base-holder-manager.mjs'
21
16
  import { InjectableScope } from '../enums/index.mjs'
22
- import { DIError } from '../errors/index.mjs'
23
- import { DefaultRequestContext } from '../internal/context/request-context.mjs'
24
- import { RequestStorage } from '../internal/holder/request-storage.mjs'
25
- import { InstanceStatus } from '../internal/holder/instance-holder.mjs'
17
+ import { UnifiedStorage } from '../internal/holder/unified-storage.mjs'
18
+ import {
19
+ BoundInjectionToken,
20
+ InjectionToken,
21
+ } from '../token/injection-token.mjs'
22
+ import { AbstractContainer } from './abstract-container.mjs'
23
+ import { Container } from './container.mjs'
26
24
 
27
25
  /**
28
26
  * Request-scoped dependency injection container.
@@ -32,45 +30,53 @@ import { InstanceStatus } from '../internal/holder/instance-holder.mjs'
32
30
  * This design eliminates race conditions that can occur with async operations
33
31
  * when multiple requests are processed concurrently.
34
32
  */
35
- export class ScopedContainer implements IContainer {
36
- private readonly requestContextHolder: RequestContext
37
- private readonly holderStorage: IHolderStorage
33
+ export class ScopedContainer extends AbstractContainer {
34
+ protected readonly defaultScope = InjectableScope.Request
35
+
36
+ private readonly storage: UnifiedStorage
38
37
  private disposed = false
38
+ private readonly metadata: Record<string, any>
39
39
 
40
40
  constructor(
41
41
  private readonly parent: Container,
42
42
  private readonly registry: Registry,
43
43
  public readonly requestId: string,
44
44
  metadata?: Record<string, any>,
45
- priority: number = 100,
46
45
  ) {
47
- this.requestContextHolder = new DefaultRequestContext(
48
- requestId,
49
- priority,
50
- metadata,
51
- )
52
- // Create storage once and reuse for all resolutions
53
- this.holderStorage = new RequestStorage(
54
- this.requestContextHolder,
55
- this.parent.getServiceLocator().getManager(),
56
- )
46
+ super()
47
+ // Create own unified storage for request-scoped services
48
+ this.storage = new UnifiedStorage(InjectableScope.Request)
49
+ this.metadata = metadata || {}
57
50
  }
58
51
 
59
- /**
60
- * Gets the request context holder for this scoped container.
61
- */
62
- getRequestContextHolder(): RequestContext {
63
- return this.requestContextHolder
52
+ // ============================================================================
53
+ // ABSTRACT METHOD IMPLEMENTATIONS - Delegate to parent
54
+ // ============================================================================
55
+
56
+ getStorage(): UnifiedStorage {
57
+ return this.storage
64
58
  }
65
59
 
66
- /**
67
- * Gets the holder storage for this scoped container.
68
- * Used by InstanceResolver for request-scoped resolution.
69
- */
70
- getHolderStorage(): IHolderStorage {
71
- return this.holderStorage
60
+ protected getRegistry(): Registry {
61
+ return this.registry
62
+ }
63
+
64
+ protected getTokenResolver(): TokenResolver {
65
+ return this.parent.getTokenResolver()
66
+ }
67
+
68
+ protected getNameResolver(): NameResolver {
69
+ return this.parent.getNameResolver()
70
+ }
71
+
72
+ protected getServiceInvalidator(): ServiceInvalidator {
73
+ return this.parent.getServiceInvalidator()
72
74
  }
73
75
 
76
+ // ============================================================================
77
+ // SCOPED CONTAINER SPECIFIC METHODS
78
+ // ============================================================================
79
+
74
80
  /**
75
81
  * Gets the request ID for this scoped container.
76
82
  */
@@ -89,26 +95,19 @@ export class ScopedContainer implements IContainer {
89
95
  * Gets metadata from the request context.
90
96
  */
91
97
  getMetadata(key: string): any | undefined {
92
- return this.requestContextHolder.getMetadata(key)
98
+ return this.metadata[key]
93
99
  }
94
100
 
95
101
  /**
96
102
  * Sets metadata on the request context.
97
103
  */
98
104
  setMetadata(key: string, value: any): void {
99
- this.requestContextHolder.setMetadata(key, value)
100
- }
101
-
102
- /**
103
- * Adds a pre-prepared instance to the request context.
104
- */
105
- addInstance(token: InjectionToken<any, undefined>, instance: any): void {
106
- this.requestContextHolder.addInstance(token, instance)
105
+ this.metadata[key] = value
107
106
  }
108
107
 
109
108
  /**
110
109
  * Gets an instance from the container.
111
- * Request-scoped services are resolved from this container's context.
110
+ * Request-scoped services are resolved from this container's storage.
112
111
  * All other services are delegated to the parent container.
113
112
  */
114
113
  // #1 Simple class
@@ -150,207 +149,126 @@ export class ScopedContainer implements IContainer {
150
149
  | BoundInjectionToken<any, any>
151
150
  | FactoryInjectionToken<any, any>,
152
151
  args?: unknown,
153
- ): Promise<any> {
152
+ ) {
154
153
  if (this.disposed) {
155
- throw DIError.unknown(
156
- `ScopedContainer for request ${this.requestId} has been disposed`,
157
- )
154
+ throw new Error('ScopedContainer has been disposed')
158
155
  }
159
156
 
160
- // Get the actual injection token using TokenProcessor for consistency
161
- const tokenProcessor = this.parent.getServiceLocator().getTokenProcessor()
162
- const actualToken = tokenProcessor.normalizeToken(token)
163
-
164
157
  // Check if this is a request-scoped service
165
- if (this.isRequestScoped(actualToken)) {
166
- return this.resolveRequestScoped(actualToken, args)
167
- }
158
+ const tokenResolver = this.getTokenResolver()
159
+ const realToken = tokenResolver.getRegistryToken(token)
168
160
 
169
- // Delegate to parent for singleton/transient services
170
- // Pass this ScopedContainer so nested inject() calls work correctly
171
- return this.parent.getWithContext(token, args, this)
172
- }
161
+ if (this.registry.has(realToken)) {
162
+ const record = this.registry.get(realToken)
163
+ if (record.scope === InjectableScope.Request) {
164
+ // Resolve request-scoped service from this container
165
+ const [error, instance] = await this.parent
166
+ .getInstanceResolver()
167
+ .resolveRequestScopedInstance(token, args, this)
173
168
 
174
- /**
175
- * Invalidates a service and its dependencies.
176
- * For request-scoped services, invalidation is handled within this context.
177
- */
178
- async invalidate(service: unknown): Promise<void> {
179
- // Check if the service is in our request context
180
- const holder = this.holderStorage.findByInstance(service)
181
- if (holder) {
182
- // Use the shared Invalidator with our request-scoped storage
183
- await this.parent
184
- .getServiceLocator()
185
- .getInvalidator()
186
- .invalidateWithStorage(holder.name, this.holderStorage, 1, {
187
- emitEvents: false, // Request-scoped services don't emit global events
188
- })
189
- return
169
+ if (error) {
170
+ throw error
171
+ }
172
+
173
+ return instance
174
+ }
190
175
  }
191
176
 
192
- // Delegate to parent for singleton services
193
- await this.parent.invalidate(service)
194
- }
177
+ // Delegate singleton/transient services to parent
178
+ const [error, instance] = await this.parent
179
+ .getInstanceResolver()
180
+ .resolveInstance(token, args, this, this.storage, this.requestId)
195
181
 
196
- /**
197
- * Checks if a service is registered.
198
- */
199
- isRegistered(token: any): boolean {
200
- return this.parent.isRegistered(token)
201
- }
182
+ if (error) {
183
+ throw error
184
+ }
202
185
 
203
- /**
204
- * Disposes this scoped container and cleans up all request-scoped instances.
205
- * This is an alias for endRequest() for IContainer compatibility.
206
- */
207
- async dispose(): Promise<void> {
208
- await this.endRequest()
186
+ return instance
209
187
  }
210
188
 
211
189
  /**
212
- * Ends the request and cleans up all request-scoped instances.
213
- * Uses the invalidation system to properly cascade to dependent singletons.
190
+ * Invalidates a service and its dependencies.
214
191
  */
215
- async endRequest(): Promise<void> {
216
- if (this.disposed) {
217
- return
192
+ async invalidate(service: unknown): Promise<void> {
193
+ // Find the service by instance in request storage
194
+ const holder = this.storage.findByInstance(service)
195
+ if (!holder) {
196
+ // Try parent storage
197
+ return this.parent.invalidate(service)
218
198
  }
219
199
 
220
- this.disposed = true
221
-
222
- // Use clearAllWithStorage to properly invalidate all request-scoped services
223
- // This will cascade invalidation to singletons that depend on request-scoped services
224
- await this.parent
225
- .getServiceLocator()
226
- .getInvalidator()
227
- .clearAllWithStorage(this.holderStorage, {
228
- waitForSettlement: true,
229
- maxRounds: 10,
230
- })
231
-
232
- // Clear the context (any remaining holders that weren't invalidated)
233
- this.requestContextHolder.clear()
234
-
235
- // Remove from parent's active requests
236
- this.parent.removeActiveRequest(this.requestId)
200
+ await this.getServiceInvalidator().invalidateWithStorage(
201
+ holder.name,
202
+ this.storage,
203
+ )
237
204
  }
238
205
 
239
206
  /**
240
- * Waits for all pending operations to complete.
207
+ * Disposes the container and cleans up all resources.
208
+ * Alias for endRequest().
241
209
  */
242
- async ready(): Promise<void> {
243
- await this.parent.ready()
210
+ async dispose(): Promise<void> {
211
+ return this.endRequest()
244
212
  }
245
213
 
246
214
  /**
247
215
  * @internal
248
- * Attempts to get an instance synchronously if it already exists and is ready.
249
- * For request-scoped services, checks this container's context first.
250
- * For other services, delegates to the parent container.
251
- *
252
- * Returns null if the instance doesn't exist or is not yet ready (still creating).
216
+ * Attempts to get an instance synchronously if it already exists.
217
+ * Checks request storage first, then delegates to parent.
253
218
  */
254
- tryGetSync<T>(token: any, args?: any): T | null {
255
- const tokenProcessor = this.parent.getServiceLocator().getTokenProcessor()
256
- const actualToken = tokenProcessor.normalizeToken(token)
257
-
258
- // Check if this is a request-scoped service
259
- if (this.isRequestScoped(actualToken)) {
260
- const serviceLocator = this.parent.getServiceLocator()
261
- const instanceName = serviceLocator.getInstanceIdentifier(token, args)
262
- const holder = this.requestContextHolder.get(instanceName)
263
- // Only return if holder exists AND is in Created status
264
- if (
265
- holder &&
266
- holder.status === InstanceStatus.Created
267
- ) {
268
- return holder.instance as T
219
+ override tryGetSync<T>(token: any, args?: any): T | null {
220
+ // Check request storage first for request-scoped services
221
+ const tokenResolver = this.getTokenResolver()
222
+ const realToken = tokenResolver.getRegistryToken(token)
223
+ const scope = this.registry.has(realToken)
224
+ ? this.registry.get(realToken).scope
225
+ : InjectableScope.Singleton
226
+
227
+ if (scope === InjectableScope.Request) {
228
+ const result = this.tryGetSyncFromStorage<T>(
229
+ token,
230
+ args,
231
+ this.storage,
232
+ this.requestId,
233
+ )
234
+ if (result !== null) {
235
+ return result
269
236
  }
270
- return null
271
237
  }
272
238
 
273
- // Delegate to parent for non-request-scoped
274
- return this.parent.tryGetSync(token, args)
239
+ // Delegate to parent for singleton/transient
240
+ return this.parent.tryGetSync<T>(token, args, this.requestId)
275
241
  }
276
242
 
277
243
  /**
278
- * Checks if a token is for a request-scoped service.
244
+ * Adds an instance to the container.
245
+ * Overrides base class to check disposed state.
279
246
  */
280
- private isRequestScoped(token: any): boolean {
281
- // Handle BoundInjectionToken and FactoryInjectionToken using TokenProcessor
282
- const tokenProcessor = this.parent.getServiceLocator().getTokenProcessor()
283
- const realToken = tokenProcessor.getRealToken(token)
284
-
285
- if (!this.registry.has(realToken)) {
286
- return false
247
+ override addInstance<T>(
248
+ token: ClassType | InjectionToken<T, any> | BoundInjectionToken<T, any>,
249
+ instance: T,
250
+ ): void {
251
+ if (this.disposed) {
252
+ throw new Error('ScopedContainer has been disposed')
287
253
  }
288
254
 
289
- const record = this.registry.get(realToken)
290
- return record.scope === InjectableScope.Request
255
+ super.addInstance(token, instance)
291
256
  }
292
257
 
293
258
  /**
294
- * Resolves a request-scoped service from this container's context.
295
- * Uses locking to prevent duplicate initialization during concurrent resolution.
259
+ * Ends the request and cleans up all request-scoped services.
296
260
  */
297
- private async resolveRequestScoped(token: any, args: unknown): Promise<any> {
298
- // Get the instance name
299
- const serviceLocator = this.parent.getServiceLocator()
300
- const instanceName = serviceLocator.getInstanceIdentifier(token, args)
301
-
302
- // Check if we already have this instance (or one is being created)
303
- const existingHolder = this.requestContextHolder.get(instanceName)
304
- if (existingHolder) {
305
- // If the holder is in error state, remove it so we can retry
306
- if (existingHolder.status === InstanceStatus.Error) {
307
- this.requestContextHolder.delete(instanceName)
308
- // Fall through to create a new instance
309
- } else {
310
- // Wait for the holder to be ready if it's still being created
311
- // This prevents race conditions where multiple concurrent calls
312
- // might try to create the same service
313
- const [error, readyHolder] =
314
- await BaseHolderManager.waitForHolderReady(existingHolder)
315
- if (error) {
316
- throw error
317
- }
318
- return readyHolder.instance
319
- }
261
+ async endRequest(): Promise<void> {
262
+ if (this.disposed) {
263
+ return
320
264
  }
321
265
 
322
- // Create new instance using parent's resolution mechanism
323
- // but store it in our request context
324
- return this.parent.resolveForRequest(token, args, this)
325
- }
326
-
327
- /**
328
- * Stores an instance in the request context.
329
- * Called by Container during request-scoped service resolution.
330
- */
331
- storeRequestInstance(
332
- instanceName: string,
333
- instance: any,
334
- holder: InstanceHolder,
335
- ): void {
336
- this.requestContextHolder.addInstance(instanceName, instance, holder)
337
- }
266
+ this.disposed = true
338
267
 
339
- /**
340
- * Gets an existing instance from the request context.
341
- * Called by Container during resolution to check for existing instances.
342
- */
343
- getRequestInstance(
344
- instanceName: string,
345
- ): InstanceHolder | undefined {
346
- return this.requestContextHolder.get(instanceName)
347
- }
268
+ // Clear all request-scoped services
269
+ await this.getServiceInvalidator().clearAllWithStorage(this.storage)
348
270
 
349
- /**
350
- * Generates a prefixed event name for request-scoped services.
351
- * Format: {requestId}:{instanceName}
352
- */
353
- getPrefixedEventName(instanceName: string): string {
354
- return `${this.requestId}:${instanceName}`
271
+ // Remove request ID from parent
272
+ this.parent.removeRequestId(this.requestId)
355
273
  }
356
274
  }
@@ -1,25 +1,27 @@
1
+ import type { Factorable, FactorableWithArgs } from '../interfaces/index.mjs'
1
2
  import type {
2
3
  ClassTypeWithInstance,
3
4
  InjectionTokenSchemaType,
4
5
  } from '../token/injection-token.mjs'
5
- import type { Factorable, FactorableWithArgs } from '../interfaces/index.mjs'
6
6
  import type { Registry } from '../token/registry.mjs'
7
7
 
8
8
  import { InjectableScope, InjectableType } from '../enums/index.mjs'
9
+ import { InjectableTokenMeta } from '../symbols/index.mjs'
9
10
  import { InjectionToken } from '../token/injection-token.mjs'
10
11
  import { globalRegistry } from '../token/registry.mjs'
11
- import { InjectableTokenMeta } from '../symbols/index.mjs'
12
12
 
13
13
  export interface FactoryOptions {
14
14
  scope?: InjectableScope
15
15
  token?: InjectionToken<any, any>
16
16
  registry?: Registry
17
+ priority?: number
17
18
  }
18
19
 
19
20
  // #1 Factory without arguments
20
21
  export function Factory<R>(options?: {
21
22
  scope?: InjectableScope
22
23
  registry?: Registry
24
+ priority?: number
23
25
  }): <T extends ClassTypeWithInstance<Factorable<R>>>(
24
26
  target: T,
25
27
  context?: ClassDecoratorContext,
@@ -30,24 +32,26 @@ export function Factory<R, S>(options: {
30
32
  scope?: InjectableScope
31
33
  token: InjectionToken<R, S>
32
34
  registry?: Registry
33
- }): R extends undefined // #2.1 Check that token has a type
34
- ? never // #2.1.1 Token must have a type
35
- : S extends InjectionTokenSchemaType // #2.2 Check that schema is an object or a record
36
- ? <T extends ClassTypeWithInstance<FactorableWithArgs<R, S>>>( // #2.2.1 Token have a schema
35
+ priority?: number
36
+ }): R extends undefined
37
+ ? never
38
+ : S extends InjectionTokenSchemaType
39
+ ? <T extends ClassTypeWithInstance<FactorableWithArgs<R, S>>>(
37
40
  target: T,
38
41
  context?: ClassDecoratorContext,
39
42
  ) => T
40
- : S extends undefined // #2.3 For a factory without schema
41
- ? <T extends ClassTypeWithInstance<Factorable<R>>>( // #2.3.1 Token without a schema
43
+ : S extends undefined
44
+ ? <T extends ClassTypeWithInstance<Factorable<R>>>(
42
45
  target: T,
43
46
  context?: ClassDecoratorContext,
44
47
  ) => T
45
- : never // #2.4 Cannot use a token without a type and schema
48
+ : never
46
49
 
47
50
  export function Factory({
48
51
  scope = InjectableScope.Singleton,
49
52
  token,
50
53
  registry = globalRegistry,
54
+ priority = 0,
51
55
  }: FactoryOptions = {}) {
52
56
  return <
53
57
  T extends ClassTypeWithInstance<
@@ -62,14 +66,14 @@ export function Factory({
62
66
  (target instanceof Function && !context)
63
67
  ) {
64
68
  throw new Error(
65
- '[ServiceLocator] @Factory decorator can only be used on classes.',
69
+ '[DI] @Factory decorator can only be used on classes.',
66
70
  )
67
71
  }
68
72
 
69
73
  let injectableToken: InjectionToken<any, any> =
70
74
  token ?? InjectionToken.create(target)
71
75
 
72
- registry.set(injectableToken, scope, target, InjectableType.Factory)
76
+ registry.set(injectableToken, scope, target, InjectableType.Factory, priority)
73
77
 
74
78
  // @ts-expect-error
75
79
  target[InjectableTokenMeta] = injectableToken
@@ -77,3 +81,4 @@ export function Factory({
77
81
  return target
78
82
  }
79
83
  }
84
+
@@ -1,4 +1,4 @@
1
- import { z } from 'zod/v4'
1
+ import type { z } from 'zod/v4'
2
2
 
3
3
  import type {
4
4
  BaseInjectionTokenSchemaType,
@@ -15,16 +15,18 @@ import type {
15
15
  import type { Registry } from '../token/registry.mjs'
16
16
 
17
17
  import { InjectableScope, InjectableType } from '../enums/index.mjs'
18
+ import { InjectableTokenMeta } from '../symbols/index.mjs'
18
19
  import { InjectionToken } from '../token/injection-token.mjs'
19
20
  import { globalRegistry } from '../token/registry.mjs'
20
- import { InjectableTokenMeta } from '../symbols/index.mjs'
21
21
 
22
22
  export interface InjectableOptions {
23
23
  scope?: InjectableScope
24
24
  token?: InjectionToken<any, any>
25
25
  schema?: InjectionTokenSchemaType
26
26
  registry?: Registry
27
+ priority?: number
27
28
  }
29
+
28
30
  // #1 Simple constructorless class
29
31
  export function Injectable(): <T extends ClassTypeWithoutArguments>(
30
32
  target: T,
@@ -33,12 +35,14 @@ export function Injectable(): <T extends ClassTypeWithoutArguments>(
33
35
  export function Injectable(options: {
34
36
  scope?: InjectableScope
35
37
  registry: Registry
38
+ priority?: number
36
39
  }): <T extends ClassTypeWithoutArguments>(
37
40
  target: T,
38
41
  context?: ClassDecoratorContext,
39
42
  ) => T
40
43
  export function Injectable(options: {
41
44
  scope: InjectableScope
45
+ priority?: number
42
46
  }): <T extends ClassTypeWithoutArguments>(
43
47
  target: T,
44
48
  context?: ClassDecoratorContext,
@@ -48,6 +52,7 @@ export function Injectable<Schema extends InjectionTokenSchemaType>(options: {
48
52
  scope?: InjectableScope
49
53
  schema: Schema
50
54
  registry?: Registry
55
+ priority?: number
51
56
  }): <T extends ClassTypeWithArgument<z.output<Schema>>>(
52
57
  target: T,
53
58
  context?: ClassDecoratorContext,
@@ -58,24 +63,24 @@ export function Injectable<Type, Schema>(options: {
58
63
  scope?: InjectableScope
59
64
  token: InjectionToken<Type, Schema>
60
65
  registry?: Registry
61
- }): Schema extends BaseInjectionTokenSchemaType // #3.1 Check that schema is an object or a record
66
+ priority?: number
67
+ }): Schema extends BaseInjectionTokenSchemaType
62
68
  ? Type extends undefined
63
- ? <T extends ClassTypeWithArgument<z.output<Schema>>>( // #3.1.1 Typeless token
69
+ ? <T extends ClassTypeWithArgument<z.output<Schema>>>(
64
70
  target: T,
65
71
  context?: ClassDecoratorContext,
66
72
  ) => T
67
- : <T extends ClassTypeWithInstanceAndArgument<Type, z.output<Schema>>>( // #3.1.2 Typed token
73
+ : <T extends ClassTypeWithInstanceAndArgument<Type, z.output<Schema>>>(
68
74
  target: T,
69
75
  context?: ClassDecoratorContext,
70
76
  ) => T
71
- : Schema extends OptionalInjectionTokenSchemaType // #3.2 Check that schema is an optional object or a record
77
+ : Schema extends OptionalInjectionTokenSchemaType
72
78
  ? Type extends undefined
73
- ? <T extends ClassTypeWithOptionalArgument<z.output<Schema>>>( // #3.2.1 Typeless token
79
+ ? <T extends ClassTypeWithOptionalArgument<z.output<Schema>>>(
74
80
  target: T,
75
81
  context?: ClassDecoratorContext,
76
82
  ) => T
77
83
  : <
78
- // #3.2.2 Typed token
79
84
  T extends ClassTypeWithInstanceAndOptionalArgument<
80
85
  Type,
81
86
  z.output<Schema>
@@ -84,18 +89,19 @@ export function Injectable<Type, Schema>(options: {
84
89
  target: T,
85
90
  context?: ClassDecoratorContext,
86
91
  ) => T
87
- : Schema extends undefined // #3.3 Check that schema is undefined
88
- ? <R extends ClassTypeWithInstance<Type>>( // #3.3.1 Token must have a type
92
+ : Schema extends undefined
93
+ ? <R extends ClassTypeWithInstance<Type>>(
89
94
  target: R,
90
95
  context?: ClassDecoratorContext,
91
96
  ) => R
92
- : never // #3.4 Cannot use a token without a type and schema
97
+ : never
93
98
 
94
99
  export function Injectable({
95
100
  scope = InjectableScope.Singleton,
96
101
  token,
97
102
  schema,
98
103
  registry = globalRegistry,
104
+ priority = 0,
99
105
  }: InjectableOptions = {}) {
100
106
  return <T extends ClassType>(
101
107
  target: T,
@@ -105,19 +111,17 @@ export function Injectable({
105
111
  (context && context.kind !== 'class') ||
106
112
  (target instanceof Function && !context)
107
113
  ) {
108
- throw new Error(
109
- '[ServiceLocator] @Injectable decorator can only be used on classes.',
110
- )
114
+ throw new Error('[DI] @Injectable decorator can only be used on classes.')
111
115
  }
112
116
  if (schema && token) {
113
117
  throw new Error(
114
- '[ServiceLocator] @Injectable decorator cannot have both a token and a schema',
118
+ '[DI] @Injectable decorator cannot have both a token and a schema',
115
119
  )
116
120
  }
117
121
  let injectableToken: InjectionToken<any, any> =
118
122
  token ?? InjectionToken.create(target, schema as InjectionTokenSchemaType)
119
123
 
120
- registry.set(injectableToken, scope, target, InjectableType.Class)
124
+ registry.set(injectableToken, scope, target, InjectableType.Class, priority)
121
125
 
122
126
  // @ts-expect-error
123
127
  target[InjectableTokenMeta] = injectableToken
@@ -1,2 +1,2 @@
1
- export * from './injectable-scope.enum.mjs'
2
- export * from './injectable-type.enum.mjs'
1
+ export { InjectableScope } from './injectable-scope.enum.mjs'
2
+ export { InjectableType } from './injectable-type.enum.mjs'
@@ -12,3 +12,4 @@ export enum InjectableScope {
12
12
  */
13
13
  Request = 'Request',
14
14
  }
15
+
@@ -2,3 +2,4 @@ export enum InjectableType {
2
2
  Class = 'Class',
3
3
  Factory = 'Factory',
4
4
  }
5
+