@navios/di 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -0
- package/README.md +117 -17
- package/lib/browser/container/abstract-container.d.mts +112 -0
- package/lib/browser/container/abstract-container.d.mts.map +1 -0
- package/lib/browser/container/abstract-container.mjs +100 -0
- package/lib/browser/container/abstract-container.mjs.map +1 -0
- package/lib/browser/container/container.d.mts +100 -0
- package/lib/browser/container/container.d.mts.map +1 -0
- package/lib/browser/container/container.mjs +424 -0
- package/lib/browser/container/container.mjs.map +1 -0
- package/lib/browser/container/scoped-container.d.mts +93 -0
- package/lib/browser/container/scoped-container.d.mts.map +1 -0
- package/lib/browser/container/scoped-container.mjs +119 -0
- package/lib/browser/container/scoped-container.mjs.map +1 -0
- package/lib/browser/decorators/factory.decorator.d.mts +26 -0
- package/lib/browser/decorators/factory.decorator.d.mts.map +1 -0
- package/lib/browser/decorators/factory.decorator.mjs +20 -0
- package/lib/browser/decorators/factory.decorator.mjs.map +1 -0
- package/lib/browser/decorators/injectable.decorator.d.mts +38 -0
- package/lib/browser/decorators/injectable.decorator.d.mts.map +1 -0
- package/lib/browser/decorators/injectable.decorator.mjs +21 -0
- package/lib/browser/decorators/injectable.decorator.mjs.map +1 -0
- package/lib/browser/enums/injectable-scope.enum.d.mts +18 -0
- package/lib/browser/enums/injectable-scope.enum.d.mts.map +1 -0
- package/lib/browser/enums/injectable-scope.enum.mjs +20 -0
- package/lib/browser/enums/injectable-scope.enum.mjs.map +1 -0
- package/lib/browser/enums/injectable-type.enum.d.mts +8 -0
- package/lib/browser/enums/injectable-type.enum.d.mts.map +1 -0
- package/lib/browser/enums/injectable-type.enum.mjs +10 -0
- package/lib/browser/enums/injectable-type.enum.mjs.map +1 -0
- package/lib/browser/errors/di-error.d.mts +43 -0
- package/lib/browser/errors/di-error.d.mts.map +1 -0
- package/lib/browser/errors/di-error.mjs +98 -0
- package/lib/browser/errors/di-error.mjs.map +1 -0
- package/lib/browser/event-emitter.d.mts +16 -0
- package/lib/browser/event-emitter.d.mts.map +1 -0
- package/lib/browser/event-emitter.mjs +320 -0
- package/lib/browser/event-emitter.mjs.map +1 -0
- package/lib/browser/index.d.mts +37 -1508
- package/lib/browser/index.mjs +29 -2650
- package/lib/browser/interfaces/container.interface.d.mts +59 -0
- package/lib/browser/interfaces/container.interface.d.mts.map +1 -0
- package/lib/browser/interfaces/factory.interface.d.mts +14 -0
- package/lib/browser/interfaces/factory.interface.d.mts.map +1 -0
- package/lib/browser/interfaces/on-service-destroy.interface.d.mts +7 -0
- package/lib/browser/interfaces/on-service-destroy.interface.d.mts.map +1 -0
- package/lib/browser/interfaces/on-service-init.interface.d.mts +7 -0
- package/lib/browser/interfaces/on-service-init.interface.d.mts.map +1 -0
- package/lib/browser/internal/context/async-local-storage.browser.mjs +20 -0
- package/lib/browser/internal/context/async-local-storage.browser.mjs.map +1 -0
- package/lib/browser/internal/context/async-local-storage.d.mts +9 -0
- package/lib/browser/internal/context/async-local-storage.d.mts.map +1 -0
- package/lib/browser/internal/context/async-local-storage.types.d.mts +11 -0
- package/lib/browser/internal/context/async-local-storage.types.d.mts.map +1 -0
- package/lib/browser/internal/context/factory-context.d.mts +23 -0
- package/lib/browser/internal/context/factory-context.d.mts.map +1 -0
- package/lib/browser/internal/context/resolution-context.d.mts +43 -0
- package/lib/browser/internal/context/resolution-context.d.mts.map +1 -0
- package/lib/browser/internal/context/resolution-context.mjs +56 -0
- package/lib/browser/internal/context/resolution-context.mjs.map +1 -0
- package/lib/browser/internal/context/service-initialization-context.d.mts +48 -0
- package/lib/browser/internal/context/service-initialization-context.d.mts.map +1 -0
- package/lib/browser/internal/context/sync-local-storage.mjs +53 -0
- package/lib/browser/internal/context/sync-local-storage.mjs.map +1 -0
- package/lib/browser/internal/core/instance-resolver.d.mts +119 -0
- package/lib/browser/internal/core/instance-resolver.d.mts.map +1 -0
- package/lib/browser/internal/core/instance-resolver.mjs +306 -0
- package/lib/browser/internal/core/instance-resolver.mjs.map +1 -0
- package/lib/browser/internal/core/name-resolver.d.mts +52 -0
- package/lib/browser/internal/core/name-resolver.d.mts.map +1 -0
- package/lib/browser/internal/core/name-resolver.mjs +118 -0
- package/lib/browser/internal/core/name-resolver.mjs.map +1 -0
- package/lib/browser/internal/core/scope-tracker.d.mts +65 -0
- package/lib/browser/internal/core/scope-tracker.d.mts.map +1 -0
- package/lib/browser/internal/core/scope-tracker.mjs +120 -0
- package/lib/browser/internal/core/scope-tracker.mjs.map +1 -0
- package/lib/browser/internal/core/service-initializer.d.mts +44 -0
- package/lib/browser/internal/core/service-initializer.d.mts.map +1 -0
- package/lib/browser/internal/core/service-initializer.mjs +109 -0
- package/lib/browser/internal/core/service-initializer.mjs.map +1 -0
- package/lib/browser/internal/core/service-invalidator.d.mts +81 -0
- package/lib/browser/internal/core/service-invalidator.d.mts.map +1 -0
- package/lib/browser/internal/core/service-invalidator.mjs +142 -0
- package/lib/browser/internal/core/service-invalidator.mjs.map +1 -0
- package/lib/browser/internal/core/token-resolver.d.mts +54 -0
- package/lib/browser/internal/core/token-resolver.d.mts.map +1 -0
- package/lib/browser/internal/core/token-resolver.mjs +77 -0
- package/lib/browser/internal/core/token-resolver.mjs.map +1 -0
- package/lib/browser/internal/holder/holder-storage.interface.d.mts +99 -0
- package/lib/browser/internal/holder/holder-storage.interface.d.mts.map +1 -0
- package/lib/browser/internal/holder/instance-holder.d.mts +101 -0
- package/lib/browser/internal/holder/instance-holder.d.mts.map +1 -0
- package/lib/browser/internal/holder/instance-holder.mjs +19 -0
- package/lib/browser/internal/holder/instance-holder.mjs.map +1 -0
- package/lib/browser/internal/holder/unified-storage.d.mts +53 -0
- package/lib/browser/internal/holder/unified-storage.d.mts.map +1 -0
- package/lib/browser/internal/holder/unified-storage.mjs +144 -0
- package/lib/browser/internal/holder/unified-storage.mjs.map +1 -0
- package/lib/browser/internal/lifecycle/circular-detector.d.mts +39 -0
- package/lib/browser/internal/lifecycle/circular-detector.d.mts.map +1 -0
- package/lib/browser/internal/lifecycle/circular-detector.mjs +55 -0
- package/lib/browser/internal/lifecycle/circular-detector.mjs.map +1 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts +18 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts.map +1 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs +43 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs.map +1 -0
- package/lib/browser/internal/stub-factory-class.d.mts +14 -0
- package/lib/browser/internal/stub-factory-class.d.mts.map +1 -0
- package/lib/browser/internal/stub-factory-class.mjs +18 -0
- package/lib/browser/internal/stub-factory-class.mjs.map +1 -0
- package/lib/browser/symbols/injectable-token.d.mts +5 -0
- package/lib/browser/symbols/injectable-token.d.mts.map +1 -0
- package/lib/browser/symbols/injectable-token.mjs +6 -0
- package/lib/browser/symbols/injectable-token.mjs.map +1 -0
- package/lib/browser/token/injection-token.d.mts +55 -0
- package/lib/browser/token/injection-token.d.mts.map +1 -0
- package/lib/browser/token/injection-token.mjs +100 -0
- package/lib/browser/token/injection-token.mjs.map +1 -0
- package/lib/browser/token/registry.d.mts +37 -0
- package/lib/browser/token/registry.d.mts.map +1 -0
- package/lib/browser/token/registry.mjs +86 -0
- package/lib/browser/token/registry.mjs.map +1 -0
- package/lib/browser/utils/default-injectors.d.mts +12 -0
- package/lib/browser/utils/default-injectors.d.mts.map +1 -0
- package/lib/browser/utils/default-injectors.mjs +13 -0
- package/lib/browser/utils/default-injectors.mjs.map +1 -0
- package/lib/browser/utils/get-injectable-token.d.mts +9 -0
- package/lib/browser/utils/get-injectable-token.d.mts.map +1 -0
- package/lib/browser/utils/get-injectable-token.mjs +13 -0
- package/lib/browser/utils/get-injectable-token.mjs.map +1 -0
- package/lib/browser/utils/get-injectors.d.mts +55 -0
- package/lib/browser/utils/get-injectors.d.mts.map +1 -0
- package/lib/browser/utils/get-injectors.mjs +121 -0
- package/lib/browser/utils/get-injectors.mjs.map +1 -0
- package/lib/browser/utils/types.d.mts +23 -0
- package/lib/browser/utils/types.d.mts.map +1 -0
- package/lib/{container-Pb_Y4Z4x.mjs → container-8-z89TyQ.mjs} +1269 -1305
- package/lib/container-8-z89TyQ.mjs.map +1 -0
- package/lib/{container-BuAutHGg.d.mts → container-CNiqesCL.d.mts} +600 -569
- package/lib/container-CNiqesCL.d.mts.map +1 -0
- package/lib/{container-DnzgpfBe.cjs → container-CaY2fDuk.cjs} +1287 -1329
- package/lib/container-CaY2fDuk.cjs.map +1 -0
- package/lib/{container-oGTgX2iX.d.cts → container-D-0Ho3qL.d.cts} +601 -565
- package/lib/container-D-0Ho3qL.d.cts.map +1 -0
- package/lib/index.cjs +13 -15
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +58 -223
- package/lib/index.d.cts.map +1 -1
- package/lib/index.d.mts +62 -222
- package/lib/index.d.mts.map +1 -1
- package/lib/index.mjs +5 -6
- package/lib/index.mjs.map +1 -1
- package/lib/testing/index.cjs +569 -311
- package/lib/testing/index.cjs.map +1 -1
- package/lib/testing/index.d.cts +370 -41
- package/lib/testing/index.d.cts.map +1 -1
- package/lib/testing/index.d.mts +370 -41
- package/lib/testing/index.d.mts.map +1 -1
- package/lib/testing/index.mjs +568 -305
- package/lib/testing/index.mjs.map +1 -1
- package/package.json +2 -1
- package/src/__tests__/circular-detector.spec.mts +193 -0
- package/src/__tests__/concurrent.spec.mts +368 -0
- package/src/__tests__/container.spec.mts +32 -30
- package/src/__tests__/di-error.spec.mts +351 -0
- package/src/__tests__/e2e.browser.spec.mts +0 -4
- package/src/__tests__/e2e.spec.mts +10 -19
- package/src/__tests__/event-emitter.spec.mts +232 -109
- package/src/__tests__/get-injectors.spec.mts +250 -39
- package/src/__tests__/injection-token.spec.mts +293 -349
- package/src/__tests__/library-findings.spec.mts +8 -8
- package/src/__tests__/registry.spec.mts +358 -210
- package/src/__tests__/resolution-context.spec.mts +255 -0
- package/src/__tests__/scope-tracker.spec.mts +598 -0
- package/src/__tests__/scope-upgrade.spec.mts +808 -0
- package/src/__tests__/scoped-container.spec.mts +595 -0
- package/src/__tests__/test-container.spec.mts +293 -0
- package/src/__tests__/token-resolver.spec.mts +207 -0
- package/src/__tests__/unified-storage.spec.mts +535 -0
- package/src/__tests__/unit-test-container.spec.mts +405 -0
- package/src/__type-tests__/container.spec-d.mts +180 -0
- package/src/__type-tests__/factory.spec-d.mts +15 -3
- package/src/__type-tests__/inject.spec-d.mts +115 -20
- package/src/__type-tests__/injectable.spec-d.mts +69 -52
- package/src/__type-tests__/injection-token.spec-d.mts +176 -0
- package/src/__type-tests__/scoped-container.spec-d.mts +212 -0
- package/src/container/abstract-container.mts +327 -0
- package/src/container/container.mts +142 -170
- package/src/container/scoped-container.mts +126 -208
- package/src/decorators/factory.decorator.mts +16 -11
- package/src/decorators/injectable.decorator.mts +20 -16
- package/src/enums/index.mts +2 -2
- package/src/enums/injectable-scope.enum.mts +1 -0
- package/src/enums/injectable-type.enum.mts +1 -0
- package/src/errors/di-error.mts +96 -0
- package/src/event-emitter.mts +3 -27
- package/src/index.mts +6 -153
- package/src/interfaces/container.interface.mts +13 -0
- package/src/interfaces/factory.interface.mts +1 -1
- package/src/interfaces/index.mts +1 -1
- package/src/internal/context/async-local-storage.mts +3 -2
- package/src/internal/context/async-local-storage.types.mts +1 -0
- package/src/internal/context/factory-context.mts +1 -0
- package/src/internal/context/index.mts +3 -1
- package/src/internal/context/resolution-context.mts +1 -0
- package/src/internal/context/service-initialization-context.mts +43 -0
- package/src/internal/core/index.mts +5 -4
- package/src/internal/core/instance-resolver.mts +461 -292
- package/src/internal/core/name-resolver.mts +196 -0
- package/src/internal/core/scope-tracker.mts +242 -0
- package/src/internal/core/{instantiator.mts → service-initializer.mts} +51 -29
- package/src/internal/core/service-invalidator.mts +290 -0
- package/src/internal/core/{token-processor.mts → token-resolver.mts} +17 -88
- package/src/internal/holder/holder-storage.interface.mts +11 -5
- package/src/internal/holder/index.mts +2 -5
- package/src/internal/holder/instance-holder.mts +1 -3
- package/src/internal/holder/unified-storage.mts +245 -0
- package/src/internal/index.mts +2 -1
- package/src/internal/lifecycle/circular-detector.mts +1 -0
- package/src/internal/lifecycle/index.mts +1 -1
- package/src/internal/lifecycle/lifecycle-event-bus.mts +1 -0
- package/src/internal/stub-factory-class.mts +16 -0
- package/src/symbols/injectable-token.mts +3 -1
- package/src/testing/index.mts +2 -0
- package/src/testing/test-container.mts +546 -85
- package/src/testing/types.mts +117 -0
- package/src/testing/unit-test-container.mts +509 -0
- package/src/token/injection-token.mts +41 -4
- package/src/token/registry.mts +75 -9
- package/src/utils/default-injectors.mts +16 -0
- package/src/utils/get-injectable-token.mts +2 -3
- package/src/utils/get-injectors.mts +26 -15
- package/src/utils/index.mts +3 -1
- package/src/utils/types.mts +1 -0
- package/tsdown.config.mts +11 -1
- package/lib/browser/index.d.mts.map +0 -1
- package/lib/browser/index.mjs.map +0 -1
- package/lib/container-BuAutHGg.d.mts.map +0 -1
- package/lib/container-DnzgpfBe.cjs.map +0 -1
- package/lib/container-Pb_Y4Z4x.mjs.map +0 -1
- package/lib/container-oGTgX2iX.d.cts.map +0 -1
- package/src/__tests__/async-local-storage.browser.spec.mts +0 -166
- package/src/__tests__/async-local-storage.spec.mts +0 -333
- package/src/__tests__/errors.spec.mts +0 -87
- package/src/__tests__/factory.spec.mts +0 -137
- package/src/__tests__/injectable.spec.mts +0 -246
- package/src/__tests__/request-scope.spec.mts +0 -416
- package/src/__tests__/service-instantiator.spec.mts +0 -410
- package/src/__tests__/service-locator-event-bus.spec.mts +0 -242
- package/src/__tests__/service-locator-manager.spec.mts +0 -300
- package/src/__tests__/service-locator.spec.mts +0 -966
- package/src/__tests__/unified-api.spec.mts +0 -130
- package/src/browser.mts +0 -11
- package/src/injectors.mts +0 -18
- package/src/internal/context/request-context.mts +0 -214
- package/src/internal/core/invalidator.mts +0 -437
- package/src/internal/core/service-locator.mts +0 -202
- package/src/internal/holder/base-holder-manager.mts +0 -238
- package/src/internal/holder/holder-manager.mts +0 -85
- package/src/internal/holder/request-storage.mts +0 -134
- package/src/internal/holder/singleton-storage.mts +0 -105
- package/src/testing/README.md +0 -80
- package/src/testing/__tests__/test-container.spec.mts +0 -173
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { InjectionToken } from "../../token/injection-token.mjs";
|
|
2
|
+
import { InjectableScope } from "../../enums/injectable-scope.enum.mjs";
|
|
3
|
+
import { Registry } from "../../token/registry.mjs";
|
|
4
|
+
import { NameResolver } from "./name-resolver.mjs";
|
|
5
|
+
import { DIError } from "../../errors/di-error.mjs";
|
|
6
|
+
import { IHolderStorage } from "../holder/holder-storage.interface.mjs";
|
|
7
|
+
|
|
8
|
+
//#region src/internal/core/scope-tracker.d.mts
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Component for tracking and handling scope upgrades.
|
|
12
|
+
*
|
|
13
|
+
* Detects when a Singleton service needs to be upgraded to Request scope
|
|
14
|
+
* and coordinates the scope upgrade process atomically.
|
|
15
|
+
*/
|
|
16
|
+
declare class ScopeTracker {
|
|
17
|
+
private readonly registry;
|
|
18
|
+
private readonly nameResolver;
|
|
19
|
+
private readonly logger;
|
|
20
|
+
constructor(registry: Registry, nameResolver: NameResolver, logger?: Console | null);
|
|
21
|
+
/**
|
|
22
|
+
* Checks if a dependency requires scope upgrade and performs it if needed.
|
|
23
|
+
* Called during service resolution when a dependency is resolved.
|
|
24
|
+
*
|
|
25
|
+
* @param currentServiceName - Name of the service being created
|
|
26
|
+
* @param currentServiceScope - Current scope of the service being created
|
|
27
|
+
* @param dependencyName - Name of the dependency being resolved
|
|
28
|
+
* @param dependencyScope - Scope of the dependency
|
|
29
|
+
* @param dependencyToken - Token of the dependency
|
|
30
|
+
* @param singletonStorage - Singleton storage instance
|
|
31
|
+
* @param requestStorage - Request storage instance (if in request context)
|
|
32
|
+
* @param requestId - Request ID (if in request context)
|
|
33
|
+
* @returns [needsUpgrade: boolean, newName?: string] - whether upgrade occurred and new name
|
|
34
|
+
*/
|
|
35
|
+
checkAndUpgradeScope(currentServiceName: string, currentServiceScope: InjectableScope, dependencyName: string, dependencyScope: InjectableScope, dependencyToken: InjectionToken<any, any>, singletonStorage: IHolderStorage, requestStorage?: IHolderStorage, requestId?: string): [boolean, string?];
|
|
36
|
+
/**
|
|
37
|
+
* Performs the actual scope upgrade from Singleton to Request.
|
|
38
|
+
* This is the core migration logic.
|
|
39
|
+
*
|
|
40
|
+
* @param serviceName - Current service name (without requestId)
|
|
41
|
+
* @param token - Service injection token
|
|
42
|
+
* @param singletonStorage - Source storage
|
|
43
|
+
* @param requestStorage - Target storage
|
|
44
|
+
* @param requestId - Request ID to include in new name
|
|
45
|
+
* @returns [success: boolean, newName?: string, error?: DIError]
|
|
46
|
+
*/
|
|
47
|
+
upgradeScopeToRequest(serviceName: string, token: InjectionToken<any, any>, singletonStorage: IHolderStorage, requestStorage: IHolderStorage, requestId: string): Promise<[boolean, string?, DIError?]>;
|
|
48
|
+
/**
|
|
49
|
+
* Synchronous part of scope upgrade - handles immediate updates.
|
|
50
|
+
* Async operations (like waiting for holder creation) should be done separately.
|
|
51
|
+
*/
|
|
52
|
+
private upgradeScopeToRequestSync;
|
|
53
|
+
/**
|
|
54
|
+
* Updates all parent dependencies to reference the new service name.
|
|
55
|
+
*
|
|
56
|
+
* @param oldName - Original service name
|
|
57
|
+
* @param newName - New service name with requestId
|
|
58
|
+
* @param singletonStorage - Singleton storage to check
|
|
59
|
+
* @param requestStorage - Request storage to check
|
|
60
|
+
*/
|
|
61
|
+
updateParentDependencies(oldName: string, newName: string, singletonStorage: IHolderStorage, requestStorage?: IHolderStorage): void;
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { ScopeTracker };
|
|
65
|
+
//# sourceMappingURL=scope-tracker.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-tracker.d.mts","names":[],"sources":["../../../../src/internal/core/scope-tracker.mts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAgBA;;;;AAuByB,cAvBZ,YAAA,CAuBY;EAEJ,iBAAA,QAAA;EACA,iBAAA,YAAA;EACC,iBAAA,MAAA;EACD,WAAA,CAAA,QAAA,EA1BU,QA0BV,EAAA,YAAA,EAzBc,YAyBd,EAAA,MAAA,CAAA,EAxBQ,OAwBR,GAAA,IAAA;EA2DV;;;;;;;;;;;;;;wEAhEc,0DAEJ,kCACA,4CACC,iCACD;;;;;;;;;;;;oDA2DV,4CACW,gCACF,oCAEf,2BAA2B;;;;;;;;;;;;;;+EA2HV,iCACD"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { InjectableScope } from "../../enums/injectable-scope.enum.mjs";
|
|
2
|
+
import { DIError } from "../../errors/di-error.mjs";
|
|
3
|
+
import { InstanceStatus } from "../holder/instance-holder.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/internal/core/scope-tracker.mts
|
|
6
|
+
/**
|
|
7
|
+
* Component for tracking and handling scope upgrades.
|
|
8
|
+
*
|
|
9
|
+
* Detects when a Singleton service needs to be upgraded to Request scope
|
|
10
|
+
* and coordinates the scope upgrade process atomically.
|
|
11
|
+
*/ var ScopeTracker = class {
|
|
12
|
+
registry;
|
|
13
|
+
nameResolver;
|
|
14
|
+
logger;
|
|
15
|
+
constructor(registry, nameResolver, logger = null) {
|
|
16
|
+
this.registry = registry;
|
|
17
|
+
this.nameResolver = nameResolver;
|
|
18
|
+
this.logger = logger;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a dependency requires scope upgrade and performs it if needed.
|
|
22
|
+
* Called during service resolution when a dependency is resolved.
|
|
23
|
+
*
|
|
24
|
+
* @param currentServiceName - Name of the service being created
|
|
25
|
+
* @param currentServiceScope - Current scope of the service being created
|
|
26
|
+
* @param dependencyName - Name of the dependency being resolved
|
|
27
|
+
* @param dependencyScope - Scope of the dependency
|
|
28
|
+
* @param dependencyToken - Token of the dependency
|
|
29
|
+
* @param singletonStorage - Singleton storage instance
|
|
30
|
+
* @param requestStorage - Request storage instance (if in request context)
|
|
31
|
+
* @param requestId - Request ID (if in request context)
|
|
32
|
+
* @returns [needsUpgrade: boolean, newName?: string] - whether upgrade occurred and new name
|
|
33
|
+
*/ checkAndUpgradeScope(currentServiceName, currentServiceScope, dependencyName, dependencyScope, dependencyToken, singletonStorage, requestStorage, requestId) {
|
|
34
|
+
if (currentServiceScope !== InjectableScope.Singleton || dependencyScope !== InjectableScope.Request) return [false];
|
|
35
|
+
if (!requestStorage || !requestId) {
|
|
36
|
+
this.logger?.warn(`[ScopeTracker] Cannot upgrade scope for ${currentServiceName}: missing requestStorage or requestId`);
|
|
37
|
+
return [false];
|
|
38
|
+
}
|
|
39
|
+
this.logger?.log(`[ScopeTracker] Upgrading ${currentServiceName} from Singleton to Request scope`);
|
|
40
|
+
try {
|
|
41
|
+
const [success, newName] = this.upgradeScopeToRequestSync(currentServiceName, dependencyToken, singletonStorage, requestStorage, requestId);
|
|
42
|
+
if (success && newName) return [true, newName];
|
|
43
|
+
} catch (error) {
|
|
44
|
+
this.logger?.error(`[ScopeTracker] Error upgrading scope for ${currentServiceName}:`, error);
|
|
45
|
+
}
|
|
46
|
+
return [false];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Performs the actual scope upgrade from Singleton to Request.
|
|
50
|
+
* This is the core migration logic.
|
|
51
|
+
*
|
|
52
|
+
* @param serviceName - Current service name (without requestId)
|
|
53
|
+
* @param token - Service injection token
|
|
54
|
+
* @param singletonStorage - Source storage
|
|
55
|
+
* @param requestStorage - Target storage
|
|
56
|
+
* @param requestId - Request ID to include in new name
|
|
57
|
+
* @returns [success: boolean, newName?: string, error?: DIError]
|
|
58
|
+
*/ async upgradeScopeToRequest(serviceName, token, singletonStorage, requestStorage, requestId) {
|
|
59
|
+
try {
|
|
60
|
+
const [success, newName] = this.upgradeScopeToRequestSync(serviceName, token, singletonStorage, requestStorage, requestId);
|
|
61
|
+
if (success && newName) return [true, newName];
|
|
62
|
+
return [
|
|
63
|
+
false,
|
|
64
|
+
void 0,
|
|
65
|
+
DIError.storageError("Scope upgrade failed", "upgradeScopeToRequest", serviceName)
|
|
66
|
+
];
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return [
|
|
69
|
+
false,
|
|
70
|
+
void 0,
|
|
71
|
+
error instanceof DIError ? error : DIError.unknown(error)
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Synchronous part of scope upgrade - handles immediate updates.
|
|
77
|
+
* Async operations (like waiting for holder creation) should be done separately.
|
|
78
|
+
*/ upgradeScopeToRequestSync(serviceName, token, singletonStorage, requestStorage, requestId) {
|
|
79
|
+
const newName = this.nameResolver.upgradeInstanceNameToRequest(serviceName, requestId);
|
|
80
|
+
if (!this.registry.updateScope(token, InjectableScope.Request)) {
|
|
81
|
+
this.logger?.warn(`[ScopeTracker] Could not update scope in registry for ${serviceName}`);
|
|
82
|
+
return [false];
|
|
83
|
+
}
|
|
84
|
+
const holderResult = singletonStorage.get(serviceName);
|
|
85
|
+
if (holderResult === null) return [true, newName];
|
|
86
|
+
const [error, holder] = holderResult;
|
|
87
|
+
if (error) {
|
|
88
|
+
this.logger?.warn(`[ScopeTracker] Holder for ${serviceName} is in error state: ${error.message}`);
|
|
89
|
+
return [false];
|
|
90
|
+
}
|
|
91
|
+
if (!holder) return [false];
|
|
92
|
+
if (holder.status === InstanceStatus.Creating) {
|
|
93
|
+
holder.name = newName;
|
|
94
|
+
requestStorage.set(newName, holder);
|
|
95
|
+
singletonStorage.delete(serviceName);
|
|
96
|
+
this.updateParentDependencies(serviceName, newName, singletonStorage, requestStorage);
|
|
97
|
+
return [true, newName];
|
|
98
|
+
}
|
|
99
|
+
holder.name = newName;
|
|
100
|
+
requestStorage.set(newName, holder);
|
|
101
|
+
singletonStorage.delete(serviceName);
|
|
102
|
+
this.updateParentDependencies(serviceName, newName, singletonStorage, requestStorage);
|
|
103
|
+
return [true, newName];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Updates all parent dependencies to reference the new service name.
|
|
107
|
+
*
|
|
108
|
+
* @param oldName - Original service name
|
|
109
|
+
* @param newName - New service name with requestId
|
|
110
|
+
* @param singletonStorage - Singleton storage to check
|
|
111
|
+
* @param requestStorage - Request storage to check
|
|
112
|
+
*/ updateParentDependencies(oldName, newName, singletonStorage, requestStorage) {
|
|
113
|
+
singletonStorage.updateDependencyReference(oldName, newName);
|
|
114
|
+
if (requestStorage) requestStorage.updateDependencyReference(oldName, newName);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
//#endregion
|
|
119
|
+
export { ScopeTracker };
|
|
120
|
+
//# sourceMappingURL=scope-tracker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-tracker.mjs","names":["InjectableScope","Scope","DIError","InstanceStatus","ScopeTracker","registry","nameResolver","logger","checkAndUpgradeScope","currentServiceName","currentServiceScope","dependencyName","dependencyScope","dependencyToken","singletonStorage","requestStorage","requestId","Singleton","Request","warn","log","success","newName","upgradeScopeToRequestSync","error","upgradeScopeToRequest","serviceName","token","undefined","storageError","unknown","upgradeInstanceNameToRequest","updated","updateScope","holderResult","get","holder","message","status","Creating","name","set","delete","updateParentDependencies","oldName","updateDependencyReference"],"sources":["../../../../src/internal/core/scope-tracker.mts"],"sourcesContent":["import type { InjectableScope } from '../../enums/index.mjs'\nimport type { InjectionToken } from '../../token/injection-token.mjs'\nimport type { IHolderStorage } from '../holder/holder-storage.interface.mjs'\n\nimport { InjectableScope as Scope } from '../../enums/index.mjs'\nimport { DIError } from '../../errors/index.mjs'\nimport { InstanceStatus } from '../holder/instance-holder.mjs'\nimport { Registry } from '../../token/registry.mjs'\nimport { NameResolver } from './name-resolver.mjs'\n\n/**\n * Component for tracking and handling scope upgrades.\n *\n * Detects when a Singleton service needs to be upgraded to Request scope\n * and coordinates the scope upgrade process atomically.\n */\nexport class ScopeTracker {\n constructor(\n private readonly registry: Registry,\n private readonly nameResolver: NameResolver,\n private readonly logger: Console | null = null,\n ) {}\n\n /**\n * Checks if a dependency requires scope upgrade and performs it if needed.\n * Called during service resolution when a dependency is resolved.\n *\n * @param currentServiceName - Name of the service being created\n * @param currentServiceScope - Current scope of the service being created\n * @param dependencyName - Name of the dependency being resolved\n * @param dependencyScope - Scope of the dependency\n * @param dependencyToken - Token of the dependency\n * @param singletonStorage - Singleton storage instance\n * @param requestStorage - Request storage instance (if in request context)\n * @param requestId - Request ID (if in request context)\n * @returns [needsUpgrade: boolean, newName?: string] - whether upgrade occurred and new name\n */\n checkAndUpgradeScope(\n currentServiceName: string,\n currentServiceScope: InjectableScope,\n dependencyName: string,\n dependencyScope: InjectableScope,\n dependencyToken: InjectionToken<any, any>,\n singletonStorage: IHolderStorage,\n requestStorage?: IHolderStorage,\n requestId?: string,\n ): [boolean, string?] {\n // Only upgrade if current service is Singleton and dependency is Request\n if (\n currentServiceScope !== Scope.Singleton ||\n dependencyScope !== Scope.Request\n ) {\n return [false]\n }\n\n // Need request storage and requestId for upgrade\n if (!requestStorage || !requestId) {\n this.logger?.warn(\n `[ScopeTracker] Cannot upgrade scope for ${currentServiceName}: missing requestStorage or requestId`,\n )\n return [false]\n }\n\n // Perform the upgrade\n this.logger?.log(\n `[ScopeTracker] Upgrading ${currentServiceName} from Singleton to Request scope`,\n )\n\n try {\n const [success, newName] = this.upgradeScopeToRequestSync(\n currentServiceName,\n dependencyToken,\n singletonStorage,\n requestStorage,\n requestId,\n )\n\n if (success && newName) {\n return [true, newName]\n }\n } catch (error) {\n this.logger?.error(\n `[ScopeTracker] Error upgrading scope for ${currentServiceName}:`,\n error,\n )\n }\n\n return [false]\n }\n\n /**\n * Performs the actual scope upgrade from Singleton to Request.\n * This is the core migration logic.\n *\n * @param serviceName - Current service name (without requestId)\n * @param token - Service injection token\n * @param singletonStorage - Source storage\n * @param requestStorage - Target storage\n * @param requestId - Request ID to include in new name\n * @returns [success: boolean, newName?: string, error?: DIError]\n */\n async upgradeScopeToRequest(\n serviceName: string,\n token: InjectionToken<any, any>,\n singletonStorage: IHolderStorage,\n requestStorage: IHolderStorage,\n requestId: string,\n ): Promise<[boolean, string?, DIError?]> {\n try {\n const [success, newName] = this.upgradeScopeToRequestSync(\n serviceName,\n token,\n singletonStorage,\n requestStorage,\n requestId,\n )\n\n if (success && newName) {\n return [true, newName]\n }\n return [\n false,\n undefined,\n DIError.storageError(\n 'Scope upgrade failed',\n 'upgradeScopeToRequest',\n serviceName,\n ),\n ]\n } catch (error) {\n return [\n false,\n undefined,\n error instanceof DIError ? error : DIError.unknown(error as Error),\n ]\n }\n }\n\n /**\n * Synchronous part of scope upgrade - handles immediate updates.\n * Async operations (like waiting for holder creation) should be done separately.\n */\n private upgradeScopeToRequestSync(\n serviceName: string,\n token: InjectionToken<any, any>,\n singletonStorage: IHolderStorage,\n requestStorage: IHolderStorage,\n requestId: string,\n ): [boolean, string?] {\n // 1. Upgrade existing instance name to include requestId\n // This preserves any args hash that might be in the original name\n const newName = this.nameResolver.upgradeInstanceNameToRequest(\n serviceName,\n requestId,\n )\n\n // 2. Update Registry scope to Request (synchronous)\n const updated = this.registry.updateScope(token, Scope.Request)\n if (!updated) {\n this.logger?.warn(\n `[ScopeTracker] Could not update scope in registry for ${serviceName}`,\n )\n return [false]\n }\n\n // 3. Check if holder exists in singleton storage\n const holderResult = singletonStorage.get(serviceName)\n if (holderResult === null) {\n // No holder exists yet - just update registry, future resolutions will use request storage\n return [true, newName]\n }\n\n const [error, holder] = holderResult\n if (error) {\n this.logger?.warn(\n `[ScopeTracker] Holder for ${serviceName} is in error state: ${error.message}`,\n )\n return [false]\n }\n\n if (!holder) {\n return [false]\n }\n\n // 4. If holder is in \"Creating\" state, we need to wait for it before migrating\n // For now, we'll update the name and move it, but the caller should wait for creation\n if (holder.status === InstanceStatus.Creating) {\n // Update holder name\n holder.name = newName\n // Move to request storage\n requestStorage.set(newName, holder)\n // Remove from singleton storage\n singletonStorage.delete(serviceName)\n // Update parent dependencies\n this.updateParentDependencies(\n serviceName,\n newName,\n singletonStorage,\n requestStorage,\n )\n return [true, newName]\n }\n\n // 5. Move holder from singleton to request storage\n holder.name = newName\n requestStorage.set(newName, holder)\n singletonStorage.delete(serviceName)\n\n // 6. Update all parent dependencies\n this.updateParentDependencies(\n serviceName,\n newName,\n singletonStorage,\n requestStorage,\n )\n\n return [true, newName]\n }\n\n /**\n * Updates all parent dependencies to reference the new service name.\n *\n * @param oldName - Original service name\n * @param newName - New service name with requestId\n * @param singletonStorage - Singleton storage to check\n * @param requestStorage - Request storage to check\n */\n updateParentDependencies(\n oldName: string,\n newName: string,\n singletonStorage: IHolderStorage,\n requestStorage?: IHolderStorage,\n ): void {\n // Update dependencies in singleton storage\n singletonStorage.updateDependencyReference(oldName, newName)\n\n // Update dependencies in request storage if provided\n if (requestStorage) {\n requestStorage.updateDependencyReference(oldName, newName)\n }\n }\n}\n"],"mappings":";;;;;;;;;;GAgBA,IAAaI,eAAb,MAAaA;;;;CACX,YACE,UACA,cACA,SAA0C,MAC1C;OAHiBC,WAAAA;OACAC,eAAAA;OACAC,SAAAA;;;;;;;;;;;;;;;IAiBnBC,qBACEC,oBACAC,qBACAC,gBACAC,iBACAC,iBACAC,kBACAC,gBACAC,WACoB;AAEpB,MACEN,wBAAwBT,gBAAMgB,aAC9BL,oBAAoBX,gBAAMiB,QAE1B,QAAO,CAAC,MAAM;AAIhB,MAAI,CAACH,kBAAkB,CAACC,WAAW;AACjC,QAAKT,QAAQY,KACX,2CAA2CV,mBAAmB,uCAAsC;AAEtG,UAAO,CAAC,MAAM;;AAIhB,OAAKF,QAAQa,IACX,4BAA4BX,mBAAmB,kCAAiC;AAGlF,MAAI;GACF,MAAM,CAACY,SAASC,WAAW,KAAKC,0BAC9Bd,oBACAI,iBACAC,kBACAC,gBACAC,UAAAA;AAGF,OAAIK,WAAWC,QACb,QAAO,CAAC,MAAMA,QAAQ;WAEjBE,OAAO;AACd,QAAKjB,QAAQiB,MACX,4CAA4Cf,mBAAmB,IAC/De,MAAAA;;AAIJ,SAAO,CAAC,MAAM;;;;;;;;;;;;IAchB,MAAMC,sBACJC,aACAC,OACAb,kBACAC,gBACAC,WACuC;AACvC,MAAI;GACF,MAAM,CAACK,SAASC,WAAW,KAAKC,0BAC9BG,aACAC,OACAb,kBACAC,gBACAC,UAAAA;AAGF,OAAIK,WAAWC,QACb,QAAO,CAAC,MAAMA,QAAQ;AAExB,UAAO;IACL;IACAM;IACA1B,QAAQ2B,aACN,wBACA,yBACAH,YAAAA;IAEH;WACMF,OAAO;AACd,UAAO;IACL;IACAI;IACAJ,iBAAiBtB,UAAUsB,QAAQtB,QAAQ4B,QAAQN,MAAAA;IACpD;;;;;;IAQL,0BACEE,aACAC,OACAb,kBACAC,gBACAC,WACoB;EAGpB,MAAMM,UAAU,KAAKhB,aAAayB,6BAChCL,aACAV,UAAAA;AAKF,MAAI,CADY,KAAKX,SAAS4B,YAAYN,OAAO1B,gBAAMiB,QAAO,EAChD;AACZ,QAAKX,QAAQY,KACX,yDAAyDO,cAAa;AAExE,UAAO,CAAC,MAAM;;EAIhB,MAAMQ,eAAepB,iBAAiBqB,IAAIT,YAAAA;AAC1C,MAAIQ,iBAAiB,KAEnB,QAAO,CAAC,MAAMZ,QAAQ;EAGxB,MAAM,CAACE,OAAOY,UAAUF;AACxB,MAAIV,OAAO;AACT,QAAKjB,QAAQY,KACX,6BAA6BO,YAAY,sBAAsBF,MAAMa,UAAS;AAEhF,UAAO,CAAC,MAAM;;AAGhB,MAAI,CAACD,OACH,QAAO,CAAC,MAAM;AAKhB,MAAIA,OAAOE,WAAWnC,eAAeoC,UAAU;AAE7CH,UAAOI,OAAOlB;AAEdP,kBAAe0B,IAAInB,SAASc,OAAAA;AAE5BtB,oBAAiB4B,OAAOhB,YAAAA;AAExB,QAAKiB,yBACHjB,aACAJ,SACAR,kBACAC,eAAAA;AAEF,UAAO,CAAC,MAAMO,QAAQ;;AAIxBc,SAAOI,OAAOlB;AACdP,iBAAe0B,IAAInB,SAASc,OAAAA;AAC5BtB,mBAAiB4B,OAAOhB,YAAAA;AAGxB,OAAKiB,yBACHjB,aACAJ,SACAR,kBACAC,eAAAA;AAGF,SAAO,CAAC,MAAMO,QAAQ;;;;;;;;;IAWxBqB,yBACEC,SACAtB,SACAR,kBACAC,gBACM;AAEND,mBAAiB+B,0BAA0BD,SAAStB,QAAAA;AAGpD,MAAIP,eACFA,gBAAe8B,0BAA0BD,SAAStB,QAAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ServiceInitializationContext } from "../context/service-initialization-context.mjs";
|
|
2
|
+
import { Injectors } from "../../utils/get-injectors.mjs";
|
|
3
|
+
import { FactoryRecord } from "../../token/registry.mjs";
|
|
4
|
+
import { DIError } from "../../errors/di-error.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/internal/core/service-initializer.d.mts
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates service instances from registry records.
|
|
10
|
+
*
|
|
11
|
+
* Handles both class-based (@Injectable) and factory-based (@Factory) services,
|
|
12
|
+
* managing the instantiation lifecycle including lifecycle hook invocation.
|
|
13
|
+
*/
|
|
14
|
+
declare class ServiceInitializer {
|
|
15
|
+
private readonly injectors;
|
|
16
|
+
constructor(injectors: Injectors);
|
|
17
|
+
/**
|
|
18
|
+
* Instantiates a service based on its registry record.
|
|
19
|
+
* @param ctx The factory context for dependency injection
|
|
20
|
+
* @param record The factory record from the registry
|
|
21
|
+
* @param args Optional arguments for the service
|
|
22
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
23
|
+
*/
|
|
24
|
+
instantiateService<T>(ctx: ServiceInitializationContext, record: FactoryRecord<T, any>, args?: any): Promise<[undefined, T] | [DIError]>;
|
|
25
|
+
/**
|
|
26
|
+
* Instantiates a class-based service (Injectable decorator).
|
|
27
|
+
* @param ctx The factory context for dependency injection
|
|
28
|
+
* @param record The factory record from the registry
|
|
29
|
+
* @param args Optional arguments for the service constructor
|
|
30
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
31
|
+
*/
|
|
32
|
+
private instantiateClass;
|
|
33
|
+
/**
|
|
34
|
+
* Instantiates a factory-based service (Factory decorator).
|
|
35
|
+
* @param ctx The factory context for dependency injection
|
|
36
|
+
* @param record The factory record from the registry
|
|
37
|
+
* @param args Optional arguments for the factory
|
|
38
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
39
|
+
*/
|
|
40
|
+
private instantiateFactory;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { ServiceInitializer };
|
|
44
|
+
//# sourceMappingURL=service-initializer.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-initializer.d.mts","names":[],"sources":["../../../../src/internal/core/service-initializer.mts"],"sourcesContent":[],"mappings":";;;;;;;;;AAaA;;;;AAYY,cAZC,kBAAA,CAYD;EAEa,iBAAA,SAAA;EAAM,WAAA,CAAA,SAAA,EAbW,SAaX;EAA1B;;;;;;;6BAHI,sCACG,cAAc,sBAErB,oBAAoB,MAAM"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { InjectableType } from "../../enums/injectable-type.enum.mjs";
|
|
2
|
+
import { DIError } from "../../errors/di-error.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/internal/core/service-initializer.mts
|
|
5
|
+
/**
|
|
6
|
+
* Creates service instances from registry records.
|
|
7
|
+
*
|
|
8
|
+
* Handles both class-based (@Injectable) and factory-based (@Factory) services,
|
|
9
|
+
* managing the instantiation lifecycle including lifecycle hook invocation.
|
|
10
|
+
*/ var ServiceInitializer = class {
|
|
11
|
+
injectors;
|
|
12
|
+
constructor(injectors) {
|
|
13
|
+
this.injectors = injectors;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Instantiates a service based on its registry record.
|
|
17
|
+
* @param ctx The factory context for dependency injection
|
|
18
|
+
* @param record The factory record from the registry
|
|
19
|
+
* @param args Optional arguments for the service
|
|
20
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
21
|
+
*/ async instantiateService(ctx, record, args = void 0) {
|
|
22
|
+
try {
|
|
23
|
+
switch (record.type) {
|
|
24
|
+
case InjectableType.Class: return this.instantiateClass(ctx, record, args);
|
|
25
|
+
case InjectableType.Factory: return this.instantiateFactory(ctx, record, args);
|
|
26
|
+
default: throw DIError.unknown(`[ServiceInitializer] Unknown service type: ${record.type}`);
|
|
27
|
+
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Instantiates a class-based service (Injectable decorator).
|
|
34
|
+
* @param ctx The factory context for dependency injection
|
|
35
|
+
* @param record The factory record from the registry
|
|
36
|
+
* @param args Optional arguments for the service constructor
|
|
37
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
38
|
+
*/ async instantiateClass(ctx, record, args) {
|
|
39
|
+
try {
|
|
40
|
+
const tryLoad = this.injectors.wrapSyncInit(() => {
|
|
41
|
+
const original = this.injectors.provideFactoryContext(ctx);
|
|
42
|
+
let result = new record.target(...args ? [args] : []);
|
|
43
|
+
this.injectors.provideFactoryContext(original);
|
|
44
|
+
return result;
|
|
45
|
+
});
|
|
46
|
+
let [instance, promises, injectState] = tryLoad();
|
|
47
|
+
if (promises.length > 0) {
|
|
48
|
+
if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
|
|
49
|
+
const newRes = tryLoad(injectState);
|
|
50
|
+
instance = newRes[0];
|
|
51
|
+
promises = newRes[1];
|
|
52
|
+
}
|
|
53
|
+
if (promises.length > 0) {
|
|
54
|
+
console.error(`[ServiceInitializer] ${record.target.name} has problem with it's definition.
|
|
55
|
+
|
|
56
|
+
One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.
|
|
57
|
+
|
|
58
|
+
Please use asyncInject instead of inject to load those dependencies.`);
|
|
59
|
+
throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
|
|
60
|
+
}
|
|
61
|
+
if ("onServiceInit" in instance) await instance.onServiceInit();
|
|
62
|
+
if ("onServiceDestroy" in instance) ctx.addDestroyListener(async () => {
|
|
63
|
+
await instance.onServiceDestroy();
|
|
64
|
+
});
|
|
65
|
+
return [void 0, instance];
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Instantiates a factory-based service (Factory decorator).
|
|
72
|
+
* @param ctx The factory context for dependency injection
|
|
73
|
+
* @param record The factory record from the registry
|
|
74
|
+
* @param args Optional arguments for the factory
|
|
75
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
76
|
+
*/ async instantiateFactory(ctx, record, args) {
|
|
77
|
+
try {
|
|
78
|
+
const tryLoad = this.injectors.wrapSyncInit(() => {
|
|
79
|
+
const original = this.injectors.provideFactoryContext(ctx);
|
|
80
|
+
let result = new record.target();
|
|
81
|
+
this.injectors.provideFactoryContext(original);
|
|
82
|
+
return result;
|
|
83
|
+
});
|
|
84
|
+
let [builder, promises, injectState] = tryLoad();
|
|
85
|
+
if (promises.length > 0) {
|
|
86
|
+
if ((await Promise.allSettled(promises)).some((result) => result.status === "rejected")) throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
|
|
87
|
+
const newRes = tryLoad(injectState);
|
|
88
|
+
builder = newRes[0];
|
|
89
|
+
promises = newRes[1];
|
|
90
|
+
}
|
|
91
|
+
if (promises.length > 0) {
|
|
92
|
+
console.error(`[ServiceInitializer] ${record.target.name} has problem with it's definition.
|
|
93
|
+
|
|
94
|
+
One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.
|
|
95
|
+
|
|
96
|
+
Please use asyncInject instead of inject to load those dependencies.`);
|
|
97
|
+
throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Service cannot be instantiated"));
|
|
98
|
+
}
|
|
99
|
+
if (typeof builder.create !== "function") throw DIError.initializationError(record.target.name, /* @__PURE__ */ new Error("Factory does not implement the create method"));
|
|
100
|
+
return [void 0, await builder.create(ctx, args)];
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return [error instanceof DIError ? error : DIError.initializationError(record.target.name, error)];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
export { ServiceInitializer };
|
|
109
|
+
//# sourceMappingURL=service-initializer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-initializer.mjs","names":["InjectableType","DIError","ServiceInitializer","injectors","instantiateService","ctx","record","args","undefined","type","Class","instantiateClass","Factory","instantiateFactory","unknown","error","initializationError","target","name","tryLoad","wrapSyncInit","original","provideFactoryContext","result","instance","promises","injectState","length","results","Promise","allSettled","some","status","Error","newRes","console","onServiceInit","addDestroyListener","onServiceDestroy","builder","create"],"sources":["../../../../src/internal/core/service-initializer.mts"],"sourcesContent":["import type { FactoryRecord } from '../../token/registry.mjs'\nimport type { Injectors } from '../../utils/index.mjs'\nimport type { ServiceInitializationContext } from '../context/service-initialization-context.mjs'\n\nimport { InjectableType } from '../../enums/index.mjs'\nimport { DIError } from '../../errors/index.mjs'\n\n/**\n * Creates service instances from registry records.\n *\n * Handles both class-based (@Injectable) and factory-based (@Factory) services,\n * managing the instantiation lifecycle including lifecycle hook invocation.\n */\nexport class ServiceInitializer {\n constructor(private readonly injectors: Injectors) {}\n\n /**\n * Instantiates a service based on its registry record.\n * @param ctx The factory context for dependency injection\n * @param record The factory record from the registry\n * @param args Optional arguments for the service\n * @returns Promise resolving to [undefined, instance] or [error]\n */\n async instantiateService<T>(\n ctx: ServiceInitializationContext,\n record: FactoryRecord<T, any>,\n args: any = undefined,\n ): Promise<[undefined, T] | [DIError]> {\n try {\n switch (record.type) {\n case InjectableType.Class:\n return this.instantiateClass(ctx, record, args)\n case InjectableType.Factory:\n return this.instantiateFactory(ctx, record, args)\n default:\n throw DIError.unknown(\n `[ServiceInitializer] Unknown service type: ${record.type}`,\n )\n }\n } catch (error) {\n return [\n error instanceof DIError\n ? error\n : DIError.initializationError(record.target.name, error as Error),\n ]\n }\n }\n\n /**\n * Instantiates a class-based service (Injectable decorator).\n * @param ctx The factory context for dependency injection\n * @param record The factory record from the registry\n * @param args Optional arguments for the service constructor\n * @returns Promise resolving to [undefined, instance] or [error]\n */\n private async instantiateClass<T>(\n ctx: ServiceInitializationContext,\n record: FactoryRecord<T, any>,\n args: any,\n ): Promise<[undefined, T] | [DIError]> {\n try {\n const tryLoad = this.injectors.wrapSyncInit(() => {\n const original = this.injectors.provideFactoryContext(\n ctx as ServiceInitializationContext,\n )\n let result = new record.target(...(args ? [args] : []))\n this.injectors.provideFactoryContext(original)\n return result\n })\n\n let [instance, promises, injectState] = tryLoad()\n if (promises.length > 0) {\n const results = await Promise.allSettled(promises)\n if (results.some((result) => result.status === 'rejected')) {\n throw DIError.initializationError(\n record.target.name,\n new Error('Service cannot be instantiated'),\n )\n }\n const newRes = tryLoad(injectState)\n instance = newRes[0]\n promises = newRes[1]\n }\n\n if (promises.length > 0) {\n console.error(\n `[ServiceInitializer] ${record.target.name} has problem with it's definition.\n\n One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.\n\n Please use asyncInject instead of inject to load those dependencies.`,\n )\n throw DIError.initializationError(\n record.target.name,\n new Error('Service cannot be instantiated'),\n )\n }\n\n // Handle lifecycle hooks\n if ('onServiceInit' in instance) {\n await (instance as any).onServiceInit()\n }\n if ('onServiceDestroy' in instance) {\n ctx.addDestroyListener(async () => {\n await (instance as any).onServiceDestroy()\n })\n }\n\n return [undefined, instance]\n } catch (error) {\n return [\n error instanceof DIError\n ? error\n : DIError.initializationError(record.target.name, error as Error),\n ]\n }\n }\n\n /**\n * Instantiates a factory-based service (Factory decorator).\n * @param ctx The factory context for dependency injection\n * @param record The factory record from the registry\n * @param args Optional arguments for the factory\n * @returns Promise resolving to [undefined, instance] or [error]\n */\n private async instantiateFactory<T>(\n ctx: ServiceInitializationContext,\n record: FactoryRecord<T, any>,\n args: any,\n ): Promise<[undefined, T] | [DIError]> {\n try {\n const tryLoad = this.injectors.wrapSyncInit(() => {\n const original = this.injectors.provideFactoryContext(ctx)\n let result = new record.target()\n this.injectors.provideFactoryContext(original)\n return result\n })\n\n let [builder, promises, injectState] = tryLoad()\n if (promises.length > 0) {\n const results = await Promise.allSettled(promises)\n if (results.some((result) => result.status === 'rejected')) {\n throw DIError.initializationError(\n record.target.name,\n new Error('Service cannot be instantiated'),\n )\n }\n const newRes = tryLoad(injectState)\n builder = newRes[0]\n promises = newRes[1]\n }\n\n if (promises.length > 0) {\n console.error(\n `[ServiceInitializer] ${record.target.name} has problem with it's definition.\n\n One or more of the dependencies are registered as a InjectableScope.Transient and are used with inject.\n\n Please use asyncInject instead of inject to load those dependencies.`,\n )\n throw DIError.initializationError(\n record.target.name,\n new Error('Service cannot be instantiated'),\n )\n }\n\n if (typeof builder.create !== 'function') {\n throw DIError.initializationError(\n record.target.name,\n new Error('Factory does not implement the create method'),\n )\n }\n\n const instance = await builder.create(ctx, args)\n return [undefined, instance]\n } catch (error) {\n return [\n error instanceof DIError\n ? error\n : DIError.initializationError(record.target.name, error as Error),\n ]\n }\n }\n}\n"],"mappings":";;;;;;;;;GAaA,IAAaE,qBAAb,MAAaA;;CACX,YAAY,WAAuC;OAAtBC,YAAAA;;;;;;;;IAS7B,MAAMC,mBACJC,KACAC,QACAC,OAAYC,QACyB;AACrC,MAAI;AACF,WAAQF,OAAOG,MAAf;IACE,KAAKT,eAAeU,MAClB,QAAO,KAAKC,iBAAiBN,KAAKC,QAAQC,KAAAA;IAC5C,KAAKP,eAAeY,QAClB,QAAO,KAAKC,mBAAmBR,KAAKC,QAAQC,KAAAA;IAC9C,QACE,OAAMN,QAAQa,QACZ,8CAA8CR,OAAOG,OAAM;;WAG1DM,OAAO;AACd,UAAO,CACLA,iBAAiBd,UACbc,QACAd,QAAQe,oBAAoBV,OAAOW,OAAOC,MAAMH,MAAAA,CACrD;;;;;;;;;IAWL,MAAcJ,iBACZN,KACAC,QACAC,MACqC;AACrC,MAAI;GACF,MAAMY,UAAU,KAAKhB,UAAUiB,mBAAa;IAC1C,MAAMC,WAAW,KAAKlB,UAAUmB,sBAC9BjB,IAAAA;IAEF,IAAIkB,SAAS,IAAIjB,OAAOW,OAAM,GAAKV,OAAO,CAACA,KAAK,GAAG,EAAE,CAAA;AACrD,SAAKJ,UAAUmB,sBAAsBD,SAAAA;AACrC,WAAOE;KACT;GAEA,IAAI,CAACC,UAAUC,UAAUC,eAAeP,SAAAA;AACxC,OAAIM,SAASE,SAAS,GAAG;AAEvB,SADgB,MAAME,QAAQC,WAAWL,SAAAA,EAC7BM,MAAMR,WAAWA,OAAOS,WAAW,WAAA,CAC7C,OAAM/B,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,iCAAA,CAAA;IAGd,MAAMC,SAASf,QAAQO,YAAAA;AACvBF,eAAWU,OAAO;AAClBT,eAAWS,OAAO;;AAGpB,OAAIT,SAASE,SAAS,GAAG;AACvBQ,YAAQpB,MACN,wBAAwBT,OAAOW,OAAOC,KAAK;;;;6EAIuB;AAEpE,UAAMjB,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,iCAAA,CAAA;;AAKd,OAAI,mBAAmBT,SACrB,OAAM,SAAkBY,eAAa;AAEvC,OAAI,sBAAsBZ,SACxBnB,KAAIgC,mBAAmB,YAAA;AACrB,UAAM,SAAkBC,kBAAgB;KAC1C;AAGF,UAAO,CAAC9B,QAAWgB,SAAS;WACrBT,OAAO;AACd,UAAO,CACLA,iBAAiBd,UACbc,QACAd,QAAQe,oBAAoBV,OAAOW,OAAOC,MAAMH,MAAAA,CACrD;;;;;;;;;IAWL,MAAcF,mBACZR,KACAC,QACAC,MACqC;AACrC,MAAI;GACF,MAAMY,UAAU,KAAKhB,UAAUiB,mBAAa;IAC1C,MAAMC,WAAW,KAAKlB,UAAUmB,sBAAsBjB,IAAAA;IACtD,IAAIkB,SAAS,IAAIjB,OAAOW,QAAM;AAC9B,SAAKd,UAAUmB,sBAAsBD,SAAAA;AACrC,WAAOE;KACT;GAEA,IAAI,CAACgB,SAASd,UAAUC,eAAeP,SAAAA;AACvC,OAAIM,SAASE,SAAS,GAAG;AAEvB,SADgB,MAAME,QAAQC,WAAWL,SAAAA,EAC7BM,MAAMR,WAAWA,OAAOS,WAAW,WAAA,CAC7C,OAAM/B,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,iCAAA,CAAA;IAGd,MAAMC,SAASf,QAAQO,YAAAA;AACvBa,cAAUL,OAAO;AACjBT,eAAWS,OAAO;;AAGpB,OAAIT,SAASE,SAAS,GAAG;AACvBQ,YAAQpB,MACN,wBAAwBT,OAAOW,OAAOC,KAAK;;;;6EAIuB;AAEpE,UAAMjB,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,iCAAA,CAAA;;AAId,OAAI,OAAOM,QAAQC,WAAW,WAC5B,OAAMvC,QAAQe,oBACZV,OAAOW,OAAOC,sBACd,IAAIe,MAAM,+CAAA,CAAA;AAKd,UAAO,CAACzB,QADS,MAAM+B,QAAQC,OAAOnC,KAAKE,KAAAA,CACf;WACrBQ,OAAO;AACd,UAAO,CACLA,iBAAiBd,UACbc,QACAd,QAAQe,oBAAoBV,OAAOW,OAAOC,MAAMH,MAAAA,CACrD"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { InstanceHolder } from "../holder/instance-holder.mjs";
|
|
2
|
+
import { IHolderStorage } from "../holder/holder-storage.interface.mjs";
|
|
3
|
+
import { LifecycleEventBus } from "../lifecycle/lifecycle-event-bus.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/internal/core/service-invalidator.d.mts
|
|
6
|
+
interface ClearAllOptions {
|
|
7
|
+
/** Whether to wait for all services to settle before starting (default: true) */
|
|
8
|
+
waitForSettlement?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface InvalidationOptions {
|
|
11
|
+
/** Whether to emit events after invalidation (default: true) */
|
|
12
|
+
emitEvents?: boolean;
|
|
13
|
+
/** Custom event emitter function */
|
|
14
|
+
onInvalidated?: (instanceName: string) => Promise<void>;
|
|
15
|
+
/** Whether to cascade invalidation to dependents (default: false - events handle it) */
|
|
16
|
+
cascade?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Manages graceful service cleanup with event-based invalidation.
|
|
20
|
+
*
|
|
21
|
+
* Uses event subscriptions instead of manual dependent finding.
|
|
22
|
+
* When a service is created, it subscribes to destroy events of its dependencies.
|
|
23
|
+
* When a dependency is destroyed, the event automatically invalidates dependents.
|
|
24
|
+
*/
|
|
25
|
+
declare class ServiceInvalidator {
|
|
26
|
+
private readonly eventBus;
|
|
27
|
+
private readonly logger;
|
|
28
|
+
constructor(eventBus: LifecycleEventBus | null, logger?: Console | null);
|
|
29
|
+
/**
|
|
30
|
+
* Invalidates a service using a specific storage.
|
|
31
|
+
* Event-based invalidation means dependents are automatically invalidated
|
|
32
|
+
* via destroy event subscriptions - no need to manually find dependents.
|
|
33
|
+
*
|
|
34
|
+
* @param service The instance name to invalidate
|
|
35
|
+
* @param storage The storage to use for this invalidation
|
|
36
|
+
* @param options Additional options for invalidation behavior
|
|
37
|
+
*/
|
|
38
|
+
invalidateWithStorage(service: string, storage: IHolderStorage, options?: InvalidationOptions): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Sets up destroy event subscriptions for a service's dependencies.
|
|
41
|
+
* Called when a service is successfully instantiated.
|
|
42
|
+
*
|
|
43
|
+
* @param serviceName The name of the service
|
|
44
|
+
* @param dependencies The set of dependency names
|
|
45
|
+
* @param storage The storage to use for invalidation
|
|
46
|
+
* @param holder The holder for the service (to add unsubscribe to destroy listeners)
|
|
47
|
+
*/
|
|
48
|
+
setupDependencySubscriptions(serviceName: string, dependencies: Set<string>, storage: IHolderStorage, holder: InstanceHolder): void;
|
|
49
|
+
/**
|
|
50
|
+
* Gracefully clears all services in a specific storage.
|
|
51
|
+
* This allows clearing request-scoped services using a RequestStorage.
|
|
52
|
+
*/
|
|
53
|
+
clearAllWithStorage(storage: IHolderStorage, options?: ClearAllOptions): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Waits for all services in a specific storage to settle.
|
|
56
|
+
*/
|
|
57
|
+
readyWithStorage(storage: IHolderStorage): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Invalidates a single holder using a specific storage.
|
|
60
|
+
*/
|
|
61
|
+
private invalidateHolderWithStorage;
|
|
62
|
+
/**
|
|
63
|
+
* Common invalidation logic for holders based on their status.
|
|
64
|
+
*/
|
|
65
|
+
private invalidateHolderByStatus;
|
|
66
|
+
/**
|
|
67
|
+
* Destroys a holder using a specific storage.
|
|
68
|
+
*/
|
|
69
|
+
private destroyHolderWithStorage;
|
|
70
|
+
/**
|
|
71
|
+
* Waits for a holder to settle (either created, destroyed, or error state).
|
|
72
|
+
*/
|
|
73
|
+
private waitForHolderToSettle;
|
|
74
|
+
/**
|
|
75
|
+
* Emits events to listeners for instance lifecycle events.
|
|
76
|
+
*/
|
|
77
|
+
private emitInstanceEvent;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { ClearAllOptions, InvalidationOptions, ServiceInvalidator };
|
|
81
|
+
//# sourceMappingURL=service-invalidator.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-invalidator.d.mts","names":[],"sources":["../../../../src/internal/core/service-invalidator.mts"],"sourcesContent":[],"mappings":";;;;;UAMiB,eAAA;;EAAA,iBAAA,CAAe,EAAA,OAAA;AAKhC;AAgBa,UAhBI,mBAAA,CAgBc;EAEA;EACF,UAAA,CAAA,EAAA,OAAA;EAchB;EACA,aAAA,CAAA,EAAA,CAAA,YAAA,EAAA,MAAA,EAAA,GA9B+B,OA8B/B,CAAA,IAAA,CAAA;EACR;EAmCa,OAAA,CAAA,EAAA,OAAA;;;;;;;;;cAtDL,kBAAA;;;wBAEkB,mCACF;;;;;;;;;;kDAchB,0BACA,sBACR;;;;;;;;;;kEAmCa,sBACL,wBACD;;;;;+BAgCC,0BACA,kBACR;;;;4BAuC6B,iBAAiB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { InstanceStatus } from "../holder/instance-holder.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/core/service-invalidator.mts
|
|
4
|
+
/**
|
|
5
|
+
* Manages graceful service cleanup with event-based invalidation.
|
|
6
|
+
*
|
|
7
|
+
* Uses event subscriptions instead of manual dependent finding.
|
|
8
|
+
* When a service is created, it subscribes to destroy events of its dependencies.
|
|
9
|
+
* When a dependency is destroyed, the event automatically invalidates dependents.
|
|
10
|
+
*/ var ServiceInvalidator = class {
|
|
11
|
+
eventBus;
|
|
12
|
+
logger;
|
|
13
|
+
constructor(eventBus, logger = null) {
|
|
14
|
+
this.eventBus = eventBus;
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Invalidates a service using a specific storage.
|
|
19
|
+
* Event-based invalidation means dependents are automatically invalidated
|
|
20
|
+
* via destroy event subscriptions - no need to manually find dependents.
|
|
21
|
+
*
|
|
22
|
+
* @param service The instance name to invalidate
|
|
23
|
+
* @param storage The storage to use for this invalidation
|
|
24
|
+
* @param options Additional options for invalidation behavior
|
|
25
|
+
*/ async invalidateWithStorage(service, storage, options = {}) {
|
|
26
|
+
const { emitEvents = true, onInvalidated } = options;
|
|
27
|
+
this.logger?.log(`[ServiceInvalidator] Starting invalidation process for ${service}`);
|
|
28
|
+
const result = storage.get(service);
|
|
29
|
+
if (result === null) return;
|
|
30
|
+
const [, holder] = result;
|
|
31
|
+
if (holder) await this.invalidateHolderWithStorage(service, holder, storage, emitEvents, onInvalidated);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Sets up destroy event subscriptions for a service's dependencies.
|
|
35
|
+
* Called when a service is successfully instantiated.
|
|
36
|
+
*
|
|
37
|
+
* @param serviceName The name of the service
|
|
38
|
+
* @param dependencies The set of dependency names
|
|
39
|
+
* @param storage The storage to use for invalidation
|
|
40
|
+
* @param holder The holder for the service (to add unsubscribe to destroy listeners)
|
|
41
|
+
*/ setupDependencySubscriptions(serviceName, dependencies, storage, holder) {
|
|
42
|
+
if (!this.eventBus) return;
|
|
43
|
+
for (const dependencyName of dependencies) {
|
|
44
|
+
const unsubscribe = this.eventBus.on(dependencyName, "destroy", () => {
|
|
45
|
+
this.logger?.log(`[ServiceInvalidator] Dependency ${dependencyName} destroyed, invalidating ${serviceName}`);
|
|
46
|
+
this.invalidateWithStorage(serviceName, storage).catch((error) => {
|
|
47
|
+
this.logger?.error(`[ServiceInvalidator] Error invalidating ${serviceName} after dependency ${dependencyName} destroyed:`, error);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
holder.destroyListeners.push(unsubscribe);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Gracefully clears all services in a specific storage.
|
|
55
|
+
* This allows clearing request-scoped services using a RequestStorage.
|
|
56
|
+
*/ async clearAllWithStorage(storage, options = {}) {
|
|
57
|
+
const { waitForSettlement = true } = options;
|
|
58
|
+
this.logger?.log("[ServiceInvalidator] Starting graceful clearing of all services");
|
|
59
|
+
if (waitForSettlement) {
|
|
60
|
+
this.logger?.log("[ServiceInvalidator] Waiting for all services to settle...");
|
|
61
|
+
await this.readyWithStorage(storage);
|
|
62
|
+
}
|
|
63
|
+
const allServiceNames = storage.getAllNames();
|
|
64
|
+
if (allServiceNames.length === 0) this.logger?.log("[ServiceInvalidator] No services to clear");
|
|
65
|
+
else {
|
|
66
|
+
this.logger?.log(`[ServiceInvalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(", ")}`);
|
|
67
|
+
const clearPromises = allServiceNames.map((serviceName) => this.invalidateWithStorage(serviceName, storage));
|
|
68
|
+
await Promise.all(clearPromises);
|
|
69
|
+
}
|
|
70
|
+
this.logger?.log("[ServiceInvalidator] Graceful clearing completed");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Waits for all services in a specific storage to settle.
|
|
74
|
+
*/ async readyWithStorage(storage) {
|
|
75
|
+
const holders = [];
|
|
76
|
+
storage.forEach((_, holder) => holders.push(holder));
|
|
77
|
+
await Promise.all(holders.map((holder) => this.waitForHolderToSettle(holder)));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Invalidates a single holder using a specific storage.
|
|
81
|
+
*/ async invalidateHolderWithStorage(key, holder, storage, emitEvents, onInvalidated) {
|
|
82
|
+
await this.invalidateHolderByStatus(holder, {
|
|
83
|
+
context: key,
|
|
84
|
+
onDestroy: () => this.destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated)
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Common invalidation logic for holders based on their status.
|
|
89
|
+
*/ async invalidateHolderByStatus(holder, options) {
|
|
90
|
+
switch (holder.status) {
|
|
91
|
+
case InstanceStatus.Destroying:
|
|
92
|
+
await holder.destroyPromise;
|
|
93
|
+
break;
|
|
94
|
+
case InstanceStatus.Creating:
|
|
95
|
+
await holder.creationPromise;
|
|
96
|
+
await options.onDestroy();
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
await options.onDestroy();
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Destroys a holder using a specific storage.
|
|
105
|
+
*/ async destroyHolderWithStorage(key, holder, storage, emitEvents, onInvalidated) {
|
|
106
|
+
holder.status = InstanceStatus.Destroying;
|
|
107
|
+
this.logger?.log(`[ServiceInvalidator] Invalidating ${key} and notifying listeners`);
|
|
108
|
+
holder.destroyPromise = Promise.all(holder.destroyListeners.map((listener) => listener())).then(async () => {
|
|
109
|
+
holder.destroyListeners = [];
|
|
110
|
+
holder.deps.clear();
|
|
111
|
+
storage.delete(key);
|
|
112
|
+
if (emitEvents && this.eventBus) await this.emitInstanceEvent(key, "destroy");
|
|
113
|
+
if (onInvalidated) await onInvalidated(key);
|
|
114
|
+
});
|
|
115
|
+
await holder.destroyPromise;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Waits for a holder to settle (either created, destroyed, or error state).
|
|
119
|
+
*/ async waitForHolderToSettle(holder) {
|
|
120
|
+
switch (holder.status) {
|
|
121
|
+
case InstanceStatus.Creating:
|
|
122
|
+
await holder.creationPromise;
|
|
123
|
+
break;
|
|
124
|
+
case InstanceStatus.Destroying:
|
|
125
|
+
await holder.destroyPromise;
|
|
126
|
+
break;
|
|
127
|
+
case InstanceStatus.Created:
|
|
128
|
+
case InstanceStatus.Error: break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Emits events to listeners for instance lifecycle events.
|
|
133
|
+
*/ emitInstanceEvent(name, event = "create") {
|
|
134
|
+
if (!this.eventBus) return Promise.resolve();
|
|
135
|
+
this.logger?.log(`[ServiceInvalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`);
|
|
136
|
+
return this.eventBus.emit(name, event);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
//#endregion
|
|
141
|
+
export { ServiceInvalidator };
|
|
142
|
+
//# sourceMappingURL=service-invalidator.mjs.map
|