@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
@@ -0,0 +1,65 @@
1
+ import { InjectionToken } from "../../token/injection-token.mjs";
2
+ import { InjectableScope } from "../../enums/injectable-scope.enum.mjs";
3
+ import { Registry } from "../../token/registry.mjs";
4
+ import { NameResolver } from "./name-resolver.mjs";
5
+ import { DIError } from "../../errors/di-error.mjs";
6
+ import { IHolderStorage } from "../holder/holder-storage.interface.mjs";
7
+
8
+ //#region src/internal/core/scope-tracker.d.mts
9
+
10
+ /**
11
+ * Component for tracking and handling scope upgrades.
12
+ *
13
+ * Detects when a Singleton service needs to be upgraded to Request scope
14
+ * and coordinates the scope upgrade process atomically.
15
+ */
16
+ declare class ScopeTracker {
17
+ private readonly registry;
18
+ private readonly nameResolver;
19
+ private readonly logger;
20
+ constructor(registry: Registry, nameResolver: NameResolver, logger?: Console | null);
21
+ /**
22
+ * Checks if a dependency requires scope upgrade and performs it if needed.
23
+ * Called during service resolution when a dependency is resolved.
24
+ *
25
+ * @param currentServiceName - Name of the service being created
26
+ * @param currentServiceScope - Current scope of the service being created
27
+ * @param dependencyName - Name of the dependency being resolved
28
+ * @param dependencyScope - Scope of the dependency
29
+ * @param dependencyToken - Token of the dependency
30
+ * @param singletonStorage - Singleton storage instance
31
+ * @param requestStorage - Request storage instance (if in request context)
32
+ * @param requestId - Request ID (if in request context)
33
+ * @returns [needsUpgrade: boolean, newName?: string] - whether upgrade occurred and new name
34
+ */
35
+ checkAndUpgradeScope(currentServiceName: string, currentServiceScope: InjectableScope, dependencyName: string, dependencyScope: InjectableScope, dependencyToken: InjectionToken<any, any>, singletonStorage: IHolderStorage, requestStorage?: IHolderStorage, requestId?: string): [boolean, string?];
36
+ /**
37
+ * Performs the actual scope upgrade from Singleton to Request.
38
+ * This is the core migration logic.
39
+ *
40
+ * @param serviceName - Current service name (without requestId)
41
+ * @param token - Service injection token
42
+ * @param singletonStorage - Source storage
43
+ * @param requestStorage - Target storage
44
+ * @param requestId - Request ID to include in new name
45
+ * @returns [success: boolean, newName?: string, error?: DIError]
46
+ */
47
+ upgradeScopeToRequest(serviceName: string, token: InjectionToken<any, any>, singletonStorage: IHolderStorage, requestStorage: IHolderStorage, requestId: string): Promise<[boolean, string?, DIError?]>;
48
+ /**
49
+ * Synchronous part of scope upgrade - handles immediate updates.
50
+ * Async operations (like waiting for holder creation) should be done separately.
51
+ */
52
+ private upgradeScopeToRequestSync;
53
+ /**
54
+ * Updates all parent dependencies to reference the new service name.
55
+ *
56
+ * @param oldName - Original service name
57
+ * @param newName - New service name with requestId
58
+ * @param singletonStorage - Singleton storage to check
59
+ * @param requestStorage - Request storage to check
60
+ */
61
+ updateParentDependencies(oldName: string, newName: string, singletonStorage: IHolderStorage, requestStorage?: IHolderStorage): void;
62
+ }
63
+ //#endregion
64
+ export { ScopeTracker };
65
+ //# sourceMappingURL=scope-tracker.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-tracker.d.mts","names":[],"sources":["../../../../src/internal/core/scope-tracker.mts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAgBA;;;;AAuByB,cAvBZ,YAAA,CAuBY;EAEJ,iBAAA,QAAA;EACA,iBAAA,YAAA;EACC,iBAAA,MAAA;EACD,WAAA,CAAA,QAAA,EA1BU,QA0BV,EAAA,YAAA,EAzBc,YAyBd,EAAA,MAAA,CAAA,EAxBQ,OAwBR,GAAA,IAAA;EA2DV;;;;;;;;;;;;;;wEAhEc,0DAEJ,kCACA,4CACC,iCACD;;;;;;;;;;;;oDA2DV,4CACW,gCACF,oCAEf,2BAA2B;;;;;;;;;;;;;;+EA2HV,iCACD"}
@@ -0,0 +1,120 @@
1
+ import { InjectableScope } from "../../enums/injectable-scope.enum.mjs";
2
+ import { DIError } from "../../errors/di-error.mjs";
3
+ import { InstanceStatus } from "../holder/instance-holder.mjs";
4
+
5
+ //#region src/internal/core/scope-tracker.mts
6
+ /**
7
+ * Component for tracking and handling scope upgrades.
8
+ *
9
+ * Detects when a Singleton service needs to be upgraded to Request scope
10
+ * and coordinates the scope upgrade process atomically.
11
+ */ var ScopeTracker = class {
12
+ registry;
13
+ nameResolver;
14
+ logger;
15
+ constructor(registry, nameResolver, logger = null) {
16
+ this.registry = registry;
17
+ this.nameResolver = nameResolver;
18
+ this.logger = logger;
19
+ }
20
+ /**
21
+ * Checks if a dependency requires scope upgrade and performs it if needed.
22
+ * Called during service resolution when a dependency is resolved.
23
+ *
24
+ * @param currentServiceName - Name of the service being created
25
+ * @param currentServiceScope - Current scope of the service being created
26
+ * @param dependencyName - Name of the dependency being resolved
27
+ * @param dependencyScope - Scope of the dependency
28
+ * @param dependencyToken - Token of the dependency
29
+ * @param singletonStorage - Singleton storage instance
30
+ * @param requestStorage - Request storage instance (if in request context)
31
+ * @param requestId - Request ID (if in request context)
32
+ * @returns [needsUpgrade: boolean, newName?: string] - whether upgrade occurred and new name
33
+ */ checkAndUpgradeScope(currentServiceName, currentServiceScope, dependencyName, dependencyScope, dependencyToken, singletonStorage, requestStorage, requestId) {
34
+ if (currentServiceScope !== InjectableScope.Singleton || dependencyScope !== InjectableScope.Request) return [false];
35
+ if (!requestStorage || !requestId) {
36
+ this.logger?.warn(`[ScopeTracker] Cannot upgrade scope for ${currentServiceName}: missing requestStorage or requestId`);
37
+ return [false];
38
+ }
39
+ this.logger?.log(`[ScopeTracker] Upgrading ${currentServiceName} from Singleton to Request scope`);
40
+ try {
41
+ const [success, newName] = this.upgradeScopeToRequestSync(currentServiceName, dependencyToken, singletonStorage, requestStorage, requestId);
42
+ if (success && newName) return [true, newName];
43
+ } catch (error) {
44
+ this.logger?.error(`[ScopeTracker] Error upgrading scope for ${currentServiceName}:`, error);
45
+ }
46
+ return [false];
47
+ }
48
+ /**
49
+ * Performs the actual scope upgrade from Singleton to Request.
50
+ * This is the core migration logic.
51
+ *
52
+ * @param serviceName - Current service name (without requestId)
53
+ * @param token - Service injection token
54
+ * @param singletonStorage - Source storage
55
+ * @param requestStorage - Target storage
56
+ * @param requestId - Request ID to include in new name
57
+ * @returns [success: boolean, newName?: string, error?: DIError]
58
+ */ async upgradeScopeToRequest(serviceName, token, singletonStorage, requestStorage, requestId) {
59
+ try {
60
+ const [success, newName] = this.upgradeScopeToRequestSync(serviceName, token, singletonStorage, requestStorage, requestId);
61
+ if (success && newName) return [true, newName];
62
+ return [
63
+ false,
64
+ void 0,
65
+ DIError.storageError("Scope upgrade failed", "upgradeScopeToRequest", serviceName)
66
+ ];
67
+ } catch (error) {
68
+ return [
69
+ false,
70
+ void 0,
71
+ error instanceof DIError ? error : DIError.unknown(error)
72
+ ];
73
+ }
74
+ }
75
+ /**
76
+ * Synchronous part of scope upgrade - handles immediate updates.
77
+ * Async operations (like waiting for holder creation) should be done separately.
78
+ */ upgradeScopeToRequestSync(serviceName, token, singletonStorage, requestStorage, requestId) {
79
+ const newName = this.nameResolver.upgradeInstanceNameToRequest(serviceName, requestId);
80
+ if (!this.registry.updateScope(token, InjectableScope.Request)) {
81
+ this.logger?.warn(`[ScopeTracker] Could not update scope in registry for ${serviceName}`);
82
+ return [false];
83
+ }
84
+ const holderResult = singletonStorage.get(serviceName);
85
+ if (holderResult === null) return [true, newName];
86
+ const [error, holder] = holderResult;
87
+ if (error) {
88
+ this.logger?.warn(`[ScopeTracker] Holder for ${serviceName} is in error state: ${error.message}`);
89
+ return [false];
90
+ }
91
+ if (!holder) return [false];
92
+ if (holder.status === InstanceStatus.Creating) {
93
+ holder.name = newName;
94
+ requestStorage.set(newName, holder);
95
+ singletonStorage.delete(serviceName);
96
+ this.updateParentDependencies(serviceName, newName, singletonStorage, requestStorage);
97
+ return [true, newName];
98
+ }
99
+ holder.name = newName;
100
+ requestStorage.set(newName, holder);
101
+ singletonStorage.delete(serviceName);
102
+ this.updateParentDependencies(serviceName, newName, singletonStorage, requestStorage);
103
+ return [true, newName];
104
+ }
105
+ /**
106
+ * Updates all parent dependencies to reference the new service name.
107
+ *
108
+ * @param oldName - Original service name
109
+ * @param newName - New service name with requestId
110
+ * @param singletonStorage - Singleton storage to check
111
+ * @param requestStorage - Request storage to check
112
+ */ updateParentDependencies(oldName, newName, singletonStorage, requestStorage) {
113
+ singletonStorage.updateDependencyReference(oldName, newName);
114
+ if (requestStorage) requestStorage.updateDependencyReference(oldName, newName);
115
+ }
116
+ };
117
+
118
+ //#endregion
119
+ export { ScopeTracker };
120
+ //# sourceMappingURL=scope-tracker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-tracker.mjs","names":["InjectableScope","Scope","DIError","InstanceStatus","ScopeTracker","registry","nameResolver","logger","checkAndUpgradeScope","currentServiceName","currentServiceScope","dependencyName","dependencyScope","dependencyToken","singletonStorage","requestStorage","requestId","Singleton","Request","warn","log","success","newName","upgradeScopeToRequestSync","error","upgradeScopeToRequest","serviceName","token","undefined","storageError","unknown","upgradeInstanceNameToRequest","updated","updateScope","holderResult","get","holder","message","status","Creating","name","set","delete","updateParentDependencies","oldName","updateDependencyReference"],"sources":["../../../../src/internal/core/scope-tracker.mts"],"sourcesContent":["import type { InjectableScope } from '../../enums/index.mjs'\nimport type { InjectionToken } from '../../token/injection-token.mjs'\nimport type { IHolderStorage } from '../holder/holder-storage.interface.mjs'\n\nimport { InjectableScope as Scope } from '../../enums/index.mjs'\nimport { DIError } from '../../errors/index.mjs'\nimport { InstanceStatus } from '../holder/instance-holder.mjs'\nimport { Registry } from '../../token/registry.mjs'\nimport { NameResolver } from './name-resolver.mjs'\n\n/**\n * Component for tracking and handling scope upgrades.\n *\n * Detects when a Singleton service needs to be upgraded to Request scope\n * and coordinates the scope upgrade process atomically.\n */\nexport class ScopeTracker {\n constructor(\n private readonly registry: Registry,\n private readonly nameResolver: NameResolver,\n private readonly logger: Console | null = null,\n ) {}\n\n /**\n * Checks if a dependency requires scope upgrade and performs it if needed.\n * Called during service resolution when a dependency is resolved.\n *\n * @param currentServiceName - Name of the service being created\n * @param currentServiceScope - Current scope of the service being created\n * @param dependencyName - Name of the dependency being resolved\n * @param dependencyScope - Scope of the dependency\n * @param dependencyToken - Token of the dependency\n * @param singletonStorage - Singleton storage instance\n * @param requestStorage - Request storage instance (if in request context)\n * @param requestId - Request ID (if in request context)\n * @returns [needsUpgrade: boolean, newName?: string] - whether upgrade occurred and new name\n */\n checkAndUpgradeScope(\n currentServiceName: string,\n currentServiceScope: InjectableScope,\n dependencyName: string,\n dependencyScope: InjectableScope,\n dependencyToken: InjectionToken<any, any>,\n singletonStorage: IHolderStorage,\n requestStorage?: IHolderStorage,\n requestId?: string,\n ): [boolean, string?] {\n // Only upgrade if current service is Singleton and dependency is Request\n if (\n currentServiceScope !== Scope.Singleton ||\n dependencyScope !== Scope.Request\n ) {\n return [false]\n }\n\n // Need request storage and requestId for upgrade\n if (!requestStorage || !requestId) {\n this.logger?.warn(\n `[ScopeTracker] Cannot upgrade scope for ${currentServiceName}: missing requestStorage or requestId`,\n )\n return [false]\n }\n\n // Perform the upgrade\n this.logger?.log(\n `[ScopeTracker] Upgrading ${currentServiceName} from Singleton to Request scope`,\n )\n\n try {\n const [success, newName] = this.upgradeScopeToRequestSync(\n currentServiceName,\n dependencyToken,\n singletonStorage,\n requestStorage,\n requestId,\n )\n\n if (success && newName) {\n return [true, newName]\n }\n } catch (error) {\n this.logger?.error(\n `[ScopeTracker] Error upgrading scope for ${currentServiceName}:`,\n error,\n )\n }\n\n return [false]\n }\n\n /**\n * Performs the actual scope upgrade from Singleton to Request.\n * This is the core migration logic.\n *\n * @param serviceName - Current service name (without requestId)\n * @param token - Service injection token\n * @param singletonStorage - Source storage\n * @param requestStorage - Target storage\n * @param requestId - Request ID to include in new name\n * @returns [success: boolean, newName?: string, error?: DIError]\n */\n async upgradeScopeToRequest(\n serviceName: string,\n token: InjectionToken<any, any>,\n singletonStorage: IHolderStorage,\n requestStorage: IHolderStorage,\n requestId: string,\n ): Promise<[boolean, string?, DIError?]> {\n try {\n const [success, newName] = this.upgradeScopeToRequestSync(\n serviceName,\n token,\n singletonStorage,\n requestStorage,\n requestId,\n )\n\n if (success && newName) {\n return [true, newName]\n }\n return [\n false,\n undefined,\n DIError.storageError(\n 'Scope upgrade failed',\n 'upgradeScopeToRequest',\n serviceName,\n ),\n ]\n } catch (error) {\n return [\n false,\n undefined,\n error instanceof DIError ? error : DIError.unknown(error as Error),\n ]\n }\n }\n\n /**\n * Synchronous part of scope upgrade - handles immediate updates.\n * Async operations (like waiting for holder creation) should be done separately.\n */\n private upgradeScopeToRequestSync(\n serviceName: string,\n token: InjectionToken<any, any>,\n singletonStorage: IHolderStorage,\n requestStorage: IHolderStorage,\n requestId: string,\n ): [boolean, string?] {\n // 1. Upgrade existing instance name to include requestId\n // This preserves any args hash that might be in the original name\n const newName = this.nameResolver.upgradeInstanceNameToRequest(\n serviceName,\n requestId,\n )\n\n // 2. Update Registry scope to Request (synchronous)\n const updated = this.registry.updateScope(token, Scope.Request)\n if (!updated) {\n this.logger?.warn(\n `[ScopeTracker] Could not update scope in registry for ${serviceName}`,\n )\n return [false]\n }\n\n // 3. Check if holder exists in singleton storage\n const holderResult = singletonStorage.get(serviceName)\n if (holderResult === null) {\n // No holder exists yet - just update registry, future resolutions will use request storage\n return [true, newName]\n }\n\n const [error, holder] = holderResult\n if (error) {\n this.logger?.warn(\n `[ScopeTracker] Holder for ${serviceName} is in error state: ${error.message}`,\n )\n return [false]\n }\n\n if (!holder) {\n return [false]\n }\n\n // 4. If holder is in \"Creating\" state, we need to wait for it before migrating\n // For now, we'll update the name and move it, but the caller should wait for creation\n if (holder.status === InstanceStatus.Creating) {\n // Update holder name\n holder.name = newName\n // Move to request storage\n requestStorage.set(newName, holder)\n // Remove from singleton storage\n singletonStorage.delete(serviceName)\n // Update parent dependencies\n this.updateParentDependencies(\n serviceName,\n newName,\n singletonStorage,\n requestStorage,\n )\n return [true, newName]\n }\n\n // 5. Move holder from singleton to request storage\n holder.name = newName\n requestStorage.set(newName, holder)\n singletonStorage.delete(serviceName)\n\n // 6. Update all parent dependencies\n this.updateParentDependencies(\n serviceName,\n newName,\n singletonStorage,\n requestStorage,\n )\n\n return [true, newName]\n }\n\n /**\n * Updates all parent dependencies to reference the new service name.\n *\n * @param oldName - Original service name\n * @param newName - New service name with requestId\n * @param singletonStorage - Singleton storage to check\n * @param requestStorage - Request storage to check\n */\n updateParentDependencies(\n oldName: string,\n newName: string,\n singletonStorage: IHolderStorage,\n requestStorage?: IHolderStorage,\n ): void {\n // Update dependencies in singleton storage\n singletonStorage.updateDependencyReference(oldName, newName)\n\n // Update dependencies in request storage if provided\n if (requestStorage) {\n requestStorage.updateDependencyReference(oldName, newName)\n }\n }\n}\n"],"mappings":";;;;;;;;;;GAgBA,IAAaI,eAAb,MAAaA;;;;CACX,YACE,UACA,cACA,SAA0C,MAC1C;OAHiBC,WAAAA;OACAC,eAAAA;OACAC,SAAAA;;;;;;;;;;;;;;;IAiBnBC,qBACEC,oBACAC,qBACAC,gBACAC,iBACAC,iBACAC,kBACAC,gBACAC,WACoB;AAEpB,MACEN,wBAAwBT,gBAAMgB,aAC9BL,oBAAoBX,gBAAMiB,QAE1B,QAAO,CAAC,MAAM;AAIhB,MAAI,CAACH,kBAAkB,CAACC,WAAW;AACjC,QAAKT,QAAQY,KACX,2CAA2CV,mBAAmB,uCAAsC;AAEtG,UAAO,CAAC,MAAM;;AAIhB,OAAKF,QAAQa,IACX,4BAA4BX,mBAAmB,kCAAiC;AAGlF,MAAI;GACF,MAAM,CAACY,SAASC,WAAW,KAAKC,0BAC9Bd,oBACAI,iBACAC,kBACAC,gBACAC,UAAAA;AAGF,OAAIK,WAAWC,QACb,QAAO,CAAC,MAAMA,QAAQ;WAEjBE,OAAO;AACd,QAAKjB,QAAQiB,MACX,4CAA4Cf,mBAAmB,IAC/De,MAAAA;;AAIJ,SAAO,CAAC,MAAM;;;;;;;;;;;;IAchB,MAAMC,sBACJC,aACAC,OACAb,kBACAC,gBACAC,WACuC;AACvC,MAAI;GACF,MAAM,CAACK,SAASC,WAAW,KAAKC,0BAC9BG,aACAC,OACAb,kBACAC,gBACAC,UAAAA;AAGF,OAAIK,WAAWC,QACb,QAAO,CAAC,MAAMA,QAAQ;AAExB,UAAO;IACL;IACAM;IACA1B,QAAQ2B,aACN,wBACA,yBACAH,YAAAA;IAEH;WACMF,OAAO;AACd,UAAO;IACL;IACAI;IACAJ,iBAAiBtB,UAAUsB,QAAQtB,QAAQ4B,QAAQN,MAAAA;IACpD;;;;;;IAQL,0BACEE,aACAC,OACAb,kBACAC,gBACAC,WACoB;EAGpB,MAAMM,UAAU,KAAKhB,aAAayB,6BAChCL,aACAV,UAAAA;AAKF,MAAI,CADY,KAAKX,SAAS4B,YAAYN,OAAO1B,gBAAMiB,QAAO,EAChD;AACZ,QAAKX,QAAQY,KACX,yDAAyDO,cAAa;AAExE,UAAO,CAAC,MAAM;;EAIhB,MAAMQ,eAAepB,iBAAiBqB,IAAIT,YAAAA;AAC1C,MAAIQ,iBAAiB,KAEnB,QAAO,CAAC,MAAMZ,QAAQ;EAGxB,MAAM,CAACE,OAAOY,UAAUF;AACxB,MAAIV,OAAO;AACT,QAAKjB,QAAQY,KACX,6BAA6BO,YAAY,sBAAsBF,MAAMa,UAAS;AAEhF,UAAO,CAAC,MAAM;;AAGhB,MAAI,CAACD,OACH,QAAO,CAAC,MAAM;AAKhB,MAAIA,OAAOE,WAAWnC,eAAeoC,UAAU;AAE7CH,UAAOI,OAAOlB;AAEdP,kBAAe0B,IAAInB,SAASc,OAAAA;AAE5BtB,oBAAiB4B,OAAOhB,YAAAA;AAExB,QAAKiB,yBACHjB,aACAJ,SACAR,kBACAC,eAAAA;AAEF,UAAO,CAAC,MAAMO,QAAQ;;AAIxBc,SAAOI,OAAOlB;AACdP,iBAAe0B,IAAInB,SAASc,OAAAA;AAC5BtB,mBAAiB4B,OAAOhB,YAAAA;AAGxB,OAAKiB,yBACHjB,aACAJ,SACAR,kBACAC,eAAAA;AAGF,SAAO,CAAC,MAAMO,QAAQ;;;;;;;;;IAWxBqB,yBACEC,SACAtB,SACAR,kBACAC,gBACM;AAEND,mBAAiB+B,0BAA0BD,SAAStB,QAAAA;AAGpD,MAAIP,eACFA,gBAAe8B,0BAA0BD,SAAStB,QAAAA"}
@@ -0,0 +1,44 @@
1
+ import { ServiceInitializationContext } from "../context/service-initialization-context.mjs";
2
+ import { Injectors } from "../../utils/get-injectors.mjs";
3
+ import { FactoryRecord } from "../../token/registry.mjs";
4
+ import { DIError } from "../../errors/di-error.mjs";
5
+
6
+ //#region src/internal/core/service-initializer.d.mts
7
+
8
+ /**
9
+ * Creates service instances from registry records.
10
+ *
11
+ * Handles both class-based (@Injectable) and factory-based (@Factory) services,
12
+ * managing the instantiation lifecycle including lifecycle hook invocation.
13
+ */
14
+ declare class ServiceInitializer {
15
+ private readonly injectors;
16
+ constructor(injectors: Injectors);
17
+ /**
18
+ * Instantiates a service based on its registry record.
19
+ * @param ctx The factory context for dependency injection
20
+ * @param record The factory record from the registry
21
+ * @param args Optional arguments for the service
22
+ * @returns Promise resolving to [undefined, instance] or [error]
23
+ */
24
+ instantiateService<T>(ctx: ServiceInitializationContext, record: FactoryRecord<T, any>, args?: any): Promise<[undefined, T] | [DIError]>;
25
+ /**
26
+ * Instantiates a class-based service (Injectable decorator).
27
+ * @param ctx The factory context for dependency injection
28
+ * @param record The factory record from the registry
29
+ * @param args Optional arguments for the service constructor
30
+ * @returns Promise resolving to [undefined, instance] or [error]
31
+ */
32
+ private instantiateClass;
33
+ /**
34
+ * Instantiates a factory-based service (Factory decorator).
35
+ * @param ctx The factory context for dependency injection
36
+ * @param record The factory record from the registry
37
+ * @param args Optional arguments for the factory
38
+ * @returns Promise resolving to [undefined, instance] or [error]
39
+ */
40
+ private instantiateFactory;
41
+ }
42
+ //#endregion
43
+ export { ServiceInitializer };
44
+ //# sourceMappingURL=service-initializer.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-initializer.d.mts","names":[],"sources":["../../../../src/internal/core/service-initializer.mts"],"sourcesContent":[],"mappings":";;;;;;;;;AAaA;;;;AAYY,cAZC,kBAAA,CAYD;EAEa,iBAAA,SAAA;EAAM,WAAA,CAAA,SAAA,EAbW,SAaX;EAA1B;;;;;;;6BAHI,sCACG,cAAc,sBAErB,oBAAoB,MAAM"}
@@ -0,0 +1,109 @@
1
+ import { InjectableType } from "../../enums/injectable-type.enum.mjs";
2
+ import { DIError } from "../../errors/di-error.mjs";
3
+
4
+ //#region src/internal/core/service-initializer.mts
5
+ /**
6
+ * Creates service instances from registry records.
7
+ *
8
+ * Handles both class-based (@Injectable) and factory-based (@Factory) services,
9
+ * managing the instantiation lifecycle including lifecycle hook invocation.
10
+ */ var ServiceInitializer = class {
11
+ injectors;
12
+ constructor(injectors) {
13
+ this.injectors = injectors;
14
+ }
15
+ /**
16
+ * Instantiates a service based on its registry record.
17
+ * @param ctx The factory context for dependency injection
18
+ * @param record The factory record from the registry
19
+ * @param args Optional arguments for the service
20
+ * @returns Promise resolving to [undefined, instance] or [error]
21
+ */ async instantiateService(ctx, record, args = void 0) {
22
+ try {
23
+ switch (record.type) {
24
+ case InjectableType.Class: return this.instantiateClass(ctx, record, args);
25
+ case InjectableType.Factory: return this.instantiateFactory(ctx, record, args);
26
+ default: throw DIError.unknown(`[ServiceInitializer] Unknown service type: ${record.type}`);
27
+ }
28
+ } catch (error) {
29
+ return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
30
+ }
31
+ }
32
+ /**
33
+ * Instantiates a class-based service (Injectable decorator).
34
+ * @param ctx The factory context for dependency injection
35
+ * @param record The factory record from the registry
36
+ * @param args Optional arguments for the service constructor
37
+ * @returns Promise resolving to [undefined, instance] or [error]
38
+ */ async instantiateClass(ctx, record, args) {
39
+ try {
40
+ const tryLoad = this.injectors.wrapSyncInit(() => {
41
+ const original = this.injectors.provideFactoryContext(ctx);
42
+ let result = new record.target(...args ? [args] : []);
43
+ this.injectors.provideFactoryContext(original);
44
+ return result;
45
+ });
46
+ let [instance, promises, injectState] = tryLoad();
47
+ if (promises.length > 0) {
48
+ if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
49
+ const newRes = tryLoad(injectState);
50
+ instance = newRes[0];
51
+ promises = newRes[1];
52
+ }
53
+ if (promises.length > 0) {
54
+ console.error(`[ServiceInitializer] ${record.target.name} has problem with it's definition.
55
+
56
+ One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.
57
+
58
+ Please use asyncInject instead of inject to load those dependencies.`);
59
+ throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
60
+ }
61
+ if ("onServiceInit" in instance) await instance.onServiceInit();
62
+ if ("onServiceDestroy" in instance) ctx.addDestroyListener(async () => {
63
+ await instance.onServiceDestroy();
64
+ });
65
+ return [void 0, instance];
66
+ } catch (error) {
67
+ return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
68
+ }
69
+ }
70
+ /**
71
+ * Instantiates a factory-based service (Factory decorator).
72
+ * @param ctx The factory context for dependency injection
73
+ * @param record The factory record from the registry
74
+ * @param args Optional arguments for the factory
75
+ * @returns Promise resolving to [undefined, instance] or [error]
76
+ */ async instantiateFactory(ctx, record, args) {
77
+ try {
78
+ const tryLoad = this.injectors.wrapSyncInit(() => {
79
+ const original = this.injectors.provideFactoryContext(ctx);
80
+ let result = new record.target();
81
+ this.injectors.provideFactoryContext(original);
82
+ return result;
83
+ });
84
+ let [builder, promises, injectState] = tryLoad();
85
+ if (promises.length > 0) {
86
+ if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
87
+ const newRes = tryLoad(injectState);
88
+ builder = newRes[0];
89
+ promises = newRes[1];
90
+ }
91
+ if (promises.length > 0) {
92
+ console.error(`[ServiceInitializer] ${record.target.name} has problem with it's definition.
93
+
94
+ One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.
95
+
96
+ Please use asyncInject instead of inject to load those dependencies.`);
97
+ throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
98
+ }
99
+ if (typeof builder.create !== "function") throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Factory does not implement the create method"));
100
+ return [void 0, await builder.create(ctx, args)];
101
+ } catch (error) {
102
+ return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
103
+ }
104
+ }
105
+ };
106
+
107
+ //#endregion
108
+ export { ServiceInitializer };
109
+ //# sourceMappingURL=service-initializer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-initializer.mjs","names":["InjectableType","DIError","ServiceInitializer","injectors","instantiateService","ctx","record","args","undefined","type","Class","instantiateClass","Factory","instantiateFactory","unknown","error","initializationError","target","name","tryLoad","wrapSyncInit","original","provideFactoryContext","result","instance","promises","injectState","length","results","Promise","allSettled","some","status","Error","newRes","console","onServiceInit","addDestroyListener","onServiceDestroy","builder","create"],"sources":["../../../../src/internal/core/service-initializer.mts"],"sourcesContent":["import type { FactoryRecord } from '../../token/registry.mjs'\nimport type { Injectors } from '../../utils/index.mjs'\nimport type { ServiceInitializationContext } from '../context/service-initialization-context.mjs'\n\nimport { InjectableType } from '../../enums/index.mjs'\nimport { DIError } from '../../errors/index.mjs'\n\n/**\n * Creates service instances from registry records.\n *\n * Handles both class-based (@Injectable) and factory-based (@Factory) services,\n * managing the instantiation lifecycle including lifecycle hook invocation.\n */\nexport class ServiceInitializer {\n constructor(private readonly injectors: Injectors) {}\n\n /**\n * Instantiates a service based on its registry record.\n * @param ctx The factory context for dependency injection\n * @param record The factory record from the registry\n * @param args Optional arguments for the service\n * @returns Promise resolving to [undefined, instance] or [error]\n */\n async instantiateService<T>(\n ctx: ServiceInitializationContext,\n record: FactoryRecord<T, any>,\n args: any = undefined,\n ): Promise<[undefined, T] | [DIError]> {\n try {\n switch (record.type) {\n case InjectableType.Class:\n return this.instantiateClass(ctx, record, args)\n case InjectableType.Factory:\n return this.instantiateFactory(ctx, record, args)\n default:\n throw DIError.unknown(\n `[ServiceInitializer] Unknown service type: ${record.type}`,\n )\n }\n } catch (error) {\n return [\n error instanceof DIError\n ? error\n : DIError.initializationError(record.target.name, error as Error),\n ]\n }\n }\n\n /**\n * Instantiates a class-based service (Injectable decorator).\n * @param ctx The factory context for dependency injection\n * @param record The factory record from the registry\n * @param args Optional arguments for the service constructor\n * @returns Promise resolving to [undefined, instance] or [error]\n */\n private async instantiateClass<T>(\n ctx: ServiceInitializationContext,\n record: FactoryRecord<T, any>,\n args: any,\n ): Promise<[undefined, T] | [DIError]> {\n try {\n const tryLoad = this.injectors.wrapSyncInit(() => {\n const original = this.injectors.provideFactoryContext(\n ctx as ServiceInitializationContext,\n )\n let result = new record.target(...(args ? [args] : []))\n this.injectors.provideFactoryContext(original)\n return result\n })\n\n let [instance, promises, injectState] = tryLoad()\n if (promises.length > 0) {\n const results = await Promise.allSettled(promises)\n if (results.some((result) => result.status === 'rejected')) {\n throw DIError.initializationError(\n record.target.name,\n new Error('Service cannot be instantiated'),\n )\n }\n const newRes = tryLoad(injectState)\n instance = newRes[0]\n promises = newRes[1]\n }\n\n if (promises.length > 0) {\n console.error(\n `[ServiceInitializer] ${record.target.name} has problem with it's definition.\n\n One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.\n\n Please use asyncInject instead of inject to load those dependencies.`,\n )\n throw DIError.initializationError(\n record.target.name,\n new Error('Service cannot be instantiated'),\n )\n }\n\n // Handle lifecycle hooks\n if ('onServiceInit' in instance) {\n await (instance as any).onServiceInit()\n }\n if ('onServiceDestroy' in instance) {\n ctx.addDestroyListener(async () => {\n await (instance as any).onServiceDestroy()\n })\n }\n\n return [undefined, instance]\n } catch (error) {\n return [\n error instanceof DIError\n ? error\n : DIError.initializationError(record.target.name, error as Error),\n ]\n }\n }\n\n /**\n * Instantiates a factory-based service (Factory decorator).\n * @param ctx The factory context for dependency injection\n * @param record The factory record from the registry\n * @param args Optional arguments for the factory\n * @returns Promise resolving to [undefined, instance] or [error]\n */\n private async instantiateFactory<T>(\n ctx: ServiceInitializationContext,\n record: FactoryRecord<T, any>,\n args: any,\n ): Promise<[undefined, T] | [DIError]> {\n try {\n const tryLoad = this.injectors.wrapSyncInit(() => {\n const original = this.injectors.provideFactoryContext(ctx)\n let result = new record.target()\n this.injectors.provideFactoryContext(original)\n return result\n })\n\n let [builder, promises, injectState] = tryLoad()\n if (promises.length > 0) {\n const results = await Promise.allSettled(promises)\n if (results.some((result) => result.status === 'rejected')) {\n throw DIError.initializationError(\n record.target.name,\n new Error('Service cannot be instantiated'),\n )\n }\n const newRes = tryLoad(injectState)\n builder = newRes[0]\n promises = newRes[1]\n }\n\n if (promises.length > 0) {\n console.error(\n `[ServiceInitializer] ${record.target.name} has problem with it's definition.\n\n One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.\n\n Please use asyncInject instead of inject to load those dependencies.`,\n )\n throw DIError.initializationError(\n record.target.name,\n new Error('Service cannot be instantiated'),\n )\n }\n\n if (typeof builder.create !== 'function') {\n throw DIError.initializationError(\n record.target.name,\n new Error('Factory does not implement the create method'),\n )\n }\n\n const instance = await builder.create(ctx, args)\n return [undefined, instance]\n } catch (error) {\n return [\n error instanceof DIError\n ? error\n : DIError.initializationError(record.target.name, error as Error),\n ]\n }\n }\n}\n"],"mappings":";;;;;;;;;GAaA,IAAaE,qBAAb,MAAaA;;CACX,YAAY,WAAuC;OAAtBC,YAAAA;;;;;;;;IAS7B,MAAMC,mBACJC,KACAC,QACAC,OAAYC,QACyB;AACrC,MAAI;AACF,WAAQF,OAAOG,MAAf;IACE,KAAKT,eAAeU,MAClB,QAAO,KAAKC,iBAAiBN,KAAKC,QAAQC,KAAAA;IAC5C,KAAKP,eAAeY,QAClB,QAAO,KAAKC,mBAAmBR,KAAKC,QAAQC,KAAAA;IAC9C,QACE,OAAMN,QAAQa,QACZ,8CAA8CR,OAAOG,OAAM;;WAG1DM,OAAO;AACd,UAAO,CACLA,iBAAiBd,UACbc,QACAd,QAAQe,oBAAoBV,OAAOW,OAAOC,MAAMH,MAAAA,CACrD;;;;;;;;;IAWL,MAAcJ,iBACZN,KACAC,QACAC,MACqC;AACrC,MAAI;GACF,MAAMY,UAAU,KAAKhB,UAAUiB,mBAAa;IAC1C,MAAMC,WAAW,KAAKlB,UAAUmB,sBAC9BjB,IAAAA;IAEF,IAAIkB,SAAS,IAAIjB,OAAOW,OAAM,GAAKV,OAAO,CAACA,KAAK,GAAG,EAAE,CAAA;AACrD,SAAKJ,UAAUmB,sBAAsBD,SAAAA;AACrC,WAAOE;KACT;GAEA,IAAI,CAACC,UAAUC,UAAUC,eAAeP,SAAAA;AACxC,OAAIM,SAASE,SAAS,GAAG;AAEvB,SADgB,MAAME,QAAQC,WAAWL,SAAAA,EAC7BM,MAAMR,WAAWA,OAAOS,WAAW,WAAA,CAC7C,OAAM/B,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,iCAAA,CAAA;IAGd,MAAMC,SAASf,QAAQO,YAAAA;AACvBF,eAAWU,OAAO;AAClBT,eAAWS,OAAO;;AAGpB,OAAIT,SAASE,SAAS,GAAG;AACvBQ,YAAQpB,MACN,wBAAwBT,OAAOW,OAAOC,KAAK;;;;6EAIuB;AAEpE,UAAMjB,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,iCAAA,CAAA;;AAKd,OAAI,mBAAmBT,SACrB,OAAM,SAAkBY,eAAa;AAEvC,OAAI,sBAAsBZ,SACxBnB,KAAIgC,mBAAmB,YAAA;AACrB,UAAM,SAAkBC,kBAAgB;KAC1C;AAGF,UAAO,CAAC9B,QAAWgB,SAAS;WACrBT,OAAO;AACd,UAAO,CACLA,iBAAiBd,UACbc,QACAd,QAAQe,oBAAoBV,OAAOW,OAAOC,MAAMH,MAAAA,CACrD;;;;;;;;;IAWL,MAAcF,mBACZR,KACAC,QACAC,MACqC;AACrC,MAAI;GACF,MAAMY,UAAU,KAAKhB,UAAUiB,mBAAa;IAC1C,MAAMC,WAAW,KAAKlB,UAAUmB,sBAAsBjB,IAAAA;IACtD,IAAIkB,SAAS,IAAIjB,OAAOW,QAAM;AAC9B,SAAKd,UAAUmB,sBAAsBD,SAAAA;AACrC,WAAOE;KACT;GAEA,IAAI,CAACgB,SAASd,UAAUC,eAAeP,SAAAA;AACvC,OAAIM,SAASE,SAAS,GAAG;AAEvB,SADgB,MAAME,QAAQC,WAAWL,SAAAA,EAC7BM,MAAMR,WAAWA,OAAOS,WAAW,WAAA,CAC7C,OAAM/B,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,iCAAA,CAAA;IAGd,MAAMC,SAASf,QAAQO,YAAAA;AACvBa,cAAUL,OAAO;AACjBT,eAAWS,OAAO;;AAGpB,OAAIT,SAASE,SAAS,GAAG;AACvBQ,YAAQpB,MACN,wBAAwBT,OAAOW,OAAOC,KAAK;;;;6EAIuB;AAEpE,UAAMjB,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,iCAAA,CAAA;;AAId,OAAI,OAAOM,QAAQC,WAAW,WAC5B,OAAMvC,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,+CAAA,CAAA;AAKd,UAAO,CAACzB,QADS,MAAM+B,QAAQC,OAAOnC,KAAKE,KAAAA,CACf;WACrBQ,OAAO;AACd,UAAO,CACLA,iBAAiBd,UACbc,QACAd,QAAQe,oBAAoBV,OAAOW,OAAOC,MAAMH,MAAAA,CACrD"}
@@ -0,0 +1,81 @@
1
+ import { InstanceHolder } from "../holder/instance-holder.mjs";
2
+ import { IHolderStorage } from "../holder/holder-storage.interface.mjs";
3
+ import { LifecycleEventBus } from "../lifecycle/lifecycle-event-bus.mjs";
4
+
5
+ //#region src/internal/core/service-invalidator.d.mts
6
+ interface ClearAllOptions {
7
+ /** Whether to wait for all services to settle before starting (default: true) */
8
+ waitForSettlement?: boolean;
9
+ }
10
+ interface InvalidationOptions {
11
+ /** Whether to emit events after invalidation (default: true) */
12
+ emitEvents?: boolean;
13
+ /** Custom event emitter function */
14
+ onInvalidated?: (instanceName: string) => Promise<void>;
15
+ /** Whether to cascade invalidation to dependents (default: false - events handle it) */
16
+ cascade?: boolean;
17
+ }
18
+ /**
19
+ * Manages graceful service cleanup with event-based invalidation.
20
+ *
21
+ * Uses event subscriptions instead of manual dependent finding.
22
+ * When a service is created, it subscribes to destroy events of its dependencies.
23
+ * When a dependency is destroyed, the event automatically invalidates dependents.
24
+ */
25
+ declare class ServiceInvalidator {
26
+ private readonly eventBus;
27
+ private readonly logger;
28
+ constructor(eventBus: LifecycleEventBus | null, logger?: Console | null);
29
+ /**
30
+ * Invalidates a service using a specific storage.
31
+ * Event-based invalidation means dependents are automatically invalidated
32
+ * via destroy event subscriptions - no need to manually find dependents.
33
+ *
34
+ * @param service The instance name to invalidate
35
+ * @param storage The storage to use for this invalidation
36
+ * @param options Additional options for invalidation behavior
37
+ */
38
+ invalidateWithStorage(service: string, storage: IHolderStorage, options?: InvalidationOptions): Promise<void>;
39
+ /**
40
+ * Sets up destroy event subscriptions for a service's dependencies.
41
+ * Called when a service is successfully instantiated.
42
+ *
43
+ * @param serviceName The name of the service
44
+ * @param dependencies The set of dependency names
45
+ * @param storage The storage to use for invalidation
46
+ * @param holder The holder for the service (to add unsubscribe to destroy listeners)
47
+ */
48
+ setupDependencySubscriptions(serviceName: string, dependencies: Set<string>, storage: IHolderStorage, holder: InstanceHolder): void;
49
+ /**
50
+ * Gracefully clears all services in a specific storage.
51
+ * This allows clearing request-scoped services using a RequestStorage.
52
+ */
53
+ clearAllWithStorage(storage: IHolderStorage, options?: ClearAllOptions): Promise<void>;
54
+ /**
55
+ * Waits for all services in a specific storage to settle.
56
+ */
57
+ readyWithStorage(storage: IHolderStorage): Promise<void>;
58
+ /**
59
+ * Invalidates a single holder using a specific storage.
60
+ */
61
+ private invalidateHolderWithStorage;
62
+ /**
63
+ * Common invalidation logic for holders based on their status.
64
+ */
65
+ private invalidateHolderByStatus;
66
+ /**
67
+ * Destroys a holder using a specific storage.
68
+ */
69
+ private destroyHolderWithStorage;
70
+ /**
71
+ * Waits for a holder to settle (either created, destroyed, or error state).
72
+ */
73
+ private waitForHolderToSettle;
74
+ /**
75
+ * Emits events to listeners for instance lifecycle events.
76
+ */
77
+ private emitInstanceEvent;
78
+ }
79
+ //#endregion
80
+ export { ClearAllOptions, InvalidationOptions, ServiceInvalidator };
81
+ //# sourceMappingURL=service-invalidator.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-invalidator.d.mts","names":[],"sources":["../../../../src/internal/core/service-invalidator.mts"],"sourcesContent":[],"mappings":";;;;;UAMiB,eAAA;;EAAA,iBAAA,CAAe,EAAA,OAAA;AAKhC;AAgBa,UAhBI,mBAAA,CAgBc;EAEA;EACF,UAAA,CAAA,EAAA,OAAA;EAchB;EACA,aAAA,CAAA,EAAA,CAAA,YAAA,EAAA,MAAA,EAAA,GA9B+B,OA8B/B,CAAA,IAAA,CAAA;EACR;EAmCa,OAAA,CAAA,EAAA,OAAA;;;;;;;;;cAtDL,kBAAA;;;wBAEkB,mCACF;;;;;;;;;;kDAchB,0BACA,sBACR;;;;;;;;;;kEAmCa,sBACL,wBACD;;;;;+BAgCC,0BACA,kBACR;;;;4BAuC6B,iBAAiB"}
@@ -0,0 +1,142 @@
1
+ import { InstanceStatus } from "../holder/instance-holder.mjs";
2
+
3
+ //#region src/internal/core/service-invalidator.mts
4
+ /**
5
+ * Manages graceful service cleanup with event-based invalidation.
6
+ *
7
+ * Uses event subscriptions instead of manual dependent finding.
8
+ * When a service is created, it subscribes to destroy events of its dependencies.
9
+ * When a dependency is destroyed, the event automatically invalidates dependents.
10
+ */ var ServiceInvalidator = class {
11
+ eventBus;
12
+ logger;
13
+ constructor(eventBus, logger = null) {
14
+ this.eventBus = eventBus;
15
+ this.logger = logger;
16
+ }
17
+ /**
18
+ * Invalidates a service using a specific storage.
19
+ * Event-based invalidation means dependents are automatically invalidated
20
+ * via destroy event subscriptions - no need to manually find dependents.
21
+ *
22
+ * @param service The instance name to invalidate
23
+ * @param storage The storage to use for this invalidation
24
+ * @param options Additional options for invalidation behavior
25
+ */ async invalidateWithStorage(service, storage, options = {}) {
26
+ const { emitEvents = true, onInvalidated } = options;
27
+ this.logger?.log(`[ServiceInvalidator] Starting invalidation process for ${service}`);
28
+ const result = storage.get(service);
29
+ if (result === null) return;
30
+ const [, holder] = result;
31
+ if (holder) await this.invalidateHolderWithStorage(service, holder, storage, emitEvents, onInvalidated);
32
+ }
33
+ /**
34
+ * Sets up destroy event subscriptions for a service's dependencies.
35
+ * Called when a service is successfully instantiated.
36
+ *
37
+ * @param serviceName The name of the service
38
+ * @param dependencies The set of dependency names
39
+ * @param storage The storage to use for invalidation
40
+ * @param holder The holder for the service (to add unsubscribe to destroy listeners)
41
+ */ setupDependencySubscriptions(serviceName, dependencies, storage, holder) {
42
+ if (!this.eventBus) return;
43
+ for (const dependencyName of dependencies) {
44
+ const unsubscribe = this.eventBus.on(dependencyName, "destroy", () => {
45
+ this.logger?.log(`[ServiceInvalidator] Dependency ${dependencyName} destroyed, invalidating ${serviceName}`);
46
+ this.invalidateWithStorage(serviceName, storage).catch((error) => {
47
+ this.logger?.error(`[ServiceInvalidator] Error invalidating ${serviceName} after dependency ${dependencyName} destroyed:`, error);
48
+ });
49
+ });
50
+ holder.destroyListeners.push(unsubscribe);
51
+ }
52
+ }
53
+ /**
54
+ * Gracefully clears all services in a specific storage.
55
+ * This allows clearing request-scoped services using a RequestStorage.
56
+ */ async clearAllWithStorage(storage, options = {}) {
57
+ const { waitForSettlement = true } = options;
58
+ this.logger?.log("[ServiceInvalidator] Starting graceful clearing of all services");
59
+ if (waitForSettlement) {
60
+ this.logger?.log("[ServiceInvalidator] Waiting for all services to settle...");
61
+ await this.readyWithStorage(storage);
62
+ }
63
+ const allServiceNames = storage.getAllNames();
64
+ if (allServiceNames.length === 0) this.logger?.log("[ServiceInvalidator] No services to clear");
65
+ else {
66
+ this.logger?.log(`[ServiceInvalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(", ")}`);
67
+ const clearPromises = allServiceNames.map((serviceName) => this.invalidateWithStorage(serviceName, storage));
68
+ await Promise.all(clearPromises);
69
+ }
70
+ this.logger?.log("[ServiceInvalidator] Graceful clearing completed");
71
+ }
72
+ /**
73
+ * Waits for all services in a specific storage to settle.
74
+ */ async readyWithStorage(storage) {
75
+ const holders = [];
76
+ storage.forEach((_, holder) => holders.push(holder));
77
+ await Promise.all(holders.map((holder) => this.waitForHolderToSettle(holder)));
78
+ }
79
+ /**
80
+ * Invalidates a single holder using a specific storage.
81
+ */ async invalidateHolderWithStorage(key, holder, storage, emitEvents, onInvalidated) {
82
+ await this.invalidateHolderByStatus(holder, {
83
+ context: key,
84
+ onDestroy: () => this.destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated)
85
+ });
86
+ }
87
+ /**
88
+ * Common invalidation logic for holders based on their status.
89
+ */ async invalidateHolderByStatus(holder, options) {
90
+ switch (holder.status) {
91
+ case InstanceStatus.Destroying:
92
+ await holder.destroyPromise;
93
+ break;
94
+ case InstanceStatus.Creating:
95
+ await holder.creationPromise;
96
+ await options.onDestroy();
97
+ break;
98
+ default:
99
+ await options.onDestroy();
100
+ break;
101
+ }
102
+ }
103
+ /**
104
+ * Destroys a holder using a specific storage.
105
+ */ async destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated) {
106
+ holder.status = InstanceStatus.Destroying;
107
+ this.logger?.log(`[ServiceInvalidator] Invalidating ${key} and notifying listeners`);
108
+ holder.destroyPromise = Promise.all(holder.destroyListeners.map((listener) => listener())).then(async () => {
109
+ holder.destroyListeners = [];
110
+ holder.deps.clear();
111
+ storage.delete(key);
112
+ if (emitEvents && this.eventBus) await this.emitInstanceEvent(key, "destroy");
113
+ if (onInvalidated) await onInvalidated(key);
114
+ });
115
+ await holder.destroyPromise;
116
+ }
117
+ /**
118
+ * Waits for a holder to settle (either created, destroyed, or error state).
119
+ */ async waitForHolderToSettle(holder) {
120
+ switch (holder.status) {
121
+ case InstanceStatus.Creating:
122
+ await holder.creationPromise;
123
+ break;
124
+ case InstanceStatus.Destroying:
125
+ await holder.destroyPromise;
126
+ break;
127
+ case InstanceStatus.Created:
128
+ case InstanceStatus.Error: break;
129
+ }
130
+ }
131
+ /**
132
+ * Emits events to listeners for instance lifecycle events.
133
+ */ emitInstanceEvent(name, event = "create") {
134
+ if (!this.eventBus) return Promise.resolve();
135
+ this.logger?.log(`[ServiceInvalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`);
136
+ return this.eventBus.emit(name, event);
137
+ }
138
+ };
139
+
140
+ //#endregion
141
+ export { ServiceInvalidator };
142
+ //# sourceMappingURL=service-invalidator.mjs.map