@codefast/di 0.3.14-canary.0 → 0.3.14-canary.2
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 +28 -0
- package/README.md +54 -29
- package/dist/binding-scope.d.mts +11 -0
- package/dist/binding-scope.mjs +19 -0
- package/dist/binding-select.d.mts +8 -26
- package/dist/binding-select.mjs +56 -49
- package/dist/binding.d.mts +107 -327
- package/dist/binding.mjs +19 -324
- package/dist/constraints.d.mts +11 -29
- package/dist/constraints.mjs +32 -36
- package/dist/constructor-type.d.mts +17 -0
- package/dist/constructor-type.mjs +1 -0
- package/dist/container.d.mts +44 -128
- package/dist/container.mjs +664 -433
- package/dist/decorators/inject.d.mts +19 -50
- package/dist/decorators/inject.mjs +131 -93
- package/dist/decorators/injectable.d.mts +16 -37
- package/dist/decorators/injectable.mjs +47 -66
- package/dist/decorators/lifecycle-decorators.d.mts +2 -22
- package/dist/decorators/lifecycle-decorators.mjs +81 -37
- package/dist/dependency-graph.d.mts +24 -54
- package/dist/dependency-graph.mjs +51 -153
- package/dist/environment.d.mts +38 -12
- package/dist/environment.mjs +82 -16
- package/dist/errors.d.mts +69 -188
- package/dist/errors.mjs +92 -219
- package/dist/graph-adapters/cytoscape.d.mts +24 -0
- package/dist/graph-adapters/cytoscape.mjs +23 -0
- package/dist/graph-adapters/dot.d.mts +6 -0
- package/dist/graph-adapters/dot.mjs +17 -0
- package/dist/graph-adapters/reactflow.d.mts +29 -0
- package/dist/graph-adapters/reactflow.mjs +29 -0
- package/dist/graph-adapters/types.d.mts +2 -0
- package/dist/graph-adapters/types.mjs +1 -0
- package/dist/index.d.mts +16 -9
- package/dist/index.mjs +8 -5
- package/dist/inspector.d.mts +34 -95
- package/dist/inspector.mjs +57 -256
- package/dist/lifecycle.d.mts +20 -53
- package/dist/lifecycle.mjs +129 -99
- package/dist/metadata/metadata-keys.d.mts +9 -26
- package/dist/metadata/metadata-keys.mjs +7 -28
- package/dist/metadata/metadata-reader-token.d.mts +7 -0
- package/dist/metadata/metadata-reader-token.mjs +5 -0
- package/dist/metadata/metadata-types.d.mts +22 -71
- package/dist/metadata/symbol-metadata-reader.d.mts +10 -26
- package/dist/metadata/symbol-metadata-reader.mjs +32 -45
- package/dist/module.d.mts +30 -84
- package/dist/module.mjs +26 -72
- package/dist/registry.d.mts +32 -63
- package/dist/registry.mjs +131 -82
- package/dist/resolve-options.d.mts +18 -0
- package/dist/resolve-options.mjs +22 -0
- package/dist/resolver.d.mts +67 -190
- package/dist/resolver.mjs +715 -424
- package/dist/scope.d.mts +19 -102
- package/dist/scope.mjs +37 -192
- package/dist/token.d.mts +8 -22
- package/dist/token.mjs +9 -11
- package/dist/types.d.mts +48 -0
- package/dist/types.mjs +1 -0
- package/package.json +52 -14
- package/dist/metadata/param-registry.d.mts +0 -16
- package/dist/metadata/param-registry.mjs +0 -31
- package/dist/scope-validation.d.mts +0 -21
- package/dist/scope-validation.mjs +0 -35
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/graph-adapters/cytoscape.ts
|
|
2
|
+
function toCytoscapeGraph(graph) {
|
|
3
|
+
const elements = [];
|
|
4
|
+
for (const node of graph.nodes) elements.push({ data: {
|
|
5
|
+
id: node.id,
|
|
6
|
+
label: node.tokenName,
|
|
7
|
+
kind: node.kind,
|
|
8
|
+
scope: node.scope,
|
|
9
|
+
fromParent: node.fromParent
|
|
10
|
+
} });
|
|
11
|
+
graph.edges.forEach((edge, idx) => {
|
|
12
|
+
const data = {
|
|
13
|
+
id: `edge-${idx}`,
|
|
14
|
+
source: edge.from,
|
|
15
|
+
target: edge.to
|
|
16
|
+
};
|
|
17
|
+
if (edge.label !== void 0) data.label = edge.label;
|
|
18
|
+
elements.push({ data });
|
|
19
|
+
});
|
|
20
|
+
return elements;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { toCytoscapeGraph };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/graph-adapters/dot.ts
|
|
2
|
+
function toDotGraph(graph) {
|
|
3
|
+
const lines = ["digraph DI {", " rankdir=TB;"];
|
|
4
|
+
for (const node of graph.nodes) {
|
|
5
|
+
const label = `${node.tokenName}\\n[${node.kind}/${node.scope}]`;
|
|
6
|
+
const style = node.fromParent ? " style=\"dashed\"" : "";
|
|
7
|
+
lines.push(` "${node.id}" [label="${label}"${style}];`);
|
|
8
|
+
}
|
|
9
|
+
for (const edge of graph.edges) {
|
|
10
|
+
const label = edge.label !== void 0 ? ` [label="${edge.label}"]` : "";
|
|
11
|
+
lines.push(` "${edge.from}" -> "${edge.to}"${label};`);
|
|
12
|
+
}
|
|
13
|
+
lines.push("}");
|
|
14
|
+
return lines.join("\n");
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { toDotGraph };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ContainerGraphJson } from "../dependency-graph.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/graph-adapters/reactflow.d.ts
|
|
4
|
+
interface ReactFlowNode {
|
|
5
|
+
id: string;
|
|
6
|
+
data: {
|
|
7
|
+
label: string;
|
|
8
|
+
kind: string;
|
|
9
|
+
scope: string;
|
|
10
|
+
fromParent: boolean;
|
|
11
|
+
};
|
|
12
|
+
position: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
interface ReactFlowEdge {
|
|
18
|
+
id: string;
|
|
19
|
+
source: string;
|
|
20
|
+
target: string;
|
|
21
|
+
label?: string;
|
|
22
|
+
}
|
|
23
|
+
interface ReactFlowGraph {
|
|
24
|
+
nodes: ReactFlowNode[];
|
|
25
|
+
edges: ReactFlowEdge[];
|
|
26
|
+
}
|
|
27
|
+
declare function toReactFlowGraph(graph: ContainerGraphJson): ReactFlowGraph;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { ReactFlowEdge, ReactFlowGraph, ReactFlowNode, toReactFlowGraph };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/graph-adapters/reactflow.ts
|
|
2
|
+
function toReactFlowGraph(graph) {
|
|
3
|
+
return {
|
|
4
|
+
nodes: graph.nodes.map((node, idx) => ({
|
|
5
|
+
id: node.id,
|
|
6
|
+
data: {
|
|
7
|
+
label: node.tokenName,
|
|
8
|
+
kind: node.kind,
|
|
9
|
+
scope: node.scope,
|
|
10
|
+
fromParent: node.fromParent
|
|
11
|
+
},
|
|
12
|
+
position: {
|
|
13
|
+
x: idx % 5 * 200,
|
|
14
|
+
y: Math.floor(idx / 5) * 100
|
|
15
|
+
}
|
|
16
|
+
})),
|
|
17
|
+
edges: graph.edges.map((edge, idx) => {
|
|
18
|
+
const reactFlowEdge = {
|
|
19
|
+
id: `edge-${idx}`,
|
|
20
|
+
source: edge.from,
|
|
21
|
+
target: edge.to
|
|
22
|
+
};
|
|
23
|
+
if (edge.label !== void 0) reactFlowEdge.label = edge.label;
|
|
24
|
+
return reactFlowEdge;
|
|
25
|
+
})
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { toReactFlowGraph };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { Constructor } from "./constructor-type.mjs";
|
|
2
|
+
import { Token, token } from "./token.mjs";
|
|
3
|
+
import { ActivationHandler, BindingIdentifier, BindingKind, BindingScope, ConstraintContext, DeactivationHandler, DependencyKey, MaterializationFrame, ResolutionContext, ResolveOptions, TokenValue } from "./types.mjs";
|
|
4
|
+
import { InjectOptions, InjectableDependency, InjectionDescriptor, inject, injectAll, isInjectionDescriptor, optional } from "./decorators/inject.mjs";
|
|
5
|
+
import { AliasBindingBuilder, BindToBuilder, BindingBuilder, ConstantBindingBuilder, ScopedBindingBuilder, SingletonBindingBuilder, SingletonLifecycleBuilder, TransientBindingBuilder } from "./binding.mjs";
|
|
6
|
+
import { effectiveBindingScope } from "./binding-scope.mjs";
|
|
7
|
+
import { AsyncModule, AsyncModuleBuilder, Module, ModuleBuilder, SyncModule } from "./module.mjs";
|
|
8
|
+
import { BindingSnapshot, ContainerSnapshot } from "./inspector.mjs";
|
|
9
|
+
import { MetadataReader, MutableLifecycleMetadata } from "./metadata/metadata-types.mjs";
|
|
10
|
+
import { ContainerGraphJson, GraphEdge, GraphNode, GraphOptions } from "./dependency-graph.mjs";
|
|
11
|
+
import { AutoRegisterRegistry, InjectableOptions, createAutoRegisterRegistry, injectable } from "./decorators/injectable.mjs";
|
|
12
|
+
import { Container, ContainerStatic } from "./container.mjs";
|
|
8
13
|
import { postConstruct, preDestroy } from "./decorators/lifecycle-decorators.mjs";
|
|
9
|
-
import { AsyncModuleLoadError, AsyncResolutionError, CircularDependencyError, DiError, InternalError, MissingMetadataError, NoMatchingBindingError, ScopeViolationDetails, ScopeViolationError, TokenNotBoundError } from "./errors.mjs";
|
|
10
|
-
|
|
14
|
+
import { AmbiguousBindingError, AsyncDeactivationError, AsyncModuleLoadError, AsyncResolutionError, CircularDependencyError, DiError, DisposedContainerError, InternalError, MissingContainerContextError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, RebindUnboundTokenError, ScopeViolationDetails, ScopeViolationError, SyncDisposalNotSupportedError, TokenNotBoundError } from "./errors.mjs";
|
|
15
|
+
import { injectableSlotToResolveOptions, slotKeyToResolveOptions } from "./resolve-options.mjs";
|
|
16
|
+
import { MetadataReaderToken } from "./metadata/metadata-reader-token.mjs";
|
|
17
|
+
export { type ActivationHandler, type AliasBindingBuilder, AmbiguousBindingError, AsyncDeactivationError, AsyncModule, type AsyncModuleBuilder, AsyncModuleLoadError, AsyncResolutionError, type AutoRegisterRegistry, type BindToBuilder, type BindingBuilder, type BindingIdentifier, type BindingKind, type BindingScope, type BindingSnapshot, CircularDependencyError, type ConstantBindingBuilder, type ConstraintContext, type Constructor, Container, type ContainerGraphJson, type Container as ContainerInterface, type ContainerSnapshot, type ContainerStatic, type DeactivationHandler, type DependencyKey, DiError, DisposedContainerError, type GraphEdge, type GraphNode, type GraphOptions, type InjectOptions, type InjectableDependency, type InjectableOptions, type InjectionDescriptor, InternalError, type MaterializationFrame, type MetadataReader, MetadataReaderToken, MissingContainerContextError, MissingMetadataError, MissingScopeContextError, Module, type ModuleBuilder, type MutableLifecycleMetadata, NoMatchingBindingError, RebindUnboundTokenError, type ResolutionContext, type ResolveOptions, type ScopeViolationDetails, ScopeViolationError, type ScopedBindingBuilder, type SingletonBindingBuilder, type SingletonLifecycleBuilder, SyncDisposalNotSupportedError, SyncModule, type Token, TokenNotBoundError, type TokenValue, type TransientBindingBuilder, createAutoRegisterRegistry, effectiveBindingScope, inject, injectAll, injectable, injectableSlotToResolveOptions, isInjectionDescriptor, optional, postConstruct, preDestroy, slotKeyToResolveOptions, token };
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { effectiveBindingScope } from "./binding-scope.mjs";
|
|
2
|
+
import { AmbiguousBindingError, AsyncDeactivationError, AsyncModuleLoadError, AsyncResolutionError, CircularDependencyError, DiError, DisposedContainerError, InternalError, MissingContainerContextError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError, TokenNotBoundError } from "./errors.mjs";
|
|
3
|
+
import { token } from "./token.mjs";
|
|
4
|
+
import { injectableSlotToResolveOptions, slotKeyToResolveOptions } from "./resolve-options.mjs";
|
|
5
|
+
import { MetadataReaderToken } from "./metadata/metadata-reader-token.mjs";
|
|
2
6
|
import { inject, injectAll, isInjectionDescriptor, optional } from "./decorators/inject.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { AsyncModule, Module } from "./module.mjs";
|
|
7
|
+
import { AsyncModule, Module, SyncModule } from "./module.mjs";
|
|
5
8
|
import { Container } from "./container.mjs";
|
|
6
|
-
import {
|
|
9
|
+
import { createAutoRegisterRegistry, injectable } from "./decorators/injectable.mjs";
|
|
7
10
|
import { postConstruct, preDestroy } from "./decorators/lifecycle-decorators.mjs";
|
|
8
|
-
export { AsyncModule, AsyncModuleLoadError, AsyncResolutionError, CircularDependencyError, Container, DiError, InternalError, MissingMetadataError, Module, NoMatchingBindingError, ScopeViolationError, TokenNotBoundError,
|
|
11
|
+
export { AmbiguousBindingError, AsyncDeactivationError, AsyncModule, AsyncModuleLoadError, AsyncResolutionError, CircularDependencyError, Container, DiError, DisposedContainerError, InternalError, MetadataReaderToken, MissingContainerContextError, MissingMetadataError, MissingScopeContextError, Module, NoMatchingBindingError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError, SyncModule, TokenNotBoundError, createAutoRegisterRegistry, effectiveBindingScope, inject, injectAll, injectable, injectableSlotToResolveOptions, isInjectionDescriptor, optional, postConstruct, preDestroy, slotKeyToResolveOptions, token };
|
package/dist/inspector.d.mts
CHANGED
|
@@ -1,100 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { Constructor } from "./constructor-type.mjs";
|
|
2
|
+
import { Token } from "./token.mjs";
|
|
3
|
+
import { BindingIdentifier, BindingKind, BindingScope, ResolveOptions } from "./types.mjs";
|
|
4
|
+
import { BindingRegistry } from "./registry.mjs";
|
|
5
|
+
import { ScopeManager } from "./scope.mjs";
|
|
5
6
|
|
|
6
7
|
//#region src/inspector.d.ts
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type BindingActivationStatus = "cached" | "not-cached" | "transient";
|
|
11
|
-
/**
|
|
12
|
-
* Per-binding row inside a {@link ContainerSnapshot}.
|
|
13
|
-
*/
|
|
14
|
-
type ContainerBindingSnapshot = {
|
|
15
|
-
readonly registryKeyLabel: string;
|
|
16
|
-
readonly bindingId: BindingIdentifier;
|
|
17
|
-
readonly kind: Binding<unknown>["kind"];
|
|
8
|
+
interface BindingSnapshot {
|
|
9
|
+
readonly tokenName: string;
|
|
10
|
+
readonly kind: BindingKind;
|
|
18
11
|
readonly scope: BindingScope;
|
|
19
|
-
readonly
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
readonly
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
readonly
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
lookupBindings(key: RegistryKey): readonly Binding<unknown>[] | undefined;
|
|
45
|
-
isBindingCached(binding: Binding<unknown>): boolean;
|
|
46
|
-
metadataReader: MetadataReader | undefined;
|
|
47
|
-
};
|
|
48
|
-
/**
|
|
49
|
-
* Options for {@link Container.generateDependencyGraph}.
|
|
50
|
-
*/
|
|
51
|
-
type DotGraphOptions = {
|
|
52
|
-
/**
|
|
53
|
-
* When true, omit registry keys whose label starts with `CODEFAST_DI_` (framework-style tokens)
|
|
54
|
-
* and any edges that would only connect hidden nodes.
|
|
55
|
-
*/
|
|
56
|
-
readonly hideInternals?: boolean;
|
|
57
|
-
};
|
|
58
|
-
/**
|
|
59
|
-
* Reads the container's registry and scope-cache state to produce debug snapshots
|
|
60
|
-
* and dependency-graph output (Graphviz DOT / typed JSON).
|
|
61
|
-
*
|
|
62
|
-
* Constructed internally by the container; advanced consumers can also construct it
|
|
63
|
-
* directly via the `@codefast/di/inspector` subpath export.
|
|
64
|
-
*/
|
|
65
|
-
declare class ContainerInspector {
|
|
66
|
-
private readonly ctx;
|
|
67
|
-
constructor(ctx: ContainerInspectorContext);
|
|
68
|
-
/**
|
|
69
|
-
* Collects all registered bindings into a flat, serialisable snapshot.
|
|
70
|
-
*/
|
|
71
|
-
getSnapshot(): ContainerSnapshot;
|
|
72
|
-
/**
|
|
73
|
-
* Produces a Graphviz `digraph` string with HTML-label nodes and styled edges.
|
|
74
|
-
* Cycles are represented as ordinary edges (Graphviz renders them correctly).
|
|
75
|
-
* Pass `hideInternals: true` to suppress `CODEFAST_DI_`-prefixed tokens.
|
|
76
|
-
*/
|
|
77
|
-
generateDotGraph(options?: DotGraphOptions): string;
|
|
78
|
-
/**
|
|
79
|
-
* Overloaded entry point: returns DOT format by default, or a typed {@link ContainerGraphJson}
|
|
80
|
-
* when `format: "json"` is specified.
|
|
81
|
-
*/
|
|
82
|
-
generateDependencyGraph(options?: DotGraphOptions & {
|
|
83
|
-
format?: "dot";
|
|
84
|
-
}): string;
|
|
85
|
-
generateDependencyGraph(options: DotGraphOptions & {
|
|
86
|
-
format: "json";
|
|
87
|
-
}): ContainerGraphJson;
|
|
88
|
-
/**
|
|
89
|
-
* Builds the typed JSON graph (nodes + edges) used by the `"json"` format path.
|
|
90
|
-
* Applies the same `hideInternals` / deduplication logic as {@link generateDotGraph}.
|
|
91
|
-
*/
|
|
92
|
-
generateDependencyGraphJsonTyped(options?: DotGraphOptions): ContainerGraphJson;
|
|
93
|
-
/**
|
|
94
|
-
* Produces a JSON graph representation with `nodes` and `edges`.
|
|
95
|
-
* @internal Use `generateDependencyGraph({ format: "json" })` for typed output.
|
|
96
|
-
*/
|
|
97
|
-
generateDependencyGraphJson(options?: DotGraphOptions): string;
|
|
12
|
+
readonly slot: {
|
|
13
|
+
readonly name?: string;
|
|
14
|
+
readonly tags: ReadonlyArray<readonly [string, unknown]>;
|
|
15
|
+
};
|
|
16
|
+
readonly id: BindingIdentifier;
|
|
17
|
+
}
|
|
18
|
+
interface ContainerSnapshot {
|
|
19
|
+
readonly ownBindings: readonly BindingSnapshot[];
|
|
20
|
+
readonly bindings: readonly BindingSnapshot[];
|
|
21
|
+
readonly cachedSingletonCount: number;
|
|
22
|
+
readonly hasParent: boolean;
|
|
23
|
+
readonly isDisposed: boolean;
|
|
24
|
+
}
|
|
25
|
+
declare class Inspector {
|
|
26
|
+
private readonly _registry;
|
|
27
|
+
private readonly _scope;
|
|
28
|
+
private readonly _hasParent;
|
|
29
|
+
private readonly _isDisposed;
|
|
30
|
+
constructor(_registry: BindingRegistry, _scope: ScopeManager, _hasParent: boolean, _isDisposed: () => boolean);
|
|
31
|
+
inspect(): ContainerSnapshot;
|
|
32
|
+
lookupBindings<Value>(token: Token<Value> | Constructor<Value>): readonly BindingSnapshot[];
|
|
33
|
+
has(token: Token<unknown> | Constructor, hint?: ResolveOptions, parentHas?: () => boolean): boolean;
|
|
34
|
+
hasOwn(token: Token<unknown> | Constructor, hint?: ResolveOptions): boolean;
|
|
35
|
+
private allBindingSnapshots;
|
|
36
|
+
private _toSnapshot;
|
|
98
37
|
}
|
|
99
38
|
//#endregion
|
|
100
|
-
export {
|
|
39
|
+
export { BindingSnapshot, ContainerSnapshot, Inspector };
|
package/dist/inspector.mjs
CHANGED
|
@@ -1,268 +1,69 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { effectiveBindingScope } from "./binding-scope.mjs";
|
|
2
|
+
import { selectBinding } from "./binding-select.mjs";
|
|
3
|
+
import { tokenName } from "./token.mjs";
|
|
3
4
|
//#region src/inspector.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Escapes a string for use as a DOT `label="..."` attribute value.
|
|
13
|
-
*/
|
|
14
|
-
function dotEscapeLabel(text) {
|
|
15
|
-
return text.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n");
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Escapes a string for safe embedding inside a DOT HTML-label (`<...>`) table cell.
|
|
19
|
-
*/
|
|
20
|
-
function dotEscapeHtml(text) {
|
|
21
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Returns the Graphviz node shape name for a given binding kind.
|
|
25
|
-
*/
|
|
26
|
-
function nodeShapeForKind(kind) {
|
|
27
|
-
switch (kind) {
|
|
28
|
-
case "constant": return "ellipse";
|
|
29
|
-
case "class": return "box";
|
|
30
|
-
case "dynamic":
|
|
31
|
-
case "async-dynamic":
|
|
32
|
-
case "resolved": return "diamond";
|
|
33
|
-
case "alias": return "octagon";
|
|
34
|
-
default: return kind;
|
|
5
|
+
var Inspector = class {
|
|
6
|
+
constructor(_registry, _scope, _hasParent, _isDisposed) {
|
|
7
|
+
this._registry = _registry;
|
|
8
|
+
this._scope = _scope;
|
|
9
|
+
this._hasParent = _hasParent;
|
|
10
|
+
this._isDisposed = _isDisposed;
|
|
35
11
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
default: return scope;
|
|
12
|
+
inspect() {
|
|
13
|
+
const snapshots = this.allBindingSnapshots();
|
|
14
|
+
return {
|
|
15
|
+
ownBindings: snapshots,
|
|
16
|
+
bindings: snapshots,
|
|
17
|
+
cachedSingletonCount: this._scope.getAllSingletons().size,
|
|
18
|
+
hasParent: this._hasParent,
|
|
19
|
+
isDisposed: this._isDisposed()
|
|
20
|
+
};
|
|
46
21
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
* Strips non-alphanumeric characters from a module name to produce a valid DOT subgraph identifier.
|
|
50
|
-
*/
|
|
51
|
-
function sanitizeClusterId(moduleName) {
|
|
52
|
-
return moduleName.replace(/[^0-9a-zA-Z_]/g, "_");
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Returns `true` when the label string belongs to a framework-internal registry key.
|
|
56
|
-
*/
|
|
57
|
-
function registryKeyLabelIsInternal(label) {
|
|
58
|
-
return label.startsWith("CODEFAST_DI_");
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Returns `true` when the registry key resolves to an internal framework label.
|
|
62
|
-
*/
|
|
63
|
-
function isInternalRegistryKey(key) {
|
|
64
|
-
return registryKeyLabelIsInternal(registryKeyLabel(key));
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Reads the container's registry and scope-cache state to produce debug snapshots
|
|
68
|
-
* and dependency-graph output (Graphviz DOT / typed JSON).
|
|
69
|
-
*
|
|
70
|
-
* Constructed internally by the container; advanced consumers can also construct it
|
|
71
|
-
* directly via the `@codefast/di/inspector` subpath export.
|
|
72
|
-
*/
|
|
73
|
-
var ContainerInspector = class {
|
|
74
|
-
constructor(ctx) {
|
|
75
|
-
this.ctx = ctx;
|
|
22
|
+
lookupBindings(token) {
|
|
23
|
+
return this._registry.getAll(token).map((b) => this._toSnapshot(b));
|
|
76
24
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
seen.add(binding.id);
|
|
90
|
-
const row = {
|
|
91
|
-
registryKeyLabel: registryLabel,
|
|
92
|
-
bindingId: binding.id,
|
|
93
|
-
kind: binding.kind,
|
|
94
|
-
scope: binding.scope,
|
|
95
|
-
activationStatus: activationStatusFor(binding, (bindingArg) => this.ctx.isBindingCached(bindingArg)),
|
|
96
|
-
hasConditionalConstraint: binding.constraint !== void 0
|
|
97
|
-
};
|
|
98
|
-
bindings.push(binding.moduleId === void 0 ? row : {
|
|
99
|
-
...row,
|
|
100
|
-
moduleId: binding.moduleId
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return { bindings };
|
|
25
|
+
has(token, hint, parentHas) {
|
|
26
|
+
const bindings = this._registry.getAll(token);
|
|
27
|
+
if (bindings.length > 0) if (hint !== void 0) {
|
|
28
|
+
if (selectBinding(bindings, hint, {
|
|
29
|
+
resolutionPath: [],
|
|
30
|
+
materializationStack: [],
|
|
31
|
+
parent: void 0,
|
|
32
|
+
ancestors: [],
|
|
33
|
+
currentResolveHint: hint
|
|
34
|
+
}, tokenName(token)) !== void 0) return true;
|
|
35
|
+
} else return true;
|
|
36
|
+
return parentHas?.() ?? false;
|
|
105
37
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"digraph codefast_di {",
|
|
118
|
-
" rankdir=LR;",
|
|
119
|
-
" graph [fontname=\"Arial\", fontsize=12, nodesep=0.8, ranksep=1.2];",
|
|
120
|
-
" node [fontname=\"Arial\", fontsize=12, shape=box, style=\"filled,rounded\", fillcolor=\"#F5F5F5\"];",
|
|
121
|
-
" edge [fontname=\"Arial\", fontsize=10];"
|
|
122
|
-
];
|
|
123
|
-
const byModule = /* @__PURE__ */ new Map();
|
|
124
|
-
for (const row of visibleRows) {
|
|
125
|
-
const key = row.moduleId;
|
|
126
|
-
const moduleGroup = byModule.get(key);
|
|
127
|
-
if (moduleGroup === void 0) byModule.set(key, [row]);
|
|
128
|
-
else moduleGroup.push(row);
|
|
129
|
-
}
|
|
130
|
-
const clusteredEntries = [...byModule.entries()].filter((entry) => entry[0] !== void 0);
|
|
131
|
-
const unclustered = byModule.get(void 0) ?? [];
|
|
132
|
-
const nodeAttributeLine = (row, indent) => {
|
|
133
|
-
const shape = nodeShapeForKind(row.kind);
|
|
134
|
-
const scopeAttrs = scopeVisualAttributes(row.scope);
|
|
135
|
-
const kindText = dotEscapeHtml(row.kind);
|
|
136
|
-
const nameText = dotEscapeHtml(row.registryKeyLabel);
|
|
137
|
-
const scopeText = dotEscapeHtml(`scope=${row.scope}`);
|
|
138
|
-
const whenRow = row.hasConditionalConstraint ? ` <TR><TD ALIGN="LEFT"><FONT POINT-SIZE="9" COLOR="#666666">when(...)</FONT></TD></TR>\n` : "";
|
|
139
|
-
const htmlLabel = [
|
|
140
|
-
"<",
|
|
141
|
-
" <TABLE BORDER=\"0\" CELLPADDING=\"4\" CELLSPACING=\"0\">",
|
|
142
|
-
` <TR><TD ALIGN="LEFT"><FONT POINT-SIZE="9" COLOR="#666666">${kindText}</FONT></TD></TR>`,
|
|
143
|
-
` <TR><TD ALIGN="LEFT"><B>${nameText}</B></TD></TR>`,
|
|
144
|
-
` <TR><TD ALIGN="LEFT"><FONT POINT-SIZE="9">${scopeText}</FONT></TD></TR>`,
|
|
145
|
-
whenRow.trimEnd(),
|
|
146
|
-
" </TABLE>",
|
|
147
|
-
" >"
|
|
148
|
-
].filter((line) => line.length > 0).join("\n");
|
|
149
|
-
return `${indent}"${row.bindingId}" [shape=${shape}, ${scopeAttrs}, label=${htmlLabel}];`;
|
|
150
|
-
};
|
|
151
|
-
for (const [moduleName, rows] of clusteredEntries) {
|
|
152
|
-
const clusterId = sanitizeClusterId(moduleName);
|
|
153
|
-
lines.push(` subgraph cluster_${clusterId} {`);
|
|
154
|
-
lines.push(` label="${dotEscapeLabel(moduleName)}";`);
|
|
155
|
-
lines.push(` style=filled;`);
|
|
156
|
-
lines.push(` fillcolor=lightgray;`);
|
|
157
|
-
for (const row of rows) lines.push(nodeAttributeLine(row, " "));
|
|
158
|
-
lines.push(` }`);
|
|
159
|
-
}
|
|
160
|
-
for (const row of unclustered) lines.push(nodeAttributeLine(row, " "));
|
|
161
|
-
const emittedNodeIds = new Set(visibleRows.map((row) => row.bindingId));
|
|
162
|
-
const edgeSeen = /* @__PURE__ */ new Set();
|
|
163
|
-
for (const registryKey of this.ctx.collectAllRegistryKeys()) {
|
|
164
|
-
if (hideInternals && isInternalRegistryKey(registryKey)) continue;
|
|
165
|
-
const list = this.ctx.lookupBindings(registryKey);
|
|
166
|
-
if (list === void 0) continue;
|
|
167
|
-
const pathStart = [registryKeyLabel(registryKey)];
|
|
168
|
-
for (const consumerBinding of list) {
|
|
169
|
-
if (hideInternals && !allowedBindingIds.has(consumerBinding.id)) continue;
|
|
170
|
-
const edges = collectStaticDependencyEdges(consumerBinding, (dependencyKey) => this.ctx.lookupBindings(dependencyKey), this.ctx.metadataReader, pathStart);
|
|
171
|
-
for (const edge of edges) {
|
|
172
|
-
if (hideInternals && (!allowedBindingIds.has(edge.fromBindingId) || !allowedBindingIds.has(edge.toBindingId))) continue;
|
|
173
|
-
const edgeKey = `${edge.fromBindingId}->${edge.toBindingId}:${edge.edgeKind}:${edge.injectHintLabel ?? ""}`;
|
|
174
|
-
if (edgeSeen.has(edgeKey)) continue;
|
|
175
|
-
edgeSeen.add(edgeKey);
|
|
176
|
-
if (!emittedNodeIds.has(edge.fromBindingId)) {
|
|
177
|
-
emittedNodeIds.add(edge.fromBindingId);
|
|
178
|
-
lines.push(` "${edge.fromBindingId}" [shape=box, style=dashed, label="(unlisted ${edge.fromBindingId})"];`);
|
|
179
|
-
}
|
|
180
|
-
if (!emittedNodeIds.has(edge.toBindingId)) {
|
|
181
|
-
emittedNodeIds.add(edge.toBindingId);
|
|
182
|
-
lines.push(` "${edge.toBindingId}" [shape=box, style=dashed, label="(unlisted ${edge.toBindingId})"];`);
|
|
183
|
-
}
|
|
184
|
-
const labelParts = [];
|
|
185
|
-
if (edge.injectHintLabel !== void 0) labelParts.push(edge.injectHintLabel);
|
|
186
|
-
labelParts.push(edge.edgeKind);
|
|
187
|
-
if (edge.toBindingConditional) labelParts.push("conditional");
|
|
188
|
-
const edgeLabel = dotEscapeLabel(labelParts.join(" | "));
|
|
189
|
-
const pathLabel = dotEscapeLabel(edge.resolutionPath.join(" -> "));
|
|
190
|
-
const edgeStyle = edge.isAliasEdge ? ", style=dashed" : "";
|
|
191
|
-
lines.push(` "${edge.fromBindingId}" -> "${edge.toBindingId}" [label="${edgeLabel}", xlabel="${pathLabel}"${edgeStyle}];`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
lines.push("}");
|
|
196
|
-
return lines.join("\n");
|
|
38
|
+
hasOwn(token, hint) {
|
|
39
|
+
const bindings = this._registry.getAll(token);
|
|
40
|
+
if (bindings.length === 0) return false;
|
|
41
|
+
if (hint !== void 0) return selectBinding(bindings, hint, {
|
|
42
|
+
resolutionPath: [],
|
|
43
|
+
materializationStack: [],
|
|
44
|
+
parent: void 0,
|
|
45
|
+
ancestors: [],
|
|
46
|
+
currentResolveHint: hint
|
|
47
|
+
}, tokenName(token)) !== void 0;
|
|
48
|
+
return true;
|
|
197
49
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return this.generateDotGraph(options);
|
|
50
|
+
allBindingSnapshots() {
|
|
51
|
+
return this._registry.allBindings().map((b) => this._toSnapshot(b));
|
|
201
52
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const snapshot = this.getSnapshot();
|
|
209
|
-
const visibleNodes = hideInternals ? snapshot.bindings.filter((row) => !registryKeyLabelIsInternal(row.registryKeyLabel)) : [...snapshot.bindings];
|
|
210
|
-
const allowedBindingIds = new Set(visibleNodes.map((row) => row.bindingId));
|
|
211
|
-
const edges = [];
|
|
212
|
-
const edgeSeen = /* @__PURE__ */ new Set();
|
|
213
|
-
for (const registryKey of this.ctx.collectAllRegistryKeys()) {
|
|
214
|
-
if (hideInternals && isInternalRegistryKey(registryKey)) continue;
|
|
215
|
-
const list = this.ctx.lookupBindings(registryKey);
|
|
216
|
-
if (list === void 0) continue;
|
|
217
|
-
const pathStart = [registryKeyLabel(registryKey)];
|
|
218
|
-
for (const consumerBinding of list) {
|
|
219
|
-
if (hideInternals && !allowedBindingIds.has(consumerBinding.id)) continue;
|
|
220
|
-
for (const edge of collectStaticDependencyEdges(consumerBinding, (dependencyKey) => this.ctx.lookupBindings(dependencyKey), this.ctx.metadataReader, pathStart)) {
|
|
221
|
-
if (hideInternals && (!allowedBindingIds.has(edge.fromBindingId) || !allowedBindingIds.has(edge.toBindingId))) continue;
|
|
222
|
-
const edgeKey = `${edge.fromBindingId}->${edge.toBindingId}:${edge.edgeKind}:${edge.injectHintLabel ?? ""}`;
|
|
223
|
-
if (edgeSeen.has(edgeKey)) continue;
|
|
224
|
-
edgeSeen.add(edgeKey);
|
|
225
|
-
edges.push(edge);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
53
|
+
_toSnapshot(b) {
|
|
54
|
+
const scope = effectiveBindingScope(b);
|
|
55
|
+
const slot = b.slot.name !== void 0 ? {
|
|
56
|
+
name: b.slot.name,
|
|
57
|
+
tags: b.slot.tags
|
|
58
|
+
} : { tags: b.slot.tags };
|
|
229
59
|
return {
|
|
230
|
-
|
|
231
|
-
|
|
60
|
+
tokenName: tokenName(b.token),
|
|
61
|
+
kind: b.kind,
|
|
62
|
+
scope,
|
|
63
|
+
slot,
|
|
64
|
+
id: b.id
|
|
232
65
|
};
|
|
233
66
|
}
|
|
234
|
-
/**
|
|
235
|
-
* Produces a JSON graph representation with `nodes` and `edges`.
|
|
236
|
-
* @internal Use `generateDependencyGraph({ format: "json" })` for typed output.
|
|
237
|
-
*/
|
|
238
|
-
generateDependencyGraphJson(options) {
|
|
239
|
-
const hideInternals = options?.hideInternals === true;
|
|
240
|
-
const snapshot = this.getSnapshot();
|
|
241
|
-
const visibleNodes = hideInternals ? snapshot.bindings.filter((row) => !registryKeyLabelIsInternal(row.registryKeyLabel)) : snapshot.bindings;
|
|
242
|
-
const allowedBindingIds = new Set(visibleNodes.map((row) => row.bindingId));
|
|
243
|
-
const edges = [];
|
|
244
|
-
const edgeSeen = /* @__PURE__ */ new Set();
|
|
245
|
-
for (const registryKey of this.ctx.collectAllRegistryKeys()) {
|
|
246
|
-
if (hideInternals && isInternalRegistryKey(registryKey)) continue;
|
|
247
|
-
const list = this.ctx.lookupBindings(registryKey);
|
|
248
|
-
if (list === void 0) continue;
|
|
249
|
-
const pathStart = [registryKeyLabel(registryKey)];
|
|
250
|
-
for (const consumerBinding of list) {
|
|
251
|
-
if (hideInternals && !allowedBindingIds.has(consumerBinding.id)) continue;
|
|
252
|
-
for (const edge of collectStaticDependencyEdges(consumerBinding, (dependencyKey) => this.ctx.lookupBindings(dependencyKey), this.ctx.metadataReader, pathStart)) {
|
|
253
|
-
if (hideInternals && (!allowedBindingIds.has(edge.fromBindingId) || !allowedBindingIds.has(edge.toBindingId))) continue;
|
|
254
|
-
const edgeKey = `${edge.fromBindingId}->${edge.toBindingId}:${edge.edgeKind}:${edge.injectHintLabel ?? ""}`;
|
|
255
|
-
if (edgeSeen.has(edgeKey)) continue;
|
|
256
|
-
edgeSeen.add(edgeKey);
|
|
257
|
-
edges.push(edge);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return JSON.stringify({
|
|
262
|
-
nodes: visibleNodes,
|
|
263
|
-
edges
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
67
|
};
|
|
267
68
|
//#endregion
|
|
268
|
-
export {
|
|
69
|
+
export { Inspector };
|