@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,69 +0,0 @@
1
- /** The container an `@inject` accessor initializer resolves from when it has no other handle. */
2
- import type { Container } from "#/container/container";
3
- import type { Token } from "#/core/token";
4
- import type { Constructor, ResolveOptions } from "#/core/types";
5
-
6
- /**
7
- * Path-continuing resolution the engine installs while constructing a class with `@inject` accessors.
8
- *
9
- * @remarks Resolving through this keeps an accessor's dependencies on the live resolution path, so a
10
- * cycle through an accessor surfaces as `CircularDependencyError` instead of unbounded recursion.
11
- *
12
- * @since 0.6.0
13
- */
14
- export interface AmbientResolution {
15
- resolve<Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Value;
16
- resolveOptional<Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Value | undefined;
17
- }
18
-
19
- let activeContainer: Container | undefined;
20
- let activeResolution: AmbientResolution | undefined;
21
-
22
- /**
23
- * Runs a callback with the given container installed as the ambient one and returns its result.
24
- *
25
- * @since 0.3.16-canary.0
26
- */
27
- export function runWithContainer<Result>(container: Container, fn: () => Result): Result {
28
- return runWithAmbientResolution(container, undefined, fn);
29
- }
30
-
31
- /**
32
- * Engine-internal variant of {@link runWithContainer} that also installs a path-continuing resolver.
33
- *
34
- * @since 0.6.0
35
- */
36
- export function runWithAmbientResolution<Result>(
37
- container: Container,
38
- resolution: AmbientResolution | undefined,
39
- fn: () => Result,
40
- ): Result {
41
- const previousContainer = activeContainer;
42
- const previousResolution = activeResolution;
43
- activeContainer = container;
44
- activeResolution = resolution;
45
- try {
46
- return fn();
47
- } finally {
48
- activeContainer = previousContainer;
49
- activeResolution = previousResolution;
50
- }
51
- }
52
-
53
- /**
54
- * Returns the ambient container for the construction in flight, if one is installed.
55
- *
56
- * @since 0.3.16-canary.0
57
- */
58
- export function getActiveContainer(): Container | undefined {
59
- return activeContainer;
60
- }
61
-
62
- /**
63
- * The path-continuing resolution for the construction in flight, when the engine installed one.
64
- *
65
- * @since 0.6.0
66
- */
67
- export function getAmbientResolution(): AmbientResolution | undefined {
68
- return activeResolution;
69
- }
@@ -1,337 +0,0 @@
1
- /**
2
- * The fluent chain `bind()` returns: it registers the binding and refines it in place.
3
- *
4
- * @remarks One object plays every role in the chain; the return type of each step is what pins the
5
- * order, so no runtime check has to.
6
- */
7
- import type {
8
- AliasBindingBuilder,
9
- Binding,
10
- BindingBuilder,
11
- BindingSlot,
12
- BindToBuilder,
13
- ConstantBindingBuilder,
14
- PartialBinding,
15
- ScopedBindingBuilder,
16
- SingletonBindingBuilder,
17
- SingletonLifecycleBuilder,
18
- TransientBindingBuilder,
19
- } from "#/core/binding";
20
- import {
21
- clearBindingFrame,
22
- createBinding,
23
- createBindingSlot,
24
- DEFAULT_BINDING_SLOT,
25
- refinableFields,
26
- } from "#/core/binding";
27
- import { mergingConstraintRequirements } from "#/core/constraint-requirement";
28
- import type { BindingRegistry } from "#/core/registry";
29
- import type { BindingTag } from "#/core/tag";
30
- import { slotName } from "#/core/tag";
31
- import type { Token } from "#/core/token";
32
- import { tokenName } from "#/core/token";
33
- import type {
34
- ActivationHandler,
35
- BindingConstraint,
36
- BindingIdentifier,
37
- BindingScope,
38
- Constructor,
39
- DeactivationHandler,
40
- ResolutionContext,
41
- } from "#/core/types";
42
- import { ChainNotRegisteredError, SelfBindingRequiresClassError } from "#/errors/errors";
43
- import type { InjectableDependency, ResolvedDependencyValue } from "#/injection/descriptor";
44
- import { normalizeToDescriptor } from "#/injection/descriptor";
45
- import type { ScopeManager } from "#/lifecycle/scope-manager";
46
-
47
- /** One criterion per key: re-tagging the same key replaces it rather than asking for both values. */
48
- function updateSlotTag(slot: BindingSlot, criterion: BindingTag): BindingSlot {
49
- const tags = [...slot.tags];
50
- const existingIndex = tags.findIndex((existing) => existing.key === criterion.key);
51
- if (existingIndex === -1) {
52
- tags.push(criterion);
53
- } else {
54
- tags[existingIndex] = criterion;
55
- }
56
- return createBindingSlot(tags);
57
- }
58
-
59
- /** Record a module's binding id, dropping the id the chain re-slotted away from. */
60
- function trackBindingForModule(
61
- ids: Array<BindingIdentifier>,
62
- id: BindingIdentifier,
63
- previousId: BindingIdentifier | undefined,
64
- ): void {
65
- if (previousId !== undefined) {
66
- const previousIndex = ids.indexOf(previousId);
67
- if (previousIndex !== -1) {
68
- ids.splice(previousIndex, 1);
69
- }
70
- }
71
- ids.push(id);
72
- }
73
-
74
- // ── Registration target ──────────────────────────────────────────────────────────────────────────────────────────────
75
-
76
- /**
77
- * Where a chain registers, and on whose behalf.
78
- *
79
- * @remarks Built once per container and shared by every chain it creates. `moduleBindingIds` is
80
- * present exactly when the chain belongs to a module load.
81
- *
82
- * @since 0.5.0-canary.8
83
- */
84
- export interface BindingRegistration {
85
- readonly registry: BindingRegistry;
86
- readonly scope: ScopeManager;
87
- readonly moduleBindingIds: Array<BindingIdentifier> | undefined;
88
- }
89
-
90
- // ── BindingChain ─────────────────────────────────────────────────────────────────────────────────────────────────────
91
-
92
- /**
93
- * The one builder behind `bind()` and every `to*()` return type. Each interface exposes only the
94
- * calls that are legal at that point in the chain; the runtime object is shared because every
95
- * refinement is the same operation — narrow the registered binding, keep its id.
96
- *
97
- * @since 0.5.0-canary.8
98
- */
99
- export class BindingChain<Value>
100
- implements
101
- AliasBindingBuilder,
102
- BindingBuilder<Value>,
103
- BindToBuilder<Value>,
104
- ConstantBindingBuilder<Value>,
105
- ScopedBindingBuilder<Value>,
106
- SingletonBindingBuilder<Value>,
107
- SingletonLifecycleBuilder<Value>,
108
- TransientBindingBuilder<Value>
109
- {
110
- // Undefined until a `to*()` call registers the binding.
111
- #binding: Binding<Value> | undefined;
112
- // Allocated only by a chain that actually displaces something — most never do.
113
- #displacedByChain: Array<Binding> | undefined;
114
- // Registry version after this chain's last write — a mismatch means someone else wrote in between.
115
- #versionAfterLastWrite = -1;
116
- readonly #token: Token<Value> | Constructor<Value>;
117
- readonly #registration: BindingRegistration;
118
-
119
- constructor(token: Token<Value> | Constructor<Value>, registration: BindingRegistration) {
120
- this.#token = token;
121
- this.#registration = registration;
122
- }
123
-
124
- /** The registered binding, or a loud failure if no `to*()` has run yet. */
125
- #registered(): Binding<Value> {
126
- if (this.#binding === undefined) {
127
- throw new ChainNotRegisteredError(tokenName(this.#token));
128
- }
129
- return this.#binding;
130
- }
131
-
132
- #register(partial: PartialBinding<Value>): this {
133
- // Each `to*()` starts its own registration, so anything a previous one displaced is not this
134
- // registration's to restore.
135
- this.#displacedByChain = undefined;
136
- this.#binding = createBinding(partial, this.#token, DEFAULT_BINDING_SLOT, undefined);
137
- this.#commit(this.#binding, undefined);
138
- return this;
139
- }
140
-
141
- // ── Registration ───────────────────────────────────────────────────────────────────────────────────────────────────
142
-
143
- to(type: Constructor<Value>): BindingBuilder<Value> {
144
- return this.#register({ kind: "class", target: type, scope: "transient" });
145
- }
146
-
147
- toSelf(): BindingBuilder<Value> {
148
- if (typeof this.#token !== "function") {
149
- throw new SelfBindingRequiresClassError(tokenName(this.#token));
150
- }
151
- return this.#register({ kind: "class", target: this.#token, scope: "transient" });
152
- }
153
-
154
- toConstantValue(value: Value): ConstantBindingBuilder<Value> {
155
- return this.#register({ kind: "constant", scope: "singleton", value });
156
- }
157
-
158
- toDynamic(factory: (ctx: ResolutionContext) => Value): BindingBuilder<Value> {
159
- return this.#register({ kind: "dynamic", factory, scope: "transient" });
160
- }
161
-
162
- toDynamicAsync(factory: (ctx: ResolutionContext) => Promise<Value>): BindingBuilder<Value> {
163
- return this.#register({ kind: "dynamic-async", factory, scope: "transient" });
164
- }
165
-
166
- toResolved<const Deps extends ReadonlyArray<InjectableDependency>>(
167
- factory: (...args: { [K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]> }) => Value,
168
- deps: Deps,
169
- ): BindingBuilder<Value> {
170
- return this.#register({
171
- kind: "resolved",
172
- deps: deps.map((dependency) => normalizeToDescriptor(dependency)),
173
- factory: factory as (...args: Array<unknown>) => Value,
174
- scope: "transient",
175
- });
176
- }
177
-
178
- toResolvedAsync<const Deps extends ReadonlyArray<InjectableDependency>>(
179
- factory: (...args: { [K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]> }) => Promise<Value>,
180
- deps: Deps,
181
- ): BindingBuilder<Value> {
182
- return this.#register({
183
- kind: "resolved-async",
184
- deps: deps.map((dependency) => normalizeToDescriptor(dependency)),
185
- factory: factory as (...args: Array<unknown>) => Promise<Value>,
186
- scope: "transient",
187
- });
188
- }
189
-
190
- toAlias(target: Token<Value> | Constructor<Value>): AliasBindingBuilder {
191
- return this.#register({ kind: "alias", scope: "transient", target });
192
- }
193
-
194
- // ── Refinement ─────────────────────────────────────────────────────────────────────────────────────────────────────
195
-
196
- // Slot and predicate are what the registry indexes on, so a re-slot rebuilds the binding and
197
- // re-registers it — under the original id, keeping `id()` stable for the whole chain.
198
- #reslot(slot: BindingSlot, predicate: BindingConstraint | undefined): this {
199
- const previous = this.#registered();
200
- this.#binding = createBinding(previous, previous.token, slot, predicate, previous.id);
201
- this.#commit(this.#binding, previous.id);
202
- // The tracked-singleton list holds object references, and the re-slot just replaced the object.
203
- this.#registration.scope.replaceSingleton(previous as Binding, this.#binding as Binding);
204
- return this;
205
- }
206
-
207
- #withScope(scope: BindingScope): this {
208
- const binding = this.#registered();
209
- if (binding.scope !== scope) {
210
- // An instance cached under the old scope must not survive the change — a later flip back
211
- // to that scope would resurrect it.
212
- this.#registration.scope.deleteSingleton(binding);
213
- this.#registration.scope.deleteScoped(binding.id);
214
- refinableFields(binding).scope = scope;
215
- }
216
- // The frame reports the scope, so a resolve before this call memoized the previous one.
217
- clearBindingFrame(binding);
218
- this.#registration.registry.touch();
219
- this.#versionAfterLastWrite = this.#registration.registry.version;
220
- return this;
221
- }
222
-
223
- // SPEC calls a candidate a binding that passes *all* of a chain's predicates, and the chain type
224
- // reads as refinement, so a second `when()` narrows rather than replaces.
225
- when(predicate: BindingConstraint): this {
226
- const binding = this.#registered();
227
- const previous = binding.predicate;
228
-
229
- if (previous === undefined) {
230
- return this.#reslot(binding.slot, predicate);
231
- }
232
- // The composite carries both sides' requirements, so validate() still sees them.
233
- const composed = mergingConstraintRequirements((ctx) => previous(ctx) && predicate(ctx), previous, predicate);
234
-
235
- return this.#reslot(binding.slot, composed);
236
- }
237
-
238
- whenNamed(name: string): this {
239
- return this.whenTagged(slotName.of(name));
240
- }
241
-
242
- whenTagged(criterion: BindingTag): this {
243
- const binding = this.#registered();
244
- return this.#reslot(updateSlotTag(binding.slot, criterion), binding.predicate);
245
- }
246
-
247
- whenDefault(): this {
248
- // The default slot is what a fresh registration already has, so there is nothing to re-slot —
249
- // but an unregistered chain must fail here exactly as it does in every other refinement.
250
- this.#registered();
251
- return this;
252
- }
253
-
254
- singleton(): SingletonBindingBuilder<Value> {
255
- return this.#withScope("singleton");
256
- }
257
-
258
- transient(): TransientBindingBuilder<Value> {
259
- return this.#withScope("transient");
260
- }
261
-
262
- scoped(): ScopedBindingBuilder<Value> {
263
- return this.#withScope("scoped");
264
- }
265
-
266
- onActivation(fn: ActivationHandler<Value>): this {
267
- refinableFields(this.#registered()).onActivation = fn;
268
- this.#registration.registry.touch();
269
- this.#versionAfterLastWrite = this.#registration.registry.version;
270
- return this;
271
- }
272
-
273
- onDeactivation(fn: DeactivationHandler<Value>): this {
274
- refinableFields(this.#registered()).onDeactivation = fn;
275
- this.#registration.registry.touch();
276
- this.#versionAfterLastWrite = this.#registration.registry.version;
277
- return this;
278
- }
279
-
280
- id(): BindingIdentifier {
281
- return this.#registered().id;
282
- }
283
-
284
- // ── Registry ───────────────────────────────────────────────────────────────────────────────────────────────────────
285
-
286
- /**
287
- * Registers `binding`, first removing `previousId` when the chain is re-slotting.
288
- *
289
- * @remarks A `when*()` that follows `to*()` re-slots an already-live binding and can displace one
290
- * the final shape would never conflict with. Those stay parked in `#displacedByChain` until the
291
- * chain settles, then get restored.
292
- */
293
- #commit(binding: Binding<Value>, previousId: BindingIdentifier | undefined): void {
294
- const { registry, moduleBindingIds } = this.#registration;
295
- // The registry is heterogeneous by design — one instance holds every value type — so the
296
- // chain's `Binding<Value>` is erased once, here, rather than at each call site.
297
- const registered = binding as Binding;
298
-
299
- if (previousId !== undefined) {
300
- // A registry someone else wrote since this chain's last write invalidates the parked
301
- // snapshot: restoring it could undo an unbind or shadow a newer binding.
302
- if (registry.version !== this.#versionAfterLastWrite) {
303
- this.#displacedByChain = undefined;
304
- }
305
- if (registry.removeById(previousId) === undefined) {
306
- // The chain's binding is no longer live (unbound or displaced) — a refinement must not
307
- // resurrect it, so the chain goes inert against the registry.
308
- this.#displacedByChain = undefined;
309
- this.#versionAfterLastWrite = registry.version;
310
- return;
311
- }
312
- }
313
- const displaced = registry.add(registered);
314
- if (displaced !== undefined) {
315
- (this.#displacedByChain ??= []).push(displaced);
316
- }
317
- if (previousId !== undefined && this.#displacedByChain !== undefined) {
318
- this.#restoreNonConflicting(this.#displacedByChain);
319
- }
320
- if (moduleBindingIds !== undefined) {
321
- trackBindingForModule(moduleBindingIds, registered.id, previousId);
322
- }
323
- this.#versionAfterLastWrite = registry.version;
324
- }
325
-
326
- #restoreNonConflicting(displaced: Array<Binding>): void {
327
- for (let index = displaced.length - 1; index >= 0; index -= 1) {
328
- const candidate = displaced[index]!;
329
- // A restore must never displace: a slot that has been re-occupied keeps its occupant, and
330
- // the candidate stays parked.
331
- if (!this.#registration.registry.hasSlotOccupant(candidate)) {
332
- this.#registration.registry.add(candidate);
333
- displaced.splice(index, 1);
334
- }
335
- }
336
- }
337
- }