@codefast/di 0.7.0 → 0.8.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 +33 -0
- package/README.md +1 -1
- package/dist/ambient/active-container.d.ts +4 -0
- package/dist/ambient/active-container.d.ts.map +1 -1
- package/dist/ambient/active-container.js +4 -0
- package/dist/ambient/active-container.js.map +1 -1
- package/dist/container/binding-builders.d.ts.map +1 -1
- package/dist/container/binding-builders.js +4 -5
- package/dist/container/binding-builders.js.map +1 -1
- package/dist/container/container.d.ts +6 -0
- package/dist/container/container.d.ts.map +1 -1
- package/dist/container/container.js +5 -4
- package/dist/container/container.js.map +1 -1
- package/dist/core/binding.d.ts +15 -2
- package/dist/core/binding.d.ts.map +1 -1
- package/dist/core/binding.js +29 -4
- package/dist/core/binding.js.map +1 -1
- package/dist/core/module.d.ts +16 -0
- package/dist/core/module.d.ts.map +1 -1
- package/dist/core/module.js +8 -0
- package/dist/core/module.js.map +1 -1
- package/dist/core/registry.d.ts +2 -3
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +16 -61
- package/dist/core/registry.js.map +1 -1
- package/dist/core/tag.d.ts +26 -0
- package/dist/core/tag.d.ts.map +1 -1
- package/dist/core/tag.js +54 -11
- package/dist/core/tag.js.map +1 -1
- package/dist/core/token.d.ts +6 -0
- package/dist/core/token.d.ts.map +1 -1
- package/dist/core/token.js +4 -0
- package/dist/core/token.js.map +1 -1
- package/dist/core/types.d.ts +20 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/decorators/inject.d.ts +2 -0
- package/dist/decorators/inject.d.ts.map +1 -1
- package/dist/decorators/inject.js +2 -0
- package/dist/decorators/inject.js.map +1 -1
- package/dist/decorators/injectable.d.ts +6 -0
- package/dist/decorators/injectable.d.ts.map +1 -1
- package/dist/decorators/injectable.js +4 -0
- package/dist/decorators/injectable.js.map +1 -1
- package/dist/decorators/lifecycle-decorators.d.ts +4 -0
- package/dist/decorators/lifecycle-decorators.d.ts.map +1 -1
- package/dist/decorators/lifecycle-decorators.js +4 -0
- package/dist/decorators/lifecycle-decorators.js.map +1 -1
- package/dist/errors/errors.d.ts +34 -0
- package/dist/errors/errors.d.ts.map +1 -1
- package/dist/errors/errors.js +60 -3
- package/dist/errors/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/injection/descriptor.d.ts +12 -0
- package/dist/injection/descriptor.d.ts.map +1 -1
- package/dist/injection/descriptor.js +8 -0
- package/dist/injection/descriptor.js.map +1 -1
- package/dist/injection/resolve-options.d.ts +17 -12
- package/dist/injection/resolve-options.d.ts.map +1 -1
- package/dist/injection/resolve-options.js +69 -15
- package/dist/injection/resolve-options.js.map +1 -1
- package/dist/introspection/dependency-graph.d.ts +6 -0
- package/dist/introspection/dependency-graph.d.ts.map +1 -1
- package/dist/introspection/dependency-graph.js +24 -9
- package/dist/introspection/dependency-graph.js.map +1 -1
- package/dist/introspection/graph-adapters/cytoscape.d.ts +8 -0
- package/dist/introspection/graph-adapters/cytoscape.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/cytoscape.js +2 -0
- package/dist/introspection/graph-adapters/cytoscape.js.map +1 -1
- package/dist/introspection/graph-adapters/dot.d.ts +2 -0
- package/dist/introspection/graph-adapters/dot.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/dot.js +2 -0
- package/dist/introspection/graph-adapters/dot.js.map +1 -1
- package/dist/introspection/graph-adapters/reactflow.d.ts +8 -0
- package/dist/introspection/graph-adapters/reactflow.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/reactflow.js +2 -0
- package/dist/introspection/graph-adapters/reactflow.js.map +1 -1
- package/dist/introspection/inspector.d.ts +6 -0
- package/dist/introspection/inspector.d.ts.map +1 -1
- package/dist/introspection/inspector.js +2 -0
- package/dist/introspection/inspector.js.map +1 -1
- package/dist/lifecycle/lifecycle-manager.d.ts +2 -0
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -1
- package/dist/lifecycle/lifecycle-manager.js +2 -0
- package/dist/lifecycle/lifecycle-manager.js.map +1 -1
- package/dist/lifecycle/scope-manager.d.ts +2 -0
- package/dist/lifecycle/scope-manager.d.ts.map +1 -1
- package/dist/lifecycle/scope-manager.js +2 -0
- package/dist/lifecycle/scope-manager.js.map +1 -1
- package/dist/metadata/metadata-keys.d.ts +6 -0
- package/dist/metadata/metadata-keys.d.ts.map +1 -1
- package/dist/metadata/metadata-keys.js +6 -0
- package/dist/metadata/metadata-keys.js.map +1 -1
- package/dist/metadata/metadata-reader-token.d.ts +2 -0
- package/dist/metadata/metadata-reader-token.d.ts.map +1 -1
- package/dist/metadata/metadata-reader-token.js +2 -0
- package/dist/metadata/metadata-reader-token.js.map +1 -1
- package/dist/metadata/metadata-types.d.ts +6 -0
- package/dist/metadata/metadata-types.d.ts.map +1 -1
- package/dist/metadata/symbol-metadata-reader.d.ts +4 -0
- package/dist/metadata/symbol-metadata-reader.d.ts.map +1 -1
- package/dist/metadata/symbol-metadata-reader.js +4 -0
- package/dist/metadata/symbol-metadata-reader.js.map +1 -1
- package/dist/resolution/cache/activation-need.d.ts +2 -0
- package/dist/resolution/cache/activation-need.d.ts.map +1 -1
- package/dist/resolution/cache/activation-need.js +2 -0
- package/dist/resolution/cache/activation-need.js.map +1 -1
- package/dist/resolution/cache/binding-lookup-cache.d.ts +2 -2
- package/dist/resolution/cache/binding-lookup-cache.d.ts.map +1 -1
- package/dist/resolution/cache/binding-lookup-cache.js +2 -37
- package/dist/resolution/cache/binding-lookup-cache.js.map +1 -1
- package/dist/resolution/cache/class-introspector.d.ts +2 -0
- package/dist/resolution/cache/class-introspector.d.ts.map +1 -1
- package/dist/resolution/cache/class-introspector.js +2 -0
- package/dist/resolution/cache/class-introspector.js.map +1 -1
- package/dist/resolution/context.d.ts +6 -0
- package/dist/resolution/context.d.ts.map +1 -1
- package/dist/resolution/context.js +4 -0
- package/dist/resolution/context.js.map +1 -1
- package/dist/resolution/plan/instantiation-plan.d.ts +6 -12
- package/dist/resolution/plan/instantiation-plan.d.ts.map +1 -1
- package/dist/resolution/plan/instantiation-plan.js +11 -27
- package/dist/resolution/plan/instantiation-plan.js.map +1 -1
- package/dist/resolution/resolver.d.ts +14 -5
- package/dist/resolution/resolver.d.ts.map +1 -1
- package/dist/resolution/resolver.js +108 -141
- package/dist/resolution/resolver.js.map +1 -1
- package/dist/resolution/select/binding-select.d.ts +4 -5
- package/dist/resolution/select/binding-select.d.ts.map +1 -1
- package/dist/resolution/select/binding-select.js +18 -20
- package/dist/resolution/select/binding-select.js.map +1 -1
- package/dist/resolution/select/constraints.d.ts +16 -0
- package/dist/resolution/select/constraints.d.ts.map +1 -1
- package/dist/resolution/select/constraints.js +16 -0
- package/dist/resolution/select/constraints.js.map +1 -1
- package/package.json +13 -6
- package/src/ambient/active-container.ts +4 -0
- package/src/container/binding-builders.ts +10 -5
- package/src/container/container.ts +9 -4
- package/src/core/binding.ts +33 -5
- package/src/core/module.ts +16 -0
- package/src/core/registry.ts +16 -66
- package/src/core/tag.ts +71 -17
- package/src/core/token.ts +6 -0
- package/src/core/types.ts +20 -0
- package/src/decorators/inject.ts +2 -0
- package/src/decorators/injectable.ts +8 -0
- package/src/decorators/lifecycle-decorators.ts +4 -0
- package/src/errors/errors.ts +62 -5
- package/src/index.ts +1 -1
- package/src/injection/descriptor.ts +12 -0
- package/src/injection/resolve-options.ts +75 -18
- package/src/introspection/dependency-graph.ts +29 -9
- package/src/introspection/graph-adapters/cytoscape.ts +8 -0
- package/src/introspection/graph-adapters/dot.ts +2 -0
- package/src/introspection/graph-adapters/reactflow.ts +8 -0
- package/src/introspection/inspector.ts +6 -0
- package/src/lifecycle/lifecycle-manager.ts +2 -0
- package/src/lifecycle/scope-manager.ts +2 -0
- package/src/metadata/metadata-keys.ts +6 -0
- package/src/metadata/metadata-reader-token.ts +2 -0
- package/src/metadata/metadata-types.ts +6 -0
- package/src/metadata/symbol-metadata-reader.ts +4 -0
- package/src/resolution/cache/activation-need.ts +2 -0
- package/src/resolution/cache/binding-lookup-cache.ts +2 -40
- package/src/resolution/cache/class-introspector.ts +2 -0
- package/src/resolution/context.ts +6 -0
- package/src/resolution/plan/instantiation-plan.ts +15 -38
- package/src/resolution/resolver.ts +122 -141
- package/src/resolution/select/binding-select.ts +18 -21
- package/src/resolution/select/constraints.ts +16 -0
|
@@ -3,7 +3,7 @@ import type { Container } from "#/container/container";
|
|
|
3
3
|
import type { Binding, ConstantBinding, DynamicAsyncBinding, DynamicBinding } from "#/core/binding";
|
|
4
4
|
import { NO_INSTANCE } from "#/core/binding";
|
|
5
5
|
import type { BindingRegistry } from "#/core/registry";
|
|
6
|
-
import { NO_TAG_KEYS } from "#/core/tag";
|
|
6
|
+
import { NO_TAG_KEYS, slotNameCriterionOf } from "#/core/tag";
|
|
7
7
|
import type { Token } from "#/core/token";
|
|
8
8
|
import { tokenName } from "#/core/token";
|
|
9
9
|
import type {
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
TokenNotBoundError,
|
|
29
29
|
} from "#/errors/errors";
|
|
30
30
|
import type { DependencySlot } from "#/injection/resolve-options";
|
|
31
|
-
import {
|
|
31
|
+
import { resolveOptionsForSlot, singleCriterionForSlot, singleCriterionOnlyOf } from "#/injection/resolve-options";
|
|
32
32
|
import type { LifecycleManager } from "#/lifecycle/lifecycle-manager";
|
|
33
33
|
import type { ScopeManager } from "#/lifecycle/scope-manager";
|
|
34
34
|
import { SCOPED_MISS } from "#/lifecycle/scope-manager";
|
|
@@ -71,6 +71,8 @@ const ROOT_CONSTRAINT_CONTEXT = {
|
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
+
* The resolution engine driving binding selection, instantiation, scoping, and lifecycle hooks.
|
|
75
|
+
*
|
|
74
76
|
* @since 0.3.16-canary.0
|
|
75
77
|
*/
|
|
76
78
|
export class DependencyResolver implements ResolverCallbacks {
|
|
@@ -161,45 +163,44 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
161
163
|
|
|
162
164
|
// ── Binding lookup ─────────────────────────────────────────────────────────────────────────────────────────────────
|
|
163
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Finds the binding a request selects in this container, walking up to the parent on a miss.
|
|
168
|
+
*
|
|
169
|
+
* @remarks `singleCriterion` is the request's lone criterion, folded once by the caller so alias
|
|
170
|
+
* hops and parent walks do not re-fold it — `undefined` when the request carries none or several.
|
|
171
|
+
*/
|
|
164
172
|
#findBinding(
|
|
165
173
|
token: Token<unknown> | Constructor,
|
|
166
174
|
options: ResolveOptions | undefined,
|
|
167
175
|
resolutionStack: Array<ResolutionFrame>,
|
|
176
|
+
singleCriterion: BindingTag | undefined,
|
|
168
177
|
): DefaultLookupEntry<DependencyResolver> | undefined {
|
|
169
178
|
if (options === undefined) {
|
|
170
179
|
const fastDefaultBinding = this.#registry.getFastDefault(token);
|
|
171
180
|
if (fastDefaultBinding !== undefined) {
|
|
172
181
|
return { binding: fastDefaultBinding, owner: this };
|
|
173
182
|
}
|
|
174
|
-
} else if (
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
177
|
-
return { binding:
|
|
178
|
-
}
|
|
179
|
-
} else
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
this.#registry.getAll(token).length > MULTI_TAG_INDEX_THRESHOLD &&
|
|
192
|
-
requestedTagKeyMask(options) !== NO_TAG_KEYS
|
|
193
|
-
) {
|
|
194
|
-
// A name-less multi-tag request matches only name-less tagged slots, and every such slot
|
|
195
|
-
// lives in one of the two tag indexes — so their union is the whole candidate set and the
|
|
196
|
-
// token's full list never needs scanning. Selection still owns predicates and specificity.
|
|
197
|
-
const selected = this.#selectMultiTagged(token, options, resolutionStack);
|
|
198
|
-
if (selected !== undefined) {
|
|
199
|
-
return { binding: selected, owner: this };
|
|
200
|
-
}
|
|
201
|
-
return this.#parent === undefined ? undefined : this.#parent.#findBinding(token, options, resolutionStack);
|
|
183
|
+
} else if (singleCriterion !== undefined) {
|
|
184
|
+
const indexed = this.#registry.getSimpleTagged(token, singleCriterion);
|
|
185
|
+
if (indexed !== undefined && this.#satisfiesPredicate(indexed, options, resolutionStack)) {
|
|
186
|
+
return { binding: indexed, owner: this };
|
|
187
|
+
}
|
|
188
|
+
} else if (
|
|
189
|
+
// A threshold switches the data structure, never the semantics: under it the generic scan
|
|
190
|
+
// below beats walking the indexes, and both paths answer identically. Sized first, so a
|
|
191
|
+
// small list pays one length read and nothing else.
|
|
192
|
+
this.#registry.getAll(token).length > MULTI_TAG_INDEX_THRESHOLD &&
|
|
193
|
+
requestedTagKeyMask(options) !== NO_TAG_KEYS
|
|
194
|
+
) {
|
|
195
|
+
// A multi-criterion request matches only slots whose every criterion it carries, and every
|
|
196
|
+
// such slot is in the two tag indexes — their union is the whole candidate set, unscanned.
|
|
197
|
+
const selected = this.#selectMultiTagged(token, options, resolutionStack);
|
|
198
|
+
if (selected !== undefined) {
|
|
199
|
+
return { binding: selected, owner: this };
|
|
202
200
|
}
|
|
201
|
+
return this.#parent === undefined
|
|
202
|
+
? undefined
|
|
203
|
+
: this.#parent.#findBinding(token, options, resolutionStack, singleCriterion);
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
const bindings = this.#registry.getAll(token);
|
|
@@ -208,7 +209,7 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
208
209
|
// specificity to weigh and no ambiguity to report.
|
|
209
210
|
const selected =
|
|
210
211
|
bindings.length === 1
|
|
211
|
-
? this.#
|
|
212
|
+
? matchesSlot(bindings[0]!.slot, options) && this.#satisfiesPredicate(bindings[0]!, options, resolutionStack)
|
|
212
213
|
? bindings[0]
|
|
213
214
|
: undefined
|
|
214
215
|
: selectBinding(bindings, options, this.#makeConstraintContext(resolutionStack, options), tokenName(token));
|
|
@@ -217,7 +218,7 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
217
218
|
}
|
|
218
219
|
}
|
|
219
220
|
if (this.#parent !== undefined) {
|
|
220
|
-
return this.#parent.#findBinding(token, options, resolutionStack);
|
|
221
|
+
return this.#parent.#findBinding(token, options, resolutionStack, singleCriterion);
|
|
221
222
|
}
|
|
222
223
|
return undefined;
|
|
223
224
|
}
|
|
@@ -232,10 +233,14 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
232
233
|
token: Token<unknown> | Constructor,
|
|
233
234
|
options: ResolveOptions | undefined,
|
|
234
235
|
resolutionStack: Array<ResolutionFrame>,
|
|
236
|
+
precomputedCriterion?: BindingTag | null,
|
|
235
237
|
): DefaultLookupEntry<DependencyResolver> {
|
|
238
|
+
// `null` is a caller's "folded: none" — only an absent precomputation re-folds.
|
|
239
|
+
const singleCriterion =
|
|
240
|
+
precomputedCriterion === undefined ? singleCriterionOnlyOf(options) : (precomputedCriterion ?? undefined);
|
|
236
241
|
let currentToken = token;
|
|
237
242
|
let visitedAliasTokens: Set<Token<unknown> | Constructor> | undefined;
|
|
238
|
-
let found = this.#findBinding(currentToken, options, resolutionStack);
|
|
243
|
+
let found = this.#findBinding(currentToken, options, resolutionStack, singleCriterion);
|
|
239
244
|
|
|
240
245
|
while (found !== undefined && found.binding.kind === "alias") {
|
|
241
246
|
const target = found.binding.target;
|
|
@@ -245,7 +250,7 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
245
250
|
}
|
|
246
251
|
visitedAliasTokens.add(target);
|
|
247
252
|
currentToken = target;
|
|
248
|
-
found = this.#findBinding(currentToken, options, resolutionStack);
|
|
253
|
+
found = this.#findBinding(currentToken, options, resolutionStack, singleCriterion);
|
|
249
254
|
}
|
|
250
255
|
|
|
251
256
|
if (found === undefined) {
|
|
@@ -270,7 +275,7 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
270
275
|
token: Token<unknown> | Constructor,
|
|
271
276
|
options: ResolveOptions | undefined,
|
|
272
277
|
): DefaultLookupEntry<DependencyResolver> | undefined {
|
|
273
|
-
return this.#findBinding(token, options, []);
|
|
278
|
+
return this.#findBinding(token, options, [], singleCriterionOnlyOf(options));
|
|
274
279
|
}
|
|
275
280
|
|
|
276
281
|
/**
|
|
@@ -427,22 +432,14 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
427
432
|
const entry = this.#lookup.defaultEntry(token);
|
|
428
433
|
return entry === null ? null : { binding: entry.binding };
|
|
429
434
|
},
|
|
430
|
-
// Exactly what #findBinding's
|
|
431
|
-
// is the compiler's cue to leave the selection to the runtime.
|
|
432
|
-
|
|
433
|
-
const
|
|
434
|
-
if (
|
|
435
|
+
// Exactly what #findBinding's single-criterion lane accepts, minus the half that reads a path:
|
|
436
|
+
// a predicate is the compiler's cue to leave the selection to the runtime.
|
|
437
|
+
lookupPathIndependentEntry: (token, options) => {
|
|
438
|
+
const singleCriterion = singleCriterionOnlyOf(options);
|
|
439
|
+
if (singleCriterion === undefined) {
|
|
435
440
|
return null;
|
|
436
441
|
}
|
|
437
|
-
|
|
438
|
-
},
|
|
439
|
-
// The named rule verbatim, on the single-tag lane's memo.
|
|
440
|
-
lookupPathIndependentTaggedEntry: (token, options) => {
|
|
441
|
-
const singleTag = singleTagOnlyOf(options);
|
|
442
|
-
if (singleTag === undefined) {
|
|
443
|
-
return null;
|
|
444
|
-
}
|
|
445
|
-
const entry = this.#lookup.taggedEntry(token, singleTag);
|
|
442
|
+
const entry = this.#lookup.taggedEntry(token, singleCriterion);
|
|
446
443
|
if (entry === null || entry.binding.predicate !== undefined || !matchesSlot(entry.binding.slot, options)) {
|
|
447
444
|
return null;
|
|
448
445
|
}
|
|
@@ -506,40 +503,30 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
506
503
|
token: Token<Value> | Constructor<Value>,
|
|
507
504
|
options: ResolveOptions | undefined,
|
|
508
505
|
resolutionStack: Array<ResolutionFrame>,
|
|
506
|
+
precomputedCriterion?: BindingTag | null,
|
|
509
507
|
): Value {
|
|
510
|
-
//
|
|
511
|
-
// semantics involve no resolution context (constants, cached singletons).
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
// Everything else keeps the full path (context, activation, guards).
|
|
523
|
-
}
|
|
524
|
-
} else if (options !== undefined) {
|
|
525
|
-
// Single-tag fast lane: the named lane's tagged twin, memoizing the chain walk.
|
|
526
|
-
const singleTag = singleTagOnlyOf(options);
|
|
527
|
-
if (singleTag !== undefined) {
|
|
528
|
-
const taggedEntry = this.#lookup.taggedEntry(token, singleTag);
|
|
529
|
-
if (taggedEntry !== null) {
|
|
530
|
-
const taggedBinding = taggedEntry.binding;
|
|
531
|
-
if (taggedEntry.owner.#isPlainConstant(taggedBinding)) {
|
|
532
|
-
return taggedBinding.value as Value;
|
|
508
|
+
// Single-criterion fast lane (a lone name folds here too): memoized lookup, dispatching just
|
|
509
|
+
// the shapes whose semantics involve no resolution context (constants, cached singletons).
|
|
510
|
+
let singleCriterion: BindingTag | undefined;
|
|
511
|
+
if (options !== undefined) {
|
|
512
|
+
singleCriterion =
|
|
513
|
+
precomputedCriterion === undefined ? singleCriterionOnlyOf(options) : (precomputedCriterion ?? undefined);
|
|
514
|
+
if (singleCriterion !== undefined) {
|
|
515
|
+
const indexedEntry = this.#lookup.taggedEntry(token, singleCriterion);
|
|
516
|
+
if (indexedEntry !== null) {
|
|
517
|
+
const indexedBinding = indexedEntry.binding;
|
|
518
|
+
if (indexedEntry.owner.#isPlainConstant(indexedBinding)) {
|
|
519
|
+
return indexedBinding.value as Value;
|
|
533
520
|
}
|
|
534
|
-
if (
|
|
535
|
-
return
|
|
521
|
+
if (indexedBinding.scope === "singleton" && indexedBinding.instance !== NO_INSTANCE) {
|
|
522
|
+
return indexedBinding.instance as Value;
|
|
536
523
|
}
|
|
537
524
|
// Everything else keeps the full path (context, activation, guards).
|
|
538
525
|
}
|
|
539
526
|
}
|
|
540
527
|
}
|
|
541
528
|
|
|
542
|
-
const { binding, owner } = this.#requireBinding(token, options, resolutionStack);
|
|
529
|
+
const { binding, owner } = this.#requireBinding(token, options, resolutionStack, singleCriterion ?? null);
|
|
543
530
|
|
|
544
531
|
// A singleton owned by a parent resolver is resolved there, so the parent caches it.
|
|
545
532
|
if (binding.scope === "singleton" && owner !== this) {
|
|
@@ -734,20 +721,26 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
734
721
|
return this.resolveAll(dep.token, options, resolutionStack);
|
|
735
722
|
}
|
|
736
723
|
if (dep.optional) {
|
|
737
|
-
return this.resolveOptional(dep.token, options, resolutionStack);
|
|
724
|
+
return this.resolveOptional(dep.token, options, resolutionStack, singleCriterionForSlot(dep));
|
|
738
725
|
}
|
|
739
726
|
if (options === undefined) {
|
|
740
727
|
return this.resolveFromContext(dep.token, resolutionStack);
|
|
741
728
|
}
|
|
742
|
-
return this.resolve(dep.token, options, resolutionStack);
|
|
729
|
+
return this.resolve(dep.token, options, resolutionStack, singleCriterionForSlot(dep));
|
|
743
730
|
}
|
|
744
731
|
|
|
745
732
|
resolveOptional<Value>(
|
|
746
733
|
token: Token<Value> | Constructor<Value>,
|
|
747
734
|
options: ResolveOptions | undefined,
|
|
748
735
|
resolutionStack: Array<ResolutionFrame>,
|
|
736
|
+
precomputedCriterion?: BindingTag | null,
|
|
749
737
|
): Value | undefined {
|
|
750
|
-
const entry = this.#findBinding(
|
|
738
|
+
const entry = this.#findBinding(
|
|
739
|
+
token,
|
|
740
|
+
options,
|
|
741
|
+
resolutionStack,
|
|
742
|
+
precomputedCriterion === undefined ? singleCriterionOnlyOf(options) : (precomputedCriterion ?? undefined),
|
|
743
|
+
);
|
|
751
744
|
if (entry === undefined) {
|
|
752
745
|
return undefined;
|
|
753
746
|
}
|
|
@@ -860,13 +853,24 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
860
853
|
return this.#resolveBindingAsync(binding, undefined, resolutionStack, branchDepth, owner);
|
|
861
854
|
}
|
|
862
855
|
|
|
856
|
+
/**
|
|
857
|
+
* Instantiates one owned binding directly, bypassing selection.
|
|
858
|
+
*
|
|
859
|
+
* @remarks Warm-up must build the binding it inspected: re-selecting by the slot's own criteria
|
|
860
|
+
* could pick a different candidate whose criteria are a subset of them.
|
|
861
|
+
*/
|
|
862
|
+
warmBindingAsync(binding: Binding, options: ResolveOptions | undefined): Promise<unknown> {
|
|
863
|
+
return this.#resolveBindingAsync(binding, options, [], ROOT_BRANCH, this);
|
|
864
|
+
}
|
|
865
|
+
|
|
863
866
|
async resolveAsync<Value>(
|
|
864
867
|
token: Token<Value> | Constructor<Value>,
|
|
865
868
|
options: ResolveOptions | undefined,
|
|
866
869
|
resolutionStack: Array<ResolutionFrame>,
|
|
867
870
|
branchDepth: BranchDepth = UNOWNED_BRANCH,
|
|
871
|
+
precomputedCriterion?: BindingTag | null,
|
|
868
872
|
): Promise<Value> {
|
|
869
|
-
const { binding, owner } = this.#requireBinding(token, options, resolutionStack);
|
|
873
|
+
const { binding, owner } = this.#requireBinding(token, options, resolutionStack, precomputedCriterion);
|
|
870
874
|
|
|
871
875
|
if (binding.scope === "singleton" && owner !== this) {
|
|
872
876
|
return owner.#resolveBindingAsync(binding, options, resolutionStack, branchDepth, owner) as Promise<Value>;
|
|
@@ -1105,12 +1109,12 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
1105
1109
|
return this.resolveAllAsync(dep.token, options, resolutionStack, branchDepth);
|
|
1106
1110
|
}
|
|
1107
1111
|
if (dep.optional) {
|
|
1108
|
-
return this.resolveOptionalAsync(dep.token, options, resolutionStack, branchDepth);
|
|
1112
|
+
return this.resolveOptionalAsync(dep.token, options, resolutionStack, branchDepth, singleCriterionForSlot(dep));
|
|
1109
1113
|
}
|
|
1110
1114
|
if (options === undefined) {
|
|
1111
1115
|
return this.resolveAsyncFromContext(dep.token, resolutionStack, branchDepth);
|
|
1112
1116
|
}
|
|
1113
|
-
return this.resolveAsync(dep.token, options, resolutionStack, branchDepth);
|
|
1117
|
+
return this.resolveAsync(dep.token, options, resolutionStack, branchDepth, singleCriterionForSlot(dep));
|
|
1114
1118
|
}
|
|
1115
1119
|
|
|
1116
1120
|
async resolveOptionalAsync<Value>(
|
|
@@ -1118,8 +1122,14 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
1118
1122
|
options: ResolveOptions | undefined,
|
|
1119
1123
|
resolutionStack: Array<ResolutionFrame>,
|
|
1120
1124
|
branchDepth: BranchDepth = UNOWNED_BRANCH,
|
|
1125
|
+
precomputedCriterion?: BindingTag | null,
|
|
1121
1126
|
): Promise<Value | undefined> {
|
|
1122
|
-
const entry = this.#findBinding(
|
|
1127
|
+
const entry = this.#findBinding(
|
|
1128
|
+
token,
|
|
1129
|
+
options,
|
|
1130
|
+
resolutionStack,
|
|
1131
|
+
precomputedCriterion === undefined ? singleCriterionOnlyOf(options) : (precomputedCriterion ?? undefined),
|
|
1132
|
+
);
|
|
1123
1133
|
if (entry === undefined) {
|
|
1124
1134
|
return undefined;
|
|
1125
1135
|
}
|
|
@@ -1170,29 +1180,11 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
1170
1180
|
return result;
|
|
1171
1181
|
}
|
|
1172
1182
|
|
|
1173
|
-
/** Every binding the chain's name indexes hold for one name, nearest container first. */
|
|
1174
|
-
#namedBindingsFromChain(token: Token<unknown> | Constructor, name: string): Array<Binding> {
|
|
1175
|
-
// A name resolves to at most one binding per registry, so a root container's answer is built
|
|
1176
|
-
// whole rather than grown — the list is sized at its allocation.
|
|
1177
|
-
const ownBinding = this.#registry.getSimpleNamed(token, name);
|
|
1178
|
-
if (this.#parent === undefined) {
|
|
1179
|
-
return ownBinding === undefined ? [] : [ownBinding];
|
|
1180
|
-
}
|
|
1181
|
-
const result: Array<Binding> = ownBinding === undefined ? [] : [ownBinding];
|
|
1182
|
-
for (let current: DependencyResolver | undefined = this.#parent; current !== undefined; current = current.#parent) {
|
|
1183
|
-
const binding = current.#registry.getSimpleNamed(token, name);
|
|
1184
|
-
if (binding !== undefined) {
|
|
1185
|
-
result.push(binding);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
return result;
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
1183
|
/**
|
|
1192
|
-
* The candidates
|
|
1184
|
+
* The candidates the index can name outright, or `null` when the request needs full selection.
|
|
1193
1185
|
*
|
|
1194
|
-
* @remarks Kept off `#candidateBindings` so that method stays the size it was: a request
|
|
1195
|
-
* index
|
|
1186
|
+
* @remarks Kept off `#candidateBindings` so that method stays the size it was: a request the
|
|
1187
|
+
* index cannot serve must not pay for the shape it does.
|
|
1196
1188
|
* An index has matched the slot already, but a hit may still carry a predicate, and evaluating
|
|
1197
1189
|
* that is the selection path's job.
|
|
1198
1190
|
*/
|
|
@@ -1201,31 +1193,26 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
1201
1193
|
options: ResolveOptions,
|
|
1202
1194
|
resolutionStack: Array<ResolutionFrame>,
|
|
1203
1195
|
): ReadonlyArray<Binding> | null {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
return anyPredicate(named)
|
|
1207
|
-
? selectAllBindings(named, options, this.#makeConstraintContext(resolutionStack, options))
|
|
1208
|
-
: named;
|
|
1209
|
-
}
|
|
1210
|
-
const singleTag = singleTagOnlyOf(options);
|
|
1211
|
-
if (singleTag === undefined) {
|
|
1196
|
+
const singleCriterion = singleCriterionOnlyOf(options);
|
|
1197
|
+
if (singleCriterion === undefined) {
|
|
1212
1198
|
return null;
|
|
1213
1199
|
}
|
|
1214
|
-
const
|
|
1215
|
-
return anyPredicate(
|
|
1216
|
-
? selectAllBindings(
|
|
1217
|
-
:
|
|
1200
|
+
const indexed = this.#taggedBindingsFromChain(token, singleCriterion);
|
|
1201
|
+
return anyPredicate(indexed)
|
|
1202
|
+
? selectAllBindings(indexed, options, this.#makeConstraintContext(resolutionStack, options))
|
|
1203
|
+
: indexed;
|
|
1218
1204
|
}
|
|
1219
1205
|
|
|
1220
1206
|
/**
|
|
1221
|
-
* Every binding the chain's
|
|
1207
|
+
* Every binding the chain's criterion indexes hold for one criterion, nearest container first.
|
|
1222
1208
|
*
|
|
1223
|
-
* @remarks A request for one
|
|
1224
|
-
* is the whole candidate set rather than a prefilter — a
|
|
1209
|
+
* @remarks A request for exactly one criterion matches exactly the bindings the index keys, so
|
|
1210
|
+
* this is the whole candidate set rather than a prefilter — a multi-criterion slot cannot satisfy
|
|
1211
|
+
* it.
|
|
1225
1212
|
*/
|
|
1226
1213
|
#taggedBindingsFromChain(token: Token<unknown> | Constructor, tag: BindingTag): Array<Binding> {
|
|
1227
|
-
// A
|
|
1228
|
-
// rather than grown — the
|
|
1214
|
+
// A criterion matches at most one binding per registry, so a root container's answer is built
|
|
1215
|
+
// whole rather than grown — the list is sized at its allocation.
|
|
1229
1216
|
const ownBinding = this.#registry.getSimpleTagged(token, tag);
|
|
1230
1217
|
if (this.#parent === undefined) {
|
|
1231
1218
|
return ownBinding === undefined ? [] : [ownBinding];
|
|
@@ -1282,18 +1269,23 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
1282
1269
|
return buildConstraintContext(resolutionStack, options);
|
|
1283
1270
|
}
|
|
1284
1271
|
|
|
1285
|
-
/** Selection for a
|
|
1272
|
+
/** Selection for a multi-criterion request, over the union of the two tag indexes. */
|
|
1286
1273
|
#selectMultiTagged(
|
|
1287
1274
|
token: Token<unknown> | Constructor,
|
|
1288
1275
|
options: ResolveOptions,
|
|
1289
1276
|
resolutionStack: Array<ResolutionFrame>,
|
|
1290
1277
|
): Binding | undefined {
|
|
1291
1278
|
const candidates: Array<Binding> = [];
|
|
1292
|
-
|
|
1279
|
+
const gathered: Array<BindingTag> = [];
|
|
1280
|
+
if (options.name !== undefined) {
|
|
1281
|
+
// Read, not minted: an unminted name has no criterion, so no slot can carry it.
|
|
1282
|
+
this.#gatherTagCandidates(token, slotNameCriterionOf(options.name), candidates, gathered);
|
|
1283
|
+
}
|
|
1284
|
+
this.#gatherTagCandidates(token, options.tag, candidates, gathered);
|
|
1293
1285
|
const listed = options.tags;
|
|
1294
1286
|
if (listed !== undefined) {
|
|
1295
1287
|
for (let index = 0; index < listed.length; index += 1) {
|
|
1296
|
-
this.#gatherTagCandidates(token, listed[index], candidates);
|
|
1288
|
+
this.#gatherTagCandidates(token, listed[index], candidates, gathered);
|
|
1297
1289
|
}
|
|
1298
1290
|
}
|
|
1299
1291
|
if (candidates.length === 0) {
|
|
@@ -1307,37 +1299,26 @@ export class DependencyResolver implements ResolverCallbacks {
|
|
|
1307
1299
|
token: Token<unknown> | Constructor,
|
|
1308
1300
|
criterion: BindingTag | undefined,
|
|
1309
1301
|
out: Array<Binding>,
|
|
1302
|
+
gathered: Array<BindingTag>,
|
|
1310
1303
|
): void {
|
|
1311
|
-
|
|
1304
|
+
// Distinct criteria never share a binding — a slot lives in exactly one bucket — so deduping
|
|
1305
|
+
// by criterion covers a request repeating one across its spellings, without scanning `out`.
|
|
1306
|
+
if (criterion === undefined || gathered.includes(criterion)) {
|
|
1312
1307
|
return;
|
|
1313
1308
|
}
|
|
1314
|
-
|
|
1309
|
+
gathered.push(criterion);
|
|
1315
1310
|
const single = this.#registry.getSimpleTagged(token, criterion);
|
|
1316
|
-
if (single !== undefined
|
|
1311
|
+
if (single !== undefined) {
|
|
1317
1312
|
out.push(single);
|
|
1318
1313
|
}
|
|
1319
1314
|
const bucket = this.#registry.getMultiTagged(token, criterion);
|
|
1320
1315
|
if (bucket !== undefined) {
|
|
1321
1316
|
for (let index = 0; index < bucket.length; index += 1) {
|
|
1322
|
-
|
|
1323
|
-
if (!out.includes(candidate)) {
|
|
1324
|
-
out.push(candidate);
|
|
1325
|
-
}
|
|
1317
|
+
out.push(bucket[index]!);
|
|
1326
1318
|
}
|
|
1327
1319
|
}
|
|
1328
1320
|
}
|
|
1329
1321
|
|
|
1330
|
-
#matchesBindingFast(
|
|
1331
|
-
binding: Binding,
|
|
1332
|
-
options: ResolveOptions | undefined,
|
|
1333
|
-
resolutionStack: Array<ResolutionFrame>,
|
|
1334
|
-
): boolean {
|
|
1335
|
-
if (!matchesSlot(binding.slot, options)) {
|
|
1336
|
-
return false;
|
|
1337
|
-
}
|
|
1338
|
-
return this.#satisfiesPredicate(binding, options, resolutionStack);
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
1322
|
/** The predicate half of a match, for a lane whose index has already settled the slot. */
|
|
1342
1323
|
#satisfiesPredicate(
|
|
1343
1324
|
binding: Binding,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Binding, BindingSlot } from "#/core/binding";
|
|
2
2
|
import type { BindingTag, TagKeyMask } from "#/core/tag";
|
|
3
|
-
import { coversTagKeys, NO_TAG_KEYS } from "#/core/tag";
|
|
3
|
+
import { coversTagKeys, NO_TAG_KEYS, slotName, slotNameCriterionOf } from "#/core/tag";
|
|
4
4
|
import type { ConstraintContext, ResolveOptions } from "#/core/types";
|
|
5
5
|
import { AmbiguousBindingError } from "#/errors/errors";
|
|
6
6
|
|
|
@@ -41,7 +41,7 @@ export function selectBinding(
|
|
|
41
41
|
}
|
|
42
42
|
// Reached only where the throw was: a slot declaring more of what the request carries is the more
|
|
43
43
|
// specific match, so an over-specified request resolves instead of being ambiguous.
|
|
44
|
-
const mostSpecific =
|
|
44
|
+
const mostSpecific = mostSpecificByCriterionCount(candidates);
|
|
45
45
|
if (mostSpecific !== undefined) {
|
|
46
46
|
return mostSpecific;
|
|
47
47
|
}
|
|
@@ -51,8 +51,8 @@ export function selectBinding(
|
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/** The lone candidate declaring more
|
|
55
|
-
function
|
|
54
|
+
/** The lone candidate declaring more criteria than every other, or `undefined` when that is a tie. */
|
|
55
|
+
function mostSpecificByCriterionCount(candidates: ReadonlyArray<Binding>): Binding | undefined {
|
|
56
56
|
let best: Binding | undefined;
|
|
57
57
|
let bestCount = -1;
|
|
58
58
|
let tied = false;
|
|
@@ -118,35 +118,24 @@ function filterBindings(
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
function hasSlotCriterion(options: ResolveOptions): boolean {
|
|
121
|
-
return
|
|
121
|
+
return requestedTagKeyMask(options) !== NO_TAG_KEYS;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
* Whether a binding's slot satisfies a request:
|
|
126
|
-
*
|
|
125
|
+
* Whether a binding's slot satisfies a request: every criterion the slot declares must be among
|
|
126
|
+
* the request's criteria, a name spelling either side folding to the reserved criterion.
|
|
127
127
|
*
|
|
128
|
-
* @remarks
|
|
129
|
-
*
|
|
130
|
-
* identity.
|
|
128
|
+
* @remarks A key-mask subset test runs before any criterion is read, so a slot the request cannot
|
|
129
|
+
* satisfy is rejected in one word compare. Criteria are interned, so what follows is identity.
|
|
131
130
|
*
|
|
132
131
|
* @since 0.5.0-canary.9
|
|
133
132
|
*/
|
|
134
133
|
export function matchesSlot(slot: BindingSlot, options: ResolveOptions | undefined): boolean {
|
|
135
|
-
const requestedName = options?.name;
|
|
136
|
-
|
|
137
|
-
if (slot.name !== undefined) {
|
|
138
|
-
if (slot.name !== requestedName) {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
} else if (requestedName !== undefined) {
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
134
|
const slotMask = slot.keyMask;
|
|
146
135
|
const requestMask = requestedTagKeyMask(options);
|
|
147
136
|
|
|
148
137
|
if (slotMask === NO_TAG_KEYS) {
|
|
149
|
-
// A request carrying
|
|
138
|
+
// A request carrying any criterion never falls back to the default slot.
|
|
150
139
|
return requestMask === NO_TAG_KEYS;
|
|
151
140
|
}
|
|
152
141
|
if (!coversTagKeys(requestMask, slotMask)) {
|
|
@@ -178,6 +167,9 @@ export function requestedTagKeyMask(options: ResolveOptions | undefined): TagKey
|
|
|
178
167
|
const listed = options.tags;
|
|
179
168
|
let mask = single === undefined ? NO_TAG_KEYS : single.mask;
|
|
180
169
|
|
|
170
|
+
if (options.name !== undefined) {
|
|
171
|
+
mask = (mask | slotName.mask) as TagKeyMask;
|
|
172
|
+
}
|
|
181
173
|
if (listed !== undefined) {
|
|
182
174
|
for (let index = 0; index < listed.length; index += 1) {
|
|
183
175
|
mask = (mask | listed[index]!.mask) as TagKeyMask;
|
|
@@ -192,6 +184,11 @@ function requestCarries(options: ResolveOptions | undefined, criterion: BindingT
|
|
|
192
184
|
if (options === undefined) {
|
|
193
185
|
return false;
|
|
194
186
|
}
|
|
187
|
+
// The `name` spelling folds through the intern read, so this too is identity — a hand-built
|
|
188
|
+
// criterion matches nothing on any lane, and an unminted name retains nothing.
|
|
189
|
+
if (criterion.key === slotName && options.name !== undefined && criterion === slotNameCriterionOf(options.name)) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
195
192
|
if (options.tag === criterion) {
|
|
196
193
|
return true;
|
|
197
194
|
}
|
|
@@ -7,6 +7,8 @@ import type { BindingConstraint, Constructor } from "#/core/types";
|
|
|
7
7
|
import { EmptyTagCriteriaError } from "#/errors/errors";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
+
* Matches when the direct parent frame resolves the given token.
|
|
11
|
+
*
|
|
10
12
|
* @since 0.3.16-canary.0
|
|
11
13
|
*/
|
|
12
14
|
export function whenParentIs(token: Token<unknown> | Constructor): BindingConstraint {
|
|
@@ -16,6 +18,8 @@ export function whenParentIs(token: Token<unknown> | Constructor): BindingConstr
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
/**
|
|
21
|
+
* Matches when there is no direct parent frame, or it resolves a different token.
|
|
22
|
+
*
|
|
19
23
|
* @since 0.3.16-canary.0
|
|
20
24
|
*/
|
|
21
25
|
export function whenNoParentIs(token: Token<unknown> | Constructor): BindingConstraint {
|
|
@@ -25,6 +29,8 @@ export function whenNoParentIs(token: Token<unknown> | Constructor): BindingCons
|
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
/**
|
|
32
|
+
* Matches when at least one ancestor frame resolves the given token.
|
|
33
|
+
*
|
|
28
34
|
* @since 0.3.16-canary.0
|
|
29
35
|
*/
|
|
30
36
|
export function whenAnyAncestorIs(token: Token<unknown> | Constructor): BindingConstraint {
|
|
@@ -34,6 +40,8 @@ export function whenAnyAncestorIs(token: Token<unknown> | Constructor): BindingC
|
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
/**
|
|
43
|
+
* Matches when no ancestor frame resolves the given token.
|
|
44
|
+
*
|
|
37
45
|
* @since 0.3.16-canary.0
|
|
38
46
|
*/
|
|
39
47
|
export function whenNoAncestorIs(token: Token<unknown> | Constructor): BindingConstraint {
|
|
@@ -43,6 +51,8 @@ export function whenNoAncestorIs(token: Token<unknown> | Constructor): BindingCo
|
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
/**
|
|
54
|
+
* Matches when the direct parent slot carries the given name.
|
|
55
|
+
*
|
|
46
56
|
* @since 0.3.16-canary.0
|
|
47
57
|
*/
|
|
48
58
|
export function whenParentNamed(name: string): BindingConstraint {
|
|
@@ -54,6 +64,8 @@ export function whenParentNamed(name: string): BindingConstraint {
|
|
|
54
64
|
}
|
|
55
65
|
|
|
56
66
|
/**
|
|
67
|
+
* Matches when at least one ancestor slot carries the given name.
|
|
68
|
+
*
|
|
57
69
|
* @since 0.3.16-canary.0
|
|
58
70
|
*/
|
|
59
71
|
export function whenAnyAncestorNamed(name: string): BindingConstraint {
|
|
@@ -65,6 +77,8 @@ export function whenAnyAncestorNamed(name: string): BindingConstraint {
|
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
/**
|
|
80
|
+
* Matches when the direct parent slot carries the given tag pair.
|
|
81
|
+
*
|
|
68
82
|
* @since 0.3.16-canary.0
|
|
69
83
|
*/
|
|
70
84
|
export function whenParentTagged(criterion: BindingTag): BindingConstraint {
|
|
@@ -73,6 +87,8 @@ export function whenParentTagged(criterion: BindingTag): BindingConstraint {
|
|
|
73
87
|
}
|
|
74
88
|
|
|
75
89
|
/**
|
|
90
|
+
* Matches when at least one ancestor slot carries the given tag pair.
|
|
91
|
+
*
|
|
76
92
|
* @since 0.3.16-canary.0
|
|
77
93
|
*/
|
|
78
94
|
export function whenAnyAncestorTagged(criterion: BindingTag): BindingConstraint {
|