@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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { NO_INSTANCE } from "#/core/binding";
|
|
2
|
-
import { NO_TAG_KEYS } from "#/core/tag";
|
|
2
|
+
import { NO_TAG_KEYS, slotNameCriterionOf } from "#/core/tag";
|
|
3
3
|
import { tokenName } from "#/core/token";
|
|
4
4
|
import { AsyncActivationError, AsyncResolutionError, CircularDependencyError, DisposedContainerError, InternalError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, TokenNotBoundError, } from "#/errors/errors";
|
|
5
|
-
import {
|
|
5
|
+
import { resolveOptionsForSlot, singleCriterionForSlot, singleCriterionOnlyOf } from "#/injection/resolve-options";
|
|
6
6
|
import { SCOPED_MISS } from "#/lifecycle/scope-manager";
|
|
7
7
|
import { ActivationNeedCache } from "#/resolution/cache/activation-need";
|
|
8
8
|
import { BindingLookupCache } from "#/resolution/cache/binding-lookup-cache";
|
|
@@ -24,6 +24,8 @@ const ROOT_CONSTRAINT_CONTEXT = {
|
|
|
24
24
|
currentResolveOptions: undefined,
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
|
+
* The resolution engine driving binding selection, instantiation, scoping, and lifecycle hooks.
|
|
28
|
+
*
|
|
27
29
|
* @since 0.3.16-canary.0
|
|
28
30
|
*/
|
|
29
31
|
export class DependencyResolver {
|
|
@@ -94,49 +96,47 @@ export class DependencyResolver {
|
|
|
94
96
|
};
|
|
95
97
|
}
|
|
96
98
|
// ── Binding lookup ─────────────────────────────────────────────────────────────────────────────────────────────────
|
|
97
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Finds the binding a request selects in this container, walking up to the parent on a miss.
|
|
101
|
+
*
|
|
102
|
+
* @remarks `singleCriterion` is the request's lone criterion, folded once by the caller so alias
|
|
103
|
+
* hops and parent walks do not re-fold it — `undefined` when the request carries none or several.
|
|
104
|
+
*/
|
|
105
|
+
#findBinding(token, options, resolutionStack, singleCriterion) {
|
|
98
106
|
if (options === undefined) {
|
|
99
107
|
const fastDefaultBinding = this.#registry.getFastDefault(token);
|
|
100
108
|
if (fastDefaultBinding !== undefined) {
|
|
101
109
|
return { binding: fastDefaultBinding, owner: this };
|
|
102
110
|
}
|
|
103
111
|
}
|
|
104
|
-
else if (
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
return { binding:
|
|
112
|
+
else if (singleCriterion !== undefined) {
|
|
113
|
+
const indexed = this.#registry.getSimpleTagged(token, singleCriterion);
|
|
114
|
+
if (indexed !== undefined && this.#satisfiesPredicate(indexed, options, resolutionStack)) {
|
|
115
|
+
return { binding: indexed, owner: this };
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
|
-
else
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// small list pays one length read and nothing else.
|
|
122
|
-
this.#registry.getAll(token).length > MULTI_TAG_INDEX_THRESHOLD &&
|
|
123
|
-
requestedTagKeyMask(options) !== NO_TAG_KEYS) {
|
|
124
|
-
// A name-less multi-tag request matches only name-less tagged slots, and every such slot
|
|
125
|
-
// lives in one of the two tag indexes — so their union is the whole candidate set and the
|
|
126
|
-
// token's full list never needs scanning. Selection still owns predicates and specificity.
|
|
127
|
-
const selected = this.#selectMultiTagged(token, options, resolutionStack);
|
|
128
|
-
if (selected !== undefined) {
|
|
129
|
-
return { binding: selected, owner: this };
|
|
130
|
-
}
|
|
131
|
-
return this.#parent === undefined ? undefined : this.#parent.#findBinding(token, options, resolutionStack);
|
|
118
|
+
else if (
|
|
119
|
+
// A threshold switches the data structure, never the semantics: under it the generic scan
|
|
120
|
+
// below beats walking the indexes, and both paths answer identically. Sized first, so a
|
|
121
|
+
// small list pays one length read and nothing else.
|
|
122
|
+
this.#registry.getAll(token).length > MULTI_TAG_INDEX_THRESHOLD &&
|
|
123
|
+
requestedTagKeyMask(options) !== NO_TAG_KEYS) {
|
|
124
|
+
// A multi-criterion request matches only slots whose every criterion it carries, and every
|
|
125
|
+
// such slot is in the two tag indexes — their union is the whole candidate set, unscanned.
|
|
126
|
+
const selected = this.#selectMultiTagged(token, options, resolutionStack);
|
|
127
|
+
if (selected !== undefined) {
|
|
128
|
+
return { binding: selected, owner: this };
|
|
132
129
|
}
|
|
130
|
+
return this.#parent === undefined
|
|
131
|
+
? undefined
|
|
132
|
+
: this.#parent.#findBinding(token, options, resolutionStack, singleCriterion);
|
|
133
133
|
}
|
|
134
134
|
const bindings = this.#registry.getAll(token);
|
|
135
135
|
if (bindings.length > 0) {
|
|
136
136
|
// A lone candidate is its own selection: matching it is the whole decision, with no
|
|
137
137
|
// specificity to weigh and no ambiguity to report.
|
|
138
138
|
const selected = bindings.length === 1
|
|
139
|
-
? this.#
|
|
139
|
+
? matchesSlot(bindings[0].slot, options) && this.#satisfiesPredicate(bindings[0], options, resolutionStack)
|
|
140
140
|
? bindings[0]
|
|
141
141
|
: undefined
|
|
142
142
|
: selectBinding(bindings, options, this.#makeConstraintContext(resolutionStack, options), tokenName(token));
|
|
@@ -145,7 +145,7 @@ export class DependencyResolver {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
if (this.#parent !== undefined) {
|
|
148
|
-
return this.#parent.#findBinding(token, options, resolutionStack);
|
|
148
|
+
return this.#parent.#findBinding(token, options, resolutionStack, singleCriterion);
|
|
149
149
|
}
|
|
150
150
|
return undefined;
|
|
151
151
|
}
|
|
@@ -155,10 +155,12 @@ export class DependencyResolver {
|
|
|
155
155
|
* @remarks Alias hops are followed iteratively with exact cycle detection — a revisited alias
|
|
156
156
|
* token raises {@link CircularDependencyError} instead of overflowing the call stack.
|
|
157
157
|
*/
|
|
158
|
-
#requireBinding(token, options, resolutionStack) {
|
|
158
|
+
#requireBinding(token, options, resolutionStack, precomputedCriterion) {
|
|
159
|
+
// `null` is a caller's "folded: none" — only an absent precomputation re-folds.
|
|
160
|
+
const singleCriterion = precomputedCriterion === undefined ? singleCriterionOnlyOf(options) : (precomputedCriterion ?? undefined);
|
|
159
161
|
let currentToken = token;
|
|
160
162
|
let visitedAliasTokens;
|
|
161
|
-
let found = this.#findBinding(currentToken, options, resolutionStack);
|
|
163
|
+
let found = this.#findBinding(currentToken, options, resolutionStack, singleCriterion);
|
|
162
164
|
while (found !== undefined && found.binding.kind === "alias") {
|
|
163
165
|
const target = found.binding.target;
|
|
164
166
|
visitedAliasTokens ??= new Set([currentToken]);
|
|
@@ -167,7 +169,7 @@ export class DependencyResolver {
|
|
|
167
169
|
}
|
|
168
170
|
visitedAliasTokens.add(target);
|
|
169
171
|
currentToken = target;
|
|
170
|
-
found = this.#findBinding(currentToken, options, resolutionStack);
|
|
172
|
+
found = this.#findBinding(currentToken, options, resolutionStack, singleCriterion);
|
|
171
173
|
}
|
|
172
174
|
if (found === undefined) {
|
|
173
175
|
// Thrown here rather than from a helper: the error captures this stack, and an error path is
|
|
@@ -183,7 +185,7 @@ export class DependencyResolver {
|
|
|
183
185
|
* Binding lookup aligned with `resolve` — used by `Container.validate` without instantiating.
|
|
184
186
|
*/
|
|
185
187
|
peekBindingForValidate(token, options) {
|
|
186
|
-
return this.#findBinding(token, options, []);
|
|
188
|
+
return this.#findBinding(token, options, [], singleCriterionOnlyOf(options));
|
|
187
189
|
}
|
|
188
190
|
/**
|
|
189
191
|
* Mirrors {@link DependencyResolver.resolveAll} candidate selection only (no instantiation).
|
|
@@ -327,22 +329,14 @@ export class DependencyResolver {
|
|
|
327
329
|
const entry = this.#lookup.defaultEntry(token);
|
|
328
330
|
return entry === null ? null : { binding: entry.binding };
|
|
329
331
|
},
|
|
330
|
-
// Exactly what #findBinding's
|
|
331
|
-
// is the compiler's cue to leave the selection to the runtime.
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
if (
|
|
332
|
+
// Exactly what #findBinding's single-criterion lane accepts, minus the half that reads a path:
|
|
333
|
+
// a predicate is the compiler's cue to leave the selection to the runtime.
|
|
334
|
+
lookupPathIndependentEntry: (token, options) => {
|
|
335
|
+
const singleCriterion = singleCriterionOnlyOf(options);
|
|
336
|
+
if (singleCriterion === undefined) {
|
|
335
337
|
return null;
|
|
336
338
|
}
|
|
337
|
-
|
|
338
|
-
},
|
|
339
|
-
// The named rule verbatim, on the single-tag lane's memo.
|
|
340
|
-
lookupPathIndependentTaggedEntry: (token, options) => {
|
|
341
|
-
const singleTag = singleTagOnlyOf(options);
|
|
342
|
-
if (singleTag === undefined) {
|
|
343
|
-
return null;
|
|
344
|
-
}
|
|
345
|
-
const entry = this.#lookup.taggedEntry(token, singleTag);
|
|
339
|
+
const entry = this.#lookup.taggedEntry(token, singleCriterion);
|
|
346
340
|
if (entry === null || entry.binding.predicate !== undefined || !matchesSlot(entry.binding.slot, options)) {
|
|
347
341
|
return null;
|
|
348
342
|
}
|
|
@@ -398,40 +392,28 @@ export class DependencyResolver {
|
|
|
398
392
|
this.#asyncPlanByBindingId.set(binding.id, compiled);
|
|
399
393
|
return compiled;
|
|
400
394
|
}
|
|
401
|
-
resolve(token, options, resolutionStack) {
|
|
402
|
-
//
|
|
403
|
-
// semantics involve no resolution context (constants, cached singletons).
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
// Everything else keeps the full path (context, activation, guards).
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
else if (options !== undefined) {
|
|
418
|
-
// Single-tag fast lane: the named lane's tagged twin, memoizing the chain walk.
|
|
419
|
-
const singleTag = singleTagOnlyOf(options);
|
|
420
|
-
if (singleTag !== undefined) {
|
|
421
|
-
const taggedEntry = this.#lookup.taggedEntry(token, singleTag);
|
|
422
|
-
if (taggedEntry !== null) {
|
|
423
|
-
const taggedBinding = taggedEntry.binding;
|
|
424
|
-
if (taggedEntry.owner.#isPlainConstant(taggedBinding)) {
|
|
425
|
-
return taggedBinding.value;
|
|
395
|
+
resolve(token, options, resolutionStack, precomputedCriterion) {
|
|
396
|
+
// Single-criterion fast lane (a lone name folds here too): memoized lookup, dispatching just
|
|
397
|
+
// the shapes whose semantics involve no resolution context (constants, cached singletons).
|
|
398
|
+
let singleCriterion;
|
|
399
|
+
if (options !== undefined) {
|
|
400
|
+
singleCriterion =
|
|
401
|
+
precomputedCriterion === undefined ? singleCriterionOnlyOf(options) : (precomputedCriterion ?? undefined);
|
|
402
|
+
if (singleCriterion !== undefined) {
|
|
403
|
+
const indexedEntry = this.#lookup.taggedEntry(token, singleCriterion);
|
|
404
|
+
if (indexedEntry !== null) {
|
|
405
|
+
const indexedBinding = indexedEntry.binding;
|
|
406
|
+
if (indexedEntry.owner.#isPlainConstant(indexedBinding)) {
|
|
407
|
+
return indexedBinding.value;
|
|
426
408
|
}
|
|
427
|
-
if (
|
|
428
|
-
return
|
|
409
|
+
if (indexedBinding.scope === "singleton" && indexedBinding.instance !== NO_INSTANCE) {
|
|
410
|
+
return indexedBinding.instance;
|
|
429
411
|
}
|
|
430
412
|
// Everything else keeps the full path (context, activation, guards).
|
|
431
413
|
}
|
|
432
414
|
}
|
|
433
415
|
}
|
|
434
|
-
const { binding, owner } = this.#requireBinding(token, options, resolutionStack);
|
|
416
|
+
const { binding, owner } = this.#requireBinding(token, options, resolutionStack, singleCriterion ?? null);
|
|
435
417
|
// A singleton owned by a parent resolver is resolved there, so the parent caches it.
|
|
436
418
|
if (binding.scope === "singleton" && owner !== this) {
|
|
437
419
|
return owner.#resolveBinding(binding, options, resolutionStack, owner);
|
|
@@ -582,15 +564,15 @@ export class DependencyResolver {
|
|
|
582
564
|
return this.resolveAll(dep.token, options, resolutionStack);
|
|
583
565
|
}
|
|
584
566
|
if (dep.optional) {
|
|
585
|
-
return this.resolveOptional(dep.token, options, resolutionStack);
|
|
567
|
+
return this.resolveOptional(dep.token, options, resolutionStack, singleCriterionForSlot(dep));
|
|
586
568
|
}
|
|
587
569
|
if (options === undefined) {
|
|
588
570
|
return this.resolveFromContext(dep.token, resolutionStack);
|
|
589
571
|
}
|
|
590
|
-
return this.resolve(dep.token, options, resolutionStack);
|
|
572
|
+
return this.resolve(dep.token, options, resolutionStack, singleCriterionForSlot(dep));
|
|
591
573
|
}
|
|
592
|
-
resolveOptional(token, options, resolutionStack) {
|
|
593
|
-
const entry = this.#findBinding(token, options, resolutionStack);
|
|
574
|
+
resolveOptional(token, options, resolutionStack, precomputedCriterion) {
|
|
575
|
+
const entry = this.#findBinding(token, options, resolutionStack, precomputedCriterion === undefined ? singleCriterionOnlyOf(options) : (precomputedCriterion ?? undefined));
|
|
594
576
|
if (entry === undefined) {
|
|
595
577
|
return undefined;
|
|
596
578
|
}
|
|
@@ -677,8 +659,17 @@ export class DependencyResolver {
|
|
|
677
659
|
}
|
|
678
660
|
return this.#resolveBindingAsync(binding, undefined, resolutionStack, branchDepth, owner);
|
|
679
661
|
}
|
|
680
|
-
|
|
681
|
-
|
|
662
|
+
/**
|
|
663
|
+
* Instantiates one owned binding directly, bypassing selection.
|
|
664
|
+
*
|
|
665
|
+
* @remarks Warm-up must build the binding it inspected: re-selecting by the slot's own criteria
|
|
666
|
+
* could pick a different candidate whose criteria are a subset of them.
|
|
667
|
+
*/
|
|
668
|
+
warmBindingAsync(binding, options) {
|
|
669
|
+
return this.#resolveBindingAsync(binding, options, [], ROOT_BRANCH, this);
|
|
670
|
+
}
|
|
671
|
+
async resolveAsync(token, options, resolutionStack, branchDepth = UNOWNED_BRANCH, precomputedCriterion) {
|
|
672
|
+
const { binding, owner } = this.#requireBinding(token, options, resolutionStack, precomputedCriterion);
|
|
682
673
|
if (binding.scope === "singleton" && owner !== this) {
|
|
683
674
|
return owner.#resolveBindingAsync(binding, options, resolutionStack, branchDepth, owner);
|
|
684
675
|
}
|
|
@@ -836,15 +827,15 @@ export class DependencyResolver {
|
|
|
836
827
|
return this.resolveAllAsync(dep.token, options, resolutionStack, branchDepth);
|
|
837
828
|
}
|
|
838
829
|
if (dep.optional) {
|
|
839
|
-
return this.resolveOptionalAsync(dep.token, options, resolutionStack, branchDepth);
|
|
830
|
+
return this.resolveOptionalAsync(dep.token, options, resolutionStack, branchDepth, singleCriterionForSlot(dep));
|
|
840
831
|
}
|
|
841
832
|
if (options === undefined) {
|
|
842
833
|
return this.resolveAsyncFromContext(dep.token, resolutionStack, branchDepth);
|
|
843
834
|
}
|
|
844
|
-
return this.resolveAsync(dep.token, options, resolutionStack, branchDepth);
|
|
835
|
+
return this.resolveAsync(dep.token, options, resolutionStack, branchDepth, singleCriterionForSlot(dep));
|
|
845
836
|
}
|
|
846
|
-
async resolveOptionalAsync(token, options, resolutionStack, branchDepth = UNOWNED_BRANCH) {
|
|
847
|
-
const entry = this.#findBinding(token, options, resolutionStack);
|
|
837
|
+
async resolveOptionalAsync(token, options, resolutionStack, branchDepth = UNOWNED_BRANCH, precomputedCriterion) {
|
|
838
|
+
const entry = this.#findBinding(token, options, resolutionStack, precomputedCriterion === undefined ? singleCriterionOnlyOf(options) : (precomputedCriterion ?? undefined));
|
|
848
839
|
if (entry === undefined) {
|
|
849
840
|
return undefined;
|
|
850
841
|
}
|
|
@@ -881,56 +872,34 @@ export class DependencyResolver {
|
|
|
881
872
|
}
|
|
882
873
|
return result;
|
|
883
874
|
}
|
|
884
|
-
/** Every binding the chain's name indexes hold for one name, nearest container first. */
|
|
885
|
-
#namedBindingsFromChain(token, name) {
|
|
886
|
-
// A name resolves to at most one binding per registry, so a root container's answer is built
|
|
887
|
-
// whole rather than grown — the list is sized at its allocation.
|
|
888
|
-
const ownBinding = this.#registry.getSimpleNamed(token, name);
|
|
889
|
-
if (this.#parent === undefined) {
|
|
890
|
-
return ownBinding === undefined ? [] : [ownBinding];
|
|
891
|
-
}
|
|
892
|
-
const result = ownBinding === undefined ? [] : [ownBinding];
|
|
893
|
-
for (let current = this.#parent; current !== undefined; current = current.#parent) {
|
|
894
|
-
const binding = current.#registry.getSimpleNamed(token, name);
|
|
895
|
-
if (binding !== undefined) {
|
|
896
|
-
result.push(binding);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
return result;
|
|
900
|
-
}
|
|
901
875
|
/**
|
|
902
|
-
* The candidates
|
|
876
|
+
* The candidates the index can name outright, or `null` when the request needs full selection.
|
|
903
877
|
*
|
|
904
|
-
* @remarks Kept off `#candidateBindings` so that method stays the size it was: a request
|
|
905
|
-
* index
|
|
878
|
+
* @remarks Kept off `#candidateBindings` so that method stays the size it was: a request the
|
|
879
|
+
* index cannot serve must not pay for the shape it does.
|
|
906
880
|
* An index has matched the slot already, but a hit may still carry a predicate, and evaluating
|
|
907
881
|
* that is the selection path's job.
|
|
908
882
|
*/
|
|
909
883
|
#indexedCandidates(token, options, resolutionStack) {
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
return anyPredicate(named)
|
|
913
|
-
? selectAllBindings(named, options, this.#makeConstraintContext(resolutionStack, options))
|
|
914
|
-
: named;
|
|
915
|
-
}
|
|
916
|
-
const singleTag = singleTagOnlyOf(options);
|
|
917
|
-
if (singleTag === undefined) {
|
|
884
|
+
const singleCriterion = singleCriterionOnlyOf(options);
|
|
885
|
+
if (singleCriterion === undefined) {
|
|
918
886
|
return null;
|
|
919
887
|
}
|
|
920
|
-
const
|
|
921
|
-
return anyPredicate(
|
|
922
|
-
? selectAllBindings(
|
|
923
|
-
:
|
|
888
|
+
const indexed = this.#taggedBindingsFromChain(token, singleCriterion);
|
|
889
|
+
return anyPredicate(indexed)
|
|
890
|
+
? selectAllBindings(indexed, options, this.#makeConstraintContext(resolutionStack, options))
|
|
891
|
+
: indexed;
|
|
924
892
|
}
|
|
925
893
|
/**
|
|
926
|
-
* Every binding the chain's
|
|
894
|
+
* Every binding the chain's criterion indexes hold for one criterion, nearest container first.
|
|
927
895
|
*
|
|
928
|
-
* @remarks A request for one
|
|
929
|
-
* is the whole candidate set rather than a prefilter — a
|
|
896
|
+
* @remarks A request for exactly one criterion matches exactly the bindings the index keys, so
|
|
897
|
+
* this is the whole candidate set rather than a prefilter — a multi-criterion slot cannot satisfy
|
|
898
|
+
* it.
|
|
930
899
|
*/
|
|
931
900
|
#taggedBindingsFromChain(token, tag) {
|
|
932
|
-
// A
|
|
933
|
-
// rather than grown — the
|
|
901
|
+
// A criterion matches at most one binding per registry, so a root container's answer is built
|
|
902
|
+
// whole rather than grown — the list is sized at its allocation.
|
|
934
903
|
const ownBinding = this.#registry.getSimpleTagged(token, tag);
|
|
935
904
|
if (this.#parent === undefined) {
|
|
936
905
|
return ownBinding === undefined ? [] : [ownBinding];
|
|
@@ -977,14 +946,19 @@ export class DependencyResolver {
|
|
|
977
946
|
}
|
|
978
947
|
return buildConstraintContext(resolutionStack, options);
|
|
979
948
|
}
|
|
980
|
-
/** Selection for a
|
|
949
|
+
/** Selection for a multi-criterion request, over the union of the two tag indexes. */
|
|
981
950
|
#selectMultiTagged(token, options, resolutionStack) {
|
|
982
951
|
const candidates = [];
|
|
983
|
-
|
|
952
|
+
const gathered = [];
|
|
953
|
+
if (options.name !== undefined) {
|
|
954
|
+
// Read, not minted: an unminted name has no criterion, so no slot can carry it.
|
|
955
|
+
this.#gatherTagCandidates(token, slotNameCriterionOf(options.name), candidates, gathered);
|
|
956
|
+
}
|
|
957
|
+
this.#gatherTagCandidates(token, options.tag, candidates, gathered);
|
|
984
958
|
const listed = options.tags;
|
|
985
959
|
if (listed !== undefined) {
|
|
986
960
|
for (let index = 0; index < listed.length; index += 1) {
|
|
987
|
-
this.#gatherTagCandidates(token, listed[index], candidates);
|
|
961
|
+
this.#gatherTagCandidates(token, listed[index], candidates, gathered);
|
|
988
962
|
}
|
|
989
963
|
}
|
|
990
964
|
if (candidates.length === 0) {
|
|
@@ -993,31 +967,24 @@ export class DependencyResolver {
|
|
|
993
967
|
return selectBinding(candidates, options, this.#makeConstraintContext(resolutionStack, options), tokenName(token));
|
|
994
968
|
}
|
|
995
969
|
/** One request criterion's candidates: its exact single-tag binding, plus its first-tag bucket. */
|
|
996
|
-
#gatherTagCandidates(token, criterion, out) {
|
|
997
|
-
|
|
970
|
+
#gatherTagCandidates(token, criterion, out, gathered) {
|
|
971
|
+
// Distinct criteria never share a binding — a slot lives in exactly one bucket — so deduping
|
|
972
|
+
// by criterion covers a request repeating one across its spellings, without scanning `out`.
|
|
973
|
+
if (criterion === undefined || gathered.includes(criterion)) {
|
|
998
974
|
return;
|
|
999
975
|
}
|
|
1000
|
-
|
|
976
|
+
gathered.push(criterion);
|
|
1001
977
|
const single = this.#registry.getSimpleTagged(token, criterion);
|
|
1002
|
-
if (single !== undefined
|
|
978
|
+
if (single !== undefined) {
|
|
1003
979
|
out.push(single);
|
|
1004
980
|
}
|
|
1005
981
|
const bucket = this.#registry.getMultiTagged(token, criterion);
|
|
1006
982
|
if (bucket !== undefined) {
|
|
1007
983
|
for (let index = 0; index < bucket.length; index += 1) {
|
|
1008
|
-
|
|
1009
|
-
if (!out.includes(candidate)) {
|
|
1010
|
-
out.push(candidate);
|
|
1011
|
-
}
|
|
984
|
+
out.push(bucket[index]);
|
|
1012
985
|
}
|
|
1013
986
|
}
|
|
1014
987
|
}
|
|
1015
|
-
#matchesBindingFast(binding, options, resolutionStack) {
|
|
1016
|
-
if (!matchesSlot(binding.slot, options)) {
|
|
1017
|
-
return false;
|
|
1018
|
-
}
|
|
1019
|
-
return this.#satisfiesPredicate(binding, options, resolutionStack);
|
|
1020
|
-
}
|
|
1021
988
|
/** The predicate half of a match, for a lane whose index has already settled the slot. */
|
|
1022
989
|
#satisfiesPredicate(binding, options, resolutionStack) {
|
|
1023
990
|
const predicate = binding.predicate;
|