@codefast/di 0.5.0 → 0.6.1

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 (266) hide show
  1. package/CHANGELOG.md +712 -0
  2. package/README.md +119 -30
  3. package/dist/ambient/active-container.d.ts +37 -0
  4. package/dist/ambient/active-container.d.ts.map +1 -0
  5. package/dist/ambient/active-container.js +41 -0
  6. package/dist/ambient/active-container.js.map +1 -0
  7. package/dist/container/binding-builders.d.ts +12 -8
  8. package/dist/container/binding-builders.d.ts.map +1 -1
  9. package/dist/container/binding-builders.js +62 -31
  10. package/dist/container/binding-builders.js.map +1 -1
  11. package/dist/container/container.d.ts +33 -19
  12. package/dist/container/container.d.ts.map +1 -1
  13. package/dist/container/container.js +281 -118
  14. package/dist/container/container.js.map +1 -1
  15. package/dist/{resolution → core}/binding-scope.d.ts +2 -2
  16. package/dist/core/binding-scope.d.ts.map +1 -0
  17. package/dist/core/binding-scope.js.map +1 -0
  18. package/dist/{binding.d.ts → core/binding.d.ts} +70 -15
  19. package/dist/core/binding.d.ts.map +1 -0
  20. package/dist/{binding.js → core/binding.js} +27 -12
  21. package/dist/core/binding.js.map +1 -0
  22. package/dist/core/constraint-requirement.d.ts +53 -0
  23. package/dist/core/constraint-requirement.d.ts.map +1 -0
  24. package/dist/core/constraint-requirement.js +62 -0
  25. package/dist/core/constraint-requirement.js.map +1 -0
  26. package/dist/{constructor-type.d.ts → core/constructor-type.d.ts} +1 -1
  27. package/dist/core/constructor-type.d.ts.map +1 -0
  28. package/dist/core/constructor-type.js.map +1 -0
  29. package/dist/{module.d.ts → core/module.d.ts} +5 -5
  30. package/dist/core/module.d.ts.map +1 -0
  31. package/dist/{module.js → core/module.js} +4 -4
  32. package/dist/core/module.js.map +1 -0
  33. package/dist/{registry.d.ts → core/registry.d.ts} +27 -7
  34. package/dist/core/registry.d.ts.map +1 -0
  35. package/dist/{registry.js → core/registry.js} +125 -52
  36. package/dist/core/registry.js.map +1 -0
  37. package/dist/core/tag.d.ts +80 -0
  38. package/dist/core/tag.d.ts.map +1 -0
  39. package/dist/core/tag.js +96 -0
  40. package/dist/core/tag.js.map +1 -0
  41. package/dist/{token.d.ts → core/token.d.ts} +2 -6
  42. package/dist/core/token.d.ts.map +1 -0
  43. package/dist/core/token.js +13 -0
  44. package/dist/core/token.js.map +1 -0
  45. package/dist/{types.d.ts → core/types.d.ts} +27 -21
  46. package/dist/core/types.d.ts.map +1 -0
  47. package/dist/core/types.js.map +1 -0
  48. package/dist/decorators/inject.d.ts +5 -49
  49. package/dist/decorators/inject.d.ts.map +1 -1
  50. package/dist/decorators/inject.js +34 -104
  51. package/dist/decorators/inject.js.map +1 -1
  52. package/dist/decorators/injectable.d.ts +25 -5
  53. package/dist/decorators/injectable.d.ts.map +1 -1
  54. package/dist/decorators/injectable.js +2 -3
  55. package/dist/decorators/injectable.js.map +1 -1
  56. package/dist/decorators/lifecycle-decorators.d.ts +4 -2
  57. package/dist/decorators/lifecycle-decorators.d.ts.map +1 -1
  58. package/dist/decorators/lifecycle-decorators.js +19 -25
  59. package/dist/decorators/lifecycle-decorators.js.map +1 -1
  60. package/dist/{resolution → errors}/diagnostics.d.ts +2 -0
  61. package/dist/errors/diagnostics.d.ts.map +1 -0
  62. package/dist/errors/diagnostics.js.map +1 -0
  63. package/dist/{errors.d.ts → errors/errors.d.ts} +89 -4
  64. package/dist/errors/errors.d.ts.map +1 -0
  65. package/dist/{errors.js → errors/errors.js} +129 -7
  66. package/dist/errors/errors.js.map +1 -0
  67. package/dist/index.d.ts +21 -14
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +15 -7
  70. package/dist/index.js.map +1 -1
  71. package/dist/injection/descriptor.d.ts +79 -0
  72. package/dist/injection/descriptor.d.ts.map +1 -0
  73. package/dist/injection/descriptor.js +118 -0
  74. package/dist/injection/descriptor.js.map +1 -0
  75. package/dist/{resolution → injection}/resolve-options.d.ts +21 -11
  76. package/dist/injection/resolve-options.d.ts.map +1 -0
  77. package/dist/injection/resolve-options.js +101 -0
  78. package/dist/injection/resolve-options.js.map +1 -0
  79. package/dist/introspection/dependency-graph.d.ts +16 -5
  80. package/dist/introspection/dependency-graph.d.ts.map +1 -1
  81. package/dist/introspection/dependency-graph.js +138 -57
  82. package/dist/introspection/dependency-graph.js.map +1 -1
  83. package/dist/introspection/graph-adapters/cytoscape.d.ts +6 -3
  84. package/dist/introspection/graph-adapters/cytoscape.d.ts.map +1 -1
  85. package/dist/introspection/graph-adapters/cytoscape.js +3 -0
  86. package/dist/introspection/graph-adapters/cytoscape.js.map +1 -1
  87. package/dist/introspection/graph-adapters/dot.d.ts.map +1 -1
  88. package/dist/introspection/graph-adapters/dot.js +12 -5
  89. package/dist/introspection/graph-adapters/dot.js.map +1 -1
  90. package/dist/introspection/graph-adapters/mermaid.d.ts +9 -0
  91. package/dist/introspection/graph-adapters/mermaid.d.ts.map +1 -0
  92. package/dist/introspection/graph-adapters/mermaid.js +47 -0
  93. package/dist/introspection/graph-adapters/mermaid.js.map +1 -0
  94. package/dist/introspection/graph-adapters/reactflow.d.ts +6 -3
  95. package/dist/introspection/graph-adapters/reactflow.d.ts.map +1 -1
  96. package/dist/introspection/graph-adapters/reactflow.js +5 -1
  97. package/dist/introspection/graph-adapters/reactflow.js.map +1 -1
  98. package/dist/introspection/inspector.d.ts +4 -4
  99. package/dist/introspection/inspector.d.ts.map +1 -1
  100. package/dist/introspection/inspector.js +10 -6
  101. package/dist/introspection/inspector.js.map +1 -1
  102. package/dist/lifecycle/lifecycle-manager.d.ts +27 -0
  103. package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
  104. package/dist/{resolution/lifecycle.js → lifecycle/lifecycle-manager.js} +39 -9
  105. package/dist/lifecycle/lifecycle-manager.js.map +1 -0
  106. package/dist/lifecycle/scope-manager.d.ts +48 -0
  107. package/dist/lifecycle/scope-manager.d.ts.map +1 -0
  108. package/dist/lifecycle/scope-manager.js +135 -0
  109. package/dist/lifecycle/scope-manager.js.map +1 -0
  110. package/dist/metadata/metadata-reader-token.d.ts +1 -1
  111. package/dist/metadata/metadata-reader-token.d.ts.map +1 -1
  112. package/dist/metadata/metadata-reader-token.js +1 -1
  113. package/dist/metadata/metadata-reader-token.js.map +1 -1
  114. package/dist/metadata/metadata-types.d.ts +9 -9
  115. package/dist/metadata/metadata-types.d.ts.map +1 -1
  116. package/dist/metadata/symbol-metadata-reader.d.ts +19 -6
  117. package/dist/metadata/symbol-metadata-reader.d.ts.map +1 -1
  118. package/dist/metadata/symbol-metadata-reader.js +89 -9
  119. package/dist/metadata/symbol-metadata-reader.js.map +1 -1
  120. package/dist/metadata/verifying-metadata-reader.d.ts +16 -0
  121. package/dist/metadata/verifying-metadata-reader.d.ts.map +1 -0
  122. package/dist/metadata/verifying-metadata-reader.js +32 -0
  123. package/dist/metadata/verifying-metadata-reader.js.map +1 -0
  124. package/dist/resolution/{activation-need.d.ts → cache/activation-need.d.ts} +5 -5
  125. package/dist/resolution/cache/activation-need.d.ts.map +1 -0
  126. package/dist/resolution/cache/activation-need.js.map +1 -0
  127. package/dist/resolution/{binding-lookup-cache.d.ts → cache/binding-lookup-cache.d.ts} +9 -5
  128. package/dist/resolution/cache/binding-lookup-cache.d.ts.map +1 -0
  129. package/dist/resolution/{binding-lookup-cache.js → cache/binding-lookup-cache.js} +55 -0
  130. package/dist/resolution/cache/binding-lookup-cache.js.map +1 -0
  131. package/dist/resolution/cache/class-introspector.d.ts +66 -0
  132. package/dist/resolution/cache/class-introspector.d.ts.map +1 -0
  133. package/dist/resolution/cache/class-introspector.js +194 -0
  134. package/dist/resolution/cache/class-introspector.js.map +1 -0
  135. package/dist/resolution/context.d.ts +84 -0
  136. package/dist/resolution/context.d.ts.map +1 -0
  137. package/dist/resolution/{environment.js → context.js} +54 -80
  138. package/dist/resolution/context.js.map +1 -0
  139. package/dist/resolution/path/resolution-path.d.ts +94 -0
  140. package/dist/resolution/path/resolution-path.d.ts.map +1 -0
  141. package/dist/resolution/path/resolution-path.js +117 -0
  142. package/dist/resolution/path/resolution-path.js.map +1 -0
  143. package/dist/resolution/{instantiation-plan.d.ts → plan/instantiation-plan.d.ts} +37 -7
  144. package/dist/resolution/plan/instantiation-plan.d.ts.map +1 -0
  145. package/dist/resolution/plan/instantiation-plan.js +419 -0
  146. package/dist/resolution/plan/instantiation-plan.js.map +1 -0
  147. package/dist/resolution/resolver.d.ts +28 -28
  148. package/dist/resolution/resolver.d.ts.map +1 -1
  149. package/dist/resolution/resolver.js +581 -239
  150. package/dist/resolution/resolver.js.map +1 -1
  151. package/dist/resolution/select/binding-select.d.ts +34 -0
  152. package/dist/resolution/select/binding-select.d.ts.map +1 -0
  153. package/dist/resolution/select/binding-select.js +175 -0
  154. package/dist/resolution/select/binding-select.js.map +1 -0
  155. package/dist/resolution/{constraints.d.ts → select/constraints.d.ts} +13 -12
  156. package/dist/resolution/select/constraints.d.ts.map +1 -0
  157. package/dist/resolution/{constraints.js → select/constraints.js} +33 -22
  158. package/dist/resolution/select/constraints.js.map +1 -0
  159. package/package.json +160 -20
  160. package/src/ambient/active-container.ts +65 -0
  161. package/src/container/binding-builders.ts +79 -44
  162. package/src/container/container.ts +357 -170
  163. package/src/{resolution → core}/binding-scope.ts +2 -2
  164. package/src/{binding.ts → core/binding.ts} +101 -39
  165. package/src/core/constraint-requirement.ts +94 -0
  166. package/src/{constructor-type.ts → core/constructor-type.ts} +1 -1
  167. package/src/{module.ts → core/module.ts} +10 -10
  168. package/src/core/registry.ts +365 -0
  169. package/src/core/tag.ts +158 -0
  170. package/src/{token.ts → core/token.ts} +2 -14
  171. package/src/{types.ts → core/types.ts} +37 -34
  172. package/src/decorators/inject.ts +39 -175
  173. package/src/decorators/injectable.ts +40 -10
  174. package/src/decorators/lifecycle-decorators.ts +22 -34
  175. package/src/{resolution → errors}/diagnostics.ts +2 -0
  176. package/src/{errors.ts → errors/errors.ts} +152 -8
  177. package/src/index.ts +43 -15
  178. package/src/injection/descriptor.ts +206 -0
  179. package/src/injection/resolve-options.ts +144 -0
  180. package/src/introspection/dependency-graph.ts +223 -63
  181. package/src/introspection/graph-adapters/cytoscape.ts +9 -3
  182. package/src/introspection/graph-adapters/dot.ts +13 -5
  183. package/src/introspection/graph-adapters/mermaid.ts +61 -0
  184. package/src/introspection/graph-adapters/reactflow.ts +11 -4
  185. package/src/introspection/inspector.ts +16 -12
  186. package/src/{resolution/lifecycle.ts → lifecycle/lifecycle-manager.ts} +68 -32
  187. package/src/lifecycle/scope-manager.ts +156 -0
  188. package/src/metadata/metadata-reader-token.ts +2 -2
  189. package/src/metadata/metadata-types.ts +9 -9
  190. package/src/metadata/symbol-metadata-reader.ts +95 -18
  191. package/src/metadata/verifying-metadata-reader.ts +41 -0
  192. package/src/resolution/{activation-need.ts → cache/activation-need.ts} +8 -8
  193. package/src/resolution/{binding-lookup-cache.ts → cache/binding-lookup-cache.ts} +63 -5
  194. package/src/resolution/cache/class-introspector.ts +251 -0
  195. package/src/resolution/{environment.ts → context.ts} +84 -145
  196. package/src/resolution/path/resolution-path.ts +167 -0
  197. package/src/resolution/plan/instantiation-plan.ts +615 -0
  198. package/src/resolution/resolver.ts +663 -313
  199. package/src/resolution/select/binding-select.ts +210 -0
  200. package/src/resolution/select/constraints.ts +130 -0
  201. package/dist/binding.d.ts.map +0 -1
  202. package/dist/binding.js.map +0 -1
  203. package/dist/constructor-type.d.ts.map +0 -1
  204. package/dist/constructor-type.js.map +0 -1
  205. package/dist/errors.d.ts.map +0 -1
  206. package/dist/errors.js.map +0 -1
  207. package/dist/module.d.ts.map +0 -1
  208. package/dist/module.js.map +0 -1
  209. package/dist/registry.d.ts.map +0 -1
  210. package/dist/registry.js.map +0 -1
  211. package/dist/resolution/activation-need.d.ts.map +0 -1
  212. package/dist/resolution/activation-need.js.map +0 -1
  213. package/dist/resolution/binding-lookup-cache.d.ts.map +0 -1
  214. package/dist/resolution/binding-lookup-cache.js.map +0 -1
  215. package/dist/resolution/binding-scope.d.ts.map +0 -1
  216. package/dist/resolution/binding-scope.js.map +0 -1
  217. package/dist/resolution/binding-select.d.ts +0 -23
  218. package/dist/resolution/binding-select.d.ts.map +0 -1
  219. package/dist/resolution/binding-select.js +0 -121
  220. package/dist/resolution/binding-select.js.map +0 -1
  221. package/dist/resolution/class-introspector.d.ts +0 -27
  222. package/dist/resolution/class-introspector.d.ts.map +0 -1
  223. package/dist/resolution/class-introspector.js +0 -60
  224. package/dist/resolution/class-introspector.js.map +0 -1
  225. package/dist/resolution/constraints.d.ts.map +0 -1
  226. package/dist/resolution/constraints.js.map +0 -1
  227. package/dist/resolution/diagnostics.d.ts.map +0 -1
  228. package/dist/resolution/diagnostics.js.map +0 -1
  229. package/dist/resolution/environment.d.ts +0 -94
  230. package/dist/resolution/environment.d.ts.map +0 -1
  231. package/dist/resolution/environment.js.map +0 -1
  232. package/dist/resolution/instantiation-plan.d.ts.map +0 -1
  233. package/dist/resolution/instantiation-plan.js +0 -183
  234. package/dist/resolution/instantiation-plan.js.map +0 -1
  235. package/dist/resolution/lifecycle.d.ts +0 -23
  236. package/dist/resolution/lifecycle.d.ts.map +0 -1
  237. package/dist/resolution/lifecycle.js.map +0 -1
  238. package/dist/resolution/resolution-path.d.ts +0 -98
  239. package/dist/resolution/resolution-path.d.ts.map +0 -1
  240. package/dist/resolution/resolution-path.js +0 -98
  241. package/dist/resolution/resolution-path.js.map +0 -1
  242. package/dist/resolution/resolve-options.d.ts.map +0 -1
  243. package/dist/resolution/resolve-options.js +0 -56
  244. package/dist/resolution/resolve-options.js.map +0 -1
  245. package/dist/resolution/scope.d.ts +0 -28
  246. package/dist/resolution/scope.d.ts.map +0 -1
  247. package/dist/resolution/scope.js +0 -88
  248. package/dist/resolution/scope.js.map +0 -1
  249. package/dist/token.d.ts.map +0 -1
  250. package/dist/token.js +0 -22
  251. package/dist/token.js.map +0 -1
  252. package/dist/types.d.ts.map +0 -1
  253. package/dist/types.js.map +0 -1
  254. package/src/registry.ts +0 -282
  255. package/src/resolution/binding-select.ts +0 -154
  256. package/src/resolution/class-introspector.ts +0 -74
  257. package/src/resolution/constraints.ts +0 -121
  258. package/src/resolution/instantiation-plan.ts +0 -292
  259. package/src/resolution/resolution-path.ts +0 -156
  260. package/src/resolution/resolve-options.ts +0 -89
  261. package/src/resolution/scope.ts +0 -105
  262. /package/dist/{resolution → core}/binding-scope.js +0 -0
  263. /package/dist/{constructor-type.js → core/constructor-type.js} +0 -0
  264. /package/dist/{types.js → core/types.js} +0 -0
  265. /package/dist/{resolution → errors}/diagnostics.js +0 -0
  266. /package/dist/resolution/{activation-need.js → cache/activation-need.js} +0 -0
