@codefast/di 0.5.0-canary.3 → 0.5.0-canary.4
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 +6 -0
- package/README.md +3 -3
- package/dist/binding-select.d.mts +3 -3
- package/dist/binding-select.mjs +32 -32
- package/dist/container.d.mts +8 -8
- package/dist/container.mjs +32 -32
- package/dist/decorators/inject.mjs +2 -2
- package/dist/environment.d.mts +15 -15
- package/dist/environment.mjs +23 -23
- package/dist/errors.d.mts +2 -2
- package/dist/errors.mjs +5 -5
- package/dist/inspector.d.mts +3 -3
- package/dist/inspector.mjs +7 -7
- package/dist/resolve-options.d.mts +1 -1
- package/dist/resolve-options.mjs +1 -1
- package/dist/resolver.d.mts +9 -9
- package/dist/resolver.mjs +110 -110
- package/dist/types.d.mts +8 -8
- package/package.json +3 -3
- package/src/binding-select.ts +46 -39
- package/src/container.ts +48 -42
- package/src/decorators/inject.ts +4 -2
- package/src/environment.ts +32 -29
- package/src/errors.ts +5 -5
- package/src/inspector.ts +8 -8
- package/src/resolve-options.ts +1 -1
- package/src/resolver.ts +138 -121
- package/src/types.ts +11 -8
package/src/binding-select.ts
CHANGED
|
@@ -10,11 +10,11 @@ import type { BindingTag, ConstraintContext, ResolveOptions } from "#/types";
|
|
|
10
10
|
*/
|
|
11
11
|
export function selectBinding(
|
|
12
12
|
bindings: ReadonlyArray<Binding>,
|
|
13
|
-
|
|
13
|
+
options: ResolveOptions | undefined,
|
|
14
14
|
ctx: ConstraintContext,
|
|
15
15
|
tokenDisplayName: string,
|
|
16
16
|
): Binding | undefined {
|
|
17
|
-
const candidates = filterBindings(bindings,
|
|
17
|
+
const candidates = filterBindings(bindings, options, ctx);
|
|
18
18
|
if (candidates.length === 0) {
|
|
19
19
|
return undefined;
|
|
20
20
|
}
|
|
@@ -31,46 +31,47 @@ export function selectBinding(
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Select all candidates matching
|
|
34
|
+
* Select all candidates matching options + predicates.
|
|
35
35
|
*
|
|
36
36
|
* @since 0.3.16-canary.0
|
|
37
37
|
*/
|
|
38
38
|
export function selectAllBindings(
|
|
39
39
|
bindings: ReadonlyArray<Binding>,
|
|
40
|
-
|
|
40
|
+
options: ResolveOptions | undefined,
|
|
41
41
|
ctx: ConstraintContext,
|
|
42
42
|
): Array<Binding> {
|
|
43
|
-
return filterBindings(bindings,
|
|
43
|
+
return filterBindings(bindings, options, ctx, "all");
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function filterBindings(
|
|
47
47
|
bindings: ReadonlyArray<Binding>,
|
|
48
|
-
|
|
48
|
+
options: ResolveOptions | undefined,
|
|
49
49
|
ctx: ConstraintContext,
|
|
50
50
|
selectionMode: "single" | "all" = "single",
|
|
51
51
|
): Array<Binding> {
|
|
52
|
-
if (
|
|
53
|
-
const
|
|
52
|
+
if (options === undefined) {
|
|
53
|
+
const resultWithoutOptions: Array<Binding> = [];
|
|
54
54
|
if (selectionMode === "all") {
|
|
55
55
|
for (const binding of bindings) {
|
|
56
56
|
if (matchesPredicate(binding, ctx)) {
|
|
57
|
-
|
|
57
|
+
resultWithoutOptions.push(binding);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
} else {
|
|
61
61
|
for (const binding of bindings) {
|
|
62
62
|
const slot = binding.slot;
|
|
63
63
|
if (slot.name === undefined && slot.tags.length === 0 && matchesPredicate(binding, ctx)) {
|
|
64
|
-
|
|
64
|
+
resultWithoutOptions.push(binding);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
-
return
|
|
68
|
+
return resultWithoutOptions;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const result: Array<Binding> = [];
|
|
72
72
|
for (const binding of bindings) {
|
|
73
|
-
const slotMatched =
|
|
73
|
+
const slotMatched =
|
|
74
|
+
selectionMode === "all" ? matchesSlotForResolveAll(binding, options) : matchesSlot(binding, options);
|
|
74
75
|
if (slotMatched && matchesPredicate(binding, ctx)) {
|
|
75
76
|
result.push(binding);
|
|
76
77
|
}
|
|
@@ -78,49 +79,51 @@ function filterBindings(
|
|
|
78
79
|
return result;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
function matchesSlotForResolveAll(binding: Binding,
|
|
82
|
+
function matchesSlotForResolveAll(binding: Binding, options: ResolveOptions | undefined): boolean {
|
|
82
83
|
const hasExplicitSlotFilter =
|
|
83
|
-
|
|
84
|
-
(
|
|
84
|
+
options !== undefined &&
|
|
85
|
+
(options.name !== undefined ||
|
|
86
|
+
(options.tags !== undefined && options.tags.length > 0) ||
|
|
87
|
+
options.tag !== undefined);
|
|
85
88
|
if (!hasExplicitSlotFilter) {
|
|
86
89
|
return true;
|
|
87
90
|
}
|
|
88
|
-
return matchesSlot(binding,
|
|
91
|
+
return matchesSlot(binding, options);
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
function matchesSlot(binding: Binding,
|
|
94
|
+
function matchesSlot(binding: Binding, options: ResolveOptions | undefined): boolean {
|
|
92
95
|
const slot = binding.slot;
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const
|
|
96
|
+
const requestedName = options?.name;
|
|
97
|
+
const requestedTags = options?.tags;
|
|
98
|
+
const singleRequestedTag = options?.tag;
|
|
99
|
+
const hasRequestedTags = (requestedTags?.length ?? 0) > 0 || singleRequestedTag !== undefined;
|
|
97
100
|
|
|
98
101
|
// Match by name
|
|
99
102
|
if (slot.name !== undefined) {
|
|
100
|
-
if (
|
|
103
|
+
if (requestedName === undefined) {
|
|
101
104
|
return false;
|
|
102
105
|
}
|
|
103
|
-
if (slot.name !==
|
|
106
|
+
if (slot.name !== requestedName) {
|
|
104
107
|
return false;
|
|
105
108
|
}
|
|
106
|
-
} else if (
|
|
107
|
-
// Binding has no name but
|
|
109
|
+
} else if (requestedName !== undefined) {
|
|
110
|
+
// Binding has no name but options requests a specific name — no match
|
|
108
111
|
return false;
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
// Match by tags — binding's tags must all be present in
|
|
114
|
+
// Match by tags — binding's tags must all be present in options
|
|
112
115
|
if (slot.tags.length > 0) {
|
|
113
|
-
if (!
|
|
116
|
+
if (!hasRequestedTags) {
|
|
114
117
|
return false;
|
|
115
118
|
}
|
|
116
119
|
for (const [tagKey, tagValue] of slot.tags) {
|
|
117
|
-
if (!
|
|
120
|
+
if (!matchesRequestedTag(tagKey, tagValue, requestedTags, singleRequestedTag)) {
|
|
118
121
|
return false;
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
|
-
} else if (
|
|
122
|
-
// Binding has no tags but
|
|
123
|
-
// But: default slot (no tags, no name) can match when
|
|
124
|
+
} else if (hasRequestedTags) {
|
|
125
|
+
// Binding has no tags but options requires tags — no match for tagged slots
|
|
126
|
+
// But: default slot (no tags, no name) can match when options has tags if there are no tag-slotted bindings
|
|
124
127
|
// Actually per spec: resolveAll with tags only returns bindings that have those tags
|
|
125
128
|
// and resolve with tags requires exact match
|
|
126
129
|
return false;
|
|
@@ -129,21 +132,25 @@ function matchesSlot(binding: Binding, hint: ResolveOptions | undefined): boolea
|
|
|
129
132
|
return true;
|
|
130
133
|
}
|
|
131
134
|
|
|
132
|
-
function
|
|
135
|
+
function matchesRequestedTag(
|
|
133
136
|
tagKey: string,
|
|
134
137
|
tagValue: unknown,
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
requestedTags: ReadonlyArray<BindingTag> | undefined,
|
|
139
|
+
singleRequestedTag: BindingTag | undefined,
|
|
137
140
|
): boolean {
|
|
138
|
-
if (
|
|
141
|
+
if (
|
|
142
|
+
singleRequestedTag !== undefined &&
|
|
143
|
+
singleRequestedTag[0] === tagKey &&
|
|
144
|
+
Object.is(singleRequestedTag[1], tagValue)
|
|
145
|
+
) {
|
|
139
146
|
return true;
|
|
140
147
|
}
|
|
141
|
-
if (
|
|
148
|
+
if (requestedTags === undefined || requestedTags.length === 0) {
|
|
142
149
|
return false;
|
|
143
150
|
}
|
|
144
|
-
for (let index = 0; index <
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
151
|
+
for (let index = 0; index < requestedTags.length; index += 1) {
|
|
152
|
+
const requestedTag = requestedTags[index]!;
|
|
153
|
+
if (requestedTag[0] === tagKey && Object.is(requestedTag[1], tagValue)) {
|
|
147
154
|
return true;
|
|
148
155
|
}
|
|
149
156
|
}
|
package/src/container.ts
CHANGED
|
@@ -78,15 +78,18 @@ export interface Container {
|
|
|
78
78
|
onActivation<const Value>(token: Token<Value> | Constructor<Value>, handler: ActivationHandler<Value>): void;
|
|
79
79
|
onDeactivation<const Value>(token: Token<Value> | Constructor<Value>, handler: DeactivationHandler<Value>): void;
|
|
80
80
|
|
|
81
|
-
resolve<const Value>(token: Token<Value> | Constructor<Value>,
|
|
82
|
-
resolveAsync<const Value>(token: Token<Value> | Constructor<Value>,
|
|
83
|
-
resolveOptional<const Value>(token: Token<Value> | Constructor<Value>,
|
|
81
|
+
resolve<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Value;
|
|
82
|
+
resolveAsync<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Promise<Value>;
|
|
83
|
+
resolveOptional<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Value | undefined;
|
|
84
84
|
resolveOptionalAsync<const Value>(
|
|
85
85
|
token: Token<Value> | Constructor<Value>,
|
|
86
|
-
|
|
86
|
+
options?: ResolveOptions,
|
|
87
87
|
): Promise<Value | undefined>;
|
|
88
|
-
resolveAll<const Value>(token: Token<Value> | Constructor<Value>,
|
|
89
|
-
resolveAllAsync<const Value>(
|
|
88
|
+
resolveAll<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Array<Value>;
|
|
89
|
+
resolveAllAsync<const Value>(
|
|
90
|
+
token: Token<Value> | Constructor<Value>,
|
|
91
|
+
options?: ResolveOptions,
|
|
92
|
+
): Promise<Array<Value>>;
|
|
90
93
|
|
|
91
94
|
createChild(): Container;
|
|
92
95
|
|
|
@@ -97,8 +100,8 @@ export interface Container {
|
|
|
97
100
|
initializeAsync(): Promise<void>;
|
|
98
101
|
validate(): void;
|
|
99
102
|
|
|
100
|
-
has(token: Token<unknown> | Constructor,
|
|
101
|
-
hasOwn(token: Token<unknown> | Constructor,
|
|
103
|
+
has(token: Token<unknown> | Constructor, options?: ResolveOptions): boolean;
|
|
104
|
+
hasOwn(token: Token<unknown> | Constructor, options?: ResolveOptions): boolean;
|
|
102
105
|
lookupBindings<const Value>(token: Token<Value> | Constructor<Value>): ReadonlyArray<BindingSnapshot>;
|
|
103
106
|
inspect(): ContainerSnapshot;
|
|
104
107
|
generateDependencyGraph(options?: GraphOptions): ContainerGraphJson;
|
|
@@ -480,43 +483,46 @@ class DefaultContainer implements Container {
|
|
|
480
483
|
|
|
481
484
|
// ── Resolution ────────────────────────────────────────────────────────────
|
|
482
485
|
|
|
483
|
-
resolve<const Value>(token: Token<Value> | Constructor<Value>,
|
|
486
|
+
resolve<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Value {
|
|
484
487
|
this._assertNotDisposed();
|
|
485
|
-
if (
|
|
488
|
+
if (options === undefined) {
|
|
486
489
|
return this._resolver.resolveFromContext(token, [], []);
|
|
487
490
|
}
|
|
488
|
-
return this._resolver.resolve(token,
|
|
491
|
+
return this._resolver.resolve(token, options, [], []);
|
|
489
492
|
}
|
|
490
493
|
|
|
491
|
-
resolveAsync<const Value>(token: Token<Value> | Constructor<Value>,
|
|
494
|
+
resolveAsync<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Promise<Value> {
|
|
492
495
|
this._assertNotDisposed();
|
|
493
|
-
if (
|
|
496
|
+
if (options === undefined) {
|
|
494
497
|
return this._resolver.resolveAsyncFromContext(token, [], []);
|
|
495
498
|
}
|
|
496
|
-
return this._resolver.resolveAsync(token,
|
|
499
|
+
return this._resolver.resolveAsync(token, options, [], []);
|
|
497
500
|
}
|
|
498
501
|
|
|
499
|
-
resolveOptional<const Value>(token: Token<Value> | Constructor<Value>,
|
|
502
|
+
resolveOptional<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Value | undefined {
|
|
500
503
|
this._assertNotDisposed();
|
|
501
|
-
return this._resolver.resolveOptional(token,
|
|
504
|
+
return this._resolver.resolveOptional(token, options, [], []);
|
|
502
505
|
}
|
|
503
506
|
|
|
504
507
|
resolveOptionalAsync<const Value>(
|
|
505
508
|
token: Token<Value> | Constructor<Value>,
|
|
506
|
-
|
|
509
|
+
options?: ResolveOptions,
|
|
507
510
|
): Promise<Value | undefined> {
|
|
508
511
|
this._assertNotDisposed();
|
|
509
|
-
return this._resolver.resolveOptionalAsync(token,
|
|
512
|
+
return this._resolver.resolveOptionalAsync(token, options, [], []);
|
|
510
513
|
}
|
|
511
514
|
|
|
512
|
-
resolveAll<const Value>(token: Token<Value> | Constructor<Value>,
|
|
515
|
+
resolveAll<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Array<Value> {
|
|
513
516
|
this._assertNotDisposed();
|
|
514
|
-
return this._resolver.resolveAll(token,
|
|
517
|
+
return this._resolver.resolveAll(token, options, [], []);
|
|
515
518
|
}
|
|
516
519
|
|
|
517
|
-
resolveAllAsync<const Value>(
|
|
520
|
+
resolveAllAsync<const Value>(
|
|
521
|
+
token: Token<Value> | Constructor<Value>,
|
|
522
|
+
options?: ResolveOptions,
|
|
523
|
+
): Promise<Array<Value>> {
|
|
518
524
|
this._assertNotDisposed();
|
|
519
|
-
return this._resolver.resolveAllAsync(token,
|
|
525
|
+
return this._resolver.resolveAllAsync(token, options, [], []);
|
|
520
526
|
}
|
|
521
527
|
|
|
522
528
|
// ── Child ─────────────────────────────────────────────────────────────────
|
|
@@ -572,8 +578,8 @@ class DefaultContainer implements Container {
|
|
|
572
578
|
if (binding.kind === "constant" && binding.onActivation === undefined) {
|
|
573
579
|
continue;
|
|
574
580
|
}
|
|
575
|
-
const
|
|
576
|
-
await this.resolveAsync(binding.token as Token<unknown>,
|
|
581
|
+
const slotOptions = bindingSlotToResolveOptions(binding.slot);
|
|
582
|
+
await this.resolveAsync(binding.token as Token<unknown>, slotOptions);
|
|
577
583
|
}
|
|
578
584
|
}
|
|
579
585
|
}
|
|
@@ -658,7 +664,7 @@ class DefaultContainer implements Container {
|
|
|
658
664
|
}
|
|
659
665
|
}
|
|
660
666
|
|
|
661
|
-
private _followAliasChainToTerminal(binding: Binding,
|
|
667
|
+
private _followAliasChainToTerminal(binding: Binding, options: ResolveOptions | undefined): Binding | undefined {
|
|
662
668
|
const cyclePath: Array<string> = [];
|
|
663
669
|
const seenAliasIds = new Set<BindingIdentifier>();
|
|
664
670
|
let current: Binding | undefined = binding;
|
|
@@ -670,7 +676,7 @@ class DefaultContainer implements Container {
|
|
|
670
676
|
seenAliasIds.add(current.id);
|
|
671
677
|
cyclePath.push(tokenName(current.token as Token<unknown>));
|
|
672
678
|
const nextToken = current.target as Token<unknown> | Constructor;
|
|
673
|
-
const next = this._resolver.peekBindingForValidate(nextToken,
|
|
679
|
+
const next = this._resolver.peekBindingForValidate(nextToken, options);
|
|
674
680
|
if (next === undefined) {
|
|
675
681
|
return undefined;
|
|
676
682
|
}
|
|
@@ -698,27 +704,27 @@ class DefaultContainer implements Container {
|
|
|
698
704
|
return edges;
|
|
699
705
|
}
|
|
700
706
|
for (const param of meta.params) {
|
|
701
|
-
const
|
|
707
|
+
const paramOptions = injectionSlotToResolveOptions(param);
|
|
702
708
|
if (param.optional) {
|
|
703
709
|
continue;
|
|
704
710
|
}
|
|
705
711
|
const tokenRef = param.token;
|
|
706
712
|
if (param.multi) {
|
|
707
|
-
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef,
|
|
713
|
+
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, paramOptions);
|
|
708
714
|
for (const cand of candidates) {
|
|
709
|
-
const term = this._followAliasChainToTerminal(cand,
|
|
715
|
+
const term = this._followAliasChainToTerminal(cand, paramOptions);
|
|
710
716
|
pushTerminal(term, term !== undefined ? tokenName(term.token as Token<unknown>) : "");
|
|
711
717
|
}
|
|
712
718
|
continue;
|
|
713
719
|
}
|
|
714
720
|
const found =
|
|
715
|
-
|
|
721
|
+
paramOptions === undefined
|
|
716
722
|
? this._resolver.peekBindingForValidate(tokenRef, undefined)
|
|
717
|
-
: this._resolver.peekBindingForValidate(tokenRef,
|
|
723
|
+
: this._resolver.peekBindingForValidate(tokenRef, paramOptions);
|
|
718
724
|
if (found === undefined) {
|
|
719
725
|
continue;
|
|
720
726
|
}
|
|
721
|
-
const term = this._followAliasChainToTerminal(found.binding,
|
|
727
|
+
const term = this._followAliasChainToTerminal(found.binding, paramOptions);
|
|
722
728
|
pushTerminal(term, term !== undefined ? tokenName(term.token as Token<unknown>) : "");
|
|
723
729
|
}
|
|
724
730
|
return edges;
|
|
@@ -726,27 +732,27 @@ class DefaultContainer implements Container {
|
|
|
726
732
|
|
|
727
733
|
if (binding.kind === "resolved" || binding.kind === "resolved-async") {
|
|
728
734
|
for (const dep of binding.deps) {
|
|
729
|
-
const
|
|
735
|
+
const depOptions = injectionSlotToResolveOptions(dep);
|
|
730
736
|
if (dep.optional) {
|
|
731
737
|
continue;
|
|
732
738
|
}
|
|
733
739
|
const tokenRef = dep.token as Token<unknown> | Constructor;
|
|
734
740
|
if (dep.multi) {
|
|
735
|
-
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef,
|
|
741
|
+
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, depOptions);
|
|
736
742
|
for (const cand of candidates) {
|
|
737
|
-
const term = this._followAliasChainToTerminal(cand,
|
|
743
|
+
const term = this._followAliasChainToTerminal(cand, depOptions);
|
|
738
744
|
pushTerminal(term, term !== undefined ? tokenName(term.token as Token<unknown>) : "");
|
|
739
745
|
}
|
|
740
746
|
continue;
|
|
741
747
|
}
|
|
742
748
|
const found =
|
|
743
|
-
|
|
749
|
+
depOptions === undefined
|
|
744
750
|
? this._resolver.peekBindingForValidate(tokenRef, undefined)
|
|
745
|
-
: this._resolver.peekBindingForValidate(tokenRef,
|
|
751
|
+
: this._resolver.peekBindingForValidate(tokenRef, depOptions);
|
|
746
752
|
if (found === undefined) {
|
|
747
753
|
continue;
|
|
748
754
|
}
|
|
749
|
-
const term = this._followAliasChainToTerminal(found.binding,
|
|
755
|
+
const term = this._followAliasChainToTerminal(found.binding, depOptions);
|
|
750
756
|
pushTerminal(term, term !== undefined ? tokenName(term.token as Token<unknown>) : "");
|
|
751
757
|
}
|
|
752
758
|
}
|
|
@@ -756,14 +762,14 @@ class DefaultContainer implements Container {
|
|
|
756
762
|
|
|
757
763
|
// ── Introspection ─────────────────────────────────────────────────────────
|
|
758
764
|
|
|
759
|
-
has(token: Token<unknown> | Constructor,
|
|
765
|
+
has(token: Token<unknown> | Constructor, options?: ResolveOptions): boolean {
|
|
760
766
|
this._assertNotDisposed();
|
|
761
|
-
return this._inspector.has(token,
|
|
767
|
+
return this._inspector.has(token, options, () => this._parent?.has(token, options) ?? false);
|
|
762
768
|
}
|
|
763
769
|
|
|
764
|
-
hasOwn(token: Token<unknown> | Constructor,
|
|
770
|
+
hasOwn(token: Token<unknown> | Constructor, options?: ResolveOptions): boolean {
|
|
765
771
|
this._assertNotDisposed();
|
|
766
|
-
return this._inspector.hasOwn(token,
|
|
772
|
+
return this._inspector.hasOwn(token, options);
|
|
767
773
|
}
|
|
768
774
|
|
|
769
775
|
lookupBindings<const Value>(token: Token<Value> | Constructor<Value>): ReadonlyArray<BindingSnapshot> {
|
package/src/decorators/inject.ts
CHANGED
|
@@ -159,14 +159,16 @@ export function inject<const Value>(
|
|
|
159
159
|
if (container === undefined) {
|
|
160
160
|
throw new MissingContainerContextError(String(context.name));
|
|
161
161
|
}
|
|
162
|
-
const
|
|
162
|
+
const resolveOptions =
|
|
163
163
|
options === undefined
|
|
164
164
|
? undefined
|
|
165
165
|
: injectionSlotToResolveOptions({
|
|
166
166
|
...(options.name !== undefined ? { name: options.name } : {}),
|
|
167
167
|
...(options.tags !== undefined ? { tags: options.tags } : {}),
|
|
168
168
|
});
|
|
169
|
-
const value = descriptor.optional
|
|
169
|
+
const value = descriptor.optional
|
|
170
|
+
? container.resolveOptional(token, resolveOptions)
|
|
171
|
+
: container.resolve(token, resolveOptions);
|
|
170
172
|
context.access.set(this, value as Value);
|
|
171
173
|
});
|
|
172
174
|
|
package/src/environment.ts
CHANGED
|
@@ -48,7 +48,7 @@ export interface ResolverCallbacks {
|
|
|
48
48
|
): Value;
|
|
49
49
|
resolve<const Value>(
|
|
50
50
|
token: Token<Value> | Constructor<Value>,
|
|
51
|
-
|
|
51
|
+
options: ResolveOptions | undefined,
|
|
52
52
|
resolutionPath: Array<string>,
|
|
53
53
|
resolutionStack: Array<ResolutionFrame>,
|
|
54
54
|
): Value;
|
|
@@ -59,31 +59,31 @@ export interface ResolverCallbacks {
|
|
|
59
59
|
): Promise<Value>;
|
|
60
60
|
resolveAsync<const Value>(
|
|
61
61
|
token: Token<Value> | Constructor<Value>,
|
|
62
|
-
|
|
62
|
+
options: ResolveOptions | undefined,
|
|
63
63
|
resolutionPath: Array<string>,
|
|
64
64
|
resolutionStack: Array<ResolutionFrame>,
|
|
65
65
|
): Promise<Value>;
|
|
66
66
|
resolveOptional<const Value>(
|
|
67
67
|
token: Token<Value> | Constructor<Value>,
|
|
68
|
-
|
|
68
|
+
options: ResolveOptions | undefined,
|
|
69
69
|
resolutionPath: Array<string>,
|
|
70
70
|
resolutionStack: Array<ResolutionFrame>,
|
|
71
71
|
): Value | undefined;
|
|
72
72
|
resolveOptionalAsync<const Value>(
|
|
73
73
|
token: Token<Value> | Constructor<Value>,
|
|
74
|
-
|
|
74
|
+
options: ResolveOptions | undefined,
|
|
75
75
|
resolutionPath: Array<string>,
|
|
76
76
|
resolutionStack: Array<ResolutionFrame>,
|
|
77
77
|
): Promise<Value | undefined>;
|
|
78
78
|
resolveAll<const Value>(
|
|
79
79
|
token: Token<Value> | Constructor<Value>,
|
|
80
|
-
|
|
80
|
+
options: ResolveOptions | undefined,
|
|
81
81
|
resolutionPath: Array<string>,
|
|
82
82
|
resolutionStack: Array<ResolutionFrame>,
|
|
83
83
|
): Array<Value>;
|
|
84
84
|
resolveAllAsync<const Value>(
|
|
85
85
|
token: Token<Value> | Constructor<Value>,
|
|
86
|
-
|
|
86
|
+
options: ResolveOptions | undefined,
|
|
87
87
|
resolutionPath: Array<string>,
|
|
88
88
|
resolutionStack: Array<ResolutionFrame>,
|
|
89
89
|
): Promise<Array<Value>>;
|
|
@@ -96,25 +96,25 @@ export class DefaultResolutionContext implements ResolutionContext {
|
|
|
96
96
|
private _resolver: ResolverCallbacks;
|
|
97
97
|
private _resolutionPath: Array<string>;
|
|
98
98
|
private _resolutionStack: Array<ResolutionFrame>;
|
|
99
|
-
private
|
|
99
|
+
private _currentOptions: ResolveOptions | undefined;
|
|
100
100
|
|
|
101
101
|
constructor(
|
|
102
102
|
resolver: ResolverCallbacks,
|
|
103
103
|
resolutionPath: Array<string>,
|
|
104
104
|
resolutionStack: Array<ResolutionFrame>,
|
|
105
|
-
|
|
105
|
+
currentOptions: ResolveOptions | undefined,
|
|
106
106
|
) {
|
|
107
107
|
this._resolver = resolver;
|
|
108
108
|
this._resolutionPath = resolutionPath;
|
|
109
109
|
this._resolutionStack = resolutionStack;
|
|
110
|
-
this.
|
|
110
|
+
this._currentOptions = currentOptions;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
private _graph: ConstraintContext | undefined;
|
|
114
114
|
|
|
115
115
|
get graph(): ConstraintContext {
|
|
116
116
|
if (this._graph === undefined) {
|
|
117
|
-
this._graph = new DefaultConstraintContext(this._resolutionPath, this._resolutionStack, this.
|
|
117
|
+
this._graph = new DefaultConstraintContext(this._resolutionPath, this._resolutionStack, this._currentOptions);
|
|
118
118
|
}
|
|
119
119
|
return this._graph;
|
|
120
120
|
}
|
|
@@ -123,46 +123,49 @@ export class DefaultResolutionContext implements ResolutionContext {
|
|
|
123
123
|
resolver: ResolverCallbacks,
|
|
124
124
|
resolutionPath: Array<string>,
|
|
125
125
|
resolutionStack: Array<ResolutionFrame>,
|
|
126
|
-
|
|
126
|
+
currentOptions: ResolveOptions | undefined,
|
|
127
127
|
): void {
|
|
128
128
|
this._resolver = resolver;
|
|
129
129
|
this._resolutionPath = resolutionPath;
|
|
130
130
|
this._resolutionStack = resolutionStack;
|
|
131
|
-
this.
|
|
131
|
+
this._currentOptions = currentOptions;
|
|
132
132
|
this._graph = undefined;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
resolve<const Value>(token: Token<Value> | Constructor<Value>,
|
|
136
|
-
if (
|
|
135
|
+
resolve<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Value {
|
|
136
|
+
if (options === undefined) {
|
|
137
137
|
return this._resolver.resolveFromContext(token, this._resolutionPath, this._resolutionStack);
|
|
138
138
|
}
|
|
139
|
-
return this._resolver.resolve(token,
|
|
139
|
+
return this._resolver.resolve(token, options, this._resolutionPath, this._resolutionStack);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
resolveAsync<const Value>(token: Token<Value> | Constructor<Value>,
|
|
143
|
-
if (
|
|
142
|
+
resolveAsync<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Promise<Value> {
|
|
143
|
+
if (options === undefined) {
|
|
144
144
|
return this._resolver.resolveAsyncFromContext(token, this._resolutionPath, this._resolutionStack);
|
|
145
145
|
}
|
|
146
|
-
return this._resolver.resolveAsync(token,
|
|
146
|
+
return this._resolver.resolveAsync(token, options, this._resolutionPath, this._resolutionStack);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
resolveOptional<const Value>(token: Token<Value> | Constructor<Value>,
|
|
150
|
-
return this._resolver.resolveOptional(token,
|
|
149
|
+
resolveOptional<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Value | undefined {
|
|
150
|
+
return this._resolver.resolveOptional(token, options, this._resolutionPath, this._resolutionStack);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
resolveOptionalAsync<const Value>(
|
|
154
154
|
token: Token<Value> | Constructor<Value>,
|
|
155
|
-
|
|
155
|
+
options?: ResolveOptions,
|
|
156
156
|
): Promise<Value | undefined> {
|
|
157
|
-
return this._resolver.resolveOptionalAsync(token,
|
|
157
|
+
return this._resolver.resolveOptionalAsync(token, options, this._resolutionPath, this._resolutionStack);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
resolveAll<const Value>(token: Token<Value> | Constructor<Value>,
|
|
161
|
-
return this._resolver.resolveAll(token,
|
|
160
|
+
resolveAll<const Value>(token: Token<Value> | Constructor<Value>, options?: ResolveOptions): Array<Value> {
|
|
161
|
+
return this._resolver.resolveAll(token, options, this._resolutionPath, this._resolutionStack);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
resolveAllAsync<const Value>(
|
|
165
|
-
|
|
164
|
+
resolveAllAsync<const Value>(
|
|
165
|
+
token: Token<Value> | Constructor<Value>,
|
|
166
|
+
options?: ResolveOptions,
|
|
167
|
+
): Promise<Array<Value>> {
|
|
168
|
+
return this._resolver.resolveAllAsync(token, options, this._resolutionPath, this._resolutionStack);
|
|
166
169
|
}
|
|
167
170
|
}
|
|
168
171
|
|
|
@@ -170,17 +173,17 @@ class DefaultConstraintContext implements ConstraintContext {
|
|
|
170
173
|
readonly resolutionPath: ReadonlyArray<string>;
|
|
171
174
|
readonly resolutionStack: ReadonlyArray<ResolutionFrame>;
|
|
172
175
|
readonly parent: ResolutionFrame | undefined;
|
|
173
|
-
readonly
|
|
176
|
+
readonly currentResolveOptions: ResolveOptions | undefined;
|
|
174
177
|
|
|
175
178
|
constructor(
|
|
176
179
|
resolutionPath: ReadonlyArray<string>,
|
|
177
180
|
resolutionStack: ReadonlyArray<ResolutionFrame>,
|
|
178
|
-
|
|
181
|
+
currentResolveOptions: ResolveOptions | undefined,
|
|
179
182
|
) {
|
|
180
183
|
this.resolutionPath = resolutionPath;
|
|
181
184
|
this.resolutionStack = resolutionStack;
|
|
182
185
|
this.parent = resolutionStack.at(-1);
|
|
183
|
-
this.
|
|
186
|
+
this.currentResolveOptions = currentResolveOptions;
|
|
184
187
|
}
|
|
185
188
|
|
|
186
189
|
private _ancestors: ReadonlyArray<ResolutionFrame> | undefined;
|
package/src/errors.ts
CHANGED
|
@@ -42,15 +42,15 @@ export class TokenNotBoundError extends DiError {
|
|
|
42
42
|
export class NoMatchingBindingError extends DiError {
|
|
43
43
|
readonly code = "NO_MATCHING_BINDING";
|
|
44
44
|
readonly tokenName: string;
|
|
45
|
-
readonly
|
|
45
|
+
readonly options: ResolveOptions;
|
|
46
46
|
readonly availableSlots: Array<string>;
|
|
47
47
|
|
|
48
|
-
constructor(tokenName: string,
|
|
49
|
-
const
|
|
48
|
+
constructor(tokenName: string, options: ResolveOptions, availableSlots: Array<string>) {
|
|
49
|
+
const optionsString = JSON.stringify(options);
|
|
50
50
|
const slotsStr = availableSlots.join(", ");
|
|
51
|
-
super(`No binding for '${tokenName}' matching ${
|
|
51
|
+
super(`No binding for '${tokenName}' matching ${optionsString}. Available slots: [${slotsStr}].`);
|
|
52
52
|
this.tokenName = tokenName;
|
|
53
|
-
this.
|
|
53
|
+
this.options = options;
|
|
54
54
|
this.availableSlots = availableSlots;
|
|
55
55
|
}
|
|
56
56
|
}
|
package/src/inspector.ts
CHANGED
|
@@ -69,11 +69,11 @@ export class Inspector {
|
|
|
69
69
|
return bindings.map((binding) => this._toSnapshot(binding));
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
has(token: Token<unknown> | Constructor,
|
|
72
|
+
has(token: Token<unknown> | Constructor, options?: ResolveOptions, parentHas?: () => boolean): boolean {
|
|
73
73
|
const bindings = this._registry.getAll(token);
|
|
74
74
|
if (bindings.length > 0) {
|
|
75
|
-
if (
|
|
76
|
-
if (selectBinding(bindings,
|
|
75
|
+
if (options !== undefined) {
|
|
76
|
+
if (selectBinding(bindings, options, this._makeConstraintContext(options), tokenName(token)) !== undefined) {
|
|
77
77
|
return true;
|
|
78
78
|
}
|
|
79
79
|
} else {
|
|
@@ -83,24 +83,24 @@ export class Inspector {
|
|
|
83
83
|
return parentHas?.() ?? false;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
hasOwn(token: Token<unknown> | Constructor,
|
|
86
|
+
hasOwn(token: Token<unknown> | Constructor, options?: ResolveOptions): boolean {
|
|
87
87
|
const bindings = this._registry.getAll(token);
|
|
88
88
|
if (bindings.length === 0) {
|
|
89
89
|
return false;
|
|
90
90
|
}
|
|
91
|
-
if (
|
|
92
|
-
return selectBinding(bindings,
|
|
91
|
+
if (options !== undefined) {
|
|
92
|
+
return selectBinding(bindings, options, this._makeConstraintContext(options), tokenName(token)) !== undefined;
|
|
93
93
|
}
|
|
94
94
|
return true;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
private
|
|
97
|
+
private _makeConstraintContext(options: ResolveOptions): ConstraintContext {
|
|
98
98
|
return {
|
|
99
99
|
resolutionPath: [],
|
|
100
100
|
resolutionStack: [],
|
|
101
101
|
parent: undefined,
|
|
102
102
|
ancestors: [],
|
|
103
|
-
|
|
103
|
+
currentResolveOptions: options,
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
|
package/src/resolve-options.ts
CHANGED
|
@@ -33,7 +33,7 @@ export function injectionSlotToResolveOptions(injectionSlot: {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Resolve options derived from a binding {@link BindingSlot} (tags may be empty; omits when nothing to match).
|
|
37
37
|
*
|
|
38
38
|
* @since 0.3.16-canary.0
|
|
39
39
|
*/
|