@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.
Files changed (66) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +54 -29
  3. package/dist/binding-scope.d.mts +11 -0
  4. package/dist/binding-scope.mjs +19 -0
  5. package/dist/binding-select.d.mts +8 -26
  6. package/dist/binding-select.mjs +56 -49
  7. package/dist/binding.d.mts +107 -327
  8. package/dist/binding.mjs +19 -324
  9. package/dist/constraints.d.mts +11 -29
  10. package/dist/constraints.mjs +32 -36
  11. package/dist/constructor-type.d.mts +17 -0
  12. package/dist/constructor-type.mjs +1 -0
  13. package/dist/container.d.mts +44 -128
  14. package/dist/container.mjs +664 -433
  15. package/dist/decorators/inject.d.mts +19 -50
  16. package/dist/decorators/inject.mjs +131 -93
  17. package/dist/decorators/injectable.d.mts +16 -37
  18. package/dist/decorators/injectable.mjs +47 -66
  19. package/dist/decorators/lifecycle-decorators.d.mts +2 -22
  20. package/dist/decorators/lifecycle-decorators.mjs +81 -37
  21. package/dist/dependency-graph.d.mts +24 -54
  22. package/dist/dependency-graph.mjs +51 -153
  23. package/dist/environment.d.mts +38 -12
  24. package/dist/environment.mjs +82 -16
  25. package/dist/errors.d.mts +69 -188
  26. package/dist/errors.mjs +92 -219
  27. package/dist/graph-adapters/cytoscape.d.mts +24 -0
  28. package/dist/graph-adapters/cytoscape.mjs +23 -0
  29. package/dist/graph-adapters/dot.d.mts +6 -0
  30. package/dist/graph-adapters/dot.mjs +17 -0
  31. package/dist/graph-adapters/reactflow.d.mts +29 -0
  32. package/dist/graph-adapters/reactflow.mjs +29 -0
  33. package/dist/graph-adapters/types.d.mts +2 -0
  34. package/dist/graph-adapters/types.mjs +1 -0
  35. package/dist/index.d.mts +16 -9
  36. package/dist/index.mjs +8 -5
  37. package/dist/inspector.d.mts +34 -95
  38. package/dist/inspector.mjs +57 -256
  39. package/dist/lifecycle.d.mts +20 -53
  40. package/dist/lifecycle.mjs +129 -99
  41. package/dist/metadata/metadata-keys.d.mts +9 -26
  42. package/dist/metadata/metadata-keys.mjs +7 -28
  43. package/dist/metadata/metadata-reader-token.d.mts +7 -0
  44. package/dist/metadata/metadata-reader-token.mjs +5 -0
  45. package/dist/metadata/metadata-types.d.mts +22 -71
  46. package/dist/metadata/symbol-metadata-reader.d.mts +10 -26
  47. package/dist/metadata/symbol-metadata-reader.mjs +32 -45
  48. package/dist/module.d.mts +30 -84
  49. package/dist/module.mjs +26 -72
  50. package/dist/registry.d.mts +32 -63
  51. package/dist/registry.mjs +131 -82
  52. package/dist/resolve-options.d.mts +18 -0
  53. package/dist/resolve-options.mjs +22 -0
  54. package/dist/resolver.d.mts +67 -190
  55. package/dist/resolver.mjs +715 -424
  56. package/dist/scope.d.mts +19 -102
  57. package/dist/scope.mjs +37 -192
  58. package/dist/token.d.mts +8 -22
  59. package/dist/token.mjs +9 -11
  60. package/dist/types.d.mts +48 -0
  61. package/dist/types.mjs +1 -0
  62. package/package.json +52 -14
  63. package/dist/metadata/param-registry.d.mts +0 -16
  64. package/dist/metadata/param-registry.mjs +0 -31
  65. package/dist/scope-validation.d.mts +0 -21
  66. 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,6 @@
1
+ import { ContainerGraphJson } from "../dependency-graph.mjs";
2
+
3
+ //#region src/graph-adapters/dot.d.ts
4
+ declare function toDotGraph(graph: ContainerGraphJson): string;
5
+ //#endregion
6
+ export { toDotGraph };
@@ -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,2 @@
1
+ import { ContainerGraphJson, GraphEdge, GraphNode, GraphOptions } from "../dependency-graph.mjs";
2
+ export { type ContainerGraphJson, type GraphEdge, type GraphNode, type GraphOptions };
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.mts CHANGED
@@ -1,10 +1,17 @@
1
- import { Token, TokenValue, token } from "./token.mjs";
2
- import { ActivationHandler, BindingBuilder, BindingIdentifier, BindingScope, ConstraintContext, Constructor, DeactivationHandler, ResolveOptions } from "./binding.mjs";
3
- import { ContainerGraphJson, ContainerSnapshot } from "./inspector.mjs";
4
- import { AsyncModule, AsyncModuleBuilder, Module, ModuleBuilder } from "./module.mjs";
5
- import { Container } from "./container.mjs";
6
- import { InjectOptions, inject, injectAll, isInjectionDescriptor, optional } from "./decorators/inject.mjs";
7
- import { InjectableDependency, getAutoRegistered, injectable } from "./decorators/injectable.mjs";
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
- export { type ActivationHandler, AsyncModule, type AsyncModuleBuilder, AsyncModuleLoadError, AsyncResolutionError, type BindingBuilder, type BindingIdentifier, type BindingScope, CircularDependencyError, type ConstraintContext, type Constructor, Container, type ContainerGraphJson, type ContainerSnapshot, type DeactivationHandler, DiError, type InjectOptions, type InjectableDependency, InternalError, MissingMetadataError, Module, type ModuleBuilder, NoMatchingBindingError, type ResolveOptions, type ScopeViolationDetails, ScopeViolationError, type Token, TokenNotBoundError, type TokenValue, getAutoRegistered, inject, injectAll, injectable, isInjectionDescriptor, optional, postConstruct, preDestroy, token };
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 { AsyncModuleLoadError, AsyncResolutionError, CircularDependencyError, DiError, InternalError, MissingMetadataError, NoMatchingBindingError, ScopeViolationError, TokenNotBoundError } from "./errors.mjs";
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 { getAutoRegistered, injectable } from "./decorators/injectable.mjs";
4
- import { AsyncModule, Module } from "./module.mjs";
7
+ import { AsyncModule, Module, SyncModule } from "./module.mjs";
5
8
  import { Container } from "./container.mjs";
