@codefast/di 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/LICENSE +1 -1
  3. package/README.md +29 -20
  4. package/dist/ambient/active-container.d.ts +13 -0
  5. package/dist/ambient/active-container.js +24 -0
  6. package/dist/container/binding-builders.d.ts +42 -20
  7. package/dist/container/binding-builders.js +155 -87
  8. package/dist/container/container.d.ts +10 -10
  9. package/dist/container/container.js +55 -23
  10. package/dist/core/binding.d.ts +55 -57
  11. package/dist/core/binding.js +11 -37
  12. package/dist/core/constraint-requirement.d.ts +19 -4
  13. package/dist/core/constraint-requirement.js +19 -9
  14. package/dist/core/registry.d.ts +47 -4
  15. package/dist/core/registry.js +361 -159
  16. package/dist/core/state-epoch.d.ts +16 -0
  17. package/dist/core/state-epoch.js +21 -0
  18. package/dist/core/tag.d.ts +2 -2
  19. package/dist/core/tag.js +2 -2
  20. package/dist/core/token.d.ts +24 -4
  21. package/dist/core/token.js +1 -1
  22. package/dist/core/types.d.ts +15 -6
  23. package/dist/decorators/inject.d.ts +1 -1
  24. package/dist/decorators/inject.js +6 -4
  25. package/dist/errors/diagnostics.d.ts +2 -0
  26. package/dist/errors/errors.d.ts +33 -1
  27. package/dist/errors/errors.js +44 -3
  28. package/dist/index.d.ts +2 -2
  29. package/dist/index.js +1 -1
  30. package/dist/injection/descriptor.d.ts +16 -8
  31. package/dist/injection/descriptor.js +3 -1
  32. package/dist/injection/resolve-options.d.ts +6 -0
  33. package/dist/injection/resolve-options.js +16 -0
  34. package/dist/introspection/dependency-graph.js +10 -5
  35. package/dist/introspection/inspector.d.ts +3 -1
  36. package/dist/introspection/inspector.js +10 -24
  37. package/dist/lifecycle/lifecycle-manager.js +10 -8
  38. package/dist/lifecycle/scope-manager.js +1 -1
  39. package/dist/metadata/metadata-reader-token.js +1 -1
  40. package/dist/resolution/cache/activation-need.d.ts +2 -0
  41. package/dist/resolution/cache/activation-need.js +13 -6
  42. package/dist/resolution/cache/binding-lookup-cache.d.ts +34 -1
  43. package/dist/resolution/cache/binding-lookup-cache.js +96 -12
  44. package/dist/resolution/cache/class-introspector.d.ts +1 -1
  45. package/dist/resolution/cache/class-introspector.js +28 -14
  46. package/dist/resolution/context.d.ts +8 -8
  47. package/dist/resolution/plan/instantiation-plan.d.ts +12 -0
  48. package/dist/resolution/plan/instantiation-plan.js +156 -65
  49. package/dist/resolution/plan/plan-codegen.d.ts +100 -0
  50. package/dist/resolution/plan/plan-codegen.js +185 -0
  51. package/dist/resolution/resolver.d.ts +14 -4
  52. package/dist/resolution/resolver.js +375 -134
  53. package/dist/resolution/select/binding-select.js +12 -7
  54. package/dist/resolution/select/constraints.d.ts +7 -4
  55. package/dist/resolution/select/constraints.js +34 -13
  56. package/package.json +14 -40
@@ -55,7 +55,7 @@ export declare const DEFAULT_BINDING_SLOT: BindingSlot;
55
55
  */
56
56
  export declare function bindingSlotToString(slot: BindingSlot): string;
57
57
  interface BindingBase<Value> {
58
- readonly id: BindingIdentifier;
58
+ readonly identifier: BindingIdentifier;
59
59
  /**
60
60
  * True while this binding's factory is executing on the current synchronous call stack.
61
61
  *
@@ -83,20 +83,25 @@ interface BindingBase<Value> {
83
83
  readonly token: Token<Value> | Constructor<Value>;
84
84
  readonly slot: BindingSlot;
85
85
  readonly predicate?: BindingConstraint | undefined;
86
+ /**
87
+ * Whether the binding is a collection member only: `resolveAll` includes it, `resolve` never
88
+ * selects it, and it neither displaces nor is displaced under slot last-wins.
89
+ */
90
+ readonly isMany: boolean;
86
91
  }
