@navios/di 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +117 -17
  3. package/lib/browser/container/abstract-container.d.mts +112 -0
  4. package/lib/browser/container/abstract-container.d.mts.map +1 -0
  5. package/lib/browser/container/abstract-container.mjs +100 -0
  6. package/lib/browser/container/abstract-container.mjs.map +1 -0
  7. package/lib/browser/container/container.d.mts +100 -0
  8. package/lib/browser/container/container.d.mts.map +1 -0
  9. package/lib/browser/container/container.mjs +424 -0
  10. package/lib/browser/container/container.mjs.map +1 -0
  11. package/lib/browser/container/scoped-container.d.mts +93 -0
  12. package/lib/browser/container/scoped-container.d.mts.map +1 -0
  13. package/lib/browser/container/scoped-container.mjs +119 -0
  14. package/lib/browser/container/scoped-container.mjs.map +1 -0
  15. package/lib/browser/decorators/factory.decorator.d.mts +26 -0
  16. package/lib/browser/decorators/factory.decorator.d.mts.map +1 -0
  17. package/lib/browser/decorators/factory.decorator.mjs +20 -0
  18. package/lib/browser/decorators/factory.decorator.mjs.map +1 -0
  19. package/lib/browser/decorators/injectable.decorator.d.mts +38 -0
  20. package/lib/browser/decorators/injectable.decorator.d.mts.map +1 -0
  21. package/lib/browser/decorators/injectable.decorator.mjs +21 -0
  22. package/lib/browser/decorators/injectable.decorator.mjs.map +1 -0
  23. package/lib/browser/enums/injectable-scope.enum.d.mts +18 -0
  24. package/lib/browser/enums/injectable-scope.enum.d.mts.map +1 -0
  25. package/lib/browser/enums/injectable-scope.enum.mjs +20 -0
  26. package/lib/browser/enums/injectable-scope.enum.mjs.map +1 -0
  27. package/lib/browser/enums/injectable-type.enum.d.mts +8 -0
  28. package/lib/browser/enums/injectable-type.enum.d.mts.map +1 -0
  29. package/lib/browser/enums/injectable-type.enum.mjs +10 -0
  30. package/lib/browser/enums/injectable-type.enum.mjs.map +1 -0
  31. package/lib/browser/errors/di-error.d.mts +43 -0
  32. package/lib/browser/errors/di-error.d.mts.map +1 -0
  33. package/lib/browser/errors/di-error.mjs +98 -0
  34. package/lib/browser/errors/di-error.mjs.map +1 -0
  35. package/lib/browser/event-emitter.d.mts +16 -0
  36. package/lib/browser/event-emitter.d.mts.map +1 -0
  37. package/lib/browser/event-emitter.mjs +320 -0
  38. package/lib/browser/event-emitter.mjs.map +1 -0
  39. package/lib/browser/index.d.mts +37 -1558
  40. package/lib/browser/index.mjs +29 -2749
  41. package/lib/browser/interfaces/container.interface.d.mts +59 -0
  42. package/lib/browser/interfaces/container.interface.d.mts.map +1 -0
  43. package/lib/browser/interfaces/factory.interface.d.mts +14 -0
  44. package/lib/browser/interfaces/factory.interface.d.mts.map +1 -0
  45. package/lib/browser/interfaces/on-service-destroy.interface.d.mts +7 -0
  46. package/lib/browser/interfaces/on-service-destroy.interface.d.mts.map +1 -0
  47. package/lib/browser/interfaces/on-service-init.interface.d.mts +7 -0
  48. package/lib/browser/interfaces/on-service-init.interface.d.mts.map +1 -0
  49. package/lib/browser/internal/context/async-local-storage.browser.mjs +20 -0
  50. package/lib/browser/internal/context/async-local-storage.browser.mjs.map +1 -0
  51. package/lib/browser/internal/context/async-local-storage.d.mts +9 -0
  52. package/lib/browser/internal/context/async-local-storage.d.mts.map +1 -0
  53. package/lib/browser/internal/context/async-local-storage.types.d.mts +11 -0
  54. package/lib/browser/internal/context/async-local-storage.types.d.mts.map +1 -0
  55. package/lib/browser/internal/context/factory-context.d.mts +23 -0
  56. package/lib/browser/internal/context/factory-context.d.mts.map +1 -0
  57. package/lib/browser/internal/context/resolution-context.d.mts +43 -0
  58. package/lib/browser/internal/context/resolution-context.d.mts.map +1 -0
  59. package/lib/browser/internal/context/resolution-context.mjs +56 -0
  60. package/lib/browser/internal/context/resolution-context.mjs.map +1 -0
  61. package/lib/browser/internal/context/service-initialization-context.d.mts +48 -0
  62. package/lib/browser/internal/context/service-initialization-context.d.mts.map +1 -0
  63. package/lib/browser/internal/context/sync-local-storage.mjs +53 -0
  64. package/lib/browser/internal/context/sync-local-storage.mjs.map +1 -0
  65. package/lib/browser/internal/core/instance-resolver.d.mts +119 -0
  66. package/lib/browser/internal/core/instance-resolver.d.mts.map +1 -0
  67. package/lib/browser/internal/core/instance-resolver.mjs +306 -0
  68. package/lib/browser/internal/core/instance-resolver.mjs.map +1 -0
  69. package/lib/browser/internal/core/name-resolver.d.mts +52 -0
  70. package/lib/browser/internal/core/name-resolver.d.mts.map +1 -0
  71. package/lib/browser/internal/core/name-resolver.mjs +118 -0
  72. package/lib/browser/internal/core/name-resolver.mjs.map +1 -0
  73. package/lib/browser/internal/core/scope-tracker.d.mts +65 -0
  74. package/lib/browser/internal/core/scope-tracker.d.mts.map +1 -0
  75. package/lib/browser/internal/core/scope-tracker.mjs +120 -0
  76. package/lib/browser/internal/core/scope-tracker.mjs.map +1 -0
  77. package/lib/browser/internal/core/service-initializer.d.mts +44 -0
  78. package/lib/browser/internal/core/service-initializer.d.mts.map +1 -0
  79. package/lib/browser/internal/core/service-initializer.mjs +109 -0
  80. package/lib/browser/internal/core/service-initializer.mjs.map +1 -0
  81. package/lib/browser/internal/core/service-invalidator.d.mts +81 -0
  82. package/lib/browser/internal/core/service-invalidator.d.mts.map +1 -0
  83. package/lib/browser/internal/core/service-invalidator.mjs +142 -0
  84. package/lib/browser/internal/core/service-invalidator.mjs.map +1 -0
  85. package/lib/browser/internal/core/token-resolver.d.mts +54 -0
  86. package/lib/browser/internal/core/token-resolver.d.mts.map +1 -0
  87. package/lib/browser/internal/core/token-resolver.mjs +77 -0
  88. package/lib/browser/internal/core/token-resolver.mjs.map +1 -0
  89. package/lib/browser/internal/holder/holder-storage.interface.d.mts +99 -0
  90. package/lib/browser/internal/holder/holder-storage.interface.d.mts.map +1 -0
  91. package/lib/browser/internal/holder/instance-holder.d.mts +101 -0
  92. package/lib/browser/internal/holder/instance-holder.d.mts.map +1 -0
  93. package/lib/browser/internal/holder/instance-holder.mjs +19 -0
  94. package/lib/browser/internal/holder/instance-holder.mjs.map +1 -0
  95. package/lib/browser/internal/holder/unified-storage.d.mts +53 -0
  96. package/lib/browser/internal/holder/unified-storage.d.mts.map +1 -0
  97. package/lib/browser/internal/holder/unified-storage.mjs +144 -0
  98. package/lib/browser/internal/holder/unified-storage.mjs.map +1 -0
  99. package/lib/browser/internal/lifecycle/circular-detector.d.mts +39 -0
  100. package/lib/browser/internal/lifecycle/circular-detector.d.mts.map +1 -0
  101. package/lib/browser/internal/lifecycle/circular-detector.mjs +55 -0
  102. package/lib/browser/internal/lifecycle/circular-detector.mjs.map +1 -0
  103. package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts +18 -0
  104. package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts.map +1 -0
  105. package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs +43 -0
  106. package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs.map +1 -0
  107. package/lib/browser/internal/stub-factory-class.d.mts +14 -0
  108. package/lib/browser/internal/stub-factory-class.d.mts.map +1 -0
  109. package/lib/browser/internal/stub-factory-class.mjs +18 -0
  110. package/lib/browser/internal/stub-factory-class.mjs.map +1 -0
  111. package/lib/browser/symbols/injectable-token.d.mts +5 -0
  112. package/lib/browser/symbols/injectable-token.d.mts.map +1 -0
  113. package/lib/browser/symbols/injectable-token.mjs +6 -0
  114. package/lib/browser/symbols/injectable-token.mjs.map +1 -0
  115. package/lib/browser/token/injection-token.d.mts +55 -0
  116. package/lib/browser/token/injection-token.d.mts.map +1 -0
  117. package/lib/browser/token/injection-token.mjs +100 -0
  118. package/lib/browser/token/injection-token.mjs.map +1 -0
  119. package/lib/browser/token/registry.d.mts +37 -0
  120. package/lib/browser/token/registry.d.mts.map +1 -0
  121. package/lib/browser/token/registry.mjs +86 -0
  122. package/lib/browser/token/registry.mjs.map +1 -0
  123. package/lib/browser/utils/default-injectors.d.mts +12 -0
  124. package/lib/browser/utils/default-injectors.d.mts.map +1 -0
  125. package/lib/browser/utils/default-injectors.mjs +13 -0
  126. package/lib/browser/utils/default-injectors.mjs.map +1 -0
  127. package/lib/browser/utils/get-injectable-token.d.mts +9 -0
  128. package/lib/browser/utils/get-injectable-token.d.mts.map +1 -0
  129. package/lib/browser/utils/get-injectable-token.mjs +13 -0
  130. package/lib/browser/utils/get-injectable-token.mjs.map +1 -0
  131. package/lib/browser/utils/get-injectors.d.mts +55 -0
  132. package/lib/browser/utils/get-injectors.d.mts.map +1 -0
  133. package/lib/browser/utils/get-injectors.mjs +121 -0
  134. package/lib/browser/utils/get-injectors.mjs.map +1 -0
  135. package/lib/browser/utils/types.d.mts +23 -0
  136. package/lib/browser/utils/types.d.mts.map +1 -0
  137. package/lib/{container-DAKOvAgr.mjs → container-8-z89TyQ.mjs} +1325 -1462
  138. package/lib/container-8-z89TyQ.mjs.map +1 -0
  139. package/lib/{container-Bp1W-pWJ.d.mts → container-CNiqesCL.d.mts} +598 -617
  140. package/lib/container-CNiqesCL.d.mts.map +1 -0
  141. package/lib/{container-DENMeJ87.cjs → container-CaY2fDuk.cjs} +1369 -1512
  142. package/lib/container-CaY2fDuk.cjs.map +1 -0
  143. package/lib/{container-YPwvmlK2.d.cts → container-D-0Ho3qL.d.cts} +598 -612
  144. package/lib/container-D-0Ho3qL.d.cts.map +1 -0
  145. package/lib/index.cjs +13 -15
  146. package/lib/index.cjs.map +1 -1
  147. package/lib/index.d.cts +58 -223
  148. package/lib/index.d.cts.map +1 -1
  149. package/lib/index.d.mts +62 -222
  150. package/lib/index.d.mts.map +1 -1
  151. package/lib/index.mjs +5 -6
  152. package/lib/index.mjs.map +1 -1
  153. package/lib/testing/index.cjs +569 -311
  154. package/lib/testing/index.cjs.map +1 -1
  155. package/lib/testing/index.d.cts +370 -41
  156. package/lib/testing/index.d.cts.map +1 -1
  157. package/lib/testing/index.d.mts +370 -41
  158. package/lib/testing/index.d.mts.map +1 -1
  159. package/lib/testing/index.mjs +568 -305
  160. package/lib/testing/index.mjs.map +1 -1
  161. package/package.json +2 -1
  162. package/src/__tests__/circular-detector.spec.mts +193 -0
  163. package/src/__tests__/concurrent.spec.mts +368 -0
  164. package/src/__tests__/container.spec.mts +32 -30
  165. package/src/__tests__/di-error.spec.mts +351 -0
  166. package/src/__tests__/e2e.browser.spec.mts +0 -4
  167. package/src/__tests__/e2e.spec.mts +10 -19
  168. package/src/__tests__/event-emitter.spec.mts +232 -109
  169. package/src/__tests__/get-injectors.spec.mts +250 -39
  170. package/src/__tests__/injection-token.spec.mts +293 -349
  171. package/src/__tests__/library-findings.spec.mts +8 -8
  172. package/src/__tests__/registry.spec.mts +358 -210
  173. package/src/__tests__/resolution-context.spec.mts +255 -0
  174. package/src/__tests__/scope-tracker.spec.mts +598 -0
  175. package/src/__tests__/scope-upgrade.spec.mts +808 -0
  176. package/src/__tests__/scoped-container.spec.mts +595 -0
  177. package/src/__tests__/test-container.spec.mts +293 -0
  178. package/src/__tests__/token-resolver.spec.mts +207 -0
  179. package/src/__tests__/unified-storage.spec.mts +535 -0
  180. package/src/__tests__/unit-test-container.spec.mts +405 -0
  181. package/src/__type-tests__/container.spec-d.mts +180 -0
  182. package/src/__type-tests__/factory.spec-d.mts +15 -3
  183. package/src/__type-tests__/inject.spec-d.mts +115 -20
  184. package/src/__type-tests__/injectable.spec-d.mts +69 -52
  185. package/src/__type-tests__/injection-token.spec-d.mts +176 -0
  186. package/src/__type-tests__/scoped-container.spec-d.mts +212 -0
  187. package/src/container/abstract-container.mts +327 -0
  188. package/src/container/container.mts +142 -170
  189. package/src/container/scoped-container.mts +126 -208
  190. package/src/decorators/factory.decorator.mts +16 -11
  191. package/src/decorators/injectable.decorator.mts +20 -16
  192. package/src/enums/index.mts +2 -2
  193. package/src/enums/injectable-scope.enum.mts +1 -0
  194. package/src/enums/injectable-type.enum.mts +1 -0
  195. package/src/errors/di-error.mts +96 -0
  196. package/src/event-emitter.mts +3 -27
  197. package/src/index.mts +6 -153
  198. package/src/interfaces/container.interface.mts +13 -0
  199. package/src/interfaces/factory.interface.mts +1 -1
  200. package/src/interfaces/index.mts +1 -1
  201. package/src/internal/context/async-local-storage.mts +3 -2
  202. package/src/internal/context/async-local-storage.types.mts +1 -0
  203. package/src/internal/context/factory-context.mts +1 -0
  204. package/src/internal/context/index.mts +3 -1
  205. package/src/internal/context/resolution-context.mts +1 -0
  206. package/src/internal/context/service-initialization-context.mts +43 -0
  207. package/src/internal/core/index.mts +5 -4
  208. package/src/internal/core/instance-resolver.mts +460 -302
  209. package/src/internal/core/name-resolver.mts +196 -0
  210. package/src/internal/core/scope-tracker.mts +242 -0
  211. package/src/internal/core/{instantiator.mts → service-initializer.mts} +51 -29
  212. package/src/internal/core/service-invalidator.mts +290 -0
  213. package/src/internal/core/token-resolver.mts +122 -0
  214. package/src/internal/holder/holder-storage.interface.mts +11 -5
  215. package/src/internal/holder/index.mts +2 -5
  216. package/src/internal/holder/instance-holder.mts +1 -3
  217. package/src/internal/holder/unified-storage.mts +245 -0
  218. package/src/internal/index.mts +2 -1
  219. package/src/internal/lifecycle/circular-detector.mts +1 -0
  220. package/src/internal/lifecycle/index.mts +1 -1
  221. package/src/internal/lifecycle/lifecycle-event-bus.mts +1 -0
  222. package/src/internal/stub-factory-class.mts +16 -0
  223. package/src/symbols/injectable-token.mts +3 -1
  224. package/src/testing/index.mts +2 -0
  225. package/src/testing/test-container.mts +546 -85
  226. package/src/testing/types.mts +117 -0
  227. package/src/testing/unit-test-container.mts +509 -0
  228. package/src/token/injection-token.mts +41 -4
  229. package/src/token/registry.mts +75 -9
  230. package/src/utils/default-injectors.mts +16 -0
  231. package/src/utils/get-injectable-token.mts +2 -3
  232. package/src/utils/get-injectors.mts +26 -15
  233. package/src/utils/index.mts +3 -1
  234. package/src/utils/types.mts +1 -0
  235. package/tsdown.config.mts +11 -1
  236. package/lib/browser/index.d.mts.map +0 -1
  237. package/lib/browser/index.mjs.map +0 -1
  238. package/lib/container-Bp1W-pWJ.d.mts.map +0 -1
  239. package/lib/container-DAKOvAgr.mjs.map +0 -1
  240. package/lib/container-DENMeJ87.cjs.map +0 -1
  241. package/lib/container-YPwvmlK2.d.cts.map +0 -1
  242. package/src/__tests__/async-local-storage.browser.spec.mts +0 -166
  243. package/src/__tests__/async-local-storage.spec.mts +0 -333
  244. package/src/__tests__/errors.spec.mts +0 -87
  245. package/src/__tests__/factory.spec.mts +0 -137
  246. package/src/__tests__/injectable.spec.mts +0 -246
  247. package/src/__tests__/request-scope.spec.mts +0 -416
  248. package/src/__tests__/service-instantiator.spec.mts +0 -410
  249. package/src/__tests__/service-locator-event-bus.spec.mts +0 -242
  250. package/src/__tests__/service-locator-manager.spec.mts +0 -300
  251. package/src/__tests__/service-locator.spec.mts +0 -966
  252. package/src/__tests__/unified-api.spec.mts +0 -130
  253. package/src/browser.mts +0 -11
  254. package/src/injectors.mts +0 -18
  255. package/src/internal/context/request-context.mts +0 -225
  256. package/src/internal/core/invalidator.mts +0 -437
  257. package/src/internal/core/service-locator.mts +0 -202
  258. package/src/internal/core/token-processor.mts +0 -252
  259. package/src/internal/holder/base-holder-manager.mts +0 -334
  260. package/src/internal/holder/holder-manager.mts +0 -85
  261. package/src/internal/holder/request-storage.mts +0 -127
  262. package/src/internal/holder/singleton-storage.mts +0 -92
  263. package/src/testing/README.md +0 -80
  264. package/src/testing/__tests__/test-container.spec.mts +0 -173
