@codefast/di 0.5.0-canary.7 → 0.5.0-canary.9
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 +205 -0
- package/README.md +6 -2
- package/dist/binding.d.ts +85 -24
- package/dist/binding.d.ts.map +1 -1
- package/dist/binding.js +55 -0
- package/dist/binding.js.map +1 -1
- package/dist/constructor-type.d.ts +4 -5
- package/dist/constructor-type.d.ts.map +1 -1
- package/dist/container/binding-builders.d.ts +32 -11
- package/dist/container/binding-builders.d.ts.map +1 -1
- package/dist/container/binding-builders.js +144 -192
- package/dist/container/binding-builders.js.map +1 -1
- package/dist/container/container.d.ts.map +1 -1
- package/dist/container/container.js +141 -201
- package/dist/container/container.js.map +1 -1
- package/dist/decorators/inject.d.ts +2 -4
- package/dist/decorators/inject.d.ts.map +1 -1
- package/dist/decorators/inject.js.map +1 -1
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +30 -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 +3 -8
- package/dist/introspection/inspector.js.map +1 -1
- package/dist/metadata/metadata-keys.d.ts +3 -6
- package/dist/metadata/metadata-keys.d.ts.map +1 -1
- package/dist/metadata/metadata-keys.js +3 -6
- package/dist/metadata/metadata-keys.js.map +1 -1
- package/dist/registry.d.ts +14 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +81 -78
- package/dist/registry.js.map +1 -1
- package/dist/resolution/activation-need.d.ts +27 -0
- package/dist/resolution/activation-need.d.ts.map +1 -0
- package/dist/resolution/activation-need.js +68 -0
- package/dist/resolution/activation-need.js.map +1 -0
- package/dist/resolution/binding-lookup-cache.d.ts +41 -0
- package/dist/resolution/binding-lookup-cache.d.ts.map +1 -0
- package/dist/resolution/binding-lookup-cache.js +118 -0
- package/dist/resolution/binding-lookup-cache.js.map +1 -0
- 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 +14 -36
- package/dist/resolution/binding-select.js.map +1 -1
- package/dist/resolution/class-introspector.d.ts +27 -0
- package/dist/resolution/class-introspector.d.ts.map +1 -0
- package/dist/resolution/class-introspector.js +60 -0
- package/dist/resolution/class-introspector.js.map +1 -0
- package/dist/resolution/diagnostics.d.ts +41 -0
- package/dist/resolution/diagnostics.d.ts.map +1 -0
- package/dist/resolution/diagnostics.js +18 -0
- package/dist/resolution/diagnostics.js.map +1 -0
- package/dist/resolution/environment.d.ts +48 -1
- package/dist/resolution/environment.d.ts.map +1 -1
- package/dist/resolution/environment.js +134 -5
- package/dist/resolution/environment.js.map +1 -1
- package/dist/resolution/instantiation-plan.d.ts +15 -15
- package/dist/resolution/instantiation-plan.d.ts.map +1 -1
- package/dist/resolution/instantiation-plan.js +69 -49
- package/dist/resolution/instantiation-plan.js.map +1 -1
- package/dist/resolution/lifecycle.d.ts +2 -0
- package/dist/resolution/lifecycle.d.ts.map +1 -1
- package/dist/resolution/lifecycle.js +60 -62
- package/dist/resolution/lifecycle.js.map +1 -1
- package/dist/resolution/resolution-path.d.ts +84 -17
- package/dist/resolution/resolution-path.d.ts.map +1 -1
- package/dist/resolution/resolution-path.js +68 -23
- 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 +33 -10
- package/dist/resolution/resolver.d.ts.map +1 -1
- package/dist/resolution/resolver.js +471 -830
- package/dist/resolution/resolver.js.map +1 -1
- package/dist/resolution/scope.d.ts +11 -15
- package/dist/resolution/scope.d.ts.map +1 -1
- package/dist/resolution/scope.js +55 -43
- package/dist/resolution/scope.js.map +1 -1
- package/package.json +10 -106
- package/src/binding.ts +146 -24
- package/src/constructor-type.ts +4 -5
- package/src/container/binding-builders.ts +184 -284
- package/src/container/container.ts +161 -221
- package/src/decorators/inject.ts +3 -5
- package/src/errors.ts +38 -0
- package/src/index.ts +4 -1
- package/src/introspection/inspector.ts +3 -9
- package/src/metadata/metadata-keys.ts +3 -6
- package/src/registry.ts +90 -94
- package/src/resolution/activation-need.ts +85 -0
- package/src/resolution/binding-lookup-cache.ts +148 -0
- package/src/resolution/binding-scope.ts +6 -17
- package/src/resolution/binding-select.ts +15 -39
- package/src/resolution/class-introspector.ts +74 -0
- package/src/resolution/diagnostics.ts +43 -0
- package/src/resolution/environment.ts +181 -5
- package/src/resolution/instantiation-plan.ts +116 -64
- package/src/resolution/lifecycle.ts +69 -62
- package/src/resolution/resolution-path.ts +122 -43
- package/src/resolution/resolve-options.ts +51 -4
- package/src/resolution/resolver.ts +649 -1081
- package/src/resolution/scope.ts +58 -47
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cycle-detection bookkeeping shared by every resolution path.
|
|
3
|
-
*
|
|
4
|
-
* The resolution path is a plain string array for cheap push/pop; past
|
|
5
|
-
* RESOLUTION_SET_THRESHOLD entries an O(1) membership Set is attached to the
|
|
6
|
-
* array itself (symbol-keyed) so very deep graphs keep bounded cycle checks.
|
|
7
|
-
*/
|
|
1
|
+
/** Cycle-detection bookkeeping carried on the resolution path itself. */
|
|
8
2
|
import { CircularDependencyError } from "#/errors";
|
|
3
|
+
import type { ResolutionFrame } from "#/types";
|
|
9
4
|
|
|
10
5
|
const RESOLUTION_SET_KEY: unique symbol = Symbol("di:resolution-set");
|
|
11
6
|
/**
|
|
12
7
|
* Where the cycle check switches from a linear `Array.includes` scan to an attached Set.
|
|
13
8
|
*
|
|
14
|
-
* @
|
|
15
|
-
* 16 / 32 / 64 / 128): a threshold of 128 gives 1275 / 3641 / 9645 / 26082, of 32 gives
|
|
16
|
-
* 1202 / 3285 / 7735 / 16837, of 16 gives 1299 / 3694 / 7449 / 15625. Switching at 32 wins
|
|
17
|
-
* the shallow-to-mid depths real graphs actually have while staying close to the best deep
|
|
18
|
-
* numbers; the previous value of 128 was the worst of the three almost everywhere.
|
|
9
|
+
* @see `ARCHITECTURE.md` — the depth sweep this value comes from.
|
|
19
10
|
*
|
|
20
11
|
* @since 0.5.0-canary.7
|
|
21
12
|
*/
|
|
@@ -23,33 +14,19 @@ export const RESOLUTION_SET_THRESHOLD = 32;
|
|
|
23
14
|
type ResolutionPathWithSet = Array<string> & { [RESOLUTION_SET_KEY]?: Set<string> };
|
|
24
15
|
|
|
25
16
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
17
|
+
* Marks a token as in-flight on this path, throwing if it is already there.
|
|
18
|
+
*
|
|
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`.
|
|
29
23
|
*
|
|
30
|
-
* Callers unmark with `resolutionPath.pop()` + `set?.delete(name)` on unwind.
|
|
31
|
-
*/
|
|
32
|
-
export function enterResolutionPath(
|
|
33
|
-
resolutionPath: Array<string>,
|
|
34
|
-
tokenDisplayName: string,
|
|
35
|
-
forceSet: true,
|
|
36
|
-
): Set<string>;
|
|
37
|
-
export function enterResolutionPath(
|
|
38
|
-
resolutionPath: Array<string>,
|
|
39
|
-
tokenDisplayName: string,
|
|
40
|
-
forceSet: boolean,
|
|
41
|
-
): Set<string> | undefined;
|
|
42
|
-
/**
|
|
43
24
|
* @since 0.5.0-canary.7
|
|
44
25
|
*/
|
|
45
|
-
export function enterResolutionPath(
|
|
46
|
-
resolutionPath: Array<string>,
|
|
47
|
-
tokenDisplayName: string,
|
|
48
|
-
forceSet: boolean,
|
|
49
|
-
): Set<string> | undefined {
|
|
26
|
+
export function enterResolutionPath(resolutionPath: Array<string>, tokenDisplayName: string): Set<string> | undefined {
|
|
50
27
|
const pathWithSet = resolutionPath as ResolutionPathWithSet;
|
|
51
28
|
let resolutionSet = pathWithSet[RESOLUTION_SET_KEY];
|
|
52
|
-
if (resolutionSet === undefined &&
|
|
29
|
+
if (resolutionSet === undefined && resolutionPath.length >= RESOLUTION_SET_THRESHOLD) {
|
|
53
30
|
resolutionSet = new Set<string>(resolutionPath);
|
|
54
31
|
pathWithSet[RESOLUTION_SET_KEY] = resolutionSet;
|
|
55
32
|
}
|
|
@@ -61,17 +38,119 @@ export function enterResolutionPath(
|
|
|
61
38
|
return resolutionSet;
|
|
62
39
|
}
|
|
63
40
|
|
|
41
|
+
declare const BRANCH_BRAND: unique symbol;
|
|
42
|
+
|
|
64
43
|
/**
|
|
65
|
-
*
|
|
66
|
-
* sync. Callers that already hold the entry's name can pop and delete directly; this exists for
|
|
67
|
-
* unwind paths that only have the path array — notably the async chain's shared settle callback,
|
|
68
|
-
* which serves every level and therefore cannot capture a per-level name.
|
|
44
|
+
* A resolution path one async branch owns, so appending to it cannot disturb another branch.
|
|
69
45
|
*
|
|
70
|
-
* @
|
|
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
|
|
71
143
|
*/
|
|
72
|
-
export function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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;
|
|
76
152
|
}
|
|
153
|
+
const branch = resolutionStack.slice(0, branchDepth === UNOWNED_BRANCH ? resolutionStack.length : branchDepth);
|
|
154
|
+
branch.push(frame);
|
|
155
|
+
return branch as OwnedBranchStack;
|
|
77
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
|
}
|