@codefast/di 0.3.14-canary.0 → 0.3.14-canary.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +54 -29
- package/dist/binding-scope.d.mts +11 -0
- package/dist/binding-scope.mjs +19 -0
- package/dist/binding-select.d.mts +8 -26
- package/dist/binding-select.mjs +56 -49
- package/dist/binding.d.mts +107 -327
- package/dist/binding.mjs +19 -324
- package/dist/constraints.d.mts +11 -29
- package/dist/constraints.mjs +32 -36
- package/dist/constructor-type.d.mts +17 -0
- package/dist/constructor-type.mjs +1 -0
- package/dist/container.d.mts +44 -128
- package/dist/container.mjs +664 -433
- package/dist/decorators/inject.d.mts +19 -50
- package/dist/decorators/inject.mjs +131 -93
- package/dist/decorators/injectable.d.mts +16 -37
- package/dist/decorators/injectable.mjs +47 -66
- package/dist/decorators/lifecycle-decorators.d.mts +2 -22
- package/dist/decorators/lifecycle-decorators.mjs +81 -37
- package/dist/dependency-graph.d.mts +24 -54
- package/dist/dependency-graph.mjs +51 -153
- package/dist/environment.d.mts +38 -12
- package/dist/environment.mjs +82 -16
- package/dist/errors.d.mts +69 -188
- package/dist/errors.mjs +92 -219
- package/dist/graph-adapters/cytoscape.d.mts +24 -0
- package/dist/graph-adapters/cytoscape.mjs +23 -0
- package/dist/graph-adapters/dot.d.mts +6 -0
- package/dist/graph-adapters/dot.mjs +17 -0
- package/dist/graph-adapters/reactflow.d.mts +29 -0
- package/dist/graph-adapters/reactflow.mjs +29 -0
- package/dist/graph-adapters/types.d.mts +2 -0
- package/dist/graph-adapters/types.mjs +1 -0
- package/dist/index.d.mts +16 -9
- package/dist/index.mjs +8 -5
- package/dist/inspector.d.mts +34 -95
- package/dist/inspector.mjs +57 -256
- package/dist/lifecycle.d.mts +20 -53
- package/dist/lifecycle.mjs +129 -99
- package/dist/metadata/metadata-keys.d.mts +9 -26
- package/dist/metadata/metadata-keys.mjs +7 -28
- package/dist/metadata/metadata-reader-token.d.mts +7 -0
- package/dist/metadata/metadata-reader-token.mjs +5 -0
- package/dist/metadata/metadata-types.d.mts +22 -71
- package/dist/metadata/symbol-metadata-reader.d.mts +10 -26
- package/dist/metadata/symbol-metadata-reader.mjs +32 -45
- package/dist/module.d.mts +30 -84
- package/dist/module.mjs +26 -72
- package/dist/registry.d.mts +32 -63
- package/dist/registry.mjs +131 -82
- package/dist/resolve-options.d.mts +18 -0
- package/dist/resolve-options.mjs +22 -0
- package/dist/resolver.d.mts +67 -190
- package/dist/resolver.mjs +715 -424
- package/dist/scope.d.mts +19 -102
- package/dist/scope.mjs +37 -192
- package/dist/token.d.mts +8 -22
- package/dist/token.mjs +9 -11
- package/dist/types.d.mts +48 -0
- package/dist/types.mjs +1 -0
- package/package.json +52 -14
- package/dist/metadata/param-registry.d.mts +0 -16
- package/dist/metadata/param-registry.mjs +0 -31
- package/dist/scope-validation.d.mts +0 -21
- package/dist/scope-validation.mjs +0 -35
|
@@ -1,55 +1,24 @@
|
|
|
1
|
+
import { Constructor } from "../constructor-type.mjs";
|
|
1
2
|
import { Token } from "../token.mjs";
|
|
2
|
-
import { Constructor, ResolveHint } from "../binding.mjs";
|
|
3
|
-
import { InjectionDescriptor } from "../metadata/metadata-types.mjs";
|
|
4
3
|
|
|
5
4
|
//#region src/decorators/inject.d.ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* @injectable([inject(Logger, { name: 'file' })])
|
|
19
|
-
* class UserService { constructor(log: Logger) {} }
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* **2. As a Stage 3 accessor decorator** — writes accessor-injection metadata into
|
|
23
|
-
* `Symbol.metadata` and returns a no-op sentinel. The container performs the actual
|
|
24
|
-
* injection after construction.
|
|
25
|
-
*
|
|
26
|
-
* ```ts
|
|
27
|
-
* @inject(Logger) accessor logger!: LoggerService;
|
|
28
|
-
* ```
|
|
29
|
-
*
|
|
30
|
-
* @param token - The injection key (token or constructor) to resolve.
|
|
31
|
-
* @param optionsOrContext - Either an {@link InjectOptions} hint or the TC39
|
|
32
|
-
* `ClassAccessorDecoratorContext` automatically supplied by the runtime.
|
|
33
|
-
*/
|
|
34
|
-
declare function inject<Value>(token: Token<Value> | Constructor<Value>, optionsOrContext?: InjectOptions | ClassAccessorDecoratorContext): InjectionDescriptor<Value>;
|
|
35
|
-
/**
|
|
36
|
-
* Same as {@link inject} but marks the dependency as optional (`InjectionDescriptor.optional = true`).
|
|
37
|
-
* During resolution, an unbound token resolves to `undefined` instead of throwing
|
|
38
|
-
* {@link TokenNotBoundError}. Only usable as a deps-array entry (not as an accessor decorator).
|
|
39
|
-
*/
|
|
40
|
-
declare function optional<Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value>;
|
|
41
|
-
/**
|
|
42
|
-
* Deps-array helper for `@injectable()`: injects **all** bindings registered for `token`
|
|
43
|
-
* (same semantics as {@link Container.resolveAll} / {@link ResolutionContext.resolveAll}).
|
|
44
|
-
* Use for multi-binding — constructor parameter type should be `T[]` (or a readonly array).
|
|
45
|
-
*
|
|
46
|
-
* Optional {@link InjectOptions.name} / `tag` narrow which bindings are collected (unusual; most
|
|
47
|
-
* callers omit options and register disambiguators on each binding instead).
|
|
48
|
-
*/
|
|
49
|
-
declare function injectAll<Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value>;
|
|
50
|
-
/**
|
|
51
|
-
* Type-guard — returns `true` when `value` is an {@link InjectionDescriptor}.
|
|
52
|
-
*/
|
|
5
|
+
interface InjectOptions {
|
|
6
|
+
name?: string;
|
|
7
|
+
tags?: ReadonlyArray<readonly [tag: string, value: unknown]>;
|
|
8
|
+
}
|
|
9
|
+
interface InjectionDescriptor<Value = unknown> {
|
|
10
|
+
readonly token: Token<Value> | Constructor<Value>;
|
|
11
|
+
readonly optional: boolean;
|
|
12
|
+
readonly multi: boolean;
|
|
13
|
+
readonly name?: string;
|
|
14
|
+
readonly tags?: ReadonlyArray<readonly [string, unknown]>;
|
|
15
|
+
}
|
|
16
|
+
type InjectableDependency<Value = unknown> = Token<Value> | Constructor<Value> | InjectionDescriptor<Value>;
|
|
53
17
|
declare function isInjectionDescriptor(value: unknown): value is InjectionDescriptor;
|
|
18
|
+
declare function normalizeToDescriptor(dep: InjectableDependency): InjectionDescriptor;
|
|
19
|
+
type ClassAccessorDecorator<This, Value> = (target: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>) => ClassAccessorDecoratorResult<This, Value> | void;
|
|
20
|
+
declare function inject<const Value>(t: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value> & ClassAccessorDecorator<unknown, Value>;
|
|
21
|
+
declare function optional<const Value>(t: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value | undefined>;
|
|
22
|
+
declare function injectAll<const Value>(t: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value[]>;
|
|
54
23
|
//#endregion
|
|
55
|
-
export { InjectOptions, inject, injectAll, isInjectionDescriptor, optional };
|
|
24
|
+
export { InjectOptions, InjectableDependency, InjectionDescriptor, inject, injectAll, isInjectionDescriptor, normalizeToDescriptor, optional };
|
|
@@ -1,108 +1,146 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { MissingContainerContextError } from "../errors.mjs";
|
|
2
|
+
import { getActiveContainer } from "../environment.mjs";
|
|
3
|
+
import { injectableSlotToResolveOptions } from "../resolve-options.mjs";
|
|
4
|
+
import { INJECT_ACCESSOR_KEY } from "../metadata/metadata-keys.mjs";
|
|
3
5
|
//#region src/decorators/inject.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (
|
|
12
|
-
return
|
|
6
|
+
function isInjectionDescriptor(value) {
|
|
7
|
+
if (value === null || value === void 0) return false;
|
|
8
|
+
const type = typeof value;
|
|
9
|
+
if (type !== "object" && type !== "function") return false;
|
|
10
|
+
return "token" in value && "optional" in value && "multi" in value && typeof value.optional === "boolean" && typeof value.multi === "boolean";
|
|
11
|
+
}
|
|
12
|
+
function normalizeToDescriptor(dep) {
|
|
13
|
+
if (isInjectionDescriptor(dep)) return materializeInjectionDescriptor(dep);
|
|
14
|
+
return {
|
|
15
|
+
token: dep,
|
|
16
|
+
optional: false,
|
|
17
|
+
multi: false
|
|
18
|
+
};
|
|
13
19
|
}
|
|
14
20
|
/**
|
|
15
|
-
*
|
|
21
|
+
* Dual-role `inject()` values are functions: [[Function]].name must not be treated as a DI slot name.
|
|
22
|
+
* Only enumerable own `name` / `tags` from `Object.defineProperties` are real injection options.
|
|
16
23
|
*/
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
function materializeInjectionDescriptor(dep) {
|
|
25
|
+
if (typeof dep !== "function") return dep;
|
|
26
|
+
const dualRole = dep;
|
|
27
|
+
const base = {
|
|
28
|
+
token: dualRole.token,
|
|
29
|
+
optional: dualRole.optional,
|
|
30
|
+
multi: dualRole.multi
|
|
23
31
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
const nameDesc = Object.getOwnPropertyDescriptor(dualRole, "name");
|
|
33
|
+
const tagsDesc = Object.getOwnPropertyDescriptor(dualRole, "tags");
|
|
34
|
+
const explicitName = nameDesc?.enumerable === true && typeof nameDesc.value === "string" ? nameDesc.value : void 0;
|
|
35
|
+
const explicitTags = tagsDesc?.enumerable === true ? tagsDesc.value : void 0;
|
|
36
|
+
if (explicitName !== void 0 && explicitTags !== void 0) return {
|
|
37
|
+
...base,
|
|
38
|
+
name: explicitName,
|
|
39
|
+
tags: explicitTags
|
|
28
40
|
};
|
|
29
|
-
return {
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
if (explicitName !== void 0) return {
|
|
42
|
+
...base,
|
|
43
|
+
name: explicitName
|
|
32
44
|
};
|
|
45
|
+
if (explicitTags !== void 0) return {
|
|
46
|
+
...base,
|
|
47
|
+
tags: explicitTags
|
|
48
|
+
};
|
|
49
|
+
return base;
|
|
33
50
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
function buildInjectionDescriptor(t, options) {
|
|
52
|
+
const base = {
|
|
53
|
+
token: t,
|
|
54
|
+
optional: false,
|
|
55
|
+
multi: false
|
|
56
|
+
};
|
|
57
|
+
if (options?.name !== void 0 && options.tags !== void 0) return {
|
|
58
|
+
...base,
|
|
59
|
+
name: options.name,
|
|
60
|
+
tags: options.tags
|
|
61
|
+
};
|
|
62
|
+
if (options?.name !== void 0) return {
|
|
63
|
+
...base,
|
|
64
|
+
name: options.name
|
|
65
|
+
};
|
|
66
|
+
if (options?.tags !== void 0) return {
|
|
67
|
+
...base,
|
|
68
|
+
tags: options.tags
|
|
69
|
+
};
|
|
70
|
+
return base;
|
|
39
71
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
* class UserService { constructor(log: Logger) {} }
|
|
49
|
-
* ```
|
|
50
|
-
*
|
|
51
|
-
* **2. As a Stage 3 accessor decorator** — writes accessor-injection metadata into
|
|
52
|
-
* `Symbol.metadata` and returns a no-op sentinel. The container performs the actual
|
|
53
|
-
* injection after construction.
|
|
54
|
-
*
|
|
55
|
-
* ```ts
|
|
56
|
-
* @inject(Logger) accessor logger!: LoggerService;
|
|
57
|
-
* ```
|
|
58
|
-
*
|
|
59
|
-
* @param token - The injection key (token or constructor) to resolve.
|
|
60
|
-
* @param optionsOrContext - Either an {@link InjectOptions} hint or the TC39
|
|
61
|
-
* `ClassAccessorDecoratorContext` automatically supplied by the runtime.
|
|
62
|
-
*/
|
|
63
|
-
function inject(token, optionsOrContext) {
|
|
64
|
-
if (isAccessorDecoratorContext(optionsOrContext)) {
|
|
65
|
-
const ctx = optionsOrContext;
|
|
66
|
-
const metaRecord = ctx.metadata;
|
|
67
|
-
const key = CODEFAST_DI_ACCESSOR_INJECTIONS;
|
|
68
|
-
if (!Array.isArray(metaRecord[key])) metaRecord[key] = [];
|
|
69
|
-
metaRecord[key].push({
|
|
70
|
-
name: String(ctx.name),
|
|
71
|
-
token,
|
|
72
|
-
optional: false
|
|
72
|
+
function inject(t, options) {
|
|
73
|
+
const descriptor = buildInjectionDescriptor(t, options);
|
|
74
|
+
const decoratorFn = (_target, context) => {
|
|
75
|
+
const meta = context.metadata;
|
|
76
|
+
if (!Array.isArray(meta[INJECT_ACCESSOR_KEY])) meta[INJECT_ACCESSOR_KEY] = [];
|
|
77
|
+
meta[INJECT_ACCESSOR_KEY].push({
|
|
78
|
+
key: context.name,
|
|
79
|
+
descriptor
|
|
73
80
|
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
context.addInitializer(function() {
|
|
82
|
+
const container = getActiveContainer();
|
|
83
|
+
if (container === void 0) throw new MissingContainerContextError(String(context.name));
|
|
84
|
+
const hint = options === void 0 ? void 0 : injectableSlotToResolveOptions({
|
|
85
|
+
...options.name !== void 0 ? { name: options.name } : {},
|
|
86
|
+
...options.tags !== void 0 ? { tags: options.tags } : {}
|
|
87
|
+
});
|
|
88
|
+
const value = descriptor.optional ? container.resolveOptional(t, hint) : container.resolve(t, hint);
|
|
89
|
+
context.access.set(this, value);
|
|
90
|
+
});
|
|
91
|
+
return {};
|
|
92
|
+
};
|
|
93
|
+
const props = {};
|
|
94
|
+
for (const key of Object.keys(descriptor)) props[key] = {
|
|
95
|
+
value: descriptor[key],
|
|
96
|
+
writable: true,
|
|
97
|
+
enumerable: true,
|
|
98
|
+
configurable: true
|
|
99
|
+
};
|
|
100
|
+
Object.defineProperties(decoratorFn, props);
|
|
101
|
+
return decoratorFn;
|
|
85
102
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
function optional(t, options) {
|
|
104
|
+
const base = {
|
|
105
|
+
token: t,
|
|
106
|
+
optional: true,
|
|
107
|
+
multi: false
|
|
108
|
+
};
|
|
109
|
+
if (options?.name !== void 0 && options.tags !== void 0) return {
|
|
110
|
+
...base,
|
|
111
|
+
name: options.name,
|
|
112
|
+
tags: options.tags
|
|
113
|
+
};
|
|
114
|
+
if (options?.name !== void 0) return {
|
|
115
|
+
...base,
|
|
116
|
+
name: options.name
|
|
98
117
|
};
|
|
118
|
+
if (options?.tags !== void 0) return {
|
|
119
|
+
...base,
|
|
120
|
+
tags: options.tags
|
|
121
|
+
};
|
|
122
|
+
return base;
|
|
99
123
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
124
|
+
function injectAll(t, options) {
|
|
125
|
+
const base = {
|
|
126
|
+
token: t,
|
|
127
|
+
optional: false,
|
|
128
|
+
multi: true
|
|
129
|
+
};
|
|
130
|
+
if (options?.name !== void 0 && options.tags !== void 0) return {
|
|
131
|
+
...base,
|
|
132
|
+
name: options.name,
|
|
133
|
+
tags: options.tags
|
|
134
|
+
};
|
|
135
|
+
if (options?.name !== void 0) return {
|
|
136
|
+
...base,
|
|
137
|
+
name: options.name
|
|
138
|
+
};
|
|
139
|
+
if (options?.tags !== void 0) return {
|
|
140
|
+
...base,
|
|
141
|
+
tags: options.tags
|
|
142
|
+
};
|
|
143
|
+
return base;
|
|
106
144
|
}
|
|
107
145
|
//#endregion
|
|
108
|
-
export { inject, injectAll, isInjectionDescriptor, optional };
|
|
146
|
+
export { inject, injectAll, isInjectionDescriptor, normalizeToDescriptor, optional };
|
|
@@ -1,41 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BindingScope
|
|
3
|
-
import {
|
|
1
|
+
import { Constructor } from "../constructor-type.mjs";
|
|
2
|
+
import { BindingScope } from "../types.mjs";
|
|
3
|
+
import { InjectableDependency } from "./inject.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/decorators/injectable.d.ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
declare function getAutoRegistered(): ReadonlyArray<{
|
|
18
|
-
implementationClass: Constructor<unknown>;
|
|
19
|
-
scope: BindingScope;
|
|
20
|
-
}>;
|
|
21
|
-
/**
|
|
22
|
-
* Stage 3 class decorator that writes constructor dependency metadata into `Symbol.metadata`.
|
|
23
|
-
*
|
|
24
|
-
* @param deps - Ordered list of constructor parameters; length must match the class arity.
|
|
25
|
-
* @param autoRegisterOptions - Optional auto-registration flags.
|
|
26
|
-
* @param autoRegisterOptions.autoRegister - When `true`, registers the class in {@link getAutoRegistered} at
|
|
27
|
-
* class-definition time so {@link Container.loadAutoRegistered} can bind it automatically.
|
|
28
|
-
* @param autoRegisterOptions.scope - Scope used when auto-registering; defaults to `"transient"`.
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```ts
|
|
32
|
-
* @injectable([Logger, inject(Config, { name: "app" })])
|
|
33
|
-
* class UserService { constructor(log: Logger, cfg: AppConfig) {} }
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
declare function injectable(deps?: readonly InjectableDependency[], autoRegisterOptions?: {
|
|
37
|
-
autoRegister?: boolean;
|
|
6
|
+
interface AutoRegisterRegistry {
|
|
7
|
+
register(target: Constructor, scope: BindingScope): void;
|
|
8
|
+
entries(): ReadonlyArray<{
|
|
9
|
+
target: Constructor;
|
|
10
|
+
scope: BindingScope;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
declare function createAutoRegisterRegistry(): AutoRegisterRegistry;
|
|
14
|
+
interface InjectableOptions {
|
|
15
|
+
autoRegister?: AutoRegisterRegistry;
|
|
38
16
|
scope?: BindingScope;
|
|
39
|
-
}
|
|
17
|
+
}
|
|
18
|
+
declare function injectable(deps?: readonly InjectableDependency[], options?: InjectableOptions): (target: unknown, context: ClassDecoratorContext) => void;
|
|
40
19
|
//#endregion
|
|
41
|
-
export { InjectableDependency,
|
|
20
|
+
export { AutoRegisterRegistry, type InjectableDependency, InjectableOptions, createAutoRegisterRegistry, injectable };
|
|
@@ -1,74 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { isInjectionDescriptor } from "./inject.mjs";
|
|
1
|
+
import { INJECTABLE_KEY, constructorMetadataMap } from "../metadata/metadata-keys.mjs";
|
|
2
|
+
import { normalizeToDescriptor } from "./inject.mjs";
|
|
4
3
|
//#region src/decorators/injectable.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* Populated at class-definition time (via `context.addInitializer`), drained by
|
|
8
|
-
* {@link Container.loadAutoRegistered}. Entries accumulate for the lifetime of the process.
|
|
9
|
-
*/
|
|
10
|
-
const AUTO_REGISTER_REGISTRY = [];
|
|
11
|
-
/**
|
|
12
|
-
* Returns all classes decorated with `@injectable({ autoRegister: true })`.
|
|
13
|
-
* Pass to {@link Container.loadAutoRegistered} or iterate manually to bind them.
|
|
14
|
-
*/
|
|
15
|
-
function getAutoRegistered() {
|
|
16
|
-
return AUTO_REGISTER_REGISTRY;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Normalises a single `@injectable` deps-array entry into the uniform {@link ParamMetadata}
|
|
20
|
-
* shape used by the resolver's constructor-instantiation path.
|
|
21
|
-
*
|
|
22
|
-
* - {@link InjectionDescriptor} entries carry `optional`, `name`, and `tag` fields.
|
|
23
|
-
* - Plain token / constructor entries are wrapped with `optional: false` and no hint.
|
|
24
|
-
*/
|
|
25
|
-
function toParamMetadata(dependency, index) {
|
|
26
|
-
if (isInjectionDescriptor(dependency)) return {
|
|
27
|
-
index,
|
|
28
|
-
token: dependency.token,
|
|
29
|
-
optional: dependency.optional,
|
|
30
|
-
name: dependency.name,
|
|
31
|
-
tag: dependency.tag,
|
|
32
|
-
all: dependency.all === true ? true : void 0
|
|
33
|
-
};
|
|
4
|
+
function createAutoRegisterRegistry() {
|
|
5
|
+
const _entries = [];
|
|
34
6
|
return {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
7
|
+
register(target, scope) {
|
|
8
|
+
_entries.push({
|
|
9
|
+
target,
|
|
10
|
+
scope
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
entries() {
|
|
14
|
+
return _entries;
|
|
15
|
+
}
|
|
38
16
|
};
|
|
39
17
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
18
|
+
function injectable(deps, options) {
|
|
19
|
+
return function(target, context) {
|
|
20
|
+
const constructorMeta = { params: (deps ?? []).map((dep, index) => {
|
|
21
|
+
const descriptor = normalizeToDescriptor(dep);
|
|
22
|
+
const base = {
|
|
23
|
+
index,
|
|
24
|
+
token: descriptor.token,
|
|
25
|
+
optional: descriptor.optional,
|
|
26
|
+
multi: descriptor.multi
|
|
27
|
+
};
|
|
28
|
+
if (descriptor.name !== void 0 && descriptor.tags !== void 0) return {
|
|
29
|
+
...base,
|
|
30
|
+
name: descriptor.name,
|
|
31
|
+
tags: descriptor.tags
|
|
32
|
+
};
|
|
33
|
+
if (descriptor.name !== void 0) return {
|
|
34
|
+
...base,
|
|
35
|
+
name: descriptor.name
|
|
36
|
+
};
|
|
37
|
+
if (descriptor.tags !== void 0) return {
|
|
38
|
+
...base,
|
|
39
|
+
tags: descriptor.tags
|
|
40
|
+
};
|
|
41
|
+
return base;
|
|
42
|
+
}) };
|
|
43
|
+
constructorMetadataMap.set(target, constructorMeta);
|
|
44
|
+
try {
|
|
45
|
+
const meta = context.metadata;
|
|
46
|
+
if (meta !== null && typeof meta === "object") meta[INJECTABLE_KEY] = constructorMeta;
|
|
47
|
+
} catch {}
|
|
48
|
+
if (options?.autoRegister !== void 0) {
|
|
49
|
+
const scope = options.scope ?? "transient";
|
|
50
|
+
options.autoRegister.register(target, scope);
|
|
70
51
|
}
|
|
71
52
|
};
|
|
72
53
|
}
|
|
73
54
|
//#endregion
|
|
74
|
-
export {
|
|
55
|
+
export { createAutoRegisterRegistry, injectable };
|
|
@@ -1,25 +1,5 @@
|
|
|
1
1
|
//#region src/decorators/lifecycle-decorators.d.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* by the container and before the `onActivation` hook runs.
|
|
5
|
-
*
|
|
6
|
-
* Lifecycle order: `new Class(…)` → **`@postConstruct()`** → `onActivation()` → scope cache.
|
|
7
|
-
*
|
|
8
|
-
* Only one method per class may carry this decorator; a second application throws.
|
|
9
|
-
* If the decorated method returns a `Promise` during synchronous resolution,
|
|
10
|
-
* {@link AsyncResolutionError} is thrown — use `Container.resolveAsync()` instead.
|
|
11
|
-
*/
|
|
12
|
-
declare function postConstruct(): (target: () => unknown, context: ClassMethodDecoratorContext) => void;
|
|
13
|
-
/**
|
|
14
|
-
* Stage 3 method decorator: marks a method to be called when the container disposes or
|
|
15
|
-
* unloads the owning binding.
|
|
16
|
-
*
|
|
17
|
-
* Lifecycle order: `onDeactivation()` → **`@preDestroy()`**.
|
|
18
|
-
*
|
|
19
|
-
* Only one method per class may carry this decorator; a second application throws.
|
|
20
|
-
* If the decorated method returns a `Promise` during synchronous disposal,
|
|
21
|
-
* an error is thrown — use `Container.disposeAsync()` instead.
|
|
22
|
-
*/
|
|
23
|
-
declare function preDestroy(): (target: () => unknown, context: ClassMethodDecoratorContext) => void;
|
|
2
|
+
declare function postConstruct(): (target: unknown, context: ClassMethodDecoratorContext) => void;
|
|
3
|
+
declare function preDestroy(): (target: unknown, context: ClassMethodDecoratorContext) => void;
|
|
24
4
|
//#endregion
|
|
25
5
|
export { postConstruct, preDestroy };
|
|
@@ -1,45 +1,89 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LIFECYCLE_KEY, lifecycleByConstructorMetadataMap, lifecycleMetadataMap } from "../metadata/metadata-keys.mjs";
|
|
2
2
|
//#region src/decorators/lifecycle-decorators.ts
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
function appendUniqueMethod(metadata, phase, methodName) {
|
|
4
|
+
if (!metadata[phase].includes(methodName)) metadata[phase].push(methodName);
|
|
5
|
+
}
|
|
6
|
+
function resolveConstructorFromDecoratorTarget(target) {
|
|
7
|
+
if (typeof target === "function") return target;
|
|
8
|
+
if (typeof target === "object" && target !== null) {
|
|
9
|
+
const ctor = target.constructor;
|
|
10
|
+
if (typeof ctor === "function") return ctor;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function registerByConstructor(target, phase, methodName) {
|
|
14
|
+
const ctor = resolveConstructorFromDecoratorTarget(target);
|
|
15
|
+
if (ctor === void 0) return;
|
|
16
|
+
const ctorExisting = lifecycleByConstructorMetadataMap.get(ctor);
|
|
17
|
+
if (ctorExisting !== void 0) appendUniqueMethod(ctorExisting, phase, methodName);
|
|
18
|
+
else lifecycleByConstructorMetadataMap.set(ctor, {
|
|
19
|
+
postConstruct: phase === "postConstruct" ? [methodName] : [],
|
|
20
|
+
preDestroy: phase === "preDestroy" ? [methodName] : []
|
|
21
|
+
});
|
|
22
|
+
}
|
|
13
23
|
function postConstruct() {
|
|
14
|
-
return (
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
postConstruct:
|
|
21
|
-
|
|
24
|
+
return function(target, context) {
|
|
25
|
+
const methodName = String(context.name);
|
|
26
|
+
registerByConstructor(target, "postConstruct", methodName);
|
|
27
|
+
const existing = lifecycleMetadataMap.get(context.metadata);
|
|
28
|
+
if (existing !== void 0) appendUniqueMethod(existing, "postConstruct", methodName);
|
|
29
|
+
else lifecycleMetadataMap.set(context.metadata, {
|
|
30
|
+
postConstruct: [methodName],
|
|
31
|
+
preDestroy: []
|
|
32
|
+
});
|
|
33
|
+
context.addInitializer(function() {
|
|
34
|
+
const targetOrInstance = this;
|
|
35
|
+
const ctor = typeof targetOrInstance === "function" ? targetOrInstance : targetOrInstance.constructor;
|
|
36
|
+
const ctorExisting = lifecycleByConstructorMetadataMap.get(ctor);
|
|
37
|
+
if (ctorExisting !== void 0) appendUniqueMethod(ctorExisting, "postConstruct", methodName);
|
|
38
|
+
else lifecycleByConstructorMetadataMap.set(ctor, {
|
|
39
|
+
postConstruct: [methodName],
|
|
40
|
+
preDestroy: []
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
try {
|
|
44
|
+
const meta = context.metadata;
|
|
45
|
+
if (meta !== null && typeof meta === "object") {
|
|
46
|
+
if (!meta[LIFECYCLE_KEY]) meta[LIFECYCLE_KEY] = {
|
|
47
|
+
postConstruct: [],
|
|
48
|
+
preDestroy: []
|
|
49
|
+
};
|
|
50
|
+
const lifecycle = meta[LIFECYCLE_KEY];
|
|
51
|
+
appendUniqueMethod(lifecycle, "postConstruct", methodName);
|
|
52
|
+
}
|
|
53
|
+
} catch {}
|
|
22
54
|
};
|
|
23
55
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Stage 3 method decorator: marks a method to be called when the container disposes or
|
|
26
|
-
* unloads the owning binding.
|
|
27
|
-
*
|
|
28
|
-
* Lifecycle order: `onDeactivation()` → **`@preDestroy()`**.
|
|
29
|
-
*
|
|
30
|
-
* Only one method per class may carry this decorator; a second application throws.
|
|
31
|
-
* If the decorated method returns a `Promise` during synchronous disposal,
|
|
32
|
-
* an error is thrown — use `Container.disposeAsync()` instead.
|
|
33
|
-
*/
|
|
34
56
|
function preDestroy() {
|
|
35
|
-
return (
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
return function(target, context) {
|
|
58
|
+
const methodName = String(context.name);
|
|
59
|
+
registerByConstructor(target, "preDestroy", methodName);
|
|
60
|
+
const existing = lifecycleMetadataMap.get(context.metadata);
|
|
61
|
+
if (existing !== void 0) appendUniqueMethod(existing, "preDestroy", methodName);
|
|
62
|
+
else lifecycleMetadataMap.set(context.metadata, {
|
|
63
|
+
postConstruct: [],
|
|
64
|
+
preDestroy: [methodName]
|
|
65
|
+
});
|
|
66
|
+
context.addInitializer(function() {
|
|
67
|
+
const targetOrInstance = this;
|
|
68
|
+
const ctor = typeof targetOrInstance === "function" ? targetOrInstance : targetOrInstance.constructor;
|
|
69
|
+
const ctorExisting = lifecycleByConstructorMetadataMap.get(ctor);
|
|
70
|
+
if (ctorExisting !== void 0) appendUniqueMethod(ctorExisting, "preDestroy", methodName);
|
|
71
|
+
else lifecycleByConstructorMetadataMap.set(ctor, {
|
|
72
|
+
postConstruct: [],
|
|
73
|
+
preDestroy: [methodName]
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
const meta = context.metadata;
|
|
78
|
+
if (meta !== null && typeof meta === "object") {
|
|
79
|
+
if (!meta[LIFECYCLE_KEY]) meta[LIFECYCLE_KEY] = {
|
|
80
|
+
postConstruct: [],
|
|
81
|
+
preDestroy: []
|
|
82
|
+
};
|
|
83
|
+
const lifecycle = meta[LIFECYCLE_KEY];
|
|
84
|
+
appendUniqueMethod(lifecycle, "preDestroy", methodName);
|
|
85
|
+
}
|
|
86
|
+
} catch {}
|
|
43
87
|
};
|
|
44
88
|
}
|
|
45
89
|
//#endregion
|