@codefast/di 0.10.1 → 0.11.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 (55) hide show
  1. package/CHANGELOG.md +265 -0
  2. package/README.md +69 -6
  3. package/dist/ambient/active-container.d.ts +7 -2
  4. package/dist/ambient/active-container.js +6 -1
  5. package/dist/container/binding-builders.d.ts +19 -1
  6. package/dist/container/binding-builders.js +75 -16
  7. package/dist/container/container.js +224 -47
  8. package/dist/core/binding-declaration.d.ts +120 -0
  9. package/dist/core/binding-declaration.js +186 -0
  10. package/dist/core/binding.d.ts +57 -5
  11. package/dist/core/binding.js +48 -1
  12. package/dist/core/module.d.ts +7 -4
  13. package/dist/core/module.js +17 -3
  14. package/dist/core/registry.d.ts +11 -7
  15. package/dist/core/registry.js +122 -38
  16. package/dist/core/state-epoch.d.ts +18 -1
  17. package/dist/core/state-epoch.js +17 -0
  18. package/dist/core/tag.js +1 -1
  19. package/dist/decorators/decorator-metadata.d.ts +9 -0
  20. package/dist/decorators/decorator-metadata.js +20 -0
  21. package/dist/decorators/inject.js +2 -1
  22. package/dist/decorators/injectable.js +3 -1
  23. package/dist/decorators/lifecycle-decorators.js +8 -2
  24. package/dist/errors/errors.d.ts +77 -3
  25. package/dist/errors/errors.js +93 -10
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.js +2 -1
  28. package/dist/injection/descriptor.js +3 -7
  29. package/dist/injection/resolve-options.js +6 -4
  30. package/dist/introspection/dependency-graph.d.ts +7 -2
  31. package/dist/introspection/dependency-graph.js +46 -23
  32. package/dist/introspection/graph-adapters/reactflow.js +6 -4
  33. package/dist/introspection/inspector.js +6 -9
  34. package/dist/lifecycle/lifecycle-manager.js +14 -2
  35. package/dist/lifecycle/scope-manager.js +28 -10
  36. package/dist/metadata/verifying-metadata-reader.d.ts +4 -3
  37. package/dist/metadata/verifying-metadata-reader.js +28 -6
  38. package/dist/resolution/async-fan-out.d.ts +12 -0
  39. package/dist/resolution/async-fan-out.js +26 -0
  40. package/dist/resolution/cache/activation-need.d.ts +0 -1
  41. package/dist/resolution/cache/activation-need.js +11 -18
  42. package/dist/resolution/cache/binding-lookup-cache.d.ts +0 -7
  43. package/dist/resolution/cache/binding-lookup-cache.js +30 -17
  44. package/dist/resolution/cache/class-introspector.d.ts +11 -0
  45. package/dist/resolution/cache/class-introspector.js +18 -0
  46. package/dist/resolution/context.d.ts +15 -23
  47. package/dist/resolution/context.js +47 -56
  48. package/dist/resolution/path/resolution-path.d.ts +48 -13
  49. package/dist/resolution/path/resolution-path.js +89 -38
  50. package/dist/resolution/plan/instantiation-plan.js +61 -21
  51. package/dist/resolution/plan/plan-codegen.d.ts +7 -4
  52. package/dist/resolution/plan/plan-codegen.js +60 -32
  53. package/dist/resolution/resolver.d.ts +4 -5
  54. package/dist/resolution/resolver.js +288 -258
  55. package/package.json +14 -2
