@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,342 +1,605 @@
1
- import { A as Injectable, C as getInjectableToken, L as InjectableType, N as globalRegistry, R as InjectableScope, t as _Container$1 } from "../container-Pb_Y4Z4x.mjs";
1
+ import { A as Registry, D as DIError, I as InjectableType, L as InjectableScope, P as InjectionToken, j as globalRegistry, l as defaultInjectors, s as getInjectableToken, t as _Container } from "../container-8-z89TyQ.mjs";
2
2
 
3
3
  //#region src/testing/test-container.mts
4
- function applyDecs2203RFactory() {
5
- function createAddInitializerMethod(initializers, decoratorFinishedRef) {
6
- return function addInitializer(initializer) {
7
- assertNotFinished(decoratorFinishedRef, "addInitializer");
8
- assertCallable(initializer, "An initializer");
9
- initializers.push(initializer);
10
- };
4
+ /**
5
+ * TestContainer extends Container with testing utilities.
6
+ *
7
+ * Provides simple value/class binding for integration/e2e tests,
8
+ * plus assertion helpers and dependency graph inspection.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const container = new TestContainer()
13
+ *
14
+ * // Bind mock values
15
+ * container.bind(DatabaseToken).toValue(mockDatabase)
16
+ * container.bind(UserService).toClass(MockUserService)
17
+ *
18
+ * // Use container normally
19
+ * const service = await container.get(MyService)
20
+ *
21
+ * // Assert on container state
22
+ * container.expectResolved(MyService)
23
+ * container.expectSingleton(MyService)
24
+ * ```
25
+ */ var TestContainer = class extends _Container {
26
+ testRegistry;
27
+ methodCalls = /* @__PURE__ */ new Map();
28
+ lifecycleEvents = /* @__PURE__ */ new Map();
29
+ instanceCounts = /* @__PURE__ */ new Map();
30
+ boundTokens = /* @__PURE__ */ new Set();
31
+ /**
32
+ * Creates a new TestContainer.
33
+ *
34
+ * @param options - Configuration options
35
+ * @param options.parentRegistry - Parent registry. Defaults to globalRegistry.
36
+ * Pass `null` for a completely isolated container.
37
+ * @param options.logger - Optional logger for debugging.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * // Uses globalRegistry as parent (default)
42
+ * const container = new TestContainer()
43
+ *
44
+ * // Isolated container (no access to @Injectable classes)
45
+ * const isolated = new TestContainer({ parentRegistry: null })
46
+ *
47
+ * // Custom parent registry
48
+ * const custom = new TestContainer({ parentRegistry: myRegistry })
49
+ * ```
50
+ */ constructor(options = {}) {
51
+ const { parentRegistry = globalRegistry, logger = null } = options;
52
+ const testRegistry = parentRegistry ? new Registry(parentRegistry) : new Registry();
53
+ super(testRegistry, logger, defaultInjectors);
54
+ this.testRegistry = testRegistry;
11
55
  }
12
- function memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value) {
13
- var kindStr;
14
- switch (kind) {
15
- case 1:
16
- kindStr = "accessor";
17
- break;
18
- case 2:
19
- kindStr = "method";
20
- break;
21
- case 3:
22
- kindStr = "getter";
23
- break;
24
- case 4:
25
- kindStr = "setter";
26
- break;
27
- default: kindStr = "field";
28
- }
29
- var ctx = {
30
- kind: kindStr,
31
- name: isPrivate ? "#" + name : name,
32
- static: isStatic,
33
- private: isPrivate,
34
- metadata
35
- };
36
- var decoratorFinishedRef = { v: false };
37
- ctx.addInitializer = createAddInitializerMethod(initializers, decoratorFinishedRef);
38
- var get, set;
39
- if (kind === 0) if (isPrivate) {
40
- get = desc.get;
41
- set = desc.set;
42
- } else {
43
- get = function() {
44
- return this[name];
45
- };
46
- set = function(v) {
47
- this[name] = v;
48
- };
49
- }
50
- else if (kind === 2) get = function() {
51
- return desc.value;
56
+ /**
57
+ * Creates a binding builder for the given token.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * container.bind(UserService).toValue(mockUserService)
62
+ * container.bind(DatabaseToken).toClass(MockDatabase)
63
+ * container.bind(ConfigToken).toFactory(() => ({ apiKey: 'test' }))
64
+ * ```
65
+ */ bind(token) {
66
+ const realToken = this.resolveToken(token);
67
+ const tokenId = realToken.id;
68
+ return {
69
+ toValue: (value) => {
70
+ this.boundTokens.add(tokenId);
71
+ this.registerValueBinding(realToken, value);
72
+ },
73
+ toClass: (cls) => {
74
+ this.boundTokens.add(tokenId);
75
+ this.registerClassBinding(realToken, cls);
76
+ },
77
+ toFactory: (factory) => {
78
+ this.boundTokens.add(tokenId);
79
+ this.registerFactoryBinding(realToken, factory);
80
+ }
52
81
  };
53
- else {
54
- if (kind === 1 || kind === 3) get = function() {
55
- return desc.get.call(this);
56
- };
57
- if (kind === 1 || kind === 4) set = function(v) {
58
- desc.set.call(this, v);
59
- };
82
+ }
83
+ /**
84
+ * Clears all bindings and resets container state.
85
+ */ async clear() {
86
+ await this.dispose();
87
+ this.methodCalls.clear();
88
+ this.lifecycleEvents.clear();
89
+ this.instanceCounts.clear();
90
+ this.boundTokens.clear();
91
+ }
92
+ /**
93
+ * Asserts that a service has been resolved at least once.
94
+ */ expectResolved(token) {
95
+ const realToken = this.resolveToken(token);
96
+ if (!this.getStorage().getAllNames().some((name) => name.includes(realToken.id))) throw new Error(`Expected ${realToken.toString()} to be resolved, but it was not`);
97
+ }
98
+ /**
99
+ * Asserts that a service has NOT been resolved.
100
+ */ expectNotResolved(token) {
101
+ const realToken = this.resolveToken(token);
102
+ if (this.getStorage().getAllNames().some((name) => name.includes(realToken.id))) throw new Error(`Expected ${realToken.toString()} to NOT be resolved, but it was`);
103
+ }
104
+ /**
105
+ * Asserts that a service is registered as singleton scope.
106
+ */ expectSingleton(token) {
107
+ const realToken = this.resolveToken(token);
108
+ const registry = this.getRegistry();
109
+ if (!registry.has(realToken)) throw new Error(`Expected ${realToken.toString()} to be registered, but it was not`);
110
+ const record = registry.get(realToken);
111
+ if (record.scope !== InjectableScope.Singleton) throw new Error(`Expected ${realToken.toString()} to be Singleton scope, but it was ${record.scope}`);
112
+ }
113
+ /**
114
+ * Asserts that a service is registered as transient scope.
115
+ */ expectTransient(token) {
116
+ const realToken = this.resolveToken(token);
117
+ const registry = this.getRegistry();
118
+ if (!registry.has(realToken)) throw new Error(`Expected ${realToken.toString()} to be registered, but it was not`);
119
+ const record = registry.get(realToken);
120
+ if (record.scope !== InjectableScope.Transient) throw new Error(`Expected ${realToken.toString()} to be Transient scope, but it was ${record.scope}`);
121
+ }
122
+ /**
123
+ * Asserts that a service is registered as request scope.
124
+ */ expectRequestScoped(token) {
125
+ const realToken = this.resolveToken(token);
126
+ const registry = this.getRegistry();
127
+ if (!registry.has(realToken)) throw new Error(`Expected ${realToken.toString()} to be registered, but it was not`);
128
+ const record = registry.get(realToken);
129
+ if (record.scope !== InjectableScope.Request) throw new Error(`Expected ${realToken.toString()} to be Request scope, but it was ${record.scope}`);
130
+ }
131
+ /**
132
+ * Asserts that two service resolutions return the same instance.
133
+ */ async expectSameInstance(token) {
134
+ if (await this.get(token) !== await this.get(token)) {
135
+ const realToken = this.resolveToken(token);
136
+ throw new Error(`Expected ${realToken.toString()} to return same instance, but got different instances`);
60
137
  }
61
- ctx.access = get && set ? {
62
- get,
63
- set
64
- } : get ? { get } : { set };
65
- try {
66
- return dec(value, ctx);
67
- } finally {
68
- decoratorFinishedRef.v = true;
138
+ }
139
+ /**
140
+ * Asserts that two service resolutions return different instances.
141
+ */ async expectDifferentInstances(token) {
142
+ if (await this.get(token) === await this.get(token)) {
143
+ const realToken = this.resolveToken(token);
144
+ throw new Error(`Expected ${realToken.toString()} to return different instances, but got same instance`);
69
145
  }
70
146
  }
71
- function assertNotFinished(decoratorFinishedRef, fnName) {
72
- if (decoratorFinishedRef.v) throw new Error("attempted to call " + fnName + " after decoration was finished");
73
- }
74
- function assertCallable(fn, hint) {
75
- if (typeof fn !== "function") throw new TypeError(hint + " must be a function");
76
- }
77
- function assertValidReturnValue(kind, value) {
78
- var type = typeof value;
79
- if (kind === 1) {
80
- if (type !== "object" || value === null) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0");
81
- if (value.get !== void 0) assertCallable(value.get, "accessor.get");
82
- if (value.set !== void 0) assertCallable(value.set, "accessor.set");
83
- if (value.init !== void 0) assertCallable(value.init, "accessor.init");
84
- } else if (type !== "function") {
85
- var hint;
86
- if (kind === 0) hint = "field";
87
- else if (kind === 10) hint = "class";
88
- else hint = "method";
89
- throw new TypeError(hint + " decorators must return a function or void 0");
147
+ /**
148
+ * Asserts that a service's onServiceInit was called.
149
+ */ expectInitialized(token) {
150
+ const realToken = this.resolveToken(token);
151
+ if (!(this.lifecycleEvents.get(realToken.id) || []).some((e) => e.event === "initialized")) throw new Error(`Expected ${realToken.toString()} to be initialized, but onServiceInit was not called`);
152
+ }
153
+ /**
154
+ * Asserts that a service's onServiceDestroy was called.
155
+ */ expectDestroyed(token) {
156
+ const realToken = this.resolveToken(token);
157
+ if (!(this.lifecycleEvents.get(realToken.id) || []).some((e) => e.event === "destroyed")) throw new Error(`Expected ${realToken.toString()} to be destroyed, but onServiceDestroy was not called`);
158
+ }
159
+ /**
160
+ * Asserts that a service has NOT been destroyed.
161
+ */ expectNotDestroyed(token) {
162
+ const realToken = this.resolveToken(token);
163
+ if ((this.lifecycleEvents.get(realToken.id) || []).some((e) => e.event === "destroyed")) throw new Error(`Expected ${realToken.toString()} to NOT be destroyed, but onServiceDestroy was called`);
164
+ }
165
+ /**
166
+ * Records a method call for tracking.
167
+ * Call this from your mock implementations.
168
+ */ recordMethodCall(token, method, args, result, error) {
169
+ const realToken = this.resolveToken(token);
170
+ const calls = this.methodCalls.get(realToken.id) || [];
171
+ calls.push({
172
+ method,
173
+ args,
174
+ timestamp: Date.now(),
175
+ result,
176
+ error
177
+ });
178
+ this.methodCalls.set(realToken.id, calls);
179
+ }
180
+ /**
181
+ * Records a lifecycle event for tracking.
182
+ */ recordLifecycleEvent(token, event, instanceName) {
183
+ const realToken = this.resolveToken(token);
184
+ const events = this.lifecycleEvents.get(realToken.id) || [];
185
+ events.push({
186
+ event,
187
+ timestamp: Date.now(),
188
+ instanceName
189
+ });
190
+ this.lifecycleEvents.set(realToken.id, events);
191
+ if (event === "created") {
192
+ const count = this.instanceCounts.get(realToken.id) || 0;
193
+ this.instanceCounts.set(realToken.id, count + 1);
90
194
  }
91
195
  }
92
- function applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata) {
93
- var decs = decInfo[0];
94
- var desc, init, value;
95
- if (isPrivate) if (kind === 0 || kind === 1) desc = {
96
- get: decInfo[3],
97
- set: decInfo[4]
98
- };
99
- else if (kind === 3) desc = { get: decInfo[3] };
100
- else if (kind === 4) desc = { set: decInfo[3] };
101
- else desc = { value: decInfo[3] };
102
- else if (kind !== 0) desc = Object.getOwnPropertyDescriptor(base, name);
103
- if (kind === 1) value = {
104
- get: desc.get,
105
- set: desc.set
196
+ /**
197
+ * Asserts that a method was called on a service.
198
+ */ expectCalled(token, method) {
199
+ const realToken = this.resolveToken(token);
200
+ if (!(this.methodCalls.get(realToken.id) || []).some((c) => c.method === method)) throw new Error(`Expected ${realToken.toString()}.${method}() to be called, but it was not`);
201
+ }
202
+ /**
203
+ * Asserts that a method was called with specific arguments.
204
+ */ expectCalledWith(token, method, expectedArgs) {
205
+ const realToken = this.resolveToken(token);
206
+ if (!(this.methodCalls.get(realToken.id) || []).some((c) => c.method === method && this.argsMatch(c.args, expectedArgs))) throw new Error(`Expected ${realToken.toString()}.${method}() to be called with ${JSON.stringify(expectedArgs)}, but it was not`);
207
+ }
208
+ /**
209
+ * Asserts that a method was called a specific number of times.
210
+ */ expectCallCount(token, method, count) {
211
+ const realToken = this.resolveToken(token);
212
+ const actualCount = (this.methodCalls.get(realToken.id) || []).filter((c) => c.method === method).length;
213
+ if (actualCount !== count) throw new Error(`Expected ${realToken.toString()}.${method}() to be called ${count} times, but was called ${actualCount} times`);
214
+ }
215
+ /**
216
+ * Gets all recorded method calls for a service.
217
+ */ getMethodCalls(token) {
218
+ const realToken = this.resolveToken(token);
219
+ return this.methodCalls.get(realToken.id) || [];
220
+ }
221
+ /**
222
+ * Gets statistics about a mocked service.
223
+ */ getServiceStats(token) {
224
+ const realToken = this.resolveToken(token);
225
+ return {
226
+ instanceCount: this.instanceCounts.get(realToken.id) || 0,
227
+ methodCalls: this.methodCalls.get(realToken.id) || [],
228
+ lifecycleEvents: this.lifecycleEvents.get(realToken.id) || []
106
229
  };
107
- else if (kind === 2) value = desc.value;
108
- else if (kind === 3) value = desc.get;
109
- else if (kind === 4) value = desc.set;
110
- var newValue, get, set;
111
- if (typeof decs === "function") {
112
- newValue = memberDec(decs, name, desc, initializers, kind, isStatic, isPrivate, metadata, value);
113
- if (newValue !== void 0) {
114
- assertValidReturnValue(kind, newValue);
115
- if (kind === 0) init = newValue;
116
- else if (kind === 1) {
117
- init = newValue.init;
118
- get = newValue.get || value.get;
119
- set = newValue.set || value.set;
120
- value = {
121
- get,
122
- set
123
- };
124
- } else value = newValue;
125
- }
126
- } else for (var i = decs.length - 1; i >= 0; i--) {
127
- var dec = decs[i];
128
- newValue = memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value);
129
- if (newValue !== void 0) {
130
- assertValidReturnValue(kind, newValue);
131
- var newInit;
132
- if (kind === 0) newInit = newValue;
133
- else if (kind === 1) {
134
- newInit = newValue.init;
135
- get = newValue.get || value.get;
136
- set = newValue.set || value.set;
137
- value = {
138
- get,
139
- set
140
- };
141
- } else value = newValue;
142
- if (newInit !== void 0) if (init === void 0) init = newInit;
143
- else if (typeof init === "function") init = [init, newInit];
144
- else init.push(newInit);
145
- }
146
- }
147
- if (kind === 0 || kind === 1) {
148
- if (init === void 0) init = function(instance, init$1) {
149
- return init$1;
230
+ }
231
+ /**
232
+ * Clears all recorded method calls.
233
+ */ clearMethodCalls() {
234
+ this.methodCalls.clear();
235
+ }
236
+ /**
237
+ * Gets the dependency graph for snapshot testing.
238
+ * Returns a serializable structure that can be used with vitest snapshots.
239
+ */ getDependencyGraph() {
240
+ const storage = this.getStorage();
241
+ const nodes = {};
242
+ const rootTokens = [];
243
+ storage.forEach((name, holder) => {
244
+ const tokenMatch = name.match(/^([^:]+)/);
245
+ nodes[name] = {
246
+ token: tokenMatch ? tokenMatch[1] : name,
247
+ instanceName: name,
248
+ scope: holder.scope,
249
+ dependencies: Array.from(holder.deps),
250
+ dependents: storage.findDependents(name)
150
251
  };
151
- else if (typeof init !== "function") {
152
- var ownInitializers = init;
153
- init = function(instance, init$1) {
154
- var value$1 = init$1;
155
- for (var i$1 = 0; i$1 < ownInitializers.length; i$1++) value$1 = ownInitializers[i$1].call(instance, value$1);
156
- return value$1;
157
- };
158
- } else {
159
- var originalInitializer = init;
160
- init = function(instance, init$1) {
161
- return originalInitializer.call(instance, init$1);
162
- };
163
- }
164
- ret.push(init);
165
- }
166
- if (kind !== 0) {
167
- if (kind === 1) {
168
- desc.get = value.get;
169
- desc.set = value.set;
170
- } else if (kind === 2) desc.value = value;
171
- else if (kind === 3) desc.get = value;
172
- else if (kind === 4) desc.set = value;
173
- if (isPrivate) if (kind === 1) {
174
- ret.push(function(instance, args) {
175
- return value.get.call(instance, args);
176
- });
177
- ret.push(function(instance, args) {
178
- return value.set.call(instance, args);
179
- });
180
- } else if (kind === 2) ret.push(value);
181
- else ret.push(function(instance, args) {
182
- return value.call(instance, args);
183
- });
184
- else Object.defineProperty(base, name, desc);
185
- }
252
+ if (storage.findDependents(name).length === 0) rootTokens.push(name);
253
+ });
254
+ return {
255
+ nodes,
256
+ rootTokens
257
+ };
186
258
  }
187
- function applyMemberDecs(Class, decInfos, metadata) {
188
- var ret = [];
189
- var protoInitializers;
190
- var staticInitializers;
191
- var existingProtoNonFields = /* @__PURE__ */ new Map();
192
- var existingStaticNonFields = /* @__PURE__ */ new Map();
193
- for (var i = 0; i < decInfos.length; i++) {
194
- var decInfo = decInfos[i];
195
- if (!Array.isArray(decInfo)) continue;
196
- var kind = decInfo[1];
197
- var name = decInfo[2];
198
- var isPrivate = decInfo.length > 3;
199
- var isStatic = kind >= 5;
200
- var base;
201
- var initializers;
202
- if (isStatic) {
203
- base = Class;
204
- kind = kind - 5;
205
- staticInitializers = staticInitializers || [];
206
- initializers = staticInitializers;
207
- } else {
208
- base = Class.prototype;
209
- protoInitializers = protoInitializers || [];
210
- initializers = protoInitializers;
211
- }
212
- if (kind !== 0 && !isPrivate) {
213
- var existingNonFields = isStatic ? existingStaticNonFields : existingProtoNonFields;
214
- var existingKind = existingNonFields.get(name) || 0;
215
- if (existingKind === true || existingKind === 3 && kind !== 4 || existingKind === 4 && kind !== 3) throw new Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + name);
216
- else if (!existingKind && kind > 2) existingNonFields.set(name, kind);
217
- else existingNonFields.set(name, true);
259
+ /**
260
+ * Gets a simplified dependency graph showing only token relationships.
261
+ * Useful for cleaner snapshot comparisons.
262
+ */ getSimplifiedDependencyGraph() {
263
+ const storage = this.getStorage();
264
+ const graph = {};
265
+ storage.forEach((name, holder) => {
266
+ const tokenMatch = name.match(/^([^:]+)/);
267
+ const tokenId = tokenMatch ? tokenMatch[1] : name;
268
+ if (!graph[tokenId]) graph[tokenId] = [];
269
+ for (const dep of holder.deps) {
270
+ const depTokenMatch = dep.match(/^([^:]+)/);
271
+ const depTokenId = depTokenMatch ? depTokenMatch[1] : dep;
272
+ if (!graph[tokenId].includes(depTokenId)) graph[tokenId].push(depTokenId);
218
273
  }
219
- applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata);
220
- }
221
- pushInitializers(ret, protoInitializers);
222
- pushInitializers(ret, staticInitializers);
223
- return ret;
224
- }
225
- function pushInitializers(ret, initializers) {
226
- if (initializers) ret.push(function(instance) {
227
- for (var i = 0; i < initializers.length; i++) initializers[i].call(instance);
228
- return instance;
229
274
  });
275
+ for (const key of Object.keys(graph)) graph[key].sort();
276
+ return graph;
277
+ }
278
+ resolveToken(token) {
279
+ if (typeof token === "function") return getInjectableToken(token);
280
+ return token;
281
+ }
282
+ registerValueBinding(token, value) {
283
+ const ValueHolder = class {
284
+ static instance = value;
285
+ };
286
+ this.testRegistry.set(token, InjectableScope.Singleton, ValueHolder, InjectableType.Class);
287
+ const instanceName = this.getNameResolver().generateInstanceName(token, void 0, void 0, InjectableScope.Singleton);
288
+ this.getStorage().storeInstance(instanceName, value);
289
+ this.recordLifecycleEvent(token, "created", instanceName);
290
+ }
291
+ registerClassBinding(token, cls) {
292
+ this.testRegistry.set(token, InjectableScope.Singleton, cls, InjectableType.Class);
230
293
  }
231
- function applyClassDecs(targetClass, classDecs, metadata) {
232
- if (classDecs.length > 0) {
233
- var initializers = [];
234
- var newClass = targetClass;
235
- var name = targetClass.name;
236
- for (var i = classDecs.length - 1; i >= 0; i--) {
237
- var decoratorFinishedRef = { v: false };
238
- try {
239
- var nextNewClass = classDecs[i](newClass, {
240
- kind: "class",
241
- name,
242
- addInitializer: createAddInitializerMethod(initializers, decoratorFinishedRef),
243
- metadata
244
- });
245
- } finally {
246
- decoratorFinishedRef.v = true;
247
- }
248
- if (nextNewClass !== void 0) {
249
- assertValidReturnValue(10, nextNewClass);
250
- newClass = nextNewClass;
251
- }
294
+ registerFactoryBinding(token, factory) {
295
+ const FactoryWrapper = class {
296
+ static factory = factory;
297
+ async create() {
298
+ return await factory();
252
299
  }
253
- return [defineMetadata(newClass, metadata), function() {
254
- for (var i$1 = 0; i$1 < initializers.length; i$1++) initializers[i$1].call(newClass);
255
- }];
256
- }
300
+ };
301
+ this.testRegistry.set(token, InjectableScope.Singleton, FactoryWrapper, InjectableType.Factory);
257
302
  }
258
- function defineMetadata(Class, metadata) {
259
- return Object.defineProperty(Class, Symbol.metadata || Symbol.for("Symbol.metadata"), {
260
- configurable: true,
261
- enumerable: true,
262
- value: metadata
303
+ argsMatch(actual, expected) {
304
+ if (actual.length !== expected.length) return false;
305
+ return actual.every((arg, index) => {
306
+ const exp = expected[index];
307
+ if (typeof exp === "object" && exp !== null) return JSON.stringify(arg) === JSON.stringify(exp);
308
+ return arg === exp;
263
309
  });
264
310
  }
265
- return function applyDecs2203R(targetClass, memberDecs, classDecs, parentClass) {
266
- if (parentClass !== void 0) var parentMetadata = parentClass[Symbol.metadata || Symbol.for("Symbol.metadata")];
267
- var metadata = Object.create(parentMetadata === void 0 ? null : parentMetadata);
268
- var e = applyMemberDecs(targetClass, memberDecs, metadata);
269
- if (!classDecs.length) defineMetadata(targetClass, metadata);
270
- return {
271
- e,
272
- get c() {
273
- return applyClassDecs(targetClass, classDecs, metadata);
311
+ };
312
+
313
+ //#endregion
314
+ //#region src/testing/unit-test-container.mts
315
+ /**
316
+ * Creates a tracking proxy that records method calls.
317
+ */ function createTrackingProxy(target, tokenId, methodCalls) {
318
+ return new Proxy(target, { get(obj, prop) {
319
+ const value = Reflect.get(obj, prop);
320
+ if (typeof value === "function" && typeof prop === "string") return function(...args) {
321
+ const calls = methodCalls.get(tokenId) || [];
322
+ const record = {
323
+ method: prop,
324
+ args,
325
+ timestamp: Date.now()
326
+ };
327
+ try {
328
+ const result = value.apply(this === void 0 ? obj : this, args);
329
+ if (result instanceof Promise) return result.then((res) => {
330
+ record.result = res;
331
+ calls.push(record);
332
+ methodCalls.set(tokenId, calls);
333
+ return res;
334
+ }).catch((err) => {
335
+ record.error = err;
336
+ calls.push(record);
337
+ methodCalls.set(tokenId, calls);
338
+ throw err;
339
+ });
340
+ record.result = result;
341
+ calls.push(record);
342
+ methodCalls.set(tokenId, calls);
343
+ return result;
344
+ } catch (err) {
345
+ record.error = err;
346
+ calls.push(record);
347
+ methodCalls.set(tokenId, calls);
348
+ throw err;
274
349
  }
275
350
  };
276
- };
351
+ return value;
352
+ } });
277
353
  }
278
- function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
279
- return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
354
+ /**
355
+ * Creates an auto-mock proxy that throws on method access.
356
+ */ function createAutoMockProxy(tokenId) {
357
+ return new Proxy({}, { get(_, prop) {
358
+ if (prop === "then" || prop === "catch" || prop === "finally") return;
359
+ if (typeof prop === "symbol") return;
360
+ throw new Error(`[UnitTestContainer] Attempted to access '${prop}' on auto-mocked service '${tokenId}'. This service was not provided in the providers list. Add it to providers or use allowUnregistered: false to catch this earlier.`);
361
+ } });
280
362
  }
281
- var _dec, _initClass, _Container;
282
363
  /**
283
- * A binding builder for the TestContainer that allows chaining binding operations.
284
- */ var TestBindingBuilder = class {
285
- container;
286
- token;
287
- constructor(container, token) {
288
- this.container = container;
289
- this.token = token;
290
- }
291
- /**
292
- * Binds the token to a specific value.
293
- * This is useful for testing with mock values or constants.
294
- * @param value The value to bind to the token
295
- */ toValue(value) {
296
- const instanceName = this.container.getServiceLocator().getInstanceIdentifier(this.token);
297
- this.container.getServiceLocator().getManager().storeCreatedHolder(instanceName, value, InjectableType.Class, InjectableScope.Singleton);
298
- return this.container;
299
- }
300
- /**
301
- * Binds the token to a class constructor.
302
- * @param target The class constructor to bind to
303
- */ toClass(target) {
304
- this.container["registry"].set(this.token, InjectableScope.Singleton, target, InjectableType.Class);
305
- return this.container;
364
+ * UnitTestContainer for isolated unit testing.
365
+ *
366
+ * Only services explicitly listed in `providers` can be resolved.
367
+ * All method calls are automatically tracked via proxies.
368
+ * Unregistered dependencies throw by default, or can be auto-mocked.
369
+ *
370
+ * @example
371
+ * ```ts
372
+ * const container = new UnitTestContainer({
373
+ * providers: [
374
+ * { token: UserService, useClass: MockUserService },
375
+ * { token: ConfigToken, useValue: { apiUrl: 'test' } },
376
+ * ],
377
+ * })
378
+ *
379
+ * const service = await container.get(UserService)
380
+ *
381
+ * // All method calls are automatically tracked
382
+ * await service.findUser('123')
383
+ *
384
+ * container.expectCalled(UserService, 'findUser')
385
+ * container.expectCalledWith(UserService, 'findUser', ['123'])
386
+ * ```
387
+ */ var UnitTestContainer = class extends _Container {
388
+ testRegistry;
389
+ methodCalls = /* @__PURE__ */ new Map();
390
+ lifecycleEvents = /* @__PURE__ */ new Map();
391
+ instanceCounts = /* @__PURE__ */ new Map();
392
+ registeredTokenIds = /* @__PURE__ */ new Set();
393
+ autoMockedTokenIds = /* @__PURE__ */ new Set();
394
+ allowUnregistered;
395
+ constructor(options) {
396
+ const testRegistry = new Registry();
397
+ super(testRegistry, options.logger ?? null, defaultInjectors);
398
+ this.testRegistry = testRegistry;
399
+ this.allowUnregistered = options.allowUnregistered ?? false;
400
+ for (const provider of options.providers) this.registerProvider(provider);
306
401
  }
307
- };
308
- let _TestContainer;
309
- _dec = Injectable();
310
- var TestContainer = class extends (_Container = _Container$1) {
311
- static {
312
- ({c: [_TestContainer, _initClass]} = _apply_decs_2203_r(this, [], [_dec], _Container));
402
+ /**
403
+ * Enables auto-mocking for unregistered dependencies.
404
+ * Call this to switch from strict mode to auto-mock mode.
405
+ */ enableAutoMocking() {
406
+ this.allowUnregistered = true;
407
+ return this;
313
408
  }
314
- constructor(registry = globalRegistry, logger = null, injectors = void 0) {
315
- super(registry, logger, injectors);
409
+ /**
410
+ * Disables auto-mocking (strict mode).
411
+ * Unregistered dependencies will throw.
412
+ */ disableAutoMocking() {
413
+ this.allowUnregistered = false;
414
+ return this;
316
415
  }
317
- bind(token) {
318
- let realToken = token;
319
- if (typeof token === "function") realToken = getInjectableToken(token);
320
- return new TestBindingBuilder(this, realToken);
416
+ /**
417
+ * Override get to wrap instances in tracking proxies.
418
+ */ async get(token, args) {
419
+ const realToken = this.resolveToken(token);
420
+ if (!this.registeredTokenIds.has(realToken.id)) {
421
+ if (!this.allowUnregistered) throw DIError.factoryNotFound(`${realToken.toString()} is not in the providers list. Add it to providers or enable allowUnregistered.`);
422
+ if (!this.autoMockedTokenIds.has(realToken.id)) this.autoMockedTokenIds.add(realToken.id);
423
+ return createAutoMockProxy(realToken.id);
424
+ }
425
+ const instance = await super.get(token, args);
426
+ if (instance && typeof instance === "object") return createTrackingProxy(instance, realToken.id, this.methodCalls);
427
+ return instance;
321
428
  }
322
- bindValue(token, value) {
323
- return this.bind(token).toValue(value);
429
+ /**
430
+ * Clears all state and disposes the container.
431
+ */ async clear() {
432
+ await this.dispose();
433
+ this.methodCalls.clear();
434
+ this.lifecycleEvents.clear();
435
+ this.instanceCounts.clear();
436
+ this.autoMockedTokenIds.clear();
324
437
  }
325
- bindClass(token, target) {
326
- return this.bind(token).toClass(target);
438
+ /**
439
+ * Asserts that a service has been resolved at least once.
440
+ */ expectResolved(token) {
441
+ const realToken = this.resolveToken(token);
442
+ if (!this.getStorage().getAllNames().some((name) => name.includes(realToken.id))) throw new Error(`Expected ${realToken.toString()} to be resolved, but it was not`);
443
+ }
444
+ /**
445
+ * Asserts that a service has NOT been resolved.
446
+ */ expectNotResolved(token) {
447
+ const realToken = this.resolveToken(token);
448
+ if (this.getStorage().getAllNames().some((name) => name.includes(realToken.id))) throw new Error(`Expected ${realToken.toString()} to NOT be resolved, but it was`);
449
+ }
450
+ /**
451
+ * Asserts that a service was auto-mocked (not in providers list).
452
+ */ expectAutoMocked(token) {
453
+ const realToken = this.resolveToken(token);
454
+ if (!this.autoMockedTokenIds.has(realToken.id)) throw new Error(`Expected ${realToken.toString()} to be auto-mocked, but it was not. Either it's in the providers list or hasn't been resolved.`);
455
+ }
456
+ /**
457
+ * Asserts that a service was NOT auto-mocked (is in providers list).
458
+ */ expectNotAutoMocked(token) {
459
+ const realToken = this.resolveToken(token);
460
+ if (this.autoMockedTokenIds.has(realToken.id)) throw new Error(`Expected ${realToken.toString()} to NOT be auto-mocked, but it was.`);
461
+ }
462
+ /**
463
+ * Records a lifecycle event for tracking.
464
+ */ recordLifecycleEvent(token, event, instanceName) {
465
+ const realToken = this.resolveToken(token);
466
+ const events = this.lifecycleEvents.get(realToken.id) || [];
467
+ events.push({
468
+ event,
469
+ timestamp: Date.now(),
470
+ instanceName
471
+ });
472
+ this.lifecycleEvents.set(realToken.id, events);
473
+ if (event === "created") {
474
+ const count = this.instanceCounts.get(realToken.id) || 0;
475
+ this.instanceCounts.set(realToken.id, count + 1);
476
+ }
477
+ }
478
+ /**
479
+ * Asserts that a service's onServiceInit was called.
480
+ */ expectInitialized(token) {
481
+ const realToken = this.resolveToken(token);
482
+ if (!(this.lifecycleEvents.get(realToken.id) || []).some((e) => e.event === "initialized")) throw new Error(`Expected ${realToken.toString()} to be initialized, but onServiceInit was not called`);
327
483
  }
328
484
  /**
329
- * Creates a new TestContainer instance with the same configuration.
330
- * This is useful for creating isolated test containers.
331
- * @returns A new TestContainer instance
332
- */ createChild() {
333
- return new _TestContainer(this.registry, this.logger, this.injectors);
485
+ * Asserts that a service's onServiceDestroy was called.
486
+ */ expectDestroyed(token) {
487
+ const realToken = this.resolveToken(token);
488
+ if (!(this.lifecycleEvents.get(realToken.id) || []).some((e) => e.event === "destroyed")) throw new Error(`Expected ${realToken.toString()} to be destroyed, but onServiceDestroy was not called`);
334
489
  }
335
- static {
336
- _initClass();
490
+ /**
491
+ * Asserts that a service has NOT been destroyed.
492
+ */ expectNotDestroyed(token) {
493
+ const realToken = this.resolveToken(token);
494
+ if ((this.lifecycleEvents.get(realToken.id) || []).some((e) => e.event === "destroyed")) throw new Error(`Expected ${realToken.toString()} to NOT be destroyed, but onServiceDestroy was called`);
495
+ }
496
+ /**
497
+ * Asserts that a method was called on a service.
498
+ */ expectCalled(token, method) {
499
+ const realToken = this.resolveToken(token);
500
+ if (!(this.methodCalls.get(realToken.id) || []).some((c) => c.method === method)) throw new Error(`Expected ${realToken.toString()}.${method}() to be called, but it was not`);
501
+ }
502
+ /**
503
+ * Asserts that a method was NOT called on a service.
504
+ */ expectNotCalled(token, method) {
505
+ const realToken = this.resolveToken(token);
506
+ if ((this.methodCalls.get(realToken.id) || []).some((c) => c.method === method)) throw new Error(`Expected ${realToken.toString()}.${method}() to NOT be called, but it was`);
507
+ }
508
+ /**
509
+ * Asserts that a method was called with specific arguments.
510
+ */ expectCalledWith(token, method, expectedArgs) {
511
+ const realToken = this.resolveToken(token);
512
+ const calls = this.methodCalls.get(realToken.id) || [];
513
+ if (!calls.some((c) => c.method === method && this.argsMatch(c.args, expectedArgs))) {
514
+ const actualArgs = calls.filter((c) => c.method === method).map((c) => JSON.stringify(c.args)).join(", ");
515
+ throw new Error(`Expected ${realToken.toString()}.${method}() to be called with ${JSON.stringify(expectedArgs)}. Actual calls: [${actualArgs}]`);
516
+ }
517
+ }
518
+ /**
519
+ * Asserts that a method was called a specific number of times.
520
+ */ expectCallCount(token, method, count) {
521
+ const realToken = this.resolveToken(token);
522
+ const actualCount = (this.methodCalls.get(realToken.id) || []).filter((c) => c.method === method).length;
523
+ if (actualCount !== count) throw new Error(`Expected ${realToken.toString()}.${method}() to be called ${count} times, but was called ${actualCount} times`);
524
+ }
525
+ /**
526
+ * Gets all recorded method calls for a service.
527
+ */ getMethodCalls(token) {
528
+ const realToken = this.resolveToken(token);
529
+ return this.methodCalls.get(realToken.id) || [];
530
+ }
531
+ /**
532
+ * Gets statistics about a service.
533
+ */ getServiceStats(token) {
534
+ const realToken = this.resolveToken(token);
535
+ return {
536
+ instanceCount: this.instanceCounts.get(realToken.id) || 0,
537
+ methodCalls: this.methodCalls.get(realToken.id) || [],
538
+ lifecycleEvents: this.lifecycleEvents.get(realToken.id) || []
539
+ };
540
+ }
541
+ /**
542
+ * Clears all recorded method calls.
543
+ */ clearMethodCalls() {
544
+ this.methodCalls.clear();
545
+ }
546
+ /**
547
+ * Gets list of all registered provider token IDs.
548
+ */ getRegisteredTokenIds() {
549
+ return this.registeredTokenIds;
550
+ }
551
+ /**
552
+ * Gets list of all auto-mocked token IDs.
553
+ */ getAutoMockedTokenIds() {
554
+ return this.autoMockedTokenIds;
555
+ }
556
+ resolveToken(token) {
557
+ if (typeof token === "function") try {
558
+ return getInjectableToken(token);
559
+ } catch {
560
+ return InjectionToken.create(token);
561
+ }
562
+ return token;
563
+ }
564
+ registerProvider(provider) {
565
+ const realToken = this.resolveToken(provider.token);
566
+ this.registeredTokenIds.add(realToken.id);
567
+ if (provider.useValue !== void 0) this.registerValueBinding(realToken, provider.useValue);
568
+ else if (provider.useClass) this.registerClassBinding(realToken, provider.useClass);
569
+ else if (provider.useFactory) this.registerFactoryBinding(realToken, provider.useFactory);
570
+ else if (typeof provider.token === "function") this.testRegistry.set(realToken, InjectableScope.Singleton, provider.token, InjectableType.Class);
571
+ }
572
+ registerValueBinding(token, value) {
573
+ const ValueHolder = class {
574
+ static instance = value;
575
+ };
576
+ this.testRegistry.set(token, InjectableScope.Singleton, ValueHolder, InjectableType.Class);
577
+ const instanceName = this.getNameResolver().generateInstanceName(token, void 0, void 0, InjectableScope.Singleton);
578
+ this.getStorage().storeInstance(instanceName, value);
579
+ this.recordLifecycleEvent(token, "created", instanceName);
580
+ }
581
+ registerClassBinding(token, cls) {
582
+ this.testRegistry.set(token, InjectableScope.Singleton, cls, InjectableType.Class);
583
+ }
584
+ registerFactoryBinding(token, factory) {
585
+ const FactoryWrapper = class {
586
+ static factory = factory;
587
+ async create() {
588
+ return await factory();
589
+ }
590
+ };
591
+ this.testRegistry.set(token, InjectableScope.Singleton, FactoryWrapper, InjectableType.Factory);
592
+ }
593
+ argsMatch(actual, expected) {
594
+ if (actual.length !== expected.length) return false;
595
+ return actual.every((arg, index) => {
596
+ const exp = expected[index];
597
+ if (typeof exp === "object" && exp !== null) return JSON.stringify(arg) === JSON.stringify(exp);
598
+ return arg === exp;
599
+ });
337
600
  }
338
601
  };
339
602
 
340
603
  //#endregion
341
- export { TestBindingBuilder, _TestContainer as TestContainer };
604
+ export { TestContainer, UnitTestContainer };
342
605
  //# sourceMappingURL=index.mjs.map