87
- type BindingBaseKeys = keyof BindingBase<unknown>;
88
92
  /**
89
93
  * The lifecycle hooks every kind but `alias` may carry.
90
94
  *
91
95
  * @remarks Declared as **methods**, not function-typed properties, so their parameters compare
92
96
  * bivariantly and `Binding<Value>` stays assignable to `Binding`. The engine erases the value type at
93
97
  * every lane boundary regardless; the public `ActivationHandler` / `DeactivationHandler` keep strict
94
- * checking, which is where a user's handler is actually verified. Not `readonly`: a fluent chain
95
- * refines both in place — see {@link RefinableBindingFields}.
98
+ * checking, which is where a user's handler is actually verified. Named apart from the fluent
99
+ * `onActivation()` / `onDeactivation()` steps because the chain that registers them is the binding
100
+ * itself, and a field cannot share a name with a method on the same object.
96
101
  */
97
102
  interface BindingLifecycleHooks<Value> {
98
- onActivation?(ctx: ResolutionContext, instance: Value): Value | Promise<Value>;
99
- onDeactivation?(instance: Value): void | Promise<void>;
103
+ activationHook?(ctx: ResolutionContext, instance: Value): Value | Promise<Value>;
104
+ deactivationHook?(instance: Value): void | Promise<void>;
100
105
  }
101
106
  /**
102
107
  * A binding that instantiates a constructor.
@@ -182,17 +187,6 @@ export interface AliasBinding<Value> extends BindingBase<Value> {
182
187
  * @since 0.3.16-canary.0
183
188
  */
184
189
  export type Binding<Value = unknown> = ClassBinding<Value> | DynamicBinding<Value> | DynamicAsyncBinding<Value> | ResolvedBinding<Value> | ResolvedAsyncBinding<Value> | ConstantBinding<Value> | AliasBinding<Value>;
185
- /** `Omit` applied per union member, since a bare `Omit` would collapse the union into one shape. */
186
- type DistributiveOmit<Union, Keys extends PropertyKey> = Union extends unknown ? Omit<Union, Keys> : never;
187
- /**
188
- * Builder-only payload before `id`, `token`, `slot`, and `predicate` are applied.
189
- *
190
- * @remarks Derived rather than listed: a new binding kind joins this the moment it joins
191
- * {@link Binding}, so the two unions cannot diverge.
192
- *
193
- * @since 0.3.16-canary.0
194
- */
195
- export type PartialBinding<Value> = DistributiveOmit<Binding<Value>, BindingBaseKeys>;
196
190
  /**
197
191
  * Returns a process-unique identifier for a new binding.
198
192
  *
@@ -200,45 +194,39 @@ export type PartialBinding<Value> = DistributiveOmit<Binding<Value>, BindingBase
200
194
  */
201
195
  export declare function generateBindingId(): BindingIdentifier;
202
196
  /**
203
- * The single construction site for bindings — one literal, one V8 hidden class.
204
- *
205
- * @remarks Field order is fixed and this is the only construction site, so every binding shares one
206
- * hidden class. Reordering the fields, or adding a second site, gives that up.
207
- *
208
- * @param source - the kind-specific payload, or an existing binding to re-slot
209
- * @param token - the key requests resolve the binding by
210
- * @param slot - the name + tags a request must match to select this binding
211
- * @param predicate - a custom constraint, or `undefined` for none
212
- * @param id - reuse a caller's id to keep a fluent chain's `id()` stable across refinements
197
+ * Writable view of the one selection field a chain may refine without re-registering: nothing
198
+ * indexes on the predicate, so the registry rewrites it in place and re-homes the binding itself.
213
199
  *
214
- * @since 0.5.0-canary.8
200
+ * @since 0.10.0
215
201
  */