@@ -0,0 +1,186 @@
1
+ import { DEFAULT_BINDING_SLOT, withSlotCriterion } from "#core/binding";
2
+ import { slotName } from "#core/tag";
3
+ import { tokenName } from "#core/token";
4
+ import { InvalidBindingDeclarationError, ManyBindingSlotError, SelfBindingRequiresClassError } from "#errors/errors";
5
+ import { normalizeToDescriptor } from "#injection/descriptor";
6
+ // Only this module can mint the symbol, so an entry carrying it was made by `binding()`.
7
+ const declarationBrand = Symbol("di:binding-declaration");
8
+ /** Whether an entry carries the brand `binding()` alone sets, and so every field a declaration holds. */
9
+ function isDeclaredBinding(entry) {
10
+ // Plain JavaScript can hand over anything, `null` included.
11
+ return typeof entry === "object" && entry !== null && entry[declarationBrand] === true;
12
+ }
13
+ /**
14
+ * Narrows a list entry to the declaration it must be, or throws for anything `binding()` did not make.
15
+ *
16
+ * @since 0.11.0
17
+ */
18
+ export function asDeclaredBinding(entry, moduleName, index) {
19
+ if (!isDeclaredBinding(entry)) {
20
+ throw new InvalidBindingDeclarationError(`${moduleName}[${String(index)}]`, "the entry is not a declaration made by binding()");
21
+ }
22
+ return entry;
23
+ }
24
+ const STRATEGIES = new Set([
25
+ "to",
26
+ "toSelf",
27
+ "toConstantValue",
28
+ "toDynamic",
29
+ "toDynamicAsync",
30
+ "toResolved",
31
+ "toResolvedAsync",
32
+ "toAlias",
33
+ ]);
34
+ const DEFINITION_KEYS = new Set([
35
+ ...STRATEGIES,
36
+ "deps",
37
+ "whenNamed",
38
+ "whenTagged",
39
+ "when",
40
+ "many",
41
+ "scope",
42
+ "onActivation",
43
+ "onDeactivation",
44
+ ]);
45
+ const SCOPES = new Set(["singleton", "transient", "scoped"]);
46
+ function isStrategy(key) {
47
+ return STRATEGIES.has(key);
48
+ }
49
+ /** `Array.isArray` for a read-only list, which the built-in guard does not narrow. */
50
+ function isReadonlyList(value) {
51
+ return Array.isArray(value);
52
+ }
53
+ /** The strategy key a definition names, when it names exactly one. */
54
+ function strategyOf(name, definition) {
55
+ let strategy;
56
+ for (const key of Object.keys(definition)) {
57
+ if (!DEFINITION_KEYS.has(key)) {
58
+ throw new InvalidBindingDeclarationError(name, `\`${key}\` is not a definition key`);
59
+ }
60
+ if (isStrategy(key)) {
61
+ if (strategy !== undefined) {
62
+ throw new InvalidBindingDeclarationError(name, `it names two strategies, \`${strategy}\` and \`${key}\``);
63
+ }
64
+ strategy = key;
65
+ }
66
+ }
67
+ if (strategy === undefined) {
68
+ throw new InvalidBindingDeclarationError(name, "it names no strategy");
69
+ }
70
+ return strategy;
71
+ }
72
+ /** The slot `whenNamed` then each `whenTagged` criterion builds, one chain step at a time. */
73
+ function slotOf(definition) {
74
+ let slot = DEFAULT_BINDING_SLOT;
75
+ if (definition.whenNamed !== undefined) {
76
+ slot = withSlotCriterion(slot, slotName.of(definition.whenNamed));
77
+ }
78
+ const tagged = definition.whenTagged;
79
+ if (tagged !== undefined) {
80
+ for (const criterion of isReadonlyList(tagged) ? tagged : [tagged]) {
81
+ slot = withSlotCriterion(slot, criterion);
82
+ }
83
+ }
84
+ return slot;
85
+ }
86
+ // The value type is erased here, once: the overloads above are what a caller is checked against.
87
+ /**
88
+ * @since 0.11.0
89
+ */
90
+ export function binding(key, definition) {
91
+ const name = tokenName(key);
92
+ const strategy = strategyOf(name, definition);
93
+ let kind;
94
+ let target;
95
+ let factory;
96
+ let value;
97
+ let deps;
98
+ switch (strategy) {
99
+ case "to":
100
+ kind = "class";
101
+ target = definition.to;
102
+ break;
103
+ case "toSelf":
104
+ // Plain JavaScript can pass `toSelf: false`; the type admits `true` alone.
105
+ if (definition.toSelf !== true) {
106
+ throw new InvalidBindingDeclarationError(name, "`toSelf` takes `true`");
107
+ }
108
+ if (typeof key !== "function") {
109
+ throw new SelfBindingRequiresClassError(name);
110
+ }
111
+ kind = "class";
112
+ target = key;
113
+ break;
114
+ case "toConstantValue":
115
+ kind = "constant";
116
+ value = definition.toConstantValue;
117
+ break;
118
+ case "toDynamic":
119
+ kind = "dynamic";
120
+ factory = definition.toDynamic;
121
+ break;
122
+ case "toDynamicAsync":
123
+ kind = "dynamic-async";
124
+ factory = definition.toDynamicAsync;
125
+ break;
126
+ case "toResolved":
127
+ case "toResolvedAsync": {
128
+ const declaredDeps = definition.deps;
129
+ if (!isReadonlyList(declaredDeps)) {
130
+ throw new InvalidBindingDeclarationError(name, `\`${strategy}\` needs \`deps\`, an array`);
131
+ }
132
+ kind = strategy === "toResolved" ? "resolved" : "resolved-async";
133
+ factory = strategy === "toResolved" ? definition.toResolved : definition.toResolvedAsync;
134
+ deps = declaredDeps.map((dependency) => normalizeToDescriptor(dependency));
135
+ break;
136
+ }
137
+ case "toAlias":
138
+ kind = "alias";
139
+ target = definition.toAlias;
140
+ break;
141
+ }
142
+ if (deps === undefined && definition.deps !== undefined) {
143
+ throw new InvalidBindingDeclarationError(name, "`deps` belongs to `toResolved` or `toResolvedAsync`");
144
+ }
145
+ let scope = kind === "constant" ? "singleton" : "transient";
146
+ if (definition.scope !== undefined) {
147
+ if (kind === "constant" || kind === "alias") {
148
+ throw new InvalidBindingDeclarationError(name, `\`${strategy}\` takes no \`scope\``);
149
+ }
150
+ if (!SCOPES.has(definition.scope)) {
151
+ throw new InvalidBindingDeclarationError(name, "`scope` is not one of singleton, transient or scoped");
152
+ }
153
+ scope = definition.scope;
154
+ }
155
+ const { onActivation, onDeactivation } = definition;
156
+ if (kind === "alias" && (onActivation !== undefined || onDeactivation !== undefined)) {
157
+ throw new InvalidBindingDeclarationError(name, "`toAlias` takes no lifecycle hook");
158
+ }
159
+ if (onDeactivation !== undefined && kind !== "constant" && scope !== "singleton") {
160
+ throw new InvalidBindingDeclarationError(name, '`onDeactivation` needs `scope: "singleton"`');
161
+ }
162
+ if (definition.many !== undefined && definition.many !== true) {
163
+ throw new InvalidBindingDeclarationError(name, "`many` takes `true`");
164
+ }
165
+ const isMany = definition.many === true;
166
+ const slot = slotOf(definition);
167
+ if (isMany && slot.tags.length !== 0) {
168
+ throw new ManyBindingSlotError(name);
169
+ }
170
+ const declared = {
171
+ [declarationBrand]: true,
172
+ token: key,
173
+ kind,
174
+ scope,
175
+ target,
176
+ factory,
177
+ value,
178
+ deps,
179
+ slot,
180
+ predicate: definition.when,
181
+ isMany,
182
+ activationHook: onActivation,
183
+ deactivationHook: onDeactivation,
184
+ };
185
+ return declared;
186
+ }
@@ -32,6 +32,14 @@ export declare function createBindingSlot(tags: ReadonlyArray<BindingTag>): Bind
32
32
  * @since 0.3.16-canary.0
