@codefast/di 0.5.0 → 0.6.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 +712 -0
- package/README.md +119 -30
- package/dist/ambient/active-container.d.ts +37 -0
- package/dist/ambient/active-container.d.ts.map +1 -0
- package/dist/ambient/active-container.js +41 -0
- package/dist/ambient/active-container.js.map +1 -0
- package/dist/container/binding-builders.d.ts +12 -8
- package/dist/container/binding-builders.d.ts.map +1 -1
- package/dist/container/binding-builders.js +62 -31
- package/dist/container/binding-builders.js.map +1 -1
- package/dist/container/container.d.ts +33 -19
- package/dist/container/container.d.ts.map +1 -1
- package/dist/container/container.js +281 -118
- package/dist/container/container.js.map +1 -1
- package/dist/{resolution → core}/binding-scope.d.ts +2 -2
- package/dist/core/binding-scope.d.ts.map +1 -0
- package/dist/core/binding-scope.js.map +1 -0
- package/dist/{binding.d.ts → core/binding.d.ts} +70 -15
- package/dist/core/binding.d.ts.map +1 -0
- package/dist/{binding.js → core/binding.js} +27 -12
- package/dist/core/binding.js.map +1 -0
- package/dist/core/constraint-requirement.d.ts +53 -0
- package/dist/core/constraint-requirement.d.ts.map +1 -0
- package/dist/core/constraint-requirement.js +62 -0
- package/dist/core/constraint-requirement.js.map +1 -0
- package/dist/{constructor-type.d.ts → core/constructor-type.d.ts} +1 -1
- package/dist/core/constructor-type.d.ts.map +1 -0
- package/dist/core/constructor-type.js.map +1 -0
- package/dist/{module.d.ts → core/module.d.ts} +5 -5
- package/dist/core/module.d.ts.map +1 -0
- package/dist/{module.js → core/module.js} +4 -4
- package/dist/core/module.js.map +1 -0
- package/dist/{registry.d.ts → core/registry.d.ts} +27 -7
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/{registry.js → core/registry.js} +125 -52
- package/dist/core/registry.js.map +1 -0
- package/dist/core/tag.d.ts +80 -0
- package/dist/core/tag.d.ts.map +1 -0
- package/dist/core/tag.js +96 -0
- package/dist/core/tag.js.map +1 -0
- package/dist/{token.d.ts → core/token.d.ts} +2 -6
- package/dist/core/token.d.ts.map +1 -0
- package/dist/core/token.js +13 -0
- package/dist/core/token.js.map +1 -0
- package/dist/{types.d.ts → core/types.d.ts} +27 -21
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js.map +1 -0
- package/dist/decorators/inject.d.ts +5 -49
- package/dist/decorators/inject.d.ts.map +1 -1
- package/dist/decorators/inject.js +34 -104
- package/dist/decorators/inject.js.map +1 -1
- package/dist/decorators/injectable.d.ts +25 -5
- package/dist/decorators/injectable.d.ts.map +1 -1
- package/dist/decorators/injectable.js +2 -3
- package/dist/decorators/injectable.js.map +1 -1
- package/dist/decorators/lifecycle-decorators.d.ts +4 -2
- package/dist/decorators/lifecycle-decorators.d.ts.map +1 -1
- package/dist/decorators/lifecycle-decorators.js +19 -25
- package/dist/decorators/lifecycle-decorators.js.map +1 -1
- package/dist/{resolution → errors}/diagnostics.d.ts +2 -0
- package/dist/errors/diagnostics.d.ts.map +1 -0
- package/dist/errors/diagnostics.js.map +1 -0
- package/dist/{errors.d.ts → errors/errors.d.ts} +89 -4
- package/dist/errors/errors.d.ts.map +1 -0
- package/dist/{errors.js → errors/errors.js} +129 -7
- package/dist/errors/errors.js.map +1 -0
- package/dist/index.d.ts +21 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/injection/descriptor.d.ts +79 -0
- package/dist/injection/descriptor.d.ts.map +1 -0
- package/dist/injection/descriptor.js +118 -0
- package/dist/injection/descriptor.js.map +1 -0
- package/dist/{resolution → injection}/resolve-options.d.ts +21 -11
- package/dist/injection/resolve-options.d.ts.map +1 -0
- package/dist/injection/resolve-options.js +101 -0
- package/dist/injection/resolve-options.js.map +1 -0
- package/dist/introspection/dependency-graph.d.ts +16 -5
- package/dist/introspection/dependency-graph.d.ts.map +1 -1
- package/dist/introspection/dependency-graph.js +138 -57
- package/dist/introspection/dependency-graph.js.map +1 -1
- package/dist/introspection/graph-adapters/cytoscape.d.ts +6 -3
- package/dist/introspection/graph-adapters/cytoscape.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/cytoscape.js +3 -0
- package/dist/introspection/graph-adapters/cytoscape.js.map +1 -1
- package/dist/introspection/graph-adapters/dot.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/dot.js +12 -5
- package/dist/introspection/graph-adapters/dot.js.map +1 -1
- package/dist/introspection/graph-adapters/mermaid.d.ts +9 -0
- package/dist/introspection/graph-adapters/mermaid.d.ts.map +1 -0
- package/dist/introspection/graph-adapters/mermaid.js +47 -0
- package/dist/introspection/graph-adapters/mermaid.js.map +1 -0
- package/dist/introspection/graph-adapters/reactflow.d.ts +6 -3
- package/dist/introspection/graph-adapters/reactflow.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/reactflow.js +5 -1
- package/dist/introspection/graph-adapters/reactflow.js.map +1 -1
- package/dist/introspection/inspector.d.ts +4 -4
- package/dist/introspection/inspector.d.ts.map +1 -1
- package/dist/introspection/inspector.js +10 -6
- package/dist/introspection/inspector.js.map +1 -1
- package/dist/lifecycle/lifecycle-manager.d.ts +27 -0
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
- package/dist/{resolution/lifecycle.js → lifecycle/lifecycle-manager.js} +39 -9
- package/dist/lifecycle/lifecycle-manager.js.map +1 -0
- package/dist/lifecycle/scope-manager.d.ts +48 -0
- package/dist/lifecycle/scope-manager.d.ts.map +1 -0
- package/dist/lifecycle/scope-manager.js +135 -0
- package/dist/lifecycle/scope-manager.js.map +1 -0
- package/dist/metadata/metadata-reader-token.d.ts +1 -1
- package/dist/metadata/metadata-reader-token.d.ts.map +1 -1
- package/dist/metadata/metadata-reader-token.js +1 -1
- package/dist/metadata/metadata-reader-token.js.map +1 -1
- package/dist/metadata/metadata-types.d.ts +9 -9
- package/dist/metadata/metadata-types.d.ts.map +1 -1
- package/dist/metadata/symbol-metadata-reader.d.ts +19 -6
- package/dist/metadata/symbol-metadata-reader.d.ts.map +1 -1
- package/dist/metadata/symbol-metadata-reader.js +89 -9
- package/dist/metadata/symbol-metadata-reader.js.map +1 -1
- package/dist/metadata/verifying-metadata-reader.d.ts +16 -0
- package/dist/metadata/verifying-metadata-reader.d.ts.map +1 -0
- package/dist/metadata/verifying-metadata-reader.js +32 -0
- package/dist/metadata/verifying-metadata-reader.js.map +1 -0
- package/dist/resolution/{activation-need.d.ts → cache/activation-need.d.ts} +5 -5
- package/dist/resolution/cache/activation-need.d.ts.map +1 -0
- package/dist/resolution/cache/activation-need.js.map +1 -0
- package/dist/resolution/{binding-lookup-cache.d.ts → cache/binding-lookup-cache.d.ts} +9 -5
- package/dist/resolution/cache/binding-lookup-cache.d.ts.map +1 -0
- package/dist/resolution/{binding-lookup-cache.js → cache/binding-lookup-cache.js} +55 -0
- package/dist/resolution/cache/binding-lookup-cache.js.map +1 -0
- package/dist/resolution/cache/class-introspector.d.ts +66 -0
- package/dist/resolution/cache/class-introspector.d.ts.map +1 -0
- package/dist/resolution/cache/class-introspector.js +194 -0
- package/dist/resolution/cache/class-introspector.js.map +1 -0
- package/dist/resolution/context.d.ts +84 -0
- package/dist/resolution/context.d.ts.map +1 -0
- package/dist/resolution/{environment.js → context.js} +54 -80
- package/dist/resolution/context.js.map +1 -0
- package/dist/resolution/path/resolution-path.d.ts +94 -0
- package/dist/resolution/path/resolution-path.d.ts.map +1 -0
- package/dist/resolution/path/resolution-path.js +117 -0
- package/dist/resolution/path/resolution-path.js.map +1 -0
- package/dist/resolution/{instantiation-plan.d.ts → plan/instantiation-plan.d.ts} +37 -7
- package/dist/resolution/plan/instantiation-plan.d.ts.map +1 -0
- package/dist/resolution/plan/instantiation-plan.js +419 -0
- package/dist/resolution/plan/instantiation-plan.js.map +1 -0
- package/dist/resolution/resolver.d.ts +28 -28
- package/dist/resolution/resolver.d.ts.map +1 -1
- package/dist/resolution/resolver.js +581 -239
- package/dist/resolution/resolver.js.map +1 -1
- package/dist/resolution/select/binding-select.d.ts +34 -0
- package/dist/resolution/select/binding-select.d.ts.map +1 -0
- package/dist/resolution/select/binding-select.js +175 -0
- package/dist/resolution/select/binding-select.js.map +1 -0
- package/dist/resolution/{constraints.d.ts → select/constraints.d.ts} +13 -12
- package/dist/resolution/select/constraints.d.ts.map +1 -0
- package/dist/resolution/{constraints.js → select/constraints.js} +33 -22
- package/dist/resolution/select/constraints.js.map +1 -0
- package/package.json +160 -20
- package/src/ambient/active-container.ts +65 -0
- package/src/container/binding-builders.ts +79 -44
- package/src/container/container.ts +357 -170
- package/src/{resolution → core}/binding-scope.ts +2 -2
- package/src/{binding.ts → core/binding.ts} +101 -39
- package/src/core/constraint-requirement.ts +94 -0
- package/src/{constructor-type.ts → core/constructor-type.ts} +1 -1
- package/src/{module.ts → core/module.ts} +10 -10
- package/src/core/registry.ts +365 -0
- package/src/core/tag.ts +158 -0
- package/src/{token.ts → core/token.ts} +2 -14
- package/src/{types.ts → core/types.ts} +37 -34
- package/src/decorators/inject.ts +39 -175
- package/src/decorators/injectable.ts +40 -10
- package/src/decorators/lifecycle-decorators.ts +22 -34
- package/src/{resolution → errors}/diagnostics.ts +2 -0
- package/src/{errors.ts → errors/errors.ts} +152 -8
- package/src/index.ts +43 -15
- package/src/injection/descriptor.ts +206 -0
- package/src/injection/resolve-options.ts +144 -0
- package/src/introspection/dependency-graph.ts +223 -63
- package/src/introspection/graph-adapters/cytoscape.ts +9 -3
- package/src/introspection/graph-adapters/dot.ts +13 -5
- package/src/introspection/graph-adapters/mermaid.ts +61 -0
- package/src/introspection/graph-adapters/reactflow.ts +11 -4
- package/src/introspection/inspector.ts +16 -12
- package/src/{resolution/lifecycle.ts → lifecycle/lifecycle-manager.ts} +68 -32
- package/src/lifecycle/scope-manager.ts +156 -0
- package/src/metadata/metadata-reader-token.ts +2 -2
- package/src/metadata/metadata-types.ts +9 -9
- package/src/metadata/symbol-metadata-reader.ts +95 -18
- package/src/metadata/verifying-metadata-reader.ts +41 -0
- package/src/resolution/{activation-need.ts → cache/activation-need.ts} +8 -8
- package/src/resolution/{binding-lookup-cache.ts → cache/binding-lookup-cache.ts} +63 -5
- package/src/resolution/cache/class-introspector.ts +251 -0
- package/src/resolution/{environment.ts → context.ts} +84 -145
- package/src/resolution/path/resolution-path.ts +167 -0
- package/src/resolution/plan/instantiation-plan.ts +615 -0
- package/src/resolution/resolver.ts +663 -313
- package/src/resolution/select/binding-select.ts +210 -0
- package/src/resolution/select/constraints.ts +130 -0
- package/dist/binding.d.ts.map +0 -1
- package/dist/binding.js.map +0 -1
- package/dist/constructor-type.d.ts.map +0 -1
- package/dist/constructor-type.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/module.d.ts.map +0 -1
- package/dist/module.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/resolution/activation-need.d.ts.map +0 -1
- package/dist/resolution/activation-need.js.map +0 -1
- package/dist/resolution/binding-lookup-cache.d.ts.map +0 -1
- package/dist/resolution/binding-lookup-cache.js.map +0 -1
- package/dist/resolution/binding-scope.d.ts.map +0 -1
- package/dist/resolution/binding-scope.js.map +0 -1
- package/dist/resolution/binding-select.d.ts +0 -23
- package/dist/resolution/binding-select.d.ts.map +0 -1
- package/dist/resolution/binding-select.js +0 -121
- package/dist/resolution/binding-select.js.map +0 -1
- package/dist/resolution/class-introspector.d.ts +0 -27
- package/dist/resolution/class-introspector.d.ts.map +0 -1
- package/dist/resolution/class-introspector.js +0 -60
- package/dist/resolution/class-introspector.js.map +0 -1
- package/dist/resolution/constraints.d.ts.map +0 -1
- package/dist/resolution/constraints.js.map +0 -1
- package/dist/resolution/diagnostics.d.ts.map +0 -1
- package/dist/resolution/diagnostics.js.map +0 -1
- package/dist/resolution/environment.d.ts +0 -94
- package/dist/resolution/environment.d.ts.map +0 -1
- package/dist/resolution/environment.js.map +0 -1
- package/dist/resolution/instantiation-plan.d.ts.map +0 -1
- package/dist/resolution/instantiation-plan.js +0 -183
- package/dist/resolution/instantiation-plan.js.map +0 -1
- package/dist/resolution/lifecycle.d.ts +0 -23
- package/dist/resolution/lifecycle.d.ts.map +0 -1
- package/dist/resolution/lifecycle.js.map +0 -1
- package/dist/resolution/resolution-path.d.ts +0 -98
- package/dist/resolution/resolution-path.d.ts.map +0 -1
- package/dist/resolution/resolution-path.js +0 -98
- package/dist/resolution/resolution-path.js.map +0 -1
- package/dist/resolution/resolve-options.d.ts.map +0 -1
- package/dist/resolution/resolve-options.js +0 -56
- package/dist/resolution/resolve-options.js.map +0 -1
- package/dist/resolution/scope.d.ts +0 -28
- package/dist/resolution/scope.d.ts.map +0 -1
- package/dist/resolution/scope.js +0 -88
- package/dist/resolution/scope.js.map +0 -1
- package/dist/token.d.ts.map +0 -1
- package/dist/token.js +0 -22
- package/dist/token.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/src/registry.ts +0 -282
- package/src/resolution/binding-select.ts +0 -154
- package/src/resolution/class-introspector.ts +0 -74
- package/src/resolution/constraints.ts +0 -121
- package/src/resolution/instantiation-plan.ts +0 -292
- package/src/resolution/resolution-path.ts +0 -156
- package/src/resolution/resolve-options.ts +0 -89
- package/src/resolution/scope.ts +0 -105
- /package/dist/{resolution → core}/binding-scope.js +0 -0
- /package/dist/{constructor-type.js → core/constructor-type.js} +0 -0
- /package/dist/{types.js → core/types.js} +0 -0
- /package/dist/{resolution → errors}/diagnostics.js +0 -0
- /package/dist/resolution/{activation-need.js → cache/activation-need.js} +0 -0
|
@@ -1,39 +1,43 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
1
|
+
import type { BindingTag, TagKeyMask } from "#/core/tag";
|
|
2
|
+
import { NO_TAG_KEYS } from "#/core/tag";
|
|
3
|
+
import type { Token } from "#/core/token";
|
|
3
4
|
import type {
|
|
4
5
|
ActivationHandler,
|
|
5
6
|
BindingIdentifier,
|
|
6
7
|
BindingScope,
|
|
7
|
-
BindingTag,
|
|
8
8
|
Constructor,
|
|
9
9
|
DeactivationHandler,
|
|
10
10
|
ResolutionContext,
|
|
11
11
|
ResolutionFrame,
|
|
12
|
-
|
|
13
|
-
} from "#/types";
|
|
12
|
+
BindingConstraint,
|
|
13
|
+
} from "#/core/types";
|
|
14
|
+
import type { InjectableDependency, InjectionDescriptor, ResolvedDependencyValue } from "#/injection/descriptor";
|
|
14
15
|
|
|
15
|
-
// ── BindingSlot
|
|
16
|
+
// ── BindingSlot ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
16
17
|
|
|
17
18
|
/**
|
|
19
|
+
* The name-plus-tags coordinate a binding registers under and a request matches against.
|
|
20
|
+
*
|
|
18
21
|
* @since 0.3.16-canary.0
|
|
19
22
|
*/
|
|
20
23
|
export interface BindingSlot {
|
|
21
24
|
readonly name: string | undefined;
|
|
22
25
|
readonly tags: ReadonlyArray<BindingTag>;
|
|
26
|
+
/** OR of this slot's tag keys, so the subset test is one word compare. */
|
|
27
|
+
readonly keyMask: TagKeyMask;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
/**
|
|
31
|
+
* Returns whether two slots carry the same name and tag set, in any tag order.
|
|
32
|
+
*
|
|
26
33
|
* @since 0.3.16-canary.0
|
|
27
34
|
*/
|
|
28
35
|
export function bindingSlotEquals(left: BindingSlot, right: BindingSlot): boolean {
|
|
29
|
-
if (left.name !== right.name) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
if (left.tags.length !== right.tags.length) {
|
|
36
|
+
if (left.name !== right.name || left.keyMask !== right.keyMask || left.tags.length !== right.tags.length) {
|
|
33
37
|
return false;
|
|
34
38
|
}
|
|
35
|
-
for (const
|
|
36
|
-
if (!right.tags.
|
|
39
|
+
for (const criterion of left.tags) {
|
|
40
|
+
if (!right.tags.includes(criterion)) {
|
|
37
41
|
return false;
|
|
38
42
|
}
|
|
39
43
|
}
|
|
@@ -48,11 +52,19 @@ export function bindingSlotEquals(left: BindingSlot, right: BindingSlot): boolea
|
|
|
48
52
|
export const NO_INSTANCE: unique symbol = Symbol("di:no-instance");
|
|
49
53
|
|
|
50
54
|
/**
|
|
55
|
+
* The slot every unconstrained binding shares.
|
|
56
|
+
*
|
|
57
|
+
* @remarks Tags are frozen where they are built (here and in the builder's re-tag), never per
|
|
58
|
+
* binding: frames and snapshots alias the array, so a caller's write throws instead of corrupting
|
|
59
|
+
* the registry.
|
|
60
|
+
*
|
|
51
61
|
* @since 0.3.16-canary.0
|
|
52
62
|
*/
|
|
53
|
-
export const DEFAULT_BINDING_SLOT = { name: undefined, tags: []
|
|
63
|
+
export const DEFAULT_BINDING_SLOT: BindingSlot = { name: undefined, tags: Object.freeze([]), keyMask: NO_TAG_KEYS };
|
|
54
64
|
|
|
55
65
|
/**
|
|
66
|
+
* Formats a slot for diagnostics — `default`, or its `name:`/`tag:` parts.
|
|
67
|
+
*
|
|
56
68
|
* @since 0.3.16-canary.0
|
|
57
69
|
*/
|
|
58
70
|
export function bindingSlotToString(slot: BindingSlot): string {
|
|
@@ -63,13 +75,13 @@ export function bindingSlotToString(slot: BindingSlot): string {
|
|
|
63
75
|
if (slot.name !== undefined) {
|
|
64
76
|
parts.push(`name:${slot.name}`);
|
|
65
77
|
}
|
|
66
|
-
for (const
|
|
67
|
-
parts.push(`tag:${
|
|
78
|
+
for (const criterion of slot.tags) {
|
|
79
|
+
parts.push(`tag:${criterion.key.name}=${String(criterion.value)}`);
|
|
68
80
|
}
|
|
69
81
|
return parts.join(",");
|
|
70
82
|
}
|
|
71
83
|
|
|
72
|
-
// ── BindingBase
|
|
84
|
+
// ── BindingBase ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
73
85
|
|
|
74
86
|
interface BindingBase<Value> {
|
|
75
87
|
readonly id: BindingIdentifier;
|
|
@@ -89,17 +101,17 @@ interface BindingBase<Value> {
|
|
|
89
101
|
*
|
|
90
102
|
* @remarks Resolver-owned bookkeeping — `registry.add` normalizes it, so callers never set it.
|
|
91
103
|
*/
|
|
92
|
-
frame
|
|
104
|
+
frame: ResolutionFrame | undefined;
|
|
93
105
|
/**
|
|
94
106
|
* Cached singleton instance, or {@link NO_INSTANCE}.
|
|
95
107
|
*
|
|
96
108
|
* @remarks A binding belongs to exactly one container, so its singleton slot is per-binding —
|
|
97
109
|
* a field read replaces a keyed lookup on the hottest resolve shape there is.
|
|
98
110
|
*/
|
|
99
|
-
instance
|
|
111
|
+
instance: unknown;
|
|
100
112
|
readonly token: Token<Value> | Constructor<Value>;
|
|
101
113
|
readonly slot: BindingSlot;
|
|
102
|
-
readonly predicate?:
|
|
114
|
+
readonly predicate?: BindingConstraint | undefined;
|
|
103
115
|
}
|
|
104
116
|
|
|
105
117
|
type BindingBaseKeys = keyof BindingBase<unknown>;
|
|
@@ -118,9 +130,11 @@ interface BindingLifecycleHooks<Value> {
|
|
|
118
130
|
onDeactivation?(instance: Value): void | Promise<void>;
|
|
119
131
|
}
|
|
120
132
|
|
|
121
|
-
// ── Binding kinds
|
|
133
|
+
// ── Binding kinds ────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
122
134
|
|
|
123
135
|
/**
|
|
136
|
+
* A binding that instantiates a constructor.
|
|
137
|
+
*
|
|
124
138
|
* @since 0.3.16-canary.0
|
|
125
139
|
*/
|
|
126
140
|
export interface ClassBinding<Value> extends BindingBase<Value>, BindingLifecycleHooks<Value> {
|
|
@@ -130,6 +144,8 @@ export interface ClassBinding<Value> extends BindingBase<Value>, BindingLifecycl
|
|
|
130
144
|
}
|
|
131
145
|
|
|
132
146
|
/**
|
|
147
|
+
* A binding that computes its value with a synchronous factory.
|
|
148
|
+
*
|
|
133
149
|
* @since 0.3.16-canary.0
|
|
134
150
|
*/
|
|
135
151
|
export interface DynamicBinding<Value> extends BindingBase<Value>, BindingLifecycleHooks<Value> {
|
|
@@ -139,6 +155,8 @@ export interface DynamicBinding<Value> extends BindingBase<Value>, BindingLifecy
|
|
|
139
155
|
}
|
|
140
156
|
|
|
141
157
|
/**
|
|
158
|
+
* A binding whose factory returns a promise of the value.
|
|
159
|
+
*
|
|
142
160
|
* @since 0.3.16-canary.0
|
|
143
161
|
*/
|
|
144
162
|
export interface DynamicAsyncBinding<Value> extends BindingBase<Value>, BindingLifecycleHooks<Value> {
|
|
@@ -148,6 +166,8 @@ export interface DynamicAsyncBinding<Value> extends BindingBase<Value>, BindingL
|
|
|
148
166
|
}
|
|
149
167
|
|
|
150
168
|
/**
|
|
169
|
+
* A binding whose factory is called with its declared dependencies already resolved.
|
|
170
|
+
*
|
|
151
171
|
* @since 0.3.16-canary.0
|
|
152
172
|
*/
|
|
153
173
|
export interface ResolvedBinding<Value> extends BindingBase<Value>, BindingLifecycleHooks<Value> {
|
|
@@ -158,6 +178,8 @@ export interface ResolvedBinding<Value> extends BindingBase<Value>, BindingLifec
|
|
|
158
178
|
}
|
|
159
179
|
|
|
160
180
|
/**
|
|
181
|
+
* The async form of {@link ResolvedBinding} — the factory returns a promise.
|
|
182
|
+
*
|
|
161
183
|
* @since 0.3.16-canary.0
|
|
162
184
|
*/
|
|
163
185
|
export interface ResolvedAsyncBinding<Value> extends BindingBase<Value>, BindingLifecycleHooks<Value> {
|
|
@@ -168,6 +190,8 @@ export interface ResolvedAsyncBinding<Value> extends BindingBase<Value>, Binding
|
|
|
168
190
|
}
|
|
169
191
|
|
|
170
192
|
/**
|
|
193
|
+
* A binding that hands out one fixed value, so its scope is always `singleton`.
|
|
194
|
+
*
|
|
171
195
|
* @since 0.3.16-canary.0
|
|
172
196
|
*/
|
|
173
197
|
export interface ConstantBinding<Value> extends BindingBase<Value>, BindingLifecycleHooks<Value> {
|
|
@@ -177,6 +201,8 @@ export interface ConstantBinding<Value> extends BindingBase<Value>, BindingLifec
|
|
|
177
201
|
}
|
|
178
202
|
|
|
179
203
|
/**
|
|
204
|
+
* A binding that defers to whatever binding its target token selects.
|
|
205
|
+
*
|
|
180
206
|
* @since 0.3.16-canary.0
|
|
181
207
|
*/
|
|
182
208
|
export interface AliasBinding<Value> extends BindingBase<Value> {
|
|
@@ -192,6 +218,8 @@ export interface AliasBinding<Value> extends BindingBase<Value> {
|
|
|
192
218
|
}
|
|
193
219
|
|
|
194
220
|
/**
|
|
221
|
+
* Every binding shape the engine resolves, discriminated by `kind`.
|
|
222
|
+
*
|
|
195
223
|
* @since 0.3.16-canary.0
|
|
196
224
|
*/
|
|
197
225
|
export type Binding<Value = unknown> =
|
|
@@ -203,35 +231,38 @@ export type Binding<Value = unknown> =
|
|
|
203
231
|
| ConstantBinding<Value>
|
|
204
232
|
| AliasBinding<Value>;
|
|
205
233
|
|
|
234
|
+
/** `Omit` applied per union member, since a bare `Omit` would collapse the union into one shape. */
|
|
235
|
+
type DistributiveOmit<Union, Keys extends PropertyKey> = Union extends unknown ? Omit<Union, Keys> : never;
|
|
236
|
+
|
|
206
237
|
/**
|
|
207
238
|
* Builder-only payload before `id`, `token`, `slot`, and `predicate` are applied.
|
|
208
239
|
*
|
|
240
|
+
* @remarks Derived rather than listed: a new binding kind joins this the moment it joins
|
|
241
|
+
* {@link Binding}, so the two unions cannot diverge.
|
|
242
|
+
*
|
|
209
243
|
* @since 0.3.16-canary.0
|
|
210
244
|
*/
|
|
211
|
-
export type PartialBinding<Value> =
|
|
212
|
-
| Omit<ClassBinding<Value>, BindingBaseKeys>
|
|
213
|
-
| Omit<DynamicBinding<Value>, BindingBaseKeys>
|
|
214
|
-
| Omit<DynamicAsyncBinding<Value>, BindingBaseKeys>
|
|
215
|
-
| Omit<ResolvedBinding<Value>, BindingBaseKeys>
|
|
216
|
-
| Omit<ResolvedAsyncBinding<Value>, BindingBaseKeys>
|
|
217
|
-
| Omit<ConstantBinding<Value>, BindingBaseKeys>
|
|
218
|
-
| Omit<AliasBinding<Value>, BindingBaseKeys>;
|
|
245
|
+
export type PartialBinding<Value> = DistributiveOmit<Binding<Value>, BindingBaseKeys>;
|
|
219
246
|
|
|
220
|
-
// ── ID generation
|
|
247
|
+
// ── ID generation ────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
221
248
|
|
|
222
249
|
let bindingIdCounter = 0;
|
|
223
250
|
/**
|
|
251
|
+
* Returns a process-unique identifier for a new binding.
|
|
252
|
+
*
|
|
224
253
|
* @since 0.3.16-canary.0
|
|
225
254
|
*/
|
|
226
255
|
export function generateBindingId(): BindingIdentifier {
|
|
227
256
|
return String(++bindingIdCounter) as BindingIdentifier;
|
|
228
257
|
}
|
|
229
258
|
|
|
230
|
-
// ── Construction
|
|
259
|
+
// ── Construction ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
260
|
+
|
|
261
|
+
/** Every key any member declares — a bare `keyof` on a union gives only the shared ones. */
|
|
262
|
+
type KeysOfUnion<Union> = Union extends unknown ? keyof Union : never;
|
|
231
263
|
|
|
232
264
|
// Superset of every kind's fields, so one literal can copy any binding shape.
|
|
233
|
-
|
|
234
|
-
type BindingFieldName = Binding<unknown> extends infer Kind ? (Kind extends unknown ? keyof Kind : never) : never;
|
|
265
|
+
type BindingFieldName = KeysOfUnion<Binding<unknown>>;
|
|
235
266
|
|
|
236
267
|
/**
|
|
237
268
|
* Completeness guard for {@link createBinding}'s literal.
|
|
@@ -256,9 +287,13 @@ type BindingFieldSuperset = {
|
|
|
256
287
|
/**
|
|
257
288
|
* The single construction site for bindings — one literal, one V8 hidden class.
|
|
258
289
|
*
|
|
259
|
-
* @
|
|
290
|
+
* @remarks Field order is fixed and this is the only construction site, so every binding shares one
|
|
291
|
+
* hidden class. Reordering the fields, or adding a second site, gives that up.
|
|
260
292
|
*
|
|
261
293
|
* @param source - the kind-specific payload, or an existing binding to re-slot
|
|
294
|
+
* @param token - the key requests resolve the binding by
|
|
295
|
+
* @param slot - the name + tags a request must match to select this binding
|
|
296
|
+
* @param predicate - a custom constraint, or `undefined` for none
|
|
262
297
|
* @param id - reuse a caller's id to keep a fluent chain's `id()` stable across refinements
|
|
263
298
|
*
|
|
264
299
|
* @since 0.5.0-canary.8
|
|
@@ -267,7 +302,7 @@ export function createBinding<Value>(
|
|
|
267
302
|
source: PartialBinding<Value> | Binding<Value>,
|
|
268
303
|
token: Token<Value> | Constructor<Value>,
|
|
269
304
|
slot: BindingSlot,
|
|
270
|
-
predicate:
|
|
305
|
+
predicate: BindingConstraint | undefined,
|
|
271
306
|
id: BindingIdentifier = generateBindingId(),
|
|
272
307
|
): Binding<Value> {
|
|
273
308
|
const fields = source as BindingFieldSuperset;
|
|
@@ -276,7 +311,8 @@ export function createBinding<Value>(
|
|
|
276
311
|
id,
|
|
277
312
|
inFlight: false,
|
|
278
313
|
frame: undefined,
|
|
279
|
-
|
|
314
|
+
// An `in` probe, not `??`: a re-slotted singleton may legitimately hold a cached `undefined`.
|
|
315
|
+
instance: "instance" in fields ? fields.instance : NO_INSTANCE,
|
|
280
316
|
token,
|
|
281
317
|
slot,
|
|
282
318
|
predicate,
|
|
@@ -314,6 +350,16 @@ export function refinableFields<Value>(binding: Binding<Value>): RefinableBindin
|
|
|
314
350
|
return binding as RefinableBindingFields<Value>;
|
|
315
351
|
}
|
|
316
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Writable view of the memoized frame, which is a cache rather than part of a binding's identity.
|
|
355
|
+
*
|
|
356
|
+
* @remarks Named for the same reason as {@link RefinableBindingFields}: a write view stated once
|
|
357
|
+
* cannot drift from `Binding`, where an inline cast at each site can.
|
|
358
|
+
*/
|
|
359
|
+
interface MemoizedFrameField {
|
|
360
|
+
frame: ResolutionFrame | undefined;
|
|
361
|
+
}
|
|
362
|
+
|
|
317
363
|
/**
|
|
318
364
|
* Drops the memoized resolution frame, for a refinement that changes what the frame reports.
|
|
319
365
|
*
|
|
@@ -323,10 +369,10 @@ export function refinableFields<Value>(binding: Binding<Value>): RefinableBindin
|
|
|
323
369
|
* @since 0.5.0-canary.9
|
|
324
370
|
*/
|
|
325
371
|
export function clearBindingFrame<Value>(binding: Binding<Value>): void {
|
|
326
|
-
(binding as
|
|
372
|
+
(binding as MemoizedFrameField).frame = undefined;
|
|
327
373
|
}
|
|
328
374
|
|
|
329
|
-
// ── Builder interfaces
|
|
375
|
+
// ── Builder interfaces ───────────────────────────────────────────────────────────────────────────────────────────────
|
|
330
376
|
|
|
331
377
|
/**
|
|
332
378
|
* Common slot-constraint + id methods shared by all concrete binding builders.
|
|
@@ -334,14 +380,16 @@ export function clearBindingFrame<Value>(binding: Binding<Value>): void {
|
|
|
334
380
|
* @since 0.3.16-canary.0
|
|
335
381
|
*/
|
|
336
382
|
export interface SlotConstrainedBuilder {
|
|
337
|
-
when(predicate:
|
|
383
|
+
when(predicate: BindingConstraint): this;
|
|
338
384
|
whenNamed(name: string): this;
|
|
339
|
-
whenTagged(
|
|
385
|
+
whenTagged(criterion: BindingTag): this;
|
|
340
386
|
whenDefault(): this;
|
|
341
387
|
id(): BindingIdentifier;
|
|
342
388
|
}
|
|
343
389
|
|
|
344
390
|
/**
|
|
391
|
+
* The `to*` step of the fluent chain, choosing what a token resolves to.
|
|
392
|
+
*
|
|
345
393
|
* @since 0.3.16-canary.0
|
|
346
394
|
*/
|
|
347
395
|
export interface BindToBuilder<Value> {
|
|
@@ -362,6 +410,8 @@ export interface BindToBuilder<Value> {
|
|
|
362
410
|
}
|
|
363
411
|
|
|
364
412
|
/**
|
|
413
|
+
* The scope-selection step of the fluent chain.
|
|
414
|
+
*
|
|
365
415
|
* @since 0.3.16-canary.0
|
|
366
416
|
*/
|
|
367
417
|
export interface BindingBuilder<Value> extends SlotConstrainedBuilder {
|
|
@@ -371,6 +421,8 @@ export interface BindingBuilder<Value> extends SlotConstrainedBuilder {
|
|
|
371
421
|
}
|
|
372
422
|
|
|
373
423
|
/**
|
|
424
|
+
* The fluent chain for a constant — lifecycle hooks only, since the scope is fixed.
|
|
425
|
+
*
|
|
374
426
|
* @since 0.3.16-canary.0
|
|
375
427
|
*/
|
|
376
428
|
export interface ConstantBindingBuilder<Value> extends SlotConstrainedBuilder {
|
|
@@ -379,11 +431,15 @@ export interface ConstantBindingBuilder<Value> extends SlotConstrainedBuilder {
|
|
|
379
431
|
}
|
|
380
432
|
|
|
381
433
|
/**
|
|
434
|
+
* The fluent chain for an alias — slot constraints only, since scoping belongs to the target.
|
|
435
|
+
*
|
|
382
436
|
* @since 0.3.16-canary.0
|
|
383
437
|
*/
|
|
384
438
|
export interface AliasBindingBuilder extends SlotConstrainedBuilder {}
|
|
385
439
|
|
|
386
440
|
/**
|
|
441
|
+
* The fluent chain after `singleton()`, where both lifecycle hooks stay available.
|
|
442
|
+
*
|
|
387
443
|
* @since 0.3.16-canary.0
|
|
388
444
|
*/
|
|
389
445
|
export interface SingletonBindingBuilder<Value> {
|
|
@@ -393,6 +449,8 @@ export interface SingletonBindingBuilder<Value> {
|
|
|
393
449
|
}
|
|
394
450
|
|
|
395
451
|
/**
|
|
452
|
+
* The fluent chain after `transient()`, where activation is the one lifecycle hook offered.
|
|
453
|
+
*
|
|
396
454
|
* @since 0.3.16-canary.0
|
|
397
455
|
*/
|
|
398
456
|
export interface TransientBindingBuilder<Value> {
|
|
@@ -401,11 +459,15 @@ export interface TransientBindingBuilder<Value> {
|
|
|
401
459
|
}
|
|
402
460
|
|
|
403
461
|
/**
|
|
462
|
+
* The fluent chain after `scoped()`, sharing the `transient()` surface.
|
|
463
|
+
*
|
|
404
464
|
* @since 0.3.16-canary.0
|
|
405
465
|
*/
|
|
406
466
|
export interface ScopedBindingBuilder<Value> extends TransientBindingBuilder<Value> {}
|
|
407
467
|
|
|
408
468
|
/**
|
|
469
|
+
* The fluent chain a constant enters once a lifecycle hook is added.
|
|
470
|
+
*
|
|
409
471
|
* @since 0.3.16-canary.0
|
|
410
472
|
*/
|
|
411
473
|
export interface SingletonLifecycleBuilder<Value> {
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/** What a constraint needs to exist before it can ever match, so `validate()` can check for it. */
|
|
2
|
+
import type { BindingConstraint } from "#/core/types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Key the requirement is attached under.
|
|
6
|
+
*
|
|
7
|
+
* @remarks A symbol on the predicate rather than a field in the binding: resolution never reads it,
|
|
8
|
+
* so carrying it costs a resolve nothing.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.6.0
|
|
11
|
+
*/
|
|
12
|
+
export const CONSTRAINT_REQUIREMENT: unique symbol = Symbol("di:constraint-requirement");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The slot name a constraint waits for on an ancestor.
|
|
16
|
+
*
|
|
17
|
+
* @remarks Only names are described. A tag criterion is interned, so a typo cannot produce one that
|
|
18
|
+
* looks valid, while a name is a bare string that nothing checks.
|
|
19
|
+
*
|
|
20
|
+
* @since 0.6.0
|
|
21
|
+
*/
|
|
22
|
+
export interface ConstraintRequirement {
|
|
23
|
+
readonly requires: "ancestorSlotName";
|
|
24
|
+
readonly name: string;
|
|
25
|
+
/** The helper that built the predicate, so a report can name what the caller wrote. */
|
|
26
|
+
readonly helperName: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Records what a predicate waits for. Called once, where the predicate is built.
|
|
31
|
+
*
|
|
32
|
+
* @since 0.6.0
|
|
33
|
+
*/
|
|
34
|
+
export function requiringAncestorSlotName(
|
|
35
|
+
predicate: BindingConstraint,
|
|
36
|
+
name: string,
|
|
37
|
+
helperName: string,
|
|
38
|
+
): BindingConstraint {
|
|
39
|
+
const requirement: ConstraintRequirement = { requires: "ancestorSlotName", name, helperName };
|
|
40
|
+
Object.defineProperty(predicate, CONSTRAINT_REQUIREMENT, { value: requirement, enumerable: false });
|
|
41
|
+
return predicate;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The requirement a predicate carries, if it was built by a helper that records one.
|
|
46
|
+
*
|
|
47
|
+
* @remarks A composed predicate may carry several; this answers the first. `validate()` reads
|
|
48
|
+
* {@link constraintRequirementsOf} so no recorded requirement is skipped.
|
|
49
|
+
*
|
|
50
|
+
* @since 0.6.0
|
|
51
|
+
*/
|
|
52
|
+
export function constraintRequirementOf(predicate: BindingConstraint): ConstraintRequirement | undefined {
|
|
53
|
+
return constraintRequirementsOf(predicate)[0];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const NO_REQUIREMENTS: ReadonlyArray<ConstraintRequirement> = [];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Every requirement a predicate carries — one from a helper, several from a composed chain.
|
|
60
|
+
*
|
|
61
|
+
* @since 0.6.1
|
|
62
|
+
*/
|
|
63
|
+
export function constraintRequirementsOf(predicate: BindingConstraint): ReadonlyArray<ConstraintRequirement> {
|
|
64
|
+
const payload = (
|
|
65
|
+
predicate as { [CONSTRAINT_REQUIREMENT]?: ConstraintRequirement | ReadonlyArray<ConstraintRequirement> }
|
|
66
|
+
)[CONSTRAINT_REQUIREMENT];
|
|
67
|
+
if (payload === undefined) {
|
|
68
|
+
return NO_REQUIREMENTS;
|
|
69
|
+
}
|
|
70
|
+
return Array.isArray(payload)
|
|
71
|
+
? (payload as ReadonlyArray<ConstraintRequirement>)
|
|
72
|
+
: [payload as ConstraintRequirement];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Carries both sides' requirements onto a composed predicate, so chaining does not lose them.
|
|
77
|
+
*
|
|
78
|
+
* @since 0.6.1
|
|
79
|
+
*/
|
|
80
|
+
export function mergingConstraintRequirements(
|
|
81
|
+
composite: BindingConstraint,
|
|
82
|
+
left: BindingConstraint,
|
|
83
|
+
right: BindingConstraint,
|
|
84
|
+
): BindingConstraint {
|
|
85
|
+
const merged = [...constraintRequirementsOf(left), ...constraintRequirementsOf(right)];
|
|
86
|
+
if (merged.length === 0) {
|
|
87
|
+
return composite;
|
|
88
|
+
}
|
|
89
|
+
Object.defineProperty(composite, CONSTRAINT_REQUIREMENT, {
|
|
90
|
+
value: merged.length === 1 ? merged[0] : merged,
|
|
91
|
+
enumerable: false,
|
|
92
|
+
});
|
|
93
|
+
return composite;
|
|
94
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @since 0.3.16-canary.0
|
|
8
8
|
*/
|
|
9
|
-
export type Constructor<Value = unknown> = new (...args: Array<never>) => Value;
|
|
9
|
+
export type Constructor<out Value = unknown> = new (...args: Array<never>) => Value;
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Class constructor as invoked by the resolver after metadata-driven
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { BindToBuilder } from "#/binding";
|
|
2
|
-
import type { Token } from "#/token";
|
|
3
|
-
import type { Constructor } from "#/types";
|
|
1
|
+
import type { BindToBuilder } from "#/core/binding";
|
|
2
|
+
import type { Token } from "#/core/token";
|
|
3
|
+
import type { Constructor } from "#/core/types";
|
|
4
4
|
|
|
5
|
-
// ── Branded types (runtime symbols for branding)
|
|
5
|
+
// ── Branded types (runtime symbols for branding) ─────────────────────────────────────────────────────────────────────
|
|
6
6
|
|
|
7
7
|
const SYNC_MODULE_BRAND: unique symbol = Symbol("di:sync-module");
|
|
8
8
|
const ASYNC_MODULE_BRAND: unique symbol = Symbol("di:async-module");
|
|
@@ -33,13 +33,13 @@ export interface AsyncModule {
|
|
|
33
33
|
readonly [MODULE_SETUP]: (builder: AsyncModuleBuilder) => Promise<void>;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// ── Builder interfaces
|
|
36
|
+
// ── Builder interfaces ───────────────────────────────────────────────────────────────────────────────────────────────
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* @since 0.3.16-canary.0
|
|
40
40
|
*/
|
|
41
41
|
export interface ModuleBuilder {
|
|
42
|
-
bind<
|
|
42
|
+
bind<Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
|
|
43
43
|
import(...modules: Array<SyncModule>): void;
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -47,11 +47,11 @@ export interface ModuleBuilder {
|
|
|
47
47
|
* @since 0.3.16-canary.0
|
|
48
48
|
*/
|
|
49
49
|
export interface AsyncModuleBuilder {
|
|
50
|
-
bind<
|
|
50
|
+
bind<Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
|
|
51
51
|
import(...modules: Array<SyncModule | AsyncModule>): void;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
// ── Static factories
|
|
54
|
+
// ── Static factories ─────────────────────────────────────────────────────────────────────────────────────────────────
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* @since 0.3.16-canary.0
|
|
@@ -79,7 +79,7 @@ export const AsyncModule = {
|
|
|
79
79
|
},
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
// ── Module — unified API
|
|
82
|
+
// ── Module — unified API ─────────────────────────────────────────────────────────────────────────────────────────────
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* @since 0.3.16-canary.0
|
|
@@ -97,5 +97,5 @@ export const Module = {
|
|
|
97
97
|
* @since 0.3.16-canary.0
|
|
98
98
|
*/
|
|
99
99
|
export function isSyncModule(module: SyncModule | AsyncModule): module is SyncModule {
|
|
100
|
-
return (module as SyncModule)[SYNC_MODULE_BRAND];
|
|
100
|
+
return (module as Partial<SyncModule>)[SYNC_MODULE_BRAND] === true;
|
|
101
101
|
}
|