@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
@@ -25,19 +25,45 @@ let InjectableType = /* @__PURE__ */ function(InjectableType$1) {
25
25
  return InjectableType$1;
26
26
  }({});
27
27
 
28
+ //#endregion
29
+ //#region src/symbols/injectable-token.mts
30
+ const InjectableTokenMeta = /* @__PURE__ */ Symbol.for("InjectableTokenMeta");
31
+
28
32
  //#endregion
29
33
  //#region src/token/injection-token.mts
34
+ /**
35
+ * Simple hash function for deterministic ID generation
36
+ */ function simpleHash(str) {
37
+ let hash = 0;
38
+ for (let i = 0; i < str.length; i++) {
39
+ const char = str.charCodeAt(i);
40
+ hash = (hash << 5) - hash + char;
41
+ hash = hash & hash;
42
+ }
43
+ return Math.abs(hash).toString(36);
44
+ }
45
+ /**
46
+ * Generate deterministic ID from token name
47
+ */ function generateTokenId(name, customId) {
48
+ if (customId) return customId;
49
+ let base;
50
+ if (typeof name === "function") base = `${name.name}_${name.toString()}`;
51
+ else if (typeof name === "symbol") base = `symbol_${name.toString()}`;
52
+ else base = `token_${name}`;
53
+ return `${base.split("_")[0]}_${simpleHash(base)}`;
54
+ }
30
55
  var InjectionToken = class InjectionToken {
31
56
  name;
32
57
  schema;
33
- id = globalThis.crypto.randomUUID();
58
+ id;
34
59
  formattedName = null;
35
- constructor(name, schema) {
60
+ constructor(name, schema, customId) {
36
61
  this.name = name;
37
62
  this.schema = schema;
63
+ this.id = generateTokenId(name, customId);
38
64
  }
39
- static create(name, schema) {
40
- return new InjectionToken(name, schema);
65
+ static create(name, schema, customId) {
66
+ return new InjectionToken(name, schema, customId);
41
67
  }
42
68
  static bound(token, value) {
43
69
  return new BoundInjectionToken(token, value);
@@ -106,6 +132,7 @@ var FactoryInjectionToken = class {
106
132
  var Registry = class {
107
133
  parent;
108
134
  factories = /* @__PURE__ */ new Map();
135
+ highestPriority = /* @__PURE__ */ new Map();
109
136
  constructor(parent) {
110
137
  this.parent = parent;
111
138
  }
@@ -115,23 +142,50 @@ var Registry = class {
115
142
  return false;
116
143
  }
117
144
  get(token) {
118
- const factory = this.factories.get(token.id);
145
+ const factory = this.highestPriority.get(token.id);
119
146
  if (!factory) {
120
147
  if (this.parent) return this.parent.get(token);
121
148
  throw new Error(`[Registry] No factory found for ${token.toString()}`);
122
149
  }
123
150
  return factory;
124
151
  }
125
- set(token, scope, target, type) {
126
- this.factories.set(token.id, {
152
+ getAll(token) {
153
+ const records = this.factories.get(token.id);
154
+ if (!records || records.length === 0) {
155
+ if (this.parent) return this.parent.getAll(token);
156
+ return [];
157
+ }
158
+ return [...records].sort((a, b) => b.priority - a.priority);
159
+ }
160
+ set(token, scope, target, type, priority = 0) {
161
+ const record = {
127
162
  scope,
128
163
  originalToken: token,
129
164
  target,
130
- type
131
- });
165
+ type,
166
+ priority
167
+ };
168
+ const existing = this.factories.get(token.id) || [];
169
+ existing.push(record);
170
+ this.factories.set(token.id, existing);
171
+ const currentHighest = this.highestPriority.get(token.id);
172
+ if (!currentHighest || priority > currentHighest.priority) this.highestPriority.set(token.id, record);
132
173
  }
133
174
  delete(token) {
134
- this.factories.delete(token.id);
175
+ const records = this.factories.get(token.id);
176
+ if (records) {
177
+ const deletedHighest = this.highestPriority.get(token.id);
178
+ this.factories.delete(token.id);
179
+ this.highestPriority.delete(token.id);
180
+ if (deletedHighest && records.length > 1) {
181
+ const remaining = records.filter((r) => r.originalToken.id !== deletedHighest.originalToken.id || r.priority !== deletedHighest.priority);
182
+ if (remaining.length > 0) {
183
+ const newHighest = remaining.reduce((max, current) => current.priority > max.priority ? current : max);
184
+ this.highestPriority.set(token.id, newHighest);
185
+ this.factories.set(token.id, remaining);
186
+ }
187
+ }
188
+ }
135
189
  }
136
190
  /**
137
191
  * Updates the scope of an already registered factory.
@@ -142,29 +196,29 @@ var Registry = class {
142
196
  * @param scope The new scope to set
143
197
  * @returns true if the scope was updated, false if the token was not found
144
198
  */ updateScope(token, scope) {
145
- const factory = this.factories.get(token.id);
146
- if (factory) {
147
- factory.scope = scope;
199
+ const records = this.factories.get(token.id);
200
+ if (records && records.length > 0) {
201
+ records.forEach((record) => {
202
+ record.scope = scope;
203
+ });
204
+ const highest = this.highestPriority.get(token.id);
205
+ if (highest) highest.scope = scope;
148
206
  return true;
149
207
  }
150
208
  if (this.parent) return this.parent.updateScope(token, scope);
151
209
  return false;
152
210
  }
153
211
  };
154
- const globalRegistry = new Registry();
155
-
156
- //#endregion
157
- //#region src/symbols/injectable-token.mts
158
- const InjectableTokenMeta = Symbol.for("InjectableTokenMeta");
212
+ const globalRegistry = /* @__PURE__ */ new Registry();
159
213
 
160
214
  //#endregion
161
215
  //#region src/decorators/injectable.decorator.mts
162
- function Injectable({ scope = InjectableScope.Singleton, token, schema, registry = globalRegistry } = {}) {
216
+ function Injectable({ scope = InjectableScope.Singleton, token, schema, registry = globalRegistry, priority = 0 } = {}) {
163
217
  return (target, context) => {
164
- if (context && context.kind !== "class" || target instanceof Function && !context) throw new Error("[ServiceLocator] @Injectable decorator can only be used on classes.");
165
- if (schema && token) throw new Error("[ServiceLocator] @Injectable decorator cannot have both a token and a schema");
218
+ if (context && context.kind !== "class" || target instanceof Function && !context) throw new Error("[DI] @Injectable decorator can only be used on classes.");
219
+ if (schema && token) throw new Error("[DI] @Injectable decorator cannot have both a token and a schema");
166
220
  let injectableToken = token ?? InjectionToken.create(target, schema);
167
- registry.set(injectableToken, scope, target, InjectableType.Class);
221
+ registry.set(injectableToken, scope, target, InjectableType.Class, priority);
168
222
  target[InjectableTokenMeta] = injectableToken;
169
223
  return target;
170
224
  };
@@ -172,45 +226,97 @@ function Injectable({ scope = InjectableScope.Singleton, token, schema, registry
172
226
 
173
227
  //#endregion
174
228
  //#region src/errors/di-error.mts
175
- let DIErrorCode = /* @__PURE__ */ function(DIErrorCode$1) {
229
+ var DIErrorCode = /* @__PURE__ */ function(DIErrorCode$1) {
176
230
  DIErrorCode$1["FactoryNotFound"] = "FactoryNotFound";
177
231
  DIErrorCode$1["FactoryTokenNotResolved"] = "FactoryTokenNotResolved";
178
232
  DIErrorCode$1["InstanceNotFound"] = "InstanceNotFound";
179
233
  DIErrorCode$1["InstanceDestroying"] = "InstanceDestroying";
180
234
  DIErrorCode$1["CircularDependency"] = "CircularDependency";
235
+ DIErrorCode$1["TokenValidationError"] = "TokenValidationError";
236
+ DIErrorCode$1["TokenSchemaRequiredError"] = "TokenSchemaRequiredError";
237
+ DIErrorCode$1["ClassNotInjectable"] = "ClassNotInjectable";
238
+ DIErrorCode$1["ScopeMismatchError"] = "ScopeMismatchError";
239
+ DIErrorCode$1["PriorityConflictError"] = "PriorityConflictError";
240
+ DIErrorCode$1["StorageError"] = "StorageError";
241
+ DIErrorCode$1["InitializationError"] = "InitializationError";
242
+ DIErrorCode$1["DependencyResolutionError"] = "DependencyResolutionError";
181
243
  DIErrorCode$1["UnknownError"] = "UnknownError";
182
244
  return DIErrorCode$1;
183
245
  }({});
184
246
  var DIError = class DIError extends Error {
247
+ code;
248
+ message;
185
249
  context;
186
250
  constructor(code, message, context) {
187
- super(message);
188
- this.code = code;
189
- this.message = message;
251
+ super(message), this.code = code, this.message = message;
190
252
  this.context = context;
253
+ this.name = "DIError";
191
254
  }
192
255
  static factoryNotFound(name) {
193
- return new DIError(DIErrorCode.FactoryNotFound, `Factory ${name} not found`, { name });
256
+ return new DIError("FactoryNotFound", `Factory ${name} not found`, { name });
194
257
  }
195
258
  static factoryTokenNotResolved(token) {
196
- return new DIError(DIErrorCode.FactoryTokenNotResolved, `Factory token not resolved: ${token?.toString() ?? "unknown"}`, { token });
259
+ return new DIError("FactoryTokenNotResolved", `Factory token not resolved: ${token?.toString() ?? "unknown"}`, { token });
197
260
  }
198
261
  static instanceNotFound(name) {
199
- return new DIError(DIErrorCode.InstanceNotFound, `Instance ${name} not found`, { name });
262
+ return new DIError("InstanceNotFound", `Instance ${name} not found`, { name });
200
263
  }
201
264
  static instanceDestroying(name) {
202
- return new DIError(DIErrorCode.InstanceDestroying, `Instance ${name} destroying`, { name });
265
+ return new DIError("InstanceDestroying", `Instance ${name} destroying`, { name });
203
266
  }
204
267
  static unknown(message, context) {
205
- if (message instanceof Error) return new DIError(DIErrorCode.UnknownError, message.message, {
268
+ if (message instanceof Error) return new DIError("UnknownError", message.message, {
206
269
  ...context,
207
270
  parent: message
208
271
  });
209
- return new DIError(DIErrorCode.UnknownError, message, context);
272
+ return new DIError("UnknownError", message, context);
210
273
  }
211
274
  static circularDependency(cycle) {
212
- const cycleStr = cycle.join(" -> ");
213
- return new DIError(DIErrorCode.CircularDependency, `Circular dependency detected: ${cycleStr}`, { cycle });
275
+ return new DIError("CircularDependency", `Circular dependency detected: ${cycle.join(" -> ")}`, { cycle });
276
+ }
277
+ static tokenValidationError(message, schema, value) {
278
+ return new DIError("TokenValidationError", message, {
279
+ schema,
280
+ value
281
+ });
282
+ }
283
+ static tokenSchemaRequiredError(token) {
284
+ return new DIError("TokenSchemaRequiredError", `Token ${token?.toString() ?? "unknown"} requires schema arguments and cannot be used with addInstance. Use BoundInjectionToken or provide arguments when resolving.`, { token });
285
+ }
286
+ static classNotInjectable(className) {
287
+ return new DIError("ClassNotInjectable", `Class ${className} is not decorated with @Injectable.`, { className });
288
+ }
289
+ static scopeMismatchError(token, expectedScope, actualScope) {
290
+ return new DIError("ScopeMismatchError", `Scope mismatch for ${token?.toString() ?? "unknown"}: expected ${expectedScope}, got ${actualScope}`, {
291
+ token,
292
+ expectedScope,
293
+ actualScope
294
+ });
295
+ }
296
+ static priorityConflictError(token, records) {
297
+ return new DIError("PriorityConflictError", `Priority conflict for ${token?.toString() ?? "unknown"}: multiple bindings with same priority`, {
298
+ token,
299
+ records
300
+ });
301
+ }
302
+ static storageError(message, operation, instanceName) {
303
+ return new DIError("StorageError", `Storage error: ${message}`, {
304
+ operation,
305
+ instanceName
306
+ });
307
+ }
308
+ static initializationError(serviceName, error) {
309
+ return new DIError("InitializationError", `Service ${serviceName} initialization failed: ${error instanceof Error ? error.message : error}`, {
310
+ serviceName,
311
+ error
312
+ });
313
+ }
314
+ static dependencyResolutionError(serviceName, dependencyName, error) {
315
+ return new DIError("DependencyResolutionError", `Failed to resolve dependency ${dependencyName} for service ${serviceName}: ${error instanceof Error ? error.message : error}`, {
316
+ serviceName,
317
+ dependencyName,
318
+ error
319
+ });
214
320
  }
215
321
  };
216
322
 
@@ -240,6 +346,9 @@ function getModule() {
240
346
  function createAsyncLocalStorage() {
241
347
  return getModule().createAsyncLocalStorage();
242
348
  }
349
+ function isUsingNativeAsyncLocalStorage() {
350
+ return getModule().isUsingNativeAsyncLocalStorage();
351
+ }
243
352
 
244
353
  //#endregion
245
354
  //#region src/internal/context/resolution-context.mts
@@ -294,135 +403,21 @@ function getResolutionContext() {
294
403
  }
295
404
 
296
405
  //#endregion
297
- //#region src/utils/get-injectors.mts
298
- function getInjectors() {
299
- let currentFactoryContext = null;
300
- function provideFactoryContext$1(context) {
301
- const original = currentFactoryContext;
302
- currentFactoryContext = context;
303
- return original;
304
- }
305
- function getFactoryContext() {
306
- if (!currentFactoryContext) throw new Error("[Injector] Trying to access injection context outside of a injectable context");
307
- return currentFactoryContext;
308
- }
309
- let promiseCollector = null;
310
- let injectState = null;
311
- function getRequest(token, args, skipCycleTracking = false) {
312
- if (!injectState) throw new Error("[Injector] Trying to make a request outside of a injectable context");
313
- if (injectState.isFrozen) {
314
- const idx = injectState.currentIndex++;
315
- const request$1 = injectState.requests[idx];
316
- if (request$1.token !== token) throw new Error(`[Injector] Wrong token order. Expected ${request$1.token.toString()} but got ${token.toString()}`);
317
- return request$1;
318
- }
319
- let result = null;
320
- let error = null;
321
- const doInject = () => getFactoryContext().inject(token, args).then((r) => {
322
- result = r;
323
- return r;
324
- }).catch((e) => {
325
- error = e;
326
- });
327
- const request = {
328
- token,
329
- promise: skipCycleTracking ? withoutResolutionContext(doInject) : doInject(),
330
- get result() {
331
- return result;
332
- },
333
- get error() {
334
- return error;
335
- }
336
- };
337
- injectState.requests.push(request);
338
- injectState.currentIndex++;
339
- return request;
340
- }
341
- function asyncInject$1(token, args) {
342
- if (!injectState) throw new Error("[Injector] Trying to access inject outside of a injectable context");
343
- const request = getRequest(token[InjectableTokenMeta] ?? token, args, true);
344
- return request.promise.then((result) => {
345
- if (request.error) throw request.error;
346
- return result;
347
- });
348
- }
349
- function wrapSyncInit$1(cb) {
350
- return (previousState) => {
351
- const promises = [];
352
- const originalPromiseCollector = promiseCollector;
353
- const originalInjectState = injectState;
354
- injectState = previousState ? {
355
- ...previousState,
356
- currentIndex: 0
357
- } : {
358
- currentIndex: 0,
359
- isFrozen: false,
360
- requests: []
361
- };
362
- promiseCollector = (promise) => {
363
- promises.push(promise);
364
- };
365
- const result = cb();
366
- promiseCollector = originalPromiseCollector;
367
- const newInjectState = {
368
- ...injectState,
369
- isFrozen: true
370
- };
371
- injectState = originalInjectState;
372
- return [
373
- result,
374
- promises,
375
- newInjectState
376
- ];
377
- };
378
- }
379
- function inject$1(token, args) {
380
- const realToken = token[InjectableTokenMeta] ?? token;
381
- if (!injectState) throw new Error("[Injector] Trying to access inject outside of a injectable context");
382
- const instance = getFactoryContext().container.tryGetSync(realToken, args);
383
- if (!instance) {
384
- const request = getRequest(realToken, args);
385
- if (request.error) throw request.error;
386
- else if (request.result) return request.result;
387
- if (promiseCollector) promiseCollector(request.promise);
388
- return new Proxy({}, { get() {
389
- throw new Error(`[Injector] Trying to access ${realToken.toString()} before it's initialized, please move the code to a onServiceInit method`);
390
- } });
391
- }
392
- return instance;
393
- }
394
- function optional$1(token, args) {
395
- try {
396
- return inject$1(token, args);
397
- } catch {
398
- return null;
399
- }
400
- }
401
- return {
402
- asyncInject: asyncInject$1,
403
- inject: inject$1,
404
- optional: optional$1,
405
- wrapSyncInit: wrapSyncInit$1,
406
- provideFactoryContext: provideFactoryContext$1
407
- };
408
- }
409
-
410
- //#endregion
411
- //#region src/utils/get-injectable-token.mts
412
- function getInjectableToken(target) {
413
- const token = target[InjectableTokenMeta];
414
- if (!token) throw new Error(`[ServiceLocator] Class ${target.name} is not decorated with @Injectable.`);
415
- return token;
416
- }
417
-
418
- //#endregion
419
- //#region src/injectors.mts
420
- const defaultInjectors = getInjectors();
421
- const asyncInject = defaultInjectors.asyncInject;
422
- const inject = defaultInjectors.inject;
423
- const optional = defaultInjectors.optional;
424
- const wrapSyncInit = defaultInjectors.wrapSyncInit;
425
- const provideFactoryContext = defaultInjectors.provideFactoryContext;
406
+ //#region src/internal/holder/instance-holder.mts
407
+ /**
408
+ * Represents the lifecycle status of an instance holder.
409
+ */
410
+ let InstanceStatus = /* @__PURE__ */ function(InstanceStatus$1) {
411
+ /** Instance has been successfully created and is ready for use */
412
+ InstanceStatus$1["Created"] = "created";
413
+ /** Instance is currently being created (async initialization in progress) */
414
+ InstanceStatus$1["Creating"] = "creating";
415
+ /** Instance is being destroyed (cleanup in progress) */
416
+ InstanceStatus$1["Destroying"] = "destroying";
417
+ /** Instance creation failed with an error */
418
+ InstanceStatus$1["Error"] = "error";
419
+ return InstanceStatus$1;
420
+ }({});
426
421
 
427
422
  //#endregion
428
423
  //#region src/internal/lifecycle/circular-detector.mts
@@ -483,784 +478,559 @@ const provideFactoryContext = defaultInjectors.provideFactoryContext;
483
478
  };
484
479
 
485
480
  //#endregion
486
- //#region src/internal/holder/instance-holder.mts
487
- /**
488
- * Represents the lifecycle status of an instance holder.
489
- */
490
- let InstanceStatus = /* @__PURE__ */ function(InstanceStatus$1) {
491
- /** Instance has been successfully created and is ready for use */
492
- InstanceStatus$1["Created"] = "created";
493
- /** Instance is currently being created (async initialization in progress) */
494
- InstanceStatus$1["Creating"] = "creating";
495
- /** Instance is being destroyed (cleanup in progress) */
496
- InstanceStatus$1["Destroying"] = "destroying";
497
- /** Instance creation failed with an error */
498
- InstanceStatus$1["Error"] = "error";
499
- return InstanceStatus$1;
500
- }({});
501
-
502
- //#endregion
503
- //#region src/internal/holder/base-holder-manager.mts
481
+ //#region src/internal/core/instance-resolver.mts
504
482
  /**
505
- * Abstract base class providing common functionality for managing InstanceHolder objects.
483
+ * Resolves instances from tokens, handling caching, creation, and scope rules.
506
484
  *
507
- * Provides shared patterns for holder storage, creation, and lifecycle management
508
- * used by both singleton (HolderManager) and request-scoped (RequestContext) managers.
509
- */ var BaseHolderManager = class BaseHolderManager {
485
+ * Uses unified storage for both singleton and request-scoped services.
486
+ * Coordinates with ServiceInitializer for actual service creation.
487
+ * Integrates ScopeTracker for automatic scope upgrades.
488
+ */ var InstanceResolver = class {
489
+ registry;
490
+ storage;
491
+ serviceInitializer;
492
+ tokenResolver;
493
+ nameResolver;
494
+ scopeTracker;
495
+ serviceInvalidator;
496
+ eventBus;
510
497
  logger;
511
- _holders;
512
- constructor(logger = null) {
498
+ constructor(registry, storage, serviceInitializer, tokenResolver, nameResolver, scopeTracker, serviceInvalidator, eventBus, logger = null) {
499
+ this.registry = registry;
500
+ this.storage = storage;
501
+ this.serviceInitializer = serviceInitializer;
502
+ this.tokenResolver = tokenResolver;
503
+ this.nameResolver = nameResolver;
504
+ this.scopeTracker = scopeTracker;
505
+ this.serviceInvalidator = serviceInvalidator;
506
+ this.eventBus = eventBus;
513
507
  this.logger = logger;
514
- this._holders = /* @__PURE__ */ new Map();
515
- }
516
- /**
517
- * Protected getter for accessing the holders map from subclasses.
518
- */ get holders() {
519
- return this._holders;
520
508
  }
521
509
  /**
522
- * Deletes a holder by name.
523
- * @param name The name of the holder to delete
524
- * @returns true if the holder was deleted, false if it didn't exist
525
- */ delete(name) {
526
- return this._holders.delete(name);
510
+ * Resolves an instance for the given token and arguments.
511
+ * This method is used for singleton and transient services.
512
+ *
513
+ * @param token The injection token
514
+ * @param args Optional arguments
515
+ * @param contextContainer The container to use for creating context
516
+ * @param requestStorage Optional request storage (for scope upgrades)
517
+ * @param requestId Optional request ID (for scope upgrades)
518
+ */ async resolveInstance(token, args, contextContainer, requestStorage, requestId) {
519
+ return this.resolveWithStorage(token, args, contextContainer, this.storage, void 0, requestStorage, requestId);
527
520
  }
528
521
  /**
529
- * Filters holders based on a predicate function.
530
- * @param predicate Function to test each holder
531
- * @returns A new Map containing only the holders that match the predicate
532
- */ filter(predicate) {
533
- return new Map([...this._holders].filter(([key, value]) => predicate(value, key)));
522
+ * Resolves a request-scoped instance for a ScopedContainer.
523
+ * The service will be stored in the ScopedContainer's request storage.
524
+ *
525
+ * @param token The injection token
526
+ * @param args Optional arguments
527
+ * @param scopedContainer The ScopedContainer that owns the request context
528
+ */ async resolveRequestScopedInstance(token, args, scopedContainer) {
529
+ return this.resolveWithStorage(token, args, scopedContainer.getParent(), scopedContainer.getParent().getStorage(), scopedContainer, scopedContainer.getStorage(), scopedContainer.getRequestId());
534
530
  }
535
531
  /**
536
- * Clears all holders from this manager.
537
- */ clear() {
538
- this._holders.clear();
532
+ * Unified resolution method that works with any IHolderStorage.
533
+ * This eliminates duplication between singleton and request-scoped resolution.
534
+ *
535
+ * IMPORTANT: The check-and-store logic is carefully designed to avoid race conditions.
536
+ * The storage check and holder creation must happen synchronously (no awaits between).
537
+ *
538
+ * @param token The injection token
539
+ * @param args Optional arguments
540
+ * @param contextContainer The container for context
541
+ * @param storage The storage strategy to use
542
+ * @param scopedContainer Optional scoped container for request-scoped services
543
+ * @param requestStorage Optional request storage (for scope upgrades)
544
+ * @param requestId Optional request ID (for scope upgrades)
545
+ */ async resolveWithStorage(token, args, contextContainer, storage, scopedContainer, requestStorage, requestId) {
546
+ const [err, data] = await this.resolveTokenAndPrepareInstanceName(token, args, contextContainer, requestId, scopedContainer);
547
+ if (err) return [err];
548
+ const { instanceName, validatedArgs, realToken, scope } = data;
549
+ const getResult = storage.get(instanceName) ?? requestStorage?.get(instanceName) ?? null;
550
+ const getHolder = (name) => {
551
+ const result = storage.get(name);
552
+ if (result && result[0] === void 0 && result[1]) return result[1];
553
+ if (requestStorage) {
554
+ const reqResult = requestStorage.get(name);
555
+ if (reqResult && reqResult[0] === void 0 && reqResult[1]) return reqResult[1];
556
+ }
557
+ };
558
+ if (getResult !== null) {
559
+ const [error, holder$1] = getResult;
560
+ if (!error && holder$1) {
561
+ const waiterHolder = getCurrentResolutionContext()?.waiterHolder;
562
+ const readyResult = await this.waitForInstanceReady(holder$1, waiterHolder, getHolder);
563
+ if (readyResult[0]) return [readyResult[0]];
564
+ return [void 0, readyResult[1].instance];
565
+ }
566
+ if (error) {
567
+ const handledResult = await this.handleStorageError(instanceName, error, holder$1, storage);
568
+ if (handledResult) return handledResult;
569
+ }
570
+ }
571
+ const [createError, holder] = await this.createAndStoreInstance(instanceName, realToken, validatedArgs, contextContainer, storage, scopedContainer, requestStorage, requestId, scope);
572
+ if (createError) return [createError];
573
+ return [void 0, holder.instance];
539
574
  }
540
575
  /**
541
- * Gets the number of holders currently managed.
542
- */ size() {
543
- return this._holders.size;
576
+ * Internal method to resolve token args and create instance name.
577
+ * Handles factory token resolution and validation.
578
+ */ async resolveTokenAndPrepareInstanceName(token, args, contextContainer, requestId, scopedContainer) {
579
+ const [err, { actualToken, validatedArgs }] = this.tokenResolver.validateAndResolveTokenArgs(token, args);
580
+ if (err instanceof DIError && err.code === DIErrorCode.TokenValidationError) return [err];
581
+ else if (err instanceof DIError && err.code === DIErrorCode.FactoryTokenNotResolved && actualToken instanceof FactoryInjectionToken) {
582
+ this.logger?.log(`[InstanceResolver]#resolveTokenAndPrepareInstanceName() Factory token not resolved, resolving it`);
583
+ const factoryCtx = {
584
+ inject: async (t, a) => (scopedContainer ?? contextContainer).get(t, a),
585
+ container: scopedContainer ?? contextContainer,
586
+ addDestroyListener: () => {}
587
+ };
588
+ await actualToken.resolve(factoryCtx);
589
+ return this.resolveTokenAndPrepareInstanceName(token, void 0, contextContainer, requestId, scopedContainer);
590
+ }
591
+ const realToken = this.tokenResolver.getRealToken(actualToken);
592
+ const scope = this.registry.get(realToken).scope;
593
+ return [void 0, {
594
+ instanceName: this.nameResolver.generateInstanceName(actualToken, validatedArgs, requestId, scope),
595
+ validatedArgs,
596
+ actualToken,
597
+ realToken,
598
+ scope
599
+ }];
544
600
  }
545
601
  /**
546
- * Creates a new holder with Creating status and a deferred creation promise.
547
- * This is useful for creating placeholder holders that can be fulfilled later.
548
- * @param name The name of the instance
549
- * @param type The injectable type
550
- * @param scope The injectable scope
551
- * @param deps Optional set of dependencies
552
- * @returns A tuple containing the deferred promise and the holder
553
- */ createCreatingHolder(name, type, scope, deps = /* @__PURE__ */ new Set()) {
554
- const deferred = Promise.withResolvers();
555
- return [deferred, {
556
- status: InstanceStatus.Creating,
557
- name,
558
- instance: null,
559
- creationPromise: deferred.promise,
560
- destroyPromise: null,
561
- type,
562
- scope,
563
- deps,
564
- destroyListeners: [],
565
- createdAt: Date.now(),
566
- waitingFor: /* @__PURE__ */ new Set()
567
- }];
602
+ * Handles storage error states (destroying, error, etc.).
603
+ * Returns a result if handled, null if should proceed with creation.
604
+ */ async handleStorageError(instanceName, error, holder, storage) {
605
+ switch (error.code) {
606
+ case DIErrorCode.InstanceDestroying:
607
+ this.logger?.log(`[InstanceResolver] Instance ${instanceName} is being destroyed, waiting...`);
608
+ if (holder?.destroyPromise) await holder.destroyPromise;
609
+ const newResult = storage.get(instanceName);
610
+ if (newResult !== null && !newResult[0]) {
611
+ const getHolder = (name) => {
612
+ const result = storage.get(name);
613
+ return result && result[0] === void 0 && result[1] ? result[1] : void 0;
614
+ };
615
+ const readyResult = await this.waitForInstanceReady(newResult[1], void 0, getHolder);
616
+ if (readyResult[0]) return [readyResult[0]];
617
+ return [void 0, readyResult[1].instance];
618
+ }
619
+ return null;
620
+ default:
621
+ if (holder) {
622
+ this.logger?.log(`[InstanceResolver] Removing failed instance ${instanceName} from storage to allow retry`);
623
+ storage.delete(instanceName);
624
+ }
625
+ return null;
626
+ }
568
627
  }
569
628
  /**
570
- * Creates a new holder with Created status and an actual instance.
571
- * This is useful for creating holders that already have their instance ready.
572
- * @param name The name of the instance
573
- * @param instance The actual instance to store
574
- * @param type The injectable type
575
- * @param scope The injectable scope
576
- * @param deps Optional set of dependencies
577
- * @returns The created holder
578
- */ createCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()) {
579
- return {
629
+ * Creates a new instance and stores it using the provided storage strategy.
630
+ * This unified method replaces instantiateServiceFromRegistry and createRequestScopedInstance.
631
+ *
632
+ * For transient services, the instance is created but not stored (no caching).
633
+ */ async createAndStoreInstance(instanceName, realToken, args, contextContainer, storage, scopedContainer, requestStorage, requestId, scope) {
634
+ this.logger?.log(`[InstanceResolver]#createAndStoreInstance() Creating instance for ${instanceName}`);
635
+ if (!this.registry.has(realToken)) return [DIError.factoryNotFound(realToken.name.toString())];
636
+ const record = this.registry.get(realToken);
637
+ const { type, scope: recordScope } = record;
638
+ const serviceScope = scope || recordScope;
639
+ if (serviceScope === InjectableScope.Transient) return this.createTransientInstance(instanceName, record, args, contextContainer, scopedContainer, requestStorage, requestId);
640
+ if (serviceScope === InjectableScope.Request && !requestStorage) return [DIError.initializationError(`Request storage is required for request-scoped services`, instanceName)];
641
+ let storageToUse;
642
+ if (serviceScope === InjectableScope.Request) storageToUse = requestStorage;
643
+ else storageToUse = storage;
644
+ const [deferred, holder] = storageToUse.createHolder(instanceName, type, /* @__PURE__ */ new Set());
645
+ storageToUse.set(instanceName, holder);
646
+ const ctx = this.createServiceInitializationContext(scopedContainer ?? contextContainer, instanceName, serviceScope, holder.deps, realToken, requestStorage, requestId);
647
+ holder.destroyListeners = ctx.getDestroyListeners();
648
+ const getHolder = (name) => {
649
+ const result = storage.get(name);
650
+ if (result && result[0] === void 0 && result[1]) return result[1];
651
+ if (requestStorage) {
652
+ const reqResult = requestStorage.get(name);
653
+ if (reqResult && reqResult[0] === void 0 && reqResult[1]) return reqResult[1];
654
+ }
655
+ };
656
+ withResolutionContext(holder, getHolder, () => {
657
+ this.serviceInitializer.instantiateService(ctx, record, args).then(async (result) => {
658
+ const [error, instance] = result.length === 2 ? result : [result[0], void 0];
659
+ const newScope = record.scope;
660
+ const newName = this.nameResolver.generateInstanceName(realToken, args, requestId, newScope);
661
+ await this.handleInstantiationResult(newName, holder, ctx, deferred, newScope, error, instance, scopedContainer, requestStorage, requestId);
662
+ }).catch(async (error) => {
663
+ const newScope = record.scope;
664
+ const newName = this.nameResolver.generateInstanceName(realToken, args, requestId, newScope);
665
+ await this.handleInstantiationError(newName, holder, deferred, newScope, error);
666
+ }).catch(() => {});
667
+ });
668
+ const waiterHolder = getCurrentResolutionContext()?.waiterHolder;
669
+ return this.waitForInstanceReady(holder, waiterHolder, getHolder);
670
+ }
671
+ /**
672
+ * Creates a transient instance without storage or locking.
673
+ * Each call creates a new instance.
674
+ */ async createTransientInstance(instanceName, record, args, contextContainer, scopedContainer, requestStorage, requestId) {
675
+ this.logger?.log(`[InstanceResolver]#createTransientInstance() Creating transient instance for ${instanceName}`);
676
+ const ctx = this.createServiceInitializationContext(scopedContainer ?? contextContainer, instanceName, InjectableScope.Transient, /* @__PURE__ */ new Set(), record.originalToken, requestStorage, requestId);
677
+ const [error, instance] = await this.serviceInitializer.instantiateService(ctx, record, args);
678
+ if (error) return [error];
679
+ return [void 0, {
580
680
  status: InstanceStatus.Created,
581
- name,
681
+ name: instanceName,
582
682
  instance,
583
683
  creationPromise: null,
584
684
  destroyPromise: null,
585
- type,
586
- scope,
587
- deps,
588
- destroyListeners: [],
685
+ type: record.type,
686
+ scope: InjectableScope.Transient,
687
+ deps: ctx.dependencies,
688
+ destroyListeners: ctx.getDestroyListeners(),
589
689
  createdAt: Date.now(),
590
690
  waitingFor: /* @__PURE__ */ new Set()
591
- };
691
+ }];
592
692
  }
593
693
  /**
594
- * Gets all holder names currently managed.
595
- */ getAllNames() {
596
- return Array.from(this._holders.keys());
694
+ * Handles successful service instantiation.
695
+ */ async handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance, _scopedContainer, requestStorage, _requestId) {
696
+ holder.instance = instance;
697
+ holder.status = InstanceStatus.Created;
698
+ const storageForSubscriptions = requestStorage || this.storage;
699
+ if (ctx.dependencies.size > 0) this.serviceInvalidator.setupDependencySubscriptions(instanceName, ctx.dependencies, storageForSubscriptions, holder);
700
+ this.logger?.log(`[InstanceResolver] Instance ${instanceName} created successfully`);
701
+ deferred.resolve([void 0, instance]);
597
702
  }
598
703
  /**
599
- * Gets all holders currently managed.
600
- */ getAllHolders() {
601
- return Array.from(this._holders.values());
704
+ * Handles service instantiation errors.
705
+ */ async handleInstantiationError(instanceName, holder, deferred, scope, error) {
706
+ holder.status = InstanceStatus.Error;
707
+ holder.instance = error instanceof DIError ? error : DIError.unknown(error);
708
+ this.logger?.error(`[InstanceResolver] Instance ${instanceName} creation failed:`, error);
709
+ deferred.reject(error instanceof DIError ? error : DIError.unknown(error));
602
710
  }
603
711
  /**
604
- * Checks if this manager has any holders.
605
- */ isEmpty() {
606
- return this._holders.size === 0;
712
+ * Handles instantiation result (success or error).
713
+ */ async handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance, scopedContainer, requestStorage, requestId) {
714
+ if (error) await this.handleInstantiationError(instanceName, holder, deferred, scope, error);
715
+ else await this.handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance, scopedContainer, requestStorage, requestId);
607
716
  }
608
717
  /**
609
- * Waits for a holder to be ready and returns the appropriate result.
610
- * This is a shared utility used by both singleton and request-scoped resolution.
718
+ * Waits for an instance holder to be ready and returns the appropriate result.
611
719
  *
612
720
  * @param holder The holder to wait for
613
721
  * @param waiterHolder Optional holder that is doing the waiting (for circular dependency detection)
614
722
  * @param getHolder Optional function to retrieve holders by name (required if waiterHolder is provided)
615
- * @returns A promise that resolves with [undefined, holder] on success or [DIError] on failure
616
- */ static async waitForHolderReady(holder, waiterHolder, getHolder) {
723
+ */ async waitForInstanceReady(holder, waiterHolder, getHolder) {
617
724
  switch (holder.status) {
618
725
  case InstanceStatus.Creating:
619
726
  if (waiterHolder && getHolder) {
620
727
  const cycle = CircularDetector.detectCycle(waiterHolder.name, holder.name, getHolder);
621
728
  if (cycle) return [DIError.circularDependency(cycle)];
622
- waiterHolder.waitingFor.add(holder.name);
729
+ if (process.env.NODE_ENV !== "production") waiterHolder.waitingFor.add(holder.name);
623
730
  }
624
731
  try {
625
732
  await holder.creationPromise;
626
733
  } finally {
627
- if (waiterHolder) waiterHolder.waitingFor.delete(holder.name);
734
+ if (process.env.NODE_ENV !== "production") {
735
+ if (waiterHolder) waiterHolder.waitingFor.delete(holder.name);
736
+ }
628
737
  }
629
- return BaseHolderManager.waitForHolderReady(holder, waiterHolder, getHolder);
738
+ return this.waitForInstanceReady(holder, waiterHolder, getHolder);
630
739
  case InstanceStatus.Destroying: return [DIError.instanceDestroying(holder.name)];
631
740
  case InstanceStatus.Error: return [holder.instance];
632
741
  case InstanceStatus.Created: return [void 0, holder];
633
- default: return [DIError.instanceNotFound("unknown")];
742
+ default: return [DIError.instanceNotFound(holder?.name ?? "unknown")];
634
743
  }
635
744
  }
745
+ /**
746
+ * Creates a ServiceInitializationContext for service instantiation.
747
+ */ createServiceInitializationContext(container, serviceName, scope, deps, serviceToken, requestStorage, requestId) {
748
+ const destroyListeners = [];
749
+ return {
750
+ inject: async (token, args) => {
751
+ const actualToken = typeof token === "function" ? this.tokenResolver.normalizeToken(token) : token;
752
+ const realToken = this.tokenResolver.getRealToken(actualToken);
753
+ const depScope = this.registry.get(realToken).scope;
754
+ const dependencyRequestId = depScope === InjectableScope.Request ? requestId : void 0;
755
+ const finalDepName = this.nameResolver.generateInstanceName(actualToken, args, dependencyRequestId, depScope);
756
+ if (scope === InjectableScope.Singleton && depScope === InjectableScope.Request && requestStorage && requestId) {
757
+ const [needsUpgrade, newServiceName] = this.scopeTracker.checkAndUpgradeScope(serviceName, scope, finalDepName, depScope, serviceToken, this.storage, requestStorage, requestId);
758
+ if (needsUpgrade && newServiceName) {}
759
+ }
760
+ deps.add(finalDepName);
761
+ return container.get(token, args);
762
+ },
763
+ container,
764
+ addDestroyListener: (listener) => {
765
+ destroyListeners.push(listener);
766
+ },
767
+ getDestroyListeners: () => destroyListeners,
768
+ serviceName,
769
+ dependencies: deps,
770
+ scope,
771
+ trackDependency: (name, depScope) => {
772
+ deps.add(name);
773
+ if (scope === InjectableScope.Singleton && depScope === InjectableScope.Request && requestStorage && requestId) this.scopeTracker.checkAndUpgradeScope(serviceName, scope, name, depScope, serviceToken, this.storage, requestStorage, requestId);
774
+ }
775
+ };
776
+ }
636
777
  };
637
778
 
638
779
  //#endregion
639
- //#region src/internal/context/request-context.mts
780
+ //#region src/internal/core/name-resolver.mts
640
781
  /**
641
- * Default implementation of RequestContext.
642
- *
643
- * Extends BaseHolderManager to provide holder management functionality
644
- * with request-specific metadata and lifecycle support.
645
- */ var DefaultRequestContext = class extends BaseHolderManager {
646
- requestId;
647
- priority;
648
- metadata = /* @__PURE__ */ new Map();
649
- createdAt = Date.now();
650
- constructor(requestId, priority = 100, initialMetadata) {
651
- super(null), this.requestId = requestId, this.priority = priority;
652
- if (initialMetadata) Object.entries(initialMetadata).forEach(([key, value]) => {
653
- this.metadata.set(key, value);
654
- });
655
- }
656
- /**
657
- * Public getter for holders to maintain interface compatibility.
658
- */ get holders() {
659
- return this._holders;
660
- }
661
- /**
662
- * Gets a holder by name. For RequestContext, this is a simple lookup.
663
- */ get(name) {
664
- return this._holders.get(name);
665
- }
666
- /**
667
- * Sets a holder by name.
668
- */ set(name, holder) {
669
- this._holders.set(name, holder);
670
- }
671
- /**
672
- * Checks if a holder exists by name.
673
- */ has(name) {
674
- return this._holders.has(name);
782
+ * Simple LRU cache for instance name generation.
783
+ * Uses a Map which maintains insertion order for efficient LRU eviction.
784
+ */ var InstanceNameCache = class {
785
+ cache = /* @__PURE__ */ new Map();
786
+ maxSize;
787
+ constructor(maxSize = 1e3) {
788
+ this.maxSize = maxSize;
789
+ }
790
+ get(key) {
791
+ const value = this.cache.get(key);
792
+ if (value !== void 0) {
793
+ this.cache.delete(key);
794
+ this.cache.set(key, value);
795
+ }
796
+ return value;
675
797
  }
676
- addInstance(instanceName, instance, holder) {
677
- if (instanceName instanceof InjectionToken) {
678
- const name = instanceName.toString();
679
- const createdHolder = this.createCreatedHolder(name, instance, InjectableType.Class, InjectableScope.Singleton, /* @__PURE__ */ new Set());
680
- this._holders.set(name, createdHolder);
681
- } else {
682
- if (!holder) throw new Error("Holder is required when adding an instance by name");
683
- this._holders.set(instanceName, holder);
798
+ set(key, value) {
799
+ if (this.cache.has(key)) this.cache.delete(key);
800
+ else if (this.cache.size >= this.maxSize) {
801
+ const firstKey = this.cache.keys().next().value;
802
+ if (firstKey !== void 0) this.cache.delete(firstKey);
684
803
  }
804
+ this.cache.set(key, value);
685
805
  }
686
806
  clear() {
687
- super.clear();
688
- this.metadata.clear();
689
- }
690
- getMetadata(key) {
691
- return this.metadata.get(key);
692
- }
693
- setMetadata(key, value) {
694
- this.metadata.set(key, value);
807
+ this.cache.clear();
695
808
  }
696
809
  };
697
810
  /**
698
- * Creates a new request context with the given parameters.
699
- */ function createRequestContext(requestId, priority = 100, initialMetadata) {
700
- return new DefaultRequestContext(requestId, priority, initialMetadata);
811
+ * Simple hash function for deterministic hashing of arguments
812
+ */ function hashArgs(args) {
813
+ const str = JSON.stringify(args, Object.keys(args || {}).sort());
814
+ let hash = 0;
815
+ for (let i = 0; i < str.length; i++) {
816
+ const char = str.charCodeAt(i);
817
+ hash = (hash << 5) - hash + char;
818
+ hash = hash & hash;
819
+ }
820
+ return Math.abs(hash).toString(36);
701
821
  }
702
-
703
- //#endregion
704
- //#region src/internal/holder/request-storage.mts
705
822
  /**
706
- * Storage implementation for Request-scoped services.
823
+ * Handles instance name generation with support for requestId and scope.
707
824
  *
708
- * Wraps a RequestContext instance from a ScopedContainer and provides
709
- * the IHolderStorage interface. This allows the InstanceResolver to work
710
- * with request-scoped storage using the same interface as singleton storage.
711
- */
712
- var RequestStorage = class {
713
- scope = InjectableScope.Request;
714
- constructor(contextHolder, holderManager) {
715
- this.contextHolder = contextHolder;
716
- this.holderManager = holderManager;
825
+ * Generates unique instance identifiers based on token, arguments, and scope.
826
+ * Request-scoped services MUST include requestId in their name for proper isolation.
827
+ */ var NameResolver = class {
828
+ logger;
829
+ instanceNameCache = new InstanceNameCache();
830
+ constructor(logger = null) {
831
+ this.logger = logger;
717
832
  }
718
- get(instanceName) {
719
- const holder = this.contextHolder.get(instanceName);
720
- if (!holder) return null;
721
- switch (holder.status) {
722
- case InstanceStatus.Destroying: return [DIError.instanceDestroying(instanceName), holder];
723
- case InstanceStatus.Error: return [holder.instance, holder];
724
- case InstanceStatus.Creating:
725
- case InstanceStatus.Created: return [void 0, holder];
726
- default: return null;
833
+ /**
834
+ * Generates a unique instance name based on token, arguments, requestId, and scope.
835
+ *
836
+ * Name formats:
837
+ * - Singleton/Transient without args: `${tokenId}`
838
+ * - Singleton/Transient with args: `${tokenId}:${argsHash}`
839
+ * - Request without args: `${tokenId}:requestId=${requestId}`
840
+ * - Request with args: `${tokenId}:requestId=${requestId}:${argsHash}`
841
+ *
842
+ * @param token The injection token
843
+ * @param args Optional arguments
844
+ * @param requestId Optional request ID (required for request-scoped services)
845
+ * @param scope Optional scope (used to determine if requestId should be included)
846
+ * @returns The generated instance name
847
+ */ generateInstanceName(token, args, requestId, scope) {
848
+ const tokenStr = token.toString();
849
+ const isRequest = scope === InjectableScope.Request;
850
+ if (isRequest && !requestId) throw new Error(`[NameResolver] requestId is required for request-scoped services`);
851
+ const cacheKey = `${tokenStr}:${scope}:${requestId || ""}:${args ? JSON.stringify(args) : ""}`;
852
+ const cached = this.instanceNameCache.get(cacheKey);
853
+ if (cached !== void 0) return cached;
854
+ let result = tokenStr;
855
+ if (isRequest && requestId) result = `${result}:requestId=${requestId}`;
856
+ if (args) {
857
+ const argsHash = hashArgs(args);
858
+ result = `${result}:${argsHash}`;
727
859
  }
860
+ this.instanceNameCache.set(cacheKey, result);
861
+ return result;
728
862
  }
729
- set(instanceName, holder) {
730
- this.contextHolder.set(instanceName, holder);
731
- }
732
- delete(instanceName) {
733
- return this.contextHolder.delete(instanceName);
734
- }
735
- createHolder(instanceName, type, deps) {
736
- return this.holderManager.createCreatingHolder(instanceName, type, this.scope, deps);
737
- }
738
- handles(scope) {
739
- return scope === InjectableScope.Request;
740
- }
741
- getAllNames() {
742
- const names = [];
743
- for (const [name] of this.contextHolder.holders) names.push(name);
744
- return names;
745
- }
746
- forEach(callback) {
747
- for (const [name, holder] of this.contextHolder.holders) callback(name, holder);
748
- }
749
- findByInstance(instance) {
750
- for (const holder of this.contextHolder.holders.values()) if (holder.instance === instance) return holder;
751
- return null;
863
+ /**
864
+ * Upgrades an existing instance name to include requestId.
865
+ * Preserves any args hash that might already be in the name.
866
+ *
867
+ * Examples:
868
+ * - `TokenName` → `TokenName:requestId=req-123`
869
+ * - `TokenName:abc123` → `TokenName:requestId=req-123:abc123`
870
+ *
871
+ * @param existingName The existing instance name (without requestId)
872
+ * @param requestId The request ID to add
873
+ * @returns The upgraded instance name with requestId
874
+ */ upgradeInstanceNameToRequest(existingName, requestId) {
875
+ if (existingName.includes(`:requestId=${requestId}`)) return existingName;
876
+ if (/:requestId=/.test(existingName)) return existingName;
877
+ const colonIndex = existingName.indexOf(":");
878
+ if (colonIndex === -1) return `${existingName}:requestId=${requestId}`;
879
+ const tokenPart = existingName.substring(0, colonIndex);
880
+ const argsPart = existingName.substring(colonIndex + 1);
881
+ if (argsPart.startsWith("requestId=")) return existingName;
882
+ return `${tokenPart}:requestId=${requestId}:${argsPart}`;
752
883
  }
753
- findDependents(instanceName) {
754
- const dependents = [];
755
- for (const [name, holder] of this.contextHolder.holders) if (holder.deps.has(instanceName)) dependents.push(name);
756
- for (const [name, holder] of this.holderManager.filter(() => true)) if (holder.deps.has(instanceName)) dependents.push(name);
757
- return dependents;
884
+ /**
885
+ * Formats a single argument value for instance name generation.
886
+ */ formatArgValue(value) {
887
+ if (typeof value === "function") return `fn_${value.name}(${value.length})`;
888
+ if (typeof value === "symbol") return value.toString();
889
+ return JSON.stringify(value).slice(0, 40);
758
890
  }
759
891
  };
760
892
 
761
893
  //#endregion
762
- //#region src/container/scoped-container.mts
894
+ //#region src/internal/core/scope-tracker.mts
763
895
  /**
764
- * Request-scoped dependency injection container.
896
+ * Component for tracking and handling scope upgrades.
765
897
  *
766
- * Wraps a parent Container and provides isolated request-scoped instances
767
- * while delegating singleton and transient resolution to the parent.
768
- * This design eliminates race conditions that can occur with async operations
769
- * when multiple requests are processed concurrently.
770
- */ var ScopedContainer = class {
771
- parent;
898
+ * Detects when a Singleton service needs to be upgraded to Request scope
899
+ * and coordinates the scope upgrade process atomically.
900
+ */ var ScopeTracker = class {
772
901
  registry;
773
- requestId;
774
- requestContextHolder;
775
- holderStorage;
776
- disposed = false;
777
- constructor(parent, registry, requestId, metadata, priority = 100) {
778
- this.parent = parent;
902
+ nameResolver;
903
+ logger;
904
+ constructor(registry, nameResolver, logger = null) {
779
905
  this.registry = registry;
780
- this.requestId = requestId;
781
- this.requestContextHolder = new DefaultRequestContext(requestId, priority, metadata);
782
- this.holderStorage = new RequestStorage(this.requestContextHolder, this.parent.getServiceLocator().getManager());
783
- }
784
- /**
785
- * Gets the request context holder for this scoped container.
786
- */ getRequestContextHolder() {
787
- return this.requestContextHolder;
788
- }
789
- /**
790
- * Gets the holder storage for this scoped container.
791
- * Used by InstanceResolver for request-scoped resolution.
792
- */ getHolderStorage() {
793
- return this.holderStorage;
794
- }
795
- /**
796
- * Gets the request ID for this scoped container.
797
- */ getRequestId() {
798
- return this.requestId;
799
- }
800
- /**
801
- * Gets the parent container.
802
- */ getParent() {
803
- return this.parent;
804
- }
805
- /**
806
- * Gets metadata from the request context.
807
- */ getMetadata(key) {
808
- return this.requestContextHolder.getMetadata(key);
809
- }
810
- /**
811
- * Sets metadata on the request context.
812
- */ setMetadata(key, value) {
813
- this.requestContextHolder.setMetadata(key, value);
814
- }
815
- /**
816
- * Adds a pre-prepared instance to the request context.
817
- */ addInstance(token, instance) {
818
- this.requestContextHolder.addInstance(token, instance);
819
- }
820
- async get(token, args) {
821
- if (this.disposed) throw DIError.unknown(`ScopedContainer for request ${this.requestId} has been disposed`);
822
- const actualToken = this.parent.getServiceLocator().getTokenProcessor().normalizeToken(token);
823
- if (this.isRequestScoped(actualToken)) return this.resolveRequestScoped(actualToken, args);
824
- return this.parent.getWithContext(token, args, this);
906
+ this.nameResolver = nameResolver;
907
+ this.logger = logger;
825
908
  }
826
909
  /**
827
- * Invalidates a service and its dependencies.
828
- * For request-scoped services, invalidation is handled within this context.
829
- */ async invalidate(service) {
830
- const holder = this.holderStorage.findByInstance(service);
831
- if (holder) {
832
- await this.parent.getServiceLocator().getInvalidator().invalidateWithStorage(holder.name, this.holderStorage, 1, { emitEvents: false });
833
- return;
910
+ * Checks if a dependency requires scope upgrade and performs it if needed.
911
+ * Called during service resolution when a dependency is resolved.
912
+ *
913
+ * @param currentServiceName - Name of the service being created
914
+ * @param currentServiceScope - Current scope of the service being created
915
+ * @param dependencyName - Name of the dependency being resolved
916
+ * @param dependencyScope - Scope of the dependency
917
+ * @param dependencyToken - Token of the dependency
918
+ * @param singletonStorage - Singleton storage instance
919
+ * @param requestStorage - Request storage instance (if in request context)
920
+ * @param requestId - Request ID (if in request context)
921
+ * @returns [needsUpgrade: boolean, newName?: string] - whether upgrade occurred and new name
922
+ */ checkAndUpgradeScope(currentServiceName, currentServiceScope, dependencyName, dependencyScope, dependencyToken, singletonStorage, requestStorage, requestId) {
923
+ if (currentServiceScope !== InjectableScope.Singleton || dependencyScope !== InjectableScope.Request) return [false];
924
+ if (!requestStorage || !requestId) {
925
+ this.logger?.warn(`[ScopeTracker] Cannot upgrade scope for ${currentServiceName}: missing requestStorage or requestId`);
926
+ return [false];
834
927
  }
835
- await this.parent.invalidate(service);
836
- }
837
- /**
838
- * Checks if a service is registered.
839
- */ isRegistered(token) {
840
- return this.parent.isRegistered(token);
841
- }
842
- /**
843
- * Disposes this scoped container and cleans up all request-scoped instances.
844
- * This is an alias for endRequest() for IContainer compatibility.
845
- */ async dispose() {
846
- await this.endRequest();
847
- }
848
- /**
849
- * Ends the request and cleans up all request-scoped instances.
850
- * Uses the invalidation system to properly cascade to dependent singletons.
851
- */ async endRequest() {
852
- if (this.disposed) return;
853
- this.disposed = true;
854
- await this.parent.getServiceLocator().getInvalidator().clearAllWithStorage(this.holderStorage, {
855
- waitForSettlement: true,
856
- maxRounds: 10
857
- });
858
- this.requestContextHolder.clear();
859
- this.parent.removeActiveRequest(this.requestId);
860
- }
861
- /**
862
- * Waits for all pending operations to complete.
863
- */ async ready() {
864
- await this.parent.ready();
928
+ this.logger?.log(`[ScopeTracker] Upgrading ${currentServiceName} from Singleton to Request scope`);
929
+ try {
930
+ const [success, newName] = this.upgradeScopeToRequestSync(currentServiceName, dependencyToken, singletonStorage, requestStorage, requestId);
931
+ if (success && newName) return [true, newName];
932
+ } catch (error) {
933
+ this.logger?.error(`[ScopeTracker] Error upgrading scope for ${currentServiceName}:`, error);
934
+ }
935
+ return [false];
865
936
  }
866
937
  /**
867
- * @internal
868
- * Attempts to get an instance synchronously if it already exists and is ready.
869
- * For request-scoped services, checks this container's context first.
870
- * For other services, delegates to the parent container.
938
+ * Performs the actual scope upgrade from Singleton to Request.
939
+ * This is the core migration logic.
871
940
  *
872
- * Returns null if the instance doesn't exist or is not yet ready (still creating).
873
- */ tryGetSync(token, args) {
874
- const actualToken = this.parent.getServiceLocator().getTokenProcessor().normalizeToken(token);
875
- if (this.isRequestScoped(actualToken)) {
876
- const instanceName = this.parent.getServiceLocator().getInstanceIdentifier(token, args);
877
- const holder = this.requestContextHolder.get(instanceName);
878
- if (holder && holder.status === InstanceStatus.Created) return holder.instance;
879
- return null;
941
+ * @param serviceName - Current service name (without requestId)
942
+ * @param token - Service injection token
943
+ * @param singletonStorage - Source storage
944
+ * @param requestStorage - Target storage
945
+ * @param requestId - Request ID to include in new name
946
+ * @returns [success: boolean, newName?: string, error?: DIError]
947
+ */ async upgradeScopeToRequest(serviceName, token, singletonStorage, requestStorage, requestId) {
948
+ try {
949
+ const [success, newName] = this.upgradeScopeToRequestSync(serviceName, token, singletonStorage, requestStorage, requestId);
950
+ if (success && newName) return [true, newName];
951
+ return [
952
+ false,
953
+ void 0,
954
+ DIError.storageError("Scope upgrade failed", "upgradeScopeToRequest", serviceName)
955
+ ];
956
+ } catch (error) {
957
+ return [
958
+ false,
959
+ void 0,
960
+ error instanceof DIError ? error : DIError.unknown(error)
961
+ ];
880
962
  }
881
- return this.parent.tryGetSync(token, args);
882
- }
883
- /**
884
- * Checks if a token is for a request-scoped service.
885
- */ isRequestScoped(token) {
886
- const realToken = this.parent.getServiceLocator().getTokenProcessor().getRealToken(token);
887
- if (!this.registry.has(realToken)) return false;
888
- return this.registry.get(realToken).scope === InjectableScope.Request;
889
963
  }
890
964
  /**
891
- * Resolves a request-scoped service from this container's context.
892
- * Uses locking to prevent duplicate initialization during concurrent resolution.
893
- */ async resolveRequestScoped(token, args) {
894
- const instanceName = this.parent.getServiceLocator().getInstanceIdentifier(token, args);
895
- const existingHolder = this.requestContextHolder.get(instanceName);
896
- if (existingHolder) if (existingHolder.status === InstanceStatus.Error) this.requestContextHolder.delete(instanceName);
897
- else {
898
- const [error, readyHolder] = await BaseHolderManager.waitForHolderReady(existingHolder);
899
- if (error) throw error;
900
- return readyHolder.instance;
965
+ * Synchronous part of scope upgrade - handles immediate updates.
966
+ * Async operations (like waiting for holder creation) should be done separately.
967
+ */ upgradeScopeToRequestSync(serviceName, token, singletonStorage, requestStorage, requestId) {
968
+ const newName = this.nameResolver.upgradeInstanceNameToRequest(serviceName, requestId);
969
+ if (!this.registry.updateScope(token, InjectableScope.Request)) {
970
+ this.logger?.warn(`[ScopeTracker] Could not update scope in registry for ${serviceName}`);
971
+ return [false];
901
972
  }
902
- return this.parent.resolveForRequest(token, args, this);
903
- }
904
- /**
905
- * Stores an instance in the request context.
906
- * Called by Container during request-scoped service resolution.
907
- */ storeRequestInstance(instanceName, instance, holder) {
908
- this.requestContextHolder.addInstance(instanceName, instance, holder);
909
- }
910
- /**
911
- * Gets an existing instance from the request context.
912
- * Called by Container during resolution to check for existing instances.
913
- */ getRequestInstance(instanceName) {
914
- return this.requestContextHolder.get(instanceName);
973
+ const holderResult = singletonStorage.get(serviceName);
974
+ if (holderResult === null) return [true, newName];
975
+ const [error, holder] = holderResult;
976
+ if (error) {
977
+ this.logger?.warn(`[ScopeTracker] Holder for ${serviceName} is in error state: ${error.message}`);
978
+ return [false];
979
+ }
980
+ if (!holder) return [false];
981
+ if (holder.status === InstanceStatus.Creating) {
982
+ holder.name = newName;
983
+ requestStorage.set(newName, holder);
984
+ singletonStorage.delete(serviceName);
985
+ this.updateParentDependencies(serviceName, newName, singletonStorage, requestStorage);
986
+ return [true, newName];
987
+ }
988
+ holder.name = newName;
989
+ requestStorage.set(newName, holder);
990
+ singletonStorage.delete(serviceName);
991
+ this.updateParentDependencies(serviceName, newName, singletonStorage, requestStorage);
992
+ return [true, newName];
915
993
  }
916
994
  /**
917
- * Generates a prefixed event name for request-scoped services.
918
- * Format: {requestId}:{instanceName}
919
- */ getPrefixedEventName(instanceName) {
920
- return `${this.requestId}:${instanceName}`;
995
+ * Updates all parent dependencies to reference the new service name.
996
+ *
997
+ * @param oldName - Original service name
998
+ * @param newName - New service name with requestId
999
+ * @param singletonStorage - Singleton storage to check
1000
+ * @param requestStorage - Request storage to check
1001
+ */ updateParentDependencies(oldName, newName, singletonStorage, requestStorage) {
1002
+ singletonStorage.updateDependencyReference(oldName, newName);
1003
+ if (requestStorage) requestStorage.updateDependencyReference(oldName, newName);
921
1004
  }
922
1005
  };
923
1006
 
924
1007
  //#endregion
925
- //#region src/internal/holder/singleton-storage.mts
1008
+ //#region src/internal/core/service-initializer.mts
926
1009
  /**
927
- * Storage implementation for Singleton-scoped services.
1010
+ * Creates service instances from registry records.
928
1011
  *
929
- * Wraps a HolderManager instance and provides the IHolderStorage interface.
930
- * This allows the InstanceResolver to work with singleton storage
931
- * using the same interface as request-scoped storage.
932
- */
933
- var SingletonStorage = class {
934
- scope = InjectableScope.Singleton;
935
- constructor(manager) {
936
- this.manager = manager;
1012
+ * Handles both class-based (@Injectable) and factory-based (@Factory) services,
1013
+ * managing the instantiation lifecycle including lifecycle hook invocation.
1014
+ */ var ServiceInitializer = class {
1015
+ injectors;
1016
+ constructor(injectors) {
1017
+ this.injectors = injectors;
937
1018
  }
938
- get(instanceName) {
939
- const [error, holder] = this.manager.get(instanceName);
940
- if (!error) return [void 0, holder];
941
- switch (error.code) {
942
- case DIErrorCode.InstanceNotFound: return null;
943
- case DIErrorCode.InstanceDestroying: return [error, holder];
944
- default: return [error];
945
- }
946
- }
947
- set(instanceName, holder) {
948
- this.manager.set(instanceName, holder);
949
- }
950
- delete(instanceName) {
951
- return this.manager.delete(instanceName);
952
- }
953
- createHolder(instanceName, type, deps) {
954
- return this.manager.createCreatingHolder(instanceName, type, this.scope, deps);
955
- }
956
- handles(scope) {
957
- return scope === InjectableScope.Singleton;
958
- }
959
- getAllNames() {
960
- return this.manager.getAllNames();
961
- }
962
- forEach(callback) {
963
- for (const [name, holder] of this.manager.filter(() => true)) callback(name, holder);
964
- }
965
- findByInstance(instance) {
966
- for (const [, holder] of this.manager.filter((h) => h.instance === instance)) return holder;
967
- return null;
968
- }
969
- findDependents(instanceName) {
970
- const dependents = [];
971
- for (const [name, holder] of this.manager.filter(() => true)) if (holder.deps.has(instanceName)) dependents.push(name);
972
- return dependents;
973
- }
974
- };
975
-
976
- //#endregion
977
- //#region src/internal/core/instance-resolver.mts
978
- /**
979
- * Resolves instances from tokens, handling caching, creation, and scope rules.
980
- *
981
- * Uses the Storage Strategy pattern for unified singleton/request-scoped handling.
982
- * Coordinates with Instantiator for actual service creation.
983
- */ var InstanceResolver = class {
984
- registry;
985
- manager;
986
- instantiator;
987
- tokenProcessor;
988
- logger;
989
- serviceLocator;
990
- singletonStorage;
991
- constructor(registry, manager, instantiator, tokenProcessor, logger = null, serviceLocator) {
992
- this.registry = registry;
993
- this.manager = manager;
994
- this.instantiator = instantiator;
995
- this.tokenProcessor = tokenProcessor;
996
- this.logger = logger;
997
- this.serviceLocator = serviceLocator;
998
- this.singletonStorage = new SingletonStorage(manager);
999
- }
1000
- /**
1001
- * Resolves an instance for the given token and arguments.
1002
- * This method is used for singleton and transient services.
1003
- *
1004
- * @param token The injection token
1005
- * @param args Optional arguments
1006
- * @param contextContainer The container to use for creating FactoryContext
1007
- */ async resolveInstance(token, args, contextContainer) {
1008
- return this.resolveWithStorage(token, args, contextContainer, this.singletonStorage);
1009
- }
1010
- /**
1011
- * Resolves a request-scoped instance for a ScopedContainer.
1012
- * The service will be stored in the ScopedContainer's request context.
1013
- *
1014
- * @param token The injection token
1015
- * @param args Optional arguments
1016
- * @param scopedContainer The ScopedContainer that owns the request context
1017
- */ async resolveRequestScopedInstance(token, args, scopedContainer) {
1018
- return this.resolveWithStorage(token, args, scopedContainer, scopedContainer.getHolderStorage(), scopedContainer);
1019
- }
1020
- /**
1021
- * Unified resolution method that works with any IHolderStorage.
1022
- * This eliminates duplication between singleton and request-scoped resolution.
1023
- *
1024
- * IMPORTANT: The check-and-store logic is carefully designed to avoid race conditions.
1025
- * The storage check and holder creation must happen synchronously (no awaits between).
1026
- *
1027
- * @param token The injection token
1028
- * @param args Optional arguments
1029
- * @param contextContainer The container for FactoryContext
1030
- * @param storage The storage strategy to use
1031
- * @param scopedContainer Optional scoped container for request-scoped services
1032
- */ async resolveWithStorage(token, args, contextContainer, storage, scopedContainer) {
1033
- const [err, data] = await this.resolveTokenAndPrepareInstanceName(token, args, contextContainer);
1034
- if (err) return [err];
1035
- const { instanceName, validatedArgs, realToken } = data;
1036
- const getResult = storage.get(instanceName);
1037
- if (getResult !== null) {
1038
- const [error, holder$1] = getResult;
1039
- if (!error && holder$1) {
1040
- const readyResult = await this.waitForInstanceReady(holder$1);
1041
- if (readyResult[0]) return [readyResult[0]];
1042
- return [void 0, readyResult[1].instance];
1043
- }
1044
- if (error) {
1045
- const handledResult = await this.handleStorageError(instanceName, error, holder$1, storage);
1046
- if (handledResult) return handledResult;
1047
- }
1048
- }
1049
- const [createError, holder] = await this.createAndStoreInstance(instanceName, realToken, validatedArgs, contextContainer, storage, scopedContainer);
1050
- if (createError) return [createError];
1051
- return [void 0, holder.instance];
1052
- }
1053
- /**
1054
- * Handles storage error states (destroying, error, etc.).
1055
- * Returns a result if handled, null if should proceed with creation.
1056
- */ async handleStorageError(instanceName, error, holder, storage) {
1057
- switch (error.code) {
1058
- case DIErrorCode.InstanceDestroying:
1059
- this.logger?.log(`[InstanceResolver] Instance ${instanceName} is being destroyed, waiting...`);
1060
- if (holder?.destroyPromise) await holder.destroyPromise;
1061
- const newResult = storage.get(instanceName);
1062
- if (newResult !== null && !newResult[0]) {
1063
- const readyResult = await this.waitForInstanceReady(newResult[1]);
1064
- if (readyResult[0]) return [readyResult[0]];
1065
- return [void 0, readyResult[1].instance];
1066
- }
1067
- return null;
1068
- default:
1069
- if (holder) {
1070
- this.logger?.log(`[InstanceResolver] Removing failed instance ${instanceName} from storage to allow retry`);
1071
- storage.delete(instanceName);
1072
- }
1073
- return null;
1074
- }
1075
- }
1076
- /**
1077
- * Creates a new instance and stores it using the provided storage strategy.
1078
- * This unified method replaces instantiateServiceFromRegistry and createRequestScopedInstance.
1079
- *
1080
- * For transient services, the instance is created but not stored (no caching).
1081
- */ async createAndStoreInstance(instanceName, realToken, args, contextContainer, storage, scopedContainer) {
1082
- this.logger?.log(`[InstanceResolver]#createAndStoreInstance() Creating instance for ${instanceName}`);
1083
- if (!this.registry.has(realToken)) return [DIError.factoryNotFound(realToken.name.toString())];
1084
- const ctx = this.createFactoryContext(contextContainer);
1085
- const record = this.registry.get(realToken);
1086
- const { scope, type } = record;
1087
- if (scope === InjectableScope.Transient) return this.createTransientInstance(instanceName, record, args, ctx);
1088
- const [deferred, holder] = this.manager.createCreatingHolder(instanceName, type, scope, ctx.deps);
1089
- storage.set(instanceName, holder);
1090
- const getHolder = (name) => {
1091
- const storageResult = storage.get(name);
1092
- if (storageResult !== null) {
1093
- const [, storageHolder] = storageResult;
1094
- if (storageHolder) return storageHolder;
1095
- }
1096
- const [, managerHolder] = this.manager.get(name);
1097
- return managerHolder;
1098
- };
1099
- withResolutionContext(holder, getHolder, () => {
1100
- this.instantiator.instantiateService(ctx, record, args).then(async (result) => {
1101
- const [error, instance] = result.length === 2 ? result : [result[0], void 0];
1102
- await this.handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance, scopedContainer);
1103
- }).catch(async (error) => {
1104
- await this.handleInstantiationError(instanceName, holder, deferred, scope, error);
1105
- }).catch(() => {});
1106
- });
1107
- return this.waitForInstanceReady(holder);
1108
- }
1109
- /**
1110
- * Creates a transient instance without storage or locking.
1111
- * Each call creates a new instance.
1112
- */ async createTransientInstance(instanceName, record, args, ctx) {
1113
- this.logger?.log(`[InstanceResolver]#createTransientInstance() Creating transient instance for ${instanceName}`);
1114
- const tempHolder = {
1115
- status: InstanceStatus.Creating,
1116
- name: instanceName,
1117
- instance: null,
1118
- creationPromise: null,
1119
- destroyPromise: null,
1120
- type: record.type,
1121
- scope: InjectableScope.Transient,
1122
- deps: ctx.deps,
1123
- destroyListeners: [],
1124
- createdAt: Date.now(),
1125
- waitingFor: /* @__PURE__ */ new Set()
1126
- };
1127
- const getHolder = (name) => {
1128
- const [, managerHolder] = this.manager.get(name);
1129
- return managerHolder;
1130
- };
1131
- const [error, instance] = await withResolutionContext(tempHolder, getHolder, () => this.instantiator.instantiateService(ctx, record, args));
1132
- if (error) return [error];
1133
- return [void 0, {
1134
- status: InstanceStatus.Created,
1135
- name: instanceName,
1136
- instance,
1137
- creationPromise: null,
1138
- destroyPromise: null,
1139
- type: record.type,
1140
- scope: InjectableScope.Transient,
1141
- deps: ctx.deps,
1142
- destroyListeners: ctx.getDestroyListeners(),
1143
- createdAt: Date.now(),
1144
- waitingFor: /* @__PURE__ */ new Set()
1145
- }];
1146
- }
1147
- /**
1148
- * Gets a synchronous instance (for sync operations).
1149
- */ getSyncInstance(token, args, contextContainer) {
1150
- const [err, { actualToken, validatedArgs }] = this.tokenProcessor.validateAndResolveTokenArgs(token, args);
1151
- if (err) return null;
1152
- const instanceName = this.tokenProcessor.generateInstanceName(actualToken, validatedArgs);
1153
- if ("getRequestInstance" in contextContainer) {
1154
- const requestHolder = contextContainer.getRequestInstance(instanceName);
1155
- if (requestHolder) return requestHolder.instance;
1156
- }
1157
- const [error, holder] = this.manager.get(instanceName);
1158
- if (error) return null;
1159
- return holder.instance;
1160
- }
1161
- /**
1162
- * Internal method to resolve token args and create instance name.
1163
- * Handles factory token resolution and validation.
1164
- */ async resolveTokenAndPrepareInstanceName(token, args, contextContainer) {
1165
- const [err, { actualToken, validatedArgs }] = this.tokenProcessor.validateAndResolveTokenArgs(token, args);
1166
- if (err instanceof DIError && err.code === DIErrorCode.UnknownError) return [err];
1167
- else if (err instanceof DIError && err.code === DIErrorCode.FactoryTokenNotResolved && actualToken instanceof FactoryInjectionToken) {
1168
- this.logger?.log(`[InstanceResolver]#resolveTokenAndPrepareInstanceName() Factory token not resolved, resolving it`);
1169
- await actualToken.resolve(this.createFactoryContext(contextContainer));
1170
- return this.resolveTokenAndPrepareInstanceName(token, void 0, contextContainer);
1171
- }
1172
- return [void 0, {
1173
- instanceName: this.tokenProcessor.generateInstanceName(actualToken, validatedArgs),
1174
- validatedArgs,
1175
- actualToken,
1176
- realToken: actualToken instanceof BoundInjectionToken || actualToken instanceof FactoryInjectionToken ? actualToken.token : actualToken
1177
- }];
1178
- }
1179
- /**
1180
- * Waits for an instance holder to be ready and returns the appropriate result.
1181
- * Uses the shared utility from BaseHolderManager.
1182
- * Passes the current resolution context for circular dependency detection.
1183
- */ waitForInstanceReady(holder) {
1184
- const ctx = getCurrentResolutionContext();
1185
- return BaseHolderManager.waitForHolderReady(holder, ctx?.waiterHolder, ctx?.getHolder);
1186
- }
1187
- /**
1188
- * Handles the result of service instantiation.
1189
- */ async handleInstantiationResult(instanceName, holder, ctx, deferred, scope, error, instance, scopedContainer) {
1190
- holder.destroyListeners = ctx.getDestroyListeners();
1191
- holder.creationPromise = null;
1192
- if (error) await this.handleInstantiationError(instanceName, holder, deferred, scope, error);
1193
- else await this.handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance, scopedContainer);
1194
- }
1195
- /**
1196
- * Handles successful service instantiation.
1197
- */ async handleInstantiationSuccess(instanceName, holder, ctx, deferred, instance, scopedContainer) {
1198
- holder.instance = instance;
1199
- holder.status = InstanceStatus.Created;
1200
- if (ctx.deps.size > 0) ctx.deps.forEach((dependency) => {
1201
- holder.destroyListeners.push(this.serviceLocator.getEventBus().on(dependency, "destroy", () => {
1202
- this.logger?.log(`[InstanceResolver] Dependency ${dependency} destroyed, invalidating ${instanceName}`);
1203
- this.serviceLocator.getInvalidator().invalidate(instanceName);
1204
- }));
1205
- if (scopedContainer) {
1206
- const prefixedDependency = scopedContainer.getPrefixedEventName(dependency);
1207
- holder.destroyListeners.push(this.serviceLocator.getEventBus().on(prefixedDependency, "destroy", () => {
1208
- this.logger?.log(`[InstanceResolver] Request-scoped dependency ${dependency} destroyed, invalidating ${instanceName}`);
1209
- scopedContainer.invalidate(instance);
1210
- }));
1211
- }
1212
- });
1213
- this.logger?.log(`[InstanceResolver] Instance ${instanceName} created successfully`);
1214
- deferred.resolve([void 0, instance]);
1215
- }
1216
- /**
1217
- * Handles service instantiation errors.
1218
- */ async handleInstantiationError(instanceName, holder, deferred, scope, error) {
1219
- this.logger?.error(`[InstanceResolver] Error creating instance for ${instanceName}`, error);
1220
- holder.status = InstanceStatus.Error;
1221
- holder.instance = error;
1222
- holder.creationPromise = null;
1223
- if (scope === InjectableScope.Singleton) {
1224
- this.logger?.log(`[InstanceResolver] Singleton ${instanceName} failed, will be invalidated`);
1225
- this.serviceLocator.getInvalidator().invalidate(instanceName).catch(() => {});
1226
- }
1227
- deferred.reject(error);
1228
- }
1229
- /**
1230
- * Creates a factory context for dependency injection during service instantiation.
1231
- */ createFactoryContext(contextContainer) {
1232
- return this.tokenProcessor.createFactoryContext(contextContainer);
1233
- }
1234
- };
1235
-
1236
- //#endregion
1237
- //#region src/internal/core/instantiator.mts
1238
- /**
1239
- * Creates service instances from registry records.
1240
- *
1241
- * Handles both class-based (@Injectable) and factory-based (@Factory) services,
1242
- * managing the instantiation lifecycle including sync initialization retries
1243
- * and lifecycle hook invocation (onServiceInit, onServiceDestroy).
1244
- */ var Instantiator = class {
1245
- injectors;
1246
- constructor(injectors) {
1247
- this.injectors = injectors;
1248
- }
1249
- /**
1250
- * Instantiates a service based on its registry record.
1251
- * @param ctx The factory context for dependency injection
1252
- * @param record The factory record from the registry
1253
- * @param args Optional arguments for the service
1254
- * @returns Promise resolving to [undefined, instance] or [error]
1255
- */ async instantiateService(ctx, record, args = void 0) {
1256
- try {
1257
- switch (record.type) {
1258
- case InjectableType.Class: return this.instantiateClass(ctx, record, args);
1259
- case InjectableType.Factory: return this.instantiateFactory(ctx, record, args);
1260
- default: throw DIError.unknown(`[Instantiator] Unknown service type: ${record.type}`);
1261
- }
1262
- } catch (error) {
1263
- return [error instanceof DIError ? error : DIError.unknown(String(error))];
1019
+ /**
1020
+ * Instantiates a service based on its registry record.
1021
+ * @param ctx The factory context for dependency injection
1022
+ * @param record The factory record from the registry
1023
+ * @param args Optional arguments for the service
1024
+ * @returns Promise resolving to [undefined, instance] or [error]
1025
+ */ async instantiateService(ctx, record, args = void 0) {
1026
+ try {
1027
+ switch (record.type) {
1028
+ case InjectableType.Class: return this.instantiateClass(ctx, record, args);
1029
+ case InjectableType.Factory: return this.instantiateFactory(ctx, record, args);
1030
+ default: throw DIError.unknown(`[ServiceInitializer] Unknown service type: ${record.type}`);
1031
+ }
1032
+ } catch (error) {
1033
+ return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
1264
1034
  }
1265
1035
  }
1266
1036
  /**
@@ -1279,18 +1049,18 @@ var SingletonStorage = class {
1279
1049
  });
1280
1050
  let [instance, promises, injectState] = tryLoad();
1281
1051
  if (promises.length > 0) {
1282
- if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.unknown(`[Instantiator] Service ${record.target.name} cannot be instantiated.`);
1052
+ if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
1283
1053
  const newRes = tryLoad(injectState);
1284
1054
  instance = newRes[0];
1285
1055
  promises = newRes[1];
1286
1056
  }
1287
1057
  if (promises.length > 0) {
1288
- console.error(`[Instantiator] ${record.target.name} has problem with it's definition.
1058
+ console.error(`[ServiceInitializer] ${record.target.name} has problem with it's definition.
1289
1059
 
1290
- One or more of the dependencies are registered as a InjectableScope.Instance and are used with inject.
1060
+ One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.
1291
1061
 
1292
- Please use inject asyncInject of inject to load those dependencies.`);
1293
- throw DIError.unknown(`[Instantiator] Service ${record.target.name} cannot be instantiated.`);
1062
+ Please use asyncInject instead of inject to load those dependencies.`);
1063
+ throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
1294
1064
  }
1295
1065
  if ("onServiceInit" in instance) await instance.onServiceInit();
1296
1066
  if ("onServiceDestroy" in instance) ctx.addDestroyListener(async () => {
@@ -1298,7 +1068,7 @@ var SingletonStorage = class {
1298
1068
  });
1299
1069
  return [void 0, instance];
1300
1070
  } catch (error) {
1301
- return [error instanceof DIError ? error : DIError.unknown(String(error))];
1071
+ return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
1302
1072
  }
1303
1073
  }
1304
1074
  /**
@@ -1317,108 +1087,96 @@ var SingletonStorage = class {
1317
1087
  });
1318
1088
  let [builder, promises, injectState] = tryLoad();
1319
1089
  if (promises.length > 0) {
1320
- if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.unknown(`[Instantiator] Service ${record.target.name} cannot be instantiated.`);
1090
+ if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
1321
1091
  const newRes = tryLoad(injectState);
1322
1092
  builder = newRes[0];
1323
1093
  promises = newRes[1];
1324
1094
  }
1325
1095
  if (promises.length > 0) {
1326
- console.error(`[Instantiator] ${record.target.name} has problem with it's definition.
1096
+ console.error(`[ServiceInitializer] ${record.target.name} has problem with it's definition.
1327
1097
 
1328
- One or more of the dependencies are registered as a InjectableScope.Instance and are used with inject.
1098
+ One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.
1329
1099
 
1330
1100
  Please use asyncInject instead of inject to load those dependencies.`);
1331
- throw DIError.unknown(`[Instantiator] Service ${record.target.name} cannot be instantiated.`);
1101
+ throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
1332
1102
  }
1333
- if (typeof builder.create !== "function") throw DIError.unknown(`[Instantiator] Factory ${record.target.name} does not implement the create method.`);
1103
+ if (typeof builder.create !== "function") throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Factory does not implement the create method"));
1334
1104
  return [void 0, await builder.create(ctx, args)];
1335
1105
  } catch (error) {
1336
- return [error instanceof DIError ? error : DIError.unknown(String(error))];
1106
+ return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
1337
1107
  }
1338
1108
  }
1339
1109
  };
1340
1110
 
1341
1111
  //#endregion
1342
- //#region src/internal/core/invalidator.mts
1112
+ //#region src/internal/core/service-invalidator.mts
1343
1113
  /**
1344
- * Manages graceful service cleanup with dependency-aware invalidation.
1114
+ * Manages graceful service cleanup with event-based invalidation.
1345
1115
  *
1346
- * Ensures services are destroyed in the correct order based on their dependencies.
1347
- * Works with any IHolderStorage implementation, enabling unified invalidation
1348
- * for both singleton and request-scoped services.
1349
- */ var Invalidator = class {
1116
+ * Uses event subscriptions instead of manual dependent finding.
1117
+ * When a service is created, it subscribes to destroy events of its dependencies.
1118
+ * When a dependency is destroyed, the event automatically invalidates dependents.
1119
+ */ var ServiceInvalidator = class {
1350
1120
  eventBus;
1351
1121
  logger;
1352
- storage;
1353
- constructor(manager, eventBus, logger = null) {
1122
+ constructor(eventBus, logger = null) {
1354
1123
  this.eventBus = eventBus;
1355
1124
  this.logger = logger;
1356
- this.storage = new SingletonStorage(manager);
1357
- }
1358
- /**
1359
- * Invalidates a service and all its dependencies.
1360
- * Works with the configured storage (singleton by default).
1361
- */ invalidate(service, round = 1) {
1362
- return this.invalidateWithStorage(service, this.storage, round);
1363
1125
  }
1364
1126
  /**
1365
1127
  * Invalidates a service using a specific storage.
1366
- * This allows request-scoped invalidation using a RequestStorage.
1128
+ * Event-based invalidation means dependents are automatically invalidated
1129
+ * via destroy event subscriptions - no need to manually find dependents.
1367
1130
  *
1368
1131
  * @param service The instance name to invalidate
1369
1132
  * @param storage The storage to use for this invalidation
1370
- * @param round Current invalidation round (for recursion limiting)
1371
1133
  * @param options Additional options for invalidation behavior
1372
- */ async invalidateWithStorage(service, storage, round = 1, options = {}) {
1373
- const { cascade = true, _invalidating = /* @__PURE__ */ new Set() } = options;
1374
- if (_invalidating.has(service)) {
1375
- this.logger?.log(`[Invalidator] Skipping ${service} - already being invalidated in this chain`);
1376
- return;
1377
- }
1378
- this.logger?.log(`[Invalidator] Starting invalidation process for ${service}`);
1134
+ */ async invalidateWithStorage(service, storage, options = {}) {
1135
+ const { emitEvents = true, onInvalidated } = options;
1136
+ this.logger?.log(`[ServiceInvalidator] Starting invalidation process for ${service}`);
1379
1137
  const result = storage.get(service);
1380
1138
  if (result === null) return;
1381
- _invalidating.add(service);
1382
- const optionsWithTracking = {
1383
- ...options,
1384
- _invalidating
1385
- };
1386
- if (cascade) {
1387
- const dependents = storage.findDependents(service);
1388
- for (const dependentName of dependents) await this.invalidateWithStorage(dependentName, storage, round, optionsWithTracking);
1389
- }
1390
1139
  const [, holder] = result;
1391
- if (holder) await this.invalidateHolderWithStorage(service, holder, storage, round, optionsWithTracking);
1140
+ if (holder) await this.invalidateHolderWithStorage(service, holder, storage, emitEvents, onInvalidated);
1392
1141
  }
1393
1142
  /**
1394
- * Gracefully clears all services using invalidation logic.
1395
- * This method respects service dependencies and ensures proper cleanup order.
1396
- * Services that depend on others will be invalidated first, then their dependencies.
1397
- */ async clearAll(options = {}) {
1398
- return this.clearAllWithStorage(this.storage, options);
1143
+ * Sets up destroy event subscriptions for a service's dependencies.
1144
+ * Called when a service is successfully instantiated.
1145
+ *
1146
+ * @param serviceName The name of the service
1147
+ * @param dependencies The set of dependency names
1148
+ * @param storage The storage to use for invalidation
1149
+ * @param holder The holder for the service (to add unsubscribe to destroy listeners)
1150
+ */ setupDependencySubscriptions(serviceName, dependencies, storage, holder) {
1151
+ if (!this.eventBus) return;
1152
+ for (const dependencyName of dependencies) {
1153
+ const unsubscribe = this.eventBus.on(dependencyName, "destroy", () => {
1154
+ this.logger?.log(`[ServiceInvalidator] Dependency ${dependencyName} destroyed, invalidating ${serviceName}`);
1155
+ this.invalidateWithStorage(serviceName, storage).catch((error) => {
1156
+ this.logger?.error(`[ServiceInvalidator] Error invalidating ${serviceName} after dependency ${dependencyName} destroyed:`, error);
1157
+ });
1158
+ });
1159
+ holder.destroyListeners.push(unsubscribe);
1160
+ }
1399
1161
  }
1400
1162
  /**
1401
1163
  * Gracefully clears all services in a specific storage.
1402
1164
  * This allows clearing request-scoped services using a RequestStorage.
1403
1165
  */ async clearAllWithStorage(storage, options = {}) {
1404
- const { maxRounds = 10, waitForSettlement = true } = options;
1405
- this.logger?.log("[Invalidator] Starting graceful clearing of all services");
1166
+ const { waitForSettlement = true } = options;
1167
+ this.logger?.log("[ServiceInvalidator] Starting graceful clearing of all services");
1406
1168
  if (waitForSettlement) {
1407
- this.logger?.log("[Invalidator] Waiting for all services to settle...");
1169
+ this.logger?.log("[ServiceInvalidator] Waiting for all services to settle...");
1408
1170
  await this.readyWithStorage(storage);
1409
1171
  }
1410
1172
  const allServiceNames = storage.getAllNames();
1411
- if (allServiceNames.length === 0) this.logger?.log("[Invalidator] No services to clear");
1173
+ if (allServiceNames.length === 0) this.logger?.log("[ServiceInvalidator] No services to clear");
1412
1174
  else {
1413
- this.logger?.log(`[Invalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(", ")}`);
1414
- await this.clearServicesWithDependencyAwarenessForStorage(allServiceNames, maxRounds, storage);
1175
+ this.logger?.log(`[ServiceInvalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(", ")}`);
1176
+ const clearPromises = allServiceNames.map((serviceName) => this.invalidateWithStorage(serviceName, storage));
1177
+ await Promise.all(clearPromises);
1415
1178
  }
1416
- this.logger?.log("[Invalidator] Graceful clearing completed");
1417
- }
1418
- /**
1419
- * Waits for all services to settle (either created, destroyed, or error state).
1420
- */ async ready() {
1421
- return this.readyWithStorage(this.storage);
1179
+ this.logger?.log("[ServiceInvalidator] Graceful clearing completed");
1422
1180
  }
1423
1181
  /**
1424
1182
  * Waits for all services in a specific storage to settle.
@@ -1429,29 +1187,22 @@ var SingletonStorage = class {
1429
1187
  }
1430
1188
  /**
1431
1189
  * Invalidates a single holder using a specific storage.
1432
- */ async invalidateHolderWithStorage(key, holder, storage, round, options = {}) {
1433
- const { emitEvents = true, onInvalidated } = options;
1434
- await this.invalidateHolderByStatus(holder, round, {
1190
+ */ async invalidateHolderWithStorage(key, holder, storage, emitEvents, onInvalidated) {
1191
+ await this.invalidateHolderByStatus(holder, {
1435
1192
  context: key,
1436
- onCreationError: () => this.logger?.error(`[Invalidator] ${key} creation triggered too many invalidation rounds`),
1437
- onRecursiveInvalidate: () => this.invalidateWithStorage(key, storage, round + 1, options),
1438
1193
  onDestroy: () => this.destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated)
1439
1194
  });
1440
1195
  }
1441
1196
  /**
1442
1197
  * Common invalidation logic for holders based on their status.
1443
- */ async invalidateHolderByStatus(holder, round, options) {
1198
+ */ async invalidateHolderByStatus(holder, options) {
1444
1199
  switch (holder.status) {
1445
1200
  case InstanceStatus.Destroying:
1446
1201
  await holder.destroyPromise;
1447
1202
  break;
1448
1203
  case InstanceStatus.Creating:
1449
1204
  await holder.creationPromise;
1450
- if (round > 3) {
1451
- options.onCreationError();
1452
- return;
1453
- }
1454
- await options.onRecursiveInvalidate();
1205
+ await options.onDestroy();
1455
1206
  break;
1456
1207
  default:
1457
1208
  await options.onDestroy();
@@ -1462,7 +1213,7 @@ var SingletonStorage = class {
1462
1213
  * Destroys a holder using a specific storage.
1463
1214
  */ async destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated) {
1464
1215
  holder.status = InstanceStatus.Destroying;
1465
- this.logger?.log(`[Invalidator] Invalidating ${key} and notifying listeners`);
1216
+ this.logger?.log(`[ServiceInvalidator] Invalidating ${key} and notifying listeners`);
1466
1217
  holder.destroyPromise = Promise.all(holder.destroyListeners.map((listener) => listener())).then(async () => {
1467
1218
  holder.destroyListeners = [];
1468
1219
  holder.deps.clear();
@@ -1470,89 +1221,369 @@ var SingletonStorage = class {
1470
1221
  if (emitEvents && this.eventBus) await this.emitInstanceEvent(key, "destroy");
1471
1222
  if (onInvalidated) await onInvalidated(key);
1472
1223
  });
1473
- await holder.destroyPromise;
1224
+ await holder.destroyPromise;
1225
+ }
1226
+ /**
1227
+ * Waits for a holder to settle (either created, destroyed, or error state).
1228
+ */ async waitForHolderToSettle(holder) {
1229
+ switch (holder.status) {
1230
+ case InstanceStatus.Creating:
1231
+ await holder.creationPromise;
1232
+ break;
1233
+ case InstanceStatus.Destroying:
1234
+ await holder.destroyPromise;
1235
+ break;
1236
+ case InstanceStatus.Created:
1237
+ case InstanceStatus.Error: break;
1238
+ }
1239
+ }
1240
+ /**
1241
+ * Emits events to listeners for instance lifecycle events.
1242
+ */ emitInstanceEvent(name, event = "create") {
1243
+ if (!this.eventBus) return Promise.resolve();
1244
+ this.logger?.log(`[ServiceInvalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`);
1245
+ return this.eventBus.emit(name, event);
1246
+ }
1247
+ };
1248
+
1249
+ //#endregion
1250
+ //#region src/utils/get-injectors.mts
1251
+ function getInjectors() {
1252
+ let currentFactoryContext = null;
1253
+ function provideFactoryContext$1(context) {
1254
+ const original = currentFactoryContext;
1255
+ currentFactoryContext = context;
1256
+ return original;
1257
+ }
1258
+ function getFactoryContext() {
1259
+ if (!currentFactoryContext) throw new Error("[Injector] Trying to access injection context outside of a injectable context");
1260
+ return currentFactoryContext;
1261
+ }
1262
+ let promiseCollector = null;
1263
+ let injectState = null;
1264
+ function getRequest(token, args, skipCycleTracking = false) {
1265
+ if (!injectState) throw new Error("[Injector] Trying to make a request outside of a injectable context");
1266
+ if (injectState.isFrozen) {
1267
+ const idx = injectState.currentIndex++;
1268
+ const request$1 = injectState.requests[idx];
1269
+ if (request$1.token !== token) throw new Error(`[Injector] Wrong token order. Expected ${request$1.token.toString()} but got ${token.toString()}`);
1270
+ return request$1;
1271
+ }
1272
+ let result = null;
1273
+ let error = null;
1274
+ const doInject = () => getFactoryContext().inject(token, args).then((r) => {
1275
+ result = r;
1276
+ return r;
1277
+ }).catch((e) => {
1278
+ error = e;
1279
+ });
1280
+ const request = {
1281
+ token,
1282
+ promise: skipCycleTracking ? withoutResolutionContext(doInject) : doInject(),
1283
+ get result() {
1284
+ return result;
1285
+ },
1286
+ get error() {
1287
+ return error;
1288
+ }
1289
+ };
1290
+ injectState.requests.push(request);
1291
+ injectState.currentIndex++;
1292
+ return request;
1293
+ }
1294
+ function asyncInject$1(token, args) {
1295
+ if (!injectState) throw new Error("[Injector] Trying to access inject outside of a injectable context");
1296
+ const request = getRequest(token[InjectableTokenMeta] ?? token, args, true);
1297
+ return request.promise.then((result) => {
1298
+ if (request.error) throw request.error;
1299
+ return result;
1300
+ });
1301
+ }
1302
+ function wrapSyncInit$1(cb) {
1303
+ return (previousState) => {
1304
+ const promises = [];
1305
+ const originalPromiseCollector = promiseCollector;
1306
+ const originalInjectState = injectState;
1307
+ injectState = previousState ? {
1308
+ ...previousState,
1309
+ currentIndex: 0
1310
+ } : {
1311
+ currentIndex: 0,
1312
+ isFrozen: false,
1313
+ requests: []
1314
+ };
1315
+ promiseCollector = (promise) => {
1316
+ promises.push(promise);
1317
+ };
1318
+ const result = cb();
1319
+ promiseCollector = originalPromiseCollector;
1320
+ const newInjectState = {
1321
+ ...injectState,
1322
+ isFrozen: true
1323
+ };
1324
+ injectState = originalInjectState;
1325
+ return [
1326
+ result,
1327
+ promises,
1328
+ newInjectState
1329
+ ];
1330
+ };
1331
+ }
1332
+ function inject$1(token, args) {
1333
+ const realToken = token[InjectableTokenMeta] ?? token;
1334
+ if (!injectState) throw new Error("[Injector] Trying to access inject outside of a injectable context");
1335
+ const ctx = getFactoryContext();
1336
+ const instance = ctx.container.tryGetSync(realToken, args);
1337
+ if (!instance) {
1338
+ const request = getRequest(realToken, args);
1339
+ if (request.error) throw request.error;
1340
+ else if (request.result) return request.result;
1341
+ if (promiseCollector) promiseCollector(request.promise);
1342
+ return new Proxy({}, { get() {
1343
+ throw new Error(`[Injector] Trying to access ${realToken.toString()} before it's initialized, please move the code to a onServiceInit method`);
1344
+ } });
1345
+ }
1346
+ ctx.inject(realToken, args).catch(() => {});
1347
+ return instance;
1348
+ }
1349
+ function optional$1(token, args) {
1350
+ try {
1351
+ return inject$1(token, args);
1352
+ } catch {
1353
+ return null;
1354
+ }
1355
+ }
1356
+ return {
1357
+ asyncInject: asyncInject$1,
1358
+ inject: inject$1,
1359
+ optional: optional$1,
1360
+ wrapSyncInit: wrapSyncInit$1,
1361
+ provideFactoryContext: provideFactoryContext$1
1362
+ };
1363
+ }
1364
+
1365
+ //#endregion
1366
+ //#region src/utils/default-injectors.mts
1367
+ const defaultInjectors = /* @__PURE__ */ getInjectors();
1368
+ const inject = defaultInjectors.inject;
1369
+ const optional = defaultInjectors.optional;
1370
+ const asyncInject = defaultInjectors.asyncInject;
1371
+ const wrapSyncInit = defaultInjectors.wrapSyncInit;
1372
+ const provideFactoryContext = defaultInjectors.provideFactoryContext;
1373
+
1374
+ //#endregion
1375
+ //#region src/utils/get-injectable-token.mts
1376
+ function getInjectableToken(target) {
1377
+ const token = target[InjectableTokenMeta];
1378
+ if (!token) throw DIError.classNotInjectable(target.name);
1379
+ return token;
1380
+ }
1381
+
1382
+ //#endregion
1383
+ //#region src/internal/core/token-resolver.mts
1384
+ /**
1385
+ * Handles token validation and resolution.
1386
+ *
1387
+ * Focuses on token validation, normalization, and argument validation.
1388
+ * Name generation is handled by NameResolver.
1389
+ */ var TokenResolver = class {
1390
+ logger;
1391
+ constructor(logger = null) {
1392
+ this.logger = logger;
1393
+ }
1394
+ /**
1395
+ * Normalizes a token to an InjectionToken.
1396
+ * Handles class constructors by getting their injectable token.
1397
+ *
1398
+ * @param token A class constructor, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
1399
+ * @returns The normalized InjectionTokenType
1400
+ */ normalizeToken(token) {
1401
+ if (typeof token === "function") return getInjectableToken(token);
1402
+ return token;
1403
+ }
1404
+ /**
1405
+ * Gets the underlying "real" token from wrapped tokens.
1406
+ * For BoundInjectionToken and FactoryInjectionToken, returns the wrapped token.
1407
+ * For other tokens, returns the token itself.
1408
+ *
1409
+ * @param token The token to unwrap
1410
+ * @returns The underlying InjectionToken
1411
+ */ getRealToken(token) {
1412
+ if (token instanceof BoundInjectionToken || token instanceof FactoryInjectionToken) return token.token;
1413
+ return token;
1414
+ }
1415
+ /**
1416
+ * Convenience method that normalizes a token and then gets the real token.
1417
+ * Useful for checking registry entries where you need the actual registered token.
1418
+ *
1419
+ * @param token Any injectable type
1420
+ * @returns The underlying InjectionToken
1421
+ */ getRegistryToken(token) {
1422
+ return this.getRealToken(this.normalizeToken(token));
1423
+ }
1424
+ /**
1425
+ * Validates and resolves token arguments, handling factory token resolution and validation.
1426
+ *
1427
+ * @param token The token to validate
1428
+ * @param args Optional arguments
1429
+ * @returns [error, { actualToken, validatedArgs }]
1430
+ */ validateAndResolveTokenArgs(token, args) {
1431
+ let actualToken = token;
1432
+ if (typeof token === "function") actualToken = getInjectableToken(token);
1433
+ let realArgs = args;
1434
+ if (actualToken instanceof BoundInjectionToken) realArgs = actualToken.value;
1435
+ else if (actualToken instanceof FactoryInjectionToken) if (actualToken.resolved) realArgs = actualToken.value;
1436
+ else return [DIError.factoryTokenNotResolved(token.name), { actualToken }];
1437
+ if (!actualToken.schema) return [void 0, {
1438
+ actualToken,
1439
+ validatedArgs: realArgs
1440
+ }];
1441
+ const validatedArgs = actualToken.schema?.safeParse(realArgs);
1442
+ if (validatedArgs && !validatedArgs.success) {
1443
+ this.logger?.error(`[TokenResolver]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`, validatedArgs.error);
1444
+ return [DIError.tokenValidationError(`Validation failed for ${actualToken.name.toString()}`, actualToken.schema, realArgs), { actualToken }];
1445
+ }
1446
+ return [void 0, {
1447
+ actualToken,
1448
+ validatedArgs: validatedArgs?.data
1449
+ }];
1450
+ }
1451
+ };
1452
+
1453
+ //#endregion
1454
+ //#region src/internal/holder/unified-storage.mts
1455
+ /**
1456
+ * Unified storage implementation that works the same way regardless of scope.
1457
+ * Replaces RequestContext, HolderManager, SingletonStorage, RequestStorage.
1458
+ *
1459
+ * Scope is just metadata - storage operations are identical for all scopes.
1460
+ * Different storage instances are just isolated storage spaces.
1461
+ */ var UnifiedStorage = class {
1462
+ scope;
1463
+ holders = /* @__PURE__ */ new Map();
1464
+ /**
1465
+ * Reverse dependency index: maps a dependency name to the set of holder names that depend on it.
1466
+ * This allows O(1) lookup of dependents instead of O(n) iteration.
1467
+ */ dependents = /* @__PURE__ */ new Map();
1468
+ constructor(scope = InjectableScope.Singleton) {
1469
+ this.scope = scope;
1470
+ }
1471
+ get(instanceName) {
1472
+ const holder = this.holders.get(instanceName);
1473
+ if (!holder) return null;
1474
+ switch (holder.status) {
1475
+ case InstanceStatus.Destroying: return [DIError.instanceDestroying(instanceName), holder];
1476
+ case InstanceStatus.Error: return [holder.instance, holder];
1477
+ case InstanceStatus.Creating:
1478
+ case InstanceStatus.Created: return [void 0, holder];
1479
+ default: return null;
1480
+ }
1481
+ }
1482
+ set(instanceName, holder) {
1483
+ this.holders.set(instanceName, holder);
1484
+ if (holder.deps.size > 0) this.registerDependencies(instanceName, holder.deps);
1485
+ }
1486
+ delete(instanceName) {
1487
+ const holder = this.holders.get(instanceName);
1488
+ if (holder) this.removeFromDependentsIndex(instanceName, holder.deps);
1489
+ return this.holders.delete(instanceName);
1490
+ }
1491
+ createHolder(instanceName, type, deps) {
1492
+ const deferred = Promise.withResolvers();
1493
+ return [deferred, {
1494
+ status: InstanceStatus.Creating,
1495
+ name: instanceName,
1496
+ instance: null,
1497
+ creationPromise: deferred.promise,
1498
+ destroyPromise: null,
1499
+ type,
1500
+ scope: this.scope,
1501
+ deps,
1502
+ destroyListeners: [],
1503
+ createdAt: Date.now(),
1504
+ waitingFor: /* @__PURE__ */ new Set()
1505
+ }];
1506
+ }
1507
+ storeInstance(instanceName, instance) {
1508
+ if (this.holders.get(instanceName)) throw DIError.storageError("Instance already stored", "storeInstance", instanceName);
1509
+ this.set(instanceName, {
1510
+ status: InstanceStatus.Created,
1511
+ name: instanceName,
1512
+ instance,
1513
+ creationPromise: null,
1514
+ destroyPromise: null,
1515
+ type: InjectableType.Class,
1516
+ scope: this.scope,
1517
+ deps: /* @__PURE__ */ new Set(),
1518
+ destroyListeners: typeof instance === "object" && instance !== null && "onServiceDestroy" in instance ? [instance.onServiceDestroy] : [],
1519
+ createdAt: Date.now(),
1520
+ waitingFor: /* @__PURE__ */ new Set()
1521
+ });
1522
+ }
1523
+ handles(scope) {
1524
+ return scope === this.scope;
1525
+ }
1526
+ getAllNames() {
1527
+ return Array.from(this.holders.keys());
1528
+ }
1529
+ forEach(callback) {
1530
+ for (const [name, holder] of this.holders) callback(name, holder);
1531
+ }
1532
+ findByInstance(instance) {
1533
+ for (const holder of this.holders.values()) if (holder.instance === instance) return holder;
1534
+ return null;
1535
+ }
1536
+ findDependents(instanceName) {
1537
+ const dependents = this.dependents.get(instanceName);
1538
+ return dependents ? Array.from(dependents) : [];
1474
1539
  }
1475
1540
  /**
1476
- * Waits for a holder to settle (either created, destroyed, or error state).
1477
- */ async waitForHolderToSettle(holder) {
1478
- switch (holder.status) {
1479
- case InstanceStatus.Creating:
1480
- await holder.creationPromise;
1481
- break;
1482
- case InstanceStatus.Destroying:
1483
- await holder.destroyPromise;
1484
- break;
1485
- case InstanceStatus.Created:
1486
- case InstanceStatus.Error: break;
1541
+ * Updates dependency references when instance names change.
1542
+ * Used during scope upgrades when instance names are regenerated with requestId.
1543
+ *
1544
+ * @param oldName The old instance name
1545
+ * @param newName The new instance name
1546
+ */ updateDependencyReference(oldName, newName) {
1547
+ for (const holder of this.holders.values()) if (holder.deps.has(oldName)) {
1548
+ holder.deps.delete(oldName);
1549
+ holder.deps.add(newName);
1550
+ }
1551
+ const oldDependents = this.dependents.get(oldName);
1552
+ if (oldDependents) {
1553
+ const newDependents = this.dependents.get(newName) || /* @__PURE__ */ new Set();
1554
+ for (const dependent of oldDependents) newDependents.add(dependent);
1555
+ this.dependents.set(newName, newDependents);
1556
+ this.dependents.delete(oldName);
1557
+ }
1558
+ for (const [depName, dependents] of this.dependents.entries()) if (depName === oldName) {
1559
+ const newDependents = this.dependents.get(newName) || /* @__PURE__ */ new Set();
1560
+ for (const dependent of dependents) newDependents.add(dependent);
1561
+ this.dependents.set(newName, newDependents);
1562
+ this.dependents.delete(oldName);
1487
1563
  }
1488
1564
  }
1489
1565
  /**
1490
- * Clears services with dependency awareness for a specific storage.
1491
- */ async clearServicesWithDependencyAwarenessForStorage(serviceNames, maxRounds, storage) {
1492
- const clearedServices = /* @__PURE__ */ new Set();
1493
- let round = 1;
1494
- while (clearedServices.size < serviceNames.length && round <= maxRounds) {
1495
- this.logger?.log(`[Invalidator] Clearing round ${round}/${maxRounds}, ${clearedServices.size}/${serviceNames.length} services cleared`);
1496
- const servicesToClearThisRound = this.findServicesReadyForClearingInStorage(serviceNames, clearedServices, storage);
1497
- if (servicesToClearThisRound.length === 0) {
1498
- const remainingServices = serviceNames.filter((name) => !clearedServices.has(name));
1499
- if (remainingServices.length > 0) {
1500
- this.logger?.warn(`[Invalidator] No services ready for clearing, forcing cleanup of remaining: ${remainingServices.join(", ")}`);
1501
- await this.forceClearServicesInStorage(remainingServices, storage);
1502
- remainingServices.forEach((name) => clearedServices.add(name));
1503
- }
1504
- break;
1566
+ * Registers a holder's dependencies in the reverse index.
1567
+ */ registerDependencies(holderName, deps) {
1568
+ for (const dep of deps) {
1569
+ let dependents = this.dependents.get(dep);
1570
+ if (!dependents) {
1571
+ dependents = /* @__PURE__ */ new Set();
1572
+ this.dependents.set(dep, dependents);
1505
1573
  }
1506
- const clearPromises = servicesToClearThisRound.map(async (serviceName) => {
1507
- try {
1508
- await this.invalidateWithStorage(serviceName, storage, round);
1509
- clearedServices.add(serviceName);
1510
- this.logger?.log(`[Invalidator] Successfully cleared service: ${serviceName}`);
1511
- } catch (error) {
1512
- this.logger?.error(`[Invalidator] Error clearing service ${serviceName}:`, error);
1513
- clearedServices.add(serviceName);
1514
- }
1515
- });
1516
- await Promise.all(clearPromises);
1517
- round++;
1574
+ dependents.add(holderName);
1518
1575
  }
1519
- if (clearedServices.size < serviceNames.length) this.logger?.warn(`[Invalidator] Clearing completed after ${maxRounds} rounds, but ${serviceNames.length - clearedServices.size} services may not have been properly cleared`);
1520
- }
1521
- /**
1522
- * Finds services that are ready to be cleared in the current round.
1523
- * A service is ready if all its dependencies have already been cleared.
1524
- */ findServicesReadyForClearingInStorage(allServiceNames, clearedServices, storage) {
1525
- return allServiceNames.filter((serviceName) => {
1526
- if (clearedServices.has(serviceName)) return false;
1527
- const result = storage.get(serviceName);
1528
- if (result === null || result[0]) return true;
1529
- const [, holder] = result;
1530
- return !Array.from(holder.deps).some((dep) => !clearedServices.has(dep));
1531
- });
1532
1576
  }
1533
1577
  /**
1534
- * Force clears services that couldn't be cleared through normal dependency resolution.
1535
- * This handles edge cases like circular dependencies.
1536
- */ async forceClearServicesInStorage(serviceNames, storage) {
1537
- const promises = serviceNames.map(async (serviceName) => {
1538
- try {
1539
- const result = storage.get(serviceName);
1540
- if (result !== null && !result[0]) {
1541
- const [, holder] = result;
1542
- await this.destroyHolderWithStorage(serviceName, holder, storage, true);
1543
- }
1544
- } catch (error) {
1545
- this.logger?.error(`[Invalidator] Error force clearing service ${serviceName}:`, error);
1578
+ * Removes a holder from the reverse dependency index.
1579
+ */ removeFromDependentsIndex(holderName, deps) {
1580
+ for (const dep of deps) {
1581
+ const dependents = this.dependents.get(dep);
1582
+ if (dependents) {
1583
+ dependents.delete(holderName);
1584
+ if (dependents.size === 0) this.dependents.delete(dep);
1546
1585
  }
1547
- });
1548
- await Promise.all(promises);
1549
- }
1550
- /**
1551
- * Emits events to listeners for instance lifecycle events.
1552
- */ emitInstanceEvent(name, event = "create") {
1553
- if (!this.eventBus) return Promise.resolve();
1554
- this.logger?.log(`[Invalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`);
1555
- return this.eventBus.emit(name, event);
1586
+ }
1556
1587
  }
1557
1588
  };
1558
1589
 
@@ -1598,270 +1629,220 @@ var SingletonStorage = class {
1598
1629
  };
1599
1630
 
1600
1631
  //#endregion
1601
- //#region src/internal/holder/holder-manager.mts
1632
+ //#region src/internal/stub-factory-class.mts
1602
1633
  /**
1603
- * Manages the storage and retrieval of singleton instance holders.
1634
+ * Stub factory class used when registering InjectionTokens without a real class implementation.
1635
+ * This is used in ScopedContainer.addInstance() to allow registering tokens that don't have
1636
+ * a corresponding class, while preventing accidental instantiation.
1604
1637
  *
1605
- * Provides CRUD operations and filtering for the holder map.
1606
- * Handles holder state validation (destroying, error states) on retrieval.
1607
- */ var HolderManager = class extends BaseHolderManager {
1608
- constructor(logger = null) {
1609
- super(logger);
1610
- }
1611
- get(name) {
1612
- const holder = this._holders.get(name);
1613
- if (holder) {
1614
- if (holder.status === InstanceStatus.Destroying) {
1615
- this.logger?.log(`[HolderManager]#get() Instance ${holder.name} is destroying`);
1616
- return [DIError.instanceDestroying(holder.name), holder];
1617
- } else if (holder.status === InstanceStatus.Error) {
1618
- this.logger?.log(`[HolderManager]#get() Instance ${holder.name} is in error state`);
1619
- return [holder.instance, holder];
1620
- }
1621
- return [void 0, holder];
1622
- } else {
1623
- this.logger?.log(`[HolderManager]#get() Instance ${name} not found`);
1624
- return [DIError.instanceNotFound(name)];
1625
- }
1626
- }
1627
- set(name, holder) {
1628
- this._holders.set(name, holder);
1629
- }
1630
- has(name) {
1631
- const [error, holder] = this.get(name);
1632
- if (!error) return [void 0, true];
1633
- if (error.code === DIErrorCode.InstanceDestroying) return [error];
1634
- return [void 0, !!holder];
1635
- }
1636
- /**
1637
- * Creates a new holder with Created status and stores it.
1638
- * This is useful for creating holders that already have their instance ready.
1639
- * @param name The name of the instance
1640
- * @param instance The actual instance to store
1641
- * @param type The injectable type
1642
- * @param scope The injectable scope
1643
- * @param deps Optional set of dependencies
1644
- * @returns The created holder
1645
- */ storeCreatedHolder(name, instance, type, scope, deps = /* @__PURE__ */ new Set()) {
1646
- const holder = this.createCreatedHolder(name, instance, type, scope, deps);
1647
- this._holders.set(name, holder);
1648
- return holder;
1638
+ * @internal
1639
+ */ var StubFactoryClass = class {
1640
+ constructor() {
1641
+ throw DIError.factoryNotFound("Trying to get instance of factory without real implementation");
1649
1642
  }
1650
1643
  };
1651
1644
 
1652
1645
  //#endregion
1653
- //#region src/internal/core/token-processor.mts
1646
+ //#region src/container/abstract-container.mts
1654
1647
  /**
1655
- * Handles token validation, normalization, and instance name generation.
1648
+ * Abstract base class for dependency injection containers.
1656
1649
  *
1657
- * Provides utilities for resolving tokens to their underlying InjectionToken,
1658
- * validating arguments against schemas, and generating unique instance identifiers.
1659
- */ var TokenProcessor = class {
1660
- logger;
1661
- constructor(logger = null) {
1662
- this.logger = logger;
1663
- }
1650
+ * Provides shared implementation for common container operations.
1651
+ * Both Container and ScopedContainer extend this class.
1652
+ */ var AbstractContainer = class {
1664
1653
  /**
1665
- * Normalizes a token to an InjectionToken.
1666
- * Handles class constructors by getting their injectable token.
1654
+ * Calculates the instance name for a given token and optional arguments.
1667
1655
  *
1668
- * @param token A class constructor, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
1669
- * @returns The normalized InjectionTokenType
1670
- */ normalizeToken(token) {
1671
- if (typeof token === "function") return getInjectableToken(token);
1672
- return token;
1656
+ * @internal
1657
+ * @param token The class type, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
1658
+ * @param args Optional arguments (ignored for BoundInjectionToken which uses its bound value)
1659
+ * @returns The calculated instance name string, or null if the token is a FactoryInjectionToken that is not yet resolved
1660
+ */ calculateInstanceName(token, args) {
1661
+ const [err, { actualToken, validatedArgs }] = this.getTokenResolver().validateAndResolveTokenArgs(token, args);
1662
+ if (err) {
1663
+ if (err instanceof DIError && err.code === DIErrorCode.FactoryTokenNotResolved) return null;
1664
+ if (err instanceof DIError && err.code === DIErrorCode.TokenValidationError) return null;
1665
+ }
1666
+ const realToken = this.getTokenResolver().getRealToken(actualToken);
1667
+ const registry = this.getRegistry();
1668
+ const scope = registry.has(realToken) ? registry.get(realToken).scope : this.defaultScope;
1669
+ return this.getNameResolver().generateInstanceName(actualToken, validatedArgs, scope === InjectableScope.Request ? this.requestId : void 0, scope);
1673
1670
  }
1674
1671
  /**
1675
- * Gets the underlying "real" token from wrapped tokens.
1676
- * For BoundInjectionToken and FactoryInjectionToken, returns the wrapped token.
1677
- * For other tokens, returns the token itself.
1678
- *
1679
- * @param token The token to unwrap
1680
- * @returns The underlying InjectionToken
1681
- */ getRealToken(token) {
1682
- if (token instanceof BoundInjectionToken || token instanceof FactoryInjectionToken) return token.token;
1683
- return token;
1672
+ * Checks if a service is registered in the container.
1673
+ */ isRegistered(token) {
1674
+ const realToken = this.getTokenResolver().getRegistryToken(token);
1675
+ return this.getRegistry().has(realToken);
1684
1676
  }
1685
1677
  /**
1686
- * Convenience method that normalizes a token and then gets the real token.
1687
- * Useful for checking registry entries where you need the actual registered token.
1688
- *
1689
- * @param token Any injectable type
1690
- * @returns The underlying InjectionToken
1691
- */ getRegistryToken(token) {
1692
- return this.getRealToken(this.normalizeToken(token));
1678
+ * Waits for all pending operations to complete.
1679
+ */ async ready() {
1680
+ await this.getServiceInvalidator().readyWithStorage(this.getStorage());
1693
1681
  }
1694
1682
  /**
1695
- * Validates and resolves token arguments, handling factory token resolution and validation.
1696
- */ validateAndResolveTokenArgs(token, args) {
1697
- let actualToken = token;
1698
- if (typeof token === "function") actualToken = getInjectableToken(token);
1699
- let realArgs = args;
1700
- if (actualToken instanceof BoundInjectionToken) realArgs = actualToken.value;
1701
- else if (actualToken instanceof FactoryInjectionToken) if (actualToken.resolved) realArgs = actualToken.value;
1702
- else return [DIError.factoryTokenNotResolved(token.name), { actualToken }];
1703
- if (!actualToken.schema) return [void 0, {
1704
- actualToken,
1705
- validatedArgs: realArgs
1706
- }];
1707
- const validatedArgs = actualToken.schema?.safeParse(realArgs);
1708
- if (validatedArgs && !validatedArgs.success) {
1709
- this.logger?.error(`[TokenProcessor]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`, validatedArgs.error);
1710
- return [DIError.unknown(validatedArgs.error), { actualToken }];
1711
- }
1712
- return [void 0, {
1713
- actualToken,
1714
- validatedArgs: validatedArgs?.data
1715
- }];
1683
+ * @internal
1684
+ * Attempts to get an instance synchronously if it already exists.
1685
+ */ tryGetSync(token, args) {
1686
+ return this.tryGetSyncFromStorage(token, args, this.getStorage(), this.requestId);
1716
1687
  }
1717
1688
  /**
1718
- * Generates a unique instance name based on token and arguments.
1719
- */ generateInstanceName(token, args) {
1720
- if (!args) return token.toString();
1721
- const formattedArgs = Object.entries(args).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)).map(([key, value]) => `${key}=${this.formatArgValue(value)}`).join(",");
1722
- return `${token.toString()}:${formattedArgs.replaceAll(/"/g, "").replaceAll(/:/g, "=")}`;
1689
+ * @internal
1690
+ * Internal method for getting instances synchronously with configurable storage.
1691
+ */ tryGetSyncFromStorage(token, args, storage, requestId) {
1692
+ const tokenResolver = this.getTokenResolver();
1693
+ const realToken = tokenResolver.getRegistryToken(token);
1694
+ const registry = this.getRegistry();
1695
+ const scope = registry.has(realToken) ? registry.get(realToken).scope : InjectableScope.Singleton;
1696
+ try {
1697
+ const instanceName = this.getNameResolver().generateInstanceName(tokenResolver.normalizeToken(token), args, requestId, scope);
1698
+ const result = storage.get(instanceName);
1699
+ if (result && result[0] === void 0 && result[1]) {
1700
+ const holder = result[1];
1701
+ if (holder.status === InstanceStatus.Created) return holder.instance;
1702
+ }
1703
+ } catch {}
1704
+ return null;
1723
1705
  }
1724
1706
  /**
1725
- * Formats a single argument value for instance name generation.
1726
- */ formatArgValue(value) {
1727
- if (typeof value === "function") return `fn_${value.name}(${value.length})`;
1728
- if (typeof value === "symbol") return value.toString();
1729
- return JSON.stringify(value).slice(0, 40);
1707
+ * Adds an instance to the container.
1708
+ * Accepts class types, InjectionTokens, and BoundInjectionTokens.
1709
+ * Rejects InjectionTokens with required schemas (use BoundInjectionToken instead).
1710
+ *
1711
+ * @param token The class type, InjectionToken, or BoundInjectionToken to register the instance for
1712
+ * @param instance The instance to store
1713
+ */ addInstance(token, instance) {
1714
+ this.addInstanceToStorage(token, instance, this.getStorage(), this.defaultScope, this.requestId);
1730
1715
  }
1731
1716
  /**
1732
- * Creates a factory context for dependency injection during service instantiation.
1733
- * @param container The container instance (Container or ScopedContainer) for dependency resolution
1734
- * @param onDependencyResolved Callback when a dependency is resolved, receives the instance name
1735
- */ createFactoryContext(container, onDependencyResolved) {
1736
- const destroyListeners = /* @__PURE__ */ new Set();
1737
- const deps = /* @__PURE__ */ new Set();
1738
- function addDestroyListener(listener) {
1739
- destroyListeners.add(listener);
1740
- }
1741
- function getDestroyListeners() {
1742
- return Array.from(destroyListeners);
1717
+ * @internal
1718
+ * Internal method for adding instances with configurable scope and storage.
1719
+ */ addInstanceToStorage(token, instance, storage, scope, requestId) {
1720
+ if (token instanceof InjectionToken) {
1721
+ if (token.schema) {
1722
+ if (token.schema?.def?.type !== "optional") throw DIError.tokenSchemaRequiredError(token.name);
1723
+ }
1743
1724
  }
1744
- const self = this;
1745
- return {
1746
- async inject(token, args) {
1747
- const actualToken = typeof token === "function" ? getInjectableToken(token) : token;
1748
- const instanceName = self.generateInstanceName(actualToken, args);
1749
- deps.add(instanceName);
1750
- if (onDependencyResolved) onDependencyResolved(instanceName);
1751
- return container.get(token, args);
1752
- },
1753
- addDestroyListener,
1754
- getDestroyListeners,
1755
- container,
1756
- deps
1757
- };
1725
+ const tokenResolver = this.getTokenResolver();
1726
+ const registry = this.getRegistry();
1727
+ const normalizedToken = tokenResolver.normalizeToken(token);
1728
+ const realToken = tokenResolver.getRegistryToken(token);
1729
+ if (typeof token === "function" && !registry.has(realToken)) registry.set(realToken, scope, token, InjectableType.Class);
1730
+ else if (!registry.has(realToken)) registry.set(realToken, scope, StubFactoryClass, InjectableType.Class, -1);
1731
+ const instanceName = this.getNameResolver().generateInstanceName(normalizedToken, normalizedToken instanceof BoundInjectionToken ? normalizedToken.value : void 0, requestId, scope);
1732
+ storage.storeInstance(instanceName, instance);
1758
1733
  }
1759
1734
  };
1760
1735
 
1761
1736
  //#endregion
1762
- //#region src/internal/core/service-locator.mts
1737
+ //#region src/container/scoped-container.mts
1763
1738
  /**
1764
- * Core DI engine that coordinates service instantiation, resolution, and lifecycle.
1739
+ * Request-scoped dependency injection container.
1765
1740
  *
1766
- * Acts as the central orchestrator for dependency injection operations,
1767
- * delegating to specialized components (InstanceResolver, Instantiator, Invalidator)
1768
- * for specific tasks.
1769
- */ var ServiceLocator = class {
1741
+ * Wraps a parent Container and provides isolated request-scoped instances
1742
+ * while delegating singleton and transient resolution to the parent.
1743
+ * This design eliminates race conditions that can occur with async operations
1744
+ * when multiple requests are processed concurrently.
1745
+ */ var ScopedContainer = class extends AbstractContainer {
1746
+ parent;
1770
1747
  registry;
1771
- logger;
1772
- injectors;
1773
- eventBus;
1774
- manager;
1775
- instantiator;
1776
- tokenProcessor;
1777
- invalidator;
1778
- instanceResolver;
1779
- constructor(registry = globalRegistry, logger = null, injectors = defaultInjectors) {
1780
- this.registry = registry;
1781
- this.logger = logger;
1782
- this.injectors = injectors;
1783
- this.eventBus = new LifecycleEventBus(logger);
1784
- this.manager = new HolderManager(logger);
1785
- this.instantiator = new Instantiator(injectors);
1786
- this.tokenProcessor = new TokenProcessor(logger);
1787
- this.invalidator = new Invalidator(this.manager, this.eventBus, logger);
1788
- this.instanceResolver = new InstanceResolver(this.registry, this.manager, this.instantiator, this.tokenProcessor, logger, this);
1748
+ requestId;
1749
+ defaultScope = InjectableScope.Request;
1750
+ storage;
1751
+ disposed = false;
1752
+ metadata;
1753
+ constructor(parent, registry, requestId, metadata) {
1754
+ super(), this.parent = parent, this.registry = registry, this.requestId = requestId;
1755
+ this.storage = new UnifiedStorage(InjectableScope.Request);
1756
+ this.metadata = metadata || {};
1789
1757
  }
1790
- getEventBus() {
1791
- return this.eventBus;
1758
+ getStorage() {
1759
+ return this.storage;
1792
1760
  }
1793
- getManager() {
1794
- return this.manager;
1761
+ getRegistry() {
1762
+ return this.registry;
1795
1763
  }
1796
- getInvalidator() {
1797
- return this.invalidator;
1764
+ getTokenResolver() {
1765
+ return this.parent.getTokenResolver();
1798
1766
  }
1799
- getTokenProcessor() {
1800
- return this.tokenProcessor;
1767
+ getNameResolver() {
1768
+ return this.parent.getNameResolver();
1801
1769
  }
1802
- getInstanceIdentifier(token, args) {
1803
- const [err, { actualToken, validatedArgs }] = this.tokenProcessor.validateAndResolveTokenArgs(token, args);
1804
- if (err) throw err;
1805
- return this.tokenProcessor.generateInstanceName(actualToken, validatedArgs);
1770
+ getServiceInvalidator() {
1771
+ return this.parent.getServiceInvalidator();
1806
1772
  }
1807
1773
  /**
1808
- * Gets or creates an instance for the given token.
1809
- * @param token The injection token
1810
- * @param args Optional arguments
1811
- * @param contextContainer The container to use for creating FactoryContext
1812
- */ async getInstance(token, args, contextContainer) {
1813
- const [err, data] = await this.instanceResolver.resolveInstance(token, args, contextContainer);
1814
- if (err) return [err];
1815
- return [void 0, data];
1774
+ * Gets the request ID for this scoped container.
1775
+ */ getRequestId() {
1776
+ return this.requestId;
1816
1777
  }
1817
1778
  /**
1818
- * Gets or throws an instance for the given token.
1819
- * @param token The injection token
1820
- * @param args Optional arguments
1821
- * @param contextContainer The container to use for creating FactoryContext
1822
- */ async getOrThrowInstance(token, args, contextContainer) {
1823
- const [error, instance] = await this.getInstance(token, args, contextContainer);
1779
+ * Gets the parent container.
1780
+ */ getParent() {
1781
+ return this.parent;
1782
+ }
1783
+ /**
1784
+ * Gets metadata from the request context.
1785
+ */ getMetadata(key) {
1786
+ return this.metadata[key];
1787
+ }
1788
+ /**
1789
+ * Sets metadata on the request context.
1790
+ */ setMetadata(key, value) {
1791
+ this.metadata[key] = value;
1792
+ }
1793
+ async get(token, args) {
1794
+ if (this.disposed) throw new Error("ScopedContainer has been disposed");
1795
+ const realToken = this.getTokenResolver().getRegistryToken(token);
1796
+ if (this.registry.has(realToken)) {
1797
+ if (this.registry.get(realToken).scope === InjectableScope.Request) {
1798
+ const [error$1, instance$1] = await this.parent.getInstanceResolver().resolveRequestScopedInstance(token, args, this);
1799
+ if (error$1) throw error$1;
1800
+ return instance$1;
1801
+ }
1802
+ }
1803
+ const [error, instance] = await this.parent.getInstanceResolver().resolveInstance(token, args, this, this.storage, this.requestId);
1824
1804
  if (error) throw error;
1825
1805
  return instance;
1826
1806
  }
1827
1807
  /**
1828
- * Resolves a request-scoped service for a ScopedContainer.
1829
- * The service will be stored in the ScopedContainer's request context.
1830
- *
1831
- * @param token The injection token
1832
- * @param args Optional arguments
1833
- * @param scopedContainer The ScopedContainer that owns the request context
1834
- */ async resolveRequestScoped(token, args, scopedContainer) {
1835
- const [err, data] = await this.instanceResolver.resolveRequestScopedInstance(token, args, scopedContainer);
1836
- if (err) throw err;
1837
- return data;
1838
- }
1839
- getSyncInstance(token, args, contextContainer) {
1840
- return this.instanceResolver.getSyncInstance(token, args, contextContainer);
1808
+ * Invalidates a service and its dependencies.
1809
+ */ async invalidate(service) {
1810
+ const holder = this.storage.findByInstance(service);
1811
+ if (!holder) return this.parent.invalidate(service);
1812
+ await this.getServiceInvalidator().invalidateWithStorage(holder.name, this.storage);
1841
1813
  }
1842
- invalidate(service, round = 1) {
1843
- return this.invalidator.invalidate(service, round);
1814
+ /**
1815
+ * Disposes the container and cleans up all resources.
1816
+ * Alias for endRequest().
1817
+ */ async dispose() {
1818
+ return this.endRequest();
1844
1819
  }
1845
1820
  /**
1846
- * Gracefully clears all services in the ServiceLocator using invalidation logic.
1847
- * This method respects service dependencies and ensures proper cleanup order.
1848
- * Services that depend on others will be invalidated first, then their dependencies.
1849
- *
1850
- * @param options Optional configuration for the clearing process
1851
- * @returns Promise that resolves when all services have been cleared
1852
- */ async clearAll(options = {}) {
1853
- return this.invalidator.clearAll(options);
1821
+ * @internal
1822
+ * Attempts to get an instance synchronously if it already exists.
1823
+ * Checks request storage first, then delegates to parent.
1824
+ */ tryGetSync(token, args) {
1825
+ const realToken = this.getTokenResolver().getRegistryToken(token);
1826
+ if ((this.registry.has(realToken) ? this.registry.get(realToken).scope : InjectableScope.Singleton) === InjectableScope.Request) {
1827
+ const result = this.tryGetSyncFromStorage(token, args, this.storage, this.requestId);
1828
+ if (result !== null) return result;
1829
+ }
1830
+ return this.parent.tryGetSync(token, args, this.requestId);
1854
1831
  }
1855
1832
  /**
1856
- * Waits for all services to settle (either created, destroyed, or error state).
1857
- */ async ready() {
1858
- return this.invalidator.ready();
1833
+ * Adds an instance to the container.
1834
+ * Overrides base class to check disposed state.
1835
+ */ addInstance(token, instance) {
1836
+ if (this.disposed) throw new Error("ScopedContainer has been disposed");
1837
+ super.addInstance(token, instance);
1859
1838
  }
1860
1839
  /**
1861
- * Helper method for InstanceResolver to generate instance names.
1862
- * This is needed for the factory context creation.
1863
- */ generateInstanceName(token, args) {
1864
- return this.tokenProcessor.generateInstanceName(token, args);
1840
+ * Ends the request and cleans up all request-scoped services.
1841
+ */ async endRequest() {
1842
+ if (this.disposed) return;
1843
+ this.disposed = true;
1844
+ await this.getServiceInvalidator().clearAllWithStorage(this.storage);
1845
+ this.parent.removeRequestId(this.requestId);
1865
1846
  }
1866
1847
  };
1867
1848
 
@@ -2144,142 +2125,125 @@ function applyDecs2203RFactory() {
2144
2125
  function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
2145
2126
  return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
2146
2127
  }
2147
- var _dec, _initClass;
2128
+ var _dec, _initClass, _AbstractContainer;
2148
2129
  let _Container;
2149
2130
  _dec = Injectable();
2150
- var Container = class {
2131
+ var Container = class extends (_AbstractContainer = AbstractContainer) {
2151
2132
  registry;
2152
2133
  logger;
2153
2134
  injectors;
2154
2135
  static {
2155
- ({c: [_Container, _initClass]} = _apply_decs_2203_r(this, [], [_dec]));
2136
+ ({c: [_Container, _initClass]} = _apply_decs_2203_r(this, [], [_dec], _AbstractContainer));
2156
2137
  }
2157
2138
  constructor(registry = globalRegistry, logger = null, injectors = defaultInjectors) {
2158
- this.registry = registry;
2159
- this.logger = logger;
2160
- this.injectors = injectors;
2161
- this.serviceLocator = new ServiceLocator(registry, logger, injectors);
2139
+ super(), this.registry = registry, this.logger = logger, this.injectors = injectors;
2140
+ this.storage = new UnifiedStorage(InjectableScope.Singleton);
2141
+ this.eventBus = new LifecycleEventBus(logger);
2142
+ this.nameResolver = new NameResolver(logger);
2143
+ this.tokenResolver = new TokenResolver(logger);
2144
+ this.scopeTracker = new ScopeTracker(registry, this.nameResolver, logger);
2145
+ this.serviceInitializer = new ServiceInitializer(injectors);
2146
+ this.serviceInvalidator = new ServiceInvalidator(this.eventBus, logger);
2147
+ this.instanceResolver = new InstanceResolver(registry, this.storage, this.serviceInitializer, this.tokenResolver, this.nameResolver, this.scopeTracker, this.serviceInvalidator, this.eventBus, logger);
2162
2148
  this.registerSelf();
2163
2149
  }
2164
- serviceLocator;
2150
+ defaultScope = InjectableScope.Singleton;
2151
+ requestId = void 0;
2152
+ storage;
2153
+ serviceInitializer;
2154
+ serviceInvalidator;
2155
+ tokenResolver;
2156
+ nameResolver;
2157
+ scopeTracker;
2158
+ eventBus;
2159
+ instanceResolver;
2165
2160
  activeRequestIds = /* @__PURE__ */ new Set();
2166
2161
  registerSelf() {
2167
2162
  const token = getInjectableToken(_Container);
2168
- const instanceName = this.serviceLocator.getInstanceIdentifier(token);
2169
- this.serviceLocator.getManager().storeCreatedHolder(instanceName, this, InjectableType.Class, InjectableScope.Singleton);
2163
+ this.registry.set(token, InjectableScope.Singleton, _Container, InjectableType.Class);
2164
+ const instanceName = this.nameResolver.generateInstanceName(token, void 0, void 0, InjectableScope.Singleton);
2165
+ this.storage.storeInstance(instanceName, this);
2170
2166
  }
2171
2167
  async get(token, args) {
2172
- const realToken = this.serviceLocator.getTokenProcessor().getRegistryToken(token);
2168
+ const realToken = this.tokenResolver.getRegistryToken(token);
2173
2169
  if (this.registry.has(realToken)) {
2174
- if (this.registry.get(realToken).scope === InjectableScope.Request) throw DIError.unknown(`Cannot resolve request-scoped service "${String(realToken.name)}" from Container. Use beginRequest() to create a ScopedContainer for request-scoped services.`);
2170
+ if (this.registry.get(realToken).scope === InjectableScope.Request) throw DIError.scopeMismatchError(realToken.name, "ScopedContainer", "Container");
2175
2171
  }
2176
- return this.serviceLocator.getOrThrowInstance(token, args, this);
2177
- }
2178
- /**
2179
- * Gets an instance with a specific container context.
2180
- * Used by ScopedContainer to delegate singleton/transient resolution
2181
- * while maintaining the correct container context for nested inject() calls.
2182
- *
2183
- * @internal
2184
- */ async getWithContext(token, args, contextContainer) {
2185
- return this.serviceLocator.getOrThrowInstance(token, args, contextContainer);
2186
- }
2187
- /**
2188
- * Resolves a request-scoped service for a ScopedContainer.
2189
- * The service will be stored in the ScopedContainer's request context.
2190
- *
2191
- * @internal
2192
- */ async resolveForRequest(token, args, scopedContainer) {
2193
- return this.serviceLocator.resolveRequestScoped(token, args, scopedContainer);
2194
- }
2195
- /**
2196
- * Gets the underlying ServiceLocator instance for advanced usage
2197
- */ getServiceLocator() {
2198
- return this.serviceLocator;
2199
- }
2200
- /**
2201
- * Gets the registry
2202
- */ getRegistry() {
2203
- return this.registry;
2172
+ const [error, instance] = await this.instanceResolver.resolveInstance(token, args, this);
2173
+ if (error) throw error;
2174
+ return instance;
2204
2175
  }
2205
2176
  /**
2206
- * Invalidates a service and its dependencies
2177
+ * Invalidates a service and its dependencies.
2207
2178
  */ async invalidate(service) {
2208
- const holder = this.getHolderByInstance(service);
2209
- if (holder) await this.serviceLocator.invalidate(holder.name);
2210
- }
2211
- /**
2212
- * Gets a service holder by instance (reverse lookup)
2213
- */ getHolderByInstance(instance) {
2214
- const holderMap = Array.from(this.serviceLocator.getManager().filter((holder) => holder.instance === instance).values());
2215
- return holderMap.length > 0 ? holderMap[0] : null;
2216
- }
2217
- /**
2218
- * Checks if a service is registered in the container
2219
- */ isRegistered(token) {
2220
- try {
2221
- return this.serviceLocator.getInstanceIdentifier(token) !== null;
2222
- } catch {
2223
- return false;
2179
+ const holder = this.storage.findByInstance(service);
2180
+ if (!holder) {
2181
+ this.logger?.warn(`[Container] Service instance not found for invalidation`);
2182
+ return;
2224
2183
  }
2184
+ await this.serviceInvalidator.invalidateWithStorage(holder.name, this.storage);
2225
2185
  }
2226
2186
  /**
2227
- * Disposes the container and cleans up all resources
2187
+ * Disposes the container and cleans up all resources.
2228
2188
  */ async dispose() {
2229
- await this.serviceLocator.clearAll();
2230
- }
2231
- /**
2232
- * Waits for all pending operations to complete
2233
- */ async ready() {
2234
- await this.serviceLocator.ready();
2189
+ await this.serviceInvalidator.clearAllWithStorage(this.storage);
2235
2190
  }
2236
2191
  /**
2237
2192
  * @internal
2238
2193
  * Attempts to get an instance synchronously if it already exists.
2239
- * Returns null if the instance doesn't exist or is not ready.
2240
- */ tryGetSync(token, args) {
2241
- return this.serviceLocator.getSyncInstance(token, args, this);
2194
+ * Overrides base class to support requestId parameter for ScopedContainer compatibility.
2195
+ */ tryGetSync(token, args, requestId) {
2196
+ return this.tryGetSyncFromStorage(token, args, this.storage, requestId ?? this.requestId);
2242
2197
  }
2243
2198
  /**
2244
2199
  * Begins a new request context and returns a ScopedContainer.
2245
- *
2246
- * The ScopedContainer provides isolated request-scoped service resolution
2247
- * while delegating singleton and transient services to this Container.
2248
- *
2249
- * @param requestId Unique identifier for this request
2250
- * @param metadata Optional metadata for the request
2251
- * @param priority Priority for resolution (higher = more priority)
2252
- * @returns A ScopedContainer for this request
2253
- */ beginRequest(requestId, metadata, priority = 100) {
2254
- if (this.activeRequestIds.has(requestId)) throw DIError.unknown(`Request context "${requestId}" already exists. Use a unique request ID.`);
2200
+ */ beginRequest(requestId, metadata) {
2201
+ if (this.activeRequestIds.has(requestId)) throw new Error(`Request with ID ${requestId} already exists`);
2255
2202
  this.activeRequestIds.add(requestId);
2256
- this.logger?.log(`[Container] Started request context: ${requestId}`);
2257
- return new ScopedContainer(this, this.registry, requestId, metadata, priority);
2258
- }
2259
- /**
2260
- * Removes a request ID from the active set.
2261
- * Called by ScopedContainer when the request ends.
2262
- *
2263
- * @internal
2264
- */ removeActiveRequest(requestId) {
2265
- this.activeRequestIds.delete(requestId);
2266
- this.logger?.log(`[Container] Ended request context: ${requestId}`);
2203
+ return new ScopedContainer(this, this.registry, requestId, metadata);
2267
2204
  }
2268
2205
  /**
2269
- * Gets the set of active request IDs.
2206
+ * Gets all active request IDs.
2270
2207
  */ getActiveRequestIds() {
2271
2208
  return this.activeRequestIds;
2272
2209
  }
2273
2210
  /**
2274
- * Checks if a request ID is currently active.
2211
+ * Checks if a request is active.
2275
2212
  */ hasActiveRequest(requestId) {
2276
2213
  return this.activeRequestIds.has(requestId);
2277
2214
  }
2278
2215
  /**
2279
- * Clears all instances and bindings from the container.
2280
- * This is useful for testing or resetting the container state.
2281
- */ clear() {
2282
- return this.serviceLocator.clearAll();
2216
+ * Removes a request ID from active requests.
2217
+ * Called by ScopedContainer when request ends.
2218
+ */ removeRequestId(requestId) {
2219
+ this.activeRequestIds.delete(requestId);
2220
+ }
2221
+ getStorage() {
2222
+ return this.storage;
2223
+ }
2224
+ getServiceInitializer() {
2225
+ return this.serviceInitializer;
2226
+ }
2227
+ getServiceInvalidator() {
2228
+ return this.serviceInvalidator;
2229
+ }
2230
+ getTokenResolver() {
2231
+ return this.tokenResolver;
2232
+ }
2233
+ getNameResolver() {
2234
+ return this.nameResolver;
2235
+ }
2236
+ getScopeTracker() {
2237
+ return this.scopeTracker;
2238
+ }
2239
+ getEventBus() {
2240
+ return this.eventBus;
2241
+ }
2242
+ getRegistry() {
2243
+ return this.registry;
2244
+ }
2245
+ getInstanceResolver() {
2246
+ return this.instanceResolver;
2283
2247
  }
2284
2248
  static {
2285
2249
  _initClass();
@@ -2287,5 +2251,5 @@ var Container = class {
2287
2251
  };
2288
2252
 
2289
2253
  //#endregion
2290
- export { Injectable as A, getInjectableToken as C, withoutResolutionContext as D, withResolutionContext as E, FactoryInjectionToken as F, InjectionToken as I, InjectableType as L, Registry as M, globalRegistry as N, DIError as O, BoundInjectionToken as P, InjectableScope as R, wrapSyncInit as S, getCurrentResolutionContext as T, asyncInject as _, LifecycleEventBus as a, optional as b, InstanceResolver as c, RequestStorage as d, DefaultRequestContext as f, CircularDetector as g, InstanceStatus as h, HolderManager as i, InjectableTokenMeta as j, DIErrorCode as k, SingletonStorage as l, BaseHolderManager as m, ServiceLocator as n, Invalidator as o, createRequestContext as p, TokenProcessor as r, Instantiator as s, _Container as t, ScopedContainer as u, defaultInjectors as v, getInjectors as w, provideFactoryContext as x, inject as y };
2291
- //# sourceMappingURL=container-Pb_Y4Z4x.mjs.map
2254
+ export { Registry as A, withResolutionContext as C, DIError as D, isUsingNativeAsyncLocalStorage as E, InjectableTokenMeta as F, InjectableType as I, InjectableScope as L, BoundInjectionToken as M, FactoryInjectionToken as N, DIErrorCode as O, InjectionToken as P, getCurrentResolutionContext as S, createAsyncLocalStorage as T, ScopeTracker as _, UnifiedStorage as a, CircularDetector as b, asyncInject as c, optional as d, provideFactoryContext as f, ServiceInitializer as g, ServiceInvalidator as h, LifecycleEventBus as i, globalRegistry as j, Injectable as k, defaultInjectors as l, getInjectors as m, ScopedContainer as n, TokenResolver as o, wrapSyncInit as p, StubFactoryClass as r, getInjectableToken as s, _Container as t, inject as u, NameResolver as v, withoutResolutionContext as w, InstanceStatus as x, InstanceResolver as y };
2255
+ //# sourceMappingURL=container-8-z89TyQ.mjs.map