33
33
  */
34
34
  export declare function bindingSlotEquals(left: BindingSlot, right: BindingSlot): boolean;
35
+ /**
36
+ * Returns the slot with one criterion added, replacing any earlier criterion of the same key.
37
+ *
38
+ * @remarks One criterion per key: re-tagging a key replaces it rather than asking for both values.
39
+ *
40
+ * @since 0.11.0
41
+ */
42
+ export declare function withSlotCriterion(slot: BindingSlot, criterion: BindingTag): BindingSlot;
35
43
  /**
36
44
  * Cached singleton absent — distinguishes "not resolved yet" from a cached `undefined`.
37
45
  *
@@ -48,6 +56,15 @@ export declare const NO_INSTANCE: unique symbol;
48
56
  * @since 0.3.16-canary.0
49
57
  */
50
58
  export declare const DEFAULT_BINDING_SLOT: BindingSlot;
59
+ /**
60
+ * Renders a tag value for a diagnostic, never throwing.
61
+ *
62
+ * @remarks A tag value is caller data — a bigint, a null-prototype object, a throwing `toString` —
63
+ * so stringifying it must not become the error that masks the real one.
64
+ *
65
+ * @since 0.11.0
66
+ */
67
+ export declare function stringifyTagValue(value: unknown): string;
51
68
  /**
52
69
  * Formats a slot for diagnostics — `default`, or its `name:`/`tag:` parts.
53
70
  *
@@ -57,12 +74,13 @@ export declare function bindingSlotToString(slot: BindingSlot): string;
57
74
  interface BindingBase<Value> {
58
75
  readonly identifier: BindingIdentifier;
59
76
  /**
60
- * True while this binding's factory is executing on the current synchronous call stack.
77
+ * True while this binding is being resolved on the current synchronous call stack.
61
78
  *
62
- * @remarks Both cycle guards that can use an `O(1)` flag read this — the sync transient-dynamic
63
- * lane and the async cascade lane — because synchronous code does not interleave, so the flag *is*
64
- * exact path membership. Not optional: the binding builder always initializes it, and a field that
65
- * may be absent is a field that can cost the shared hidden class. Resolver-owned; callers never set it.
79
+ * @remarks Every synchronous cycle guard reads this flag and nothing else: synchronous code does not
80
+ * interleave, so the flag *is* exact path membership at any depth. The async lanes hold it only for a
81
+ * factory's synchronous prefix, or for the seeded path a synchronous call from an async level runs
82
+ * over. Not optional: the binding builder always initializes it, and a field that may be absent is a
83
+ * field that can cost the shared hidden class. Resolver-owned; callers never set it.
66
84
  */
