@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,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
|
+
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
3
|
-
|
|
4
|
-
import type { FactoryContext } from '../context/factory-context.mjs'
|
|
5
1
|
import type {
|
|
6
2
|
AnyInjectableType,
|
|
7
3
|
InjectionTokenType,
|
|
8
4
|
} from '../../token/injection-token.mjs'
|
|
9
|
-
import type { IContainer } from '../../interfaces/container.interface.mjs'
|
|
10
5
|
|
|
11
6
|
import { DIError } from '../../errors/index.mjs'
|
|
12
7
|
import {
|
|
@@ -17,12 +12,12 @@ import {
|
|
|
17
12
|
import { getInjectableToken } from '../../utils/index.mjs'
|
|
18
13
|
|
|
19
14
|
/**
|
|
20
|
-
* Handles token validation
|
|
15
|
+
* Handles token validation and resolution.
|
|
21
16
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* Focuses on token validation, normalization, and argument validation.
|
|
18
|
+
* Name generation is handled by NameResolver.
|
|
24
19
|
*/
|
|
25
|
-
export class
|
|
20
|
+
export class TokenResolver {
|
|
26
21
|
constructor(private readonly logger: Console | null = null) {}
|
|
27
22
|
|
|
28
23
|
// ============================================================================
|
|
@@ -78,6 +73,10 @@ export class TokenProcessor {
|
|
|
78
73
|
|
|
79
74
|
/**
|
|
80
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 }]
|
|
81
80
|
*/
|
|
82
81
|
validateAndResolveTokenArgs(
|
|
83
82
|
token: AnyInjectableType,
|
|
@@ -106,88 +105,18 @@ export class TokenProcessor {
|
|
|
106
105
|
const validatedArgs = actualToken.schema?.safeParse(realArgs)
|
|
107
106
|
if (validatedArgs && !validatedArgs.success) {
|
|
108
107
|
this.logger?.error(
|
|
109
|
-
`[
|
|
108
|
+
`[TokenResolver]#validateAndResolveTokenArgs(): Error validating args for ${actualToken.name.toString()}`,
|
|
110
109
|
validatedArgs.error,
|
|
111
110
|
)
|
|
112
|
-
return [
|
|
111
|
+
return [
|
|
112
|
+
DIError.tokenValidationError(
|
|
113
|
+
`Validation failed for ${actualToken.name.toString()}`,
|
|
114
|
+
actualToken.schema,
|
|
115
|
+
realArgs,
|
|
116
|
+
),
|
|
117
|
+
{ actualToken },
|
|
118
|
+
]
|
|
113
119
|
}
|
|
114
120
|
return [undefined, { actualToken, validatedArgs: validatedArgs?.data }]
|
|
115
121
|
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Generates a unique instance name based on token and arguments.
|
|
119
|
-
*/
|
|
120
|
-
generateInstanceName(token: InjectionTokenType, args: any): string {
|
|
121
|
-
if (!args) {
|
|
122
|
-
return token.toString()
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const formattedArgs = Object.entries(args)
|
|
126
|
-
.sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
|
|
127
|
-
.map(([key, value]) => `${key}=${this.formatArgValue(value)}`)
|
|
128
|
-
.join(',')
|
|
129
|
-
|
|
130
|
-
return `${token.toString()}:${formattedArgs.replaceAll(/"/g, '').replaceAll(/:/g, '=')}`
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Formats a single argument value for instance name generation.
|
|
135
|
-
*/
|
|
136
|
-
formatArgValue(value: any): string {
|
|
137
|
-
if (typeof value === 'function') {
|
|
138
|
-
return `fn_${value.name}(${value.length})`
|
|
139
|
-
}
|
|
140
|
-
if (typeof value === 'symbol') {
|
|
141
|
-
return value.toString()
|
|
142
|
-
}
|
|
143
|
-
return JSON.stringify(value).slice(0, 40)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Creates a factory context for dependency injection during service instantiation.
|
|
148
|
-
* @param container The container instance (Container or ScopedContainer) for dependency resolution
|
|
149
|
-
* @param onDependencyResolved Callback when a dependency is resolved, receives the instance name
|
|
150
|
-
*/
|
|
151
|
-
createFactoryContext(
|
|
152
|
-
container: IContainer,
|
|
153
|
-
onDependencyResolved?: (instanceName: string) => void,
|
|
154
|
-
): FactoryContext & {
|
|
155
|
-
getDestroyListeners: () => (() => void)[]
|
|
156
|
-
deps: Set<string>
|
|
157
|
-
} {
|
|
158
|
-
const destroyListeners = new Set<() => void>()
|
|
159
|
-
const deps = new Set<string>()
|
|
160
|
-
|
|
161
|
-
function addDestroyListener(listener: () => void) {
|
|
162
|
-
destroyListeners.add(listener)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function getDestroyListeners() {
|
|
166
|
-
return Array.from(destroyListeners)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const self = this
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
// @ts-expect-error This is correct type
|
|
173
|
-
async inject(token, args) {
|
|
174
|
-
// Get the instance name for dependency tracking
|
|
175
|
-
const actualToken =
|
|
176
|
-
typeof token === 'function' ? getInjectableToken(token) : token
|
|
177
|
-
const instanceName = self.generateInstanceName(actualToken, args)
|
|
178
|
-
deps.add(instanceName)
|
|
179
|
-
|
|
180
|
-
if (onDependencyResolved) {
|
|
181
|
-
onDependencyResolved(instanceName)
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Use the container's get method for resolution
|
|
185
|
-
return container.get(token, args)
|
|
186
|
-
},
|
|
187
|
-
addDestroyListener,
|
|
188
|
-
getDestroyListeners,
|
|
189
|
-
container,
|
|
190
|
-
deps,
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
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
|
+
|