@@ -1,4 +1,4 @@
1
- import type { BindingIdentifier, BindingScope, ResolveOptions } from "#/types";
1
+ import type { BindingIdentifier, BindingScope, ResolveOptions } from "#/core/types";
2
2
 
3
3
  /**
4
4
  * @since 0.3.16-canary.0
@@ -36,6 +36,19 @@ export class TokenNotBoundError extends DiError {
36
36
  }
37
37
  }
38
38
 
39
+ // Options carry caller values a tag may hold — a bigint or a circular object must not make the
40
+ // diagnostic itself throw and mask the real error.
41
+ function describeResolveOptions(options: ResolveOptions): string {
42
+ try {
43
+ return (
44
+ JSON.stringify(options, (_key, value: unknown) => (typeof value === "bigint" ? `${String(value)}n` : value)) ??
45
+ "undefined"
46
+ );
47
+ } catch {
48
+ return "[unserializable options]";
49
+ }
50
+ }
51
+
39
52
  /**
40
53
  * @since 0.3.16-canary.0
41
54
  */
@@ -46,7 +59,7 @@ export class NoMatchingBindingError extends DiError {
46
59
  readonly availableSlots: Array<string>;
47
60
 
48
61
  constructor(tokenName: string, options: ResolveOptions, availableSlots: Array<string>) {
49
- const optionsString = JSON.stringify(options);
62
+ const optionsString = describeResolveOptions(options);
50
63
  const slotsStr = availableSlots.join(", ");
51
64
  super(`No binding for '${tokenName}' matching ${optionsString}. Available slots: [${slotsStr}].`);
52
65
  this.tokenName = tokenName;
@@ -90,12 +103,16 @@ export class CircularDependencyError extends DiError {
90
103
  */
91
104
  export class AsyncResolutionError extends DiError {
92
105
  readonly code = "ASYNC_RESOLUTION";
106
+ /** The token the caller asked for — what `resolveAsync` has to be called with. */
93
107
  readonly tokenName: string;
108
+ /** The token whose factory is async: `tokenName` itself unless a dependency forced it. */
94
109
  readonly asyncSourceToken: string;
95
110
 
96
- constructor(tokenName: string, asyncSourceToken: string) {
111
+ constructor(tokenName: string, asyncSourceToken: string = tokenName) {
97
112
  super(
98
- `Token '${tokenName}' requires async resolution because '${asyncSourceToken}' in its dependency chain has an async factory. Use container.resolveAsync(${tokenName}).`,
113
+ asyncSourceToken === tokenName
114
+ ? `Token '${tokenName}' requires async resolution because its factory is async. Use container.resolveAsync(${tokenName}).`
115
+ : `Token '${tokenName}' requires async resolution because '${asyncSourceToken}' in its dependency chain has an async factory. Use container.resolveAsync(${tokenName}).`,
99
116
  );
100
117
  this.tokenName = tokenName;
101
118
  this.asyncSourceToken = asyncSourceToken;
@@ -141,6 +158,98 @@ export class ScopeViolationError extends DiError {
141
158
  }
142
159
  }
143
160
 
161
+ /**
162
+ * A `…TaggedAll` constraint built from a criteria list with nothing in it.
163
+ *
164
+ * @remarks Reported where the list is passed rather than where it fails to match, because it does
165
+ * not fail to match — "carries all of no criteria" holds for any ancestor, so the constraint quietly
166
+ * becomes a weaker one that still outranks an unconstrained binding.
167
+ *
168
+ * @since 0.6.0
169
+ */
170
+ export class EmptyTagCriteriaError extends DiError {
171
+ readonly code = "EMPTY_TAG_CRITERIA";
172
+ readonly helperName: string;
173
+
174
+ constructor(helperName: string) {
175
+ super(
176
+ `${helperName}() was given no criteria. An empty list matches any ancestor, which is not what the call says — pass the criteria to require, or drop the constraint if there are none.`,
177
+ );
178
+ this.helperName = helperName;
179
+ }
180
+ }
181
+
182
+ /**
183
+ * A constraint waiting on a slot name that nothing in the container chain declares.
184
+ *
185
+ * @remarks A name is a bare string, so a typo produces a constraint that is never satisfied and
186
+ * never reported. Reported by `validate()` rather than at bind time, because the binding carrying
187
+ * the name may be registered after the constraint is built.
188
+ *
189
+ * @since 0.6.0
190
+ */
191
+ export class UnreachableConstraintError extends DiError {
192
+ readonly code = "UNREACHABLE_CONSTRAINT";
193
+ readonly tokenName: string;
194
+ readonly requiredName: string;
195
+ readonly helperName: string;
196
+
197
+ constructor(tokenName: string, requiredName: string, helperName: string) {
198
+ super(
199
+ `The binding for '${tokenName}' is constrained by ${helperName}('${requiredName}'), but no binding in this container or its ancestors declares the slot name '${requiredName}', so the constraint can never hold. Name the slot with .whenNamed('${requiredName}') on the binding it should match, or correct the name here.`,
200
+ );
201
+ this.tokenName = tokenName;
202
+ this.requiredName = requiredName;
203
+ this.helperName = helperName;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * A container-level lifecycle hook whose token nothing is bound to, so it can never run.
209
+ *
210
+ * @remarks Hooks are keyed by token identity, which makes a class that is only ever a `to()` target
211
+ * look like a token and match nothing. Reported by `validate()` rather than at registration, because
212
+ * binding after registering the hook is a supported order.
213
+ *
214
+ * @since 0.6.0
215
+ */
216
+ export class UnreachableLifecycleHookError extends DiError {
217
+ readonly code = "UNREACHABLE_LIFECYCLE_HOOK";
218
+ readonly tokenName: string;
219
+ readonly phase: "onActivation" | "onDeactivation";
220
+
221
+ constructor(tokenName: string, phase: "onActivation" | "onDeactivation") {
222
+ super(
223
+ `${phase}() is registered for '${tokenName}', which nothing is bound to in this container or its ancestors, so the hook can never run. Bind the token, or — if '${tokenName}' is a class you bound as an implementation via .to(${tokenName}) — register the hook against the token you bound instead.`,
224
+ );
225
+ this.tokenName = tokenName;
226
+ this.phase = phase;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * A {@link MetadataReader} described a class with something the container cannot use.
232
+ *
233
+ * @remarks Separate from {@link MissingMetadataError}: absent metadata is a class the container was
234
+ * never told about, while invalid metadata is a reader that answered wrongly. Covers both the
235
+ * constructor answer and the lifecycle one, since only the `reason` differs.
236
+ *
237
+ * @since 0.6.0
238
+ */
239
+ export class InvalidMetadataError extends DiError {
240
+ readonly code = "INVALID_METADATA";
241
+ readonly targetName: string;
242
+ readonly reason: string;
243
+
244
+ constructor(targetName: string, reason: string) {
245
+ super(
246
+ `MetadataReader returned invalid metadata for class '${targetName}': ${reason}. Check the reader bound to MetadataReaderToken or passed to Container.create().`,
247
+ );
248
+ this.targetName = targetName;
249
+ this.reason = reason;
250
+ }
251
+ }
252
+
144
253
  /**
145
254
  * @since 0.3.16-canary.0
146
255
  */
@@ -198,17 +307,29 @@ export class MissingScopeContextError extends DiError {
198
307
  }
199
308
 
200
309
  /**
310
+ * An `@inject` accessor initialized with no container context open.
311
+ *
312
+ * @remarks Carries the class and the accessor separately: a nameless class is possible (an anonymous
313
+ * class expression has an empty `name`), and a message that claims a class it does not have is what
314
+ * sent readers looking for a class called `clock`.
315
+ *
201
316
  * @since 0.3.16-canary.0
202
317
  */
203
318
  export class MissingContainerContextError extends DiError {
204
319
  readonly code = "MISSING_CONTAINER_CONTEXT";
205
- readonly targetName: string;
320
+ /** The class being constructed, or `undefined` when it has no readable name. */
321
+ readonly className: string | undefined;
322
+ readonly accessorName: string | symbol;
206
323
 
207
- constructor(targetName: string) {
324
+ constructor(className: string | undefined, accessorName: string | symbol) {
325
+ const accessor = `@inject accessor '${String(accessorName)}'`;
208
326
  super(
209
- `Class '${targetName}' has @inject accessor fields but was instantiated outside a container context. Resolve it via container.resolve(${targetName}) instead.`,
327
+ className === undefined
328
+ ? `An ${accessor} was initialized outside a container context. Resolve its class through a container, or open a context with runWithContainer().`
329
+ : `Class '${className}' has an ${accessor} but was constructed outside a container context. Resolve it via container.resolve(${className}), or open a context with runWithContainer().`,
210
330
  );
211
- this.targetName = targetName;
331
+ this.className = className;
332
+ this.accessorName = accessorName;
212
333
  }
213
334
  }
214
335
 
@@ -265,6 +386,29 @@ export class SelfBindingRequiresClassError extends DiError {
265
386
  }
266
387
  }
267
388
 
389
+ /**
390
+ * A decorator that acts on one instance was applied to a static member.
391
+ *
392
+ * @remarks Instance-only by construction: `@inject` resolves through the container active while an
393
+ * instance is built, and `@postConstruct`/`@preDestroy` bracket one instance's lifecycle. A static
394
+ * member belongs to the class, which no container constructs.
395
+ *
396
+ * @since 0.6.0
397
+ */
398
+ export class StaticMemberDecoratorError extends DiError {
399
+ readonly code = "STATIC_MEMBER_DECORATOR";
400
+ readonly decoratorName: string;
401
+ readonly memberName: string;
402
+
403
+ constructor(decoratorName: string, memberName: string) {
404
+ super(
405
+ `@${decoratorName}() applies to instance members only, and '${memberName}' is static. Move it to an instance member, or read the value from the container where the static member is used.`,
406
+ );
407
+ this.decoratorName = decoratorName;
408
+ this.memberName = memberName;
409
+ }
410
+ }
411
+
268
412
  /**
269
413
  * @since 0.3.16-canary.0
270
414
  */
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // Foundation types
2
2
  export type {
3
3
  ActivationHandler,
4
+ BindingConstraint,
4
5
  BindingIdentifier,
5
6
  BindingKind,
6
7
  BindingScope,
@@ -13,11 +14,15 @@ export type {
13
14
  ResolveOptions,
14
15
  ResolutionContext,
15
16
  TokenValue,
16
- } from "#/types";
17
+ } from "#/core/types";
17
18
 
18
19
  // Token
19
- export { token, tokenName, isToken } from "#/token";
20
- export type { Token } from "#/token";
20
+ export { token, tokenName } from "#/core/token";
21
+ export type { Token } from "#/core/token";
22
+
23
+ // Tag — the interned slot criteria a `whenTagged` and a resolve both take
24
+ export { coversTagKeys, NO_TAG_KEYS, tag, tagKeyMaskOf } from "#/core/tag";
25
+ export type { TagKey, TagKeyMask } from "#/core/tag";
21
26
 
22
27
  // Binding builders — types only
23
28
  export type {
@@ -30,15 +35,24 @@ export type {
30
35
  SingletonLifecycleBuilder,
31
36
  SlotConstrainedBuilder,
32
37
  TransientBindingBuilder,
33
- } from "#/binding";
38
+ } from "#/core/binding";
34
39
 
35
40
  // Container
36
41
  export { Container } from "#/container/container";
37
- export type { Container as ContainerInterface, ContainerStatic } from "#/container/container";
42
+ export type { Container as ContainerInterface, ContainerOptions, ContainerStatic } from "#/container/container";
43
+
44
+ // Ambient container — the context an `@inject` accessor initializer resolves from. `resolution/context`
45
+ // stays internal: it hands out resolver callbacks, not public values.
46
+ export { getActiveContainer, runWithContainer } from "#/ambient/active-container";
38
47
 
39
48
  // `effectiveBindingScope` is deliberately absent: it reads a `Binding`, which is internal, and no
40
49
  // public API hands one out. `BindingSnapshot.scope` and `GraphNode.scope` are the public answers.
41
- export { injectionSlotToResolveOptions, bindingSlotToResolveOptions } from "#/resolution/resolve-options";
50
+ export {
51
+ bindingSlotToResolveOptions,
52
+ injectionSlotToResolveOptions,
53
+ resolveOptionsForSlot,
54
+ } from "#/injection/resolve-options";
55
+ export type { DependencySlot } from "#/injection/resolve-options";
42
56
 
43
57
  // Introspection types
44
58
  export type { BindingSnapshot, ContainerSnapshot } from "#/introspection/inspector";
@@ -47,12 +61,13 @@ export type { BindingSnapshot, ContainerSnapshot } from "#/introspection/inspect
47
61
  export type { ContainerGraphJson, GraphEdge, GraphNode, GraphOptions } from "#/introspection/dependency-graph";
48
62
 
49
63
  // Module
50
- export { AsyncModule, isSyncModule, Module, SyncModule } from "#/module";
51
- export type { AsyncModuleBuilder, ModuleBuilder } from "#/module";
64
+ export { AsyncModule, isSyncModule, Module, SyncModule } from "#/core/module";
65
+ export type { AsyncModuleBuilder, ModuleBuilder } from "#/core/module";
52
66
 
53
67
  // Decorators
54
- export { inject, injectAll, isInjectionDescriptor, optional } from "#/decorators/inject";
55
- export type { InjectionDescriptor, InjectOptions } from "#/decorators/inject";
68
+ export { inject } from "#/decorators/inject";
69
+ export { injectAll, isInjectionDescriptor, optional } from "#/injection/descriptor";
70
+ export type { InjectionDescriptor, InjectOptions } from "#/injection/descriptor";
56
71
  export { injectable } from "#/decorators/injectable";
57
72
  export type { InjectableDependency, InjectableOptions } from "#/decorators/injectable";
58
73
  export { postConstruct, preDestroy } from "#/decorators/lifecycle-decorators";
@@ -61,9 +76,16 @@ export { postConstruct, preDestroy } from "#/decorators/lifecycle-decorators";
61
76
  export { createAutoRegisterRegistry } from "#/decorators/injectable";
62
77
  export type { AutoRegisterRegistry } from "#/decorators/injectable";
63
78
 
64
- // MetadataReader
79
+ // MetadataReader — everything a consumer needs to write one and pass it to Container.create()
65
80
  export { MetadataReaderToken } from "#/metadata/metadata-reader-token";
66
- export type { MetadataReader, MutableLifecycleMetadata } from "#/metadata/metadata-types";
81
+ export type {
82
+ ConstructorMetadata,
83
+ LifecycleMetadata,
84
+ MetadataReader,
85
+ MutableLifecycleMetadata,
86
+ ParamMetadata,
87
+ } from "#/metadata/metadata-types";
88
+ export { defaultMetadataReader, SymbolMetadataReader } from "#/metadata/symbol-metadata-reader";
67
89
 
68
90
  // Constraints — contextual injection predicates for .when()
69
91
  export {
@@ -77,7 +99,7 @@ export {
77
99
  whenParentNamed,
78
100
  whenParentTagged,
79
101
  whenParentTaggedAll,
80
- } from "#/resolution/constraints";
102
+ } from "#/resolution/select/constraints";
81
103
 
82
104
  // Errors
83
105
  export {
@@ -91,6 +113,7 @@ export {
91
113
  DiError,
92
114
  DisposedContainerError,
93
115
  InternalError,
116
+ InvalidMetadataError,
94
117
  MissingContainerContextError,
95
118
  MissingMetadataError,
96
119
  MissingScopeContextError,
@@ -98,10 +121,14 @@ export {
98
121
  RebindUnboundTokenError,
99
122
  ScopeViolationError,
100
123
  SelfBindingRequiresClassError,
124
+ StaticMemberDecoratorError,
101
125
  SyncDisposalNotSupportedError,
126
+ EmptyTagCriteriaError,
102
127
  TokenNotBoundError,
103
- } from "#/errors";
104
- export type { ScopeViolationDetails } from "#/errors";
128
+ UnreachableConstraintError,
129
+ UnreachableLifecycleHookError,
130
+ } from "#/errors/errors";
131
+ export type { ScopeViolationDetails } from "#/errors/errors";
105
132
 
106
133
  // Graph adapters — render `generateDependencyGraph()` output for common viewers
107
134
  export { toDotGraph } from "#/introspection/graph-adapters/dot";
@@ -109,3 +136,4 @@ export { toCytoscapeGraph } from "#/introspection/graph-adapters/cytoscape";
109
136
  export type { CytoscapeEdge, CytoscapeElements, CytoscapeNode } from "#/introspection/graph-adapters/cytoscape";
110
137
  export { toReactFlowGraph } from "#/introspection/graph-adapters/reactflow";
111
138
  export type { ReactFlowEdge, ReactFlowGraph, ReactFlowNode } from "#/introspection/graph-adapters/reactflow";
139
+ export { toMermaidGraph } from "#/introspection/graph-adapters/mermaid";
@@ -0,0 +1,206 @@
1
+ /** The one shape every declared dependency is normalised to, whatever channel declared it. */
2
+ import type { Token } from "#/core/token";
3
+ import type { BindingTag, Constructor, TokenValue } from "#/core/types";
4
+ import type { DependencySlot } from "#/injection/resolve-options";
5
+
6
+ /**
7
+ * @since 0.3.16-canary.0
8
+ */
9
+ export interface InjectOptions {
10
+ name?: string | undefined;
11
+ /**
12
+ * Single-tag shorthand, equivalent to listing the one pair in `tags`.
13
+ *
14
+ * @remarks Folded into `tags` when the descriptor is built, so nothing downstream sees two
15
+ * spellings of one request. Giving both is a request for every pair across the two.
16
+ */
17
+ tag?: BindingTag | undefined;
18
+ tags?: ReadonlyArray<BindingTag> | undefined;
19
+ }
20
+
21
+ /**
22
+ * One dependency a `toResolved` factory or an `@inject` accessor declares.
23
+ *
24
+ * @remarks Extends {@link DependencySlot} so the two dependency sources stay literally one shape,
25
+ * narrowing only `token` to carry the value type a factory's parameter is checked against.
26
+ *
27
+ * @since 0.3.16-canary.0
28
+ */
29
+ export interface InjectionDescriptor<out Value = unknown> extends DependencySlot {
30
+ readonly token: Token<Value> | Constructor<Value>;
31
+ }
32
+
33
+ /**
34
+ * @since 0.3.16-canary.0
35
+ */
36
+ export type InjectableDependency<Value = unknown> = Token<Value> | Constructor<Value> | InjectionDescriptor<Value>;
37
+
38
+ /**
39
+ * The value a factory receives for one declared dependency.
40
+ *
41
+ * @remarks `optional()` and `injectAll()` fold their effect into the descriptor's own type
42
+ * parameter, so reading it back is enough; bare tokens fall through to {@link TokenValue}.
43
+ *
44
+ * @since 0.5.0-canary.7
45
+ */
46
+ export type ResolvedDependencyValue<Dependency> = Dependency extends { readonly multi: true }
47
+ ? Array<DescribedValue<Dependency>>
48
+ : Dependency extends { readonly optional: true }
49
+ ? DescribedValue<Dependency> | undefined
50
+ : DescribedValue<Dependency>;
51
+
52
+ /**
53
+ * The value a dependency's own type parameter carries, before its flags are read.
54
+ *
55
+ * @remarks Split out because a hand-written descriptor states its flags and its value type
56
+ * separately, and only the flags are load-bearing: `{ token: Plugin, multi: true }` says `Plugin`
57
+ * and delivers `Array<Plugin>`. `injectAll()` and `optional()` already fold their effect in, so the
58
+ * flags find an array or an optional there and leave it alone.
59
+ */
60
+ type DescribedValue<Dependency> = Dependency extends InjectionDescriptor<infer Value> ? Value : TokenValue<Dependency>;
61
+
62
+ /**
63
+ * @since 0.3.16-canary.0
64
+ */
65
+ export function isInjectionDescriptor(value: unknown): value is InjectionDescriptor {
66
+ if (value === null || value === undefined) {
67
+ return false;
68
+ }
69
+ const type = typeof value;
70
+ // inject() returns a function (dual-role), so must check both object and function
71
+ if (type !== "object" && type !== "function") {
72
+ return false;
73
+ }
74
+ return (
75
+ "token" in (value as object) &&
76
+ "optional" in (value as object) &&
77
+ "multi" in (value as object) &&
78
+ typeof (value as InjectionDescriptor).optional === "boolean" &&
79
+ typeof (value as InjectionDescriptor).multi === "boolean"
80
+ );
81
+ }
82
+
83
+ /**
84
+ * @since 0.3.16-canary.0
85
+ */
86
+ export function normalizeToDescriptor(dependency: InjectableDependency): InjectionDescriptor {
87
+ if (isInjectionDescriptor(dependency)) {
88
+ return materializeInjectionDescriptor(dependency);
89
+ }
90
+ return { token: dependency, optional: false, multi: false };
91
+ }
92
+
93
+ /**
94
+ * Dual-role `inject()` values are functions: [[Function]].name must not be treated as a DI slot name.
95
+ * Only enumerable own `name` / `tags` from `Object.defineProperties` are real injection options.
96
+ */
97
+ function materializeInjectionDescriptor(dependency: InjectionDescriptor): InjectionDescriptor {
98
+ if (typeof dependency !== "function") {
99
+ return dependency;
100
+ }
101
+ const dualRole = dependency as InjectionDescriptor & ((...args: Array<unknown>) => unknown);
102
+ const base: Pick<InjectionDescriptor, "token" | "optional" | "multi"> = {
103
+ token: dualRole.token,
104
+ optional: dualRole.optional,
105
+ multi: dualRole.multi,
106
+ };
107
+ const nameDesc = Object.getOwnPropertyDescriptor(dualRole, "name");
108
+ const tagsDesc = Object.getOwnPropertyDescriptor(dualRole, "tags");
109
+ const explicitName = nameDesc?.enumerable === true && typeof nameDesc.value === "string" ? nameDesc.value : undefined;
110
+ // Annotated because `PropertyDescriptor.value` is `any`, and an `any` reaching the cast below
111
+ // would make it look checked when nothing checked it.
112
+ const explicitTags: unknown = tagsDesc?.enumerable === true ? tagsDesc.value : undefined;
113
+
114
+ if (explicitName !== undefined && explicitTags !== undefined) {
115
+ return {
116
+ ...base,
117
+ name: explicitName,
118
+ tags: explicitTags as NonNullable<InjectionDescriptor["tags"]>,
119
+ };
120
+ }
121
+ if (explicitName !== undefined) {
122
+ return { ...base, name: explicitName };
123
+ }
124
+ if (explicitTags !== undefined) {
125
+ return { ...base, tags: explicitTags as NonNullable<InjectionDescriptor["tags"]> };
126
+ }
127
+ return base;
128
+ }
129
+
130
+ /**
131
+ * The one tag list a request carries, with the single-tag shorthand folded in.
132
+ *
133
+ * @remarks Folding here is what keeps `tag` from reaching `InjectionDescriptor` and everything
134
+ * derived from it, so one request never has two spellings past this point.
135
+ */
136
+ function requestedTagsOf(options: InjectOptions | undefined): ReadonlyArray<BindingTag> | undefined {
137
+ const shorthand = options?.tag;
138
+ const listed = options?.tags;
139
+ if (shorthand === undefined) {
140
+ return listed;
141
+ }
142
+ return listed === undefined || listed.length === 0 ? [shorthand] : [shorthand, ...listed];
143
+ }
144
+
145
+ function withOptions<DescValue>(
146
+ base: Pick<InjectionDescriptor<DescValue>, "token" | "optional" | "multi">,
147
+ options: InjectOptions | undefined,
148
+ ): InjectionDescriptor<DescValue> {
149
+ const tags = requestedTagsOf(options);
150
+ if (options?.name !== undefined && tags !== undefined) {
151
+ return { ...base, name: options.name, tags };
152
+ }
153
+ if (options?.name !== undefined) {
154
+ return { ...base, name: options.name };
155
+ }
156
+ if (tags !== undefined) {
157
+ return { ...base, tags };
158
+ }
159
+ return base;
160
+ }
161
+
162
+ /**
163
+ * The descriptor half of `inject()`, shared with the accessor decorator that wraps it.
164
+ *
165
+ * @since 0.6.0
166
+ */
167
+ export function buildInjectionDescriptor<Value>(
168
+ token: Token<Value> | Constructor<Value>,
169
+ options?: InjectOptions,
170
+ ): InjectionDescriptor<Value> {
171
+ return withOptions({ token, optional: false, multi: false }, options);
172
+ }
173
+
174
+ /**
175
+ * @since 0.3.16-canary.0
176
+ */
177
+ export function optional<Value>(
178
+ token: Token<Value> | Constructor<Value>,
179
+ options?: InjectOptions,
180
+ ): InjectionDescriptor<Value | undefined> {
181
+ return withOptions(
182
+ {
183
+ token: token as Token<Value | undefined> | Constructor<Value | undefined>,
184
+ optional: true,
185
+ multi: false,
186
+ },
187
+ options,
188
+ );
189
+ }
190
+
191
+ /**
192
+ * @since 0.3.16-canary.0
193
+ */
194
+ export function injectAll<Value>(
195
+ token: Token<Value> | Constructor<Value>,
196
+ options?: InjectOptions,
197
+ ): InjectionDescriptor<Array<Value>> {
198
+ return withOptions(
199
+ {
200
+ token: token as Token<Array<Value>> | Constructor<Array<Value>>,
201
+ optional: false,
202
+ multi: true,
203
+ },
204
+ options,
205
+ );
206
+ }
@@ -0,0 +1,144 @@
1
+ import type { BindingTag } from "#/core/tag";
2
+ import type { Token } from "#/core/token";
3
+ import type { Constructor, ResolveOptions } from "#/core/types";
4
+
5
+ /**
6
+ * What one resolvable dependency declares.
7
+ *
8
+ * @remarks Both dependency sources — a constructor's `ParamMetadata` and a `toResolved`
9
+ * `InjectionDescriptor` — are this shape, which is why one resolve routine serves both.
10
+ *
11
+ * @since 0.5.0-canary.9
12
+ */
13
+ export interface DependencySlot {
14
+ readonly token: Token<unknown> | Constructor;
15
+ readonly optional: boolean;
16
+ readonly multi: boolean;
17
+ readonly name?: string | undefined;
18
+ readonly tags?: ReadonlyArray<BindingTag> | undefined;
19
+ }
20
+
21
+ /**
22
+ * A request whose only criterion is a name — the shape the registry has a direct index for.
23
+ *
24
+ * @since 0.5.0-canary.9
25
+ */
26
+ export function isNameOnlyOptions(options: ResolveOptions): options is ResolveOptions & { name: string } {
27
+ return (
28
+ options.name !== undefined && options.tag === undefined && (options.tags === undefined || options.tags.length === 0)
29
+ );
30
+ }
31
+
32
+ /**
33
+ * The lone tag of a request that asks for exactly one, written either way — the shape the registry
34
+ * has a direct tag index for.
35
+ *
36
+ * @remarks Both spellings answer here, so the index is not something one of them silently misses.
37
+ *
38
+ * @since 0.5.0-canary.9
39
+ */
40
+ export function singleTagOnlyOf(options: ResolveOptions): BindingTag | undefined {
41
+ if (options.name !== undefined) {
42
+ return undefined;
43
+ }
44
+ const listed = options.tags;
45
+ const shorthand = options.tag;
46
+ if (shorthand !== undefined) {
47
+ // Both sources present means the request carries two tags, which no single-tag index can answer.
48
+ return listed === undefined || listed.length === 0 ? shorthand : undefined;
49
+ }
50
+ return listed !== undefined && listed.length === 1 ? listed[0] : undefined;
51
+ }
52
+
53
+ /** Shared core: build a ResolveOptions from already-normalised name + tags. */
54
+ function buildOptions(
55
+ name: string | undefined,
56
+ tags: ReadonlyArray<BindingTag> | undefined,
57
+ ): ResolveOptions | undefined {
58
+ if (name === undefined && tags === undefined) {
59
+ return undefined;
60
+ }
61
+ const options: ResolveOptions = {};
62
+ if (name !== undefined) {
63
+ options.name = name;
64
+ }
65
+ if (tags !== undefined) {
66
+ options.tags = tags;
67
+ }
68
+ return options;
69
+ }
70
+
71
+ /**
72
+ * Builds a {@link ResolveOptions} safe for `exactOptionalPropertyTypes`:
73
+ * omits keys instead of assigning `undefined`.
74
+ *
75
+ * @since 0.3.16-canary.0
76
+ */
77
+ export function injectionSlotToResolveOptions(
78
+ injectionSlot: Pick<DependencySlot, "name" | "tags">,
79
+ ): ResolveOptions | undefined {
80
+ return buildOptions(injectionSlot.name, injectionSlot.tags);
81
+ }
82
+
83
+ /** Where a slot's derived options are memoized, so the same object is handed out every resolve. */
84
+ const MEMOIZED_RESOLVE_OPTIONS: unique symbol = Symbol("di:resolve-options");
85
+
86
+ interface SlotWithMemoizedOptions {
87
+ [MEMOIZED_RESOLVE_OPTIONS]?: ResolveOptions;
88
+ }
89
+
90
+ /**
91
+ * The options a dependency resolves with — one object per slot, since a slot's criteria are fixed
92
+ * when it is declared.
93
+ *
94
+ * @remarks A slot carrying no criterion answers from its two fields, so the common shape never
95
+ * reaches the memo.
96
+ *
97
+ * @since 0.6.0
98
+ */
99
+ export function resolveOptionsForSlot(injectionSlot: DependencySlot): ResolveOptions | undefined {
100
+ const { name, tags } = injectionSlot;
101
+ if (name === undefined && tags === undefined) {
102
+ return undefined;
103
+ }
104
+ const slot = injectionSlot as SlotWithMemoizedOptions;
105
+ const memoized = slot[MEMOIZED_RESOLVE_OPTIONS];
106
+ return memoized ?? memoizeResolveOptions(slot, name, tags);
107
+ }
108
+
109
+ /**
110
+ * Builds, freezes and stores a slot's options on first use.
111
+ *
112
+ * @remarks Frozen because one object answers every resolve of the slot and a constraint predicate is
113
+ * handed it. Split out for the `try`: in the caller it would cost the early return its inlining.
114
+ */
115
+ function memoizeResolveOptions(
116
+ slot: SlotWithMemoizedOptions,
117
+ name: string | undefined,
118
+ tags: ReadonlyArray<BindingTag> | undefined,
119
+ ): ResolveOptions {
120
+ const built = Object.freeze(buildOptions(name, tags) as ResolveOptions);
121
+ try {
122
+ slot[MEMOIZED_RESOLVE_OPTIONS] = built;
123
+ } catch {
124
+ // A frozen slot rebuilds on every hop rather than throwing.
125
+ }
126
+ return built;
127
+ }
128
+
129
+ /**
130
+ * Resolve options derived from a binding slot (tags may be empty; omits when nothing to match).
131
+ *
132
+ * @remarks Takes the slot structurally rather than as `BindingSlot`, so the slot on a public
133
+ * `BindingSnapshot` — where `name` is an optional property, not a required one holding `undefined` —
134
+ * is accepted by the same call.
135
+ *
136
+ * @since 0.3.16-canary.0
137
+ */
138
+ export function bindingSlotToResolveOptions(bindingSlot: {
139
+ readonly name?: string | undefined;
140
+ readonly tags?: ReadonlyArray<BindingTag> | undefined;
141
+ }): ResolveOptions | undefined {
142
+ const tags = bindingSlot.tags;
143
+ return buildOptions(bindingSlot.name, tags !== undefined && tags.length > 0 ? tags : undefined);
144
+ }