package/CHANGELOG.md CHANGED
@@ -5,6 +5,93 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.9.0] - 2025-12-21
9
+
10
+ ### Added
11
+
12
+ - **Priority System for Multiple Registrations**: Services can now be registered with priority levels. Higher priority wins when multiple registrations exist for the same token
13
+ - New `priority` option in `@Injectable()` decorator
14
+ - `Registry.getAll(token)` method to retrieve all registrations sorted by priority
15
+ - `FactoryRecord` now includes `priority` field
16
+ - **Unified Storage Architecture**: Replaced multiple storage classes with a single `UnifiedStorage` class
17
+ - Replaces `HolderManager`, `SingletonStorage`, `RequestStorage`, and `RequestContext`
18
+ - Simpler mental model: one storage class regardless of scope
19
+ - Reverse dependency index for O(1) dependent lookups (vs O(n) iteration)
20
+ - Consistent API across all scopes
21
+ - **Enhanced Testing Utilities**: Significantly extended `TestContainer` with new assertion helpers
22
+ - Fluent binding API extended with `toFactory()` method
23
+ - Assertion helpers: `expectResolved()`, `expectNotResolved()`, `expectSingleton()`, `expectTransient()`, `expectRequestScoped()`
24
+ - Lifecycle assertions: `expectInitialized()`, `expectDestroyed()`, `expectNotDestroyed()`
25
+ - Method call tracking: `recordMethodCall()`, `expectCalled()`, `expectCalledWith()`, `expectCallCount()`, `getMethodCalls()`, `getServiceStats()`
26
+ - Dependency graph utilities: `getDependencyGraph()`, `getSimplifiedDependencyGraph()`
27
+ - **New UnitTestContainer**: Strict isolated unit testing container with auto-tracking
28
+ - Automatic method call tracking via Proxy
29
+ - Strict mode (default): unregistered dependencies throw errors
30
+ - Auto-mocking mode: unregistered dependencies return mock proxies
31
+ - Simplified provider-based configuration
32
+ - **Enhanced Error Messages**: Extended `DIErrorCode` enum with 8 new error types
33
+ - `TokenValidationError`: Zod schema validation failed
34
+ - `TokenSchemaRequiredError`: Schema args required but not provided
35
+ - `ClassNotInjectable`: Missing `@Injectable` decorator
36
+ - `ScopeMismatchError`: Wrong container for scope
37
+ - `PriorityConflictError`: Multiple same-priority registrations
38
+ - `StorageError`: Storage operation failed
39
+ - `InitializationError`: Service init failed
40
+ - `DependencyResolutionError`: Dependency chain error
41
+ - **New Internal Components**:
42
+ - `NameResolver`: Generates deterministic instance names
43
+ - `ScopeTracker`: Tracks and validates scope relationships
44
+ - `AbstractContainer`: Base class for containers
45
+ - **New Container Methods**: Direct access to internal components
46
+ - `getStorage()`: Returns `UnifiedStorage`
47
+ - `getServiceInitializer()`: Returns `ServiceInitializer`
48
+ - `getServiceInvalidator()`: Returns `ServiceInvalidator`
49
+ - `getTokenResolver()`: Returns `TokenResolver`
50
+ - `getNameResolver()`: Returns `NameResolver`
51
+ - `getScopeTracker()`: Returns `ScopeTracker`
52
+ - `getEventBus()`: Returns `LifecycleEventBus`
53
+ - `getInstanceResolver()`: Returns `InstanceResolver`
54
+
55
+ ### Changed
56
+
57
+ - **BREAKING**: `ServiceLocator` wrapper class removed
58
+ - Container now uses components directly (no ServiceLocator wrapper)
59
+ - `container.getServiceLocator()` method removed
60
+ - Use direct component access methods instead (e.g., `container.getStorage()`)
61
+ - **BREAKING**: Internal component renames
62
+ - `Instantiator` → `ServiceInitializer`
63
+ - `Invalidator` → `ServiceInvalidator`
64
+ - `TokenProcessor` → `TokenResolver`
65
+ - `HolderManager` → `UnifiedStorage`
66
+ - `SingletonStorage` → `UnifiedStorage` (same class, different instance)
67
+ - `RequestStorage` → `UnifiedStorage` (same class, different instance)
68
+ - `RequestContext` → merged into `UnifiedStorage`
69
+ - **BREAKING**: `Container.removeActiveRequest()` renamed to `Container.removeRequestId()`
70
+ - **Registry Changes**:
71
+ - `Registry.set()` now accepts optional `priority` parameter (5 params instead of 4)
72
+ - `Registry` stores `FactoryRecord[]` per token instead of single `FactoryRecord`
73
+ - `FactoryRecord` interface extended with `priority: number` field
74
+ - **Scope Error Handling**: Request scope violations now throw dedicated `ScopeMismatchError` with structured context instead of generic errors
75
+ - **Build Configuration**: Updated tsdown config with external dependencies
76
+ - Node build: external `node:async_hooks` and `zod`
77
+ - Browser build: external `zod`
78
+
79
+ ### Performance
80
+
81
+ - **Reverse Dependency Index**: O(1) dependent lookup during invalidation (vs O(n) iteration)
82
+ - **Priority Cache**: Registry maintains `highestPriority` Map for fast access to winning registration
83
+ - **Unified Storage**: Single storage class eliminates wrapper overhead
84
+ - **No ServiceLocator Wrapper**: Container directly uses components, reducing indirection
85
+
86
+ ### Migration
87
+
88
+ - Replace `container.getServiceLocator()` with direct component access methods
89
+ - Replace `container.removeActiveRequest()` with `container.removeRequestId()`
90
+ - Update internal class references if you were using them directly
91
+ - Consider using new testing utilities (`TestContainer` assertions, `UnitTestContainer`)
92
+ - Use `priority` option for service registration if needed
93
+ - Use `registry.getAll(token)` if you need all registrations, not just highest priority
94
+
8
95
  ## [0.8.0] - 2025-12-21