216
- export declare function createBinding<Value>(source: PartialBinding<Value> | Binding<Value>, token: Token<Value> | Constructor<Value>, slot: BindingSlot, predicate: BindingConstraint | undefined, id?: BindingIdentifier): Binding<Value>;
202
+ export interface PredicateField {
203
+ predicate: BindingConstraint | undefined;
204
+ }
217
205
  /**
218
- * Writable view of the only fields a fluent chain may refine after registration.
206
+ * Narrows a registered binding to its predicate for the registry to rewrite.
219
207
  *
220
- * @remarks No registry index is keyed on these, so a builder that owns the registered object
221
- * can write them directly instead of re-registering. `token`, `slot`, `predicate` and `id`
222
- * are excluded on purpose — changing those means re-indexing.
208
+ * @since 0.10.0
209
+ */
210
+ export declare function writablePredicate(binding: Binding): PredicateField;
211
+ /**
212
+ * Writable view of collection membership, which the registry sets because it decides the binding's map.
223
213
  *
224
- * @since 0.5.0-canary.8
214
+ * @since 0.10.0
225
215
  */
226
- export interface RefinableBindingFields<Value> {
227
- onActivation: ActivationHandler<Value> | undefined;
228
- onDeactivation: DeactivationHandler<Value> | undefined;
229
- scope: BindingScope;
216
+ export interface MembershipField {
217
+ isMany: boolean;
230
218
  }
231
219
  /**
232
- * Narrows a registered binding to the fields a fluent chain may still refine.
220
+ * Narrows a registered binding to its membership flag for the registry to set.
233
221
  *
234
- * @since 0.5.0-canary.8
222
+ * @since 0.10.0
235
223
  */
236
- export declare function refinableFields<Value>(binding: Binding<Value>): RefinableBindingFields<Value>;
224
+ export declare function writableMembership(binding: Binding): MembershipField;
237
225
  /**
238
226
  * Drops the memoized resolution frame, for a refinement that changes what the frame reports.
239
227
  *
240
- * @remarks `scope` is the only field a chain writes in place that the frame derives from — a
241
- * re-slot builds a fresh binding, whose frame starts empty anyway.
228
+ * @remarks The frame derives from `scope` and `slot`, both of which a chain now writes in place on
229
+ * the registered object, so every such refinement clears it.
242
230
  *
243
231
  * @since 0.5.0-canary.9
244
232
  */
@@ -248,11 +236,21 @@ export declare function clearBindingFrame<Value>(binding: Binding<Value>): void;
248
236
  *
249
237
  * @since 0.3.16-canary.0
250
238
  */
251
- export interface SlotConstrainedBuilder {
239
+ export interface SlotConstrainedBuilder<Names extends string = string> {
240
+ /** Narrows the binding to requests the predicate accepts, evaluated on every resolve. */
252
241
  when(predicate: BindingConstraint): this;
253
- whenNamed(name: string): this;
242
+ /** Declares the binding's slot name, one of the names the token declares. */
243
+ whenNamed(name: Names): this;
244
+ /** Declares one criterion of the binding's slot, replacing any earlier criterion of the same key. */
254
245
  whenTagged(criterion: BindingTag): this;
246
+ /** Keeps the binding on the default slot, the one an unconstrained request selects. */
255
247
  whenDefault(): this;
248
+ /**
249
+ * Makes the binding a collection member: one of several the token's `resolveAll` returns, never
250
+ * what a single `resolve` selects, and outside slot last-wins. It keeps the default slot.
251
+ */
252
+ many(): this;
253
+ /** The identifier this binding is registered under. */
256
254
  id(): BindingIdentifier;
257
255
  }
258
256
  /**
@@ -260,26 +258,26 @@ export interface SlotConstrainedBuilder {
260
258
  *
261
259
  * @since 0.3.16-canary.0
262
260
  */
263
- export interface BindToBuilder<Value> {
264
- to(type: Constructor<Value>): BindingBuilder<Value>;
265
- toSelf(): BindingBuilder<Value>;
266
- toConstantValue(value: Value): ConstantBindingBuilder<Value>;
267
- toDynamic(factory: (ctx: ResolutionContext) => Value): BindingBuilder<Value>;
268
- toDynamicAsync(factory: (ctx: ResolutionContext) => Promise<Value>): BindingBuilder<Value>;
261
+ export interface BindToBuilder<Value, Names extends string = string> {
262
+ to(type: Constructor<Value>): BindingBuilder<Value, Names>;
263
+ toSelf(): BindingBuilder<Value, Names>;
264
+ toConstantValue(value: Value): ConstantBindingBuilder<Value, Names>;
265
+ toDynamic(factory: (ctx: ResolutionContext) => Value): BindingBuilder<Value, Names>;
266
+ toDynamicAsync(factory: (ctx: ResolutionContext) => Promise<Value>): BindingBuilder<Value, Names>;
269
267
  toResolved<const Deps extends ReadonlyArray<InjectableDependency>>(factory: (...args: {
270
268
  [K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]>;
271
- }) => Value, deps: Deps): BindingBuilder<Value>;
269
+ }) => Value, deps: Deps): BindingBuilder<Value, Names>;
272
270
  toResolvedAsync<const Deps extends ReadonlyArray<InjectableDependency>>(factory: (...args: {
273
271
  [K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]>;
274
- }) => Promise<Value>, deps: Deps): BindingBuilder<Value>;
275
- toAlias(target: Token<Value> | Constructor<Value>): AliasBindingBuilder;
272
+ }) => Promise<Value>, deps: Deps): BindingBuilder<Value, Names>;
273
+ toAlias(target: Token<Value> | Constructor<Value>): AliasBindingBuilder<Names>;
276
274
  }