67
85
  inFlight: boolean;
68
86
  /**
@@ -73,6 +91,28 @@ interface BindingBase<Value> {
73
91
  * @remarks Resolver-owned bookkeeping — `registry.add` normalizes it, so callers never set it.
74
92
  */
75
93
  frame: ResolutionFrame | undefined;
94
+ /**
95
+ * The context a transient factory root is handed, built once per binding: the root's path is its own frame
96
+ * alone, so every resolve of the root reads the same one.
97
+ *
98
+ * @remarks Resolver-owned bookkeeping, cleared with the frame it is built over.
99
+ */
100
+ rootContext: ResolutionContext | undefined;
101
+ /**
102
+ * The activation need last computed for this binding, stamped with the versions it was computed
103
+ * under, or {@link NO_ACTIVATION_STAMP}.
104
+ *
105
+ * @remarks Resolver-owned bookkeeping: a field the level reads beats a per-resolver map that a
106
+ * container resolving each binding once would build and never read again.
107
+ */
108
+ activationStamp: number;
109
+ /**
110
+ * Where the binding stands in registration order, or {@link UNREGISTERED_ORDER} before its first add.
111
+ *
112
+ * @remarks Registry-owned: stamped by the first `add` and kept through every re-slot and restore, so a
113
+ * binding taken out and put back returns to its place rather than behind later registrations.
114
+ */
115
+ registrationOrder: number;
76
116
  /**
77
117
  * Cached singleton instance, or {@link NO_INSTANCE}.
78
118
  *
@@ -222,6 +262,18 @@ export interface MembershipField {
222
262
  * @since 0.10.0
223
263
  */
