@codefast/di 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +242 -159
- package/dist/ambient/active-container.d.ts +1 -2
- package/dist/ambient/active-container.js +1 -2
- package/dist/container/binding-builders.d.ts +1 -2
- package/dist/container/binding-builders.js +1 -2
- package/dist/container/container.d.ts +1 -2
- package/dist/container/container.js +1 -2
- package/dist/core/binding-scope.d.ts +1 -2
- package/dist/core/binding-scope.js +1 -2
- package/dist/core/binding.d.ts +1 -2
- package/dist/core/binding.js +1 -2
- package/dist/core/constraint-requirement.d.ts +1 -2
- package/dist/core/constraint-requirement.js +1 -2
- package/dist/core/constructor-type.d.ts +1 -2
- package/dist/core/constructor-type.js +1 -2
- package/dist/core/map-upsert.d.ts +1 -2
- package/dist/core/map-upsert.js +1 -2
- package/dist/core/module.d.ts +1 -2
- package/dist/core/module.js +1 -2
- package/dist/core/registry.d.ts +1 -2
- package/dist/core/registry.js +1 -2
- package/dist/core/tag.d.ts +1 -2
- package/dist/core/tag.js +1 -2
- package/dist/core/token.d.ts +1 -2
- package/dist/core/token.js +1 -2
- package/dist/core/types.d.ts +1 -2
- package/dist/core/types.js +1 -2
- package/dist/decorators/inject.d.ts +1 -2
- package/dist/decorators/inject.js +1 -2
- package/dist/decorators/injectable.d.ts +1 -2
- package/dist/decorators/injectable.js +1 -2
- package/dist/decorators/lifecycle-decorators.d.ts +1 -2
- package/dist/decorators/lifecycle-decorators.js +1 -2
- package/dist/errors/diagnostics.d.ts +1 -2
- package/dist/errors/diagnostics.js +1 -2
- package/dist/errors/errors.d.ts +1 -2
- package/dist/errors/errors.js +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/injection/descriptor.d.ts +1 -2
- package/dist/injection/descriptor.js +1 -2
- package/dist/injection/resolve-options.d.ts +1 -2
- package/dist/injection/resolve-options.js +1 -2
- package/dist/introspection/dependency-graph.d.ts +1 -2
- package/dist/introspection/dependency-graph.js +1 -2
- package/dist/introspection/graph-adapters/cytoscape.d.ts +1 -2
- package/dist/introspection/graph-adapters/cytoscape.js +1 -2
- package/dist/introspection/graph-adapters/dot.d.ts +1 -2
- package/dist/introspection/graph-adapters/dot.js +1 -2
- package/dist/introspection/graph-adapters/mermaid.d.ts +1 -2
- package/dist/introspection/graph-adapters/mermaid.js +1 -2
- package/dist/introspection/graph-adapters/reactflow.d.ts +1 -2
- package/dist/introspection/graph-adapters/reactflow.js +1 -2
- package/dist/introspection/inspector.d.ts +1 -2
- package/dist/introspection/inspector.js +1 -2
- package/dist/lifecycle/lifecycle-manager.d.ts +1 -2
- package/dist/lifecycle/lifecycle-manager.js +1 -2
- package/dist/lifecycle/scope-manager.d.ts +1 -2
- package/dist/lifecycle/scope-manager.js +1 -2
- package/dist/metadata/metadata-keys.d.ts +1 -2
- package/dist/metadata/metadata-keys.js +1 -2
- package/dist/metadata/metadata-reader-token.d.ts +1 -2
- package/dist/metadata/metadata-reader-token.js +1 -2
- package/dist/metadata/metadata-types.d.ts +1 -2
- package/dist/metadata/metadata-types.js +1 -2
- package/dist/metadata/symbol-metadata-reader.d.ts +1 -2
- package/dist/metadata/symbol-metadata-reader.js +1 -2
- package/dist/metadata/verifying-metadata-reader.d.ts +1 -2
- package/dist/metadata/verifying-metadata-reader.js +1 -2
- package/dist/resolution/cache/activation-need.d.ts +1 -2
- package/dist/resolution/cache/activation-need.js +1 -2
- package/dist/resolution/cache/binding-lookup-cache.d.ts +1 -2
- package/dist/resolution/cache/binding-lookup-cache.js +1 -2
- package/dist/resolution/cache/class-introspector.d.ts +1 -2
- package/dist/resolution/cache/class-introspector.js +1 -2
- package/dist/resolution/context.d.ts +1 -2
- package/dist/resolution/context.js +1 -2
- package/dist/resolution/path/resolution-path.d.ts +1 -2
- package/dist/resolution/path/resolution-path.js +1 -2
- package/dist/resolution/plan/instantiation-plan.d.ts +1 -2
- package/dist/resolution/plan/instantiation-plan.js +1 -2
- package/dist/resolution/resolver.d.ts +1 -2
- package/dist/resolution/resolver.js +1 -2
- package/dist/resolution/select/binding-select.d.ts +1 -2
- package/dist/resolution/select/binding-select.js +1 -2
- package/dist/resolution/select/constraints.d.ts +1 -2
- package/dist/resolution/select/constraints.js +1 -2
- package/package.json +6 -58
- package/dist/ambient/active-container.d.ts.map +0 -1
- package/dist/ambient/active-container.js.map +0 -1
- package/dist/container/binding-builders.d.ts.map +0 -1
- package/dist/container/binding-builders.js.map +0 -1
- package/dist/container/container.d.ts.map +0 -1
- package/dist/container/container.js.map +0 -1
- package/dist/core/binding-scope.d.ts.map +0 -1
- package/dist/core/binding-scope.js.map +0 -1
- package/dist/core/binding.d.ts.map +0 -1
- package/dist/core/binding.js.map +0 -1
- package/dist/core/constraint-requirement.d.ts.map +0 -1
- package/dist/core/constraint-requirement.js.map +0 -1
- package/dist/core/constructor-type.d.ts.map +0 -1
- package/dist/core/constructor-type.js.map +0 -1
- package/dist/core/map-upsert.d.ts.map +0 -1
- package/dist/core/map-upsert.js.map +0 -1
- package/dist/core/module.d.ts.map +0 -1
- package/dist/core/module.js.map +0 -1
- package/dist/core/registry.d.ts.map +0 -1
- package/dist/core/registry.js.map +0 -1
- package/dist/core/tag.d.ts.map +0 -1
- package/dist/core/tag.js.map +0 -1
- package/dist/core/token.d.ts.map +0 -1
- package/dist/core/token.js.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/decorators/inject.d.ts.map +0 -1
- package/dist/decorators/inject.js.map +0 -1
- package/dist/decorators/injectable.d.ts.map +0 -1
- package/dist/decorators/injectable.js.map +0 -1
- package/dist/decorators/lifecycle-decorators.d.ts.map +0 -1
- package/dist/decorators/lifecycle-decorators.js.map +0 -1
- package/dist/errors/diagnostics.d.ts.map +0 -1
- package/dist/errors/diagnostics.js.map +0 -1
- package/dist/errors/errors.d.ts.map +0 -1
- package/dist/errors/errors.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/injection/descriptor.d.ts.map +0 -1
- package/dist/injection/descriptor.js.map +0 -1
- package/dist/injection/resolve-options.d.ts.map +0 -1
- package/dist/injection/resolve-options.js.map +0 -1
- package/dist/introspection/dependency-graph.d.ts.map +0 -1
- package/dist/introspection/dependency-graph.js.map +0 -1
- package/dist/introspection/graph-adapters/cytoscape.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/cytoscape.js.map +0 -1
- package/dist/introspection/graph-adapters/dot.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/dot.js.map +0 -1
- package/dist/introspection/graph-adapters/mermaid.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/mermaid.js.map +0 -1
- package/dist/introspection/graph-adapters/reactflow.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/reactflow.js.map +0 -1
- package/dist/introspection/inspector.d.ts.map +0 -1
- package/dist/introspection/inspector.js.map +0 -1
- package/dist/lifecycle/lifecycle-manager.d.ts.map +0 -1
- package/dist/lifecycle/lifecycle-manager.js.map +0 -1
- package/dist/lifecycle/scope-manager.d.ts.map +0 -1
- package/dist/lifecycle/scope-manager.js.map +0 -1
- package/dist/metadata/metadata-keys.d.ts.map +0 -1
- package/dist/metadata/metadata-keys.js.map +0 -1
- package/dist/metadata/metadata-reader-token.d.ts.map +0 -1
- package/dist/metadata/metadata-reader-token.js.map +0 -1
- package/dist/metadata/metadata-types.d.ts.map +0 -1
- package/dist/metadata/metadata-types.js.map +0 -1
- package/dist/metadata/symbol-metadata-reader.d.ts.map +0 -1
- package/dist/metadata/symbol-metadata-reader.js.map +0 -1
- package/dist/metadata/verifying-metadata-reader.d.ts.map +0 -1
- package/dist/metadata/verifying-metadata-reader.js.map +0 -1
- package/dist/resolution/cache/activation-need.d.ts.map +0 -1
- package/dist/resolution/cache/activation-need.js.map +0 -1
- package/dist/resolution/cache/binding-lookup-cache.d.ts.map +0 -1
- package/dist/resolution/cache/binding-lookup-cache.js.map +0 -1
- package/dist/resolution/cache/class-introspector.d.ts.map +0 -1
- package/dist/resolution/cache/class-introspector.js.map +0 -1
- package/dist/resolution/context.d.ts.map +0 -1
- package/dist/resolution/context.js.map +0 -1
- package/dist/resolution/path/resolution-path.d.ts.map +0 -1
- package/dist/resolution/path/resolution-path.js.map +0 -1
- package/dist/resolution/plan/instantiation-plan.d.ts.map +0 -1
- package/dist/resolution/plan/instantiation-plan.js.map +0 -1
- package/dist/resolution/resolver.d.ts.map +0 -1
- package/dist/resolution/resolver.js.map +0 -1
- package/dist/resolution/select/binding-select.d.ts.map +0 -1
- package/dist/resolution/select/binding-select.js.map +0 -1
- package/dist/resolution/select/constraints.d.ts.map +0 -1
- package/dist/resolution/select/constraints.js.map +0 -1
- package/src/ambient/active-container.ts +0 -69
- package/src/container/binding-builders.ts +0 -337
- package/src/container/container.ts +0 -972
- package/src/core/binding-scope.ts +0 -15
- package/src/core/binding.ts +0 -505
- package/src/core/constraint-requirement.ts +0 -94
- package/src/core/constructor-type.ts +0 -18
- package/src/core/map-upsert.ts +0 -53
- package/src/core/module.ts +0 -117
- package/src/core/registry.ts +0 -319
- package/src/core/tag.ts +0 -212
- package/src/core/token.ts +0 -31
- package/src/core/types.ts +0 -149
- package/src/decorators/inject.ts +0 -101
- package/src/decorators/injectable.ts +0 -134
- package/src/decorators/lifecycle-decorators.ts +0 -43
- package/src/errors/diagnostics.ts +0 -45
- package/src/errors/errors.ts +0 -499
- package/src/index.ts +0 -139
- package/src/injection/descriptor.ts +0 -218
- package/src/injection/resolve-options.ts +0 -201
- package/src/introspection/dependency-graph.ts +0 -296
- package/src/introspection/graph-adapters/cytoscape.ts +0 -78
- package/src/introspection/graph-adapters/dot.ts +0 -32
- package/src/introspection/graph-adapters/mermaid.ts +0 -61
- package/src/introspection/graph-adapters/reactflow.ts +0 -84
- package/src/introspection/inspector.ts +0 -134
- package/src/lifecycle/lifecycle-manager.ts +0 -272
- package/src/lifecycle/scope-manager.ts +0 -158
- package/src/metadata/metadata-keys.ts +0 -28
- package/src/metadata/metadata-reader-token.ts +0 -10
- package/src/metadata/metadata-types.ts +0 -57
- package/src/metadata/symbol-metadata-reader.ts +0 -126
- package/src/metadata/verifying-metadata-reader.ts +0 -41
- package/src/resolution/cache/activation-need.ts +0 -87
- package/src/resolution/cache/binding-lookup-cache.ts +0 -169
- package/src/resolution/cache/class-introspector.ts +0 -256
- package/src/resolution/context.ts +0 -331
- package/src/resolution/path/resolution-path.ts +0 -167
- package/src/resolution/plan/instantiation-plan.ts +0 -592
- package/src/resolution/resolver.ts +0 -1654
- package/src/resolution/select/binding-select.ts +0 -207
- package/src/resolution/select/constraints.ts +0 -146
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
import type { Binding } from "#/core/binding";
|
|
2
|
-
import { effectiveBindingScope } from "#/core/binding-scope";
|
|
3
|
-
import type { BindingRegistry } from "#/core/registry";
|
|
4
|
-
import { slotName } from "#/core/tag";
|
|
5
|
-
import type { Token } from "#/core/token";
|
|
6
|
-
import { tokenName } from "#/core/token";
|
|
7
|
-
import type { BindingKind, BindingScope, Constructor } from "#/core/types";
|
|
8
|
-
import type { DependencySlot } from "#/injection/resolve-options";
|
|
9
|
-
import { bindingSlotToResolveOptions } from "#/injection/resolve-options";
|
|
10
|
-
import type { MetadataReader } from "#/metadata/metadata-types";
|
|
11
|
-
import { matchesSlot } from "#/resolution/select/binding-select";
|
|
12
|
-
|
|
13
|
-
// ── Types ────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @remarks `kind`/`scope` are `"unbound"` for the placeholder node an optional, currently
|
|
17
|
-
* unsatisfied dependency points at.
|
|
18
|
-
*
|
|
19
|
-
* @since 0.3.16-canary.0
|
|
20
|
-
*/
|
|
21
|
-
export interface GraphNode {
|
|
22
|
-
readonly id: string;
|
|
23
|
-
readonly tokenName: string;
|
|
24
|
-
/** Identifies the token itself, so bindings that share a display name stay distinguishable. */
|
|
25
|
-
readonly tokenKey: string;
|
|
26
|
-
readonly kind: BindingKind | "unbound";
|
|
27
|
-
readonly scope: BindingScope | "unbound";
|
|
28
|
-
readonly fromParent: boolean;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @remarks `label` is presentation, assembled for the adapters; read `optional` and `slotName`
|
|
33
|
-
* rather than parsing it.
|
|
34
|
-
*
|
|
35
|
-
* @since 0.3.16-canary.0
|
|
36
|
-
*/
|
|
37
|
-
export interface GraphEdge {
|
|
38
|
-
readonly from: string;
|
|
39
|
-
readonly to: string;
|
|
40
|
-
readonly label?: string;
|
|
41
|
-
readonly optional: boolean;
|
|
42
|
-
/** The named slot this edge resolves to, when the binding declares one. */
|
|
43
|
-
readonly slotName?: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* A container's dependency graph as plain JSON — nodes, edges, and whether the parent is included.
|
|
48
|
-
*
|
|
49
|
-
* @since 0.3.16-canary.0
|
|
50
|
-
*/
|
|
51
|
-
export interface ContainerGraphJson {
|
|
52
|
-
readonly nodes: ReadonlyArray<GraphNode>;
|
|
53
|
-
readonly edges: ReadonlyArray<GraphEdge>;
|
|
54
|
-
readonly includesParent: boolean;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Options controlling what a dependency graph includes.
|
|
59
|
-
*
|
|
60
|
-
* @since 0.3.16-canary.0
|
|
61
|
-
*/
|
|
62
|
-
export interface GraphOptions {
|
|
63
|
-
readonly includeParent?: boolean | undefined;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ── Builder ──────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
// Tokens are compared by object identity, and a name is free to repeat, so the graph mints its
|
|
69
|
-
// own per-process key. Weakly held: a discarded token takes its key with it.
|
|
70
|
-
const tokenKeys = new WeakMap<object, string>();
|
|
71
|
-
let tokenKeySequence = 0;
|
|
72
|
-
|
|
73
|
-
function tokenKeyOf(dependency: Token<unknown> | Constructor): string {
|
|
74
|
-
const existing = tokenKeys.get(dependency);
|
|
75
|
-
|
|
76
|
-
if (existing !== undefined) {
|
|
77
|
-
return existing;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
tokenKeySequence += 1;
|
|
81
|
-
const key = `${tokenName(dependency)}@${String(tokenKeySequence)}`;
|
|
82
|
-
|
|
83
|
-
tokenKeys.set(dependency, key);
|
|
84
|
-
|
|
85
|
-
return key;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Mirrors filterBindings' slot semantics; predicates need a live resolution context, so the graph
|
|
89
|
-
// keeps every predicate-carrying candidate.
|
|
90
|
-
function matchingTargets(candidates: ReadonlyArray<Binding>, ref: DependencySlot): ReadonlyArray<Binding> {
|
|
91
|
-
const criterion = bindingSlotToResolveOptions(ref);
|
|
92
|
-
|
|
93
|
-
if (ref.multi && criterion === undefined) {
|
|
94
|
-
return candidates;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return candidates.filter((candidate) => matchesSlot(candidate.slot, criterion));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** The name a dependency asks for, whichever spelling carries it. */
|
|
101
|
-
function refSlotName(ref: DependencySlot): string | undefined {
|
|
102
|
-
if (ref.name !== undefined) {
|
|
103
|
-
return ref.name;
|
|
104
|
-
}
|
|
105
|
-
const reserved = ref.tags?.find((criterion) => criterion.key === slotName);
|
|
106
|
-
return reserved === undefined ? undefined : String(reserved.value);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function edgeLabel(ref: DependencySlot, index: number): string {
|
|
110
|
-
const name = refSlotName(ref);
|
|
111
|
-
const firstPlainTag = ref.tags?.find((criterion) => criterion.key !== slotName);
|
|
112
|
-
const criterion =
|
|
113
|
-
name !== undefined
|
|
114
|
-
? `name:${name}`
|
|
115
|
-
: firstPlainTag !== undefined
|
|
116
|
-
? `tag:${firstPlainTag.key.name}=${String(firstPlainTag.value)}`
|
|
117
|
-
: `[${index}]`;
|
|
118
|
-
|
|
119
|
-
return ref.optional ? `${criterion} optional` : criterion;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** The collections a graph walk fills, so each step of the walk can be a function of its own. */
|
|
123
|
-
interface GraphAccumulator {
|
|
124
|
-
readonly nodes: Array<GraphNode>;
|
|
125
|
-
readonly edges: Array<GraphEdge>;
|
|
126
|
-
// One placeholder node per optional-but-unbound token keeps the declared edge visible.
|
|
127
|
-
readonly unboundNodeIds: Map<string, string>;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Own bindings shadow the fallback, mirroring resolution's upward walk. */
|
|
131
|
-
type BindingLookup = (token: Token<unknown> | Constructor) => ReadonlyArray<Binding>;
|
|
132
|
-
|
|
133
|
-
function bindingLookup(sourceRegistry: BindingRegistry, fallbackRegistry: BindingRegistry | undefined): BindingLookup {
|
|
134
|
-
return (token) => {
|
|
135
|
-
const own = sourceRegistry.getAll(token);
|
|
136
|
-
|
|
137
|
-
if (own.length > 0 || fallbackRegistry === undefined) {
|
|
138
|
-
return own;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return fallbackRegistry.getAll(token);
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** The placeholder node an optional-but-unbound dependency points at, minted once per token. */
|
|
146
|
-
function unboundNodeIdFor(accumulator: GraphAccumulator, dependency: Token<unknown> | Constructor): string {
|
|
147
|
-
const key = tokenKeyOf(dependency);
|
|
148
|
-
const existing = accumulator.unboundNodeIds.get(key);
|
|
149
|
-
|
|
150
|
-
if (existing !== undefined) {
|
|
151
|
-
return existing;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const id = `unbound:${key}`;
|
|
155
|
-
|
|
156
|
-
accumulator.unboundNodeIds.set(key, id);
|
|
157
|
-
accumulator.nodes.push({
|
|
158
|
-
id,
|
|
159
|
-
tokenName: tokenName(dependency),
|
|
160
|
-
tokenKey: key,
|
|
161
|
-
kind: "unbound",
|
|
162
|
-
scope: "unbound",
|
|
163
|
-
fromParent: false,
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
return id;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function addDependencyEdges(
|
|
170
|
-
accumulator: GraphAccumulator,
|
|
171
|
-
from: string,
|
|
172
|
-
ref: DependencySlot,
|
|
173
|
-
index: number,
|
|
174
|
-
lookup: BindingLookup,
|
|
175
|
-
): void {
|
|
176
|
-
const targets = matchingTargets(lookup(ref.token), ref);
|
|
177
|
-
const label = edgeLabel(ref, index);
|
|
178
|
-
|
|
179
|
-
if (targets.length === 0) {
|
|
180
|
-
// A required-but-unbound dependency is validate()'s story, not the graph's.
|
|
181
|
-
if (!ref.optional) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const unboundSlotName = refSlotName(ref);
|
|
186
|
-
accumulator.edges.push({
|
|
187
|
-
from,
|
|
188
|
-
to: unboundNodeIdFor(accumulator, ref.token),
|
|
189
|
-
label,
|
|
190
|
-
optional: true,
|
|
191
|
-
...(unboundSlotName !== undefined ? { slotName: unboundSlotName } : {}),
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const requestedName = refSlotName(ref);
|
|
198
|
-
for (const target of targets) {
|
|
199
|
-
// A multi dep with no criterion of its own fans out — each edge names the slot it hits.
|
|
200
|
-
const edgeSlotName = target.slot.name ?? requestedName;
|
|
201
|
-
const perTargetLabel =
|
|
202
|
-
ref.multi && requestedName === undefined && edgeSlotName !== undefined
|
|
203
|
-
? edgeLabel({ ...ref, name: edgeSlotName }, index)
|
|
204
|
-
: label;
|
|
205
|
-
|
|
206
|
-
accumulator.edges.push({
|
|
207
|
-
from,
|
|
208
|
-
to: target.id,
|
|
209
|
-
label: perTargetLabel,
|
|
210
|
-
optional: ref.optional,
|
|
211
|
-
...(edgeSlotName !== undefined ? { slotName: edgeSlotName } : {}),
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/** What one binding declares up front — a class's params, a factory's descriptors, an alias's target. */
|
|
217
|
-
function addBindingEdges(
|
|
218
|
-
accumulator: GraphAccumulator,
|
|
219
|
-
binding: Binding,
|
|
220
|
-
metadataReader: MetadataReader,
|
|
221
|
-
lookup: BindingLookup,
|
|
222
|
-
): void {
|
|
223
|
-
if (binding.kind === "class") {
|
|
224
|
-
const meta = metadataReader.getConstructorMetadata(binding.target);
|
|
225
|
-
|
|
226
|
-
if (meta !== undefined) {
|
|
227
|
-
for (const [index, param] of meta.params.entries()) {
|
|
228
|
-
addDependencyEdges(accumulator, binding.id, param, index, lookup);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (binding.kind === "resolved" || binding.kind === "resolved-async") {
|
|
236
|
-
for (const [index, dependency] of binding.deps.entries()) {
|
|
237
|
-
addDependencyEdges(accumulator, binding.id, dependency, index, lookup);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (binding.kind === "alias") {
|
|
244
|
-
const aliasRef: DependencySlot = { token: binding.target, optional: false, multi: false };
|
|
245
|
-
|
|
246
|
-
for (const target of matchingTargets(lookup(binding.target), aliasRef)) {
|
|
247
|
-
accumulator.edges.push({ from: binding.id, to: target.id, label: "alias", optional: false });
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function addRegistryBindings(
|
|
253
|
-
accumulator: GraphAccumulator,
|
|
254
|
-
sourceRegistry: BindingRegistry,
|
|
255
|
-
metadataReader: MetadataReader,
|
|
256
|
-
fromParent: boolean,
|
|
257
|
-
fallbackRegistry?: BindingRegistry,
|
|
258
|
-
): void {
|
|
259
|
-
const lookup = bindingLookup(sourceRegistry, fallbackRegistry);
|
|
260
|
-
|
|
261
|
-
for (const binding of sourceRegistry.allBindings()) {
|
|
262
|
-
accumulator.nodes.push({
|
|
263
|
-
id: binding.id,
|
|
264
|
-
tokenName: tokenName(binding.token),
|
|
265
|
-
tokenKey: tokenKeyOf(binding.token),
|
|
266
|
-
kind: binding.kind,
|
|
267
|
-
scope: effectiveBindingScope(binding),
|
|
268
|
-
fromParent,
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
addBindingEdges(accumulator, binding, metadataReader, lookup);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Builds the JSON dependency graph of a registry's bindings, optionally including the parent's.
|
|
277
|
-
*
|
|
278
|
-
* @since 0.3.16-canary.0
|
|
279
|
-
*/
|
|
280
|
-
export function buildDependencyGraph(
|
|
281
|
-
registry: BindingRegistry,
|
|
282
|
-
metadataReader: MetadataReader,
|
|
283
|
-
options: GraphOptions | undefined,
|
|
284
|
-
parentRegistry?: BindingRegistry,
|
|
285
|
-
): ContainerGraphJson {
|
|
286
|
-
const accumulator: GraphAccumulator = { nodes: [], edges: [], unboundNodeIds: new Map() };
|
|
287
|
-
const includesParent = options?.includeParent === true;
|
|
288
|
-
|
|
289
|
-
addRegistryBindings(accumulator, registry, metadataReader, false, includesParent ? parentRegistry : undefined);
|
|
290
|
-
|
|
291
|
-
if (includesParent && parentRegistry !== undefined) {
|
|
292
|
-
addRegistryBindings(accumulator, parentRegistry, metadataReader, true);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return { nodes: accumulator.nodes, edges: accumulator.edges, includesParent };
|
|
296
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import type { ContainerGraphJson, GraphNode } from "#/introspection/dependency-graph";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A dependency-graph node in Cytoscape's element format.
|
|
5
|
-
*
|
|
6
|
-
* @since 0.3.16-canary.0
|
|
7
|
-
*/
|
|
8
|
-
export interface CytoscapeNode {
|
|
9
|
-
readonly data: {
|
|
10
|
-
readonly id: string;
|
|
11
|
-
readonly label: string;
|
|
12
|
-
readonly tokenKey: string;
|
|
13
|
-
readonly kind: GraphNode["kind"];
|
|
14
|
-
readonly scope: GraphNode["scope"];
|
|
15
|
-
readonly fromParent: boolean;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* A dependency-graph edge in Cytoscape's element format.
|
|
21
|
-
*
|
|
22
|
-
* @since 0.3.16-canary.0
|
|
23
|
-
*/
|
|
24
|
-
export interface CytoscapeEdge {
|
|
25
|
-
readonly data: {
|
|
26
|
-
readonly id: string;
|
|
27
|
-
readonly source: string;
|
|
28
|
-
readonly target: string;
|
|
29
|
-
readonly label?: string;
|
|
30
|
-
readonly optional: boolean;
|
|
31
|
-
readonly slotName?: string;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* The flat node-and-edge list Cytoscape takes as its `elements` input.
|
|
37
|
-
*
|
|
38
|
-
* @since 0.3.16-canary.0
|
|
39
|
-
*/
|
|
40
|
-
export type CytoscapeElements = ReadonlyArray<CytoscapeNode | CytoscapeEdge>;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Converts a container's dependency graph into Cytoscape's element list.
|
|
44
|
-
*
|
|
45
|
-
* @since 0.3.16-canary.0
|
|
46
|
-
*/
|
|
47
|
-
export function toCytoscapeGraph(graph: ContainerGraphJson): CytoscapeElements {
|
|
48
|
-
const elements: Array<CytoscapeNode | CytoscapeEdge> = [];
|
|
49
|
-
|
|
50
|
-
for (const node of graph.nodes) {
|
|
51
|
-
elements.push({
|
|
52
|
-
data: {
|
|
53
|
-
id: node.id,
|
|
54
|
-
label: node.tokenName,
|
|
55
|
-
tokenKey: node.tokenKey,
|
|
56
|
-
kind: node.kind,
|
|
57
|
-
scope: node.scope,
|
|
58
|
-
fromParent: node.fromParent,
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
for (let idx = 0; idx < graph.edges.length; idx += 1) {
|
|
64
|
-
const edge = graph.edges[idx]!;
|
|
65
|
-
elements.push({
|
|
66
|
-
data: {
|
|
67
|
-
id: `edge-${idx}`,
|
|
68
|
-
source: edge.from,
|
|
69
|
-
target: edge.to,
|
|
70
|
-
...(edge.label !== undefined ? { label: edge.label } : {}),
|
|
71
|
-
optional: edge.optional,
|
|
72
|
-
...(edge.slotName !== undefined ? { slotName: edge.slotName } : {}),
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return elements;
|
|
78
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { ContainerGraphJson } from "#/introspection/dependency-graph";
|
|
2
|
-
|
|
3
|
-
// Token names are caller-supplied and land inside DOT quoted strings — ids included, since an
|
|
4
|
-
// unbound placeholder id embeds the name it was minted from.
|
|
5
|
-
function escapeDotString(value: string): string {
|
|
6
|
-
return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Renders a container's dependency graph as Graphviz DOT source.
|
|
11
|
-
*
|
|
12
|
-
* @since 0.3.16-canary.0
|
|
13
|
-
*/
|
|
14
|
-
export function toDotGraph(graph: ContainerGraphJson): string {
|
|
15
|
-
const lines: Array<string> = ["digraph DI {", " rankdir=TB;"];
|
|
16
|
-
|
|
17
|
-
for (const node of graph.nodes) {
|
|
18
|
-
const label = `${escapeDotString(node.tokenName)}\\n[${node.kind}/${node.scope}]`;
|
|
19
|
-
// Dashed for anything that is not a live binding of this container: the parent chain
|
|
20
|
-
// and unbound-optional placeholders.
|
|
21
|
-
const style = node.fromParent || node.kind === "unbound" ? ' style="dashed"' : "";
|
|
22
|
-
lines.push(` "${escapeDotString(node.id)}" [label="${label}"${style}];`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
for (const edge of graph.edges) {
|
|
26
|
-
const label = edge.label === undefined ? "" : ` [label="${escapeDotString(edge.label)}"]`;
|
|
27
|
-
lines.push(` "${escapeDotString(edge.from)}" -> "${escapeDotString(edge.to)}"${label};`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
lines.push("}");
|
|
31
|
-
return lines.join("\n");
|
|
32
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { ContainerGraphJson } from "#/introspection/dependency-graph";
|
|
2
|
-
|
|
3
|
-
// Token names are caller-supplied and Mermaid labels are HTML-bearing quoted strings, so quotes
|
|
4
|
-
// and markup are neutralized with Mermaid's decimal entity codes before they reach a renderer.
|
|
5
|
-
function escapeMermaidLabel(value: string): string {
|
|
6
|
-
return value.replaceAll("&", "#38;").replaceAll('"', "#34;").replaceAll("<", "#60;").replaceAll(">", "#62;");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Mermaid `flowchart TD` source for a container graph — renders anywhere Mermaid does
|
|
11
|
-
* (GitHub markdown, docs tooling, mermaid.live) with no extra library.
|
|
12
|
-
*
|
|
13
|
-
* @since 0.6.0
|
|
14
|
-
*/
|
|
15
|
-
export function toMermaidGraph(graph: ContainerGraphJson): string {
|
|
16
|
-
// Mermaid identifiers must stay word-safe; labels carry the real token names.
|
|
17
|
-
const idByNode = new Map<string, string>();
|
|
18
|
-
const lines: Array<string> = ["flowchart TD"];
|
|
19
|
-
const parentIds: Array<string> = [];
|
|
20
|
-
const unboundIds: Array<string> = [];
|
|
21
|
-
|
|
22
|
-
graph.nodes.forEach((node, index) => {
|
|
23
|
-
const id = `n${String(index)}`;
|
|
24
|
-
|
|
25
|
-
idByNode.set(node.id, id);
|
|
26
|
-
lines.push(` ${id}["${escapeMermaidLabel(node.tokenName)}<br/>${node.kind} · ${node.scope}"]`);
|
|
27
|
-
|
|
28
|
-
if (node.fromParent) {
|
|
29
|
-
parentIds.push(id);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (node.kind === "unbound") {
|
|
33
|
-
unboundIds.push(id);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
for (const edge of graph.edges) {
|
|
38
|
-
const from = idByNode.get(edge.from);
|
|
39
|
-
const to = idByNode.get(edge.to);
|
|
40
|
-
|
|
41
|
-
if (from === undefined || to === undefined) {
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
lines.push(
|
|
46
|
-
edge.label === undefined ? ` ${from} --> ${to}` : ` ${from} -->|"${escapeMermaidLabel(edge.label)}"| ${to}`,
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (parentIds.length > 0) {
|
|
51
|
-
lines.push(" classDef fromParent stroke-dasharray: 4 4;");
|
|
52
|
-
lines.push(` class ${parentIds.join(",")} fromParent;`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (unboundIds.length > 0) {
|
|
56
|
-
lines.push(" classDef unbound stroke-dasharray: 4 4,opacity:0.6;");
|
|
57
|
-
lines.push(` class ${unboundIds.join(",")} unbound;`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return lines.join("\n");
|
|
61
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import type { ContainerGraphJson, GraphNode } from "#/introspection/dependency-graph";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A dependency-graph node in React Flow's node format.
|
|
5
|
-
*
|
|
6
|
-
* @since 0.3.16-canary.0
|
|
7
|
-
*/
|
|
8
|
-
export interface ReactFlowNode {
|
|
9
|
-
readonly id: string;
|
|
10
|
-
readonly data: {
|
|
11
|
-
readonly label: string;
|
|
12
|
-
readonly tokenKey: string;
|
|
13
|
-
readonly kind: GraphNode["kind"];
|
|
14
|
-
readonly scope: GraphNode["scope"];
|
|
15
|
-
readonly fromParent: boolean;
|
|
16
|
-
};
|
|
17
|
-
readonly position: { readonly x: number; readonly y: number };
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* A dependency-graph edge in React Flow's edge format.
|
|
22
|
-
*
|
|
23
|
-
* @since 0.3.16-canary.0
|
|
24
|
-
*/
|
|
25
|
-
export interface ReactFlowEdge {
|
|
26
|
-
readonly id: string;
|
|
27
|
-
readonly source: string;
|
|
28
|
-
readonly target: string;
|
|
29
|
-
readonly label?: string;
|
|
30
|
-
readonly optional: boolean;
|
|
31
|
-
readonly slotName?: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* The nodes-and-edges pair a React Flow viewer renders.
|
|
36
|
-
*
|
|
37
|
-
* @since 0.3.16-canary.0
|
|
38
|
-
*/
|
|
39
|
-
export interface ReactFlowGraph {
|
|
40
|
-
readonly nodes: ReadonlyArray<ReactFlowNode>;
|
|
41
|
-
readonly edges: ReadonlyArray<ReactFlowEdge>;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Initial grid layout: React Flow expects concrete positions; viewers re-layout anyway.
|
|
46
|
-
*
|
|
47
|
-
* @since 0.3.16-canary.0
|
|
48
|
-
*/
|
|
49
|
-
const GRID_COLUMN_COUNT = 5;
|
|
50
|
-
const GRID_CELL_WIDTH_PX = 200;
|
|
51
|
-
const GRID_CELL_HEIGHT_PX = 100;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Converts a container's dependency graph into React Flow nodes and edges on an initial grid.
|
|
55
|
-
*
|
|
56
|
-
* @since 0.5.0-canary.7
|
|
57
|
-
*/
|
|
58
|
-
export function toReactFlowGraph(graph: ContainerGraphJson): ReactFlowGraph {
|
|
59
|
-
const nodes: Array<ReactFlowNode> = graph.nodes.map((node, idx) => ({
|
|
60
|
-
id: node.id,
|
|
61
|
-
data: {
|
|
62
|
-
label: node.tokenName,
|
|
63
|
-
tokenKey: node.tokenKey,
|
|
64
|
-
kind: node.kind,
|
|
65
|
-
scope: node.scope,
|
|
66
|
-
fromParent: node.fromParent,
|
|
67
|
-
},
|
|
68
|
-
position: {
|
|
69
|
-
x: (idx % GRID_COLUMN_COUNT) * GRID_CELL_WIDTH_PX,
|
|
70
|
-
y: Math.floor(idx / GRID_COLUMN_COUNT) * GRID_CELL_HEIGHT_PX,
|
|
71
|
-
},
|
|
72
|
-
}));
|
|
73
|
-
|
|
74
|
-
const edges: Array<ReactFlowEdge> = graph.edges.map((edge, idx) => ({
|
|
75
|
-
id: `edge-${idx}`,
|
|
76
|
-
source: edge.from,
|
|
77
|
-
target: edge.to,
|
|
78
|
-
...(edge.label !== undefined ? { label: edge.label } : {}),
|
|
79
|
-
optional: edge.optional,
|
|
80
|
-
...(edge.slotName !== undefined ? { slotName: edge.slotName } : {}),
|
|
81
|
-
}));
|
|
82
|
-
|
|
83
|
-
return { nodes, edges };
|
|
84
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import type { Binding } from "#/core/binding";
|
|
2
|
-
import { effectiveBindingScope } from "#/core/binding-scope";
|
|
3
|
-
import type { BindingRegistry } from "#/core/registry";
|
|
4
|
-
import type { Token } from "#/core/token";
|
|
5
|
-
import { tokenName } from "#/core/token";
|
|
6
|
-
import type {
|
|
7
|
-
BindingIdentifier,
|
|
8
|
-
BindingKind,
|
|
9
|
-
BindingScope,
|
|
10
|
-
BindingTag,
|
|
11
|
-
ConstraintContext,
|
|
12
|
-
Constructor,
|
|
13
|
-
ResolveOptions,
|
|
14
|
-
} from "#/core/types";
|
|
15
|
-
import type { ScopeManager } from "#/lifecycle/scope-manager";
|
|
16
|
-
import { selectAllBindings } from "#/resolution/select/binding-select";
|
|
17
|
-
|
|
18
|
-
// ── Public types ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* A read-only view of one binding: token, kind, scope, slot, and id.
|
|
22
|
-
*
|
|
23
|
-
* @since 0.3.16-canary.0
|
|
24
|
-
*/
|
|
25
|
-
export interface BindingSnapshot {
|
|
26
|
-
readonly tokenName: string;
|
|
27
|
-
readonly kind: BindingKind;
|
|
28
|
-
readonly scope: BindingScope;
|
|
29
|
-
readonly slot: {
|
|
30
|
-
readonly name?: string;
|
|
31
|
-
readonly tags: ReadonlyArray<BindingTag>;
|
|
32
|
-
};
|
|
33
|
-
readonly id: BindingIdentifier;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* A read-only view of one container's own bindings and state.
|
|
38
|
-
*
|
|
39
|
-
* @since 0.3.16-canary.0
|
|
40
|
-
*/
|
|
41
|
-
export interface ContainerSnapshot {
|
|
42
|
-
readonly ownBindings: ReadonlyArray<BindingSnapshot>;
|
|
43
|
-
readonly cachedSingletonCount: number;
|
|
44
|
-
readonly hasParent: boolean;
|
|
45
|
-
readonly isDisposed: boolean;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ── Inspector ────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* The read-only introspection surface behind a container's `inspect()`.
|
|
52
|
-
*
|
|
53
|
-
* @since 0.3.16-canary.0
|
|
54
|
-
*/
|
|
55
|
-
export class Inspector {
|
|
56
|
-
readonly #registry: BindingRegistry;
|
|
57
|
-
readonly #scope: ScopeManager;
|
|
58
|
-
readonly #hasParent: boolean;
|
|
59
|
-
readonly #isDisposed: () => boolean;
|
|
60
|
-
|
|
61
|
-
constructor(registry: BindingRegistry, scope: ScopeManager, hasParent: boolean, isDisposed: () => boolean) {
|
|
62
|
-
this.#registry = registry;
|
|
63
|
-
this.#scope = scope;
|
|
64
|
-
this.#hasParent = hasParent;
|
|
65
|
-
this.#isDisposed = isDisposed;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
inspect(): ContainerSnapshot {
|
|
69
|
-
return {
|
|
70
|
-
ownBindings: this.#registry.allBindings().map((binding) => this.#toSnapshot(binding)),
|
|
71
|
-
cachedSingletonCount: this.#scope.cachedSingletons().length,
|
|
72
|
-
hasParent: this.#hasParent,
|
|
73
|
-
isDisposed: this.#isDisposed(),
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
lookupBindings<Value>(token: Token<Value> | Constructor<Value>): ReadonlyArray<BindingSnapshot> {
|
|
78
|
-
const bindings = this.#registry.getAll(token);
|
|
79
|
-
return bindings.map((binding) => this.#toSnapshot(binding));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
has(token: Token<unknown> | Constructor, options?: ResolveOptions, parentHas?: () => boolean): boolean {
|
|
83
|
-
const bindings = this.#registry.getAll(token);
|
|
84
|
-
if (bindings.length > 0) {
|
|
85
|
-
// An existence probe answers ambiguity with `true` — several matches still exist; only
|
|
86
|
-
// resolution has to pick one.
|
|
87
|
-
if (options !== undefined) {
|
|
88
|
-
if (selectAllBindings(bindings, options, this.#makeConstraintContext(options)).length > 0) {
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return parentHas?.() ?? false;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
hasOwn(token: Token<unknown> | Constructor, options?: ResolveOptions): boolean {
|
|
99
|
-
const bindings = this.#registry.getAll(token);
|
|
100
|
-
if (bindings.length === 0) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
if (options !== undefined) {
|
|
104
|
-
return selectAllBindings(bindings, options, this.#makeConstraintContext(options)).length > 0;
|
|
105
|
-
}
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
#makeConstraintContext(options: ResolveOptions): ConstraintContext {
|
|
110
|
-
return {
|
|
111
|
-
resolutionPath: [],
|
|
112
|
-
resolutionStack: [],
|
|
113
|
-
parent: undefined,
|
|
114
|
-
ancestors: [],
|
|
115
|
-
currentResolveOptions: options,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
#toSnapshot(binding: Binding): BindingSnapshot {
|
|
120
|
-
// Aliased, not copied: slot tags are frozen where they are built, so a caller's write throws
|
|
121
|
-
// instead of corrupting the registry — and the snapshot skips an allocation per binding.
|
|
122
|
-
const slot: BindingSnapshot["slot"] =
|
|
123
|
-
binding.slot.name !== undefined
|
|
124
|
-
? { name: binding.slot.name, tags: binding.slot.tags }
|
|
125
|
-
: { tags: binding.slot.tags };
|
|
126
|
-
return {
|
|
127
|
-
tokenName: tokenName(binding.token),
|
|
128
|
-
kind: binding.kind,
|
|
129
|
-
scope: effectiveBindingScope(binding),
|
|
130
|
-
slot,
|
|
131
|
-
id: binding.id,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
}
|