@stitchem/core 0.0.3 → 0.0.7
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/dist/context/context.d.ts +2 -2
- package/dist/context/context.js +12 -29
- package/dist/core/core.lifecycle.d.ts +0 -14
- package/dist/core/core.lifecycle.js +0 -12
- package/dist/index.d.ts +1 -1
- package/dist/injector/injector.d.ts +9 -2
- package/dist/injector/injector.js +26 -12
- package/dist/module/module.ref.js +12 -12
- package/dist/test/test.d.ts +1 -2
- package/dist/test/test.js +1 -2
- package/dist/test/test.module-builder.js +0 -26
- package/dist/test/test.module.d.ts +2 -5
- package/dist/test/test.module.js +24 -28
- package/package.json +2 -2
|
@@ -170,12 +170,12 @@ export declare class Context implements AsyncDisposable {
|
|
|
170
170
|
private resolveSingletons;
|
|
171
171
|
/** Instantiates component classes from registered component keys. */
|
|
172
172
|
private resolveComponents;
|
|
173
|
-
/** Calls onReady() on all singleton providers, module classes, and components. */
|
|
174
|
-
private fireOnReady;
|
|
175
173
|
/** Disposes injectables in reverse module order, before component disposal. */
|
|
176
174
|
private disposeInjectables;
|
|
177
175
|
/** Disposes components in reverse module order, before provider disposal. */
|
|
178
176
|
private disposeComponents;
|
|
177
|
+
/** Calls onDispose() on module class instances in reverse module order. */
|
|
178
|
+
private disposeModuleInstances;
|
|
179
179
|
private ensureInitialized;
|
|
180
180
|
}
|
|
181
181
|
//# sourceMappingURL=context.d.ts.map
|
package/dist/context/context.js
CHANGED
|
@@ -57,7 +57,7 @@ import { ModuleGraph } from '../module/module.graph.js';
|
|
|
57
57
|
import { ModuleRef } from '../module/module.ref.js';
|
|
58
58
|
import { Injector } from '../injector/injector.js';
|
|
59
59
|
import { CoreError } from '../errors/core.error.js';
|
|
60
|
-
import {
|
|
60
|
+
import { hasOnDispose } from '../core/core.lifecycle.js';
|
|
61
61
|
import { ConsoleLogger, NoopLogger } from '../logger/console.logger.js';
|
|
62
62
|
import { LOGGER } from '../logger/logger.token.js';
|
|
63
63
|
import { Scope } from './scope.js';
|
|
@@ -163,9 +163,6 @@ export class Context {
|
|
|
163
163
|
return cached;
|
|
164
164
|
const scope = Scope.current() ?? Scope.STATIC;
|
|
165
165
|
const instance = await this.injector.instantiateClassGlobal(ctor, scope);
|
|
166
|
-
if (hasOnReady(instance)) {
|
|
167
|
-
await instance.onReady();
|
|
168
|
-
}
|
|
169
166
|
this.rootModule.setInjectable(ctor, instance);
|
|
170
167
|
return instance;
|
|
171
168
|
}
|
|
@@ -263,6 +260,7 @@ export class Context {
|
|
|
263
260
|
}
|
|
264
261
|
await this.container.dispose(scope);
|
|
265
262
|
if (!scope) {
|
|
263
|
+
await this.disposeModuleInstances();
|
|
266
264
|
this.initialized = false;
|
|
267
265
|
}
|
|
268
266
|
}
|
|
@@ -274,7 +272,6 @@ export class Context {
|
|
|
274
272
|
this.container.buildGlobalExports();
|
|
275
273
|
await this.resolveSingletons();
|
|
276
274
|
await this.resolveComponents();
|
|
277
|
-
await this.fireOnReady();
|
|
278
275
|
this.initialized = true;
|
|
279
276
|
}
|
|
280
277
|
/** Registers a ModuleRef factory provider for each module. */
|
|
@@ -346,30 +343,6 @@ export class Context {
|
|
|
346
343
|
}
|
|
347
344
|
}
|
|
348
345
|
}
|
|
349
|
-
/** Calls onReady() on all singleton providers, module classes, and components. */
|
|
350
|
-
async fireOnReady() {
|
|
351
|
-
for (const id of this.moduleGraph.sortedIds) {
|
|
352
|
-
const module = this.container.getModule(id);
|
|
353
|
-
for (const [, wrapper] of module.providers) {
|
|
354
|
-
if (!wrapper.isSingleton)
|
|
355
|
-
continue;
|
|
356
|
-
const instance = wrapper.getInstance(Scope.STATIC);
|
|
357
|
-
if (instance !== undefined && hasOnReady(instance)) {
|
|
358
|
-
await instance.onReady();
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
if (module.instance !== undefined && hasOnReady(module.instance)) {
|
|
362
|
-
await module.instance.onReady();
|
|
363
|
-
}
|
|
364
|
-
for (const key of this.componentKeys) {
|
|
365
|
-
for (const entry of module.getComponents(key)) {
|
|
366
|
-
if (hasOnReady(entry.instance)) {
|
|
367
|
-
await entry.instance.onReady();
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
346
|
/** Disposes injectables in reverse module order, before component disposal. */
|
|
374
347
|
async disposeInjectables() {
|
|
375
348
|
const moduleIds = [...this.moduleGraph.sortedIds].reverse();
|
|
@@ -386,6 +359,16 @@ export class Context {
|
|
|
386
359
|
await module.disposeComponents();
|
|
387
360
|
}
|
|
388
361
|
}
|
|
362
|
+
/** Calls onDispose() on module class instances in reverse module order. */
|
|
363
|
+
async disposeModuleInstances() {
|
|
364
|
+
const moduleIds = [...this.moduleGraph.sortedIds].reverse();
|
|
365
|
+
for (const id of moduleIds) {
|
|
366
|
+
const module = this.container.getModule(id);
|
|
367
|
+
if (module.instance !== undefined && hasOnDispose(module.instance)) {
|
|
368
|
+
await module.instance.onDispose();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
389
372
|
ensureInitialized() {
|
|
390
373
|
if (!this.initialized) {
|
|
391
374
|
throw new Error('Context is not initialized. Call Context.create() first.');
|
|
@@ -10,13 +10,6 @@ export interface OnInit {
|
|
|
10
10
|
export interface OnDispose {
|
|
11
11
|
onDispose(): void | Promise<void>;
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Interface for providers that need to run logic after all modules are initialized.
|
|
15
|
-
* Fires after every singleton provider has been created and onInit() has completed.
|
|
16
|
-
*/
|
|
17
|
-
export interface OnReady {
|
|
18
|
-
onReady(): void | Promise<void>;
|
|
19
|
-
}
|
|
20
13
|
/**
|
|
21
14
|
* Type guard for OnInit.
|
|
22
15
|
*
|
|
@@ -31,11 +24,4 @@ export declare function hasOnInit(instance: unknown): instance is OnInit;
|
|
|
31
24
|
* @returns True if the instance implements the OnDispose interface.
|
|
32
25
|
*/
|
|
33
26
|
export declare function hasOnDispose(instance: unknown): instance is OnDispose;
|
|
34
|
-
/**
|
|
35
|
-
* Type guard for OnReady.
|
|
36
|
-
*
|
|
37
|
-
* @param instance - The value to check for OnReady conformance.
|
|
38
|
-
* @returns True if the instance implements the OnReady interface.
|
|
39
|
-
*/
|
|
40
|
-
export declare function hasOnReady(instance: unknown): instance is OnReady;
|
|
41
27
|
//# sourceMappingURL=core.lifecycle.d.ts.map
|
|
@@ -22,16 +22,4 @@ export function hasOnDispose(instance) {
|
|
|
22
22
|
'onDispose' in instance &&
|
|
23
23
|
typeof instance.onDispose === 'function');
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Type guard for OnReady.
|
|
27
|
-
*
|
|
28
|
-
* @param instance - The value to check for OnReady conformance.
|
|
29
|
-
* @returns True if the instance implements the OnReady interface.
|
|
30
|
-
*/
|
|
31
|
-
export function hasOnReady(instance) {
|
|
32
|
-
return (typeof instance === 'object' &&
|
|
33
|
-
instance !== null &&
|
|
34
|
-
'onReady' in instance &&
|
|
35
|
-
typeof instance.onReady === 'function');
|
|
36
|
-
}
|
|
37
25
|
//# sourceMappingURL=core.lifecycle.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type { Token } from './token/token.types.js';
|
|
|
6
6
|
export { Lifetime } from './core/core.lifetime.js';
|
|
7
7
|
export { lazy } from './token/lazy.token.js';
|
|
8
8
|
export { isConstructor } from './core/core.utils.js';
|
|
9
|
-
export type { OnInit, OnDispose
|
|
9
|
+
export type { OnInit, OnDispose } from './core/core.lifecycle.js';
|
|
10
10
|
export type { Provider, ClassProvider, ValueProvider, FactoryProvider, ExistingProvider, } from './provider/provider.interface.js';
|
|
11
11
|
export type { ModuleMetadata, DynamicModule, ModuleImport, } from './module/module.types.js';
|
|
12
12
|
export { Context } from './context/context.js';
|
|
@@ -10,8 +10,15 @@ import { Container } from '../container/container.js';
|
|
|
10
10
|
*/
|
|
11
11
|
export declare class Injector {
|
|
12
12
|
private readonly container;
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Per-scope resolution stack for circular dependency detection.
|
|
15
|
+
* Each scope (e.g. per HTTP request) gets its own stack, preventing
|
|
16
|
+
* false circular dependency errors under concurrency.
|
|
17
|
+
* WeakMap ensures stacks are GC'd when their scope is disposed.
|
|
18
|
+
*/
|
|
19
|
+
private readonly resolutionStacks;
|
|
20
|
+
/** Returns or creates the resolution stack for the given scope. */
|
|
21
|
+
private getResolutionStack;
|
|
15
22
|
constructor(container: Container);
|
|
16
23
|
/**
|
|
17
24
|
* Resolves an instance from a wrapper (async).
|
|
@@ -12,8 +12,22 @@ import { isValueProvider, isFactoryProvider, isExistingProvider, isClassProvider
|
|
|
12
12
|
*/
|
|
13
13
|
export class Injector {
|
|
14
14
|
container;
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Per-scope resolution stack for circular dependency detection.
|
|
17
|
+
* Each scope (e.g. per HTTP request) gets its own stack, preventing
|
|
18
|
+
* false circular dependency errors under concurrency.
|
|
19
|
+
* WeakMap ensures stacks are GC'd when their scope is disposed.
|
|
20
|
+
*/
|
|
21
|
+
resolutionStacks = new WeakMap();
|
|
22
|
+
/** Returns or creates the resolution stack for the given scope. */
|
|
23
|
+
getResolutionStack(scope) {
|
|
24
|
+
let stack = this.resolutionStacks.get(scope);
|
|
25
|
+
if (!stack) {
|
|
26
|
+
stack = new Set();
|
|
27
|
+
this.resolutionStacks.set(scope, stack);
|
|
28
|
+
}
|
|
29
|
+
return stack;
|
|
30
|
+
}
|
|
17
31
|
constructor(container) {
|
|
18
32
|
this.container = container;
|
|
19
33
|
}
|
|
@@ -28,7 +42,7 @@ export class Injector {
|
|
|
28
42
|
const cached = this.guardResolution(wrapper, scope);
|
|
29
43
|
if (cached !== null)
|
|
30
44
|
return cached;
|
|
31
|
-
this.
|
|
45
|
+
this.getResolutionStack(scope).add(wrapper.token);
|
|
32
46
|
try {
|
|
33
47
|
const resolver = this.moduleResolver(module);
|
|
34
48
|
const instance = await this.createInstance(wrapper, resolver, scope);
|
|
@@ -39,7 +53,7 @@ export class Injector {
|
|
|
39
53
|
return instance;
|
|
40
54
|
}
|
|
41
55
|
finally {
|
|
42
|
-
this.
|
|
56
|
+
this.getResolutionStack(scope).delete(wrapper.token);
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
59
|
/**
|
|
@@ -53,7 +67,7 @@ export class Injector {
|
|
|
53
67
|
const cached = this.guardResolution(wrapper, scope);
|
|
54
68
|
if (cached !== null)
|
|
55
69
|
return cached;
|
|
56
|
-
this.
|
|
70
|
+
this.getResolutionStack(scope).add(wrapper.token);
|
|
57
71
|
try {
|
|
58
72
|
const resolver = this.moduleResolver(module);
|
|
59
73
|
const instance = this.createInstanceSync(wrapper, resolver, scope);
|
|
@@ -67,7 +81,7 @@ export class Injector {
|
|
|
67
81
|
return instance;
|
|
68
82
|
}
|
|
69
83
|
finally {
|
|
70
|
-
this.
|
|
84
|
+
this.getResolutionStack(scope).delete(wrapper.token);
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
/**
|
|
@@ -77,13 +91,13 @@ export class Injector {
|
|
|
77
91
|
* @throws CoreError (CIRCULAR_DEPENDENCY) if a circular dependency is detected
|
|
78
92
|
*/
|
|
79
93
|
async createUncached(wrapper, module, scope = Scope.STATIC) {
|
|
80
|
-
if (this.
|
|
94
|
+
if (this.getResolutionStack(scope).has(wrapper.token)) {
|
|
81
95
|
throw CoreError.circularDependency([
|
|
82
|
-
...this.
|
|
96
|
+
...this.getResolutionStack(scope),
|
|
83
97
|
wrapper.token,
|
|
84
98
|
]);
|
|
85
99
|
}
|
|
86
|
-
this.
|
|
100
|
+
this.getResolutionStack(scope).add(wrapper.token);
|
|
87
101
|
try {
|
|
88
102
|
const resolver = this.moduleResolver(module);
|
|
89
103
|
const instance = await this.createInstance(wrapper, resolver, scope);
|
|
@@ -93,7 +107,7 @@ export class Injector {
|
|
|
93
107
|
return instance;
|
|
94
108
|
}
|
|
95
109
|
finally {
|
|
96
|
-
this.
|
|
110
|
+
this.getResolutionStack(scope).delete(wrapper.token);
|
|
97
111
|
}
|
|
98
112
|
}
|
|
99
113
|
/**
|
|
@@ -142,9 +156,9 @@ export class Injector {
|
|
|
142
156
|
if (wrapper.isResolved(scope)) {
|
|
143
157
|
return wrapper.getInstance(scope);
|
|
144
158
|
}
|
|
145
|
-
if (this.
|
|
159
|
+
if (this.getResolutionStack(scope).has(wrapper.token)) {
|
|
146
160
|
throw CoreError.circularDependency([
|
|
147
|
-
...this.
|
|
161
|
+
...this.getResolutionStack(scope),
|
|
148
162
|
wrapper.token,
|
|
149
163
|
]);
|
|
150
164
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Scope } from '../context/scope.js';
|
|
2
|
-
import { hasOnReady } from '../core/core.lifecycle.js';
|
|
3
2
|
import { CoreError } from '../errors/core.error.js';
|
|
4
3
|
/**
|
|
5
4
|
* Reference to a module, injectable into providers.
|
|
@@ -47,14 +46,16 @@ export class ModuleRef {
|
|
|
47
46
|
* Resolves a dependency from this module's context.
|
|
48
47
|
* Respects module visibility (can only resolve visible tokens).
|
|
49
48
|
*/
|
|
50
|
-
async resolve(token, scope
|
|
49
|
+
async resolve(token, scope) {
|
|
50
|
+
const resolvedScope = scope ?? Scope.current() ?? Scope.STATIC;
|
|
51
51
|
const { wrapper, module } = this.container.getProviderByToken(token, this.module);
|
|
52
|
-
return this.injector.loadInstance(wrapper, module,
|
|
52
|
+
return this.injector.loadInstance(wrapper, module, resolvedScope);
|
|
53
53
|
}
|
|
54
54
|
/** Synchronously resolves a dependency from this module's context. */
|
|
55
|
-
resolveSync(token, scope
|
|
55
|
+
resolveSync(token, scope) {
|
|
56
|
+
const resolvedScope = scope ?? Scope.current() ?? Scope.STATIC;
|
|
56
57
|
const { wrapper, module } = this.container.getProviderByToken(token, this.module);
|
|
57
|
-
return this.injector.loadInstanceSync(wrapper, module,
|
|
58
|
+
return this.injector.loadInstanceSync(wrapper, module, resolvedScope);
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
60
61
|
* Creates a new instance of a registered provider, bypassing cache.
|
|
@@ -62,10 +63,11 @@ export class ModuleRef {
|
|
|
62
63
|
*
|
|
63
64
|
* @throws CoreError (PROVIDER_NOT_FOUND) if the class is not registered
|
|
64
65
|
*/
|
|
65
|
-
async create(ctor, scope
|
|
66
|
+
async create(ctor, scope) {
|
|
67
|
+
const resolvedScope = scope ?? Scope.current() ?? Scope.STATIC;
|
|
66
68
|
const wrapper = this.module.getProvider(ctor);
|
|
67
69
|
if (wrapper) {
|
|
68
|
-
return this.injector.createUncached(wrapper, this.module,
|
|
70
|
+
return this.injector.createUncached(wrapper, this.module, resolvedScope);
|
|
69
71
|
}
|
|
70
72
|
throw CoreError.providerNotFound(ctor, this.module.id);
|
|
71
73
|
}
|
|
@@ -73,8 +75,9 @@ export class ModuleRef {
|
|
|
73
75
|
* Instantiates any class by resolving its dependencies.
|
|
74
76
|
* The class does not need to be registered as a provider.
|
|
75
77
|
*/
|
|
76
|
-
async constructClass(ctor, scope
|
|
77
|
-
|
|
78
|
+
async constructClass(ctor, scope) {
|
|
79
|
+
const resolvedScope = scope ?? Scope.current() ?? Scope.STATIC;
|
|
80
|
+
return this.injector.instantiateClass(ctor, this.module, resolvedScope);
|
|
78
81
|
}
|
|
79
82
|
/**
|
|
80
83
|
* Resolves an injectable class instance with caching and lifecycle hooks.
|
|
@@ -97,9 +100,6 @@ export class ModuleRef {
|
|
|
97
100
|
const instance = await (options?.strict === false
|
|
98
101
|
? this.injector.instantiateClassGlobal(ctor, scope)
|
|
99
102
|
: this.injector.instantiateClass(ctor, this.module, scope));
|
|
100
|
-
if (hasOnReady(instance)) {
|
|
101
|
-
await instance.onReady();
|
|
102
|
-
}
|
|
103
103
|
this.module.setInjectable(ctor, instance);
|
|
104
104
|
return instance;
|
|
105
105
|
}
|
package/dist/test/test.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { TestModuleBuilder } from './test.module-builder.js';
|
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```ts
|
|
8
|
-
*
|
|
8
|
+
* await using module = await Test.createModule({
|
|
9
9
|
* imports: [UserModule],
|
|
10
10
|
* providers: [UserService],
|
|
11
11
|
* })
|
|
@@ -13,7 +13,6 @@ import { TestModuleBuilder } from './test.module-builder.js';
|
|
|
13
13
|
* .compile();
|
|
14
14
|
*
|
|
15
15
|
* const userService = await module.resolve(UserService);
|
|
16
|
-
* await module.close();
|
|
17
16
|
* ```
|
|
18
17
|
*/
|
|
19
18
|
export declare class Test {
|
package/dist/test/test.js
CHANGED
|
@@ -4,7 +4,7 @@ import { TestModuleBuilder } from './test.module-builder.js';
|
|
|
4
4
|
*
|
|
5
5
|
* @example
|
|
6
6
|
* ```ts
|
|
7
|
-
*
|
|
7
|
+
* await using module = await Test.createModule({
|
|
8
8
|
* imports: [UserModule],
|
|
9
9
|
* providers: [UserService],
|
|
10
10
|
* })
|
|
@@ -12,7 +12,6 @@ import { TestModuleBuilder } from './test.module-builder.js';
|
|
|
12
12
|
* .compile();
|
|
13
13
|
*
|
|
14
14
|
* const userService = await module.resolve(UserService);
|
|
15
|
-
* await module.close();
|
|
16
15
|
* ```
|
|
17
16
|
*/
|
|
18
17
|
export class Test {
|
|
@@ -38,7 +38,6 @@ import { ModuleGraph } from '../module/module.graph.js';
|
|
|
38
38
|
import { ModuleRef } from '../module/module.ref.js';
|
|
39
39
|
import { Injector } from '../injector/injector.js';
|
|
40
40
|
import { Scope } from '../context/scope.js';
|
|
41
|
-
import { hasOnReady } from '../core/core.lifecycle.js';
|
|
42
41
|
import { getProviderToken } from '../provider/provider.guards.js';
|
|
43
42
|
import { TestModule } from './test.module.js';
|
|
44
43
|
// ---------------------------------------------------------------------------
|
|
@@ -178,9 +177,6 @@ export class TestModuleBuilder {
|
|
|
178
177
|
const result = override.factory(...deps);
|
|
179
178
|
instance = result instanceof Promise ? await result : result;
|
|
180
179
|
}
|
|
181
|
-
if (hasOnReady(instance)) {
|
|
182
|
-
await instance.onReady();
|
|
183
|
-
}
|
|
184
180
|
for (const id of moduleGraph.sortedIds) {
|
|
185
181
|
const mod = container.getModule(id);
|
|
186
182
|
mod.setInjectable(override.ctor, instance);
|
|
@@ -215,28 +211,6 @@ export class TestModuleBuilder {
|
|
|
215
211
|
}
|
|
216
212
|
}
|
|
217
213
|
}
|
|
218
|
-
// Fire onReady lifecycle.
|
|
219
|
-
for (const id of moduleGraph.sortedIds) {
|
|
220
|
-
const mod = container.getModule(id);
|
|
221
|
-
for (const [, wrapper] of mod.providers) {
|
|
222
|
-
if (!wrapper.isSingleton)
|
|
223
|
-
continue;
|
|
224
|
-
const instance = wrapper.getInstance(Scope.STATIC);
|
|
225
|
-
if (instance !== undefined && hasOnReady(instance)) {
|
|
226
|
-
await instance.onReady();
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
if (mod.instance !== undefined && hasOnReady(mod.instance)) {
|
|
230
|
-
await mod.instance.onReady();
|
|
231
|
-
}
|
|
232
|
-
for (const key of componentKeys) {
|
|
233
|
-
for (const entry of mod.getComponents(key)) {
|
|
234
|
-
if (hasOnReady(entry.instance)) {
|
|
235
|
-
await entry.instance.onReady();
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
214
|
return new TestModule(container, injector, rootModule, moduleGraph.sortedIds);
|
|
241
215
|
}
|
|
242
216
|
/** @internal */
|
|
@@ -61,11 +61,8 @@ export declare class TestModule implements AsyncDisposable {
|
|
|
61
61
|
/** Creates a new disposable scope for scoped resolution. */
|
|
62
62
|
createScope(): Scope;
|
|
63
63
|
[Symbol.asyncDispose](): Promise<void>;
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
* Call in afterEach/afterAll to clean up.
|
|
67
|
-
*/
|
|
68
|
-
close(scope?: Scope): Promise<void>;
|
|
64
|
+
/** Disposes a single scope's instances. */
|
|
65
|
+
private disposeScope;
|
|
69
66
|
private ensureNotDisposed;
|
|
70
67
|
}
|
|
71
68
|
//# sourceMappingURL=test.module.d.ts.map
|
package/dist/test/test.module.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Scope } from '../context/scope.js';
|
|
2
2
|
import { ModuleRef } from '../module/module.ref.js';
|
|
3
|
-
import {
|
|
3
|
+
import { hasOnDispose } from '../core/core.lifecycle.js';
|
|
4
4
|
/**
|
|
5
5
|
* A compiled test module.
|
|
6
6
|
* Provides resolution and lifecycle management for test scenarios.
|
|
@@ -83,9 +83,6 @@ export class TestModule {
|
|
|
83
83
|
return cached;
|
|
84
84
|
const scope = Scope.current() ?? Scope.STATIC;
|
|
85
85
|
const instance = await this.injector.instantiateClassGlobal(ctor, scope);
|
|
86
|
-
if (hasOnReady(instance)) {
|
|
87
|
-
await instance.onReady();
|
|
88
|
-
}
|
|
89
86
|
this.rootModule.setInjectable(ctor, instance);
|
|
90
87
|
return instance;
|
|
91
88
|
}
|
|
@@ -111,36 +108,35 @@ export class TestModule {
|
|
|
111
108
|
/** Creates a new disposable scope for scoped resolution. */
|
|
112
109
|
createScope() {
|
|
113
110
|
this.ensureNotDisposed();
|
|
114
|
-
return Scope.create((scope) => this.
|
|
111
|
+
return Scope.create((scope) => this.disposeScope(scope));
|
|
115
112
|
}
|
|
116
113
|
async [Symbol.asyncDispose]() {
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Disposes the test module.
|
|
121
|
-
* Call in afterEach/afterAll to clean up.
|
|
122
|
-
*/
|
|
123
|
-
async close(scope) {
|
|
124
|
-
if (this.disposed && !scope)
|
|
114
|
+
if (this.disposed)
|
|
125
115
|
return;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
116
|
+
// Dispose injectables → components → providers → module instances.
|
|
117
|
+
const moduleIds = [...this.sortedIds].reverse();
|
|
118
|
+
for (const id of moduleIds) {
|
|
119
|
+
const module = this.container.getModule(id);
|
|
120
|
+
if (module)
|
|
121
|
+
await module.disposeInjectables();
|
|
122
|
+
}
|
|
123
|
+
for (const id of moduleIds) {
|
|
124
|
+
const module = this.container.getModule(id);
|
|
125
|
+
if (module)
|
|
126
|
+
await module.disposeComponents();
|
|
127
|
+
}
|
|
128
|
+
await this.container.dispose();
|
|
129
|
+
for (const id of moduleIds) {
|
|
130
|
+
const module = this.container.getModule(id);
|
|
131
|
+
if (module?.instance !== undefined && hasOnDispose(module.instance)) {
|
|
132
|
+
await module.instance.onDispose();
|
|
138
133
|
}
|
|
139
134
|
}
|
|
135
|
+
this.disposed = true;
|
|
136
|
+
}
|
|
137
|
+
/** Disposes a single scope's instances. */
|
|
138
|
+
async disposeScope(scope) {
|
|
140
139
|
await this.container.dispose(scope);
|
|
141
|
-
if (!scope) {
|
|
142
|
-
this.disposed = true;
|
|
143
|
-
}
|
|
144
140
|
}
|
|
145
141
|
ensureNotDisposed() {
|
|
146
142
|
if (this.disposed) {
|
package/package.json
CHANGED