224
264
  export declare function writableMembership(binding: Binding): MembershipField;
265
+ /**
266
+ * The stamp of a binding whose activation need has not been computed under the current versions.
267
+ *
268
+ * @since 0.11.0
269
+ */
270
+ export declare const NO_ACTIVATION_STAMP = -1;
271
+ /**
272
+ * The registration order of a binding no registry has added yet.
273
+ *
274
+ * @since 0.11.0
275
+ */
276
+ export declare const UNREGISTERED_ORDER = -1;
225
277
  /**
226
278
  * Drops the memoized resolution frame, for a refinement that changes what the frame reports.
227
279
  *
@@ -36,6 +36,24 @@ export function bindingSlotEquals(left, right) {
36
36
  }
37
37
  return true;
38
38
  }
39
+ /**
40
+ * Returns the slot with one criterion added, replacing any earlier criterion of the same key.
41
+ *
42
+ * @remarks One criterion per key: re-tagging a key replaces it rather than asking for both values.
43
+ *
44
+ * @since 0.11.0
45
+ */
46
+ export function withSlotCriterion(slot, criterion) {
47
+ const tags = [...slot.tags];
48
+ const existingIndex = tags.findIndex((existing) => existing.key === criterion.key);
49
+ if (existingIndex === -1) {
50
+ tags.push(criterion);
51
+ }
52
+ else {
53
+ tags[existingIndex] = criterion;
54
+ }
55
+ return createBindingSlot(tags);
56
+ }
39
57
  /**
40
58
  * Cached singleton absent — distinguishes "not resolved yet" from a cached `undefined`.
41
59
  *
@@ -52,6 +70,22 @@ export const NO_INSTANCE = Symbol("di:no-instance");
52
70
  * @since 0.3.16-canary.0
53
71
  */
54
72
  export const DEFAULT_BINDING_SLOT = { name: undefined, tags: Object.freeze([]), keyMask: NO_TAG_KEYS };
73
+ /**
74
+ * Renders a tag value for a diagnostic, never throwing.
75
+ *
76
+ * @remarks A tag value is caller data — a bigint, a null-prototype object, a throwing `toString` —
77
+ * so stringifying it must not become the error that masks the real one.
78
+ *
79
+ * @since 0.11.0
80
+ */
81
+ export function stringifyTagValue(value) {
82
+ try {
83
+ return String(value);
84
+ }
85
+ catch {
86
+ return "<unprintable>";
87
+ }
88
+ }
55
89
  /**
56
90
  * Formats a slot for diagnostics — `default`, or its `name:`/`tag:` parts.
57
91
  *
@@ -70,7 +104,7 @@ export function bindingSlotToString(slot) {
70
104
  if (criterion.key === slotName) {
71
105
  continue;
72
106
  }
73
- parts.push(`tag:${criterion.key.name}=${String(criterion.value)}`);
107
+ parts.push(`tag:${criterion.key.name}=${stringifyTagValue(criterion.value)}`);
74
108
  }
75
109
  return parts.join(",");
76
110
  }
@@ -101,6 +135,18 @@ export function writablePredicate(binding) {
101
135
  export function writableMembership(binding) {
102
136
  return binding;
103
137
  }
138
+ /**
139
+ * The stamp of a binding whose activation need has not been computed under the current versions.
140
+ *
141
+ * @since 0.11.0
142
+ */
143
+ export const NO_ACTIVATION_STAMP = -1;
144
+ /**
145
+ * The registration order of a binding no registry has added yet.
146
+ *
147
+ * @since 0.11.0
148
+ */
149
+ export const UNREGISTERED_ORDER = -1;
104
150
  /**
105
151
  * Drops the memoized resolution frame, for a refinement that changes what the frame reports.
106
152
  *
@@ -111,4 +157,5 @@ export function writableMembership(binding) {
111
157
  */
