@navios/di 0.8.0 → 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 +87 -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 -1558
- package/lib/browser/index.mjs +29 -2749
- 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-DAKOvAgr.mjs → container-8-z89TyQ.mjs} +1325 -1462
- package/lib/container-8-z89TyQ.mjs.map +1 -0
- package/lib/{container-Bp1W-pWJ.d.mts → container-CNiqesCL.d.mts} +598 -617
- package/lib/container-CNiqesCL.d.mts.map +1 -0
- package/lib/{container-DENMeJ87.cjs → container-CaY2fDuk.cjs} +1369 -1512
- package/lib/container-CaY2fDuk.cjs.map +1 -0
- package/lib/{container-YPwvmlK2.d.cts → container-D-0Ho3qL.d.cts} +598 -612
- 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 +460 -302
- 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-resolver.mts +122 -0
- 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-Bp1W-pWJ.d.mts.map +0 -1
- package/lib/container-DAKOvAgr.mjs.map +0 -1
- package/lib/container-DENMeJ87.cjs.map +0 -1
- package/lib/container-YPwvmlK2.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 -225
- package/src/internal/core/invalidator.mts +0 -437
- package/src/internal/core/service-locator.mts +0 -202
- package/src/internal/core/token-processor.mts +0 -252
- package/src/internal/holder/base-holder-manager.mts +0 -334
- package/src/internal/holder/holder-manager.mts +0 -85
- package/src/internal/holder/request-storage.mts +0 -127
- package/src/internal/holder/singleton-storage.mts +0 -92
- package/src/testing/README.md +0 -80
- package/src/testing/__tests__/test-container.spec.mts +0 -173
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import type { IHolderStorage } from '../holder/holder-storage.interface.mjs'
|
|
2
|
+
import type { InstanceHolder } from '../holder/instance-holder.mjs'
|
|
3
|
+
import type { LifecycleEventBus } from '../lifecycle/lifecycle-event-bus.mjs'
|
|
4
|
+
|
|
5
|
+
import { InstanceStatus } from '../holder/instance-holder.mjs'
|
|
6
|
+
|
|
7
|
+
export interface ClearAllOptions {
|
|
8
|
+
/** Whether to wait for all services to settle before starting (default: true) */
|
|
9
|
+
waitForSettlement?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface InvalidationOptions {
|
|
13
|
+
/** Whether to emit events after invalidation (default: true) */
|
|
14
|
+
emitEvents?: boolean
|
|
15
|
+
/** Custom event emitter function */
|
|
16
|
+
onInvalidated?: (instanceName: string) => Promise<void>
|
|
17
|
+
/** Whether to cascade invalidation to dependents (default: false - events handle it) */
|
|
18
|
+
cascade?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Manages graceful service cleanup with event-based invalidation.
|
|
23
|
+
*
|
|
24
|
+
* Uses event subscriptions instead of manual dependent finding.
|
|
25
|
+
* When a service is created, it subscribes to destroy events of its dependencies.
|
|
26
|
+
* When a dependency is destroyed, the event automatically invalidates dependents.
|
|
27
|
+
*/
|
|
28
|
+
export class ServiceInvalidator {
|
|
29
|
+
constructor(
|
|
30
|
+
private readonly eventBus: LifecycleEventBus | null,
|
|
31
|
+
private readonly logger: Console | null = null,
|
|
32
|
+
) {}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Invalidates a service using a specific storage.
|
|
36
|
+
* Event-based invalidation means dependents are automatically invalidated
|
|
37
|
+
* via destroy event subscriptions - no need to manually find dependents.
|
|
38
|
+
*
|
|
39
|
+
* @param service The instance name to invalidate
|
|
40
|
+
* @param storage The storage to use for this invalidation
|
|
41
|
+
* @param options Additional options for invalidation behavior
|
|
42
|
+
*/
|
|
43
|
+
async invalidateWithStorage(
|
|
44
|
+
service: string,
|
|
45
|
+
storage: IHolderStorage,
|
|
46
|
+
options: InvalidationOptions = {},
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
const { emitEvents = true, onInvalidated } = options
|
|
49
|
+
|
|
50
|
+
this.logger?.log(
|
|
51
|
+
`[ServiceInvalidator] Starting invalidation process for ${service}`,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const result = storage.get(service)
|
|
55
|
+
if (result === null) {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const [, holder] = result
|
|
60
|
+
if (holder) {
|
|
61
|
+
await this.invalidateHolderWithStorage(
|
|
62
|
+
service,
|
|
63
|
+
holder,
|
|
64
|
+
storage,
|
|
65
|
+
emitEvents,
|
|
66
|
+
onInvalidated,
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Sets up destroy event subscriptions for a service's dependencies.
|
|
73
|
+
* Called when a service is successfully instantiated.
|
|
74
|
+
*
|
|
75
|
+
* @param serviceName The name of the service
|
|
76
|
+
* @param dependencies The set of dependency names
|
|
77
|
+
* @param storage The storage to use for invalidation
|
|
78
|
+
* @param holder The holder for the service (to add unsubscribe to destroy listeners)
|
|
79
|
+
*/
|
|
80
|
+
setupDependencySubscriptions(
|
|
81
|
+
serviceName: string,
|
|
82
|
+
dependencies: Set<string>,
|
|
83
|
+
storage: IHolderStorage,
|
|
84
|
+
holder: InstanceHolder,
|
|
85
|
+
): void {
|
|
86
|
+
if (!this.eventBus) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const dependencyName of dependencies) {
|
|
91
|
+
// Subscribe to the dependency's destroy event
|
|
92
|
+
const unsubscribe = this.eventBus.on(dependencyName, 'destroy', () => {
|
|
93
|
+
this.logger?.log(
|
|
94
|
+
`[ServiceInvalidator] Dependency ${dependencyName} destroyed, invalidating ${serviceName}`,
|
|
95
|
+
)
|
|
96
|
+
// Automatically invalidate this service when dependency is destroyed
|
|
97
|
+
this.invalidateWithStorage(serviceName, storage).catch((error) => {
|
|
98
|
+
this.logger?.error(
|
|
99
|
+
`[ServiceInvalidator] Error invalidating ${serviceName} after dependency ${dependencyName} destroyed:`,
|
|
100
|
+
error,
|
|
101
|
+
)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Store unsubscribe function in the service's destroy listeners
|
|
106
|
+
// so it's cleaned up when the service is destroyed
|
|
107
|
+
holder.destroyListeners.push(unsubscribe)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Gracefully clears all services in a specific storage.
|
|
113
|
+
* This allows clearing request-scoped services using a RequestStorage.
|
|
114
|
+
*/
|
|
115
|
+
async clearAllWithStorage(
|
|
116
|
+
storage: IHolderStorage,
|
|
117
|
+
options: ClearAllOptions = {},
|
|
118
|
+
): Promise<void> {
|
|
119
|
+
const { waitForSettlement = true } = options
|
|
120
|
+
|
|
121
|
+
this.logger?.log(
|
|
122
|
+
'[ServiceInvalidator] Starting graceful clearing of all services',
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
// Wait for all services to settle if requested
|
|
126
|
+
if (waitForSettlement) {
|
|
127
|
+
this.logger?.log(
|
|
128
|
+
'[ServiceInvalidator] Waiting for all services to settle...',
|
|
129
|
+
)
|
|
130
|
+
await this.readyWithStorage(storage)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Get all service names that need to be cleared
|
|
134
|
+
const allServiceNames = storage.getAllNames()
|
|
135
|
+
|
|
136
|
+
if (allServiceNames.length === 0) {
|
|
137
|
+
this.logger?.log('[ServiceInvalidator] No services to clear')
|
|
138
|
+
} else {
|
|
139
|
+
this.logger?.log(
|
|
140
|
+
`[ServiceInvalidator] Found ${allServiceNames.length} services to clear: ${allServiceNames.join(', ')}`,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
// Clear services - events will handle dependent invalidation
|
|
144
|
+
const clearPromises = allServiceNames.map((serviceName) =>
|
|
145
|
+
this.invalidateWithStorage(serviceName, storage),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
await Promise.all(clearPromises)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.logger?.log('[ServiceInvalidator] Graceful clearing completed')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Waits for all services in a specific storage to settle.
|
|
156
|
+
*/
|
|
157
|
+
async readyWithStorage(storage: IHolderStorage): Promise<void> {
|
|
158
|
+
const holders: InstanceHolder<any>[] = []
|
|
159
|
+
storage.forEach((_: string, holder: InstanceHolder) => holders.push(holder))
|
|
160
|
+
await Promise.all(
|
|
161
|
+
holders.map((holder) => this.waitForHolderToSettle(holder)),
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ============================================================================
|
|
166
|
+
// INTERNAL INVALIDATION HELPERS
|
|
167
|
+
// ============================================================================
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Invalidates a single holder using a specific storage.
|
|
171
|
+
*/
|
|
172
|
+
private async invalidateHolderWithStorage(
|
|
173
|
+
key: string,
|
|
174
|
+
holder: InstanceHolder<any>,
|
|
175
|
+
storage: IHolderStorage,
|
|
176
|
+
emitEvents: boolean,
|
|
177
|
+
onInvalidated?: (instanceName: string) => Promise<void>,
|
|
178
|
+
): Promise<void> {
|
|
179
|
+
await this.invalidateHolderByStatus(holder, {
|
|
180
|
+
context: key,
|
|
181
|
+
onDestroy: () =>
|
|
182
|
+
this.destroyHolderWithStorage(
|
|
183
|
+
key,
|
|
184
|
+
holder,
|
|
185
|
+
storage,
|
|
186
|
+
emitEvents,
|
|
187
|
+
onInvalidated,
|
|
188
|
+
),
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Common invalidation logic for holders based on their status.
|
|
194
|
+
*/
|
|
195
|
+
private async invalidateHolderByStatus(
|
|
196
|
+
holder: InstanceHolder<any>,
|
|
197
|
+
options: {
|
|
198
|
+
context: string
|
|
199
|
+
onDestroy: () => Promise<void>
|
|
200
|
+
},
|
|
201
|
+
): Promise<void> {
|
|
202
|
+
switch (holder.status) {
|
|
203
|
+
case InstanceStatus.Destroying:
|
|
204
|
+
await holder.destroyPromise
|
|
205
|
+
break
|
|
206
|
+
|
|
207
|
+
case InstanceStatus.Creating:
|
|
208
|
+
// Wait for creation to complete before destroying
|
|
209
|
+
await holder.creationPromise
|
|
210
|
+
await options.onDestroy()
|
|
211
|
+
break
|
|
212
|
+
|
|
213
|
+
default:
|
|
214
|
+
await options.onDestroy()
|
|
215
|
+
break
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Destroys a holder using a specific storage.
|
|
221
|
+
*/
|
|
222
|
+
private async destroyHolderWithStorage(
|
|
223
|
+
key: string,
|
|
224
|
+
holder: InstanceHolder<any>,
|
|
225
|
+
storage: IHolderStorage,
|
|
226
|
+
emitEvents: boolean,
|
|
227
|
+
onInvalidated?: (instanceName: string) => Promise<void>,
|
|
228
|
+
): Promise<void> {
|
|
229
|
+
holder.status = InstanceStatus.Destroying
|
|
230
|
+
this.logger?.log(
|
|
231
|
+
`[ServiceInvalidator] Invalidating ${key} and notifying listeners`,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
holder.destroyPromise = Promise.all(
|
|
235
|
+
holder.destroyListeners.map((listener) => listener()),
|
|
236
|
+
).then(async () => {
|
|
237
|
+
holder.destroyListeners = []
|
|
238
|
+
holder.deps.clear()
|
|
239
|
+
storage.delete(key)
|
|
240
|
+
|
|
241
|
+
// Emit events if enabled and event bus exists
|
|
242
|
+
if (emitEvents && this.eventBus) {
|
|
243
|
+
await this.emitInstanceEvent(key, 'destroy')
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Call custom callback if provided
|
|
247
|
+
if (onInvalidated) {
|
|
248
|
+
await onInvalidated(key)
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
await holder.destroyPromise
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Waits for a holder to settle (either created, destroyed, or error state).
|
|
257
|
+
*/
|
|
258
|
+
private async waitForHolderToSettle(
|
|
259
|
+
holder: InstanceHolder<any>,
|
|
260
|
+
): Promise<void> {
|
|
261
|
+
switch (holder.status) {
|
|
262
|
+
case InstanceStatus.Creating:
|
|
263
|
+
await holder.creationPromise
|
|
264
|
+
break
|
|
265
|
+
case InstanceStatus.Destroying:
|
|
266
|
+
await holder.destroyPromise
|
|
267
|
+
break
|
|
268
|
+
// Already settled states
|
|
269
|
+
case InstanceStatus.Created:
|
|
270
|
+
case InstanceStatus.Error:
|
|
271
|
+
break
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Emits events to listeners for instance lifecycle events.
|
|
277
|
+
*/
|
|
278
|
+
private emitInstanceEvent(
|
|
279
|
+
name: string,
|
|
280
|
+
event: 'create' | 'destroy' = 'create',
|
|
281
|
+
) {
|
|
282
|
+
if (!this.eventBus) {
|
|
283
|
+
return Promise.resolve()
|
|
284
|
+
}
|
|
285
|
+
this.logger?.log(
|
|
286
|
+
`[ServiceInvalidator]#emitInstanceEvent() Notifying listeners for ${name} with event ${event}`,
|
|
287
|
+
)
|
|
288
|
+
return this.eventBus.emit(name, event)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnyInjectableType,
|
|
3
|
+
InjectionTokenType,
|
|
4
|
+
} from '../../token/injection-token.mjs'
|
|
5
|
+
|
|
6
|
+
import { DIError } from '../../errors/index.mjs'
|
|
7
|
+
import {
|
|
8
|
+
BoundInjectionToken,
|
|
9
|
+
FactoryInjectionToken,
|
|
10
|
+
InjectionToken,
|
|
11
|
+
} from '../../token/injection-token.mjs'
|
|
12
|
+
import { getInjectableToken } from '../../utils/index.mjs'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Handles token validation and resolution.
|
|
16
|
+
*
|
|
17
|
+
* Focuses on token validation, normalization, and argument validation.
|
|
18
|
+
* Name generation is handled by NameResolver.
|
|
19
|
+
*/
|
|
20
|
+
export class TokenResolver {
|
|
21
|
+
constructor(private readonly logger: Console | null = null) {}
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// TOKEN NORMALIZATION
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Normalizes a token to an InjectionToken.
|
|
29
|
+
* Handles class constructors by getting their injectable token.
|
|
30
|
+
*
|
|
31
|
+
* @param token A class constructor, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
|
|
32
|
+
* @returns The normalized InjectionTokenType
|
|
33
|
+
*/
|
|
34
|
+
normalizeToken(token: AnyInjectableType): InjectionTokenType {
|
|
35
|
+
if (typeof token === 'function') {
|
|
36
|
+
return getInjectableToken(token)
|
|
37
|
+
}
|
|
38
|
+
return token as InjectionTokenType
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Gets the underlying "real" token from wrapped tokens.
|
|
43
|
+
* For BoundInjectionToken and FactoryInjectionToken, returns the wrapped token.
|
|
44
|
+
* For other tokens, returns the token itself.
|
|
45
|
+
*
|
|
46
|
+
* @param token The token to unwrap
|
|
47
|
+
* @returns The underlying InjectionToken
|
|
48
|
+
*/
|
|
49
|
+
getRealToken<T = unknown>(token: InjectionTokenType): InjectionToken<T> {
|
|
50
|
+
if (
|
|
51
|
+
token instanceof BoundInjectionToken ||
|
|
52
|
+
token instanceof FactoryInjectionToken
|
|
53
|
+
) {
|
|
54
|
+
return token.token as InjectionToken<T>
|
|
55
|
+
}
|
|
56
|
+
return token as InjectionToken<T>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Convenience method that normalizes a token and then gets the real token.
|
|
61
|
+
* Useful for checking registry entries where you need the actual registered token.
|
|
62
|
+
*
|
|
63
|
+
* @param token Any injectable type
|
|
64
|
+
* @returns The underlying InjectionToken
|
|
65
|
+
*/
|
|
66
|
+
getRegistryToken<T = unknown>(token: AnyInjectableType): InjectionToken<T> {
|
|
67
|
+
return this.getRealToken(this.normalizeToken(token))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// TOKEN VALIDATION
|
|
72
|
+
// ============================================================================
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Validates and resolves token arguments, handling factory token resolution and validation.
|
|
76
|
+
*
|
|
77
|
+
* @param token The token to validate
|
|
78
|
+
* @param args Optional arguments
|
|
79
|
+
* @returns [error, { actualToken, validatedArgs }]
|
|
80
|
+
*/
|
|
81
|
+
validateAndResolveTokenArgs(
|
|
82
|
+
token: AnyInjectableType,
|
|
83
|
+
args?: any,
|
|
84
|
+
): [
|
|
85
|
+
DIError | undefined,
|
|
86
|
+
{ actualToken: InjectionTokenType; validatedArgs?: any },
|
|
87
|
+
] {
|
|
88
|
+
let actualToken = token as InjectionToken<any, any>
|
|
89
|
+
if (typeof token === 'function') {
|
|
90
|
+
actualToken = getInjectableToken(token)
|
|
91
|
+
}
|
|
92
|
+
let realArgs = args
|
|
93
|
+
if (actualToken instanceof BoundInjectionToken) {
|
|
94
|
+
realArgs = actualToken.value
|
|
95
|
+
} else if (actualToken instanceof FactoryInjectionToken) {
|
|
96
|
+
if (actualToken.resolved) {
|
|
97
|
+
realArgs = actualToken.value
|
|
98
|
+
} else {
|
|
99
|
+
return [DIError.factoryTokenNotResolved(token.name), { actualToken }]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (!actualToken.schema) {
|
|
103
|
+
return [undefined, { actualToken, validatedArgs: realArgs }]
|
|
104
|
+
}
|
|
105
|
+
const validatedArgs = actualToken.schema?.safeParse(realArgs)
|
|
106
|
+
if (validatedArgs && !validatedArgs.success) {
|
|
107
|
+
this.logger?.error(
|
|
108
|
+
`[TokenResolver]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`,
|
|
109
|
+
validatedArgs.error,
|
|
110
|
+
)
|
|
111
|
+
return [
|
|
112
|
+
DIError.tokenValidationError(
|
|
113
|
+
`Validation failed for ${actualToken.name.toString()}`,
|
|
114
|
+
actualToken.schema,
|
|
115
|
+
realArgs,
|
|
116
|
+
),
|
|
117
|
+
{ actualToken },
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
return [undefined, { actualToken, validatedArgs: validatedArgs?.data }]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -17,8 +17,7 @@ export type HolderGetResult<T = unknown> =
|
|
|
17
17
|
* Interface for abstracting holder storage operations.
|
|
18
18
|
*
|
|
19
19
|
* Enables unified instance resolution logic regardless of where
|
|
20
|
-
* holders are stored
|
|
21
|
-
* This is the key abstraction for the Storage Strategy pattern.
|
|
20
|
+
* holders are stored. This is the key abstraction for the unified storage pattern.
|
|
22
21
|
*/
|
|
23
22
|
export interface IHolderStorage {
|
|
24
23
|
/**
|
|
@@ -94,9 +93,7 @@ export interface IHolderStorage {
|
|
|
94
93
|
*
|
|
95
94
|
* @param callback Function called for each holder with (name, holder)
|
|
96
95
|
*/
|
|
97
|
-
forEach(
|
|
98
|
-
callback: (name: string, holder: InstanceHolder) => void,
|
|
99
|
-
): void
|
|
96
|
+
forEach(callback: (name: string, holder: InstanceHolder) => void): void
|
|
100
97
|
|
|
101
98
|
/**
|
|
102
99
|
* Finds a holder by its instance value (reverse lookup).
|
|
@@ -113,4 +110,13 @@ export interface IHolderStorage {
|
|
|
113
110
|
* @returns Array of instance names that have this instance as a dependency
|
|
114
111
|
*/
|
|
115
112
|
findDependents(instanceName: string): string[]
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Updates dependency references when instance names change.
|
|
116
|
+
* Used during scope upgrades when instance names are regenerated with requestId.
|
|
117
|
+
*
|
|
118
|
+
* @param oldName The old instance name
|
|
119
|
+
* @param newName The new instance name
|
|
120
|
+
*/
|
|
121
|
+
updateDependencyReference(oldName: string, newName: string): void
|
|
116
122
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export * from './instance-holder.mjs'
|
|
2
|
-
export * from './base-holder-manager.mjs'
|
|
3
|
-
export * from './holder-manager.mjs'
|
|
4
1
|
export * from './holder-storage.interface.mjs'
|
|
5
|
-
export * from './
|
|
6
|
-
export * from './
|
|
2
|
+
export * from './instance-holder.mjs'
|
|
3
|
+
export * from './unified-storage.mjs'
|
|
@@ -14,9 +14,6 @@ export enum InstanceStatus {
|
|
|
14
14
|
Error = 'error',
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
/** Callback function for instance effects */
|
|
18
|
-
export type InstanceEffect = () => void
|
|
19
|
-
|
|
20
17
|
/** Callback function for instance destruction listeners */
|
|
21
18
|
export type InstanceDestroyListener = () => void | Promise<void>
|
|
22
19
|
|
|
@@ -107,3 +104,4 @@ export type InstanceHolder<Instance = unknown> =
|
|
|
107
104
|
| InstanceHolderCreated<Instance>
|
|
108
105
|
| InstanceHolderDestroying<Instance>
|
|
109
106
|
| InstanceHolderError
|
|
107
|
+
|