@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.
- package/CHANGELOG.md +164 -0
- package/LICENSE +1 -1
- package/README.md +29 -20
- package/dist/ambient/active-container.d.ts +13 -0
- package/dist/ambient/active-container.js +24 -0
- package/dist/container/binding-builders.d.ts +42 -20
- package/dist/container/binding-builders.js +155 -87
- package/dist/container/container.d.ts +10 -10
- package/dist/container/container.js +55 -23
- package/dist/core/binding.d.ts +55 -57
- package/dist/core/binding.js +11 -37
- package/dist/core/constraint-requirement.d.ts +19 -4
- package/dist/core/constraint-requirement.js +19 -9
- package/dist/core/registry.d.ts +47 -4
- package/dist/core/registry.js +361 -159
- package/dist/core/state-epoch.d.ts +16 -0
- package/dist/core/state-epoch.js +21 -0
- package/dist/core/tag.d.ts +2 -2
- package/dist/core/tag.js +2 -2
- package/dist/core/token.d.ts +24 -4
- package/dist/core/token.js +1 -1
- package/dist/core/types.d.ts +15 -6
- package/dist/decorators/inject.d.ts +1 -1
- package/dist/decorators/inject.js +6 -4
- package/dist/errors/diagnostics.d.ts +2 -0
- package/dist/errors/errors.d.ts +33 -1
- package/dist/errors/errors.js +44 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/injection/descriptor.d.ts +16 -8
- package/dist/injection/descriptor.js +3 -1
- package/dist/injection/resolve-options.d.ts +6 -0
- package/dist/injection/resolve-options.js +16 -0
- package/dist/introspection/dependency-graph.js +10 -5
- package/dist/introspection/inspector.d.ts +3 -1
- package/dist/introspection/inspector.js +10 -24
- package/dist/lifecycle/lifecycle-manager.js +10 -8
- package/dist/lifecycle/scope-manager.js +1 -1
- package/dist/metadata/metadata-reader-token.js +1 -1
- package/dist/resolution/cache/activation-need.d.ts +2 -0
- package/dist/resolution/cache/activation-need.js +13 -6
- package/dist/resolution/cache/binding-lookup-cache.d.ts +34 -1
- package/dist/resolution/cache/binding-lookup-cache.js +96 -12
- package/dist/resolution/cache/class-introspector.d.ts +1 -1
- package/dist/resolution/cache/class-introspector.js +28 -14
- package/dist/resolution/context.d.ts +8 -8
- package/dist/resolution/plan/instantiation-plan.d.ts +12 -0
- package/dist/resolution/plan/instantiation-plan.js +156 -65
- package/dist/resolution/plan/plan-codegen.d.ts +100 -0
- package/dist/resolution/plan/plan-codegen.js +185 -0
- package/dist/resolution/resolver.d.ts +14 -4
- package/dist/resolution/resolver.js +375 -134
- package/dist/resolution/select/binding-select.js +12 -7
- package/dist/resolution/select/constraints.d.ts +7 -4
- package/dist/resolution/select/constraints.js +34 -13
- package/package.json +14 -40
package/dist/core/binding.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
95
|
-
*
|
|
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
|
-
|
|
99
|
-
|
|
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
|
-
*
|
|
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.
|
|
200
|
+
* @since 0.10.0
|
|
215
201
|
*/
|
|
216
|
-
export
|
|
202
|
+
export interface PredicateField {
|
|
203
|
+
predicate: BindingConstraint | undefined;
|
|
204
|
+
}
|
|
217
205
|
/**
|
|
218
|
-
*
|
|
206
|
+
* Narrows a registered binding to its predicate for the registry to rewrite.
|
|
219
207
|
*
|
|
220
|
-
* @
|
|
221
|
-
|
|
222
|
-
|
|
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.
|
|
214
|
+
* @since 0.10.0
|
|
225
215
|
*/
|
|
226
|
-
export interface
|
|
227
|
-
|
|
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
|
|
220
|
+
* Narrows a registered binding to its membership flag for the registry to set.
|
|
233
221
|
*
|
|
234
|
-
* @since 0.
|
|
222
|
+
* @since 0.10.0
|
|
235
223
|
*/
|
|
236
|
-
export declare function
|
|
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`
|
|
241
|
-
*
|
|
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
|
-
|
|
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.
|
package/dist/core/binding.js
CHANGED
|
@@ -82,56 +82,30 @@ let bindingIdCounter = 0;
|
|
|
82
82
|
* @since 0.3.16-canary.0
|
|
83
83
|
*/
|
|
84
84
|
export function generateBindingId() {
|
|
85
|
-
|
|
85
|
+
bindingIdCounter += 1;
|
|
86
|
+
return bindingIdCounter;
|
|
86
87
|
}
|
|
87
88
|
/**
|
|
88
|
-
*
|
|
89
|
+
* Narrows a registered binding to its predicate for the registry to rewrite.
|
|
89
90
|
*
|
|
90
|
-
* @
|
|
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
|
|
102
|
-
|
|
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
|
|
97
|
+
* Narrows a registered binding to its membership flag for the registry to set.
|
|
124
98
|
*
|
|
125
|
-
* @since 0.
|
|
99
|
+
* @since 0.10.0
|
|
126
100
|
*/
|
|
127
|
-
export function
|
|
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`
|
|
134
|
-
*
|
|
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
|
|
16
|
-
* looks valid, while a name is a bare string that
|
|
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
|
-
*
|
|
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,
|
|
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
|
|
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,
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
57
|
-
value: merged.length === 1 ? merged[0] : merged,
|
|
58
|
-
enumerable: false,
|
|
59
|
-
});
|
|
60
|
-
return composite;
|
|
70
|
+
return attachRequirements(composite, merged);
|
|
61
71
|
}
|
package/dist/core/registry.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
}
|