112
158
  export function clearBindingFrame(binding) {
113
159
  binding.frame = undefined;
160
+ binding.rootContext = undefined;
114
161
  }
@@ -1,11 +1,12 @@
1
1
  import type { BindToBuilder } from "#core/binding";
2
+ import type { BindingDeclaration, DeclaredBinding } from "#core/binding-declaration";
2
3
  import type { Token } from "#core/token";
3
4
  import type { Constructor } from "#core/types";
4
5
  declare const SYNC_MODULE_BRAND: unique symbol;
5
6
  declare const ASYNC_MODULE_BRAND: unique symbol;
6
7
  /**
7
- * Key for the module's setup callback. A symbol (not exported from the package root)
8
- * keeps the container-only member out of consumer-facing autocomplete entirely.
8
+ * Key for what a module applies when it loads: its setup callback, or a declared module's
9
+ * declarations. A symbol (not exported from the package root) keeps it out of autocomplete.
9
10
  *
10
11
  * @since 0.5.0-canary.7
11
12
  */
@@ -18,7 +19,7 @@ export declare const MODULE_SETUP: unique symbol;
18
19
  export interface SyncModule {
19
20
  readonly name: string;
20
21
  readonly [SYNC_MODULE_BRAND]: true;
21
- readonly [MODULE_SETUP]: (builder: ModuleBuilder) => void;
22
+ readonly [MODULE_SETUP]: ((builder: ModuleBuilder) => void) | ReadonlyArray<DeclaredBinding>;
22
23
  }
23
24
  /**
24
25
  * A named group of bindings whose setup is async, applied via `loadAsync()`.
@@ -55,6 +56,7 @@ export interface AsyncModuleBuilder {
55
56
  */
56
57
  export declare const SyncModule: {
57
58
  create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule;
59
+ fromBindings(name: string, declarations: ReadonlyArray<BindingDeclaration>): SyncModule;
58
60
  };
59
61
  /**
60
62
  * The companion factory that creates `AsyncModule` values.
@@ -65,12 +67,13 @@ export declare const AsyncModule: {
65
67
  create(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
66
68
  };
67
69
  /**
68
- * The unified module factory — `create` for sync modules, `createAsync` for async ones.
70
+ * The unified module factory — `create` and `fromBindings` for sync modules, `createAsync` for async ones.
69
71
  *
70
72
  * @since 0.3.16-canary.0
71
73
  */
72
74
  export declare const Module: {
73
75
  create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule;
76
+ fromBindings(name: string, declarations: ReadonlyArray<BindingDeclaration>): SyncModule;
74
77
  createAsync(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
75
78
  };
76
79
  /**
@@ -1,9 +1,10 @@
1
+ import { asDeclaredBinding } from "#core/binding-declaration";
1
2
  // ── Branded types (runtime symbols for branding) ─────────────────────────────────────────────────────────────────────
2
3
  const SYNC_MODULE_BRAND = Symbol("di:sync-module");
3
4
  const ASYNC_MODULE_BRAND = Symbol("di:async-module");
4
5
  /**
5
- * Key for the module's setup callback. A symbol (not exported from the package root)
6
- * keeps the container-only member out of consumer-facing autocomplete entirely.
6
+ * Key for what a module applies when it loads: its setup callback, or a declared module's
7
+ * declarations. A symbol (not exported from the package root) keeps it out of autocomplete.
7
8
  *
8
9
  * @since 0.5.0-canary.7
9
10
  */
@@ -22,6 +23,16 @@ export const SyncModule = {
22
23
  [MODULE_SETUP]: setup,
23
24
  };
24
25
  },
