@codefast/di 0.5.0-canary.8 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +387 -0
- package/README.md +3 -1
- package/dist/binding.d.ts +41 -22
- package/dist/binding.d.ts.map +1 -1
- package/dist/binding.js +11 -0
- package/dist/binding.js.map +1 -1
- package/dist/container/binding-builders.d.ts.map +1 -1
- package/dist/container/binding-builders.js +8 -5
- package/dist/container/binding-builders.js.map +1 -1
- package/dist/container/container.d.ts.map +1 -1
- package/dist/container/container.js +67 -110
- package/dist/container/container.js.map +1 -1
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/introspection/inspector.d.ts +0 -1
- package/dist/introspection/inspector.d.ts.map +1 -1
- package/dist/introspection/inspector.js +2 -7
- package/dist/introspection/inspector.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +46 -33
- package/dist/registry.js.map +1 -1
- package/dist/resolution/activation-need.d.ts +4 -2
- package/dist/resolution/activation-need.d.ts.map +1 -1
- package/dist/resolution/activation-need.js +15 -11
- package/dist/resolution/activation-need.js.map +1 -1
- package/dist/resolution/binding-lookup-cache.d.ts +1 -1
- package/dist/resolution/binding-lookup-cache.d.ts.map +1 -1
- package/dist/resolution/binding-lookup-cache.js +18 -2
- package/dist/resolution/binding-lookup-cache.js.map +1 -1
- package/dist/resolution/binding-scope.d.ts +5 -2
- package/dist/resolution/binding-scope.d.ts.map +1 -1
- package/dist/resolution/binding-scope.js +6 -17
- package/dist/resolution/binding-scope.js.map +1 -1
- package/dist/resolution/binding-select.d.ts +8 -1
- package/dist/resolution/binding-select.d.ts.map +1 -1
- package/dist/resolution/binding-select.js +13 -32
- package/dist/resolution/binding-select.js.map +1 -1
- package/dist/resolution/diagnostics.d.ts +2 -2
- package/dist/resolution/diagnostics.d.ts.map +1 -1
- package/dist/resolution/environment.d.ts +47 -21
- package/dist/resolution/environment.d.ts.map +1 -1
- package/dist/resolution/environment.js +134 -31
- package/dist/resolution/environment.js.map +1 -1
- package/dist/resolution/instantiation-plan.d.ts.map +1 -1
- package/dist/resolution/instantiation-plan.js +1 -1
- package/dist/resolution/instantiation-plan.js.map +1 -1
- package/dist/resolution/lifecycle.d.ts.map +1 -1
- package/dist/resolution/lifecycle.js +46 -53
- package/dist/resolution/lifecycle.js.map +1 -1
- package/dist/resolution/resolution-path.d.ts +82 -6
- package/dist/resolution/resolution-path.d.ts.map +1 -1
- package/dist/resolution/resolution-path.js +66 -8
- package/dist/resolution/resolution-path.js.map +1 -1
- package/dist/resolution/resolve-options.d.ts +41 -4
- package/dist/resolution/resolve-options.d.ts.map +1 -1
- package/dist/resolution/resolve-options.js +25 -1
- package/dist/resolution/resolve-options.js.map +1 -1
- package/dist/resolution/resolver.d.ts +29 -11
- package/dist/resolution/resolver.d.ts.map +1 -1
- package/dist/resolution/resolver.js +417 -564
- package/dist/resolution/resolver.js.map +1 -1
- package/dist/resolution/scope.d.ts +4 -0
- package/dist/resolution/scope.d.ts.map +1 -1
- package/dist/resolution/scope.js +8 -0
- package/dist/resolution/scope.js.map +1 -1
- package/package.json +5 -11
- package/src/binding.ts +46 -23
- package/src/container/binding-builders.ts +8 -5
- package/src/container/container.ts +72 -119
- package/src/errors.ts +17 -0
- package/src/index.ts +3 -1
- package/src/introspection/inspector.ts +2 -8
- package/src/registry.ts +52 -35
- package/src/resolution/activation-need.ts +18 -14
- package/src/resolution/binding-lookup-cache.ts +18 -2
- package/src/resolution/binding-scope.ts +6 -17
- package/src/resolution/binding-select.ts +14 -35
- package/src/resolution/diagnostics.ts +2 -2
- package/src/resolution/environment.ts +181 -35
- package/src/resolution/instantiation-plan.ts +5 -5
- package/src/resolution/lifecycle.ts +55 -53
- package/src/resolution/resolution-path.ts +119 -25
- package/src/resolution/resolve-options.ts +51 -4
- package/src/resolution/resolver.ts +582 -785
- package/src/resolution/scope.ts +10 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Cycle-detection bookkeeping carried on the resolution path itself. */
|
|
2
2
|
import { CircularDependencyError } from "#/errors";
|
|
3
|
+
import type { ResolutionFrame } from "#/types";
|
|
3
4
|
|
|
4
5
|
const RESOLUTION_SET_KEY: unique symbol = Symbol("di:resolution-set");
|
|
5
6
|
/**
|
|
@@ -15,29 +16,17 @@ type ResolutionPathWithSet = Array<string> & { [RESOLUTION_SET_KEY]?: Set<string
|
|
|
15
16
|
/**
|
|
16
17
|
* Marks a token as in-flight on this path, throwing if it is already there.
|
|
17
18
|
*
|
|
18
|
-
* @remarks Unmark with `resolutionPath.pop()` plus `set?.delete(name)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
forceSet: true,
|
|
24
|
-
): Set<string>;
|
|
25
|
-
export function enterResolutionPath(
|
|
26
|
-
resolutionPath: Array<string>,
|
|
27
|
-
tokenDisplayName: string,
|
|
28
|
-
forceSet: boolean,
|
|
29
|
-
): Set<string> | undefined;
|
|
30
|
-
/**
|
|
19
|
+
* @remarks Unmark with `resolutionPath.pop()` plus `set?.delete(name)`. Sync only — the async lane
|
|
20
|
+
* never removes an entry, so it extends a branch instead; see {@link extendResolutionBranch}.
|
|
21
|
+
*
|
|
22
|
+
* @returns the membership set once the path is deep enough to carry one, else `undefined`.
|
|
23
|
+
*
|
|
31
24
|
* @since 0.5.0-canary.7
|
|
32
25
|
*/
|
|
33
|
-
export function enterResolutionPath(
|
|
34
|
-
resolutionPath: Array<string>,
|
|
35
|
-
tokenDisplayName: string,
|
|
36
|
-
forceSet: boolean,
|
|
37
|
-
): Set<string> | undefined {
|
|
26
|
+
export function enterResolutionPath(resolutionPath: Array<string>, tokenDisplayName: string): Set<string> | undefined {
|
|
38
27
|
const pathWithSet = resolutionPath as ResolutionPathWithSet;
|
|
39
28
|
let resolutionSet = pathWithSet[RESOLUTION_SET_KEY];
|
|
40
|
-
if (resolutionSet === undefined &&
|
|
29
|
+
if (resolutionSet === undefined && resolutionPath.length >= RESOLUTION_SET_THRESHOLD) {
|
|
41
30
|
resolutionSet = new Set<string>(resolutionPath);
|
|
42
31
|
pathWithSet[RESOLUTION_SET_KEY] = resolutionSet;
|
|
43
32
|
}
|
|
@@ -49,14 +38,119 @@ export function enterResolutionPath(
|
|
|
49
38
|
return resolutionSet;
|
|
50
39
|
}
|
|
51
40
|
|
|
41
|
+
declare const BRANCH_BRAND: unique symbol;
|
|
42
|
+
|
|
52
43
|
/**
|
|
53
|
-
*
|
|
44
|
+
* A resolution path one async branch owns, so appending to it cannot disturb another branch.
|
|
54
45
|
*
|
|
55
|
-
* @
|
|
46
|
+
* @remarks Only {@link extendResolutionBranch} mints one. That is what makes "may this lane append
|
|
47
|
+
* to this array" a question the compiler answers instead of a rule in a doc — a sync frame's path,
|
|
48
|
+
* which that frame will pop, is a plain `Array<string>` and cannot reach a level that owns its own.
|
|
49
|
+
*
|
|
50
|
+
* @since 0.5.0-canary.9
|
|
51
|
+
*/
|
|
52
|
+
export type OwnedBranchPath = Array<string> & { readonly [BRANCH_BRAND]: true };
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The stack half of {@link OwnedBranchPath}, minted only by {@link extendResolutionStackBranch}.
|
|
56
|
+
*
|
|
57
|
+
* @since 0.5.0-canary.9
|
|
58
|
+
*/
|
|
59
|
+
export type OwnedBranchStack = Array<ResolutionFrame> & { readonly [BRANCH_BRAND]: true };
|
|
60
|
+
|
|
61
|
+
declare const BRANCH_DEPTH_BRAND: unique symbol;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* How many leading entries of a path belong to one async branch.
|
|
65
|
+
*
|
|
66
|
+
* @remarks Branded so a bare number cannot be passed: a depth from anywhere but this branch silently
|
|
67
|
+
* re-parents a level.
|
|
68
|
+
*
|
|
69
|
+
* @since 0.5.0-canary.9
|
|
70
|
+
*/
|
|
71
|
+
export type OwnedBranchDepth = number & { readonly [BRANCH_DEPTH_BRAND]: true };
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A path no async branch owns yet, so its first extension must copy rather than append.
|
|
75
|
+
*
|
|
76
|
+
* @since 0.5.0-canary.9
|
|
77
|
+
*/
|
|
78
|
+
export const UNOWNED_BRANCH = -1;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* How far into a path one extension may reach: this branch's own depth, or nobody's.
|
|
82
|
+
*
|
|
83
|
+
* @remarks A union rather than a sentinel hidden inside the branded number, so the two cases are
|
|
84
|
+
* visible at every signature that takes one and `=== UNOWNED_BRANCH` narrows to the owned case.
|
|
85
|
+
*
|
|
86
|
+
* @since 0.5.0-canary.9
|
|
87
|
+
*/
|
|
88
|
+
export type BranchDepth = OwnedBranchDepth | typeof UNOWNED_BRANCH;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The depth a chain's first level extends from, over an array its caller just minted.
|
|
92
|
+
*
|
|
93
|
+
* @since 0.5.0-canary.9
|
|
94
|
+
*/
|
|
95
|
+
export const ROOT_BRANCH = 0 as OwnedBranchDepth;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A branch's own depth: the length its path had when this level took it.
|
|
99
|
+
*
|
|
100
|
+
* @since 0.5.0-canary.9
|
|
101
|
+
*/
|
|
102
|
+
export function branchDepthOf(branch: OwnedBranchPath): OwnedBranchDepth {
|
|
103
|
+
return branch.length as OwnedBranchDepth;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Extends one branch of an append-only path, throwing if the entry is already an ancestor.
|
|
108
|
+
*
|
|
109
|
+
* @remarks Appends in place while this branch still owns the next slot, and copies its own prefix
|
|
110
|
+
* once a sibling has claimed it. Nothing is ever removed, so no async level has to observe its own
|
|
111
|
+
* settlement to unwind — see `ARCHITECTURE.md`.
|
|
112
|
+
*
|
|
113
|
+
* @since 0.5.0-canary.9
|
|
114
|
+
*/
|
|
115
|
+
export function extendResolutionBranch(
|
|
116
|
+
resolutionPath: Array<string>,
|
|
117
|
+
branchDepth: BranchDepth,
|
|
118
|
+
tokenDisplayName: string,
|
|
119
|
+
): OwnedBranchPath {
|
|
120
|
+
const depth = branchDepth === UNOWNED_BRANCH ? resolutionPath.length : branchDepth;
|
|
121
|
+
for (let index = 0; index < depth; index += 1) {
|
|
122
|
+
if (resolutionPath[index] === tokenDisplayName) {
|
|
123
|
+
throw new CircularDependencyError([...resolutionPath.slice(0, depth), tokenDisplayName]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// An unowned array belongs to a sync frame that will pop it, or carries a membership Set this
|
|
127
|
+
// lane cannot keep true; copying is what makes the branch's own appends safe.
|
|
128
|
+
if (branchDepth === resolutionPath.length) {
|
|
129
|
+
// The sole mint: appending in place needs a depth that came from a branch already owned, or
|
|
130
|
+
// ROOT_BRANCH over an array its caller minted for this chain alone.
|
|
131
|
+
resolutionPath.push(tokenDisplayName);
|
|
132
|
+
return resolutionPath as OwnedBranchPath;
|
|
133
|
+
}
|
|
134
|
+
const branch = resolutionPath.slice(0, depth);
|
|
135
|
+
branch.push(tokenDisplayName);
|
|
136
|
+
return branch as OwnedBranchPath;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The stack half of {@link extendResolutionBranch}; frames carry no cycle to detect.
|
|
141
|
+
*
|
|
142
|
+
* @since 0.5.0-canary.9
|
|
56
143
|
*/
|
|
57
|
-
export function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
144
|
+
export function extendResolutionStackBranch(
|
|
145
|
+
resolutionStack: Array<ResolutionFrame>,
|
|
146
|
+
branchDepth: BranchDepth,
|
|
147
|
+
frame: ResolutionFrame,
|
|
148
|
+
): OwnedBranchStack {
|
|
149
|
+
if (branchDepth === resolutionStack.length) {
|
|
150
|
+
resolutionStack.push(frame);
|
|
151
|
+
return resolutionStack as OwnedBranchStack;
|
|
61
152
|
}
|
|
153
|
+
const branch = resolutionStack.slice(0, branchDepth === UNOWNED_BRANCH ? resolutionStack.length : branchDepth);
|
|
154
|
+
branch.push(frame);
|
|
155
|
+
return branch as OwnedBranchStack;
|
|
62
156
|
}
|
|
@@ -1,5 +1,45 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { BindingTag, ResolveOptions } from "#/types";
|
|
1
|
+
import type { Token } from "#/token";
|
|
2
|
+
import type { BindingTag, Constructor, ResolveOptions } from "#/types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What one resolvable dependency declares.
|
|
6
|
+
*
|
|
7
|
+
* @remarks Both dependency sources — a constructor's `ParamMetadata` and a `toResolved`
|
|
8
|
+
* `InjectionDescriptor` — are this shape, which is why one resolve routine serves both.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.5.0-canary.9
|
|
11
|
+
*/
|
|
12
|
+
export interface DependencySlot {
|
|
13
|
+
readonly token: Token<unknown> | Constructor;
|
|
14
|
+
readonly optional: boolean;
|
|
15
|
+
readonly multi: boolean;
|
|
16
|
+
readonly name?: string;
|
|
17
|
+
readonly tags?: ReadonlyArray<BindingTag>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A request whose only criterion is a name — the shape the registry has a direct index for.
|
|
22
|
+
*
|
|
23
|
+
* @since 0.5.0-canary.9
|
|
24
|
+
*/
|
|
25
|
+
export function isNameOnlyOptions(options: ResolveOptions): options is ResolveOptions & { name: string } {
|
|
26
|
+
return (
|
|
27
|
+
options.name !== undefined && options.tag === undefined && (options.tags === undefined || options.tags.length === 0)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The lone entry of a request whose only criterion is a one-element `tags` list — the shape the
|
|
33
|
+
* registry has a direct tag index for.
|
|
34
|
+
*
|
|
35
|
+
* @since 0.5.0-canary.9
|
|
36
|
+
*/
|
|
37
|
+
export function singleTagOnlyOf(options: ResolveOptions): BindingTag | undefined {
|
|
38
|
+
if (options.name !== undefined || options.tag !== undefined) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return options.tags !== undefined && options.tags.length === 1 ? options.tags[0] : undefined;
|
|
42
|
+
}
|
|
3
43
|
|
|
4
44
|
/** Shared core: build a ResolveOptions from already-normalised name + tags. */
|
|
5
45
|
function buildOptions(
|
|
@@ -33,10 +73,17 @@ export function injectionSlotToResolveOptions(injectionSlot: {
|
|
|
33
73
|
}
|
|
34
74
|
|
|
35
75
|
/**
|
|
36
|
-
* Resolve options derived from a binding
|
|
76
|
+
* Resolve options derived from a binding slot (tags may be empty; omits when nothing to match).
|
|
77
|
+
*
|
|
78
|
+
* @remarks Takes the slot structurally rather than as `BindingSlot`, so the slot on a public
|
|
79
|
+
* `BindingSnapshot` — where `name` is an optional property, not a required one holding `undefined` —
|
|
80
|
+
* is accepted by the same call.
|
|
37
81
|
*
|
|
38
82
|
* @since 0.3.16-canary.0
|
|
39
83
|
*/
|
|
40
|
-
export function bindingSlotToResolveOptions(bindingSlot:
|
|
84
|
+
export function bindingSlotToResolveOptions(bindingSlot: {
|
|
85
|
+
readonly name?: string | undefined;
|
|
86
|
+
readonly tags: ReadonlyArray<BindingTag>;
|
|
87
|
+
}): ResolveOptions | undefined {
|
|
41
88
|
return buildOptions(bindingSlot.name, bindingSlot.tags.length > 0 ? bindingSlot.tags : undefined);
|
|
42
89
|
}
|