9
96
 
10
97
  ### Added
package/README.md CHANGED
@@ -10,10 +10,12 @@ A powerful, type-safe dependency injection library for TypeScript applications.
10
10
  - **Factory Pattern**: Create instances using factory classes
11
11
  - **Injection Tokens**: Flexible token-based dependency resolution
12
12
  - **Scoped Instances**: Singleton, transient, and request scopes
13
+ - **Priority System**: Register multiple services per token with priority levels
13
14
  - **Async/Sync Injection**: Both synchronous and asynchronous dependency resolution
14
15
  - **Container API**: Simple container-based API for dependency management
15
16
  - **Request Context**: Manage request-scoped services with automatic cleanup via ScopedContainer
16
17
  - **Circular Dependency Detection**: Automatic detection and helpful error messages for circular dependencies
18
+ - **Enhanced Testing**: Comprehensive test utilities with assertion helpers and auto-tracking
17
19
 
18
20
  ## Installation
19
21
 
@@ -132,6 +134,13 @@ class RequestService {}
132
134
  @Injectable({ token: MyToken })
133
135
  class TokenizedService {}
134
136
 
137
+ // With priority (higher priority wins when multiple registrations exist)
138
+ @Injectable({ priority: 100 })
139
+ class DefaultService {}
140
+
141
+ @Injectable({ priority: 200 }) // This wins
142
+ class OverrideService {}
143
+
135
144
  // With schema for constructor arguments