26
+ fromBindings(name, declarations) {
27
+ // Copied, so a later write to the caller's list cannot change what the module loads. Not frozen:
28
+ // every load walks this list, and V8 reads a frozen array's elements on a slower path.
29
+ const declared = declarations.map((declaration, index) => asDeclaredBinding(declaration, name, index));
30
+ return {
31
+ name,
32
+ [SYNC_MODULE_BRAND]: true,
33
+ [MODULE_SETUP]: declared,
34
+ };
35
+ },
25
36
  };
26
37
  /**
27
38
  * The companion factory that creates `AsyncModule` values.
@@ -39,7 +50,7 @@ export const AsyncModule = {
39
50
  };
40
51
  // ── Module — unified API ─────────────────────────────────────────────────────────────────────────────────────────────
41
52
  /**
42
- * The unified module factory — `create` for sync modules, `createAsync` for async ones.
53
+ * The unified module factory — `create` and `fromBindings` for sync modules, `createAsync` for async ones.
43
54
  *
44
55
  * @since 0.3.16-canary.0
45
56
  */
@@ -47,6 +58,9 @@ export const Module = {
47
58
  create(name, setup) {
48
59
  return SyncModule.create(name, setup);
49
60
  },
61
+ fromBindings(name, declarations) {
62
+ return SyncModule.fromBindings(name, declarations);
63
+ },
50
64
  createAsync(name, setup) {
51
65
  return AsyncModule.create(name, setup);
52
66
  },
@@ -3,9 +3,7 @@ import type { BindingTag } from "#core/tag";
3
3
  import type { Token } from "#core/token";
4
4
  import type { BindingConstraint, BindingIdentifier, Constructor } from "#core/types";
5
5
  /**
6
- * One container's binding store, indexed by token, binding id, and slot for fast lookup.
7
- *
8
- * @since 0.3.16-canary.0
6
+ * @since 0.11.0
9
7
  */
10
8
  export declare class BindingRegistry {
11
9
  #private;
@@ -49,8 +47,6 @@ export declare class BindingRegistry {
49
47
  * to ask, and a lone binding's one-element list is never materialised here.
50
48
  */
51
49
  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;
54
50
  /** Get binding by ID. */
55
51
  getById(id: BindingIdentifier): Binding | undefined;
56
52
  /** Check if any binding exists for token. */
@@ -77,6 +73,16 @@ export declare class BindingRegistry {
77
73
  * against the request — first-criterion bucketing only guarantees each candidate appears once.
78
74
  */
79
75
  getMultiTagged(token: Token<unknown> | Constructor, criterion: BindingTag): ReadonlyArray<Binding> | undefined;
76
+ /**
77
+ * Whether any binding for the token carries a `when()` predicate.
78
+ *
79
+ * @remarks Only a token that keeps a record can hold a predicate — a lone binding is default-slot
80
+ * with none — so an index fast lane consults this to decline to full selection when the
81
+ * more-specific rule's predicate step could apply.
82
+ */
83
+ hasPredicateCandidate(token: Token<unknown> | Constructor): boolean;
84
+ /** The binding holding a token's default slot — lone or recorded — or `undefined` when none does. */
85
+ getDefaultSlotBinding(token: Token<unknown> | Constructor): Binding | undefined;
80
86
  /** A token's lone default-slot binding — the first read of every synchronous resolve. */
81
87
  getFastDefault(token: Token<unknown> | Constructor): Binding | undefined;
82
88
  /**
@@ -101,6 +107,4 @@ export declare class BindingRegistry {
101
107
  * binding moves to a record because the lone map holds default-slot bindings with no predicate.
102
108
  */
103
109
  setPredicate(binding: Binding, predicate: BindingConstraint): void;
104
- /** Summarize available slot strings for a token (for error messages). */
105
- availableSlotStrings(token: Token<unknown> | Constructor): Array<string>;
106
110
  }