@codefast/di 0.10.0 → 0.11.0
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 +293 -0
- package/README.md +71 -7
- package/dist/ambient/active-container.d.ts +13 -11
- package/dist/ambient/active-container.js +8 -6
- package/dist/container/binding-builders.d.ts +25 -7
- package/dist/container/binding-builders.js +110 -42
- package/dist/container/container.d.ts +8 -8
- package/dist/container/container.js +244 -67
- package/dist/core/binding-declaration.d.ts +120 -0
- package/dist/core/binding-declaration.js +186 -0
- package/dist/core/binding-scope.d.ts +2 -2
- package/dist/core/binding.d.ts +61 -9
- package/dist/core/binding.js +49 -2
- package/dist/core/constraint-requirement.d.ts +1 -1
- package/dist/core/module.d.ts +10 -7
- package/dist/core/module.js +17 -3
- package/dist/core/registry.d.ts +17 -13
- package/dist/core/registry.js +155 -59
- package/dist/core/state-epoch.d.ts +18 -1
- package/dist/core/state-epoch.js +17 -0
- package/dist/core/tag.js +1 -1
- package/dist/core/token.d.ts +1 -1
- package/dist/core/types.d.ts +5 -5
- package/dist/decorators/decorator-metadata.d.ts +9 -0
- package/dist/decorators/decorator-metadata.js +20 -0
- package/dist/decorators/inject.d.ts +3 -3
- package/dist/decorators/inject.js +7 -6
- package/dist/decorators/injectable.d.ts +2 -2
- package/dist/decorators/injectable.js +5 -3
- package/dist/decorators/lifecycle-decorators.js +9 -3
- package/dist/errors/errors.d.ts +85 -14
- package/dist/errors/errors.js +100 -20
- package/dist/index.d.ts +37 -35
- package/dist/index.js +20 -19
- package/dist/injection/descriptor.d.ts +3 -3
- package/dist/injection/descriptor.js +3 -7
- package/dist/injection/resolve-options.d.ts +3 -3
- package/dist/injection/resolve-options.js +7 -5
- package/dist/introspection/dependency-graph.d.ts +10 -5
- package/dist/introspection/dependency-graph.js +51 -28
- package/dist/introspection/graph-adapters/cytoscape.d.ts +1 -1
- package/dist/introspection/graph-adapters/dot.d.ts +1 -1
- package/dist/introspection/graph-adapters/mermaid.d.ts +1 -1
- package/dist/introspection/graph-adapters/reactflow.d.ts +1 -1
- package/dist/introspection/graph-adapters/reactflow.js +6 -4
- package/dist/introspection/inspector.d.ts +4 -4
- package/dist/introspection/inspector.js +9 -12
- package/dist/lifecycle/lifecycle-manager.d.ts +4 -4
- package/dist/lifecycle/lifecycle-manager.js +18 -6
- package/dist/lifecycle/scope-manager.d.ts +2 -2
- package/dist/lifecycle/scope-manager.js +31 -13
- package/dist/metadata/metadata-reader-token.d.ts +2 -2
- package/dist/metadata/metadata-reader-token.js +1 -1
- package/dist/metadata/metadata-types.d.ts +3 -3
- package/dist/metadata/symbol-metadata-reader.d.ts +3 -3
- package/dist/metadata/symbol-metadata-reader.js +1 -1
- package/dist/metadata/verifying-metadata-reader.d.ts +5 -4
- package/dist/metadata/verifying-metadata-reader.js +30 -8
- package/dist/resolution/async-fan-out.d.ts +12 -0
- package/dist/resolution/async-fan-out.js +26 -0
- package/dist/resolution/cache/activation-need.d.ts +4 -5
- package/dist/resolution/cache/activation-need.js +11 -18
- package/dist/resolution/cache/binding-lookup-cache.d.ts +5 -12
- package/dist/resolution/cache/binding-lookup-cache.js +32 -19
- package/dist/resolution/cache/class-introspector.d.ts +16 -5
- package/dist/resolution/cache/class-introspector.js +73 -56
- package/dist/resolution/context.d.ts +17 -25
- package/dist/resolution/context.js +47 -56
- package/dist/resolution/path/resolution-path.d.ts +48 -13
- package/dist/resolution/path/resolution-path.js +90 -39
- package/dist/resolution/plan/instantiation-plan.d.ts +5 -5
- package/dist/resolution/plan/instantiation-plan.js +66 -26
- package/dist/resolution/plan/plan-codegen.d.ts +10 -7
- package/dist/resolution/plan/plan-codegen.js +62 -34
- package/dist/resolution/resolver.d.ts +16 -17
- package/dist/resolution/resolver.js +316 -273
- package/dist/resolution/select/binding-select.d.ts +6 -5
- package/dist/resolution/select/binding-select.js +5 -4
- package/dist/resolution/select/constraints.d.ts +3 -3
- package/dist/resolution/select/constraints.js +4 -4
- package/package.json +14 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,35 +1,37 @@
|
|
|
1
|
-
export type { ActivationHandler, BindingConstraint, BindingIdentifier, BindingKind, BindingScope, BindingTag, ConstraintContext, Constructor, DependencyKey, DeactivationHandler, ResolutionFrame, ResolveOptions, ResolutionContext, TokenValue, } from "
|
|
2
|
-
export { token, tokenName } from "
|
|
3
|
-
export type { SlotNamesOf, Token } from "
|
|
4
|
-
export { coversTagKeys, NO_TAG_KEYS, slotName, tag, tagKeyMaskOf } from "
|
|
5
|
-
export type { TagKey, TagKeyMask } from "
|
|
6
|
-
export type { AliasBindingBuilder, BindToBuilder, BindingBuilder, ConstantBindingBuilder, ScopedBindingBuilder, SingletonBindingBuilder, SingletonLifecycleBuilder, SlotConstrainedBuilder, TransientBindingBuilder, } from "
|
|
7
|
-
export { Container } from "
|
|
8
|
-
export type { Container as ContainerInterface, ContainerOptions, ContainerStatic } from "
|
|
9
|
-
export { getActiveContainer, runWithContainer } from "
|
|
10
|
-
export { bindingSlotToResolveOptions, injectionSlotToResolveOptions, resolveOptionsForSlot, } from "
|
|
11
|
-
export type { DependencySlot } from "
|
|
12
|
-
export type { BindingSnapshot, ContainerSnapshot } from "
|
|
13
|
-
export type { ContainerGraphJson, GraphEdge, GraphNode, GraphOptions } from "
|
|
14
|
-
export { AsyncModule, isSyncModule, Module, SyncModule } from "
|
|
15
|
-
export type { AsyncModuleBuilder, ModuleBuilder } from "
|
|
16
|
-
export {
|
|
17
|
-
export {
|
|
18
|
-
export
|
|
19
|
-
export {
|
|
20
|
-
export type {
|
|
21
|
-
export {
|
|
22
|
-
export {
|
|
23
|
-
export
|
|
24
|
-
export {
|
|
25
|
-
export type {
|
|
26
|
-
export {
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
29
|
-
export
|
|
30
|
-
export {
|
|
31
|
-
export {
|
|
32
|
-
export
|
|
33
|
-
export {
|
|
34
|
-
export type {
|
|
35
|
-
export {
|
|
1
|
+
export type { ActivationHandler, BindingConstraint, BindingIdentifier, BindingKind, BindingScope, BindingTag, ConstraintContext, Constructor, DependencyKey, DeactivationHandler, ResolutionFrame, ResolveOptions, ResolutionContext, TokenValue, } from "#core/types";
|
|
2
|
+
export { token, tokenName } from "#core/token";
|
|
3
|
+
export type { SlotNamesOf, Token } from "#core/token";
|
|
4
|
+
export { coversTagKeys, NO_TAG_KEYS, slotName, tag, tagKeyMaskOf } from "#core/tag";
|
|
5
|
+
export type { TagKey, TagKeyMask } from "#core/tag";
|
|
6
|
+
export type { AliasBindingBuilder, BindToBuilder, BindingBuilder, ConstantBindingBuilder, ScopedBindingBuilder, SingletonBindingBuilder, SingletonLifecycleBuilder, SlotConstrainedBuilder, TransientBindingBuilder, } from "#core/binding";
|
|
7
|
+
export { Container } from "#container/container";
|
|
8
|
+
export type { Container as ContainerInterface, ContainerOptions, ContainerStatic } from "#container/container";
|
|
9
|
+
export { getActiveContainer, runWithContainer } from "#ambient/active-container";
|
|
10
|
+
export { bindingSlotToResolveOptions, injectionSlotToResolveOptions, resolveOptionsForSlot, } from "#injection/resolve-options";
|
|
11
|
+
export type { DependencySlot } from "#injection/resolve-options";
|
|
12
|
+
export type { BindingSnapshot, ContainerSnapshot } from "#introspection/inspector";
|
|
13
|
+
export type { ContainerGraphJson, GraphEdge, GraphNode, GraphOptions } from "#introspection/dependency-graph";
|
|
14
|
+
export { AsyncModule, isSyncModule, Module, SyncModule } from "#core/module";
|
|
15
|
+
export type { AsyncModuleBuilder, ModuleBuilder } from "#core/module";
|
|
16
|
+
export { binding } from "#core/binding-declaration";
|
|
17
|
+
export type { BindingDeclaration, BindingDefinition } from "#core/binding-declaration";
|
|
18
|
+
export { inject } from "#decorators/inject";
|
|
19
|
+
export { injectAll, isInjectionDescriptor, optional } from "#injection/descriptor";
|
|
20
|
+
export type { InjectionDescriptor, InjectOptions } from "#injection/descriptor";
|
|
21
|
+
export { injectable } from "#decorators/injectable";
|
|
22
|
+
export type { InjectableDependency, InjectableOptions } from "#decorators/injectable";
|
|
23
|
+
export { postConstruct, preDestroy } from "#decorators/lifecycle-decorators";
|
|
24
|
+
export { createAutoRegisterRegistry } from "#decorators/injectable";
|
|
25
|
+
export type { AutoRegisterRegistry } from "#decorators/injectable";
|
|
26
|
+
export { MetadataReaderToken } from "#metadata/metadata-reader-token";
|
|
27
|
+
export type { ConstructorMetadata, LifecycleMetadata, MetadataReader, MutableLifecycleMetadata, ParamMetadata, } from "#metadata/metadata-types";
|
|
28
|
+
export { defaultMetadataReader, SymbolMetadataReader } from "#metadata/symbol-metadata-reader";
|
|
29
|
+
export { whenAnyAncestorIs, whenAnyAncestorNamed, whenAnyAncestorTagged, whenAnyAncestorTaggedAll, whenNoAncestorIs, whenNoParentIs, whenParentIs, whenParentNamed, whenParentTagged, whenParentTaggedAll, } from "#resolution/select/constraints";
|
|
30
|
+
export { AmbiguousBindingError, AsyncActivationError, AsyncDeactivationError, AsyncModuleLoadError, AsyncResolutionError, ChainAlreadyRegisteredError, ChainNotRegisteredError, ManyBindingSlotError, CircularDependencyError, DiError, DisposedContainerError, InternalError, InvalidBindingDeclarationError, InvalidMetadataError, MissingContainerContextError, MissingDecoratorMetadataError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, RebindUnboundTokenError, ScopeViolationError, SelfBindingRequiresClassError, StaticMemberDecoratorError, SymbolKeyedLifecycleError, SyncDisposalNotSupportedError, EmptyTagCriteriaError, TokenNotBoundError, UnreachableConstraintError, UnreachableLifecycleHookError, } from "#errors/errors";
|
|
31
|
+
export type { ScopeViolationDetails } from "#errors/errors";
|
|
32
|
+
export { toDotGraph } from "#introspection/graph-adapters/dot";
|
|
33
|
+
export { toCytoscapeGraph } from "#introspection/graph-adapters/cytoscape";
|
|
34
|
+
export type { CytoscapeEdge, CytoscapeElements, CytoscapeNode } from "#introspection/graph-adapters/cytoscape";
|
|
35
|
+
export { toReactFlowGraph } from "#introspection/graph-adapters/reactflow";
|
|
36
|
+
export type { ReactFlowEdge, ReactFlowGraph, ReactFlowNode } from "#introspection/graph-adapters/reactflow";
|
|
37
|
+
export { toMermaidGraph } from "#introspection/graph-adapters/mermaid";
|
package/dist/index.js
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
// Token
|
|
2
|
-
export { token, tokenName } from "
|
|
2
|
+
export { token, tokenName } from "#core/token";
|
|
3
3
|
// Tag — the interned slot criteria a `whenTagged` and a resolve both take
|
|
4
|
-
export { coversTagKeys, NO_TAG_KEYS, slotName, tag, tagKeyMaskOf } from "
|
|
4
|
+
export { coversTagKeys, NO_TAG_KEYS, slotName, tag, tagKeyMaskOf } from "#core/tag";
|
|
5
5
|
// Container
|
|
6
|
-
export { Container } from "
|
|
6
|
+
export { Container } from "#container/container";
|
|
7
7
|
// Ambient container — the context an `@inject` accessor initializer resolves from. `resolution/context`
|
|
8
8
|
// stays internal: it hands out resolver callbacks, not public values.
|
|
9
|
-
export { getActiveContainer, runWithContainer } from "
|
|
9
|
+
export { getActiveContainer, runWithContainer } from "#ambient/active-container";
|
|
10
10
|
// `effectiveBindingScope` is deliberately absent: it reads a `Binding`, which is internal, and no
|
|
11
11
|
// public API hands one out. `BindingSnapshot.scope` and `GraphNode.scope` are the public answers.
|
|
12
|
-
export { bindingSlotToResolveOptions, injectionSlotToResolveOptions, resolveOptionsForSlot, } from "
|
|
12
|
+
export { bindingSlotToResolveOptions, injectionSlotToResolveOptions, resolveOptionsForSlot, } from "#injection/resolve-options";
|
|
13
13
|
// Module
|
|
14
|
-
export { AsyncModule, isSyncModule, Module, SyncModule } from "
|
|
14
|
+
export { AsyncModule, isSyncModule, Module, SyncModule } from "#core/module";
|
|
15
|
+
export { binding } from "#core/binding-declaration";
|
|
15
16
|
// Decorators
|
|
16
|
-
export { inject } from "
|
|
17
|
-
export { injectAll, isInjectionDescriptor, optional } from "
|
|
18
|
-
export { injectable } from "
|
|
19
|
-
export { postConstruct, preDestroy } from "
|
|
17
|
+
export { inject } from "#decorators/inject";
|
|
18
|
+
export { injectAll, isInjectionDescriptor, optional } from "#injection/descriptor";
|
|
19
|
+
export { injectable } from "#decorators/injectable";
|
|
20
|
+
export { postConstruct, preDestroy } from "#decorators/lifecycle-decorators";
|
|
20
21
|
// Auto-register
|
|
21
|
-
export { createAutoRegisterRegistry } from "
|
|
22
|
+
export { createAutoRegisterRegistry } from "#decorators/injectable";
|
|
22
23
|
// MetadataReader — everything a consumer needs to write one and pass it to Container.create()
|
|
23
|
-
export { MetadataReaderToken } from "
|
|
24
|
-
export { defaultMetadataReader, SymbolMetadataReader } from "
|
|
24
|
+
export { MetadataReaderToken } from "#metadata/metadata-reader-token";
|
|
25
|
+
export { defaultMetadataReader, SymbolMetadataReader } from "#metadata/symbol-metadata-reader";
|
|
25
26
|
// Constraints — contextual injection predicates for .when()
|
|
26
|
-
export { whenAnyAncestorIs, whenAnyAncestorNamed, whenAnyAncestorTagged, whenAnyAncestorTaggedAll, whenNoAncestorIs, whenNoParentIs, whenParentIs, whenParentNamed, whenParentTagged, whenParentTaggedAll, } from "
|
|
27
|
+
export { whenAnyAncestorIs, whenAnyAncestorNamed, whenAnyAncestorTagged, whenAnyAncestorTaggedAll, whenNoAncestorIs, whenNoParentIs, whenParentIs, whenParentNamed, whenParentTagged, whenParentTaggedAll, } from "#resolution/select/constraints";
|
|
27
28
|
// Errors
|
|
28
|
-
export { AmbiguousBindingError, AsyncActivationError, AsyncDeactivationError, AsyncModuleLoadError, AsyncResolutionError, ChainAlreadyRegisteredError, ChainNotRegisteredError, ManyBindingSlotError, CircularDependencyError, DiError, DisposedContainerError, InternalError, InvalidMetadataError, MissingContainerContextError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, RebindUnboundTokenError, ScopeViolationError, SelfBindingRequiresClassError, StaticMemberDecoratorError, SyncDisposalNotSupportedError, EmptyTagCriteriaError, TokenNotBoundError, UnreachableConstraintError, UnreachableLifecycleHookError, } from "
|
|
29
|
+
export { AmbiguousBindingError, AsyncActivationError, AsyncDeactivationError, AsyncModuleLoadError, AsyncResolutionError, ChainAlreadyRegisteredError, ChainNotRegisteredError, ManyBindingSlotError, CircularDependencyError, DiError, DisposedContainerError, InternalError, InvalidBindingDeclarationError, InvalidMetadataError, MissingContainerContextError, MissingDecoratorMetadataError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, RebindUnboundTokenError, ScopeViolationError, SelfBindingRequiresClassError, StaticMemberDecoratorError, SymbolKeyedLifecycleError, SyncDisposalNotSupportedError, EmptyTagCriteriaError, TokenNotBoundError, UnreachableConstraintError, UnreachableLifecycleHookError, } from "#errors/errors";
|
|
29
30
|
// Graph adapters — render `generateDependencyGraph()` output for common viewers
|
|
30
|
-
export { toDotGraph } from "
|
|
31
|
-
export { toCytoscapeGraph } from "
|
|
32
|
-
export { toReactFlowGraph } from "
|
|
33
|
-
export { toMermaidGraph } from "
|
|
31
|
+
export { toDotGraph } from "#introspection/graph-adapters/dot";
|
|
32
|
+
export { toCytoscapeGraph } from "#introspection/graph-adapters/cytoscape";
|
|
33
|
+
export { toReactFlowGraph } from "#introspection/graph-adapters/reactflow";
|
|
34
|
+
export { toMermaidGraph } from "#introspection/graph-adapters/mermaid";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The one shape every declared dependency is normalised to, whatever channel declared it. */
|
|
2
|
-
import type { Token } from "
|
|
3
|
-
import type { BindingTag, Constructor, TokenValue } from "
|
|
4
|
-
import type { DependencySlot } from "
|
|
2
|
+
import type { Token } from "#core/token";
|
|
3
|
+
import type { BindingTag, Constructor, TokenValue } from "#core/types";
|
|
4
|
+
import type { DependencySlot } from "#injection/resolve-options";
|
|
5
5
|
/**
|
|
6
6
|
* Slot-selection options — a name and tags — a declared dependency narrows its binding with.
|
|
7
7
|
*
|
|
@@ -4,12 +4,8 @@
|
|
|
4
4
|
* @since 0.3.16-canary.0
|
|
5
5
|
*/
|
|
6
6
|
export function isInjectionDescriptor(value) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
const type = typeof value;
|
|
11
|
-
// inject() returns a function (dual-role), so must check both object and function
|
|
12
|
-
if (type !== "object" && type !== "function") {
|
|
7
|
+
// inject() returns a function (dual-role), so both an object and a function qualify.
|
|
8
|
+
if ((typeof value !== "object" && typeof value !== "function") || value === null) {
|
|
13
9
|
return false;
|
|
14
10
|
}
|
|
15
11
|
return ("token" in value &&
|
|
@@ -106,7 +102,7 @@ export function buildInjectionDescriptor(token, options) {
|
|
|
106
102
|
*/
|
|
107
103
|
export function optional(token, options) {
|
|
108
104
|
return withOptions({
|
|
109
|
-
token
|
|
105
|
+
token,
|
|
110
106
|
optional: true,
|
|
111
107
|
multi: false,
|
|
112
108
|
}, options);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { BindingTag } from "
|
|
2
|
-
import type { Token } from "
|
|
3
|
-
import type { Constructor, ResolveOptions } from "
|
|
1
|
+
import type { BindingTag } from "#core/tag";
|
|
2
|
+
import type { Token } from "#core/token";
|
|
3
|
+
import type { Constructor, ResolveOptions } from "#core/types";
|
|
4
4
|
/**
|
|
5
5
|
* What one resolvable dependency declares.
|
|
6
6
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { slotName, slotNameCriterionOf } from "
|
|
1
|
+
import { slotName, slotNameCriterionOf } from "#core/tag";
|
|
2
2
|
/**
|
|
3
3
|
* The lone criterion of a request that carries exactly one, whatever its spelling — the shape the
|
|
4
4
|
* registry has a direct index for.
|
|
@@ -49,15 +49,17 @@ function loneNameCriterionOf(options) {
|
|
|
49
49
|
}
|
|
50
50
|
/** Shared core: build a ResolveOptions from already-normalised name + tags. */
|
|
51
51
|
function buildOptions(name, tags) {
|
|
52
|
-
|
|
52
|
+
// An empty criterion list selects nothing, so it states no criterion at all.
|
|
53
|
+
const criteria = tags !== undefined && tags.length > 0 ? tags : undefined;
|
|
54
|
+
if (name === undefined && criteria === undefined) {
|
|
53
55
|
return undefined;
|
|
54
56
|
}
|
|
55
57
|
const options = {};
|
|
56
58
|
if (name !== undefined) {
|
|
57
59
|
options.name = name;
|
|
58
60
|
}
|
|
59
|
-
if (
|
|
60
|
-
options.tags =
|
|
61
|
+
if (criteria !== undefined) {
|
|
62
|
+
options.tags = criteria;
|
|
61
63
|
}
|
|
62
64
|
return options;
|
|
63
65
|
}
|
|
@@ -85,7 +87,7 @@ const MEMOIZED_SINGLE_CRITERION = Symbol("di:single-criterion");
|
|
|
85
87
|
*/
|
|
86
88
|
export function resolveOptionsForSlot(injectionSlot) {
|
|
87
89
|
const { name, tags } = injectionSlot;
|
|
88
|
-
if (name === undefined && tags === undefined) {
|
|
90
|
+
if (name === undefined && (tags === undefined || tags.length === 0)) {
|
|
89
91
|
return undefined;
|
|
90
92
|
}
|
|
91
93
|
const slot = injectionSlot;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { BindingRegistry } from "
|
|
2
|
-
import type { BindingKind, BindingScope } from "
|
|
3
|
-
import type { MetadataReader } from "
|
|
1
|
+
import type { BindingRegistry } from "#core/registry";
|
|
2
|
+
import type { BindingKind, BindingScope } from "#core/types";
|
|
3
|
+
import type { MetadataReader } from "#metadata/metadata-types";
|
|
4
4
|
/**
|
|
5
5
|
* @remarks `kind`/`scope` are `"unbound"` for the placeholder node an optional, currently
|
|
6
6
|
* unsatisfied dependency points at.
|
|
@@ -49,8 +49,13 @@ export interface GraphOptions {
|
|
|
49
49
|
readonly includeParent?: boolean | undefined;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
* Builds the JSON dependency graph of a registry's bindings, optionally including
|
|
52
|
+
* Builds the JSON dependency graph of a registry's bindings, optionally including its ancestors'.
|
|
53
|
+
*
|
|
54
|
+
* @param registry - The registry whose bindings the graph is for.
|
|
55
|
+
* @param metadataReader - The reader class dependencies are read through.
|
|
56
|
+
* @param options - Whether the ancestors' bindings join the graph.
|
|
57
|
+
* @param ancestorRegistries - The ancestor containers' registries, nearest first.
|
|
53
58
|
*
|
|
54
59
|
* @since 0.3.16-canary.0
|
|
55
60
|
*/
|
|
56
|
-
export declare function buildDependencyGraph(registry: BindingRegistry, metadataReader: MetadataReader, options: GraphOptions | undefined,
|
|
61
|
+
export declare function buildDependencyGraph(registry: BindingRegistry, metadataReader: MetadataReader, options: GraphOptions | undefined, ancestorRegistries?: ReadonlyArray<BindingRegistry>): ContainerGraphJson;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { stringifyTagValue } from "#core/binding";
|
|
2
|
+
import { effectiveBindingScope } from "#core/binding-scope";
|
|
3
|
+
import { slotName } from "#core/tag";
|
|
4
|
+
import { tokenName } from "#core/token";
|
|
5
|
+
import { bindingSlotToResolveOptions } from "#injection/resolve-options";
|
|
6
|
+
import { matchesSlot } from "#resolution/select/binding-select";
|
|
6
7
|
// ── Builder ──────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
7
8
|
// Tokens are compared by object identity, and a name is free to repeat, so the graph mints its
|
|
8
9
|
// own per-process key. Weakly held: a discarded token takes its key with it.
|
|
@@ -41,18 +42,31 @@ function edgeLabel(ref, index) {
|
|
|
41
42
|
const criterion = name !== undefined
|
|
42
43
|
? `name:${name}`
|
|
43
44
|
: firstPlainTag !== undefined
|
|
44
|
-
? `tag:${firstPlainTag.key.name}=${
|
|
45
|
+
? `tag:${firstPlainTag.key.name}=${stringifyTagValue(firstPlainTag.value)}`
|
|
45
46
|
: `[${index}]`;
|
|
46
47
|
return ref.optional ? `${criterion} optional` : criterion;
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
/**
|
|
50
|
+
* The bindings a dependency reaches, found the way resolution finds them.
|
|
51
|
+
*
|
|
52
|
+
* @remarks A single dependency stops at the nearest registry holding a binding its slot matches, and
|
|
53
|
+
* a collection gathers every registry's matches — `resolve` and `resolveAll` respectively.
|
|
54
|
+
*/
|
|
55
|
+
function dependencyTargets(chain, ref) {
|
|
56
|
+
if (ref.multi) {
|
|
57
|
+
const gathered = [];
|
|
58
|
+
for (const registry of chain) {
|
|
59
|
+
gathered.push(...matchingTargets(registry.getAll(ref.token), ref));
|
|
60
|
+
}
|
|
61
|
+
return gathered;
|
|
62
|
+
}
|
|
63
|
+
for (const registry of chain) {
|
|
64
|
+
const targets = matchingTargets(registry.getAll(ref.token), ref);
|
|
65
|
+
if (targets.length > 0) {
|
|
66
|
+
return targets;
|
|
53
67
|
}
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
}
|
|
69
|
+
return [];
|
|
56
70
|
}
|
|
57
71
|
/** The placeholder node an optional-but-unbound dependency points at, minted once per token. */
|
|
58
72
|
function unboundNodeIdFor(accumulator, dependency) {
|
|
@@ -73,8 +87,8 @@ function unboundNodeIdFor(accumulator, dependency) {
|
|
|
73
87
|
});
|
|
74
88
|
return id;
|
|
75
89
|
}
|
|
76
|
-
function addDependencyEdges(accumulator, from, ref, index,
|
|
77
|
-
const targets =
|
|
90
|
+
function addDependencyEdges(accumulator, from, ref, index, chain) {
|
|
91
|
+
const targets = dependencyTargets(chain, ref);
|
|
78
92
|
const label = edgeLabel(ref, index);
|
|
79
93
|
if (targets.length === 0) {
|
|
80
94
|
// A required-but-unbound dependency is validate()'s story, not the graph's.
|
|
@@ -108,25 +122,25 @@ function addDependencyEdges(accumulator, from, ref, index, lookup) {
|
|
|
108
122
|
}
|
|
109
123
|
}
|
|
110
124
|
/** What one binding declares up front — a class's params, a factory's descriptors, an alias's target. */
|
|
111
|
-
function addBindingEdges(accumulator, binding, metadataReader,
|
|
125
|
+
function addBindingEdges(accumulator, binding, metadataReader, chain) {
|
|
112
126
|
if (binding.kind === "class") {
|
|
113
127
|
const meta = metadataReader.getConstructorMetadata(binding.target);
|
|
114
128
|
if (meta !== undefined) {
|
|
115
129
|
for (const [index, param] of meta.params.entries()) {
|
|
116
|
-
addDependencyEdges(accumulator, String(binding.identifier), param, index,
|
|
130
|
+
addDependencyEdges(accumulator, String(binding.identifier), param, index, chain);
|
|
117
131
|
}
|
|
118
132
|
}
|
|
119
133
|
return;
|
|
120
134
|
}
|
|
121
135
|
if (binding.kind === "resolved" || binding.kind === "resolved-async") {
|
|
122
136
|
for (const [index, dependency] of binding.deps.entries()) {
|
|
123
|
-
addDependencyEdges(accumulator, String(binding.identifier), dependency, index,
|
|
137
|
+
addDependencyEdges(accumulator, String(binding.identifier), dependency, index, chain);
|
|
124
138
|
}
|
|
125
139
|
return;
|
|
126
140
|
}
|
|
127
141
|
if (binding.kind === "alias") {
|
|
128
142
|
const aliasRef = { token: binding.target, optional: false, multi: false };
|
|
129
|
-
for (const target of
|
|
143
|
+
for (const target of dependencyTargets(chain, aliasRef)) {
|
|
130
144
|
accumulator.edges.push({
|
|
131
145
|
from: String(binding.identifier),
|
|
132
146
|
to: String(target.identifier),
|
|
@@ -136,9 +150,9 @@ function addBindingEdges(accumulator, binding, metadataReader, lookup) {
|
|
|
136
150
|
}
|
|
137
151
|
}
|
|
138
152
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
for (const binding of
|
|
153
|
+
/** Adds the nodes of `chain[0]`, the registry the chain starts at, and the edges each of its bindings declares. */
|
|
154
|
+
function addRegistryBindings(accumulator, chain, metadataReader, fromParent) {
|
|
155
|
+
for (const binding of chain[0].allBindings()) {
|
|
142
156
|
accumulator.nodes.push({
|
|
143
157
|
id: String(binding.identifier),
|
|
144
158
|
tokenName: tokenName(binding.token),
|
|
@@ -147,20 +161,29 @@ function addRegistryBindings(accumulator, sourceRegistry, metadataReader, fromPa
|
|
|
147
161
|
scope: effectiveBindingScope(binding),
|
|
148
162
|
fromParent,
|
|
149
163
|
});
|
|
150
|
-
addBindingEdges(accumulator, binding, metadataReader,
|
|
164
|
+
addBindingEdges(accumulator, binding, metadataReader, chain);
|
|
151
165
|
}
|
|
152
166
|
}
|
|
153
167
|
/**
|
|
154
|
-
* Builds the JSON dependency graph of a registry's bindings, optionally including
|
|
168
|
+
* Builds the JSON dependency graph of a registry's bindings, optionally including its ancestors'.
|
|
169
|
+
*
|
|
170
|
+
* @param registry - The registry whose bindings the graph is for.
|
|
171
|
+
* @param metadataReader - The reader class dependencies are read through.
|
|
172
|
+
* @param options - Whether the ancestors' bindings join the graph.
|
|
173
|
+
* @param ancestorRegistries - The ancestor containers' registries, nearest first.
|
|
155
174
|
*
|
|
156
175
|
* @since 0.3.16-canary.0
|
|
157
176
|
*/
|
|
158
|
-
export function buildDependencyGraph(registry, metadataReader, options,
|
|
177
|
+
export function buildDependencyGraph(registry, metadataReader, options, ancestorRegistries = []) {
|
|
159
178
|
const accumulator = { nodes: [], edges: [], unboundNodeIds: new Map() };
|
|
160
179
|
const includesParent = options?.includeParent === true;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
180
|
+
if (!includesParent) {
|
|
181
|
+
addRegistryBindings(accumulator, [registry], metadataReader, false);
|
|
182
|
+
return { nodes: accumulator.nodes, edges: accumulator.edges, includesParent };
|
|
183
|
+
}
|
|
184
|
+
const chain = [registry, ...ancestorRegistries];
|
|
185
|
+
for (let depth = 0; depth < chain.length; depth += 1) {
|
|
186
|
+
addRegistryBindings(accumulator, chain.slice(depth), metadataReader, depth > 0);
|
|
164
187
|
}
|
|
165
188
|
return { nodes: accumulator.nodes, edges: accumulator.edges, includesParent };
|
|
166
189
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ContainerGraphJson } from "
|
|
1
|
+
import type { ContainerGraphJson } from "#introspection/dependency-graph";
|
|
2
2
|
/**
|
|
3
3
|
* Mermaid `flowchart TD` source for a container graph — renders anywhere Mermaid does
|
|
4
4
|
* (GitHub markdown, docs tooling, mermaid.live) with no extra library.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Initial grid
|
|
2
|
+
* Initial grid cell, in the pixels React Flow positions in: a starting layout viewers re-lay out,
|
|
3
|
+
* fixed by no contract.
|
|
3
4
|
*
|
|
4
5
|
* @since 0.3.16-canary.0
|
|
5
6
|
*/
|
|
6
|
-
const GRID_COLUMN_COUNT = 5;
|
|
7
7
|
const GRID_CELL_WIDTH_PX = 200;
|
|
8
8
|
const GRID_CELL_HEIGHT_PX = 100;
|
|
9
9
|
/**
|
|
@@ -12,6 +12,8 @@ const GRID_CELL_HEIGHT_PX = 100;
|
|
|
12
12
|
* @since 0.5.0-canary.7
|
|
13
13
|
*/
|
|
14
14
|
export function toReactFlowGraph(graph) {
|
|
15
|
+
// A square grid over the graph: as many columns as rows, derived from the node count.
|
|
16
|
+
const columnCount = Math.max(1, Math.ceil(Math.sqrt(graph.nodes.length)));
|
|
15
17
|
const nodes = graph.nodes.map((node, idx) => ({
|
|
16
18
|
id: node.id,
|
|
17
19
|
data: {
|
|
@@ -22,8 +24,8 @@ export function toReactFlowGraph(graph) {
|
|
|
22
24
|
fromParent: node.fromParent,
|
|
23
25
|
},
|
|
24
26
|
position: {
|
|
25
|
-
x: (idx %
|
|
26
|
-
y: Math.floor(idx /
|
|
27
|
+
x: (idx % columnCount) * GRID_CELL_WIDTH_PX,
|
|
28
|
+
y: Math.floor(idx / columnCount) * GRID_CELL_HEIGHT_PX,
|
|
27
29
|
},
|
|
28
30
|
}));
|
|
29
31
|
const edges = graph.edges.map((edge, idx) => ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { BindingRegistry } from "
|
|
2
|
-
import type { Token } from "
|
|
3
|
-
import type { BindingIdentifier, BindingKind, BindingScope, BindingTag, Constructor, ResolveOptions } from "
|
|
4
|
-
import type { ScopeManager } from "
|
|
1
|
+
import type { BindingRegistry } from "#core/registry";
|
|
2
|
+
import type { Token } from "#core/token";
|
|
3
|
+
import type { BindingIdentifier, BindingKind, BindingScope, BindingTag, Constructor, ResolveOptions } from "#core/types";
|
|
4
|
+
import type { ScopeManager } from "#lifecycle/scope-manager";
|
|
5
5
|
/**
|
|
6
6
|
* A read-only view of one binding: token, kind, scope, slot, and id.
|
|
7
7
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { effectiveBindingScope } from "
|
|
2
|
-
import { tokenName } from "
|
|
3
|
-
import {
|
|
1
|
+
import { effectiveBindingScope } from "#core/binding-scope";
|
|
2
|
+
import { tokenName } from "#core/token";
|
|
3
|
+
import { DefaultConstraintContext } from "#resolution/context";
|
|
4
|
+
import { selectAllBindings } from "#resolution/select/binding-select";
|
|
4
5
|
// ── Inspector ────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
5
6
|
/**
|
|
6
7
|
* The read-only introspection surface behind a container's `inspect()`.
|
|
@@ -38,17 +39,13 @@ export class Inspector {
|
|
|
38
39
|
}
|
|
39
40
|
const bindings = this.#registry.getAll(token);
|
|
40
41
|
// An existence probe answers ambiguity with `true` — several matches still exist; only
|
|
41
|
-
// resolution has to pick one.
|
|
42
|
-
return bindings.length > 0 &&
|
|
42
|
+
// resolution has to pick one. A default-slot alias answers any criteria by forwarding them.
|
|
43
|
+
return (bindings.length > 0 &&
|
|
44
|
+
(selectAllBindings(bindings, options, this.#makeConstraintContext(options)).length > 0 ||
|
|
45
|
+
this.#registry.getDefaultSlotBinding(token)?.kind === "alias"));
|
|
43
46
|
}
|
|
44
47
|
#makeConstraintContext(options) {
|
|
45
|
-
return
|
|
46
|
-
resolutionPath: [],
|
|
47
|
-
resolutionStack: [],
|
|
48
|
-
parent: undefined,
|
|
49
|
-
ancestors: [],
|
|
50
|
-
currentResolveOptions: options,
|
|
51
|
-
};
|
|
48
|
+
return new DefaultConstraintContext([], options);
|
|
52
49
|
}
|
|
53
50
|
#toSnapshot(binding) {
|
|
54
51
|
// Aliased, not copied: slot tags are frozen where they are built, so a caller's write throws
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Binding } from "
|
|
2
|
-
import type { Token } from "
|
|
3
|
-
import type { ActivationHandler, Constructor, DeactivationHandler, DependencyKey, ResolutionContext } from "
|
|
4
|
-
import type { MetadataReader } from "
|
|
1
|
+
import type { Binding } from "#core/binding";
|
|
2
|
+
import type { Token } from "#core/token";
|
|
3
|
+
import type { ActivationHandler, Constructor, DeactivationHandler, DependencyKey, ResolutionContext } from "#core/types";
|
|
4
|
+
import type { MetadataReader } from "#metadata/metadata-types";
|
|
5
5
|
/**
|
|
6
6
|
* One container's registry of container-level activation and deactivation hooks, keyed by token.
|
|
7
7
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { getOrInsert } from "
|
|
2
|
-
import { advanceStateEpoch } from "
|
|
3
|
-
import { tokenName } from "
|
|
4
|
-
import { AsyncActivationError, AsyncDeactivationError, InvalidMetadataError } from "
|
|
1
|
+
import { getOrInsert } from "#core/map-upsert";
|
|
2
|
+
import { advanceStateEpoch } from "#core/state-epoch";
|
|
3
|
+
import { tokenName } from "#core/token";
|
|
4
|
+
import { AsyncActivationError, AsyncDeactivationError, InvalidMetadataError } from "#errors/errors";
|
|
5
5
|
/**
|
|
6
6
|
* One container's registry of container-level activation and deactivation hooks, keyed by token.
|
|
7
7
|
*
|
|
@@ -97,7 +97,11 @@ export class LifecycleManager {
|
|
|
97
97
|
let activatedInstance = instance;
|
|
98
98
|
// 1. @postConstruct() — must be sync (instance fully constructed per TC39 order)
|
|
99
99
|
for (const methodName of lifecycleMethods(binding, metadataReader, "postConstruct")) {
|
|
100
|
-
|
|
100
|
+
const hookResult = callHook(activatedInstance, methodName);
|
|
101
|
+
if (hookResult instanceof Promise) {
|
|
102
|
+
// The hook has already run; adopt its rejection so a failing async hook cannot become an
|
|
103
|
+
// unhandled rejection that ends the process, then report the sync-lane violation.
|
|
104
|
+
void hookResult.catch(() => { });
|
|
101
105
|
throw new AsyncActivationError(tokenName(binding.token), "postConstruct", methodName);
|
|
102
106
|
}
|
|
103
107
|
}
|
|
@@ -105,6 +109,7 @@ export class LifecycleManager {
|
|
|
105
109
|
if (binding.kind !== "alias" && binding.activationHook !== undefined) {
|
|
106
110
|
const activationResult = binding.activationHook(resolutionContext, activatedInstance);
|
|
107
111
|
if (activationResult instanceof Promise) {
|
|
112
|
+
void activationResult.catch(() => { });
|
|
108
113
|
throw new AsyncActivationError(tokenName(binding.token), "onActivation");
|
|
109
114
|
}
|
|
110
115
|
activatedInstance = activationResult;
|
|
@@ -116,6 +121,7 @@ export class LifecycleManager {
|
|
|
116
121
|
for (const hook of containerHooks) {
|
|
117
122
|
const activationResult = hook(resolutionContext, activatedInstance);
|
|
118
123
|
if (activationResult instanceof Promise) {
|
|
124
|
+
void activationResult.catch(() => { });
|
|
119
125
|
throw new AsyncActivationError(tokenDisplayName, "onActivation");
|
|
120
126
|
}
|
|
121
127
|
activatedInstance = activationResult;
|
|
@@ -159,6 +165,9 @@ export class LifecycleManager {
|
|
|
159
165
|
for (const hook of containerHooks) {
|
|
160
166
|
const hookResult = hook(instance);
|
|
161
167
|
if (hookResult instanceof Promise) {
|
|
168
|
+
// The hook has already run; adopt its rejection so a failing async hook cannot become an
|
|
169
|
+
// unhandled rejection that ends the process, then report the sync-lane violation.
|
|
170
|
+
void hookResult.catch(() => { });
|
|
162
171
|
throw new AsyncDeactivationError(tokenDisplayName);
|
|
163
172
|
}
|
|
164
173
|
}
|
|
@@ -167,12 +176,15 @@ export class LifecycleManager {
|
|
|
167
176
|
if (binding.kind !== "alias" && binding.deactivationHook !== undefined) {
|
|
168
177
|
const hookResult = binding.deactivationHook(instance);
|
|
169
178
|
if (hookResult instanceof Promise) {
|
|
179
|
+
void hookResult.catch(() => { });
|
|
170
180
|
throw new AsyncDeactivationError(tokenDisplayName);
|
|
171
181
|
}
|
|
172
182
|
}
|
|
173
183
|
// 3. @preDestroy()
|
|
174
184
|
for (const methodName of lifecycleMethods(binding, metadataReader, "preDestroy")) {
|
|
175
|
-
|
|
185
|
+
const hookResult = callHook(instance, methodName);
|
|
186
|
+
if (hookResult instanceof Promise) {
|
|
187
|
+
void hookResult.catch(() => { });
|
|
176
188
|
throw new AsyncDeactivationError(tokenDisplayName);
|
|
177
189
|
}
|
|
178
190
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Binding } from "
|
|
2
|
-
import type { BindingIdentifier } from "
|
|
1
|
+
import type { Binding } from "#core/binding";
|
|
2
|
+
import type { BindingIdentifier } from "#core/types";
|
|
3
3
|
/**
|
|
4
4
|
* One container's instance caches — singletons, in-flight async creations, and the scoped cache.
|
|
5
5
|
*
|