136
145
  const configSchema = z.object({
137
146
  host: z.string(),
@@ -426,6 +435,9 @@ import { Container, Registry } from '@navios/di'
426
435
 
427
436
  const customRegistry = new Registry()
428
437
  const container = new Container(customRegistry)
438
+
439
+ // Get all registrations for a token (sorted by priority, highest first)
440
+ const allRegistrations = customRegistry.getAll(MyToken)
429
441
  ```
430
442
 
431
443
  ### Error Handling
@@ -444,6 +456,13 @@ try {
444
456
  case DIErrorCode.InstanceDestroying:
445
457
  console.error('Service is being destroyed')
446
458
  break
459
+ case DIErrorCode.ScopeMismatchError:
460
+ console.error('Wrong container for scope')
461
+ break
462
+ case DIErrorCode.TokenValidationError:
463
+ console.error('Token validation failed')
464
+ break
465
+ // ... and more error codes
447
466
  }
448
467
  }
449
468
  }
@@ -469,11 +488,21 @@ const newService = await container.get(MyService)
469
488
  - `dispose(): Promise<void>` - Clean up all resources
470
489
  - `clear(): Promise<void>` - Clear all instances and bindings
471
490
  - `isRegistered(token: any): boolean` - Check if service is registered
472
- - `getServiceLocator(): ServiceLocator` - Get underlying service locator
491
+ - `calculateInstanceName(token, args?): string | null` - Calculate the instance name for a token (returns null for unresolved factory tokens or validation errors)
473
492
  - `getRegistry(): Registry` - Get the registry
474
493
  - `beginRequest(requestId: string, metadata?, priority?): ScopedContainer` - Begin request context
475
494
  - `getActiveRequestIds(): ReadonlySet<string>` - Get active request IDs
476
495
  - `hasActiveRequest(requestId: string): boolean` - Check if request is active
496
+ - `removeRequestId(requestId: string): void` - Remove a request ID from tracking
497
+ - **Component Access Methods** (for advanced usage):
498
+ - `getStorage(): UnifiedStorage` - Get storage instance
499
+ - `getServiceInitializer(): ServiceInitializer` - Get service initializer
500
+ - `getServiceInvalidator(): ServiceInvalidator` - Get service invalidator
501
+ - `getTokenResolver(): TokenResolver` - Get token resolver
502
+ - `getNameResolver(): NameResolver` - Get name resolver
503
+ - `getScopeTracker(): ScopeTracker` - Get scope tracker
504
+ - `getEventBus(): LifecycleEventBus` - Get event bus
505
+ - `getInstanceResolver(): InstanceResolver` - Get instance resolver
477
506
 
478
507
  ### ScopedContainer
479
508
 
@@ -483,12 +512,12 @@ const newService = await container.get(MyService)
483
512
  - `dispose(): Promise<void>` - Alias for endRequest()
484
513
  - `ready(): Promise<void>` - Wait for pending operations
485
514
  - `isRegistered(token: any): boolean` - Check if service is registered
515
+ - `calculateInstanceName(token, args?): string | null` - Calculate the instance name for a token (returns null for unresolved factory tokens or validation errors)
486
516
  - `getMetadata(key: string): any` - Get request metadata
487
517
  - `setMetadata(key: string, value: any): void` - Set request metadata
488
- - `addInstance(token: InjectionToken, instance: any): void` - Add pre-prepared instance
489
518
  - `getRequestId(): string` - Get the request ID
490
519
  - `getParent(): Container` - Get the parent container
491
- - `getRequestContextHolder(): RequestContext` - Get the underlying request context
520
+ - `getStorage(): UnifiedStorage` - Get the underlying storage instance
492
521
 
493
522
  ### Injectable Decorator
494
523
 
@@ -498,6 +527,7 @@ const newService = await container.get(MyService)
498
527
  - `token?: InjectionToken` - Custom injection token
499
528
  - `schema?: ZodSchema` - Zod schema for constructor arguments
500
529
  - `registry?: Registry` - Custom registry
530
+ - `priority?: number` - Priority level (higher wins when multiple registrations exist, default: 0)
501
531
  - Note: Cannot use both `token` and `schema` options together
502
532
 
503
533
  ### Factory Decorator
@@ -528,24 +558,21 @@ const newService = await container.get(MyService)
528
558
  - `OnServiceInit` - Implement `onServiceInit(): Promise<void> | void`
529
559
  - `OnServiceDestroy` - Implement `onServiceDestroy(): Promise<void> | void`
530
560
 
531
- ### RequestContext
561
+ ### Registry
532
562
 
533
- - `requestId: string` - Unique request identifier
534
- - `priority: number` - Priority for resolution
535
- - `metadata: Map<string, any>` - Request metadata
536
- - `createdAt: number` - Creation timestamp
537
- - `addInstance(token, instance): void` - Add pre-prepared instance
538
- - `get(instanceName): InstanceHolder | undefined` - Get instance holder
539
- - `set(instanceName, holder): void` - Set instance holder
540
- - `has(instanceName): boolean` - Check if instance exists
541
- - `clear(): void` - Clear all instances
542
- - `getMetadata(key): any` - Get metadata value
543
- - `setMetadata(key, value): void` - Set metadata value
563
+ - `set(token, scope, target, type, priority?): void` - Register a service factory
564
+ - `get(token): FactoryRecord` - Get the highest priority factory record for a token
565
+ - `getAll(token): FactoryRecord[]` - Get all factory records for a token (sorted by priority, highest first)
566
+ - `has(token): boolean` - Check if a token is registered
567
+ - `delete(token): void` - Remove all registrations for a token
568
+ - `updateScope(token, scope): boolean` - Update the scope of an already registered factory
544
569
 
545
570
  ## Testing
546
571
 
547
572
  ### TestContainer
548
573
 
574
+ `TestContainer` extends `Container` with enhanced testing utilities, including fluent binding API, assertion helpers, method call tracking, and dependency graph inspection.
575
+
549
576
  ```typescript
550
577
  import { TestContainer } from '@navios/di/testing'
551
578
 
@@ -557,19 +584,89 @@ describe('UserService', () => {
557
584
  })
558
585
 
559
586
  afterEach(async () => {
560
- await container.dispose()
587
+ await container.clear()
561
588
  })
562
589
 
563
590
  it('should create user', async () => {
564
- // Bind mock
591
+ // Fluent binding API
565
592
  container.bind(DatabaseService).toValue({
566
593
  save: vi.fn().mockResolvedValue({ id: '1' }),
567
594
  })
595
+
596
+ // Or bind to class
597
+ container.bind(UserService).toClass(MockUserService)
598
+
599
+ // Or bind to factory
600
+ container.bind(ConfigToken).toFactory(() => ({ apiKey: 'test' }))
568
601
 
569
602
  const userService = await container.get(UserService)
570
603
  const user = await userService.create({ name: 'John' })
571
604
 
572
605
  expect(user.id).toBe('1')
606
+
607
+ // Assertion helpers
608
+ container.expectResolved(UserService)
609
+ container.expectSingleton(UserService)
610
+ container.expectInitialized(UserService)
611
+
612
+ // Method call tracking
613
+ container.recordMethodCall(UserService, 'create', [{ name: 'John' }], user)
614
+ container.expectCalled(UserService, 'create')
615
+ container.expectCalledWith(UserService, 'create', [{ name: 'John' }])
616
+ container.expectCallCount(UserService, 'create', 1)
617
+
618
+ // Dependency graph inspection
619
+ const graph = container.getDependencyGraph()
620
+ console.log(graph)
621
+ })
622
+ })
623
+ ```
624
+
625
+ ### UnitTestContainer
626
+
627
+ `UnitTestContainer` provides strict isolated unit testing with automatic method call tracking via Proxy. Only services explicitly provided can be resolved.
628
+
629
+ ```typescript
630
+ import { UnitTestContainer } from '@navios/di/testing'
631
+
632
+ describe('UserService Unit Tests', () => {
633
+ let container: UnitTestContainer
634
+
635
+ beforeEach(() => {
636
+ container = new UnitTestContainer({
637
+ providers: [
638
+ { token: UserService, useClass: MockUserService },
639
+ { token: ConfigToken, useValue: { apiUrl: 'test' } },
640
+ { token: ApiClient, useFactory: () => new MockApiClient() },
641
+ ],
642
+ })
643
+ })
644
+
645
+ afterEach(async () => {
646
+ await container.dispose()
647
+ })
648
+
649
+ it('should track method calls automatically', async () => {
650
+ const service = await container.get(UserService)
651
+ await service.findUser('123')
652
+
653
+ // Auto-tracked assertions (no manual recording needed)
654
+ container.expectCalled(UserService, 'findUser')
655
+ container.expectCalledWith(UserService, 'findUser', ['123'])
656
+ container.expectNotCalled(UserService, 'deleteUser')
657
+ })
658
+
659
+ it('should throw on unregistered dependencies (strict mode)', async () => {
660
+ // Strict mode (default): unregistered dependencies throw
661
+ await expect(container.get(UnregisteredService)).rejects.toThrow(DIError)
662
+ })
663
+
664
+ it('should auto-mock unregistered dependencies', async () => {
665
+ // Enable auto-mocking mode
666
+ container.enableAutoMocking()
667
+ const mock = await container.get(UnregisteredService)
668
+ container.expectAutoMocked(UnregisteredService)
669
+ container.disableAutoMocking()
573
670
  })
574
671
  })
