@zudojs/container 0.0.1 → 1.0.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/README.md +205 -8
- package/dist/containerCore/containerCore.core.d.ts +105 -14
- package/dist/containerCore/containerCore.core.js +211 -44
- package/dist/containerCore/containerCore.scope.d.ts +63 -18
- package/dist/containerCore/containerCore.scope.js +108 -29
- package/dist/containerCore/containerCore.type.d.ts +22 -16
- package/dist/containerLifecycle/containerLifecycle.core.d.ts +59 -9
- package/dist/containerLifecycle/containerLifecycle.core.js +58 -15
- package/dist/containerOptions/containerOptions.type.d.ts +7 -17
- package/dist/containerOptions/containerOptions.type.js +0 -19
- package/dist/containerProvider/containerProvider.core.d.ts +11 -3
- package/dist/containerProvider/containerProvider.core.js +11 -5
- package/dist/containerRegistration/containerRegistration.core.d.ts +9 -3
- package/dist/containerRegistration/containerRegistration.core.js +0 -6
- package/dist/containerRegistry/containerRegistry.core.d.ts +23 -9
- package/dist/containerRegistry/containerRegistry.core.js +50 -47
- package/dist/containerRegistry/containerRegistry.type.d.ts +3 -1
- package/dist/containerRegistry/containerRegistry.type.js +2 -0
- package/dist/containerRegistry/index.d.ts +0 -1
- package/dist/containerRegistry/index.js +0 -1
- package/dist/containerResolution/containerResolution.core.d.ts +42 -10
- package/dist/containerResolution/containerResolution.core.js +207 -64
- package/dist/containerResolution/containerResolution.error.d.ts +68 -3
- package/dist/containerResolution/containerResolution.error.js +107 -3
- package/dist/containerResolution/containerResolution.type.d.ts +35 -3
- package/dist/containerResolution/index.d.ts +1 -0
- package/dist/containerResolution/index.js +1 -0
- package/dist/containerToken/containerToken.type.d.ts +26 -1
- package/dist/containerToken/containerToken.type.js +18 -0
- package/package.json +17 -8
- package/dist/.tsbuildinfo +0 -1
- package/dist/containerCore/containerCore.core.d.ts.map +0 -1
- package/dist/containerCore/containerCore.core.js.map +0 -1
- package/dist/containerCore/containerCore.scope.d.ts.map +0 -1
- package/dist/containerCore/containerCore.scope.js.map +0 -1
- package/dist/containerCore/containerCore.type.d.ts.map +0 -1
- package/dist/containerCore/containerCore.type.js.map +0 -1
- package/dist/containerCore/index.d.ts.map +0 -1
- package/dist/containerCore/index.js.map +0 -1
- package/dist/containerLifecycle/containerLifecycle.core.d.ts.map +0 -1
- package/dist/containerLifecycle/containerLifecycle.core.js.map +0 -1
- package/dist/containerLifecycle/index.d.ts.map +0 -1
- package/dist/containerLifecycle/index.js.map +0 -1
- package/dist/containerOptions/containerOptions.type.d.ts.map +0 -1
- package/dist/containerOptions/containerOptions.type.js.map +0 -1
- package/dist/containerOptions/index.d.ts.map +0 -1
- package/dist/containerOptions/index.js.map +0 -1
- package/dist/containerProvider/containerProvider.core.d.ts.map +0 -1
- package/dist/containerProvider/containerProvider.core.js.map +0 -1
- package/dist/containerProvider/index.d.ts.map +0 -1
- package/dist/containerProvider/index.js.map +0 -1
- package/dist/containerRegistration/containerRegistration.core.d.ts.map +0 -1
- package/dist/containerRegistration/containerRegistration.core.js.map +0 -1
- package/dist/containerRegistration/index.d.ts.map +0 -1
- package/dist/containerRegistration/index.js.map +0 -1
- package/dist/containerRegistry/containerRegistry.core.d.ts.map +0 -1
- package/dist/containerRegistry/containerRegistry.core.js.map +0 -1
- package/dist/containerRegistry/containerRegistry.error.d.ts +0 -12
- package/dist/containerRegistry/containerRegistry.error.d.ts.map +0 -1
- package/dist/containerRegistry/containerRegistry.error.js +0 -22
- package/dist/containerRegistry/containerRegistry.error.js.map +0 -1
- package/dist/containerRegistry/containerRegistry.type.d.ts.map +0 -1
- package/dist/containerRegistry/containerRegistry.type.js.map +0 -1
- package/dist/containerRegistry/index.d.ts.map +0 -1
- package/dist/containerRegistry/index.js.map +0 -1
- package/dist/containerResolution/containerResolution.core.d.ts.map +0 -1
- package/dist/containerResolution/containerResolution.core.js.map +0 -1
- package/dist/containerResolution/containerResolution.error.d.ts.map +0 -1
- package/dist/containerResolution/containerResolution.error.js.map +0 -1
- package/dist/containerResolution/containerResolution.type.d.ts.map +0 -1
- package/dist/containerResolution/containerResolution.type.js.map +0 -1
- package/dist/containerResolution/index.d.ts.map +0 -1
- package/dist/containerResolution/index.js.map +0 -1
- package/dist/containerScope/containerScope.type.d.ts.map +0 -1
- package/dist/containerScope/containerScope.type.js.map +0 -1
- package/dist/containerScope/index.d.ts.map +0 -1
- package/dist/containerScope/index.js.map +0 -1
- package/dist/containerToken/containerToken.type.d.ts.map +0 -1
- package/dist/containerToken/containerToken.type.js.map +0 -1
- package/dist/containerToken/index.d.ts.map +0 -1
- package/dist/containerToken/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Types for the core container.
|
|
3
3
|
*/
|
|
4
4
|
import type { RegistrationToken } from "../containerRegistration/containerRegistration.core.js";
|
|
5
|
+
import type { ResolutionCache, ResolutionResult } from "../containerResolution/containerResolution.type.js";
|
|
6
|
+
import type { ContainerScopeContext } from "./containerCore.scope.js";
|
|
5
7
|
/**
|
|
6
8
|
* Options used when creating a child container scope.
|
|
7
9
|
*/
|
|
@@ -16,25 +18,29 @@ export interface ContainerScopeOptions {
|
|
|
16
18
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
+
* The surface a ContainerScopeContext needs from its owning container.
|
|
22
|
+
* Implemented by Container; the `resolveInScope` / `createScopeCache` /
|
|
23
|
+
* `releaseScope` members are internal plumbing for scopes and should not be
|
|
24
|
+
* called by application code.
|
|
21
25
|
*/
|
|
22
26
|
export interface ContainerLike {
|
|
23
27
|
readonly name: string;
|
|
24
|
-
readonly resolver: {
|
|
25
|
-
createScope(): import("../containerResolution/containerResolution.type.js").ResolutionCache;
|
|
26
|
-
resolveDetailed(token: unknown, options: unknown): {
|
|
27
|
-
token: unknown;
|
|
28
|
-
value: unknown;
|
|
29
|
-
};
|
|
30
|
-
canResolve(token: unknown): boolean;
|
|
31
|
-
};
|
|
32
|
-
readonly resolutionOptions: {
|
|
33
|
-
autoRegisterClasses: boolean;
|
|
34
|
-
detectCircularDependencies: boolean;
|
|
35
|
-
maxResolutionDepth: number;
|
|
36
|
-
};
|
|
37
28
|
has<T>(token: RegistrationToken<T>): boolean;
|
|
38
|
-
|
|
29
|
+
canResolve<T>(token: RegistrationToken<T>): boolean;
|
|
30
|
+
isDisposed(): boolean;
|
|
31
|
+
createScope(options?: ContainerScopeOptions): ContainerScopeContext;
|
|
32
|
+
/**
|
|
33
|
+
* @internal Creates a fresh scoped-instance cache, optionally chained to a
|
|
34
|
+
* parent scope's cache (nested scopes).
|
|
35
|
+
*/
|
|
36
|
+
createScopeCache(parent?: ResolutionCache): ResolutionCache;
|
|
37
|
+
/**
|
|
38
|
+
* @internal Resolves a token using the given scope cache. `onInstanceCreated`
|
|
39
|
+
* is invoked for every instance created during the resolution (including
|
|
40
|
+
* transitively created dependencies) so the scope can track SCOPED ones.
|
|
41
|
+
*/
|
|
42
|
+
resolveInScope<T>(token: RegistrationToken<T>, cache: ResolutionCache, onInstanceCreated?: (result: ResolutionResult<unknown>) => void): ResolutionResult<T>;
|
|
43
|
+
/** @internal Unregisters a top-level scope from the container's live-scope set. */
|
|
44
|
+
releaseScope(scope: ContainerScopeContext): void;
|
|
39
45
|
}
|
|
40
46
|
//# sourceMappingURL=containerCore.type.d.ts.map
|
|
@@ -1,16 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lifecycle management for resolved container instances.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Tracks SINGLETON instances (container-owned) and SCOPED instances
|
|
5
|
+
* (scope-owned) that need cleanup and disposes them when their owner is
|
|
6
|
+
* destroyed, in reverse creation order.
|
|
7
|
+
*
|
|
8
|
+
* TRANSIENT instances are deliberately NOT tracked: the container creates a
|
|
9
|
+
* new transient per resolution, so tracking them per token would either leak
|
|
10
|
+
* (unbounded growth) or silently drop all but the last instance. Callers own
|
|
11
|
+
* the disposal of transient instances they resolve.
|
|
4
12
|
*/
|
|
5
13
|
import type { Token } from "../containerToken/containerToken.type.js";
|
|
6
14
|
import { ContainerLifecycleError } from "@zudojs/errors";
|
|
7
|
-
|
|
8
|
-
|
|
15
|
+
/**
|
|
16
|
+
* An object disposable via a `dispose()` method (sync or async).
|
|
17
|
+
* Named `DisposableLike` to avoid shadowing the ES2023 `Disposable` built-in.
|
|
18
|
+
*/
|
|
19
|
+
export interface DisposableLike {
|
|
20
|
+
dispose(): void | Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/** An object disposable via the ES2023 `Symbol.dispose` protocol. */
|
|
23
|
+
export interface SymbolDisposableLike {
|
|
24
|
+
[Symbol.dispose](): void;
|
|
9
25
|
}
|
|
10
|
-
|
|
11
|
-
|
|
26
|
+
/**
|
|
27
|
+
* An object disposable via the ES2023 `Symbol.asyncDispose` protocol.
|
|
28
|
+
* Named `AsyncDisposableLike` to avoid shadowing the `AsyncDisposable`
|
|
29
|
+
* built-in.
|
|
30
|
+
*/
|
|
31
|
+
export interface AsyncDisposableLike {
|
|
32
|
+
[Symbol.asyncDispose](): Promise<void> | void;
|
|
12
33
|
}
|
|
13
|
-
|
|
34
|
+
/** @deprecated Use {@link DisposableLike} instead. */
|
|
35
|
+
export type Disposable = DisposableLike;
|
|
36
|
+
/** @deprecated Use {@link AsyncDisposableLike} instead. */
|
|
37
|
+
export type AsyncDisposable = AsyncDisposableLike;
|
|
38
|
+
export type DisposableInstance = DisposableLike | SymbolDisposableLike | AsyncDisposableLike;
|
|
14
39
|
export declare enum ContainerLifecycleOwner {
|
|
15
40
|
CONTAINER = "container",
|
|
16
41
|
SCOPE = "scope"
|
|
@@ -30,26 +55,51 @@ export declare class ContainerDisposalError extends ContainerLifecycleError {
|
|
|
30
55
|
readonly tokens: readonly Token<unknown>[];
|
|
31
56
|
constructor(errors: readonly unknown[], tokens: readonly Token<unknown>[]);
|
|
32
57
|
}
|
|
33
|
-
export declare function isDisposable(value: unknown): value is
|
|
34
|
-
export declare function
|
|
58
|
+
export declare function isDisposable(value: unknown): value is DisposableLike;
|
|
59
|
+
export declare function isSymbolDisposable(value: unknown): value is SymbolDisposableLike;
|
|
60
|
+
export declare function isAsyncDisposable(value: unknown): value is AsyncDisposableLike;
|
|
35
61
|
export declare function isDisposableInstance(value: unknown): value is DisposableInstance;
|
|
36
62
|
export declare class ContainerLifecycle {
|
|
37
63
|
private readonly instances;
|
|
38
64
|
private readonly options;
|
|
39
65
|
private disposed;
|
|
40
66
|
constructor(options?: ContainerLifecycleOptions);
|
|
67
|
+
/**
|
|
68
|
+
* Tracks a disposable instance for later cleanup.
|
|
69
|
+
*
|
|
70
|
+
* Re-tracking a token removes the previous entry and re-inserts it at the
|
|
71
|
+
* end of the map, so reverse-creation disposal order stays sound.
|
|
72
|
+
* Non-disposable instances are ignored.
|
|
73
|
+
*/
|
|
41
74
|
track<T>(token: Token<T>, instance: T, owner?: ContainerLifecycleOwner): void;
|
|
42
75
|
has<T>(token: Token<T>): boolean;
|
|
43
76
|
get<T>(token: Token<T>): TrackedInstance<T> | undefined;
|
|
44
77
|
getAll(): readonly TrackedInstance[];
|
|
45
78
|
get size(): number;
|
|
46
79
|
untrack<T>(token: Token<T>): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Disposes a single tracked instance and untracks it. Untracking happens
|
|
82
|
+
* even when disposal fails — a failed disposal is terminal for the entry.
|
|
83
|
+
*/
|
|
47
84
|
disposeInstance<T>(token: Token<T>): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Disposes tracked instances in reverse creation order.
|
|
87
|
+
*
|
|
88
|
+
* Entries are untracked even when their disposal fails (terminal). When
|
|
89
|
+
* `owner` is omitted the whole lifecycle is marked disposed — this happens
|
|
90
|
+
* even on failure — and every failure is reported in the thrown
|
|
91
|
+
* ContainerDisposalError.
|
|
92
|
+
*/
|
|
48
93
|
dispose(owner?: ContainerLifecycleOwner): Promise<void>;
|
|
49
94
|
disposeScope(): Promise<void>;
|
|
50
95
|
disposeContainer(): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Releases all tracked references WITHOUT disposing them and marks the
|
|
98
|
+
* lifecycle disposed so further `track()` calls are refused. Used when a
|
|
99
|
+
* container is disposed with `autoDispose: false`.
|
|
100
|
+
*/
|
|
101
|
+
shutdown(): void;
|
|
51
102
|
isDisposed(): boolean;
|
|
52
|
-
reset(): void;
|
|
53
103
|
getTrackedTokens(): readonly Token<unknown>[];
|
|
54
104
|
}
|
|
55
105
|
export declare function createContainerLifecycle(options?: ContainerLifecycleOptions): ContainerLifecycle;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lifecycle management for resolved container instances.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Tracks SINGLETON instances (container-owned) and SCOPED instances
|
|
5
|
+
* (scope-owned) that need cleanup and disposes them when their owner is
|
|
6
|
+
* destroyed, in reverse creation order.
|
|
7
|
+
*
|
|
8
|
+
* TRANSIENT instances are deliberately NOT tracked: the container creates a
|
|
9
|
+
* new transient per resolution, so tracking them per token would either leak
|
|
10
|
+
* (unbounded growth) or silently drop all but the last instance. Callers own
|
|
11
|
+
* the disposal of transient instances they resolve.
|
|
4
12
|
*/
|
|
5
13
|
import { describeToken } from "../containerToken/containerToken.type.js";
|
|
6
14
|
import { ContainerLifecycleError } from "@zudojs/errors";
|
|
@@ -24,6 +32,12 @@ export function isDisposable(value) {
|
|
|
24
32
|
"dispose" in value &&
|
|
25
33
|
typeof value.dispose === "function");
|
|
26
34
|
}
|
|
35
|
+
export function isSymbolDisposable(value) {
|
|
36
|
+
return (typeof value === "object" &&
|
|
37
|
+
value !== null &&
|
|
38
|
+
Symbol.dispose in value &&
|
|
39
|
+
typeof value[Symbol.dispose] === "function");
|
|
40
|
+
}
|
|
27
41
|
export function isAsyncDisposable(value) {
|
|
28
42
|
return (typeof value === "object" &&
|
|
29
43
|
value !== null &&
|
|
@@ -31,7 +45,7 @@ export function isAsyncDisposable(value) {
|
|
|
31
45
|
typeof value[Symbol.asyncDispose] === "function");
|
|
32
46
|
}
|
|
33
47
|
export function isDisposableInstance(value) {
|
|
34
|
-
return isDisposable(value) || isAsyncDisposable(value);
|
|
48
|
+
return (isDisposable(value) || isSymbolDisposable(value) || isAsyncDisposable(value));
|
|
35
49
|
}
|
|
36
50
|
export class ContainerLifecycle {
|
|
37
51
|
instances = new Map();
|
|
@@ -40,11 +54,19 @@ export class ContainerLifecycle {
|
|
|
40
54
|
constructor(options = {}) {
|
|
41
55
|
this.options = { failFast: options.failFast ?? false };
|
|
42
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Tracks a disposable instance for later cleanup.
|
|
59
|
+
*
|
|
60
|
+
* Re-tracking a token removes the previous entry and re-inserts it at the
|
|
61
|
+
* end of the map, so reverse-creation disposal order stays sound.
|
|
62
|
+
* Non-disposable instances are ignored.
|
|
63
|
+
*/
|
|
43
64
|
track(token, instance, owner = ContainerLifecycleOwner.CONTAINER) {
|
|
44
65
|
if (this.disposed)
|
|
45
|
-
throw new
|
|
66
|
+
throw new ContainerLifecycleError("track", "Cannot track an instance after the container lifecycle has been disposed.");
|
|
46
67
|
if (!isDisposableInstance(instance))
|
|
47
68
|
return;
|
|
69
|
+
this.instances.delete(token);
|
|
48
70
|
this.instances.set(token, {
|
|
49
71
|
token,
|
|
50
72
|
instance,
|
|
@@ -68,14 +90,18 @@ export class ContainerLifecycle {
|
|
|
68
90
|
untrack(token) {
|
|
69
91
|
return this.instances.delete(token);
|
|
70
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Disposes a single tracked instance and untracks it. Untracking happens
|
|
95
|
+
* even when disposal fails — a failed disposal is terminal for the entry.
|
|
96
|
+
*/
|
|
71
97
|
async disposeInstance(token) {
|
|
72
|
-
const tracked = this.get(token);
|
|
98
|
+
const tracked = this.instances.get(token);
|
|
73
99
|
if (!tracked || tracked.disposed)
|
|
74
100
|
return;
|
|
101
|
+
tracked.disposed = true;
|
|
102
|
+
this.instances.delete(token);
|
|
75
103
|
try {
|
|
76
104
|
await disposeValue(tracked.instance);
|
|
77
|
-
tracked.disposed = true;
|
|
78
|
-
this.instances.delete(token);
|
|
79
105
|
}
|
|
80
106
|
catch (error) {
|
|
81
107
|
if (this.options.failFast)
|
|
@@ -83,6 +109,14 @@ export class ContainerLifecycle {
|
|
|
83
109
|
throw new ContainerDisposalError([error], [token]);
|
|
84
110
|
}
|
|
85
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Disposes tracked instances in reverse creation order.
|
|
114
|
+
*
|
|
115
|
+
* Entries are untracked even when their disposal fails (terminal). When
|
|
116
|
+
* `owner` is omitted the whole lifecycle is marked disposed — this happens
|
|
117
|
+
* even on failure — and every failure is reported in the thrown
|
|
118
|
+
* ContainerDisposalError.
|
|
119
|
+
*/
|
|
86
120
|
async dispose(owner) {
|
|
87
121
|
if (this.disposed)
|
|
88
122
|
return;
|
|
@@ -95,10 +129,10 @@ export class ContainerLifecycle {
|
|
|
95
129
|
for (const entry of selected) {
|
|
96
130
|
if (entry.disposed)
|
|
97
131
|
continue;
|
|
132
|
+
entry.disposed = true;
|
|
133
|
+
this.instances.delete(entry.token);
|
|
98
134
|
try {
|
|
99
135
|
await disposeValue(entry.instance);
|
|
100
|
-
entry.disposed = true;
|
|
101
|
-
this.instances.delete(entry.token);
|
|
102
136
|
}
|
|
103
137
|
catch (error) {
|
|
104
138
|
errors.push(error);
|
|
@@ -107,10 +141,10 @@ export class ContainerLifecycle {
|
|
|
107
141
|
break;
|
|
108
142
|
}
|
|
109
143
|
}
|
|
110
|
-
if (errors.length > 0)
|
|
111
|
-
throw new ContainerDisposalError(errors, failedTokens);
|
|
112
144
|
if (owner === undefined)
|
|
113
145
|
this.disposed = true;
|
|
146
|
+
if (errors.length > 0)
|
|
147
|
+
throw new ContainerDisposalError(errors, failedTokens);
|
|
114
148
|
}
|
|
115
149
|
async disposeScope() {
|
|
116
150
|
await this.dispose(ContainerLifecycleOwner.SCOPE);
|
|
@@ -118,13 +152,18 @@ export class ContainerLifecycle {
|
|
|
118
152
|
async disposeContainer() {
|
|
119
153
|
await this.dispose(ContainerLifecycleOwner.CONTAINER);
|
|
120
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Releases all tracked references WITHOUT disposing them and marks the
|
|
157
|
+
* lifecycle disposed so further `track()` calls are refused. Used when a
|
|
158
|
+
* container is disposed with `autoDispose: false`.
|
|
159
|
+
*/
|
|
160
|
+
shutdown() {
|
|
161
|
+
this.instances.clear();
|
|
162
|
+
this.disposed = true;
|
|
163
|
+
}
|
|
121
164
|
isDisposed() {
|
|
122
165
|
return this.disposed;
|
|
123
166
|
}
|
|
124
|
-
reset() {
|
|
125
|
-
this.instances.clear();
|
|
126
|
-
this.disposed = false;
|
|
127
|
-
}
|
|
128
167
|
getTrackedTokens() {
|
|
129
168
|
return [...this.instances.keys()];
|
|
130
169
|
}
|
|
@@ -134,8 +173,12 @@ async function disposeValue(value) {
|
|
|
134
173
|
await value[Symbol.asyncDispose]();
|
|
135
174
|
return;
|
|
136
175
|
}
|
|
176
|
+
if (isSymbolDisposable(value)) {
|
|
177
|
+
value[Symbol.dispose]();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
137
180
|
if (isDisposable(value)) {
|
|
138
|
-
value.dispose();
|
|
181
|
+
await value.dispose();
|
|
139
182
|
return;
|
|
140
183
|
}
|
|
141
184
|
}
|
|
@@ -4,26 +4,18 @@
|
|
|
4
4
|
import type { ContainerLifecycleOptions } from "../containerLifecycle/containerLifecycle.core.js";
|
|
5
5
|
import type { ContainerRegistryOptions } from "../containerRegistry/containerRegistry.type.js";
|
|
6
6
|
import type { ResolutionOptions } from "../containerResolution/containerResolution.type.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
export declare const DEFAULT_CONTAINER_LOG_LEVEL: ContainerLogLevel;
|
|
16
|
-
export interface ContainerResolutionOptions extends ResolutionOptions {
|
|
17
|
-
readonly autoRegisterClasses?: boolean;
|
|
18
|
-
readonly detectCircularDependencies?: boolean;
|
|
19
|
-
readonly maxResolutionDepth?: number;
|
|
20
|
-
}
|
|
7
|
+
/**
|
|
8
|
+
* The subset of {@link ResolutionOptions} a container accepts at
|
|
9
|
+
* construction time. Per-call plumbing (`cache`, `path`,
|
|
10
|
+
* `allowRegistration`, `onInstanceCreated`) is managed by the container
|
|
11
|
+
* itself and deliberately not configurable here.
|
|
12
|
+
*/
|
|
13
|
+
export type ContainerResolutionOptions = Pick<ResolutionOptions, "autoRegisterClasses" | "detectCircularDependencies" | "maxResolutionDepth">;
|
|
21
14
|
export interface ContainerOptions {
|
|
22
15
|
readonly name?: string;
|
|
23
16
|
readonly registry?: ContainerRegistryOptions;
|
|
24
17
|
readonly lifecycle?: ContainerLifecycleOptions;
|
|
25
18
|
readonly resolution?: ContainerResolutionOptions;
|
|
26
|
-
readonly logLevel?: ContainerLogLevel;
|
|
27
19
|
readonly autoDispose?: boolean;
|
|
28
20
|
readonly allowScopes?: boolean;
|
|
29
21
|
readonly freezeRegistrations?: boolean;
|
|
@@ -34,7 +26,6 @@ export interface ResolvedContainerOptions {
|
|
|
34
26
|
readonly registry: ContainerRegistryOptions;
|
|
35
27
|
readonly lifecycle: ContainerLifecycleOptions;
|
|
36
28
|
readonly resolution: ContainerResolutionOptions;
|
|
37
|
-
readonly logLevel: ContainerLogLevel;
|
|
38
29
|
readonly autoDispose: boolean;
|
|
39
30
|
readonly allowScopes: boolean;
|
|
40
31
|
readonly freezeRegistrations: boolean;
|
|
@@ -47,7 +38,6 @@ export declare const DEFAULT_FREEZE_REGISTRATIONS = false;
|
|
|
47
38
|
export declare const DEFAULT_RESOLUTION_OPTIONS: Required<Pick<ContainerResolutionOptions, "autoRegisterClasses" | "detectCircularDependencies" | "maxResolutionDepth">>;
|
|
48
39
|
export declare function resolveContainerOptions(options?: ContainerOptions): ResolvedContainerOptions;
|
|
49
40
|
export declare function validateResolutionOptions(options: ContainerResolutionOptions): void;
|
|
50
|
-
export declare function isContainerLogLevel(value: unknown): value is ContainerLogLevel;
|
|
51
41
|
export declare function allowsContainerScopes(options: ResolvedContainerOptions): boolean;
|
|
52
42
|
export declare function shouldAutoDisposeContainer(options: ResolvedContainerOptions): boolean;
|
|
53
43
|
export declare function canModifyRegistrations(options: ResolvedContainerOptions): boolean;
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configuration options for the Zudojs dependency injection container.
|
|
3
3
|
*/
|
|
4
|
-
export var ContainerLogLevel;
|
|
5
|
-
(function (ContainerLogLevel) {
|
|
6
|
-
ContainerLogLevel["NONE"] = "none";
|
|
7
|
-
ContainerLogLevel["ERROR"] = "error";
|
|
8
|
-
ContainerLogLevel["WARN"] = "warn";
|
|
9
|
-
ContainerLogLevel["INFO"] = "info";
|
|
10
|
-
ContainerLogLevel["DEBUG"] = "debug";
|
|
11
|
-
ContainerLogLevel["TRACE"] = "trace";
|
|
12
|
-
})(ContainerLogLevel || (ContainerLogLevel = {}));
|
|
13
|
-
export const DEFAULT_CONTAINER_LOG_LEVEL = ContainerLogLevel.WARN;
|
|
14
4
|
export const DEFAULT_CONTAINER_NAME = "zudojs-container";
|
|
15
5
|
export const DEFAULT_AUTO_DISPOSE = true;
|
|
16
6
|
export const DEFAULT_ALLOW_SCOPES = true;
|
|
@@ -39,7 +29,6 @@ export function resolveContainerOptions(options = {}) {
|
|
|
39
29
|
registry: Object.freeze({ ...(options.registry ?? {}) }),
|
|
40
30
|
lifecycle: Object.freeze({ ...(options.lifecycle ?? {}) }),
|
|
41
31
|
resolution: Object.freeze({ ...resolution }),
|
|
42
|
-
logLevel: options.logLevel ?? DEFAULT_CONTAINER_LOG_LEVEL,
|
|
43
32
|
autoDispose: options.autoDispose ?? DEFAULT_AUTO_DISPOSE,
|
|
44
33
|
allowScopes: options.allowScopes ?? DEFAULT_ALLOW_SCOPES,
|
|
45
34
|
freezeRegistrations: options.freezeRegistrations ?? DEFAULT_FREEZE_REGISTRATIONS,
|
|
@@ -53,14 +42,6 @@ export function validateResolutionOptions(options) {
|
|
|
53
42
|
throw new RangeError("Container maxResolutionDepth must be a positive integer.");
|
|
54
43
|
}
|
|
55
44
|
}
|
|
56
|
-
export function isContainerLogLevel(value) {
|
|
57
|
-
return (value === ContainerLogLevel.NONE ||
|
|
58
|
-
value === ContainerLogLevel.ERROR ||
|
|
59
|
-
value === ContainerLogLevel.WARN ||
|
|
60
|
-
value === ContainerLogLevel.INFO ||
|
|
61
|
-
value === ContainerLogLevel.DEBUG ||
|
|
62
|
-
value === ContainerLogLevel.TRACE);
|
|
63
|
-
}
|
|
64
45
|
export function allowsContainerScopes(options) {
|
|
65
46
|
return options.allowScopes;
|
|
66
47
|
}
|
|
@@ -6,6 +6,11 @@ import type { Constructor, InjectionToken, Token } from "../containerToken/conta
|
|
|
6
6
|
export type ProviderToken<T = unknown> = Token<T> | InjectionToken<T>;
|
|
7
7
|
export interface ClassProvider<T> {
|
|
8
8
|
readonly useClass: Constructor<T>;
|
|
9
|
+
/**
|
|
10
|
+
* Tokens resolved and passed to the constructor, in order.
|
|
11
|
+
* Omit (or leave empty) for zero-argument constructors.
|
|
12
|
+
*/
|
|
13
|
+
readonly inject?: readonly ProviderToken[];
|
|
9
14
|
}
|
|
10
15
|
export interface FactoryProvider<T> {
|
|
11
16
|
readonly useFactory: (...dependencies: unknown[]) => T;
|
|
@@ -25,6 +30,7 @@ export interface TokenProvider<T = unknown> {
|
|
|
25
30
|
export interface ClassRegistration<T = unknown> {
|
|
26
31
|
readonly provide: ProviderToken<T>;
|
|
27
32
|
readonly useClass: Constructor<T>;
|
|
33
|
+
readonly inject?: readonly ProviderToken[];
|
|
28
34
|
}
|
|
29
35
|
export interface FactoryRegistration<T = unknown> {
|
|
30
36
|
readonly provide: ProviderToken<T>;
|
|
@@ -45,12 +51,14 @@ export declare function isFactoryProvider<T = unknown>(provider: Provider<T>): p
|
|
|
45
51
|
export declare function isValueProvider<T = unknown>(provider: Provider<T>): provider is ValueProvider<T>;
|
|
46
52
|
export declare function isExistingProvider<T = unknown>(provider: Provider<T>): provider is ExistingProvider<T>;
|
|
47
53
|
export declare function isTokenProvider<T = unknown>(provider: ContainerProvider<T>): provider is TokenProvider<T>;
|
|
48
|
-
export declare function hasInjectedDependencies<T = unknown>(provider: Provider<T>): provider is FactoryProvider<T
|
|
49
|
-
|
|
54
|
+
export declare function hasInjectedDependencies<T = unknown>(provider: Provider<T>): provider is (FactoryProvider<T> | ClassProvider<T>) & {
|
|
55
|
+
readonly inject: readonly ProviderToken[];
|
|
56
|
+
};
|
|
57
|
+
export declare function classProvider<T>(useClass: Constructor<T>, inject?: readonly ProviderToken[]): ClassProvider<T>;
|
|
50
58
|
export declare function factoryProvider<T>(useFactory: (...dependencies: unknown[]) => T, inject?: readonly ProviderToken[]): FactoryProvider<T>;
|
|
51
59
|
export declare function valueProvider<T>(useValue: T): ValueProvider<T>;
|
|
52
60
|
export declare function existingProvider<T>(useExisting: ProviderToken<T>): ExistingProvider<T>;
|
|
53
|
-
export declare function provideClass<T>(provide: ProviderToken<T>, useClass: Constructor<T
|
|
61
|
+
export declare function provideClass<T>(provide: ProviderToken<T>, useClass: Constructor<T>, inject?: readonly ProviderToken[]): ClassRegistration<T>;
|
|
54
62
|
export declare function provideFactory<T>(provide: ProviderToken<T>, useFactory: (...dependencies: unknown[]) => T, inject?: readonly ProviderToken[]): FactoryRegistration<T>;
|
|
55
63
|
export declare function provideValue<T>(provide: ProviderToken<T>, useValue: T): ValueRegistration<T>;
|
|
56
64
|
export declare function provideExisting<T>(provide: ProviderToken<T>, useExisting: ProviderToken<T>): ExistingRegistration<T>;
|
|
@@ -18,10 +18,12 @@ export function isTokenProvider(provider) {
|
|
|
18
18
|
return "provide" in provider && "provider" in provider;
|
|
19
19
|
}
|
|
20
20
|
export function hasInjectedDependencies(provider) {
|
|
21
|
-
return isFactoryProvider(provider)
|
|
21
|
+
return ((isFactoryProvider(provider) || isClassProvider(provider)) &&
|
|
22
|
+
Array.isArray(provider.inject) &&
|
|
23
|
+
provider.inject.length > 0);
|
|
22
24
|
}
|
|
23
|
-
export function classProvider(useClass) {
|
|
24
|
-
return Object.freeze({ useClass });
|
|
25
|
+
export function classProvider(useClass, inject = []) {
|
|
26
|
+
return Object.freeze({ useClass, inject: Object.freeze([...inject]) });
|
|
25
27
|
}
|
|
26
28
|
export function factoryProvider(useFactory, inject = []) {
|
|
27
29
|
return Object.freeze({ useFactory, inject: Object.freeze([...inject]) });
|
|
@@ -32,8 +34,12 @@ export function valueProvider(useValue) {
|
|
|
32
34
|
export function existingProvider(useExisting) {
|
|
33
35
|
return Object.freeze({ useExisting });
|
|
34
36
|
}
|
|
35
|
-
export function provideClass(provide, useClass) {
|
|
36
|
-
return Object.freeze({
|
|
37
|
+
export function provideClass(provide, useClass, inject = []) {
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
provide,
|
|
40
|
+
useClass,
|
|
41
|
+
inject: Object.freeze([...inject]),
|
|
42
|
+
});
|
|
37
43
|
}
|
|
38
44
|
export function provideFactory(provide, useFactory, inject = []) {
|
|
39
45
|
return Object.freeze({
|
|
@@ -6,6 +6,15 @@ import { ContainerScope } from "../containerScope/containerScope.type.js";
|
|
|
6
6
|
import type { ContainerProvider, ProviderToken } from "../containerProvider/containerProvider.core.js";
|
|
7
7
|
import type { InjectionToken, Token } from "../containerToken/containerToken.type.js";
|
|
8
8
|
export type RegistrationToken<T = unknown> = Token<T> | InjectionToken<T>;
|
|
9
|
+
/** The type a registration token resolves to (`unknown` for untyped tokens). */
|
|
10
|
+
export type ResolvedToken<T extends RegistrationToken> = T extends RegistrationToken<infer U> ? U : never;
|
|
11
|
+
/**
|
|
12
|
+
* Maps a tuple of registration tokens to a tuple of their resolved types, so
|
|
13
|
+
* `resolveMany([LOGGER, DB])` is typed `[Logger, Db]`.
|
|
14
|
+
*/
|
|
15
|
+
export type ResolvedTokens<Tokens extends readonly RegistrationToken[]> = {
|
|
16
|
+
-readonly [K in keyof Tokens]: Tokens[K] extends RegistrationToken ? ResolvedToken<Tokens[K]> : never;
|
|
17
|
+
};
|
|
9
18
|
export interface RegistrationMetadata {
|
|
10
19
|
readonly name?: string;
|
|
11
20
|
readonly description?: string;
|
|
@@ -18,7 +27,6 @@ export interface ContainerRegistration<T = unknown> {
|
|
|
18
27
|
readonly provider: ContainerProvider<T>;
|
|
19
28
|
readonly scope: ContainerScope;
|
|
20
29
|
readonly metadata: RegistrationMetadata;
|
|
21
|
-
readonly initialized: boolean;
|
|
22
30
|
readonly createdAt: Date;
|
|
23
31
|
}
|
|
24
32
|
export interface CreateRegistrationOptions extends RegistrationMetadata {
|
|
@@ -29,13 +37,11 @@ export interface MutableRegistrationState<T = unknown> {
|
|
|
29
37
|
readonly provider: ContainerProvider<T>;
|
|
30
38
|
scope: ContainerScope;
|
|
31
39
|
metadata: RegistrationMetadata;
|
|
32
|
-
initialized: boolean;
|
|
33
40
|
createdAt: Date;
|
|
34
41
|
}
|
|
35
42
|
export declare function createRegistration<T>(token: RegistrationToken<T>, provider: ContainerProvider<T>, options?: CreateRegistrationOptions): ContainerRegistration<T>;
|
|
36
43
|
export declare function createMutableRegistrationState<T>(registration: ContainerRegistration<T>): MutableRegistrationState<T>;
|
|
37
44
|
export declare function freezeRegistration<T>(state: MutableRegistrationState<T>): ContainerRegistration<T>;
|
|
38
|
-
export declare function markRegistrationInitialized<T>(registration: ContainerRegistration<T>): ContainerRegistration<T>;
|
|
39
45
|
export declare function getRegistrationToken<T>(registration: ContainerRegistration<T>): Token<T>;
|
|
40
46
|
export declare function getRegistrationProviderToken<T>(registration: ContainerRegistration<T>): ProviderToken<T> | undefined;
|
|
41
47
|
export declare function isSingletonRegistration<T>(registration: ContainerRegistration<T>): boolean;
|
|
@@ -13,7 +13,6 @@ export function createRegistration(token, provider, options = {}) {
|
|
|
13
13
|
provider: normalizedProvider,
|
|
14
14
|
scope: resolveContainerScope(options.scope),
|
|
15
15
|
metadata: createRegistrationMetadata(options, normalizedToken),
|
|
16
|
-
initialized: false,
|
|
17
16
|
createdAt: new Date(),
|
|
18
17
|
};
|
|
19
18
|
return Object.freeze({
|
|
@@ -43,7 +42,6 @@ export function createMutableRegistrationState(registration) {
|
|
|
43
42
|
provider: registration.provider,
|
|
44
43
|
scope: registration.scope,
|
|
45
44
|
metadata: registration.metadata,
|
|
46
|
-
initialized: registration.initialized,
|
|
47
45
|
createdAt: registration.createdAt,
|
|
48
46
|
};
|
|
49
47
|
}
|
|
@@ -58,13 +56,9 @@ export function freezeRegistration(state) {
|
|
|
58
56
|
? Object.freeze({ ...state.metadata.metadata })
|
|
59
57
|
: undefined,
|
|
60
58
|
}),
|
|
61
|
-
initialized: state.initialized,
|
|
62
59
|
createdAt: state.createdAt,
|
|
63
60
|
});
|
|
64
61
|
}
|
|
65
|
-
export function markRegistrationInitialized(registration) {
|
|
66
|
-
return Object.freeze({ ...registration, initialized: true });
|
|
67
|
-
}
|
|
68
62
|
export function getRegistrationToken(registration) {
|
|
69
63
|
return unwrapToken(registration.token);
|
|
70
64
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Container registration registry.
|
|
3
|
+
*
|
|
4
|
+
* The registry stores normalized registrations and emits change events
|
|
5
|
+
* (REGISTER / REPLACE / REMOVE / CLEAR / RESTORE) so collaborators — notably
|
|
6
|
+
* the resolver's singleton cache — can react to mutations.
|
|
3
7
|
*/
|
|
4
8
|
import type { ContainerProvider } from "../containerProvider/containerProvider.core.js";
|
|
5
|
-
import type {
|
|
6
|
-
import type { ContainerRegistration, CreateRegistrationOptions, RegistrationMetadata, RegistrationToken } from "../containerRegistration/containerRegistration.core.js";
|
|
9
|
+
import type { ContainerRegistration, CreateRegistrationOptions, RegistrationToken } from "../containerRegistration/containerRegistration.core.js";
|
|
7
10
|
import type { Token } from "../containerToken/containerToken.type.js";
|
|
8
11
|
import type { ContainerRegistryOptions, RegistryListener } from "./containerRegistry.type.js";
|
|
9
12
|
export declare class ContainerRegistry {
|
|
@@ -12,25 +15,36 @@ export declare class ContainerRegistry {
|
|
|
12
15
|
private readonly allowDuplicates;
|
|
13
16
|
constructor(options?: ContainerRegistryOptions);
|
|
14
17
|
register<T>(token: RegistrationToken<T>, provider: ContainerProvider<T>, options?: CreateRegistrationOptions): ContainerRegistration<T>;
|
|
15
|
-
registerRegistration<T>(registration: ContainerRegistration<T>): ContainerRegistration<T>;
|
|
16
18
|
replace<T>(token: RegistrationToken<T>, provider: ContainerProvider<T>, options?: CreateRegistrationOptions): ContainerRegistration<T>;
|
|
17
19
|
get<T>(token: RegistrationToken<T>): ContainerRegistration<T> | undefined;
|
|
18
|
-
getOrThrow<T>(token: RegistrationToken<T>): ContainerRegistration<T>;
|
|
19
20
|
has<T>(token: RegistrationToken<T>): boolean;
|
|
20
21
|
remove<T>(token: RegistrationToken<T>): boolean;
|
|
21
|
-
removeOrThrow<T>(token: RegistrationToken<T>): void;
|
|
22
22
|
get size(): number;
|
|
23
23
|
getAll(): readonly ContainerRegistration[];
|
|
24
24
|
getTokens(): readonly Token<unknown>[];
|
|
25
|
-
getByModule(module: string): readonly ContainerRegistration[];
|
|
26
|
-
getByScope(scope: ContainerScope): readonly ContainerRegistration[];
|
|
27
|
-
find(predicate: (r: ContainerRegistration) => boolean): readonly ContainerRegistration[];
|
|
28
|
-
getMetadata<T>(token: RegistrationToken<T>): RegistrationMetadata | undefined;
|
|
29
25
|
clear(): void;
|
|
30
26
|
subscribe(listener: RegistryListener): () => void;
|
|
31
27
|
clearListeners(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Notifies all listeners. Listener errors are collected and rethrown as a
|
|
30
|
+
* single AggregateError after every listener has been notified — they are
|
|
31
|
+
* never silently swallowed.
|
|
32
|
+
*/
|
|
32
33
|
private emit;
|
|
34
|
+
/**
|
|
35
|
+
* Returns an immutable snapshot of the current registrations.
|
|
36
|
+
* Pass the result to {@link restore} to roll the registry back.
|
|
37
|
+
*/
|
|
33
38
|
snapshot(): readonly ContainerRegistration[];
|
|
39
|
+
/**
|
|
40
|
+
* Wholesale replacement of the registry's contents.
|
|
41
|
+
*
|
|
42
|
+
* Every existing registration is discarded and replaced by `regs` (entries
|
|
43
|
+
* are validated first; duplicate tokens in `regs` throw unless
|
|
44
|
+
* `allowDuplicates` is enabled, in which case the last entry wins).
|
|
45
|
+
* Emits a single RESTORE event so caches keyed on the old registrations
|
|
46
|
+
* can be evicted.
|
|
47
|
+
*/
|
|
34
48
|
restore(regs: readonly ContainerRegistration[]): void;
|
|
35
49
|
}
|
|
36
50
|
//# sourceMappingURL=containerRegistry.core.d.ts.map
|