@codefast/di 0.7.1 → 0.8.1
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 +40 -0
- package/README.md +242 -159
- package/dist/ambient/active-container.d.ts +1 -2
- package/dist/ambient/active-container.js +1 -2
- package/dist/container/binding-builders.d.ts +1 -2
- package/dist/container/binding-builders.js +5 -7
- package/dist/container/container.d.ts +1 -2
- package/dist/container/container.js +4 -6
- package/dist/core/binding-scope.d.ts +1 -2
- package/dist/core/binding-scope.js +1 -2
- package/dist/core/binding.d.ts +16 -4
- package/dist/core/binding.js +30 -6
- package/dist/core/constraint-requirement.d.ts +1 -2
- package/dist/core/constraint-requirement.js +1 -2
- package/dist/core/constructor-type.d.ts +1 -2
- package/dist/core/constructor-type.js +1 -2
- package/dist/core/map-upsert.d.ts +1 -2
- package/dist/core/map-upsert.js +1 -2
- package/dist/core/module.d.ts +1 -2
- package/dist/core/module.js +1 -2
- package/dist/core/registry.d.ts +1 -5
- package/dist/core/registry.js +15 -63
- package/dist/core/tag.d.ts +27 -2
- package/dist/core/tag.js +55 -13
- package/dist/core/token.d.ts +1 -2
- package/dist/core/token.js +1 -2
- package/dist/core/types.d.ts +1 -2
- package/dist/core/types.js +1 -2
- package/dist/decorators/inject.d.ts +1 -2
- package/dist/decorators/inject.js +1 -2
- package/dist/decorators/injectable.d.ts +1 -2
- package/dist/decorators/injectable.js +1 -2
- package/dist/decorators/lifecycle-decorators.d.ts +1 -2
- package/dist/decorators/lifecycle-decorators.js +1 -2
- package/dist/errors/diagnostics.d.ts +1 -2
- package/dist/errors/diagnostics.js +1 -2
- package/dist/errors/errors.d.ts +1 -2
- package/dist/errors/errors.js +29 -5
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -3
- package/dist/injection/descriptor.d.ts +1 -2
- package/dist/injection/descriptor.js +1 -2
- package/dist/injection/resolve-options.d.ts +18 -14
- package/dist/injection/resolve-options.js +70 -17
- package/dist/introspection/dependency-graph.d.ts +1 -2
- package/dist/introspection/dependency-graph.js +23 -11
- package/dist/introspection/graph-adapters/cytoscape.d.ts +1 -2
- package/dist/introspection/graph-adapters/cytoscape.js +1 -2
- package/dist/introspection/graph-adapters/dot.d.ts +1 -2
- package/dist/introspection/graph-adapters/dot.js +1 -2
- package/dist/introspection/graph-adapters/mermaid.d.ts +1 -2
- package/dist/introspection/graph-adapters/mermaid.js +1 -2
- package/dist/introspection/graph-adapters/reactflow.d.ts +1 -2
- package/dist/introspection/graph-adapters/reactflow.js +1 -2
- package/dist/introspection/inspector.d.ts +1 -2
- package/dist/introspection/inspector.js +1 -2
- package/dist/lifecycle/lifecycle-manager.d.ts +1 -2
- package/dist/lifecycle/lifecycle-manager.js +1 -2
- package/dist/lifecycle/scope-manager.d.ts +1 -2
- package/dist/lifecycle/scope-manager.js +1 -2
- package/dist/metadata/metadata-keys.d.ts +1 -2
- package/dist/metadata/metadata-keys.js +1 -2
- package/dist/metadata/metadata-reader-token.d.ts +1 -2
- package/dist/metadata/metadata-reader-token.js +1 -2
- package/dist/metadata/metadata-types.d.ts +1 -2
- package/dist/metadata/metadata-types.js +1 -2
- package/dist/metadata/symbol-metadata-reader.d.ts +1 -2
- package/dist/metadata/symbol-metadata-reader.js +1 -2
- package/dist/metadata/verifying-metadata-reader.d.ts +1 -2
- package/dist/metadata/verifying-metadata-reader.js +1 -2
- package/dist/resolution/cache/activation-need.d.ts +1 -2
- package/dist/resolution/cache/activation-need.js +1 -2
- package/dist/resolution/cache/binding-lookup-cache.d.ts +2 -5
- package/dist/resolution/cache/binding-lookup-cache.js +2 -40
- package/dist/resolution/cache/class-introspector.d.ts +1 -2
- package/dist/resolution/cache/class-introspector.js +1 -2
- package/dist/resolution/context.d.ts +1 -2
- package/dist/resolution/context.js +1 -2
- package/dist/resolution/path/resolution-path.d.ts +1 -2
- package/dist/resolution/path/resolution-path.js +1 -2
- package/dist/resolution/plan/instantiation-plan.d.ts +5 -14
- package/dist/resolution/plan/instantiation-plan.js +10 -29
- package/dist/resolution/resolver.d.ts +13 -7
- package/dist/resolution/resolver.js +107 -143
- package/dist/resolution/select/binding-select.d.ts +5 -7
- package/dist/resolution/select/binding-select.js +19 -22
- package/dist/resolution/select/constraints.d.ts +1 -2
- package/dist/resolution/select/constraints.js +1 -2
- package/package.json +6 -58
- package/dist/ambient/active-container.d.ts.map +0 -1
- package/dist/ambient/active-container.js.map +0 -1
- package/dist/container/binding-builders.d.ts.map +0 -1
- package/dist/container/binding-builders.js.map +0 -1
- package/dist/container/container.d.ts.map +0 -1
- package/dist/container/container.js.map +0 -1
- package/dist/core/binding-scope.d.ts.map +0 -1
- package/dist/core/binding-scope.js.map +0 -1
- package/dist/core/binding.d.ts.map +0 -1
- package/dist/core/binding.js.map +0 -1
- package/dist/core/constraint-requirement.d.ts.map +0 -1
- package/dist/core/constraint-requirement.js.map +0 -1
- package/dist/core/constructor-type.d.ts.map +0 -1
- package/dist/core/constructor-type.js.map +0 -1
- package/dist/core/map-upsert.d.ts.map +0 -1
- package/dist/core/map-upsert.js.map +0 -1
- package/dist/core/module.d.ts.map +0 -1
- package/dist/core/module.js.map +0 -1
- package/dist/core/registry.d.ts.map +0 -1
- package/dist/core/registry.js.map +0 -1
- package/dist/core/tag.d.ts.map +0 -1
- package/dist/core/tag.js.map +0 -1
- package/dist/core/token.d.ts.map +0 -1
- package/dist/core/token.js.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/decorators/inject.d.ts.map +0 -1
- package/dist/decorators/inject.js.map +0 -1
- package/dist/decorators/injectable.d.ts.map +0 -1
- package/dist/decorators/injectable.js.map +0 -1
- package/dist/decorators/lifecycle-decorators.d.ts.map +0 -1
- package/dist/decorators/lifecycle-decorators.js.map +0 -1
- package/dist/errors/diagnostics.d.ts.map +0 -1
- package/dist/errors/diagnostics.js.map +0 -1
- package/dist/errors/errors.d.ts.map +0 -1
- package/dist/errors/errors.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/injection/descriptor.d.ts.map +0 -1
- package/dist/injection/descriptor.js.map +0 -1
- package/dist/injection/resolve-options.d.ts.map +0 -1
- package/dist/injection/resolve-options.js.map +0 -1
- package/dist/introspection/dependency-graph.d.ts.map +0 -1
- package/dist/introspection/dependency-graph.js.map +0 -1
- package/dist/introspection/graph-adapters/cytoscape.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/cytoscape.js.map +0 -1
- package/dist/introspection/graph-adapters/dot.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/dot.js.map +0 -1
- package/dist/introspection/graph-adapters/mermaid.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/mermaid.js.map +0 -1
- package/dist/introspection/graph-adapters/reactflow.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/reactflow.js.map +0 -1
- package/dist/introspection/inspector.d.ts.map +0 -1
- package/dist/introspection/inspector.js.map +0 -1
- package/dist/lifecycle/lifecycle-manager.d.ts.map +0 -1
- package/dist/lifecycle/lifecycle-manager.js.map +0 -1
- package/dist/lifecycle/scope-manager.d.ts.map +0 -1
- package/dist/lifecycle/scope-manager.js.map +0 -1
- package/dist/metadata/metadata-keys.d.ts.map +0 -1
- package/dist/metadata/metadata-keys.js.map +0 -1
- package/dist/metadata/metadata-reader-token.d.ts.map +0 -1
- package/dist/metadata/metadata-reader-token.js.map +0 -1
- package/dist/metadata/metadata-types.d.ts.map +0 -1
- package/dist/metadata/metadata-types.js.map +0 -1
- package/dist/metadata/symbol-metadata-reader.d.ts.map +0 -1
- package/dist/metadata/symbol-metadata-reader.js.map +0 -1
- package/dist/metadata/verifying-metadata-reader.d.ts.map +0 -1
- package/dist/metadata/verifying-metadata-reader.js.map +0 -1
- package/dist/resolution/cache/activation-need.d.ts.map +0 -1
- package/dist/resolution/cache/activation-need.js.map +0 -1
- package/dist/resolution/cache/binding-lookup-cache.d.ts.map +0 -1
- package/dist/resolution/cache/binding-lookup-cache.js.map +0 -1
- package/dist/resolution/cache/class-introspector.d.ts.map +0 -1
- package/dist/resolution/cache/class-introspector.js.map +0 -1
- package/dist/resolution/context.d.ts.map +0 -1
- package/dist/resolution/context.js.map +0 -1
- package/dist/resolution/path/resolution-path.d.ts.map +0 -1
- package/dist/resolution/path/resolution-path.js.map +0 -1
- package/dist/resolution/plan/instantiation-plan.d.ts.map +0 -1
- package/dist/resolution/plan/instantiation-plan.js.map +0 -1
- package/dist/resolution/resolver.d.ts.map +0 -1
- package/dist/resolution/resolver.js.map +0 -1
- package/dist/resolution/select/binding-select.d.ts.map +0 -1
- package/dist/resolution/select/binding-select.js.map +0 -1
- package/dist/resolution/select/constraints.d.ts.map +0 -1
- package/dist/resolution/select/constraints.js.map +0 -1
- package/src/ambient/active-container.ts +0 -69
- package/src/container/binding-builders.ts +0 -332
- package/src/container/container.ts +0 -973
- package/src/core/binding-scope.ts +0 -15
- package/src/core/binding.ts +0 -477
- package/src/core/constraint-requirement.ts +0 -94
- package/src/core/constructor-type.ts +0 -18
- package/src/core/map-upsert.ts +0 -53
- package/src/core/module.ts +0 -117
- package/src/core/registry.ts +0 -371
- package/src/core/tag.ts +0 -158
- package/src/core/token.ts +0 -31
- package/src/core/types.ts +0 -149
- package/src/decorators/inject.ts +0 -101
- package/src/decorators/injectable.ts +0 -134
- package/src/decorators/lifecycle-decorators.ts +0 -43
- package/src/errors/diagnostics.ts +0 -45
- package/src/errors/errors.ts +0 -476
- package/src/index.ts +0 -139
- package/src/injection/descriptor.ts +0 -218
- package/src/injection/resolve-options.ts +0 -144
- package/src/introspection/dependency-graph.ts +0 -282
- package/src/introspection/graph-adapters/cytoscape.ts +0 -78
- package/src/introspection/graph-adapters/dot.ts +0 -32
- package/src/introspection/graph-adapters/mermaid.ts +0 -61
- package/src/introspection/graph-adapters/reactflow.ts +0 -84
- package/src/introspection/inspector.ts +0 -134
- package/src/lifecycle/lifecycle-manager.ts +0 -272
- package/src/lifecycle/scope-manager.ts +0 -158
- package/src/metadata/metadata-keys.ts +0 -28
- package/src/metadata/metadata-reader-token.ts +0 -10
- package/src/metadata/metadata-types.ts +0 -57
- package/src/metadata/symbol-metadata-reader.ts +0 -126
- package/src/metadata/verifying-metadata-reader.ts +0 -41
- package/src/resolution/cache/activation-need.ts +0 -87
- package/src/resolution/cache/binding-lookup-cache.ts +0 -209
- package/src/resolution/cache/class-introspector.ts +0 -256
- package/src/resolution/context.ts +0 -331
- package/src/resolution/path/resolution-path.ts +0 -167
- package/src/resolution/plan/instantiation-plan.ts +0 -617
- package/src/resolution/resolver.ts +0 -1675
- package/src/resolution/select/binding-select.ts +0 -210
- package/src/resolution/select/constraints.ts +0 -146
|
@@ -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";
|
|
@@ -96,49 +96,47 @@ export class DependencyResolver {
|
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
// ── Binding lookup ─────────────────────────────────────────────────────────────────────────────────────────────────
|
|
99
|
-
|
|
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) {
|
|
100
106
|
if (options === undefined) {
|
|
101
107
|
const fastDefaultBinding = this.#registry.getFastDefault(token);
|
|
102
108
|
if (fastDefaultBinding !== undefined) {
|
|
103
109
|
return { binding: fastDefaultBinding, owner: this };
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
|
-
else if (
|
|
107
|
-
const
|
|
108
|
-
if (
|
|
109
|
-
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 };
|
|
110
116
|
}
|
|
111
117
|
}
|
|
112
|
-
else
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// small list pays one length read and nothing else.
|
|
124
|
-
this.#registry.getAll(token).length > MULTI_TAG_INDEX_THRESHOLD &&
|
|
125
|
-
requestedTagKeyMask(options) !== NO_TAG_KEYS) {
|
|
126
|
-
// A name-less multi-tag request matches only name-less tagged slots, and every such slot
|
|
127
|
-
// lives in one of the two tag indexes — so their union is the whole candidate set and the
|
|
128
|
-
// token's full list never needs scanning. Selection still owns predicates and specificity.
|
|
129
|
-
const selected = this.#selectMultiTagged(token, options, resolutionStack);
|
|
130
|
-
if (selected !== undefined) {
|
|
131
|
-
return { binding: selected, owner: this };
|
|
132
|
-
}
|
|
133
|
-
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 };
|
|
134
129
|
}
|
|
130
|
+
return this.#parent === undefined
|
|
131
|
+
? undefined
|
|
132
|
+
: this.#parent.#findBinding(token, options, resolutionStack, singleCriterion);
|
|
135
133
|
}
|
|
136
134
|
const bindings = this.#registry.getAll(token);
|
|
137
135
|
if (bindings.length > 0) {
|
|
138
136
|
// A lone candidate is its own selection: matching it is the whole decision, with no
|
|
139
137
|
// specificity to weigh and no ambiguity to report.
|
|
140
138
|
const selected = bindings.length === 1
|
|
141
|
-
? this.#
|
|
139
|
+
? matchesSlot(bindings[0].slot, options) && this.#satisfiesPredicate(bindings[0], options, resolutionStack)
|
|
142
140
|
? bindings[0]
|
|
143
141
|
: undefined
|
|
144
142
|
: selectBinding(bindings, options, this.#makeConstraintContext(resolutionStack, options), tokenName(token));
|
|
@@ -147,7 +145,7 @@ export class DependencyResolver {
|
|
|
147
145
|
}
|
|
148
146
|
}
|
|
149
147
|
if (this.#parent !== undefined) {
|
|
150
|
-
return this.#parent.#findBinding(token, options, resolutionStack);
|
|
148
|
+
return this.#parent.#findBinding(token, options, resolutionStack, singleCriterion);
|
|
151
149
|
}
|
|
152
150
|
return undefined;
|
|
153
151
|
}
|
|
@@ -157,10 +155,12 @@ export class DependencyResolver {
|
|
|
157
155
|
* @remarks Alias hops are followed iteratively with exact cycle detection — a revisited alias
|
|
158
156
|
* token raises {@link CircularDependencyError} instead of overflowing the call stack.
|
|
159
157
|
*/
|
|
160
|
-
#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);
|
|
161
161
|
let currentToken = token;
|
|
162
162
|
let visitedAliasTokens;
|
|
163
|
-
let found = this.#findBinding(currentToken, options, resolutionStack);
|
|
163
|
+
let found = this.#findBinding(currentToken, options, resolutionStack, singleCriterion);
|
|
164
164
|
while (found !== undefined && found.binding.kind === "alias") {
|
|
165
165
|
const target = found.binding.target;
|
|
166
166
|
visitedAliasTokens ??= new Set([currentToken]);
|
|
@@ -169,7 +169,7 @@ export class DependencyResolver {
|
|
|
169
169
|
}
|
|
170
170
|
visitedAliasTokens.add(target);
|
|
171
171
|
currentToken = target;
|
|
172
|
-
found = this.#findBinding(currentToken, options, resolutionStack);
|
|
172
|
+
found = this.#findBinding(currentToken, options, resolutionStack, singleCriterion);
|
|
173
173
|
}
|
|
174
174
|
if (found === undefined) {
|
|
175
175
|
// Thrown here rather than from a helper: the error captures this stack, and an error path is
|
|
@@ -185,7 +185,7 @@ export class DependencyResolver {
|
|
|
185
185
|
* Binding lookup aligned with `resolve` — used by `Container.validate` without instantiating.
|
|
186
186
|
*/
|
|
187
187
|
peekBindingForValidate(token, options) {
|
|
188
|
-
return this.#findBinding(token, options, []);
|
|
188
|
+
return this.#findBinding(token, options, [], singleCriterionOnlyOf(options));
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
191
191
|
* Mirrors {@link DependencyResolver.resolveAll} candidate selection only (no instantiation).
|
|
@@ -329,22 +329,14 @@ export class DependencyResolver {
|
|
|
329
329
|
const entry = this.#lookup.defaultEntry(token);
|
|
330
330
|
return entry === null ? null : { binding: entry.binding };
|
|
331
331
|
},
|
|
332
|
-
// Exactly what #findBinding's
|
|
333
|
-
// is the compiler's cue to leave the selection to the runtime.
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
if (
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
return { binding: entry.binding };
|
|
340
|
-
},
|
|
341
|
-
// The named rule verbatim, on the single-tag lane's memo.
|
|
342
|
-
lookupPathIndependentTaggedEntry: (token, options) => {
|
|
343
|
-
const singleTag = singleTagOnlyOf(options);
|
|
344
|
-
if (singleTag === undefined) {
|
|
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) {
|
|
345
337
|
return null;
|
|
346
338
|
}
|
|
347
|
-
const entry = this.#lookup.taggedEntry(token,
|
|
339
|
+
const entry = this.#lookup.taggedEntry(token, singleCriterion);
|
|
348
340
|
if (entry === null || entry.binding.predicate !== undefined || !matchesSlot(entry.binding.slot, options)) {
|
|
349
341
|
return null;
|
|
350
342
|
}
|
|
@@ -400,40 +392,28 @@ export class DependencyResolver {
|
|
|
400
392
|
this.#asyncPlanByBindingId.set(binding.id, compiled);
|
|
401
393
|
return compiled;
|
|
402
394
|
}
|
|
403
|
-
resolve(token, options, resolutionStack) {
|
|
404
|
-
//
|
|
405
|
-
// semantics involve no resolution context (constants, cached singletons).
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
// Everything else keeps the full path (context, activation, guards).
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
else if (options !== undefined) {
|
|
420
|
-
// Single-tag fast lane: the named lane's tagged twin, memoizing the chain walk.
|
|
421
|
-
const singleTag = singleTagOnlyOf(options);
|
|
422
|
-
if (singleTag !== undefined) {
|
|
423
|
-
const taggedEntry = this.#lookup.taggedEntry(token, singleTag);
|
|
424
|
-
if (taggedEntry !== null) {
|
|
425
|
-
const taggedBinding = taggedEntry.binding;
|
|
426
|
-
if (taggedEntry.owner.#isPlainConstant(taggedBinding)) {
|
|
427
|
-
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;
|
|
428
408
|
}
|
|
429
|
-
if (
|
|
430
|
-
return
|
|
409
|
+
if (indexedBinding.scope === "singleton" && indexedBinding.instance !== NO_INSTANCE) {
|
|
410
|
+
return indexedBinding.instance;
|
|
431
411
|
}
|
|
432
412
|
// Everything else keeps the full path (context, activation, guards).
|
|
433
413
|
}
|
|
434
414
|
}
|
|
435
415
|
}
|
|
436
|
-
const { binding, owner } = this.#requireBinding(token, options, resolutionStack);
|
|
416
|
+
const { binding, owner } = this.#requireBinding(token, options, resolutionStack, singleCriterion ?? null);
|
|
437
417
|
// A singleton owned by a parent resolver is resolved there, so the parent caches it.
|
|
438
418
|
if (binding.scope === "singleton" && owner !== this) {
|
|
439
419
|
return owner.#resolveBinding(binding, options, resolutionStack, owner);
|
|
@@ -584,15 +564,15 @@ export class DependencyResolver {
|
|
|
584
564
|
return this.resolveAll(dep.token, options, resolutionStack);
|
|
585
565
|
}
|
|
586
566
|
if (dep.optional) {
|
|
587
|
-
return this.resolveOptional(dep.token, options, resolutionStack);
|
|
567
|
+
return this.resolveOptional(dep.token, options, resolutionStack, singleCriterionForSlot(dep));
|
|
588
568
|
}
|
|
589
569
|
if (options === undefined) {
|
|
590
570
|
return this.resolveFromContext(dep.token, resolutionStack);
|
|
591
571
|
}
|
|
592
|
-
return this.resolve(dep.token, options, resolutionStack);
|
|
572
|
+
return this.resolve(dep.token, options, resolutionStack, singleCriterionForSlot(dep));
|
|
593
573
|
}
|
|
594
|
-
resolveOptional(token, options, resolutionStack) {
|
|
595
|
-
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));
|
|
596
576
|
if (entry === undefined) {
|
|
597
577
|
return undefined;
|
|
598
578
|
}
|
|
@@ -679,8 +659,17 @@ export class DependencyResolver {
|
|
|
679
659
|
}
|
|
680
660
|
return this.#resolveBindingAsync(binding, undefined, resolutionStack, branchDepth, owner);
|
|
681
661
|
}
|
|
682
|
-
|
|
683
|
-
|
|
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);
|
|
684
673
|
if (binding.scope === "singleton" && owner !== this) {
|
|
685
674
|
return owner.#resolveBindingAsync(binding, options, resolutionStack, branchDepth, owner);
|
|
686
675
|
}
|
|
@@ -838,15 +827,15 @@ export class DependencyResolver {
|
|
|
838
827
|
return this.resolveAllAsync(dep.token, options, resolutionStack, branchDepth);
|
|
839
828
|
}
|
|
840
829
|
if (dep.optional) {
|
|
841
|
-
return this.resolveOptionalAsync(dep.token, options, resolutionStack, branchDepth);
|
|
830
|
+
return this.resolveOptionalAsync(dep.token, options, resolutionStack, branchDepth, singleCriterionForSlot(dep));
|
|
842
831
|
}
|
|
843
832
|
if (options === undefined) {
|
|
844
833
|
return this.resolveAsyncFromContext(dep.token, resolutionStack, branchDepth);
|
|
845
834
|
}
|
|
846
|
-
return this.resolveAsync(dep.token, options, resolutionStack, branchDepth);
|
|
835
|
+
return this.resolveAsync(dep.token, options, resolutionStack, branchDepth, singleCriterionForSlot(dep));
|
|
847
836
|
}
|
|
848
|
-
async resolveOptionalAsync(token, options, resolutionStack, branchDepth = UNOWNED_BRANCH) {
|
|
849
|
-
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));
|
|
850
839
|
if (entry === undefined) {
|
|
851
840
|
return undefined;
|
|
852
841
|
}
|
|
@@ -883,56 +872,34 @@ export class DependencyResolver {
|
|
|
883
872
|
}
|
|
884
873
|
return result;
|
|
885
874
|
}
|
|
886
|
-
/** Every binding the chain's name indexes hold for one name, nearest container first. */
|
|
887
|
-
#namedBindingsFromChain(token, name) {
|
|
888
|
-
// A name resolves to at most one binding per registry, so a root container's answer is built
|
|
889
|
-
// whole rather than grown — the list is sized at its allocation.
|
|
890
|
-
const ownBinding = this.#registry.getSimpleNamed(token, name);
|
|
891
|
-
if (this.#parent === undefined) {
|
|
892
|
-
return ownBinding === undefined ? [] : [ownBinding];
|
|
893
|
-
}
|
|
894
|
-
const result = ownBinding === undefined ? [] : [ownBinding];
|
|
895
|
-
for (let current = this.#parent; current !== undefined; current = current.#parent) {
|
|
896
|
-
const binding = current.#registry.getSimpleNamed(token, name);
|
|
897
|
-
if (binding !== undefined) {
|
|
898
|
-
result.push(binding);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
return result;
|
|
902
|
-
}
|
|
903
875
|
/**
|
|
904
|
-
* The candidates
|
|
876
|
+
* The candidates the index can name outright, or `null` when the request needs full selection.
|
|
905
877
|
*
|
|
906
|
-
* @remarks Kept off `#candidateBindings` so that method stays the size it was: a request
|
|
907
|
-
* 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.
|
|
908
880
|
* An index has matched the slot already, but a hit may still carry a predicate, and evaluating
|
|
909
881
|
* that is the selection path's job.
|
|
910
882
|
*/
|
|
911
883
|
#indexedCandidates(token, options, resolutionStack) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
return anyPredicate(named)
|
|
915
|
-
? selectAllBindings(named, options, this.#makeConstraintContext(resolutionStack, options))
|
|
916
|
-
: named;
|
|
917
|
-
}
|
|
918
|
-
const singleTag = singleTagOnlyOf(options);
|
|
919
|
-
if (singleTag === undefined) {
|
|
884
|
+
const singleCriterion = singleCriterionOnlyOf(options);
|
|
885
|
+
if (singleCriterion === undefined) {
|
|
920
886
|
return null;
|
|
921
887
|
}
|
|
922
|
-
const
|
|
923
|
-
return anyPredicate(
|
|
924
|
-
? selectAllBindings(
|
|
925
|
-
:
|
|
888
|
+
const indexed = this.#taggedBindingsFromChain(token, singleCriterion);
|
|
889
|
+
return anyPredicate(indexed)
|
|
890
|
+
? selectAllBindings(indexed, options, this.#makeConstraintContext(resolutionStack, options))
|
|
891
|
+
: indexed;
|
|
926
892
|
}
|
|
927
893
|
/**
|
|
928
|
-
* Every binding the chain's
|
|
894
|
+
* Every binding the chain's criterion indexes hold for one criterion, nearest container first.
|
|
929
895
|
*
|
|
930
|
-
* @remarks A request for one
|
|
931
|
-
* 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.
|
|
932
899
|
*/
|
|
933
900
|
#taggedBindingsFromChain(token, tag) {
|
|
934
|
-
// A
|
|
935
|
-
// 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.
|
|
936
903
|
const ownBinding = this.#registry.getSimpleTagged(token, tag);
|
|
937
904
|
if (this.#parent === undefined) {
|
|
938
905
|
return ownBinding === undefined ? [] : [ownBinding];
|
|
@@ -979,14 +946,19 @@ export class DependencyResolver {
|
|
|
979
946
|
}
|
|
980
947
|
return buildConstraintContext(resolutionStack, options);
|
|
981
948
|
}
|
|
982
|
-
/** Selection for a
|
|
949
|
+
/** Selection for a multi-criterion request, over the union of the two tag indexes. */
|
|
983
950
|
#selectMultiTagged(token, options, resolutionStack) {
|
|
984
951
|
const candidates = [];
|
|
985
|
-
|
|
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);
|
|
986
958
|
const listed = options.tags;
|
|
987
959
|
if (listed !== undefined) {
|
|
988
960
|
for (let index = 0; index < listed.length; index += 1) {
|
|
989
|
-
this.#gatherTagCandidates(token, listed[index], candidates);
|
|
961
|
+
this.#gatherTagCandidates(token, listed[index], candidates, gathered);
|
|
990
962
|
}
|
|
991
963
|
}
|
|
992
964
|
if (candidates.length === 0) {
|
|
@@ -995,31 +967,24 @@ export class DependencyResolver {
|
|
|
995
967
|
return selectBinding(candidates, options, this.#makeConstraintContext(resolutionStack, options), tokenName(token));
|
|
996
968
|
}
|
|
997
969
|
/** One request criterion's candidates: its exact single-tag binding, plus its first-tag bucket. */
|
|
998
|
-
#gatherTagCandidates(token, criterion, out) {
|
|
999
|
-
|
|
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)) {
|
|
1000
974
|
return;
|
|
1001
975
|
}
|
|
1002
|
-
|
|
976
|
+
gathered.push(criterion);
|
|
1003
977
|
const single = this.#registry.getSimpleTagged(token, criterion);
|
|
1004
|
-
if (single !== undefined
|
|
978
|
+
if (single !== undefined) {
|
|
1005
979
|
out.push(single);
|
|
1006
980
|
}
|
|
1007
981
|
const bucket = this.#registry.getMultiTagged(token, criterion);
|
|
1008
982
|
if (bucket !== undefined) {
|
|
1009
983
|
for (let index = 0; index < bucket.length; index += 1) {
|
|
1010
|
-
|
|
1011
|
-
if (!out.includes(candidate)) {
|
|
1012
|
-
out.push(candidate);
|
|
1013
|
-
}
|
|
984
|
+
out.push(bucket[index]);
|
|
1014
985
|
}
|
|
1015
986
|
}
|
|
1016
987
|
}
|
|
1017
|
-
#matchesBindingFast(binding, options, resolutionStack) {
|
|
1018
|
-
if (!matchesSlot(binding.slot, options)) {
|
|
1019
|
-
return false;
|
|
1020
|
-
}
|
|
1021
|
-
return this.#satisfiesPredicate(binding, options, resolutionStack);
|
|
1022
|
-
}
|
|
1023
988
|
/** The predicate half of a match, for a lane whose index has already settled the slot. */
|
|
1024
989
|
#satisfiesPredicate(binding, options, resolutionStack) {
|
|
1025
990
|
const predicate = binding.predicate;
|
|
@@ -1301,5 +1266,4 @@ function asyncResolutionErrorFor(binding, resolutionStack) {
|
|
|
1301
1266
|
/** Only a factory is handed the resolution context; everything else gets its deps directly. */
|
|
1302
1267
|
function requiresResolutionContext(binding) {
|
|
1303
1268
|
return binding.kind === "dynamic" || binding.kind === "dynamic-async";
|
|
1304
|
-
}
|
|
1305
|
-
//# sourceMappingURL=resolver.js.map
|
|
1269
|
+
}
|
|
@@ -15,12 +15,11 @@ export declare function selectBinding(bindings: ReadonlyArray<Binding>, options:
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function selectAllBindings(bindings: ReadonlyArray<Binding>, options: ResolveOptions | undefined, ctx: ConstraintContext): Array<Binding>;
|
|
17
17
|
/**
|
|
18
|
-
* Whether a binding's slot satisfies a request:
|
|
19
|
-
*
|
|
18
|
+
* Whether a binding's slot satisfies a request: every criterion the slot declares must be among
|
|
19
|
+
* the request's criteria, a name spelling either side folding to the reserved criterion.
|
|
20
20
|
*
|
|
21
|
-
* @remarks
|
|
22
|
-
*
|
|
23
|
-
* identity.
|
|
21
|
+
* @remarks A key-mask subset test runs before any criterion is read, so a slot the request cannot
|
|
22
|
+
* satisfy is rejected in one word compare. Criteria are interned, so what follows is identity.
|
|
24
23
|
*
|
|
25
24
|
* @since 0.5.0-canary.9
|
|
26
25
|
*/
|
|
@@ -30,5 +29,4 @@ export declare function matchesSlot(slot: BindingSlot, options: ResolveOptions |
|
|
|
30
29
|
*
|
|
31
30
|
* @since 0.6.0
|
|
32
31
|
*/
|
|
33
|
-
export declare function requestedTagKeyMask(options: ResolveOptions | undefined): TagKeyMask;
|
|
34
|
-
//# sourceMappingURL=binding-select.d.ts.map
|
|
32
|
+
export declare function requestedTagKeyMask(options: ResolveOptions | undefined): TagKeyMask;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { coversTagKeys, NO_TAG_KEYS } from "#/core/tag";
|
|
1
|
+
import { coversTagKeys, NO_TAG_KEYS, slotName, slotNameCriterionOf } from "#/core/tag";
|
|
2
2
|
import { AmbiguousBindingError } from "#/errors/errors";
|
|
3
3
|
/**
|
|
4
4
|
* Selects a single candidate from a list of bindings using slot matching + predicates.
|
|
@@ -32,14 +32,14 @@ export function selectBinding(bindings, options, ctx, tokenDisplayName) {
|
|
|
32
32
|
}
|
|
33
33
|
// Reached only where the throw was: a slot declaring more of what the request carries is the more
|
|
34
34
|
// specific match, so an over-specified request resolves instead of being ambiguous.
|
|
35
|
-
const mostSpecific =
|
|
35
|
+
const mostSpecific = mostSpecificByCriterionCount(candidates);
|
|
36
36
|
if (mostSpecific !== undefined) {
|
|
37
37
|
return mostSpecific;
|
|
38
38
|
}
|
|
39
39
|
throw new AmbiguousBindingError(tokenDisplayName, candidates.map((c) => c.id));
|
|
40
40
|
}
|
|
41
|
-
/** The lone candidate declaring more
|
|
42
|
-
function
|
|
41
|
+
/** The lone candidate declaring more criteria than every other, or `undefined` when that is a tie. */
|
|
42
|
+
function mostSpecificByCriterionCount(candidates) {
|
|
43
43
|
let best;
|
|
44
44
|
let bestCount = -1;
|
|
45
45
|
let tied = false;
|
|
@@ -94,32 +94,22 @@ function filterBindings(bindings, options, ctx, requiresSlotMatch) {
|
|
|
94
94
|
return result;
|
|
95
95
|
}
|
|
96
96
|
function hasSlotCriterion(options) {
|
|
97
|
-
return
|
|
97
|
+
return requestedTagKeyMask(options) !== NO_TAG_KEYS;
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
|
-
* Whether a binding's slot satisfies a request:
|
|
101
|
-
*
|
|
100
|
+
* Whether a binding's slot satisfies a request: every criterion the slot declares must be among
|
|
101
|
+
* the request's criteria, a name spelling either side folding to the reserved criterion.
|
|
102
102
|
*
|
|
103
|
-
* @remarks
|
|
104
|
-
*
|
|
105
|
-
* identity.
|
|
103
|
+
* @remarks A key-mask subset test runs before any criterion is read, so a slot the request cannot
|
|
104
|
+
* satisfy is rejected in one word compare. Criteria are interned, so what follows is identity.
|
|
106
105
|
*
|
|
107
106
|
* @since 0.5.0-canary.9
|
|
108
107
|
*/
|
|
109
108
|
export function matchesSlot(slot, options) {
|
|
110
|
-
const requestedName = options?.name;
|
|
111
|
-
if (slot.name !== undefined) {
|
|
112
|
-
if (slot.name !== requestedName) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
else if (requestedName !== undefined) {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
109
|
const slotMask = slot.keyMask;
|
|
120
110
|
const requestMask = requestedTagKeyMask(options);
|
|
121
111
|
if (slotMask === NO_TAG_KEYS) {
|
|
122
|
-
// A request carrying
|
|
112
|
+
// A request carrying any criterion never falls back to the default slot.
|
|
123
113
|
return requestMask === NO_TAG_KEYS;
|
|
124
114
|
}
|
|
125
115
|
if (!coversTagKeys(requestMask, slotMask)) {
|
|
@@ -146,6 +136,9 @@ export function requestedTagKeyMask(options) {
|
|
|
146
136
|
const single = options.tag;
|
|
147
137
|
const listed = options.tags;
|
|
148
138
|
let mask = single === undefined ? NO_TAG_KEYS : single.mask;
|
|
139
|
+
if (options.name !== undefined) {
|
|
140
|
+
mask = (mask | slotName.mask);
|
|
141
|
+
}
|
|
149
142
|
if (listed !== undefined) {
|
|
150
143
|
for (let index = 0; index < listed.length; index += 1) {
|
|
151
144
|
mask = (mask | listed[index].mask);
|
|
@@ -158,6 +151,11 @@ function requestCarries(options, criterion) {
|
|
|
158
151
|
if (options === undefined) {
|
|
159
152
|
return false;
|
|
160
153
|
}
|
|
154
|
+
// The `name` spelling folds through the intern read, so this too is identity — a hand-built
|
|
155
|
+
// criterion matches nothing on any lane, and an unminted name retains nothing.
|
|
156
|
+
if (criterion.key === slotName && options.name !== undefined && criterion === slotNameCriterionOf(options.name)) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
161
159
|
if (options.tag === criterion) {
|
|
162
160
|
return true;
|
|
163
161
|
}
|
|
@@ -171,5 +169,4 @@ function requestCarries(options, criterion) {
|
|
|
171
169
|
}
|
|
172
170
|
}
|
|
173
171
|
return false;
|
|
174
|
-
}
|
|
175
|
-
//# sourceMappingURL=binding-select.js.map
|
|
172
|
+
}
|
|
@@ -64,5 +64,4 @@ export declare function whenParentTaggedAll(tags: ReadonlyArray<BindingTag>): Bi
|
|
|
64
64
|
*
|
|
65
65
|
* @since 0.3.16-canary.1
|
|
66
66
|
*/
|
|
67
|
-
export declare function whenAnyAncestorTaggedAll(tags: ReadonlyArray<BindingTag>): BindingConstraint;
|
|
68
|
-
//# sourceMappingURL=constraints.d.ts.map
|
|
67
|
+
export declare function whenAnyAncestorTaggedAll(tags: ReadonlyArray<BindingTag>): BindingConstraint;
|