277
275
  /**
278
276
  * The scope-selection step of the fluent chain.
279
277
  *
280
278
  * @since 0.3.16-canary.0
281
279
  */
282
- export interface BindingBuilder<Value> extends SlotConstrainedBuilder {
280
+ export interface BindingBuilder<Value, Names extends string = string> extends SlotConstrainedBuilder<Names> {
283
281
  singleton(): SingletonBindingBuilder<Value>;
284
282
  transient(): TransientBindingBuilder<Value>;
285
283
  scoped(): ScopedBindingBuilder<Value>;
@@ -289,7 +287,7 @@ export interface BindingBuilder<Value> extends SlotConstrainedBuilder {
289
287
  *
290
288
  * @since 0.3.16-canary.0
291
289
  */
292
- export interface ConstantBindingBuilder<Value> extends SlotConstrainedBuilder {
290
+ export interface ConstantBindingBuilder<Value, Names extends string = string> extends SlotConstrainedBuilder<Names> {
293
291
  onActivation(fn: ActivationHandler<Value>): SingletonLifecycleBuilder<Value>;
294
292
  onDeactivation(fn: DeactivationHandler<Value>): SingletonLifecycleBuilder<Value>;
295
293
  }
@@ -298,7 +296,7 @@ export interface ConstantBindingBuilder<Value> extends SlotConstrainedBuilder {
298
296
  *
299
297
  * @since 0.3.16-canary.0
300
298
  */
301
- export interface AliasBindingBuilder extends SlotConstrainedBuilder {
299
+ export interface AliasBindingBuilder<Names extends string = string> extends SlotConstrainedBuilder<Names> {
302
300
  }
303
301
  /**
304
302
  * The fluent chain after `singleton()`, where both lifecycle hooks stay available.
@@ -82,56 +82,30 @@ let bindingIdCounter = 0;
82
82
  * @since 0.3.16-canary.0
83
83
  */
84
84
  export function generateBindingId() {
85
- return String(++bindingIdCounter);
85
+ bindingIdCounter += 1;
86
+ return bindingIdCounter;
86
87
  }
87
88
  /**
88
- * The single construction site for bindings — one literal, one V8 hidden class.
89
+ * Narrows a registered binding to its predicate for the registry to rewrite.
89
90
  *
90
- * @remarks Field order is fixed and this is the only construction site, so every binding shares one
91
- * hidden class. Reordering the fields, or adding a second site, gives that up.
92
- *
93
- * @param source - the kind-specific payload, or an existing binding to re-slot
94
- * @param token - the key requests resolve the binding by
95
- * @param slot - the name + tags a request must match to select this binding
96
- * @param predicate - a custom constraint, or `undefined` for none
97
- * @param id - reuse a caller's id to keep a fluent chain's `id()` stable across refinements
98
- *
99
- * @since 0.5.0-canary.8
91
+ * @since 0.10.0
100
92
  */
101
- export function createBinding(source, token, slot, predicate, id = generateBindingId()) {
102
- const fields = source;
103
- return {
104
- kind: fields.kind,
105
- id,
106
- inFlight: false,
107
- frame: undefined,
108
- // An `in` probe, not `??`: a re-slotted singleton may legitimately hold a cached `undefined`.
109
- instance: "instance" in fields ? fields.instance : NO_INSTANCE,
110
- token,
111
- slot,
112
- predicate,
113
- scope: fields.scope,
114
- target: fields.target,
115
- factory: fields.factory,
116
- deps: fields.deps,
117
- value: fields.value,
118
- onActivation: fields.onActivation,
119
- onDeactivation: fields.onDeactivation,
120
- };
93
+ export function writablePredicate(binding) {
94
+ return binding;
121
95
  }
122
96
  /**
123
- * Narrows a registered binding to the fields a fluent chain may still refine.
97
+ * Narrows a registered binding to its membership flag for the registry to set.
124
98
  *
125
- * @since 0.5.0-canary.8
99
+ * @since 0.10.0
126
100
  */
127
- export function refinableFields(binding) {
101
+ export function writableMembership(binding) {
128
102
  return binding;
129
103
  }
130
104
  /**
131
105
  * Drops the memoized resolution frame, for a refinement that changes what the frame reports.
132
106
  *
133
- * @remarks `scope` is the only field a chain writes in place that the frame derives from — a
134
- * re-slot builds a fresh binding, whose frame starts empty anyway.
107
+ * @remarks The frame derives from `scope` and `slot`, both of which a chain now writes in place on
108
+ * the registered object, so every such refinement clears it.
135
109
  *
136
110
  * @since 0.5.0-canary.9
137
111
  */
@@ -12,23 +12,38 @@ export declare const CONSTRAINT_REQUIREMENT: unique symbol;
12
12
  /**
13
13
  * The slot name a constraint waits for on an ancestor.
14
14
  *
15
- * @remarks Only names are described. A tag criterion is interned, so a typo cannot produce one that
16
- * looks valid, while a name is a bare string that nothing checks.
15
+ * @remarks Only names are described, however spelled: a tag criterion of any other key is minted from
16
+ * a typed key, so a typo cannot produce one that looks valid, while a name is a bare string that
17
+ * nothing checks — through `whenNamed`'s reserved key included.
17
18
  *
18
19
  * @since 0.6.0
19
20
  */
20
21
  export interface ConstraintRequirement {
21
22
  readonly requires: "ancestorSlotName";
23
+ /** The token whose slot must carry the name, or `undefined` when the spelling named no token. */
24
+ readonly tokenName: string | undefined;
22
25
  readonly name: string;
23
26
  /** The helper that built the predicate, so a report can name what the caller wrote. */
24
27
  readonly helperName: string;
25
28
  }
26
29
  /**
27
- * Records what a predicate waits for. Called once, where the predicate is built.
30
+ * What a helper records about one waited-for slot name, before the discriminant is stamped on.
31
+ *
32
+ * @since 0.9.0
33
+ */
34
+ export type SlotNameRequirement = Omit<ConstraintRequirement, "requires">;
35
+ /**
36
+ * Records the one slot name a predicate waits for. Called once, where the predicate is built.
28
37
  *
29
38
  * @since 0.6.0
30
39
  */
31
- export declare function requiringAncestorSlotName(predicate: BindingConstraint, name: string, helperName: string): BindingConstraint;
40
+ export declare function requiringAncestorSlotName(predicate: BindingConstraint, requirement: SlotNameRequirement): BindingConstraint;
41
+ /**
42
+ * Records every slot name a predicate waits for — one per reserved criterion in a `…TaggedAll` list.
43
+ *
44
+ * @since 0.9.0
45
+ */
46
+ export declare function requiringAncestorSlotNames(predicate: BindingConstraint, requirements: ReadonlyArray<SlotNameRequirement>): BindingConstraint;
32
47
  /**
33
48
  * The requirement a predicate carries, if it was built by a helper that records one.
34
49
  *
@@ -8,13 +8,27 @@
8
8
  */
9
9
  export const CONSTRAINT_REQUIREMENT = Symbol("di:constraint-requirement");
10
10
  /**
11
- * Records what a predicate waits for. Called once, where the predicate is built.
11
+ * Records the one slot name a predicate waits for. Called once, where the predicate is built.
12
12
  *
13
13
  * @since 0.6.0
14
14
  */
15
- export function requiringAncestorSlotName(predicate, name, helperName) {
16
- const requirement = { requires: "ancestorSlotName", name, helperName };
17
- Object.defineProperty(predicate, CONSTRAINT_REQUIREMENT, { value: requirement, enumerable: false });
15
+ export function requiringAncestorSlotName(predicate, requirement) {
16
+ return requiringAncestorSlotNames(predicate, [requirement]);
17
+ }
18
+ /**
19
+ * Records every slot name a predicate waits for — one per reserved criterion in a `…TaggedAll` list.
20
+ *
21
+ * @since 0.9.0
22
+ */
23
+ export function requiringAncestorSlotNames(predicate, requirements) {
24
+ return attachRequirements(predicate, requirements.map((requirement) => ({ requires: "ancestorSlotName", ...requirement })));
25
+ }
26
+ /** One non-enumerable write per predicate: a second define on the same key would throw. */
27
+ function attachRequirements(predicate, requirements) {
28
+ Object.defineProperty(predicate, CONSTRAINT_REQUIREMENT, {
29
+ value: requirements.length === 1 ? requirements[0] : requirements,
30
+ enumerable: false,
31
+ });
18
32
  return predicate;
19
33
  }
20
34
  /**
@@ -53,9 +67,5 @@ export function mergingConstraintRequirements(composite, left, right) {
53
67
  if (merged.length === 0) {
54
68
  return composite;
55
69
  }
56
- Object.defineProperty(composite, CONSTRAINT_REQUIREMENT, {
57
- value: merged.length === 1 ? merged[0] : merged,
58
- enumerable: false,
59
- });
60
- return composite;
70
+ return attachRequirements(composite, merged);
61
71
  }
@@ -1,7 +1,7 @@
1
1
  import type { Binding } from "#/core/binding";
2
2
  import type { BindingTag } from "#/core/tag";
3
3
  import type { Token } from "#/core/token";
4
- import type { BindingIdentifier, Constructor } from "#/core/types";
4
+ import type { BindingConstraint, BindingIdentifier, Constructor } from "#/core/types";
5
5
  /**
6
6
  * One container's binding store, indexed by token, binding id, and slot for fast lookup.
7
7
  *
@@ -13,6 +13,12 @@ export declare class BindingRegistry {
13
13
  get version(): number;
14
14
  /** Whether a constant has ever been registered here, and so whether teardown has anything to sweep. */
15
15
  get hasHeldConstantBinding(): boolean;
16
+ /** Whether the deferred tagged-slot index has had to be built. */
17
+ get isTaggedIndexBuilt(): boolean;
18
+ /** Whether an id-keyed operation has had to build the id index. */
19
+ get isIdIndexBuilt(): boolean;
20
+ /** Whether a token carrying more than one default-slot binding has had to build the record map. */
21
+ get isRecordMapBuilt(): boolean;
16
22
  /**
17
23
  * Registers a mutation the indexes don't care about (a fluent chain refining scope or an
18
24
  * activation hook in place), so version-stamped resolver caches still invalidate.
@@ -29,8 +35,22 @@ export declare class BindingRegistry {
29
35
  removeByToken(token: Token<unknown> | Constructor): Array<Binding>;
30
36
  /** Remove a specific binding by ID. Returns the removed binding or undefined. */
31
37
  removeById(id: BindingIdentifier): Binding | undefined;
32
- /** Get all bindings for a token. */
38
+ /**
39
+ * Get all bindings for a token.
40
+ *
41
+ * @remarks Allocates a one-element list for a lone default-slot binding, so a hot path asks
42
+ * `getFastDefault()` first and reaches here only for a token that keeps a record.
43
+ */
33
44
  getAll(token: Token<unknown> | Constructor): ReadonlyArray<Binding>;
45
+ /**
46
+ * The bindings of a token that keeps a record, or none.
47
+ *
48
+ * @remarks For a caller whose lone-map probe has just missed: the record map is all that is left
49
+ * to ask, and a lone binding's one-element list is never materialised here.
50
+ */
51
+ getRecorded(token: Token<unknown> | Constructor): ReadonlyArray<Binding>;
52
+ /** How many bindings a token holds, without materialising a lone binding's list. */
53
+ countBindings(token: Token<unknown> | Constructor): number;
34
54
  /** Get binding by ID. */
35
55
  getById(id: BindingIdentifier): Binding | undefined;
36
56
  /** Check if any binding exists for token. */
@@ -48,6 +68,8 @@ export declare class BindingRegistry {
48
68
  * identity — where a value-keyed map answered by SameValueZero and parted from `Object.is` on ±0.
49
69
  */
50
70
  getSimpleTagged(token: Token<unknown> | Constructor, criterion: BindingTag): Binding | undefined;
71
+ /** The binding whose slot is exactly these two criteria, declared in either order, or `undefined`. */
72
+ getPairTagged(token: Token<unknown> | Constructor, first: BindingTag, second: BindingTag): Binding | undefined;
51
73
  /**
52
74
  * The multi-tag bindings whose slot's first criterion is `criterion`.
53
75
  *
@@ -55,9 +77,30 @@ export declare class BindingRegistry {
55
77
  * against the request — first-criterion bucketing only guarantees each candidate appears once.
56
78
  */
57
79
  getMultiTagged(token: Token<unknown> | Constructor, criterion: BindingTag): ReadonlyArray<Binding> | undefined;
80
+ /** A token's lone default-slot binding — the first read of every synchronous resolve. */
58
81
  getFastDefault(token: Token<unknown> | Constructor): Binding | undefined;
82
+ /**
83
+ * Takes a live binding out of every index, lets `rewrite` change its slot or predicate, and reports
84
+ * whether it was live; the caller registers it again with `add`.
85
+ *
86
+ * @remarks The binding keeps its object and its id, so the id index needs no touch and nothing
87
+ * that holds the object has to be told. `false` means the binding was unbound or displaced since
88
+ * it registered, and a refinement must not resurrect it.
89
+ */
90
+ reslot(binding: Binding, rewrite: () => void): boolean;
91
+ /**
92
+ * Marks a live binding as a collection member in place, moving it out of the lone map: a member is
93
+ * never the token's lone default answer, and nothing else indexes on membership.
94
+ */
95
+ setMany(binding: Binding): void;
96
+ /**
97
+ * Adds a predicate to a live binding in place.
98
+ *
99
+ * @remarks Only ever narrows — `when()` composes with any existing predicate — so the argument is
100
+ * never absent. Nothing indexes on the predicate, so the binding object and its id stay; a lone
101
+ * binding moves to a record because the lone map holds default-slot bindings with no predicate.
102
+ */
103
+ setPredicate(binding: Binding, predicate: BindingConstraint): void;
59
104
  /** Summarize available slot strings for a token (for error messages). */
60
105
  availableSlotStrings(token: Token<unknown> | Constructor): Array<string>;
61
- /** Whether the deferred tagged-slot index has had to be built. */
62
- get isTaggedIndexBuilt(): boolean;
63
106
  }