@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,210 +0,0 @@
|
|
|
1
|
-
import type { Binding, BindingSlot } from "#/core/binding";
|
|
2
|
-
import type { BindingTag, TagKeyMask } from "#/core/tag";
|
|
3
|
-
import { coversTagKeys, NO_TAG_KEYS } from "#/core/tag";
|
|
4
|
-
import type { ConstraintContext, ResolveOptions } from "#/core/types";
|
|
5
|
-
import { AmbiguousBindingError } from "#/errors/errors";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Selects a single candidate from a list of bindings using slot matching + predicates.
|
|
9
|
-
* Returns undefined if no match, throws AmbiguousBindingError if multiple match.
|
|
10
|
-
*
|
|
11
|
-
* @since 0.3.16-canary.0
|
|
12
|
-
*/
|
|
13
|
-
export function selectBinding(
|
|
14
|
-
bindings: ReadonlyArray<Binding>,
|
|
15
|
-
options: ResolveOptions | undefined,
|
|
16
|
-
ctx: ConstraintContext,
|
|
17
|
-
tokenDisplayName: string,
|
|
18
|
-
): Binding | undefined {
|
|
19
|
-
const candidates = filterBindings(bindings, options, ctx, true);
|
|
20
|
-
if (candidates.length === 0) {
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
if (candidates.length === 1) {
|
|
24
|
-
return candidates[0];
|
|
25
|
-
}
|
|
26
|
-
// Most specific wins, predicate before tag count: a lone predicate-carrying candidate beats
|
|
27
|
-
// predicate-less ones, a predicate being a deliberate specialization of the default. This order is
|
|
28
|
-
// what keeps every resolution that already succeeds deciding the same way.
|
|
29
|
-
let predicatedCandidate: Binding | undefined;
|
|
30
|
-
for (const candidate of candidates) {
|
|
31
|
-
if (candidate.predicate !== undefined) {
|
|
32
|
-
if (predicatedCandidate !== undefined) {
|
|
33
|
-
predicatedCandidate = undefined;
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
predicatedCandidate = candidate;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (predicatedCandidate !== undefined) {
|
|
40
|
-
return predicatedCandidate;
|
|
41
|
-
}
|
|
42
|
-
// Reached only where the throw was: a slot declaring more of what the request carries is the more
|
|
43
|
-
// specific match, so an over-specified request resolves instead of being ambiguous.
|
|
44
|
-
const mostSpecific = mostSpecificByTagCount(candidates);
|
|
45
|
-
if (mostSpecific !== undefined) {
|
|
46
|
-
return mostSpecific;
|
|
47
|
-
}
|
|
48
|
-
throw new AmbiguousBindingError(
|
|
49
|
-
tokenDisplayName,
|
|
50
|
-
candidates.map((c) => c.id),
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** The lone candidate declaring more tags than every other, or `undefined` when that is a tie. */
|
|
55
|
-
function mostSpecificByTagCount(candidates: ReadonlyArray<Binding>): Binding | undefined {
|
|
56
|
-
let best: Binding | undefined;
|
|
57
|
-
let bestCount = -1;
|
|
58
|
-
let tied = false;
|
|
59
|
-
for (const candidate of candidates) {
|
|
60
|
-
const count = candidate.slot.tags.length;
|
|
61
|
-
if (count > bestCount) {
|
|
62
|
-
best = candidate;
|
|
63
|
-
bestCount = count;
|
|
64
|
-
tied = false;
|
|
65
|
-
} else if (count === bestCount) {
|
|
66
|
-
tied = true;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return tied ? undefined : best;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Selects all candidates matching options + predicates.
|
|
74
|
-
*
|
|
75
|
-
* @since 0.3.16-canary.0
|
|
76
|
-
*/
|
|
77
|
-
export function selectAllBindings(
|
|
78
|
-
bindings: ReadonlyArray<Binding>,
|
|
79
|
-
options: ResolveOptions | undefined,
|
|
80
|
-
ctx: ConstraintContext,
|
|
81
|
-
): Array<Binding> {
|
|
82
|
-
// `resolveAll` matches the slot only when the request carries a criterion; with none it takes
|
|
83
|
-
// every binding, where `resolve` would read an absent criterion as "the default slot".
|
|
84
|
-
return filterBindings(bindings, options, ctx, options !== undefined && hasSlotCriterion(options));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @param bindings - the token's candidates, in registration order
|
|
89
|
-
* @param options - the request's selection criteria, if any
|
|
90
|
-
* @param ctx - what the constraint predicates read
|
|
91
|
-
* @param requiresSlotMatch - `resolve` always matches the slot, where an absent criterion means
|
|
92
|
-
* "the default slot"; `resolveAll` matches only when the request carries one
|
|
93
|
-
*/
|
|
94
|
-
function filterBindings(
|
|
95
|
-
bindings: ReadonlyArray<Binding>,
|
|
96
|
-
options: ResolveOptions | undefined,
|
|
97
|
-
ctx: ConstraintContext,
|
|
98
|
-
requiresSlotMatch: boolean,
|
|
99
|
-
): Array<Binding> {
|
|
100
|
-
const result: Array<Binding> = [];
|
|
101
|
-
// A predicate is user code that may rebind the token mid-walk, but the registry replaces a
|
|
102
|
-
// token's list on mutation instead of splicing it, so this walk keeps its own list — no copy.
|
|
103
|
-
for (let index = 0; index < bindings.length; index += 1) {
|
|
104
|
-
const binding = bindings[index]!;
|
|
105
|
-
if (requiresSlotMatch && !matchesSlot(binding.slot, options)) {
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
const predicate = binding.predicate;
|
|
109
|
-
if (predicate === undefined) {
|
|
110
|
-
result.push(binding);
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (predicate(ctx)) {
|
|
114
|
-
result.push(binding);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return result;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function hasSlotCriterion(options: ResolveOptions): boolean {
|
|
121
|
-
return options.name !== undefined || requestedTagKeyMask(options) !== NO_TAG_KEYS;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Whether a binding's slot satisfies a request: names must be equal, and every tag the slot
|
|
126
|
-
* declares must be among the tags requested.
|
|
127
|
-
*
|
|
128
|
-
* @remarks The tag half is a key-mask subset test before any criterion is read, so a slot the
|
|
129
|
-
* request cannot satisfy is rejected in one word compare. Criteria are interned, so what follows is
|
|
130
|
-
* identity.
|
|
131
|
-
*
|
|
132
|
-
* @since 0.5.0-canary.9
|
|
133
|
-
*/
|
|
134
|
-
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
|
-
const slotMask = slot.keyMask;
|
|
146
|
-
const requestMask = requestedTagKeyMask(options);
|
|
147
|
-
|
|
148
|
-
if (slotMask === NO_TAG_KEYS) {
|
|
149
|
-
// A request carrying tags needs a tagged slot: an untagged binding never matches.
|
|
150
|
-
return requestMask === NO_TAG_KEYS;
|
|
151
|
-
}
|
|
152
|
-
if (!coversTagKeys(requestMask, slotMask)) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// The mask can collide two keys onto one bit, so the exact criteria still have to be confirmed.
|
|
157
|
-
const slotTags = slot.tags;
|
|
158
|
-
|
|
159
|
-
for (let index = 0; index < slotTags.length; index += 1) {
|
|
160
|
-
if (!requestCarries(options, slotTags[index]!)) {
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* The key set a request covers, across both spellings the shorthand folds into one.
|
|
170
|
-
*
|
|
171
|
-
* @since 0.6.0
|
|
172
|
-
*/
|
|
173
|
-
export function requestedTagKeyMask(options: ResolveOptions | undefined): TagKeyMask {
|
|
174
|
-
if (options === undefined) {
|
|
175
|
-
return NO_TAG_KEYS;
|
|
176
|
-
}
|
|
177
|
-
const single = options.tag;
|
|
178
|
-
const listed = options.tags;
|
|
179
|
-
let mask = single === undefined ? NO_TAG_KEYS : single.mask;
|
|
180
|
-
|
|
181
|
-
if (listed !== undefined) {
|
|
182
|
-
for (let index = 0; index < listed.length; index += 1) {
|
|
183
|
-
mask = (mask | listed[index]!.mask) as TagKeyMask;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return mask;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/** Whether a request names this exact criterion — identity, because criteria are interned. */
|
|
191
|
-
function requestCarries(options: ResolveOptions | undefined, criterion: BindingTag): boolean {
|
|
192
|
-
if (options === undefined) {
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
if (options.tag === criterion) {
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
const listed = options.tags;
|
|
199
|
-
|
|
200
|
-
if (listed === undefined) {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
for (let index = 0; index < listed.length; index += 1) {
|
|
204
|
-
if (listed[index] === criterion) {
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { requiringAncestorSlotName } from "#/core/constraint-requirement";
|
|
2
|
-
import type { BindingTag } from "#/core/tag";
|
|
3
|
-
import { coversTagKeys, tagKeyMaskOf } from "#/core/tag";
|
|
4
|
-
import type { Token } from "#/core/token";
|
|
5
|
-
import { tokenName } from "#/core/token";
|
|
6
|
-
import type { BindingConstraint, Constructor } from "#/core/types";
|
|
7
|
-
import { EmptyTagCriteriaError } from "#/errors/errors";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Matches when the direct parent frame resolves the given token.
|
|
11
|
-
*
|
|
12
|
-
* @since 0.3.16-canary.0
|
|
13
|
-
*/
|
|
14
|
-
export function whenParentIs(token: Token<unknown> | Constructor): BindingConstraint {
|
|
15
|
-
const tokenDisplayName = tokenName(token);
|
|
16
|
-
return (constraintContext) =>
|
|
17
|
-
constraintContext.parent !== undefined && constraintContext.parent.tokenName === tokenDisplayName;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Matches when there is no direct parent frame, or it resolves a different token.
|
|
22
|
-
*
|
|
23
|
-
* @since 0.3.16-canary.0
|
|
24
|
-
*/
|
|
25
|
-
export function whenNoParentIs(token: Token<unknown> | Constructor): BindingConstraint {
|
|
26
|
-
const tokenDisplayName = tokenName(token);
|
|
27
|
-
return (constraintContext) =>
|
|
28
|
-
constraintContext.parent === undefined || constraintContext.parent.tokenName !== tokenDisplayName;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Matches when at least one ancestor frame resolves the given token.
|
|
33
|
-
*
|
|
34
|
-
* @since 0.3.16-canary.0
|
|
35
|
-
*/
|
|
36
|
-
export function whenAnyAncestorIs(token: Token<unknown> | Constructor): BindingConstraint {
|
|
37
|
-
const tokenDisplayName = tokenName(token);
|
|
38
|
-
return (constraintContext) =>
|
|
39
|
-
constraintContext.ancestors.some((ancestorFrame) => ancestorFrame.tokenName === tokenDisplayName);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Matches when no ancestor frame resolves the given token.
|
|
44
|
-
*
|
|
45
|
-
* @since 0.3.16-canary.0
|
|
46
|
-
*/
|
|
47
|
-
export function whenNoAncestorIs(token: Token<unknown> | Constructor): BindingConstraint {
|
|
48
|
-
const tokenDisplayName = tokenName(token);
|
|
49
|
-
return (constraintContext) =>
|
|
50
|
-
constraintContext.ancestors.every((ancestorFrame) => ancestorFrame.tokenName !== tokenDisplayName);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Matches when the direct parent slot carries the given name.
|
|
55
|
-
*
|
|
56
|
-
* @since 0.3.16-canary.0
|
|
57
|
-
*/
|
|
58
|
-
export function whenParentNamed(name: string): BindingConstraint {
|
|
59
|
-
return requiringAncestorSlotName(
|
|
60
|
-
(constraintContext) => constraintContext.parent !== undefined && constraintContext.parent.slot.name === name,
|
|
61
|
-
name,
|
|
62
|
-
"whenParentNamed",
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Matches when at least one ancestor slot carries the given name.
|
|
68
|
-
*
|
|
69
|
-
* @since 0.3.16-canary.0
|
|
70
|
-
*/
|
|
71
|
-
export function whenAnyAncestorNamed(name: string): BindingConstraint {
|
|
72
|
-
return requiringAncestorSlotName(
|
|
73
|
-
(constraintContext) => constraintContext.ancestors.some((ancestorFrame) => ancestorFrame.slot.name === name),
|
|
74
|
-
name,
|
|
75
|
-
"whenAnyAncestorNamed",
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Matches when the direct parent slot carries the given tag pair.
|
|
81
|
-
*
|
|
82
|
-
* @since 0.3.16-canary.0
|
|
83
|
-
*/
|
|
84
|
-
export function whenParentTagged(criterion: BindingTag): BindingConstraint {
|
|
85
|
-
return (constraintContext) =>
|
|
86
|
-
constraintContext.parent !== undefined && constraintContext.parent.slot.tags.includes(criterion);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Matches when at least one ancestor slot carries the given tag pair.
|
|
91
|
-
*
|
|
92
|
-
* @since 0.3.16-canary.0
|
|
93
|
-
*/
|
|
94
|
-
export function whenAnyAncestorTagged(criterion: BindingTag): BindingConstraint {
|
|
95
|
-
return (constraintContext) =>
|
|
96
|
-
constraintContext.ancestors.some((ancestorFrame) => ancestorFrame.slot.tags.includes(criterion));
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Matches when the direct parent slot carries **all** of the given tag pairs.
|
|
101
|
-
* Equivalent to AND-composing multiple `whenParentTagged` calls but evaluates
|
|
102
|
-
* in a single predicate invocation — no intermediate closure allocations.
|
|
103
|
-
*
|
|
104
|
-
* @since 0.3.16-canary.1
|
|
105
|
-
*/
|
|
106
|
-
export function whenParentTaggedAll(tags: ReadonlyArray<BindingTag>): BindingConstraint {
|
|
107
|
-
assertHasCriteria(tags, "whenParentTaggedAll");
|
|
108
|
-
const wanted = tagKeyMaskOf(tags);
|
|
109
|
-
return (constraintContext) => {
|
|
110
|
-
const { parent } = constraintContext;
|
|
111
|
-
if (parent === undefined || !coversTagKeys(parent.slot.keyMask, wanted)) {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
return tags.every((criterion) => parent.slot.tags.includes(criterion));
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Matches when at least one ancestor slot carries **all** of the given tag pairs.
|
|
120
|
-
* Equivalent to AND-composing multiple `whenAnyAncestorTagged` calls but evaluates
|
|
121
|
-
* in a single predicate invocation — no intermediate closure allocations.
|
|
122
|
-
*
|
|
123
|
-
* @since 0.3.16-canary.1
|
|
124
|
-
*/
|
|
125
|
-
export function whenAnyAncestorTaggedAll(tags: ReadonlyArray<BindingTag>): BindingConstraint {
|
|
126
|
-
assertHasCriteria(tags, "whenAnyAncestorTaggedAll");
|
|
127
|
-
const wanted = tagKeyMaskOf(tags);
|
|
128
|
-
return (constraintContext) =>
|
|
129
|
-
constraintContext.ancestors.some(
|
|
130
|
-
(frame) =>
|
|
131
|
-
coversTagKeys(frame.slot.keyMask, wanted) && tags.every((criterion) => frame.slot.tags.includes(criterion)),
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Refuses a criteria list with nothing in it.
|
|
137
|
-
*
|
|
138
|
-
* @remarks "carries all of no criteria" is vacuously true, so an empty list quietly turns the
|
|
139
|
-
* constraint into "has an ancestor at all" — a weaker predicate than the caller wrote, and one that
|
|
140
|
-
* still wins specificity over an unconstrained binding.
|
|
141
|
-
*/
|
|
142
|
-
function assertHasCriteria(tags: ReadonlyArray<BindingTag>, helperName: string): void {
|
|
143
|
-
if (tags.length === 0) {
|
|
144
|
-
throw new EmptyTagCriteriaError(helperName);
|
|
145
|
-
}
|
|
146
|
-
}
|