575
672
  ```
@@ -584,6 +681,9 @@ describe('UserService', () => {
584
681
  6. **Prefer singletons** - Unless you specifically need new instances each time
585
682
  7. **Use factories** - For complex object creation logic
586
683
  8. **Leverage ScopedContainer** - For request-scoped data and cleanup
684
+ 9. **Use priority system** - When you need multiple implementations of the same token, use priority to control which one wins
685
+ 10. **Use TestContainer for integration tests** - Provides comprehensive assertion helpers and dependency graph inspection
686
+ 11. **Use UnitTestContainer for unit tests** - Provides strict isolation and automatic method call tracking
587
687
 
588
688
  ## License
589
689
 
@@ -0,0 +1,112 @@
1
+ import { BoundInjectionToken, ClassType, ClassTypeWithArgument, FactoryInjectionToken, InjectionToken, InjectionTokenSchemaType } from "../token/injection-token.mjs";
2
+ import { Join, UnionToArray } from "../utils/types.mjs";
3
+ import { IContainer } from "../interfaces/container.interface.mjs";
4
+ import { InjectableScope } from "../enums/injectable-scope.enum.mjs";
5
+ import { Factorable } from "../interfaces/factory.interface.mjs";
6
+ import { Registry } from "../token/registry.mjs";
7
+ import { NameResolver } from "../internal/core/name-resolver.mjs";
8
+ import { ServiceInvalidator } from "../internal/core/service-invalidator.mjs";
9
+ import { TokenResolver } from "../internal/core/token-resolver.mjs";
10
+ import { UnifiedStorage } from "../internal/holder/unified-storage.mjs";
11
+ import { ZodType, z } from "zod/v4";
12
+
13
+ //#region src/container/abstract-container.d.mts
14
+
15
+ /**
16
+ * Abstract base class for dependency injection containers.
17
+ *
18
+ * Provides shared implementation for common container operations.
19
+ * Both Container and ScopedContainer extend this class.
20
+ */
21
+ declare abstract class AbstractContainer implements IContainer {
22
+ /**
23
+ * The default scope used when adding instances without explicit registration.
24
+ */
25
+ protected abstract readonly defaultScope: InjectableScope;
26
+ /**
27
+ * The request ID for scoped containers, undefined for root container.
28
+ */
29
+ protected abstract readonly requestId: string | undefined;
30
+ /**
31
+ * Gets the storage for this container.
32
+ */
33
+ abstract getStorage(): UnifiedStorage;
34
+ /**
35
+ * Gets the registry for this container.
36
+ */
37
+ protected abstract getRegistry(): Registry;
38
+ /**
39
+ * Gets the token resolver.
40
+ */
41
+ protected abstract getTokenResolver(): TokenResolver;
42
+ /**
43
+ * Gets the name resolver.
44
+ */
45
+ protected abstract getNameResolver(): NameResolver;
46
+ /**
47
+ * Gets the service invalidator.
48
+ */
49
+ protected abstract getServiceInvalidator(): ServiceInvalidator;
50
+ /**
51
+ * Gets an instance from the container.
52
+ */
53
+ abstract get<T extends ClassType>(token: T): InstanceType<T> extends Factorable<infer R> ? Promise<R> : Promise<InstanceType<T>>;
54
+ abstract get<T extends ClassTypeWithArgument<R>, R>(token: T, args: R): Promise<InstanceType<T>>;
55
+ abstract get<T, S extends InjectionTokenSchemaType>(token: InjectionToken<T, S>, args: z.input<S>): Promise<T>;
56
+ abstract get<T, S extends InjectionTokenSchemaType, R extends boolean>(token: InjectionToken<T, S, R>): R extends false ? Promise<T> : S extends ZodType<infer Type> ? `Error: Your token requires args: ${Join<UnionToArray<keyof Type>, ', '>}` : 'Error: Your token requires args';
57
+ abstract get<T>(token: InjectionToken<T, undefined>): Promise<T>;
58
+ abstract get<T>(token: BoundInjectionToken<T, any>): Promise<T>;
59
+ abstract get<T>(token: FactoryInjectionToken<T, any>): Promise<T>;
60
+ /**
61
+ * Invalidates a service and its dependencies.
62
+ */
63
+ abstract invalidate(service: unknown): Promise<void>;
64
+ /**
65
+ * Disposes the container and cleans up all resources.
66
+ */
67
+ abstract dispose(): Promise<void>;
68
+ /**
69
+ * Calculates the instance name for a given token and optional arguments.
70
+ *
71
+ * @internal
72
+ * @param token The class type, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
73
+ * @param args Optional arguments (ignored for BoundInjectionToken which uses its bound value)
74
+ * @returns The calculated instance name string, or null if the token is a FactoryInjectionToken that is not yet resolved
75
+ */
76
+ calculateInstanceName(token: ClassType | InjectionToken<any, any> | BoundInjectionToken<any, any> | FactoryInjectionToken<any, any>, args?: unknown): string | null;
77
+ /**
78
+ * Checks if a service is registered in the container.
79
+ */
80
+ isRegistered(token: any): boolean;
81
+ /**
82
+ * Waits for all pending operations to complete.
83
+ */
84
+ ready(): Promise<void>;
85
+ /**
86
+ * @internal
87
+ * Attempts to get an instance synchronously if it already exists.
88
+ */
89
+ tryGetSync<T>(token: any, args?: any): T | null;
90
+ /**
91
+ * @internal
92
+ * Internal method for getting instances synchronously with configurable storage.
93
+ */
94
+ protected tryGetSyncFromStorage<T>(token: any, args: any, storage: UnifiedStorage, requestId?: string): T | null;
95
+ /**
96
+ * Adds an instance to the container.
97
+ * Accepts class types, InjectionTokens, and BoundInjectionTokens.
98
+ * Rejects InjectionTokens with required schemas (use BoundInjectionToken instead).
99
+ *
100
+ * @param token The class type, InjectionToken, or BoundInjectionToken to register the instance for
101
+ * @param instance The instance to store
102
+ */
103
+ addInstance<T>(token: ClassType | InjectionToken<T, any> | BoundInjectionToken<T, any>, instance: T): void;
104
+ /**
105
+ * @internal
106
+ * Internal method for adding instances with configurable scope and storage.
107
+ */
108
+ protected addInstanceToStorage<T>(token: ClassType | InjectionToken<T, any> | BoundInjectionToken<T, any>, instance: T, storage: UnifiedStorage, scope: InjectableScope, requestId?: string): void;
109
+ }
110
+ //#endregion
111
+ export { AbstractContainer };
112
+ //# sourceMappingURL=abstract-container.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abstract-container.d.mts","names":[],"sources":["../../../src/container/abstract-container.mts"],"sourcesContent":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ import { InjectableScope } from "../enums/injectable-scope.enum.mjs";
2
+ import { InjectableType } from "../enums/injectable-type.enum.mjs";
3
+ import { BoundInjectionToken, InjectionToken } from "../token/injection-token.mjs";
4
+ import { DIError, DIErrorCode } from "../errors/di-error.mjs";
5
+ import { InstanceStatus } from "../internal/holder/instance-holder.mjs";
6
+ import { StubFactoryClass } from "../internal/stub-factory-class.mjs";
7
+
8
+ //#region src/container/abstract-container.mts
9
+ /**
10
+ * Abstract base class for dependency injection containers.
11
+ *
12
+ * Provides shared implementation for common container operations.
13
+ * Both Container and ScopedContainer extend this class.
14
+ */ var AbstractContainer = class {
15
+ /**
16
+ * Calculates the instance name for a given token and optional arguments.
17
+ *
18
+ * @internal
19
+ * @param token The class type, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
20
+ * @param args Optional arguments (ignored for BoundInjectionToken which uses its bound value)
21
+ * @returns The calculated instance name string, or null if the token is a FactoryInjectionToken that is not yet resolved
22
+ */ calculateInstanceName(token, args) {
23
+ const [err, { actualToken, validatedArgs }] = this.getTokenResolver().validateAndResolveTokenArgs(token, args);
24
+ if (err) {
25
+ if (err instanceof DIError && err.code === DIErrorCode.FactoryTokenNotResolved) return null;
26
+ if (err instanceof DIError && err.code === DIErrorCode.TokenValidationError) return null;
27
+ }
28
+ const realToken = this.getTokenResolver().getRealToken(actualToken);
29
+ const registry = this.getRegistry();
30
+ const scope = registry.has(realToken) ? registry.get(realToken).scope : this.defaultScope;
31
+ return this.getNameResolver().generateInstanceName(actualToken, validatedArgs, scope === InjectableScope.Request ? this.requestId : void 0, scope);
32
+ }
33
+ /**
34
+ * Checks if a service is registered in the container.
35
+ */ isRegistered(token) {
36
+ const realToken = this.getTokenResolver().getRegistryToken(token);
37
+ return this.getRegistry().has(realToken);
38
+ }
39
+ /**
40
+ * Waits for all pending operations to complete.
41
+ */ async ready() {
42
+ await this.getServiceInvalidator().readyWithStorage(this.getStorage());
43
+ }
44
+ /**
45
+ * @internal
46
+ * Attempts to get an instance synchronously if it already exists.
47
+ */ tryGetSync(token, args) {
48
+ return this.tryGetSyncFromStorage(token, args, this.getStorage(), this.requestId);
49
+ }
50
+ /**
51
+ * @internal
52
+ * Internal method for getting instances synchronously with configurable storage.
53
+ */ tryGetSyncFromStorage(token, args, storage, requestId) {
54
+ const tokenResolver = this.getTokenResolver();
55
+ const realToken = tokenResolver.getRegistryToken(token);
56
+ const registry = this.getRegistry();
57
+ const scope = registry.has(realToken) ? registry.get(realToken).scope : InjectableScope.Singleton;
58
+ try {
59
+ const instanceName = this.getNameResolver().generateInstanceName(tokenResolver.normalizeToken(token), args, requestId, scope);
60
+ const result = storage.get(instanceName);
61
+ if (result && result[0] === void 0 && result[1]) {
62
+ const holder = result[1];
63
+ if (holder.status === InstanceStatus.Created) return holder.instance;
64
+ }
65
+ } catch {}
66
+ return null;
67
+ }
68
+ /**
69
+ * Adds an instance to the container.
70
+ * Accepts class types, InjectionTokens, and BoundInjectionTokens.
71
+ * Rejects InjectionTokens with required schemas (use BoundInjectionToken instead).
72
+ *
73
+ * @param token The class type, InjectionToken, or BoundInjectionToken to register the instance for
74
+ * @param instance The instance to store
75
+ */ addInstance(token, instance) {
76
+ this.addInstanceToStorage(token, instance, this.getStorage(), this.defaultScope, this.requestId);
77
+ }
78
+ /**
79
+ * @internal
80
+ * Internal method for adding instances with configurable scope and storage.
81
+ */ addInstanceToStorage(token, instance, storage, scope, requestId) {
82
+ if (token instanceof InjectionToken) {
83
+ if (token.schema) {
84
+ if (token.schema?.def?.type !== "optional") throw DIError.tokenSchemaRequiredError(token.name);
85
+ }
86
+ }
87
+ const tokenResolver = this.getTokenResolver();
88
+ const registry = this.getRegistry();
89
+ const normalizedToken = tokenResolver.normalizeToken(token);
90
+ const realToken = tokenResolver.getRegistryToken(token);
91
+ if (typeof token === "function" && !registry.has(realToken)) registry.set(realToken, scope, token, InjectableType.Class);
92
+ else if (!registry.has(realToken)) registry.set(realToken, scope, StubFactoryClass, InjectableType.Class, -1);
93
+ const instanceName = this.getNameResolver().generateInstanceName(normalizedToken, normalizedToken instanceof BoundInjectionToken ? normalizedToken.value : void 0, requestId, scope);
94
+ storage.storeInstance(instanceName, instance);
95
+ }
96
+ };
97
+
98
+ //#endregion
99
+ export { AbstractContainer };
100
+ //# sourceMappingURL=abstract-container.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abstract-container.mjs","names":["InjectableScope","InjectableType","DIError","DIErrorCode","InstanceStatus","StubFactoryClass","BoundInjectionToken","InjectionToken","AbstractContainer","calculateInstanceName","token","args","tokenResolver","getTokenResolver","err","actualToken","validatedArgs","validateAndResolveTokenArgs","code","FactoryTokenNotResolved","TokenValidationError","realToken","getRealToken","registry","getRegistry","scope","has","get","defaultScope","getNameResolver","generateInstanceName","Request","requestId","undefined","isRegistered","getRegistryToken","ready","getServiceInvalidator","readyWithStorage","getStorage","tryGetSync","tryGetSyncFromStorage","storage","Singleton","instanceName","normalizeToken","result","holder","status","Created","instance","addInstance","addInstanceToStorage","schema","schemaType","def","type","tokenSchemaRequiredError","name","normalizedToken","set","Class","value","storeInstance"],"sources":["../../../src/container/abstract-container.mts"],"sourcesContent":["import type { z, ZodType } from 'zod/v4'\n\nimport type { IContainer } from '../interfaces/container.interface.mjs'\nimport type { Factorable } from '../interfaces/factory.interface.mjs'\nimport type { NameResolver } from '../internal/core/name-resolver.mjs'\nimport type { ServiceInvalidator } from '../internal/core/service-invalidator.mjs'\nimport type { TokenResolver } from '../internal/core/token-resolver.mjs'\nimport type {\n ClassType,\n ClassTypeWithArgument,\n InjectionTokenSchemaType,\n} from '../token/injection-token.mjs'\nimport type { Registry } from '../token/registry.mjs'\nimport type { Join, UnionToArray } from '../utils/types.mjs'\n\nimport { InjectableScope, InjectableType } from '../enums/index.mjs'\nimport { DIError, DIErrorCode } from '../errors/index.mjs'\nimport { InstanceStatus } from '../internal/holder/instance-holder.mjs'\nimport { UnifiedStorage } from '../internal/holder/unified-storage.mjs'\nimport { StubFactoryClass } from '../internal/index.mjs'\nimport {\n BoundInjectionToken,\n FactoryInjectionToken,\n InjectionToken,\n} from '../token/injection-token.mjs'\n\n/**\n * Abstract base class for dependency injection containers.\n *\n * Provides shared implementation for common container operations.\n * Both Container and ScopedContainer extend this class.\n */\nexport abstract class AbstractContainer implements IContainer {\n /**\n * The default scope used when adding instances without explicit registration.\n */\n protected abstract readonly defaultScope: InjectableScope\n\n /**\n * The request ID for scoped containers, undefined for root container.\n */\n protected abstract readonly requestId: string | undefined\n\n // ============================================================================\n // ABSTRACT METHODS - Must be implemented by subclasses\n // ============================================================================\n\n /**\n * Gets the storage for this container.\n */\n abstract getStorage(): UnifiedStorage\n\n /**\n * Gets the registry for this container.\n */\n protected abstract getRegistry(): Registry\n\n /**\n * Gets the token resolver.\n */\n protected abstract getTokenResolver(): TokenResolver\n\n /**\n * Gets the name resolver.\n */\n protected abstract getNameResolver(): NameResolver\n\n /**\n * Gets the service invalidator.\n */\n protected abstract getServiceInvalidator(): ServiceInvalidator\n\n /**\n * Gets an instance from the container.\n */\n // #1 Simple class\n abstract get<T extends ClassType>(\n token: T,\n ): InstanceType<T> extends Factorable<infer R>\n ? Promise<R>\n : Promise<InstanceType<T>>\n // #1.1 Simple class with args\n abstract get<T extends ClassTypeWithArgument<R>, R>(\n token: T,\n args: R,\n ): Promise<InstanceType<T>>\n // #2 Token with required Schema\n abstract get<T, S extends InjectionTokenSchemaType>(\n token: InjectionToken<T, S>,\n args: z.input<S>,\n ): Promise<T>\n // #3 Token with optional Schema\n abstract get<T, S extends InjectionTokenSchemaType, R extends boolean>(\n token: InjectionToken<T, S, R>,\n ): R extends false\n ? Promise<T>\n : S extends ZodType<infer Type>\n ? `Error: Your token requires args: ${Join<\n UnionToArray<keyof Type>,\n ', '\n >}`\n : 'Error: Your token requires args'\n // #4 Token with no Schema\n abstract get<T>(token: InjectionToken<T, undefined>): Promise<T>\n abstract get<T>(token: BoundInjectionToken<T, any>): Promise<T>\n abstract get<T>(token: FactoryInjectionToken<T, any>): Promise<T>\n\n /**\n * Invalidates a service and its dependencies.\n */\n abstract invalidate(service: unknown): Promise<void>\n\n /**\n * Disposes the container and cleans up all resources.\n */\n abstract dispose(): Promise<void>\n\n // ============================================================================\n // SHARED IMPLEMENTATIONS\n // ============================================================================\n\n /**\n * Calculates the instance name for a given token and optional arguments.\n *\n * @internal\n * @param token The class type, InjectionToken, BoundInjectionToken, or FactoryInjectionToken\n * @param args Optional arguments (ignored for BoundInjectionToken which uses its bound value)\n * @returns The calculated instance name string, or null if the token is a FactoryInjectionToken that is not yet resolved\n */\n calculateInstanceName(\n token:\n | ClassType\n | InjectionToken<any, any>\n | BoundInjectionToken<any, any>\n | FactoryInjectionToken<any, any>,\n args?: unknown,\n ): string | null {\n const tokenResolver = this.getTokenResolver()\n\n // Use validateAndResolveTokenArgs to handle token normalization and arg resolution\n const [err, { actualToken, validatedArgs }] =\n tokenResolver.validateAndResolveTokenArgs(token, args)\n\n if (err) {\n // Return null if factory token is not resolved\n if (\n err instanceof DIError &&\n err.code === DIErrorCode.FactoryTokenNotResolved\n ) {\n return null\n }\n\n // Return null if validation fails (can't calculate name with invalid args)\n if (\n err instanceof DIError &&\n err.code === DIErrorCode.TokenValidationError\n ) {\n return null\n }\n }\n\n // Get the real token for registry lookup to determine scope\n const realToken = this.getTokenResolver().getRealToken(actualToken)\n\n const registry = this.getRegistry()\n\n // Get scope from registry, or use default scope if not registered\n const scope = registry.has(realToken)\n ? registry.get(realToken).scope\n : this.defaultScope\n\n // Generate instance name using the name resolver with actual token and validated args\n return this.getNameResolver().generateInstanceName(\n actualToken,\n validatedArgs,\n scope === InjectableScope.Request ? this.requestId : undefined,\n scope,\n )\n }\n\n /**\n * Checks if a service is registered in the container.\n */\n isRegistered(token: any): boolean {\n const realToken = this.getTokenResolver().getRegistryToken(token)\n return this.getRegistry().has(realToken)\n }\n\n /**\n * Waits for all pending operations to complete.\n */\n async ready(): Promise<void> {\n await this.getServiceInvalidator().readyWithStorage(this.getStorage())\n }\n\n /**\n * @internal\n * Attempts to get an instance synchronously if it already exists.\n */\n tryGetSync<T>(token: any, args?: any): T | null {\n return this.tryGetSyncFromStorage(\n token,\n args,\n this.getStorage(),\n this.requestId,\n )\n }\n\n /**\n * @internal\n * Internal method for getting instances synchronously with configurable storage.\n */\n protected tryGetSyncFromStorage<T>(\n token: any,\n args: any,\n storage: UnifiedStorage,\n requestId?: string,\n ): T | null {\n const tokenResolver = this.getTokenResolver()\n const realToken = tokenResolver.getRegistryToken(token)\n const registry = this.getRegistry()\n const scope = registry.has(realToken)\n ? registry.get(realToken).scope\n : InjectableScope.Singleton\n\n try {\n const instanceName = this.getNameResolver().generateInstanceName(\n tokenResolver.normalizeToken(token),\n args,\n requestId,\n scope,\n )\n\n const result = storage.get(instanceName)\n if (result && result[0] === undefined && result[1]) {\n const holder = result[1]\n if (holder.status === InstanceStatus.Created) {\n return holder.instance as T\n }\n }\n } catch {\n // Ignore error\n }\n\n return null\n }\n\n /**\n * Adds an instance to the container.\n * Accepts class types, InjectionTokens, and BoundInjectionTokens.\n * Rejects InjectionTokens with required schemas (use BoundInjectionToken instead).\n *\n * @param token The class type, InjectionToken, or BoundInjectionToken to register the instance for\n * @param instance The instance to store\n */\n addInstance<T>(\n token: ClassType | InjectionToken<T, any> | BoundInjectionToken<T, any>,\n instance: T,\n ): void {\n this.addInstanceToStorage(\n token,\n instance,\n this.getStorage(),\n this.defaultScope,\n this.requestId,\n )\n }\n\n /**\n * @internal\n * Internal method for adding instances with configurable scope and storage.\n */\n protected addInstanceToStorage<T>(\n token: ClassType | InjectionToken<T, any> | BoundInjectionToken<T, any>,\n instance: T,\n storage: UnifiedStorage,\n scope: InjectableScope,\n requestId?: string,\n ): void {\n // Check if token is an InjectionToken with required schema\n // BoundInjectionToken is allowed (it already has a value bound)\n if (token instanceof InjectionToken) {\n // Check if schema exists and is required (not optional)\n if (token.schema) {\n const schemaType = (token.schema as ZodType)?.def?.type\n if (schemaType !== 'optional') {\n throw DIError.tokenSchemaRequiredError(token.name)\n }\n }\n }\n\n const tokenResolver = this.getTokenResolver()\n const registry = this.getRegistry()\n\n // Normalize the token\n const normalizedToken = tokenResolver.normalizeToken(token)\n const realToken = tokenResolver.getRegistryToken(token)\n\n // If it's a class type and not registered, register it with the given scope\n if (typeof token === 'function' && !registry.has(realToken)) {\n registry.set(realToken, scope, token, InjectableType.Class)\n } else if (!registry.has(realToken)) {\n // Set a stub factory class to avoid errors when getting instances of unregistered factory tokens\n registry.set(\n realToken,\n scope,\n StubFactoryClass,\n InjectableType.Class,\n // Lowest priority to avoid conflicts with other registered tokens\n -1,\n )\n }\n\n // Generate instance name with the given scope\n const instanceName = this.getNameResolver().generateInstanceName(\n normalizedToken,\n normalizedToken instanceof BoundInjectionToken\n ? normalizedToken.value\n : undefined,\n requestId,\n scope,\n )\n\n // Store the instance\n storage.storeInstance(instanceName, instance)\n }\n}\n"],"mappings":"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"}