@codefast/di 0.8.0 → 0.8.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 (218) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +242 -159
  3. package/dist/ambient/active-container.d.ts +1 -2
  4. package/dist/ambient/active-container.js +1 -2
  5. package/dist/container/binding-builders.d.ts +1 -2
  6. package/dist/container/binding-builders.js +1 -2
  7. package/dist/container/container.d.ts +1 -2
  8. package/dist/container/container.js +1 -2
  9. package/dist/core/binding-scope.d.ts +1 -2
  10. package/dist/core/binding-scope.js +1 -2
  11. package/dist/core/binding.d.ts +1 -2
  12. package/dist/core/binding.js +1 -2
  13. package/dist/core/constraint-requirement.d.ts +1 -2
  14. package/dist/core/constraint-requirement.js +1 -2
  15. package/dist/core/constructor-type.d.ts +1 -2
  16. package/dist/core/constructor-type.js +1 -2
  17. package/dist/core/map-upsert.d.ts +1 -2
  18. package/dist/core/map-upsert.js +1 -2
  19. package/dist/core/module.d.ts +1 -2
  20. package/dist/core/module.js +1 -2
  21. package/dist/core/registry.d.ts +1 -2
  22. package/dist/core/registry.js +1 -2
  23. package/dist/core/tag.d.ts +1 -2
  24. package/dist/core/tag.js +1 -2
  25. package/dist/core/token.d.ts +1 -2
  26. package/dist/core/token.js +1 -2
  27. package/dist/core/types.d.ts +1 -2
  28. package/dist/core/types.js +1 -2
  29. package/dist/decorators/inject.d.ts +1 -2
  30. package/dist/decorators/inject.js +1 -2
  31. package/dist/decorators/injectable.d.ts +1 -2
  32. package/dist/decorators/injectable.js +1 -2
  33. package/dist/decorators/lifecycle-decorators.d.ts +1 -2
  34. package/dist/decorators/lifecycle-decorators.js +1 -2
  35. package/dist/errors/diagnostics.d.ts +1 -2
  36. package/dist/errors/diagnostics.js +1 -2
  37. package/dist/errors/errors.d.ts +1 -2
  38. package/dist/errors/errors.js +1 -2
  39. package/dist/index.d.ts +1 -2
  40. package/dist/index.js +1 -2
  41. package/dist/injection/descriptor.d.ts +1 -2
  42. package/dist/injection/descriptor.js +1 -2
  43. package/dist/injection/resolve-options.d.ts +1 -2
  44. package/dist/injection/resolve-options.js +1 -2
  45. package/dist/introspection/dependency-graph.d.ts +1 -2
  46. package/dist/introspection/dependency-graph.js +1 -2
  47. package/dist/introspection/graph-adapters/cytoscape.d.ts +1 -2
  48. package/dist/introspection/graph-adapters/cytoscape.js +1 -2
  49. package/dist/introspection/graph-adapters/dot.d.ts +1 -2
  50. package/dist/introspection/graph-adapters/dot.js +1 -2
  51. package/dist/introspection/graph-adapters/mermaid.d.ts +1 -2
  52. package/dist/introspection/graph-adapters/mermaid.js +1 -2
  53. package/dist/introspection/graph-adapters/reactflow.d.ts +1 -2
  54. package/dist/introspection/graph-adapters/reactflow.js +1 -2
  55. package/dist/introspection/inspector.d.ts +1 -2
  56. package/dist/introspection/inspector.js +1 -2
  57. package/dist/lifecycle/lifecycle-manager.d.ts +1 -2
  58. package/dist/lifecycle/lifecycle-manager.js +1 -2
  59. package/dist/lifecycle/scope-manager.d.ts +1 -2
  60. package/dist/lifecycle/scope-manager.js +1 -2
  61. package/dist/metadata/metadata-keys.d.ts +1 -2
  62. package/dist/metadata/metadata-keys.js +1 -2
  63. package/dist/metadata/metadata-reader-token.d.ts +1 -2
  64. package/dist/metadata/metadata-reader-token.js +1 -2
  65. package/dist/metadata/metadata-types.d.ts +1 -2
  66. package/dist/metadata/metadata-types.js +1 -2
  67. package/dist/metadata/symbol-metadata-reader.d.ts +1 -2
  68. package/dist/metadata/symbol-metadata-reader.js +1 -2
  69. package/dist/metadata/verifying-metadata-reader.d.ts +1 -2
  70. package/dist/metadata/verifying-metadata-reader.js +1 -2
  71. package/dist/resolution/cache/activation-need.d.ts +1 -2
  72. package/dist/resolution/cache/activation-need.js +1 -2
  73. package/dist/resolution/cache/binding-lookup-cache.d.ts +1 -2
  74. package/dist/resolution/cache/binding-lookup-cache.js +1 -2
  75. package/dist/resolution/cache/class-introspector.d.ts +1 -2
  76. package/dist/resolution/cache/class-introspector.js +1 -2
  77. package/dist/resolution/context.d.ts +1 -2
  78. package/dist/resolution/context.js +1 -2
  79. package/dist/resolution/path/resolution-path.d.ts +1 -2
  80. package/dist/resolution/path/resolution-path.js +1 -2
  81. package/dist/resolution/plan/instantiation-plan.d.ts +1 -2
  82. package/dist/resolution/plan/instantiation-plan.js +1 -2
  83. package/dist/resolution/resolver.d.ts +1 -2
  84. package/dist/resolution/resolver.js +1 -2
  85. package/dist/resolution/select/binding-select.d.ts +1 -2
  86. package/dist/resolution/select/binding-select.js +1 -2
  87. package/dist/resolution/select/constraints.d.ts +1 -2
  88. package/dist/resolution/select/constraints.js +1 -2
  89. package/package.json +6 -58
  90. package/dist/ambient/active-container.d.ts.map +0 -1
  91. package/dist/ambient/active-container.js.map +0 -1
  92. package/dist/container/binding-builders.d.ts.map +0 -1
  93. package/dist/container/binding-builders.js.map +0 -1
  94. package/dist/container/container.d.ts.map +0 -1
  95. package/dist/container/container.js.map +0 -1
  96. package/dist/core/binding-scope.d.ts.map +0 -1
  97. package/dist/core/binding-scope.js.map +0 -1
  98. package/dist/core/binding.d.ts.map +0 -1
  99. package/dist/core/binding.js.map +0 -1
  100. package/dist/core/constraint-requirement.d.ts.map +0 -1
  101. package/dist/core/constraint-requirement.js.map +0 -1
  102. package/dist/core/constructor-type.d.ts.map +0 -1
  103. package/dist/core/constructor-type.js.map +0 -1
  104. package/dist/core/map-upsert.d.ts.map +0 -1
  105. package/dist/core/map-upsert.js.map +0 -1
  106. package/dist/core/module.d.ts.map +0 -1
  107. package/dist/core/module.js.map +0 -1
  108. package/dist/core/registry.d.ts.map +0 -1
  109. package/dist/core/registry.js.map +0 -1
  110. package/dist/core/tag.d.ts.map +0 -1
  111. package/dist/core/tag.js.map +0 -1
  112. package/dist/core/token.d.ts.map +0 -1
  113. package/dist/core/token.js.map +0 -1
  114. package/dist/core/types.d.ts.map +0 -1
  115. package/dist/core/types.js.map +0 -1
  116. package/dist/decorators/inject.d.ts.map +0 -1
  117. package/dist/decorators/inject.js.map +0 -1
  118. package/dist/decorators/injectable.d.ts.map +0 -1
  119. package/dist/decorators/injectable.js.map +0 -1
  120. package/dist/decorators/lifecycle-decorators.d.ts.map +0 -1
  121. package/dist/decorators/lifecycle-decorators.js.map +0 -1
  122. package/dist/errors/diagnostics.d.ts.map +0 -1
  123. package/dist/errors/diagnostics.js.map +0 -1
  124. package/dist/errors/errors.d.ts.map +0 -1
  125. package/dist/errors/errors.js.map +0 -1
  126. package/dist/index.d.ts.map +0 -1
  127. package/dist/index.js.map +0 -1
  128. package/dist/injection/descriptor.d.ts.map +0 -1
  129. package/dist/injection/descriptor.js.map +0 -1
  130. package/dist/injection/resolve-options.d.ts.map +0 -1
  131. package/dist/injection/resolve-options.js.map +0 -1
  132. package/dist/introspection/dependency-graph.d.ts.map +0 -1
  133. package/dist/introspection/dependency-graph.js.map +0 -1
  134. package/dist/introspection/graph-adapters/cytoscape.d.ts.map +0 -1
  135. package/dist/introspection/graph-adapters/cytoscape.js.map +0 -1
  136. package/dist/introspection/graph-adapters/dot.d.ts.map +0 -1
  137. package/dist/introspection/graph-adapters/dot.js.map +0 -1
  138. package/dist/introspection/graph-adapters/mermaid.d.ts.map +0 -1
  139. package/dist/introspection/graph-adapters/mermaid.js.map +0 -1
  140. package/dist/introspection/graph-adapters/reactflow.d.ts.map +0 -1
  141. package/dist/introspection/graph-adapters/reactflow.js.map +0 -1
  142. package/dist/introspection/inspector.d.ts.map +0 -1
  143. package/dist/introspection/inspector.js.map +0 -1
  144. package/dist/lifecycle/lifecycle-manager.d.ts.map +0 -1
  145. package/dist/lifecycle/lifecycle-manager.js.map +0 -1
  146. package/dist/lifecycle/scope-manager.d.ts.map +0 -1
  147. package/dist/lifecycle/scope-manager.js.map +0 -1
  148. package/dist/metadata/metadata-keys.d.ts.map +0 -1
  149. package/dist/metadata/metadata-keys.js.map +0 -1
  150. package/dist/metadata/metadata-reader-token.d.ts.map +0 -1
  151. package/dist/metadata/metadata-reader-token.js.map +0 -1
  152. package/dist/metadata/metadata-types.d.ts.map +0 -1
  153. package/dist/metadata/metadata-types.js.map +0 -1
  154. package/dist/metadata/symbol-metadata-reader.d.ts.map +0 -1
  155. package/dist/metadata/symbol-metadata-reader.js.map +0 -1
  156. package/dist/metadata/verifying-metadata-reader.d.ts.map +0 -1
  157. package/dist/metadata/verifying-metadata-reader.js.map +0 -1
  158. package/dist/resolution/cache/activation-need.d.ts.map +0 -1
  159. package/dist/resolution/cache/activation-need.js.map +0 -1
  160. package/dist/resolution/cache/binding-lookup-cache.d.ts.map +0 -1
  161. package/dist/resolution/cache/binding-lookup-cache.js.map +0 -1
  162. package/dist/resolution/cache/class-introspector.d.ts.map +0 -1
  163. package/dist/resolution/cache/class-introspector.js.map +0 -1
  164. package/dist/resolution/context.d.ts.map +0 -1
  165. package/dist/resolution/context.js.map +0 -1
  166. package/dist/resolution/path/resolution-path.d.ts.map +0 -1
  167. package/dist/resolution/path/resolution-path.js.map +0 -1
  168. package/dist/resolution/plan/instantiation-plan.d.ts.map +0 -1
  169. package/dist/resolution/plan/instantiation-plan.js.map +0 -1
  170. package/dist/resolution/resolver.d.ts.map +0 -1
  171. package/dist/resolution/resolver.js.map +0 -1
  172. package/dist/resolution/select/binding-select.d.ts.map +0 -1
  173. package/dist/resolution/select/binding-select.js.map +0 -1
  174. package/dist/resolution/select/constraints.d.ts.map +0 -1
  175. package/dist/resolution/select/constraints.js.map +0 -1
  176. package/src/ambient/active-container.ts +0 -69
  177. package/src/container/binding-builders.ts +0 -337
  178. package/src/container/container.ts +0 -972
  179. package/src/core/binding-scope.ts +0 -15
  180. package/src/core/binding.ts +0 -505
  181. package/src/core/constraint-requirement.ts +0 -94
  182. package/src/core/constructor-type.ts +0 -18
  183. package/src/core/map-upsert.ts +0 -53
  184. package/src/core/module.ts +0 -117
  185. package/src/core/registry.ts +0 -319
  186. package/src/core/tag.ts +0 -212
  187. package/src/core/token.ts +0 -31
  188. package/src/core/types.ts +0 -149
  189. package/src/decorators/inject.ts +0 -101
  190. package/src/decorators/injectable.ts +0 -134
  191. package/src/decorators/lifecycle-decorators.ts +0 -43
  192. package/src/errors/diagnostics.ts +0 -45
  193. package/src/errors/errors.ts +0 -499
  194. package/src/index.ts +0 -139
  195. package/src/injection/descriptor.ts +0 -218
  196. package/src/injection/resolve-options.ts +0 -201
  197. package/src/introspection/dependency-graph.ts +0 -296
  198. package/src/introspection/graph-adapters/cytoscape.ts +0 -78
  199. package/src/introspection/graph-adapters/dot.ts +0 -32
  200. package/src/introspection/graph-adapters/mermaid.ts +0 -61
  201. package/src/introspection/graph-adapters/reactflow.ts +0 -84
  202. package/src/introspection/inspector.ts +0 -134
  203. package/src/lifecycle/lifecycle-manager.ts +0 -272
  204. package/src/lifecycle/scope-manager.ts +0 -158
  205. package/src/metadata/metadata-keys.ts +0 -28
  206. package/src/metadata/metadata-reader-token.ts +0 -10
  207. package/src/metadata/metadata-types.ts +0 -57
  208. package/src/metadata/symbol-metadata-reader.ts +0 -126
  209. package/src/metadata/verifying-metadata-reader.ts +0 -41
  210. package/src/resolution/cache/activation-need.ts +0 -87
  211. package/src/resolution/cache/binding-lookup-cache.ts +0 -169
  212. package/src/resolution/cache/class-introspector.ts +0 -256
  213. package/src/resolution/context.ts +0 -331
  214. package/src/resolution/path/resolution-path.ts +0 -167
  215. package/src/resolution/plan/instantiation-plan.ts +0 -592
  216. package/src/resolution/resolver.ts +0 -1654
  217. package/src/resolution/select/binding-select.ts +0 -207
  218. package/src/resolution/select/constraints.ts +0 -146
