@zudojs/container 0.1.0 → 1.1.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/LICENSE +21 -0
- package/README.md +221 -8
- package/dist/containerCore/containerCore.core.d.ts +112 -14
- package/dist/containerCore/containerCore.core.js +228 -46
- package/dist/containerCore/containerCore.scope.d.ts +66 -18
- package/dist/containerCore/containerCore.scope.js +120 -31
- package/dist/containerCore/containerCore.type.d.ts +22 -16
- package/dist/containerLifecycle/containerLifecycle.core.d.ts +59 -9
- package/dist/containerLifecycle/containerLifecycle.core.js +60 -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 +51 -10
- package/dist/containerResolution/containerResolution.core.js +255 -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/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/package.json +25 -12
- 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
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core dependency injection container for Zudojs.
|
|
3
|
+
*
|
|
4
|
+
* Lifetime and disposal model:
|
|
5
|
+
* - SINGLETON instances are cached in the container's root singleton cache
|
|
6
|
+
* and tracked by the container lifecycle — including singletons created
|
|
7
|
+
* transitively as dependencies of another resolution; they are disposed
|
|
8
|
+
* when the container is disposed (or when their registration is
|
|
9
|
+
* replaced/removed).
|
|
10
|
+
* - SCOPED instances only exist inside a scope created with `createScope()`;
|
|
11
|
+
* resolving a SCOPED token at the root throws.
|
|
12
|
+
* - TRANSIENT instances are never cached and never tracked — callers own
|
|
13
|
+
* their disposal.
|
|
3
14
|
*/
|
|
4
15
|
import { existingProvider, factoryProvider, valueProvider, classProvider, } from "../containerProvider/containerProvider.core.js";
|
|
5
16
|
import { ContainerScope as Scope } from "../containerScope/containerScope.type.js";
|
|
@@ -7,36 +18,64 @@ import { ContainerRegistry } from "../containerRegistry/containerRegistry.core.j
|
|
|
7
18
|
import { ContainerResolver } from "../containerResolution/containerResolution.core.js";
|
|
8
19
|
import { ContainerLifecycle, ContainerLifecycleOwner, } from "../containerLifecycle/containerLifecycle.core.js";
|
|
9
20
|
import { resolveContainerOptions } from "../containerOptions/containerOptions.type.js";
|
|
21
|
+
import { unwrapToken } from "../containerToken/containerToken.type.js";
|
|
22
|
+
import { RegistrationNotFoundError } from "@zudojs/errors";
|
|
10
23
|
import { ContainerScopeContext } from "./containerCore.scope.js";
|
|
11
24
|
export class Container {
|
|
12
25
|
name;
|
|
13
26
|
options;
|
|
14
|
-
registry;
|
|
15
|
-
resolver;
|
|
16
|
-
lifecycle;
|
|
17
|
-
|
|
18
|
-
|
|
27
|
+
#registry;
|
|
28
|
+
#resolver;
|
|
29
|
+
#lifecycle;
|
|
30
|
+
#liveScopes = new Set();
|
|
31
|
+
#started = false;
|
|
32
|
+
#disposed = false;
|
|
33
|
+
#disposing;
|
|
19
34
|
constructor(options = {}) {
|
|
20
35
|
this.options = resolveContainerOptions(options);
|
|
21
36
|
this.name = this.options.name;
|
|
22
|
-
this
|
|
23
|
-
this
|
|
24
|
-
this
|
|
37
|
+
this.#registry = new ContainerRegistry(this.options.registry);
|
|
38
|
+
this.#lifecycle = new ContainerLifecycle(this.options.lifecycle);
|
|
39
|
+
this.#resolver = new ContainerResolver(this.#registry, (token) => {
|
|
40
|
+
// A cached singleton was evicted because its registration changed;
|
|
41
|
+
// dispose the tracked instance. Eviction disposal is best-effort:
|
|
42
|
+
// failures cannot propagate through the synchronous registry mutation
|
|
43
|
+
// that triggered them.
|
|
44
|
+
void this.#lifecycle.disposeInstance(token).catch(() => {
|
|
45
|
+
/* see clearSingletons()/dispose() for error-surfacing disposal */
|
|
46
|
+
});
|
|
47
|
+
});
|
|
25
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Marks the container as started.
|
|
51
|
+
*
|
|
52
|
+
* Calling `start()` explicitly is optional: `resolve()` (and
|
|
53
|
+
* `createScope()`) auto-start the container on first use. Note that with
|
|
54
|
+
* `freezeRegistrations: true`, starting — including the implicit start
|
|
55
|
+
* performed by the first `resolve()` — freezes the registration set.
|
|
56
|
+
*/
|
|
26
57
|
start() {
|
|
27
58
|
this.ensureNotDisposed();
|
|
28
|
-
if (this
|
|
59
|
+
if (this.#started)
|
|
29
60
|
return this;
|
|
30
|
-
this
|
|
61
|
+
this.#started = true;
|
|
31
62
|
return this;
|
|
32
63
|
}
|
|
33
64
|
register(token, provider, options = {}) {
|
|
34
65
|
this.ensureMutable();
|
|
35
|
-
return this
|
|
66
|
+
return this.#registry.register(token, provider, options);
|
|
36
67
|
}
|
|
37
68
|
registerClass(token, ctor, options = {}) {
|
|
38
|
-
|
|
69
|
+
const { inject, ...rest } = options;
|
|
70
|
+
return this.register(token, classProvider(ctor, inject ?? []), rest);
|
|
39
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Registers a pre-built value.
|
|
74
|
+
*
|
|
75
|
+
* Note: the scope is forcibly set to SINGLETON (overriding any
|
|
76
|
+
* `options.scope`) — a value provider always returns the same instance, so
|
|
77
|
+
* any other lifetime would be misleading.
|
|
78
|
+
*/
|
|
40
79
|
registerValue(token, value, options = {}) {
|
|
41
80
|
return this.register(token, valueProvider(value), {
|
|
42
81
|
...options,
|
|
@@ -49,73 +88,182 @@ export class Container {
|
|
|
49
88
|
registerExisting(token, existing, options = {}) {
|
|
50
89
|
return this.register(token, existingProvider(existing), options);
|
|
51
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Resolves a dependency at the container root.
|
|
93
|
+
*
|
|
94
|
+
* Auto-starts the container on first use. SCOPED registrations cannot be
|
|
95
|
+
* resolved here — create a scope with {@link createScope} instead.
|
|
96
|
+
*/
|
|
52
97
|
resolve(token) {
|
|
53
98
|
this.ensureActive();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return result.value;
|
|
99
|
+
return this.#resolver.resolveDetailed(token, this.buildResolutionOptions())
|
|
100
|
+
.value;
|
|
57
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Resolves several tokens at once. The result is a tuple typed per token,
|
|
104
|
+
* so heterogeneous token lists keep their individual types.
|
|
105
|
+
*/
|
|
58
106
|
resolveMany(tokens) {
|
|
59
107
|
return tokens.map((t) => this.resolve(t));
|
|
60
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Like {@link resolve} but returns `undefined` when the token has no
|
|
111
|
+
* registration (and cannot be auto-registered). Other resolution failures
|
|
112
|
+
* — broken factories, missing dependencies, captive dependencies — still
|
|
113
|
+
* throw.
|
|
114
|
+
*/
|
|
61
115
|
resolveOptional(token) {
|
|
62
|
-
|
|
63
|
-
return
|
|
64
|
-
|
|
116
|
+
try {
|
|
117
|
+
return this.resolve(token);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error instanceof RegistrationNotFoundError)
|
|
121
|
+
return undefined;
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
65
124
|
}
|
|
66
125
|
canResolve(token) {
|
|
67
126
|
this.ensureNotDisposed();
|
|
68
|
-
return this
|
|
127
|
+
return this.#resolver.canResolve(token, this.resolutionOptions.autoRegisterClasses);
|
|
69
128
|
}
|
|
70
129
|
has(token) {
|
|
71
130
|
this.ensureNotDisposed();
|
|
72
|
-
return this
|
|
131
|
+
return this.#registry.has(token);
|
|
73
132
|
}
|
|
74
133
|
getRegistration(token) {
|
|
75
134
|
this.ensureNotDisposed();
|
|
76
|
-
return this
|
|
135
|
+
return this.#registry.get(token);
|
|
136
|
+
}
|
|
137
|
+
replace(token, provider, options = {}) {
|
|
138
|
+
this.ensureMutable();
|
|
139
|
+
return this.#registry.replace(token, provider, options);
|
|
77
140
|
}
|
|
78
141
|
remove(token) {
|
|
79
142
|
this.ensureMutable();
|
|
80
|
-
return this
|
|
143
|
+
return this.#registry.remove(token);
|
|
144
|
+
}
|
|
145
|
+
/** Removes every registration (evicting and disposing cached singletons). */
|
|
146
|
+
clearRegistrations() {
|
|
147
|
+
this.ensureMutable();
|
|
148
|
+
this.#registry.clear();
|
|
81
149
|
}
|
|
82
150
|
createScope(options = {}) {
|
|
83
151
|
this.ensureActive();
|
|
84
152
|
if (!this.options.allowScopes)
|
|
85
|
-
throw new Error(
|
|
86
|
-
|
|
153
|
+
throw new Error(`Container scopes are disabled for "${this.name}".`);
|
|
154
|
+
const scope = new ContainerScopeContext(this, options);
|
|
155
|
+
this.#liveScopes.add(scope);
|
|
156
|
+
return scope;
|
|
87
157
|
}
|
|
88
158
|
getRegistrations() {
|
|
89
159
|
this.ensureNotDisposed();
|
|
90
|
-
return this
|
|
160
|
+
return this.#registry.getAll();
|
|
161
|
+
}
|
|
162
|
+
getTokens() {
|
|
163
|
+
this.ensureNotDisposed();
|
|
164
|
+
return this.#registry.getTokens();
|
|
91
165
|
}
|
|
92
166
|
get registrationCount() {
|
|
93
|
-
return this
|
|
167
|
+
return this.#registry.size;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Returns an immutable snapshot of the current registrations, suitable for
|
|
171
|
+
* later {@link restoreSnapshot}. Cached instances are NOT part of the
|
|
172
|
+
* snapshot — only the registrations.
|
|
173
|
+
*/
|
|
174
|
+
snapshot() {
|
|
175
|
+
this.ensureNotDisposed();
|
|
176
|
+
return this.#registry.snapshot();
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Wholesale-replaces the registration set with a previous snapshot.
|
|
180
|
+
* Entries are validated, cached singletons for the old set are evicted and
|
|
181
|
+
* disposed, and the operation is refused when registrations are frozen.
|
|
182
|
+
*/
|
|
183
|
+
restoreSnapshot(registrations) {
|
|
184
|
+
this.ensureMutable();
|
|
185
|
+
this.#registry.restore(registrations);
|
|
94
186
|
}
|
|
95
187
|
isStarted() {
|
|
96
|
-
return this
|
|
188
|
+
return this.#started;
|
|
97
189
|
}
|
|
98
190
|
isDisposed() {
|
|
99
|
-
return this
|
|
191
|
+
return this.#disposed;
|
|
100
192
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Evicts all cached singleton instances and disposes the tracked ones.
|
|
195
|
+
* Fails with an AggregateError listing every disposal failure.
|
|
196
|
+
*/
|
|
197
|
+
async clearSingletons() {
|
|
198
|
+
this.ensureNotDisposed();
|
|
199
|
+
const tokens = this.#resolver.getCachedSingletonTokens();
|
|
200
|
+
this.#resolver.clearSingletonCache();
|
|
201
|
+
const failures = [];
|
|
202
|
+
for (const token of tokens) {
|
|
203
|
+
try {
|
|
204
|
+
await this.#lifecycle.disposeInstance(token);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
failures.push(error);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (failures.length > 0)
|
|
211
|
+
throw new AggregateError(failures, `Failed to dispose ${failures.length} singleton instance(s) of container "${this.name}".`);
|
|
104
212
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Disposes the container: all live scopes first, then (with
|
|
215
|
+
* `autoDispose: true`, the default) every tracked singleton in reverse
|
|
216
|
+
* creation order.
|
|
217
|
+
*
|
|
218
|
+
* With `autoDispose: false` tracked instances are NOT disposed — their
|
|
219
|
+
* references are simply released and disposal becomes the caller's
|
|
220
|
+
* responsibility.
|
|
221
|
+
*
|
|
222
|
+
* Disposal is terminal: the container is marked disposed even when some
|
|
223
|
+
* instances fail to dispose; every failure is reported in the thrown
|
|
224
|
+
* AggregateError. Idempotent.
|
|
225
|
+
*
|
|
226
|
+
* The container is marked disposed *before* any cleanup runs, so a
|
|
227
|
+
* `resolve()` racing the disposal throws instead of creating a singleton
|
|
228
|
+
* that would be tracked after the disposal snapshot and then silently
|
|
229
|
+
* dropped. Concurrent `dispose()` calls share the in-flight disposal and
|
|
230
|
+
* settle only when it has finished.
|
|
231
|
+
*/
|
|
232
|
+
dispose() {
|
|
233
|
+
if (this.#disposing)
|
|
234
|
+
return this.#disposing;
|
|
235
|
+
if (this.#disposed)
|
|
236
|
+
return Promise.resolve();
|
|
237
|
+
this.#disposed = true;
|
|
238
|
+
this.#started = false;
|
|
239
|
+
this.#disposing = this.runDispose().finally(() => {
|
|
240
|
+
this.#disposing = undefined;
|
|
241
|
+
});
|
|
242
|
+
return this.#disposing;
|
|
243
|
+
}
|
|
244
|
+
async runDispose() {
|
|
245
|
+
const failures = [];
|
|
246
|
+
for (const scope of [...this.#liveScopes]) {
|
|
247
|
+
try {
|
|
248
|
+
await scope.dispose();
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
failures.push(error);
|
|
252
|
+
}
|
|
113
253
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
254
|
+
this.#liveScopes.clear();
|
|
255
|
+
if (this.options.autoDispose) {
|
|
256
|
+
try {
|
|
257
|
+
await this.#lifecycle.dispose();
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
failures.push(error);
|
|
261
|
+
}
|
|
118
262
|
}
|
|
263
|
+
this.#lifecycle.shutdown();
|
|
264
|
+
this.#resolver.clearSingletonCache();
|
|
265
|
+
if (failures.length > 0)
|
|
266
|
+
throw new AggregateError(failures, `Container "${this.name}" was disposed, but ${failures.length} cleanup step(s) failed.`);
|
|
119
267
|
}
|
|
120
268
|
get resolutionOptions() {
|
|
121
269
|
return {
|
|
@@ -124,20 +272,54 @@ export class Container {
|
|
|
124
272
|
maxResolutionDepth: this.options.resolution.maxResolutionDepth ?? 100,
|
|
125
273
|
};
|
|
126
274
|
}
|
|
275
|
+
/** @internal Creates a fresh scoped-instance cache for a scope. */
|
|
276
|
+
createScopeCache(parent) {
|
|
277
|
+
return this.#resolver.createScope(parent);
|
|
278
|
+
}
|
|
279
|
+
/** @internal Resolves on behalf of a scope, tracking singleton results. */
|
|
280
|
+
resolveInScope(token, cache, onInstanceCreated) {
|
|
281
|
+
this.ensureActive();
|
|
282
|
+
return this.#resolver.resolveDetailed(token, this.buildResolutionOptions(cache, onInstanceCreated));
|
|
283
|
+
}
|
|
284
|
+
/** @internal Unregisters a top-level scope from the live-scope set. */
|
|
285
|
+
releaseScope(scope) {
|
|
286
|
+
this.#liveScopes.delete(scope);
|
|
287
|
+
}
|
|
288
|
+
buildResolutionOptions(cache, onInstanceCreated) {
|
|
289
|
+
return {
|
|
290
|
+
...this.resolutionOptions,
|
|
291
|
+
allowRegistration: !this.options.freezeRegistrations,
|
|
292
|
+
cache,
|
|
293
|
+
onInstanceCreated: (result) => {
|
|
294
|
+
this.trackCreated(result);
|
|
295
|
+
onInstanceCreated?.(result);
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Tracks every freshly created SINGLETON instance — top-level results and
|
|
301
|
+
* transitively created dependencies alike — in the container lifecycle.
|
|
302
|
+
* SCOPED instances are owned by scopes and TRANSIENT ones by callers.
|
|
303
|
+
*/
|
|
304
|
+
trackCreated(result) {
|
|
305
|
+
if (result.scope !== Scope.SINGLETON)
|
|
306
|
+
return;
|
|
307
|
+
this.#lifecycle.track(unwrapToken(result.token), result.value, ContainerLifecycleOwner.CONTAINER);
|
|
308
|
+
}
|
|
127
309
|
ensureActive() {
|
|
128
310
|
this.ensureNotDisposed();
|
|
129
|
-
if (!this
|
|
311
|
+
if (!this.#started)
|
|
130
312
|
this.start();
|
|
131
313
|
}
|
|
132
314
|
ensureNotDisposed() {
|
|
133
|
-
if (this
|
|
315
|
+
if (this.#disposed)
|
|
134
316
|
throw new Error(`Container "${this.name}" has already been disposed.`);
|
|
135
317
|
}
|
|
136
318
|
ensureMutable() {
|
|
137
319
|
this.ensureNotDisposed();
|
|
138
320
|
if (!this.options.freezeRegistrations)
|
|
139
321
|
return;
|
|
140
|
-
if (this
|
|
322
|
+
if (this.#started)
|
|
141
323
|
throw new Error(`Registrations for container "${this.name}" are frozen.`);
|
|
142
324
|
}
|
|
143
325
|
}
|
|
@@ -1,31 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ContainerScopeContext —
|
|
3
|
-
* its own
|
|
4
|
-
*/
|
|
5
|
-
import type { RegistrationToken } from "../containerRegistration/containerRegistration.core.js";
|
|
6
|
-
import type { ContainerScopeOptions, ContainerLike } from "./containerCore.type.js";
|
|
7
|
-
/**
|
|
8
|
-
* Represents a child dependency scope.
|
|
2
|
+
* ContainerScopeContext — a dependency scope with its own scoped-instance
|
|
3
|
+
* cache and its own lifecycle for SCOPED instances.
|
|
9
4
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* Ownership model:
|
|
6
|
+
* - SCOPED instances created through this scope — including those created
|
|
7
|
+
* transitively as dependencies — are owned by the scope and disposed when
|
|
8
|
+
* the scope is disposed.
|
|
9
|
+
* - SINGLETON instances resolved through a scope stay owned by the parent
|
|
10
|
+
* container — a scope's disposal never touches them.
|
|
11
|
+
* - TRANSIENT instances are not tracked anywhere; callers own their disposal.
|
|
12
|
+
*
|
|
13
|
+
* Nesting: `scope.createScope()` creates a true child scope. A child sees
|
|
14
|
+
* SCOPED instances already created by its ancestors (lookups chain upward),
|
|
15
|
+
* while instances it creates itself are private to the child. Disposing a
|
|
16
|
+
* scope disposes its children first, and a child refuses to resolve once any
|
|
17
|
+
* ancestor (scope or container) is disposed.
|
|
12
18
|
*/
|
|
19
|
+
import type { RegistrationToken, ResolvedTokens } from "../containerRegistration/containerRegistration.core.js";
|
|
20
|
+
import type { ContainerScopeOptions, ContainerLike } from "./containerCore.type.js";
|
|
13
21
|
export declare class ContainerScopeContext {
|
|
14
|
-
private readonly parent;
|
|
15
22
|
private disposed;
|
|
23
|
+
private disposing;
|
|
16
24
|
private readonly cache;
|
|
17
25
|
private readonly lifecycle;
|
|
26
|
+
private readonly container;
|
|
27
|
+
private readonly parentScope;
|
|
28
|
+
private readonly children;
|
|
18
29
|
readonly name: string;
|
|
19
30
|
readonly metadata: Readonly<Record<string, unknown>>;
|
|
20
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @param container The container that owns the scope tree.
|
|
33
|
+
* @param options Scope name / metadata.
|
|
34
|
+
* @param parentScope When given, the new scope is a nested child of it.
|
|
35
|
+
* Application code should use `container.createScope()` /
|
|
36
|
+
* `scope.createScope()` rather than constructing scopes directly.
|
|
37
|
+
*/
|
|
38
|
+
constructor(container: ContainerLike, options?: ContainerScopeOptions, parentScope?: ContainerScopeContext);
|
|
21
39
|
/**
|
|
22
40
|
* Resolves a dependency within this scope.
|
|
41
|
+
*
|
|
42
|
+
* SCOPED instances are cached per scope (visible to child scopes) and
|
|
43
|
+
* tracked for disposal with the scope that created them. SINGLETON
|
|
44
|
+
* instances come from (and are tracked by) the container. TRANSIENT
|
|
45
|
+
* instances are created fresh and never tracked.
|
|
23
46
|
*/
|
|
24
47
|
resolve<T>(token: RegistrationToken<T>): T;
|
|
25
48
|
/**
|
|
26
|
-
* Resolves multiple dependencies within this scope.
|
|
49
|
+
* Resolves multiple dependencies within this scope, typed per token.
|
|
27
50
|
*/
|
|
28
|
-
resolveMany<
|
|
51
|
+
resolveMany<const Tokens extends readonly RegistrationToken[]>(tokens: Tokens): ResolvedTokens<Tokens>;
|
|
29
52
|
/**
|
|
30
53
|
* Checks whether a dependency can be resolved.
|
|
31
54
|
*/
|
|
@@ -35,23 +58,48 @@ export declare class ContainerScopeContext {
|
|
|
35
58
|
*/
|
|
36
59
|
has<T>(token: RegistrationToken<T>): boolean;
|
|
37
60
|
/**
|
|
38
|
-
* Creates
|
|
61
|
+
* Creates a nested child scope.
|
|
62
|
+
*
|
|
63
|
+
* The child inherits this scope's cached SCOPED instances for lookups,
|
|
64
|
+
* caches the SCOPED instances it creates itself, and is disposed
|
|
65
|
+
* automatically when this scope is disposed.
|
|
39
66
|
*/
|
|
40
67
|
createScope(options?: ContainerScopeOptions): ContainerScopeContext;
|
|
41
68
|
/**
|
|
42
|
-
* Disposes
|
|
69
|
+
* Disposes child scopes (most recent first), then all SCOPED instances
|
|
70
|
+
* belonging to this scope in reverse creation order, and detaches the scope
|
|
71
|
+
* from its parent. Container-owned singletons are not touched. Idempotent.
|
|
72
|
+
*
|
|
73
|
+
* Every failure is collected; the scope is marked disposed regardless and
|
|
74
|
+
* an AggregateError listing the failures is thrown afterwards. Concurrent
|
|
75
|
+
* callers share the in-flight disposal rather than returning early.
|
|
43
76
|
*/
|
|
44
77
|
dispose(): Promise<void>;
|
|
78
|
+
private runDispose;
|
|
45
79
|
/**
|
|
46
80
|
* Returns whether the scope has been disposed.
|
|
47
81
|
*/
|
|
48
82
|
isDisposed(): boolean;
|
|
49
83
|
/**
|
|
50
|
-
* Returns the parent container
|
|
84
|
+
* Returns the direct parent: the container for a top-level scope, or the
|
|
85
|
+
* parent scope for a nested one.
|
|
86
|
+
*/
|
|
87
|
+
getParent(): ContainerLike | ContainerScopeContext;
|
|
88
|
+
/**
|
|
89
|
+
* Returns the container that owns the whole scope tree.
|
|
90
|
+
*/
|
|
91
|
+
getContainer(): ContainerLike;
|
|
92
|
+
/** @internal Detaches a disposed child scope. */
|
|
93
|
+
releaseChild(scope: ContainerScopeContext): void;
|
|
94
|
+
/**
|
|
95
|
+
* Tracks SCOPED instances created during a resolution through this scope
|
|
96
|
+
* (top-level result or transitively created dependency). Instances served
|
|
97
|
+
* from an ancestor's cache are cache hits and never reach here.
|
|
51
98
|
*/
|
|
52
|
-
|
|
99
|
+
private trackCreated;
|
|
53
100
|
/**
|
|
54
|
-
* Throws when the scope
|
|
101
|
+
* Throws when the scope — or any ancestor scope, or the container that
|
|
102
|
+
* owns it — is no longer active.
|
|
55
103
|
*/
|
|
56
104
|
private ensureActive;
|
|
57
105
|
}
|