6
- import { token } from "./token.mjs";
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, getAutoRegistered, inject, injectAll, injectable, isInjectionDescriptor, optional, postConstruct, preDestroy, token };
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 };
@@ -1,100 +1,39 @@
1
- import { RegistryKey } from "./registry.mjs";
2
- import { Binding, BindingIdentifier, BindingScope } from "./binding.mjs";
3
- import { MetadataReader } from "./metadata/metadata-types.mjs";
4
- import { collectStaticDependencyEdges } from "./dependency-graph.mjs";
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
- * Whether a singleton/scoped binding's instance is currently held in the scope cache.
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 activationStatus: BindingActivationStatus;
20
- /**
21
- * True when {@link BindingBuilder.when} was used (runtime predicate; static graph may still show edges).
22
- */
23
- readonly hasConditionalConstraint: boolean;
24
- readonly moduleId?: string;
25
- };
26
- /**
27
- * Full debug snapshot returned by {@link Container.inspect}.
28
- */
29
- type ContainerSnapshot = {
30
- readonly bindings: readonly ContainerBindingSnapshot[];
31
- };
32
- /**
33
- * Structured dependency graph returned by {@link Container.generateDependencyGraph} with `format: "json"`.
34
- */
35
- type ContainerGraphJson = {
36
- nodes: ContainerBindingSnapshot[];
37
- edges: ReturnType<typeof collectStaticDependencyEdges>[number][];
38
- };
39
- /**
40
- * Read-only view of the container internals exposed to {@link ContainerInspector}.
41
- */
42
- type ContainerInspectorContext = {
43
- collectAllRegistryKeys(): readonly RegistryKey[];
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 { BindingActivationStatus, ContainerBindingSnapshot, ContainerGraphJson, ContainerInspector, ContainerInspectorContext, ContainerSnapshot, DotGraphOptions };
39
+ export { BindingSnapshot, ContainerSnapshot, Inspector };
@@ -1,268 +1,69 @@
1
- import { registryKeyLabel } from "./binding-select.mjs";
2
- import { collectStaticDependencyEdges } from "./dependency-graph.mjs";
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
- * Maps a binding's scope and cache state to a {@link BindingActivationStatus} label.
6
- */
7
- function activationStatusFor(binding, isCached) {
8
- if (binding.scope === "transient") return "transient";
9
- return isCached(binding) ? "cached" : "not-cached";
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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
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
- * Returns DOT fill-color and style attributes that visually distinguish binding scopes.
39
- */
40
- function scopeVisualAttributes(scope) {
41
- switch (scope) {
42
- case "singleton": return "style=\"filled\", fillcolor=\"#FFD700\", penwidth=2";
43
- case "scoped": return "style=\"filled\", fillcolor=\"#ADD8E6\"";
44
- case "transient": return "style=\"dashed\"";
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
- * Collects all registered bindings into a flat, serialisable snapshot.
79
- */
80
- getSnapshot() {
81
- const bindings = [];
82
- const seen = /* @__PURE__ */ new Set();
83
- for (const registryKey of this.ctx.collectAllRegistryKeys()) {
84
- const list = this.ctx.lookupBindings(registryKey);
85
- if (list === void 0 || list.length === 0) continue;
86
- const registryLabel = registryKeyLabel(registryKey);
87
- for (const binding of list) {
88
- if (seen.has(binding.id)) continue;
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
- * Produces a Graphviz `digraph` string with HTML-label nodes and styled edges.
108
- * Cycles are represented as ordinary edges (Graphviz renders them correctly).
109
- * Pass `hideInternals: true` to suppress `CODEFAST_DI_`-prefixed tokens.
110
- */
111
- generateDotGraph(options) {
112
- const hideInternals = options?.hideInternals === true;
113
- const fullSnapshot = this.getSnapshot();
114
- const visibleRows = hideInternals ? fullSnapshot.bindings.filter((row) => !registryKeyLabelIsInternal(row.registryKeyLabel)) : fullSnapshot.bindings;
115
- const allowedBindingIds = new Set(visibleRows.map((row) => row.bindingId));
116
- const lines = [
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
- generateDependencyGraph(options) {
199
- if (options?.format === "json") return this.generateDependencyGraphJsonTyped(options);
200
- return this.generateDotGraph(options);
50
+ allBindingSnapshots() {
51
+ return this._registry.allBindings().map((b) => this._toSnapshot(b));
201
52
  }
202
- /**
203
- * Builds the typed JSON graph (nodes + edges) used by the `"json"` format path.
204
- * Applies the same `hideInternals` / deduplication logic as {@link generateDotGraph}.
205
- */
206
- generateDependencyGraphJsonTyped(options) {
207
- const hideInternals = options?.hideInternals === true;
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
- nodes: visibleNodes,
231
- edges
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 { ContainerInspector };
69
+ export { Inspector };