@@ -1,218 +0,0 @@
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
- * Slot-selection options — a name and tags — a declared dependency narrows its binding with.
8
- *
9
- * @since 0.3.16-canary.0
10
- */
11
- export interface InjectOptions {
12
- name?: string | undefined;
13
- /**
14
- * Single-tag shorthand, equivalent to listing the one pair in `tags`.
15
- *
16
- * @remarks Folded into `tags` when the descriptor is built, so nothing downstream sees two
17
- * spellings of one request. Giving both is a request for every pair across the two.
18
- */
19
- tag?: BindingTag | undefined;
20
- tags?: ReadonlyArray<BindingTag> | undefined;
21
- }
22
-
23
- /**
24
- * One dependency a `toResolved` factory or an `@inject` accessor declares.
25
- *
26
- * @remarks Extends {@link DependencySlot} so the two dependency sources stay literally one shape,
27
- * narrowing only `token` to carry the value type a factory's parameter is checked against.
28
- *
29
- * @since 0.3.16-canary.0
30
- */
31
- export interface InjectionDescriptor<out Value = unknown> extends DependencySlot {
32
- readonly token: Token<Value> | Constructor<Value>;
33
- }
34
-
35
- /**
36
- * A dependency as declared: a bare token, a class, or a full injection descriptor.
37
- *
38
- * @since 0.3.16-canary.0
39
- */
40
- export type InjectableDependency<Value = unknown> = Token<Value> | Constructor<Value> | InjectionDescriptor<Value>;
41
-
42
- /**
43
- * The value a factory receives for one declared dependency.
44
- *
45
- * @remarks `optional()` and `injectAll()` fold their effect into the descriptor's own type
46
- * parameter, so reading it back is enough; bare tokens fall through to {@link TokenValue}.
47
- *
48
- * @since 0.5.0-canary.7
49
- */
50
- export type ResolvedDependencyValue<Dependency> = Dependency extends { readonly multi: true }
51
- ? Array<DescribedValue<Dependency>>
52
- : Dependency extends { readonly optional: true }
53
- ? DescribedValue<Dependency> | undefined
54
- : DescribedValue<Dependency>;
55
-
56
- /**
57
- * The value a dependency's own type parameter carries, before its flags are read.
58
- *
59
- * @remarks Split out because a hand-written descriptor states its flags and its value type
60
- * separately, and only the flags are load-bearing: `{ token: Plugin, multi: true }` says `Plugin`
61
- * and delivers `Array<Plugin>`. `injectAll()` and `optional()` already fold their effect in, so the
62
- * flags find an array or an optional there and leave it alone.
63
- */
64
- type DescribedValue<Dependency> = Dependency extends InjectionDescriptor<infer Value> ? Value : TokenValue<Dependency>;
65
-
66
- /**
67
- * Returns whether the value is an `InjectionDescriptor`, dual-role `inject()` functions included.
68
- *
69
- * @since 0.3.16-canary.0
70
- */
71
- export function isInjectionDescriptor(value: unknown): value is InjectionDescriptor {
72
- if (value === null || value === undefined) {
73
- return false;
74
- }
75
- const type = typeof value;
76
- // inject() returns a function (dual-role), so must check both object and function
77
- if (type !== "object" && type !== "function") {
78
- return false;
79
- }
80
- return (
81
- "token" in (value as object) &&
82
- "optional" in (value as object) &&
83
- "multi" in (value as object) &&
84
- typeof (value as InjectionDescriptor).optional === "boolean" &&
85
- typeof (value as InjectionDescriptor).multi === "boolean"
86
- );
87
- }
88
-
89
- /**
90
- * Normalises a declared dependency to a plain `InjectionDescriptor`.
91
- *
92
- * @since 0.3.16-canary.0
93
- */
94
- export function normalizeToDescriptor(dependency: InjectableDependency): InjectionDescriptor {
95
- if (isInjectionDescriptor(dependency)) {
96
- return materializeInjectionDescriptor(dependency);
97
- }
98
- return { token: dependency, optional: false, multi: false };
99
- }
100
-
101
- /**
102
- * Dual-role `inject()` values are functions: [[Function]].name must not be treated as a DI slot name.
103
- * Only enumerable own `name` / `tags` from `Object.defineProperties` are real injection options.
104
- */
105
- function materializeInjectionDescriptor(dependency: InjectionDescriptor): InjectionDescriptor {
106
- if (typeof dependency !== "function") {
107
- return dependency;
108
- }
109
- const dualRole = dependency as InjectionDescriptor & ((...args: Array<unknown>) => unknown);
110
- const base: Pick<InjectionDescriptor, "token" | "optional" | "multi"> = {
111
- token: dualRole.token,
112
- optional: dualRole.optional,
113
- multi: dualRole.multi,
114
- };
115
- const nameDesc = Object.getOwnPropertyDescriptor(dualRole, "name");
116
- const tagsDesc = Object.getOwnPropertyDescriptor(dualRole, "tags");
117
- const explicitName = nameDesc?.enumerable === true && typeof nameDesc.value === "string" ? nameDesc.value : undefined;
118
- // Annotated because `PropertyDescriptor.value` is `any`, and an `any` reaching the cast below
119
- // would make it look checked when nothing checked it.
120
- const explicitTags: unknown = tagsDesc?.enumerable === true ? tagsDesc.value : undefined;
121
-
122
- if (explicitName !== undefined && explicitTags !== undefined) {
123
- return {
124
- ...base,
125
- name: explicitName,
126
- tags: explicitTags as NonNullable<InjectionDescriptor["tags"]>,
127
- };
128
- }
129
- if (explicitName !== undefined) {
130
- return { ...base, name: explicitName };
131
- }
132
- if (explicitTags !== undefined) {
133
- return { ...base, tags: explicitTags as NonNullable<InjectionDescriptor["tags"]> };
134
- }
135
- return base;
136
- }
137
-
138
- /**
139
- * The one tag list a request carries, with the single-tag shorthand folded in.
140
- *
141
- * @remarks Folding here is what keeps `tag` from reaching `InjectionDescriptor` and everything
142
- * derived from it, so one request never has two spellings past this point.
143
- */
144
- function requestedTagsOf(options: InjectOptions | undefined): ReadonlyArray<BindingTag> | undefined {
145
- const shorthand = options?.tag;
146
- const listed = options?.tags;
147
- if (shorthand === undefined) {
148
- return listed;
149
- }
150
- return listed === undefined || listed.length === 0 ? [shorthand] : [shorthand, ...listed];
151
- }
152
-
153
- function withOptions<DescValue>(
154
- base: Pick<InjectionDescriptor<DescValue>, "token" | "optional" | "multi">,
155
- options: InjectOptions | undefined,
156
- ): InjectionDescriptor<DescValue> {
157
- const tags = requestedTagsOf(options);
158
- if (options?.name !== undefined && tags !== undefined) {
159
- return { ...base, name: options.name, tags };
160
- }
161
- if (options?.name !== undefined) {
162
- return { ...base, name: options.name };
163
- }
164
- if (tags !== undefined) {
165
- return { ...base, tags };
166
- }
167
- return base;
168
- }
169
-
170
- /**
171
- * The descriptor half of `inject()`, shared with the accessor decorator that wraps it.
172
- *
173
- * @since 0.6.0
174
- */
175
- export function buildInjectionDescriptor<Value>(
176
- token: Token<Value> | Constructor<Value>,
177
- options?: InjectOptions,
178
- ): InjectionDescriptor<Value> {
179
- return withOptions({ token, optional: false, multi: false }, options);
180
- }
181
-
182
- /**
183
- * Creates a descriptor that resolves to `undefined` instead of throwing when no binding matches.
184
- *
185
- * @since 0.3.16-canary.0
186
- */
187
- export function optional<Value>(
188
- token: Token<Value> | Constructor<Value>,
189
- options?: InjectOptions,
190
- ): InjectionDescriptor<Value | undefined> {
191
- return withOptions(
192
- {
193
- token: token as Token<Value | undefined> | Constructor<Value | undefined>,
194
- optional: true,
195
- multi: false,
196
- },
197
- options,
198
- );
199
- }
200
-
201
- /**
202
- * Creates a descriptor that resolves every matching binding for the token into an array.
203
- *
204
- * @since 0.3.16-canary.0
205
- */
206
- export function injectAll<Value>(
207
- token: Token<Value> | Constructor<Value>,
208
- options?: InjectOptions,
209
- ): InjectionDescriptor<Array<Value>> {
210
- return withOptions(
211
- {
212
- token: token as Token<Array<Value>> | Constructor<Array<Value>>,
213
- optional: false,
214
- multi: true,
215
- },
216
- options,
217
- );
218
- }
@@ -1,201 +0,0 @@
1
- import type { BindingTag } from "#/core/tag";
2
- import { slotName, slotNameCriterionOf } from "#/core/tag";
3
- import type { Token } from "#/core/token";
4
- import type { Constructor, ResolveOptions } from "#/core/types";
5
-
6
- /**
7
- * What one resolvable dependency declares.
8
- *
9
- * @remarks Both dependency sources — a constructor's `ParamMetadata` and a `toResolved`
10
- * `InjectionDescriptor` — are this shape, which is why one resolve routine serves both.
11
- *
12
- * @since 0.5.0-canary.9
13
- */
14
- export interface DependencySlot {
15
- readonly token: Token<unknown> | Constructor;
16
- readonly optional: boolean;
17
- readonly multi: boolean;
18
- readonly name?: string | undefined;
19
- readonly tags?: ReadonlyArray<BindingTag> | undefined;
20
- }
21
-
22
- /**
23
- * The lone criterion of a request that carries exactly one, whatever its spelling — the shape the
24
- * registry has a direct index for.
25
- *
26
- * @remarks Every spelling answers here, so the index is not something one of them silently misses;
27
- * a name folds to the reserved `slotName` criterion, making the name lane the tag lane.
28
- *
29
- * @since 0.5.0-canary.9
30
- */
31
- export function singleCriterionOnlyOf(options: ResolveOptions | undefined): BindingTag | undefined {
32
- if (options === undefined) {
33
- return undefined;
34
- }
35
- if (options.name !== undefined) {
36
- return loneNameCriterionOf(options);
37
- }
38
- const listed = options.tags;
39
- const shorthand = options.tag;
40
- if (shorthand !== undefined) {
41
- return listed === undefined || listed.length === 0 ? shorthand : undefined;
42
- }
43
- return listed !== undefined && listed.length === 1 ? listed[0] : undefined;
44
- }
45
-
46
- /** The name spelling's half of the fold, kept apart so the common body stays small enough to inline. */
47
- function loneNameCriterionOf(options: ResolveOptions): BindingTag | undefined {
48
- // A name next to any tag means the request carries two criteria, which no single index answers.
49
- if (options.tag !== undefined || (options.tags !== undefined && options.tags.length > 0)) {
50
- return undefined;
51
- }
52
- // Read, not minted: a request must not retain a name no binding ever declared.
53
- return slotNameCriterionOf(options.name as string);
54
- }
55
-
56
- /** Shared core: build a ResolveOptions from already-normalised name + tags. */
57
- function buildOptions(
58
- name: string | undefined,
59
- tags: ReadonlyArray<BindingTag> | undefined,
60
- ): ResolveOptions | undefined {
61
- if (name === undefined && tags === undefined) {
62
- return undefined;
63
- }
64
- const options: ResolveOptions = {};
65
- if (name !== undefined) {
66
- options.name = name;
67
- }
68
- if (tags !== undefined) {
69
- options.tags = tags;
70
- }
71
- return options;
72
- }
73
-
74
- /**
75
- * Builds a {@link ResolveOptions} safe for `exactOptionalPropertyTypes`:
76
- * omits keys instead of assigning `undefined`.
77
- *
78
- * @since 0.3.16-canary.0
79
- */
80
- export function injectionSlotToResolveOptions(
81
- injectionSlot: Pick<DependencySlot, "name" | "tags">,
82
- ): ResolveOptions | undefined {
83
- return buildOptions(injectionSlot.name, injectionSlot.tags);
84
- }
85
-
86
- /** Where a slot's derived options are memoized, so the same object is handed out every resolve. */
87
- const MEMOIZED_RESOLVE_OPTIONS: unique symbol = Symbol("di:resolve-options");
88
-
89
- /** Where a slot's folded lone criterion is memoized — `null` records "computed: none". */
90
- const MEMOIZED_SINGLE_CRITERION: unique symbol = Symbol("di:single-criterion");
91
-
92
- interface SlotWithMemoizedOptions {
93
- [MEMOIZED_RESOLVE_OPTIONS]?: ResolveOptions;
94
- [MEMOIZED_SINGLE_CRITERION]?: BindingTag | null;
95
- }
96
-
97
- /**
98
- * The options a dependency resolves with — one object per slot, since a slot's criteria are fixed
99
- * when it is declared.
100
- *
101
- * @remarks A slot carrying no criterion answers from its two fields, so the common shape never
102
- * reaches the memo.
103
- *
104
- * @since 0.6.0
105
- */
106
- export function resolveOptionsForSlot(injectionSlot: DependencySlot): ResolveOptions | undefined {
107
- const { name, tags } = injectionSlot;
108
- if (name === undefined && tags === undefined) {
109
- return undefined;
110
- }
111
- const slot = injectionSlot as SlotWithMemoizedOptions;
112
- const memoized = slot[MEMOIZED_RESOLVE_OPTIONS];
113
- return memoized ?? memoizeResolveOptions(slot, name, tags);
114
- }
115
-
116
- /**
117
- * Builds, freezes and stores a slot's options on first use.
118
- *
119
- * @remarks Frozen because one object answers every resolve of the slot and a constraint predicate is
120
- * handed it. Split out for the `try`: in the caller it would cost the early return its inlining.
121
- */
122
- function memoizeResolveOptions(
123
- slot: SlotWithMemoizedOptions,
124
- name: string | undefined,
125
- tags: ReadonlyArray<BindingTag> | undefined,
126
- ): ResolveOptions {
127
- const built = Object.freeze(buildOptions(name, tags) as ResolveOptions);
128
- try {
129
- slot[MEMOIZED_RESOLVE_OPTIONS] = built;
130
- } catch {
131
- // A frozen slot rebuilds on every hop rather than throwing.
132
- }
133
- return built;
134
- }
135
-
136
- /**
137
- * The lone criterion a dependency's fixed criteria fold to, memoized on the slot like its options —
138
- * `null` means the fold answered "none".
139
- *
140
- * @remarks A slot's criteria never change after declaration, so the fold happens once per slot
141
- * rather than per hop — which is what keeps a named dependency's resolve off the intern map. A
142
- * lone name whose criterion is not interned yet is left unmemoized: a later `whenNamed` binding
143
- * mints it, and the next fold must see that.
144
- *
145
- * @since 0.8.0
146
- */
147
- export function singleCriterionForSlot(injectionSlot: DependencySlot): BindingTag | null {
148
- const slot = injectionSlot as SlotWithMemoizedOptions;
149
- const memoized = slot[MEMOIZED_SINGLE_CRITERION];
150
- if (memoized !== undefined) {
151
- return memoized;
152
- }
153
- const options = resolveOptionsForSlot(injectionSlot);
154
- const criterion = singleCriterionOnlyOf(options);
155
- const folded = criterion ?? null;
156
- if (criterion !== undefined || !isLoneNameOptions(options)) {
157
- try {
158
- slot[MEMOIZED_SINGLE_CRITERION] = folded;
159
- } catch {
160
- // A frozen slot re-folds on every hop rather than throwing.
161
- }
162
- }
163
- return folded;
164
- }
165
-
166
- /** A slot request whose only criterion is a name — the one shape whose fold can change after a bind. */
167
- function isLoneNameOptions(options: ResolveOptions | undefined): boolean {
168
- // Slot-derived options never carry the `tag` shorthand — `buildOptions` folds it into `tags`.
169
- return (
170
- options !== undefined && options.name !== undefined && (options.tags === undefined || options.tags.length === 0)
171
- );
172
- }
173
-
174
- /**
175
- * Resolve options derived from a binding slot (tags may be empty; omits when nothing to match).
176
- *
177
- * @remarks Takes the slot structurally rather than as `BindingSlot`, so the slot on a public
178
- * `BindingSnapshot` — where `name` is an optional property, not a required one holding `undefined` —
179
- * is accepted by the same call.
180
- *
181
- * @since 0.3.16-canary.0
182
- */
183
- export function bindingSlotToResolveOptions(bindingSlot: {
184
- readonly name?: string | undefined;
185
- readonly tags?: ReadonlyArray<BindingTag> | undefined;
186
- }): ResolveOptions | undefined {
187
- const tags = bindingSlot.tags;
188
- let name = bindingSlot.name;
189
- let criteria: ReadonlyArray<BindingTag> | undefined = tags !== undefined && tags.length > 0 ? tags : undefined;
190
- if (criteria !== undefined) {
191
- // A reserved criterion folds into `name` — carried in `tags` it would be said twice or, on a
192
- // descriptor that spells its name as a tag, not at all.
193
- const reserved = criteria.find((criterion) => criterion.key === slotName);
194
- if (reserved !== undefined) {
195
- name ??= reserved.value as string;
196
- const rest = criteria.filter((criterion) => criterion.key !== slotName);
197
- criteria = rest.length > 0 ? rest : undefined;
198
- }
199
- }
200
- return buildOptions(name, criteria);
201
- }