@codefast/di 0.5.0 → 0.6.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 +615 -0
- package/README.md +119 -30
- package/dist/ambient/active-container.d.ts +37 -0
- package/dist/ambient/active-container.d.ts.map +1 -0
- package/dist/ambient/active-container.js +41 -0
- package/dist/ambient/active-container.js.map +1 -0
- package/dist/container/binding-builders.d.ts +12 -8
- package/dist/container/binding-builders.d.ts.map +1 -1
- package/dist/container/binding-builders.js +56 -31
- package/dist/container/binding-builders.js.map +1 -1
- package/dist/container/container.d.ts +33 -19
- package/dist/container/container.d.ts.map +1 -1
- package/dist/container/container.js +279 -118
- package/dist/container/container.js.map +1 -1
- package/dist/{resolution → core}/binding-scope.d.ts +2 -2
- package/dist/core/binding-scope.d.ts.map +1 -0
- package/dist/core/binding-scope.js.map +1 -0
- package/dist/{binding.d.ts → core/binding.d.ts} +70 -15
- package/dist/core/binding.d.ts.map +1 -0
- package/dist/{binding.js → core/binding.js} +27 -12
- package/dist/core/binding.js.map +1 -0
- package/dist/core/constraint-requirement.d.ts +38 -0
- package/dist/core/constraint-requirement.d.ts.map +1 -0
- package/dist/core/constraint-requirement.js +28 -0
- package/dist/core/constraint-requirement.js.map +1 -0
- package/dist/{constructor-type.d.ts → core/constructor-type.d.ts} +1 -1
- package/dist/core/constructor-type.d.ts.map +1 -0
- package/dist/core/constructor-type.js.map +1 -0
- package/dist/{module.d.ts → core/module.d.ts} +5 -5
- package/dist/core/module.d.ts.map +1 -0
- package/dist/{module.js → core/module.js} +4 -4
- package/dist/core/module.js.map +1 -0
- package/dist/{registry.d.ts → core/registry.d.ts} +27 -7
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/{registry.js → core/registry.js} +125 -52
- package/dist/core/registry.js.map +1 -0
- package/dist/core/tag.d.ts +80 -0
- package/dist/core/tag.d.ts.map +1 -0
- package/dist/core/tag.js +84 -0
- package/dist/core/tag.js.map +1 -0
- package/dist/{token.d.ts → core/token.d.ts} +2 -6
- package/dist/core/token.d.ts.map +1 -0
- package/dist/core/token.js +13 -0
- package/dist/core/token.js.map +1 -0
- package/dist/{types.d.ts → core/types.d.ts} +27 -21
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js.map +1 -0
- package/dist/decorators/inject.d.ts +5 -49
- package/dist/decorators/inject.d.ts.map +1 -1
- package/dist/decorators/inject.js +34 -104
- package/dist/decorators/inject.js.map +1 -1
- package/dist/decorators/injectable.d.ts +25 -5
- package/dist/decorators/injectable.d.ts.map +1 -1
- package/dist/decorators/injectable.js +2 -3
- package/dist/decorators/injectable.js.map +1 -1
- package/dist/decorators/lifecycle-decorators.d.ts +4 -2
- package/dist/decorators/lifecycle-decorators.d.ts.map +1 -1
- package/dist/decorators/lifecycle-decorators.js +19 -25
- package/dist/decorators/lifecycle-decorators.js.map +1 -1
- package/dist/{resolution → errors}/diagnostics.d.ts +2 -0
- package/dist/errors/diagnostics.d.ts.map +1 -0
- package/dist/errors/diagnostics.js.map +1 -0
- package/dist/{errors.d.ts → errors/errors.d.ts} +89 -4
- package/dist/errors/errors.d.ts.map +1 -0
- package/dist/{errors.js → errors/errors.js} +129 -7
- package/dist/errors/errors.js.map +1 -0
- package/dist/index.d.ts +21 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/injection/descriptor.d.ts +79 -0
- package/dist/injection/descriptor.d.ts.map +1 -0
- package/dist/injection/descriptor.js +118 -0
- package/dist/injection/descriptor.js.map +1 -0
- package/dist/{resolution → injection}/resolve-options.d.ts +21 -11
- package/dist/injection/resolve-options.d.ts.map +1 -0
- package/dist/injection/resolve-options.js +101 -0
- package/dist/injection/resolve-options.js.map +1 -0
- package/dist/introspection/dependency-graph.d.ts +16 -5
- package/dist/introspection/dependency-graph.d.ts.map +1 -1
- package/dist/introspection/dependency-graph.js +138 -57
- package/dist/introspection/dependency-graph.js.map +1 -1
- package/dist/introspection/graph-adapters/cytoscape.d.ts +6 -3
- package/dist/introspection/graph-adapters/cytoscape.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/cytoscape.js +3 -0
- package/dist/introspection/graph-adapters/cytoscape.js.map +1 -1
- package/dist/introspection/graph-adapters/dot.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/dot.js +12 -5
- package/dist/introspection/graph-adapters/dot.js.map +1 -1
- package/dist/introspection/graph-adapters/mermaid.d.ts +9 -0
- package/dist/introspection/graph-adapters/mermaid.d.ts.map +1 -0
- package/dist/introspection/graph-adapters/mermaid.js +47 -0
- package/dist/introspection/graph-adapters/mermaid.js.map +1 -0
- package/dist/introspection/graph-adapters/reactflow.d.ts +6 -3
- package/dist/introspection/graph-adapters/reactflow.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/reactflow.js +5 -1
- package/dist/introspection/graph-adapters/reactflow.js.map +1 -1
- package/dist/introspection/inspector.d.ts +4 -4
- package/dist/introspection/inspector.d.ts.map +1 -1
- package/dist/introspection/inspector.js +10 -6
- package/dist/introspection/inspector.js.map +1 -1
- package/dist/lifecycle/lifecycle-manager.d.ts +27 -0
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
- package/dist/{resolution/lifecycle.js → lifecycle/lifecycle-manager.js} +39 -9
- package/dist/lifecycle/lifecycle-manager.js.map +1 -0
- package/dist/lifecycle/scope-manager.d.ts +48 -0
- package/dist/lifecycle/scope-manager.d.ts.map +1 -0
- package/dist/lifecycle/scope-manager.js +135 -0
- package/dist/lifecycle/scope-manager.js.map +1 -0
- package/dist/metadata/metadata-reader-token.d.ts +1 -1
- package/dist/metadata/metadata-reader-token.d.ts.map +1 -1
- package/dist/metadata/metadata-reader-token.js +1 -1
- package/dist/metadata/metadata-reader-token.js.map +1 -1
- package/dist/metadata/metadata-types.d.ts +9 -9
- package/dist/metadata/metadata-types.d.ts.map +1 -1
- package/dist/metadata/symbol-metadata-reader.d.ts +19 -6
- package/dist/metadata/symbol-metadata-reader.d.ts.map +1 -1
- package/dist/metadata/symbol-metadata-reader.js +89 -9
- package/dist/metadata/symbol-metadata-reader.js.map +1 -1
- package/dist/metadata/verifying-metadata-reader.d.ts +16 -0
- package/dist/metadata/verifying-metadata-reader.d.ts.map +1 -0
- package/dist/metadata/verifying-metadata-reader.js +32 -0
- package/dist/metadata/verifying-metadata-reader.js.map +1 -0
- package/dist/resolution/{activation-need.d.ts → cache/activation-need.d.ts} +5 -5
- package/dist/resolution/cache/activation-need.d.ts.map +1 -0
- package/dist/resolution/cache/activation-need.js.map +1 -0
- package/dist/resolution/{binding-lookup-cache.d.ts → cache/binding-lookup-cache.d.ts} +6 -5
- package/dist/resolution/cache/binding-lookup-cache.d.ts.map +1 -0
- package/dist/resolution/cache/binding-lookup-cache.js.map +1 -0
- package/dist/resolution/cache/class-introspector.d.ts +66 -0
- package/dist/resolution/cache/class-introspector.d.ts.map +1 -0
- package/dist/resolution/cache/class-introspector.js +194 -0
- package/dist/resolution/cache/class-introspector.js.map +1 -0
- package/dist/resolution/context.d.ts +84 -0
- package/dist/resolution/context.d.ts.map +1 -0
- package/dist/resolution/{environment.js → context.js} +54 -80
- package/dist/resolution/context.js.map +1 -0
- package/dist/resolution/path/resolution-path.d.ts +94 -0
- package/dist/resolution/path/resolution-path.d.ts.map +1 -0
- package/dist/resolution/path/resolution-path.js +117 -0
- package/dist/resolution/path/resolution-path.js.map +1 -0
- package/dist/resolution/{instantiation-plan.d.ts → plan/instantiation-plan.d.ts} +30 -7
- package/dist/resolution/plan/instantiation-plan.d.ts.map +1 -0
- package/dist/resolution/plan/instantiation-plan.js +390 -0
- package/dist/resolution/plan/instantiation-plan.js.map +1 -0
- package/dist/resolution/resolver.d.ts +28 -28
- package/dist/resolution/resolver.d.ts.map +1 -1
- package/dist/resolution/resolver.js +552 -239
- package/dist/resolution/resolver.js.map +1 -1
- package/dist/resolution/select/binding-select.d.ts +34 -0
- package/dist/resolution/select/binding-select.d.ts.map +1 -0
- package/dist/resolution/select/binding-select.js +175 -0
- package/dist/resolution/select/binding-select.js.map +1 -0
- package/dist/resolution/{constraints.d.ts → select/constraints.d.ts} +13 -12
- package/dist/resolution/select/constraints.d.ts.map +1 -0
- package/dist/resolution/{constraints.js → select/constraints.js} +33 -22
- package/dist/resolution/select/constraints.js.map +1 -0
- package/package.json +160 -20
- package/src/ambient/active-container.ts +65 -0
- package/src/container/binding-builders.ts +72 -44
- package/src/container/container.ts +355 -170
- package/src/{resolution → core}/binding-scope.ts +2 -2
- package/src/{binding.ts → core/binding.ts} +101 -39
- package/src/core/constraint-requirement.ts +51 -0
- package/src/{constructor-type.ts → core/constructor-type.ts} +1 -1
- package/src/{module.ts → core/module.ts} +10 -10
- package/src/core/registry.ts +365 -0
- package/src/core/tag.ts +144 -0
- package/src/{token.ts → core/token.ts} +2 -14
- package/src/{types.ts → core/types.ts} +37 -34
- package/src/decorators/inject.ts +39 -175
- package/src/decorators/injectable.ts +40 -10
- package/src/decorators/lifecycle-decorators.ts +22 -34
- package/src/{resolution → errors}/diagnostics.ts +2 -0
- package/src/{errors.ts → errors/errors.ts} +152 -8
- package/src/index.ts +43 -15
- package/src/injection/descriptor.ts +206 -0
- package/src/injection/resolve-options.ts +144 -0
- package/src/introspection/dependency-graph.ts +223 -63
- package/src/introspection/graph-adapters/cytoscape.ts +9 -3
- package/src/introspection/graph-adapters/dot.ts +13 -5
- package/src/introspection/graph-adapters/mermaid.ts +61 -0
- package/src/introspection/graph-adapters/reactflow.ts +11 -4
- package/src/introspection/inspector.ts +16 -12
- package/src/{resolution/lifecycle.ts → lifecycle/lifecycle-manager.ts} +68 -32
- package/src/lifecycle/scope-manager.ts +156 -0
- package/src/metadata/metadata-reader-token.ts +2 -2
- package/src/metadata/metadata-types.ts +9 -9
- package/src/metadata/symbol-metadata-reader.ts +95 -18
- package/src/metadata/verifying-metadata-reader.ts +41 -0
- package/src/resolution/{activation-need.ts → cache/activation-need.ts} +8 -8
- package/src/resolution/{binding-lookup-cache.ts → cache/binding-lookup-cache.ts} +6 -5
- package/src/resolution/cache/class-introspector.ts +251 -0
- package/src/resolution/{environment.ts → context.ts} +84 -145
- package/src/resolution/path/resolution-path.ts +167 -0
- package/src/resolution/plan/instantiation-plan.ts +579 -0
- package/src/resolution/resolver.ts +635 -313
- package/src/resolution/select/binding-select.ts +210 -0
- package/src/resolution/select/constraints.ts +130 -0
- package/dist/binding.d.ts.map +0 -1
- package/dist/binding.js.map +0 -1
- package/dist/constructor-type.d.ts.map +0 -1
- package/dist/constructor-type.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/module.d.ts.map +0 -1
- package/dist/module.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/resolution/activation-need.d.ts.map +0 -1
- package/dist/resolution/activation-need.js.map +0 -1
- package/dist/resolution/binding-lookup-cache.d.ts.map +0 -1
- package/dist/resolution/binding-lookup-cache.js.map +0 -1
- package/dist/resolution/binding-scope.d.ts.map +0 -1
- package/dist/resolution/binding-scope.js.map +0 -1
- package/dist/resolution/binding-select.d.ts +0 -23
- package/dist/resolution/binding-select.d.ts.map +0 -1
- package/dist/resolution/binding-select.js +0 -121
- package/dist/resolution/binding-select.js.map +0 -1
- package/dist/resolution/class-introspector.d.ts +0 -27
- package/dist/resolution/class-introspector.d.ts.map +0 -1
- package/dist/resolution/class-introspector.js +0 -60
- package/dist/resolution/class-introspector.js.map +0 -1
- package/dist/resolution/constraints.d.ts.map +0 -1
- package/dist/resolution/constraints.js.map +0 -1
- package/dist/resolution/diagnostics.d.ts.map +0 -1
- package/dist/resolution/diagnostics.js.map +0 -1
- package/dist/resolution/environment.d.ts +0 -94
- package/dist/resolution/environment.d.ts.map +0 -1
- package/dist/resolution/environment.js.map +0 -1
- package/dist/resolution/instantiation-plan.d.ts.map +0 -1
- package/dist/resolution/instantiation-plan.js +0 -183
- package/dist/resolution/instantiation-plan.js.map +0 -1
- package/dist/resolution/lifecycle.d.ts +0 -23
- package/dist/resolution/lifecycle.d.ts.map +0 -1
- package/dist/resolution/lifecycle.js.map +0 -1
- package/dist/resolution/resolution-path.d.ts +0 -98
- package/dist/resolution/resolution-path.d.ts.map +0 -1
- package/dist/resolution/resolution-path.js +0 -98
- package/dist/resolution/resolution-path.js.map +0 -1
- package/dist/resolution/resolve-options.d.ts.map +0 -1
- package/dist/resolution/resolve-options.js +0 -56
- package/dist/resolution/resolve-options.js.map +0 -1
- package/dist/resolution/scope.d.ts +0 -28
- package/dist/resolution/scope.d.ts.map +0 -1
- package/dist/resolution/scope.js +0 -88
- package/dist/resolution/scope.js.map +0 -1
- package/dist/token.d.ts.map +0 -1
- package/dist/token.js +0 -22
- package/dist/token.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/src/registry.ts +0 -282
- package/src/resolution/binding-select.ts +0 -154
- package/src/resolution/class-introspector.ts +0 -74
- package/src/resolution/constraints.ts +0 -121
- package/src/resolution/instantiation-plan.ts +0 -292
- package/src/resolution/resolution-path.ts +0 -156
- package/src/resolution/resolve-options.ts +0 -89
- package/src/resolution/scope.ts +0 -105
- /package/dist/{resolution → core}/binding-scope.js +0 -0
- /package/dist/{constructor-type.js → core/constructor-type.js} +0 -0
- /package/dist/{types.js → core/types.js} +0 -0
- /package/dist/{resolution → errors}/diagnostics.js +0 -0
- /package/dist/resolution/{activation-need.js → cache/activation-need.js} +0 -0
- /package/dist/resolution/{binding-lookup-cache.js → cache/binding-lookup-cache.js} +0 -0
package/src/decorators/inject.ts
CHANGED
|
@@ -1,137 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getActiveContainer, getAmbientResolution } from "#/ambient/active-container";
|
|
2
|
+
/** `@inject` — the accessor-decorator channel, resolving from the ambient container. */
|
|
3
|
+
import type { Token } from "#/core/token";
|
|
4
|
+
import type { Constructor } from "#/core/types";
|
|
5
|
+
import { MissingContainerContextError, StaticMemberDecoratorError } from "#/errors/errors";
|
|
6
|
+
import type { InjectionDescriptor, InjectOptions } from "#/injection/descriptor";
|
|
7
|
+
import { buildInjectionDescriptor } from "#/injection/descriptor";
|
|
8
|
+
import { injectionSlotToResolveOptions } from "#/injection/resolve-options";
|
|
2
9
|
import { INJECT_ACCESSOR_KEY } from "#/metadata/metadata-keys";
|
|
3
|
-
import { getActiveContainer } from "#/resolution/environment";
|
|
4
|
-
import { injectionSlotToResolveOptions } from "#/resolution/resolve-options";
|
|
5
|
-
import type { Token } from "#/token";
|
|
6
|
-
import type { BindingTag, Constructor, TokenValue } from "#/types";
|
|
7
|
-
|
|
8
|
-
// ── InjectionDescriptor ───────────────────────────────────────────────────────
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @since 0.3.16-canary.0
|
|
12
|
-
*/
|
|
13
|
-
export interface InjectOptions {
|
|
14
|
-
name?: string;
|
|
15
|
-
tags?: ReadonlyArray<BindingTag>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @since 0.3.16-canary.0
|
|
20
|
-
*/
|
|
21
|
-
export interface InjectionDescriptor<Value = unknown> {
|
|
22
|
-
readonly token: Token<Value> | Constructor<Value>;
|
|
23
|
-
readonly optional: boolean;
|
|
24
|
-
readonly multi: boolean;
|
|
25
|
-
readonly name?: string;
|
|
26
|
-
readonly tags?: ReadonlyArray<BindingTag>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @since 0.3.16-canary.0
|
|
31
|
-
*/
|
|
32
|
-
export type InjectableDependency<Value = unknown> = Token<Value> | Constructor<Value> | InjectionDescriptor<Value>;
|
|
33
10
|
|
|
34
11
|
/**
|
|
35
|
-
* The
|
|
36
|
-
*
|
|
37
|
-
* @remarks `optional()` and `injectAll()` fold their effect into the descriptor's own type
|
|
38
|
-
* parameter, so reading it back is enough; bare tokens fall through to {@link TokenValue}.
|
|
12
|
+
* The name of the class being constructed, or `undefined` when there is none to report.
|
|
39
13
|
*
|
|
40
|
-
* @
|
|
14
|
+
* @remarks Two ways there is none: an anonymous class expression has an empty `name`, and an
|
|
15
|
+
* instance whose prototype chain answers no `constructor` has nothing to read. Narrowed rather than
|
|
16
|
+
* asserted — the value arrives as `unknown` and the platform makes no promise about it.
|
|
41
17
|
*/
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @since 0.3.16-canary.0
|
|
47
|
-
*/
|
|
48
|
-
export function isInjectionDescriptor(value: unknown): value is InjectionDescriptor {
|
|
49
|
-
if (value === null || value === undefined) {
|
|
50
|
-
return false;
|
|
18
|
+
function classNameOf(instance: unknown): string | undefined {
|
|
19
|
+
if (typeof instance !== "object" || instance === null) {
|
|
20
|
+
return undefined;
|
|
51
21
|
}
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return false;
|
|
22
|
+
const constructor: unknown = Reflect.get(instance, "constructor");
|
|
23
|
+
if (typeof constructor !== "function" || constructor.name === "") {
|
|
24
|
+
return undefined;
|
|
56
25
|
}
|
|
57
|
-
return (
|
|
58
|
-
"token" in (value as object) &&
|
|
59
|
-
"optional" in (value as object) &&
|
|
60
|
-
"multi" in (value as object) &&
|
|
61
|
-
typeof (value as InjectionDescriptor).optional === "boolean" &&
|
|
62
|
-
typeof (value as InjectionDescriptor).multi === "boolean"
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
26
|
|
|
66
|
-
|
|
67
|
-
* @since 0.3.16-canary.0
|
|
68
|
-
*/
|
|
69
|
-
export function normalizeToDescriptor(dependency: InjectableDependency): InjectionDescriptor {
|
|
70
|
-
if (isInjectionDescriptor(dependency)) {
|
|
71
|
-
return materializeInjectionDescriptor(dependency);
|
|
72
|
-
}
|
|
73
|
-
return { token: dependency as Token<unknown> | Constructor, optional: false, multi: false };
|
|
27
|
+
return constructor.name;
|
|
74
28
|
}
|
|
75
29
|
|
|
76
|
-
|
|
77
|
-
* Dual-role `inject()` values are functions: [[Function]].name must not be treated as a DI slot name.
|
|
78
|
-
* Only enumerable own `name` / `tags` from `Object.defineProperties` are real injection options.
|
|
79
|
-
*/
|
|
80
|
-
function materializeInjectionDescriptor(dependency: InjectionDescriptor): InjectionDescriptor {
|
|
81
|
-
if (typeof dependency !== "function") {
|
|
82
|
-
return dependency;
|
|
83
|
-
}
|
|
84
|
-
const dualRole = dependency as InjectionDescriptor & ((...args: Array<unknown>) => unknown);
|
|
85
|
-
const base: Pick<InjectionDescriptor, "token" | "optional" | "multi"> = {
|
|
86
|
-
token: dualRole.token,
|
|
87
|
-
optional: dualRole.optional,
|
|
88
|
-
multi: dualRole.multi,
|
|
89
|
-
};
|
|
90
|
-
const nameDesc = Object.getOwnPropertyDescriptor(dualRole, "name");
|
|
91
|
-
const tagsDesc = Object.getOwnPropertyDescriptor(dualRole, "tags");
|
|
92
|
-
const explicitName = nameDesc?.enumerable === true && typeof nameDesc.value === "string" ? nameDesc.value : undefined;
|
|
93
|
-
const explicitTags = tagsDesc?.enumerable === true ? tagsDesc.value : undefined;
|
|
94
|
-
|
|
95
|
-
if (explicitName !== undefined && explicitTags !== undefined) {
|
|
96
|
-
return {
|
|
97
|
-
...base,
|
|
98
|
-
name: explicitName,
|
|
99
|
-
tags: explicitTags as NonNullable<InjectionDescriptor["tags"]>,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
if (explicitName !== undefined) {
|
|
103
|
-
return { ...base, name: explicitName };
|
|
104
|
-
}
|
|
105
|
-
if (explicitTags !== undefined) {
|
|
106
|
-
return { ...base, tags: explicitTags as NonNullable<InjectionDescriptor["tags"]> };
|
|
107
|
-
}
|
|
108
|
-
return base;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function withOptions<DescValue>(
|
|
112
|
-
base: Pick<InjectionDescriptor<DescValue>, "token" | "optional" | "multi">,
|
|
113
|
-
options: InjectOptions | undefined,
|
|
114
|
-
): InjectionDescriptor<DescValue> {
|
|
115
|
-
if (options?.name !== undefined && options.tags !== undefined) {
|
|
116
|
-
return { ...base, name: options.name, tags: options.tags };
|
|
117
|
-
}
|
|
118
|
-
if (options?.name !== undefined) {
|
|
119
|
-
return { ...base, name: options.name };
|
|
120
|
-
}
|
|
121
|
-
if (options?.tags !== undefined) {
|
|
122
|
-
return { ...base, tags: options.tags };
|
|
123
|
-
}
|
|
124
|
-
return base;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function buildInjectionDescriptor<const Value>(
|
|
128
|
-
token: Token<Value> | Constructor<Value>,
|
|
129
|
-
options?: InjectOptions,
|
|
130
|
-
): InjectionDescriptor<Value> {
|
|
131
|
-
return withOptions({ token, optional: false, multi: false }, options);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// ── inject() — dual-role ──────────────────────────────────────────────────────
|
|
30
|
+
// ── inject() — dual-role ─────────────────────────────────────────────────────────────────────────────────────────────
|
|
135
31
|
|
|
136
32
|
type ClassAccessorDecorator<This, Value> = (
|
|
137
33
|
target: ClassAccessorDecoratorTarget<This, Value>,
|
|
@@ -141,23 +37,26 @@ type ClassAccessorDecorator<This, Value> = (
|
|
|
141
37
|
/**
|
|
142
38
|
* @since 0.3.16-canary.0
|
|
143
39
|
*/
|
|
144
|
-
export function inject<
|
|
40
|
+
export function inject<Value>(
|
|
145
41
|
token: Token<Value> | Constructor<Value>,
|
|
146
42
|
options?: InjectOptions,
|
|
147
43
|
): InjectionDescriptor<Value> & ClassAccessorDecorator<unknown, Value> {
|
|
148
44
|
const descriptor = buildInjectionDescriptor(token, options);
|
|
45
|
+
// Derived from the descriptor, not from `options`: the descriptor is where the tag shorthand has
|
|
46
|
+
// already been folded. Built once here rather than per constructed instance.
|
|
47
|
+
const resolveOptions = injectionSlotToResolveOptions(descriptor);
|
|
149
48
|
|
|
150
49
|
const decoratorFn = (
|
|
151
50
|
_target: ClassAccessorDecoratorTarget<unknown, Value>,
|
|
152
51
|
context: ClassAccessorDecoratorContext<unknown, Value>,
|
|
153
52
|
): ClassAccessorDecoratorResult<unknown, Value> => {
|
|
154
53
|
if (context.static) {
|
|
155
|
-
throw new
|
|
156
|
-
"@inject() on static accessors is not supported; only instance accessors participate in runWithContainer-based property injection.",
|
|
157
|
-
);
|
|
54
|
+
throw new StaticMemberDecoratorError("inject", String(context.name));
|
|
158
55
|
}
|
|
159
56
|
const meta = context.metadata as Record<string | symbol, unknown>;
|
|
160
|
-
|
|
57
|
+
// Own bucket only: the metadata record inherits the base class's, and pushing into an inherited
|
|
58
|
+
// array would register this accessor on the base class instead.
|
|
59
|
+
if (!Object.hasOwn(meta, INJECT_ACCESSOR_KEY) || !Array.isArray(meta[INJECT_ACCESSOR_KEY])) {
|
|
161
60
|
meta[INJECT_ACCESSOR_KEY] = [];
|
|
162
61
|
}
|
|
163
62
|
(meta[INJECT_ACCESSOR_KEY] as Array<{ key: string | symbol; descriptor: InjectionDescriptor }>).push({
|
|
@@ -166,17 +65,20 @@ export function inject<const Value>(
|
|
|
166
65
|
});
|
|
167
66
|
|
|
168
67
|
context.addInitializer(function (this: unknown) {
|
|
68
|
+
// Prefer the engine's path-continuing resolver: it keeps this accessor's dependencies on the
|
|
69
|
+
// live resolution path, so a cycle through an accessor is detected instead of recursing.
|
|
70
|
+
const ambient = getAmbientResolution();
|
|
71
|
+
if (ambient !== undefined) {
|
|
72
|
+
const value = descriptor.optional
|
|
73
|
+
? ambient.resolveOptional(token, resolveOptions)
|
|
74
|
+
: ambient.resolve(token, resolveOptions);
|
|
75
|
+
context.access.set(this, value as Value);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
169
78
|
const container = getActiveContainer();
|
|
170
79
|
if (container === undefined) {
|
|
171
|
-
throw new MissingContainerContextError(
|
|
80
|
+
throw new MissingContainerContextError(classNameOf(this), context.name);
|
|
172
81
|
}
|
|
173
|
-
const resolveOptions =
|
|
174
|
-
options === undefined
|
|
175
|
-
? undefined
|
|
176
|
-
: injectionSlotToResolveOptions({
|
|
177
|
-
...(options.name !== undefined ? { name: options.name } : {}),
|
|
178
|
-
...(options.tags !== undefined ? { tags: options.tags } : {}),
|
|
179
|
-
});
|
|
180
82
|
const value = descriptor.optional
|
|
181
83
|
? container.resolveOptional(token, resolveOptions)
|
|
182
84
|
: container.resolve(token, resolveOptions);
|
|
@@ -195,41 +97,3 @@ export function inject<const Value>(
|
|
|
195
97
|
|
|
196
98
|
return decoratorFn as InjectionDescriptor<Value> & ClassAccessorDecorator<unknown, Value>;
|
|
197
99
|
}
|
|
198
|
-
|
|
199
|
-
// ── optional() ────────────────────────────────────────────────────────────────
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* @since 0.3.16-canary.0
|
|
203
|
-
*/
|
|
204
|
-
export function optional<const Value>(
|
|
205
|
-
token: Token<Value> | Constructor<Value>,
|
|
206
|
-
options?: InjectOptions,
|
|
207
|
-
): InjectionDescriptor<Value | undefined> {
|
|
208
|
-
return withOptions(
|
|
209
|
-
{
|
|
210
|
-
token: token as Token<Value | undefined> | Constructor<Value | undefined>,
|
|
211
|
-
optional: true,
|
|
212
|
-
multi: false,
|
|
213
|
-
},
|
|
214
|
-
options,
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// ── injectAll() ───────────────────────────────────────────────────────────────
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* @since 0.3.16-canary.0
|
|
222
|
-
*/
|
|
223
|
-
export function injectAll<const Value>(
|
|
224
|
-
token: Token<Value> | Constructor<Value>,
|
|
225
|
-
options?: InjectOptions,
|
|
226
|
-
): InjectionDescriptor<Array<Value>> {
|
|
227
|
-
return withOptions(
|
|
228
|
-
{
|
|
229
|
-
token: token as Token<Array<Value>> | Constructor<Array<Value>>,
|
|
230
|
-
optional: false,
|
|
231
|
-
multi: true,
|
|
232
|
-
},
|
|
233
|
-
options,
|
|
234
|
-
);
|
|
235
|
-
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { BindingScope, Constructor } from "#/core/types";
|
|
2
|
+
import type { InjectableDependency, ResolvedDependencyValue } from "#/injection/descriptor";
|
|
3
|
+
import { normalizeToDescriptor } from "#/injection/descriptor";
|
|
3
4
|
import { INJECTABLE_KEY } from "#/metadata/metadata-keys";
|
|
4
5
|
import type { ParamMetadata } from "#/metadata/metadata-types";
|
|
5
|
-
import type { BindingScope, Constructor } from "#/types";
|
|
6
6
|
|
|
7
|
-
// ── AutoRegisterRegistry
|
|
7
|
+
// ── AutoRegisterRegistry ─────────────────────────────────────────────────────────────────────────────────────────────
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @since 0.3.16-canary.0
|
|
@@ -29,26 +29,56 @@ export function createAutoRegisterRegistry(): AutoRegisterRegistry {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
// ── InjectableOptions
|
|
32
|
+
// ── InjectableOptions ────────────────────────────────────────────────────────────────────────────────────────────────
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* @since 0.3.16-canary.0
|
|
36
36
|
*/
|
|
37
37
|
export interface InjectableOptions {
|
|
38
|
-
autoRegister?: AutoRegisterRegistry;
|
|
39
|
-
scope?: BindingScope;
|
|
38
|
+
autoRegister?: AutoRegisterRegistry | undefined;
|
|
39
|
+
scope?: BindingScope | undefined;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
// ── @injectable()
|
|
42
|
+
// ── @injectable() ────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
+
* The parameters a constructor is handed, given what its `deps` declare.
|
|
46
|
+
*
|
|
47
|
+
* @remarks Reuses the same reading a `toResolved` factory gets, so `injectAll` arrives as an array
|
|
48
|
+
* and `optional` as possibly undefined in both places.
|
|
49
|
+
*/
|
|
50
|
+
type InjectedParameters<Deps extends ReadonlyArray<InjectableDependency>> = {
|
|
51
|
+
-readonly [Index in keyof Deps]: ResolvedDependencyValue<Deps[Index]>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Declare a class injectable, and what its constructor is to be handed.
|
|
56
|
+
*
|
|
45
57
|
* @since 0.3.16-canary.0
|
|
46
58
|
*/
|
|
59
|
+
export function injectable(): (target: unknown, context: ClassDecoratorContext) => void;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @remarks Declaring dependencies constrains the class: the decorator only accepts one whose
|
|
63
|
+
* constructor takes exactly what `deps` resolve to, in that order. A class taking fewer parameters
|
|
64
|
+
* than `deps` declares still satisfies it, which is the one mismatch TypeScript's arity rules let
|
|
65
|
+
* through — the surplus dependency is resolved and discarded.
|
|
66
|
+
*
|
|
67
|
+
* @since 0.3.16-canary.0
|
|
68
|
+
*/
|
|
69
|
+
export function injectable<const Deps extends ReadonlyArray<InjectableDependency>>(
|
|
70
|
+
deps: Deps,
|
|
71
|
+
options?: InjectableOptions,
|
|
72
|
+
): (target: abstract new (...args: InjectedParameters<Deps>) => unknown, context: ClassDecoratorContext) => void;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @since 0.6.0
|
|
76
|
+
*/
|
|
47
77
|
export function injectable(
|
|
48
78
|
deps?: ReadonlyArray<InjectableDependency>,
|
|
49
79
|
options?: InjectableOptions,
|
|
50
|
-
): (target:
|
|
51
|
-
return function (target:
|
|
80
|
+
): (target: never, context: ClassDecoratorContext) => void {
|
|
81
|
+
return function (target: never, context: ClassDecoratorContext): void {
|
|
52
82
|
const parameterMetadataList: Array<ParamMetadata> = (deps ?? []).map((dependency, index) => {
|
|
53
83
|
const descriptor = normalizeToDescriptor(dependency);
|
|
54
84
|
const baseParameterMetadata: Pick<ParamMetadata, "index" | "token" | "optional" | "multi"> = {
|
|
@@ -1,51 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StaticMemberDecoratorError } from "#/errors/errors";
|
|
2
2
|
import { LIFECYCLE_KEY } from "#/metadata/metadata-keys";
|
|
3
3
|
import type { MutableLifecycleMetadata } from "#/metadata/metadata-types";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
metadata: MutableLifecycleMetadata,
|
|
7
|
-
phase: "postConstruct" | "preDestroy",
|
|
8
|
-
methodName: string,
|
|
9
|
-
): void {
|
|
10
|
-
if (!metadata[phase].includes(methodName)) {
|
|
11
|
-
metadata[phase].push(methodName);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
5
|
+
type MethodDecorator = (target: unknown, context: ClassMethodDecoratorContext) => void;
|
|
14
6
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
*/
|
|
18
|
-
export function postConstruct(): (target: unknown, context: ClassMethodDecoratorContext) => void {
|
|
7
|
+
/** Records the decorated method under one lifecycle phase; both decorators differ only in that phase. */
|
|
8
|
+
function recordLifecycleMethod(phase: "postConstruct" | "preDestroy"): MethodDecorator {
|
|
19
9
|
return function (target: unknown, context: ClassMethodDecoratorContext): void {
|
|
20
10
|
if (context.static) {
|
|
21
|
-
throw new
|
|
22
|
-
"@postConstruct() applies to instance methods only; static methods are not invoked during instance lifecycle.",
|
|
23
|
-
);
|
|
11
|
+
throw new StaticMemberDecoratorError(phase, String(context.name));
|
|
24
12
|
}
|
|
25
|
-
const methodName = String(context.name);
|
|
26
13
|
const meta = context.metadata as Record<string | symbol, unknown>;
|
|
27
|
-
|
|
14
|
+
// Own bucket only: `context.metadata` inherits the base class's record, and writing through an
|
|
15
|
+
// inherited bucket would register this hook on the base class instead.
|
|
16
|
+
if (!Object.hasOwn(meta, LIFECYCLE_KEY)) {
|
|
28
17
|
meta[LIFECYCLE_KEY] = { postConstruct: [], preDestroy: [] };
|
|
29
18
|
}
|
|
30
|
-
|
|
19
|
+
const lifecycle = meta[LIFECYCLE_KEY] as MutableLifecycleMetadata;
|
|
20
|
+
const methodName = String(context.name);
|
|
21
|
+
if (!lifecycle[phase].includes(methodName)) {
|
|
22
|
+
lifecycle[phase].push(methodName);
|
|
23
|
+
}
|
|
31
24
|
};
|
|
32
25
|
}
|
|
33
26
|
|
|
34
27
|
/**
|
|
35
28
|
* @since 0.3.16-canary.0
|
|
36
29
|
*/
|
|
37
|
-
export function
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!meta[LIFECYCLE_KEY]) {
|
|
47
|
-
meta[LIFECYCLE_KEY] = { postConstruct: [], preDestroy: [] };
|
|
48
|
-
}
|
|
49
|
-
appendUniqueMethod(meta[LIFECYCLE_KEY] as MutableLifecycleMetadata, "preDestroy", methodName);
|
|
50
|
-
};
|
|
30
|
+
export function postConstruct(): MethodDecorator {
|
|
31
|
+
return recordLifecycleMethod("postConstruct");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @since 0.3.16-canary.0
|
|
36
|
+
*/
|
|
37
|
+
export function preDestroy(): MethodDecorator {
|
|
38
|
+
return recordLifecycleMethod("preDestroy");
|
|
51
39
|
}
|
|
@@ -25,6 +25,8 @@ export const RESOLUTION_DIAGNOSTICS: unique symbol = Symbol("di:resolution-diagn
|
|
|
25
25
|
export interface ResolutionDiagnostics {
|
|
26
26
|
/** Bindings with a compiled instantiation plan. */
|
|
27
27
|
readonly compiledPlanCount: number;
|
|
28
|
+
/** Bindings with a compiled async instantiation plan. */
|
|
29
|
+
readonly compiledAsyncPlanCount: number;
|
|
28
30
|
/** Contexts held by the depth-indexed sync pool. */
|
|
29
31
|
readonly syncContextPoolSize: number;
|
|
30
32
|
/** Scoped instances currently cached by this container's scope manager. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BindingIdentifier, BindingScope, ResolveOptions } from "#/types";
|
|
1
|
+
import type { BindingIdentifier, BindingScope, ResolveOptions } from "#/core/types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @since 0.3.16-canary.0
|
|
@@ -36,6 +36,19 @@ export class TokenNotBoundError extends DiError {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// Options carry caller values a tag may hold — a bigint or a circular object must not make the
|
|
40
|
+
// diagnostic itself throw and mask the real error.
|
|
41
|
+
function describeResolveOptions(options: ResolveOptions): string {
|
|
42
|
+
try {
|
|
43
|
+
return (
|
|
44
|
+
JSON.stringify(options, (_key, value: unknown) => (typeof value === "bigint" ? `${String(value)}n` : value)) ??
|
|
45
|
+
"undefined"
|
|
46
|
+
);
|
|
47
|
+
} catch {
|
|
48
|
+
return "[unserializable options]";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
/**
|
|
40
53
|
* @since 0.3.16-canary.0
|
|
41
54
|
*/
|
|
@@ -46,7 +59,7 @@ export class NoMatchingBindingError extends DiError {
|
|
|
46
59
|
readonly availableSlots: Array<string>;
|
|
47
60
|
|
|
48
61
|
constructor(tokenName: string, options: ResolveOptions, availableSlots: Array<string>) {
|
|
49
|
-
const optionsString =
|
|
62
|
+
const optionsString = describeResolveOptions(options);
|
|
50
63
|
const slotsStr = availableSlots.join(", ");
|
|
51
64
|
super(`No binding for '${tokenName}' matching ${optionsString}. Available slots: [${slotsStr}].`);
|
|
52
65
|
this.tokenName = tokenName;
|
|
@@ -90,12 +103,16 @@ export class CircularDependencyError extends DiError {
|
|
|
90
103
|
*/
|
|
91
104
|
export class AsyncResolutionError extends DiError {
|
|
92
105
|
readonly code = "ASYNC_RESOLUTION";
|
|
106
|
+
/** The token the caller asked for — what `resolveAsync` has to be called with. */
|
|
93
107
|
readonly tokenName: string;
|
|
108
|
+
/** The token whose factory is async: `tokenName` itself unless a dependency forced it. */
|
|
94
109
|
readonly asyncSourceToken: string;
|
|
95
110
|
|
|
96
|
-
constructor(tokenName: string, asyncSourceToken: string) {
|
|
111
|
+
constructor(tokenName: string, asyncSourceToken: string = tokenName) {
|
|
97
112
|
super(
|
|
98
|
-
|
|
113
|
+
asyncSourceToken === tokenName
|
|
114
|
+
? `Token '${tokenName}' requires async resolution because its factory is async. Use container.resolveAsync(${tokenName}).`
|
|
115
|
+
: `Token '${tokenName}' requires async resolution because '${asyncSourceToken}' in its dependency chain has an async factory. Use container.resolveAsync(${tokenName}).`,
|
|
99
116
|
);
|
|
100
117
|
this.tokenName = tokenName;
|
|
101
118
|
this.asyncSourceToken = asyncSourceToken;
|
|
@@ -141,6 +158,98 @@ export class ScopeViolationError extends DiError {
|
|
|
141
158
|
}
|
|
142
159
|
}
|
|
143
160
|
|
|
161
|
+
/**
|
|
162
|
+
* A `…TaggedAll` constraint built from a criteria list with nothing in it.
|
|
163
|
+
*
|
|
164
|
+
* @remarks Reported where the list is passed rather than where it fails to match, because it does
|
|
165
|
+
* not fail to match — "carries all of no criteria" holds for any ancestor, so the constraint quietly
|
|
166
|
+
* becomes a weaker one that still outranks an unconstrained binding.
|
|
167
|
+
*
|
|
168
|
+
* @since 0.6.0
|
|
169
|
+
*/
|
|
170
|
+
export class EmptyTagCriteriaError extends DiError {
|
|
171
|
+
readonly code = "EMPTY_TAG_CRITERIA";
|
|
172
|
+
readonly helperName: string;
|
|
173
|
+
|
|
174
|
+
constructor(helperName: string) {
|
|
175
|
+
super(
|
|
176
|
+
`${helperName}() was given no criteria. An empty list matches any ancestor, which is not what the call says — pass the criteria to require, or drop the constraint if there are none.`,
|
|
177
|
+
);
|
|
178
|
+
this.helperName = helperName;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* A constraint waiting on a slot name that nothing in the container chain declares.
|
|
184
|
+
*
|
|
185
|
+
* @remarks A name is a bare string, so a typo produces a constraint that is never satisfied and
|
|
186
|
+
* never reported. Reported by `validate()` rather than at bind time, because the binding carrying
|
|
187
|
+
* the name may be registered after the constraint is built.
|
|
188
|
+
*
|
|
189
|
+
* @since 0.6.0
|
|
190
|
+
*/
|
|
191
|
+
export class UnreachableConstraintError extends DiError {
|
|
192
|
+
readonly code = "UNREACHABLE_CONSTRAINT";
|
|
193
|
+
readonly tokenName: string;
|
|
194
|
+
readonly requiredName: string;
|
|
195
|
+
readonly helperName: string;
|
|
196
|
+
|
|
197
|
+
constructor(tokenName: string, requiredName: string, helperName: string) {
|
|
198
|
+
super(
|
|
199
|
+
`The binding for '${tokenName}' is constrained by ${helperName}('${requiredName}'), but no binding in this container or its ancestors declares the slot name '${requiredName}', so the constraint can never hold. Name the slot with .whenNamed('${requiredName}') on the binding it should match, or correct the name here.`,
|
|
200
|
+
);
|
|
201
|
+
this.tokenName = tokenName;
|
|
202
|
+
this.requiredName = requiredName;
|
|
203
|
+
this.helperName = helperName;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* A container-level lifecycle hook whose token nothing is bound to, so it can never run.
|
|
209
|
+
*
|
|
210
|
+
* @remarks Hooks are keyed by token identity, which makes a class that is only ever a `to()` target
|
|
211
|
+
* look like a token and match nothing. Reported by `validate()` rather than at registration, because
|
|
212
|
+
* binding after registering the hook is a supported order.
|
|
213
|
+
*
|
|
214
|
+
* @since 0.6.0
|
|
215
|
+
*/
|
|
216
|
+
export class UnreachableLifecycleHookError extends DiError {
|
|
217
|
+
readonly code = "UNREACHABLE_LIFECYCLE_HOOK";
|
|
218
|
+
readonly tokenName: string;
|
|
219
|
+
readonly phase: "onActivation" | "onDeactivation";
|
|
220
|
+
|
|
221
|
+
constructor(tokenName: string, phase: "onActivation" | "onDeactivation") {
|
|
222
|
+
super(
|
|
223
|
+
`${phase}() is registered for '${tokenName}', which nothing is bound to in this container or its ancestors, so the hook can never run. Bind the token, or — if '${tokenName}' is a class you bound as an implementation via .to(${tokenName}) — register the hook against the token you bound instead.`,
|
|
224
|
+
);
|
|
225
|
+
this.tokenName = tokenName;
|
|
226
|
+
this.phase = phase;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* A {@link MetadataReader} described a class with something the container cannot use.
|
|
232
|
+
*
|
|
233
|
+
* @remarks Separate from {@link MissingMetadataError}: absent metadata is a class the container was
|
|
234
|
+
* never told about, while invalid metadata is a reader that answered wrongly. Covers both the
|
|
235
|
+
* constructor answer and the lifecycle one, since only the `reason` differs.
|
|
236
|
+
*
|
|
237
|
+
* @since 0.6.0
|
|
238
|
+
*/
|
|
239
|
+
export class InvalidMetadataError extends DiError {
|
|
240
|
+
readonly code = "INVALID_METADATA";
|
|
241
|
+
readonly targetName: string;
|
|
242
|
+
readonly reason: string;
|
|
243
|
+
|
|
244
|
+
constructor(targetName: string, reason: string) {
|
|
245
|
+
super(
|
|
246
|
+
`MetadataReader returned invalid metadata for class '${targetName}': ${reason}. Check the reader bound to MetadataReaderToken or passed to Container.create().`,
|
|
247
|
+
);
|
|
248
|
+
this.targetName = targetName;
|
|
249
|
+
this.reason = reason;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
144
253
|
/**
|
|
145
254
|
* @since 0.3.16-canary.0
|
|
146
255
|
*/
|
|
@@ -198,17 +307,29 @@ export class MissingScopeContextError extends DiError {
|
|
|
198
307
|
}
|
|
199
308
|
|
|
200
309
|
/**
|
|
310
|
+
* An `@inject` accessor initialized with no container context open.
|
|
311
|
+
*
|
|
312
|
+
* @remarks Carries the class and the accessor separately: a nameless class is possible (an anonymous
|
|
313
|
+
* class expression has an empty `name`), and a message that claims a class it does not have is what
|
|
314
|
+
* sent readers looking for a class called `clock`.
|
|
315
|
+
*
|
|
201
316
|
* @since 0.3.16-canary.0
|
|
202
317
|
*/
|
|
203
318
|
export class MissingContainerContextError extends DiError {
|
|
204
319
|
readonly code = "MISSING_CONTAINER_CONTEXT";
|
|
205
|
-
|
|
320
|
+
/** The class being constructed, or `undefined` when it has no readable name. */
|
|
321
|
+
readonly className: string | undefined;
|
|
322
|
+
readonly accessorName: string | symbol;
|
|
206
323
|
|
|
207
|
-
constructor(
|
|
324
|
+
constructor(className: string | undefined, accessorName: string | symbol) {
|
|
325
|
+
const accessor = `@inject accessor '${String(accessorName)}'`;
|
|
208
326
|
super(
|
|
209
|
-
|
|
327
|
+
className === undefined
|
|
328
|
+
? `An ${accessor} was initialized outside a container context. Resolve its class through a container, or open a context with runWithContainer().`
|
|
329
|
+
: `Class '${className}' has an ${accessor} but was constructed outside a container context. Resolve it via container.resolve(${className}), or open a context with runWithContainer().`,
|
|
210
330
|
);
|
|
211
|
-
this.
|
|
331
|
+
this.className = className;
|
|
332
|
+
this.accessorName = accessorName;
|
|
212
333
|
}
|
|
213
334
|
}
|
|
214
335
|
|
|
@@ -265,6 +386,29 @@ export class SelfBindingRequiresClassError extends DiError {
|
|
|
265
386
|
}
|
|
266
387
|
}
|
|
267
388
|
|
|
389
|
+
/**
|
|
390
|
+
* A decorator that acts on one instance was applied to a static member.
|
|
391
|
+
*
|
|
392
|
+
* @remarks Instance-only by construction: `@inject` resolves through the container active while an
|
|
393
|
+
* instance is built, and `@postConstruct`/`@preDestroy` bracket one instance's lifecycle. A static
|
|
394
|
+
* member belongs to the class, which no container constructs.
|
|
395
|
+
*
|
|
396
|
+
* @since 0.6.0
|
|
397
|
+
*/
|
|
398
|
+
export class StaticMemberDecoratorError extends DiError {
|
|
399
|
+
readonly code = "STATIC_MEMBER_DECORATOR";
|
|
400
|
+
readonly decoratorName: string;
|
|
401
|
+
readonly memberName: string;
|
|
402
|
+
|
|
403
|
+
constructor(decoratorName: string, memberName: string) {
|
|
404
|
+
super(
|
|
405
|
+
`@${decoratorName}() applies to instance members only, and '${memberName}' is static. Move it to an instance member, or read the value from the container where the static member is used.`,
|
|
406
|
+
);
|
|
407
|
+
this.decoratorName = decoratorName;
|
|
408
|
+
this.memberName = memberName;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
268
412
|
/**
|
|
269
413
|
* @since 0.3.16-canary.0
|
|
270
414
|
*/
|