@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
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
3
|
-
import type { z, ZodObject, ZodOptional } from 'zod/v4'
|
|
4
|
-
|
|
5
2
|
import type { ScopedContainer } from '../../container/scoped-container.mjs'
|
|
6
3
|
import type { IContainer } from '../../interfaces/container.interface.mjs'
|
|
7
4
|
import type {
|
|
8
5
|
AnyInjectableType,
|
|
9
|
-
InjectionTokenSchemaType,
|
|
10
6
|
InjectionTokenType,
|
|
11
7
|
} from '../../token/injection-token.mjs'
|
|
12
8
|
import type { Registry } from '../../token/registry.mjs'
|
|
13
|
-
import type {
|
|
14
|
-
import type { HolderManager } from '../holder/holder-manager.mjs'
|
|
9
|
+
import type { ServiceInitializationContext } from '../context/service-initialization-context.mjs'
|
|
15
10
|
import type { IHolderStorage } from '../holder/holder-storage.interface.mjs'
|
|
16
11
|
import type { InstanceHolder } from '../holder/instance-holder.mjs'
|
|
17
|
-
import type {
|
|
18
|
-
import type { ServiceLocator } from './service-locator.mjs'
|
|
19
|
-
import type { TokenProcessor } from './token-processor.mjs'
|
|
12
|
+
import type { LifecycleEventBus } from '../lifecycle/lifecycle-event-bus.mjs'
|
|
20
13
|
|
|
21
14
|
import { InjectableScope } from '../../enums/index.mjs'
|
|
22
15
|
import { DIError, DIErrorCode } from '../../errors/index.mjs'
|
|
@@ -29,29 +22,33 @@ import {
|
|
|
29
22
|
getCurrentResolutionContext,
|
|
30
23
|
withResolutionContext,
|
|
31
24
|
} from '../context/resolution-context.mjs'
|
|
32
|
-
import { BaseHolderManager } from '../holder/base-holder-manager.mjs'
|
|
33
25
|
import { InstanceStatus } from '../holder/instance-holder.mjs'
|
|
34
|
-
import {
|
|
26
|
+
import { CircularDetector } from '../lifecycle/circular-detector.mjs'
|
|
27
|
+
import { NameResolver } from './name-resolver.mjs'
|
|
28
|
+
import { ScopeTracker } from './scope-tracker.mjs'
|
|
29
|
+
import { ServiceInitializer } from './service-initializer.mjs'
|
|
30
|
+
import { ServiceInvalidator } from './service-invalidator.mjs'
|
|
31
|
+
import { TokenResolver } from './token-resolver.mjs'
|
|
35
32
|
|
|
36
33
|
/**
|
|
37
34
|
* Resolves instances from tokens, handling caching, creation, and scope rules.
|
|
38
35
|
*
|
|
39
|
-
* Uses
|
|
40
|
-
* Coordinates with
|
|
36
|
+
* Uses unified storage for both singleton and request-scoped services.
|
|
37
|
+
* Coordinates with ServiceInitializer for actual service creation.
|
|
38
|
+
* Integrates ScopeTracker for automatic scope upgrades.
|
|
41
39
|
*/
|
|
42
40
|
export class InstanceResolver {
|
|
43
|
-
private readonly singletonStorage: IHolderStorage
|
|
44
|
-
|
|
45
41
|
constructor(
|
|
46
42
|
private readonly registry: Registry,
|
|
47
|
-
private readonly
|
|
48
|
-
private readonly
|
|
49
|
-
private readonly
|
|
43
|
+
private readonly storage: IHolderStorage,
|
|
44
|
+
private readonly serviceInitializer: ServiceInitializer,
|
|
45
|
+
private readonly tokenResolver: TokenResolver,
|
|
46
|
+
private readonly nameResolver: NameResolver,
|
|
47
|
+
private readonly scopeTracker: ScopeTracker,
|
|
48
|
+
private readonly serviceInvalidator: ServiceInvalidator,
|
|
49
|
+
private readonly eventBus: LifecycleEventBus,
|
|
50
50
|
private readonly logger: Console | null = null,
|
|
51
|
-
|
|
52
|
-
) {
|
|
53
|
-
this.singletonStorage = new SingletonStorage(manager)
|
|
54
|
-
}
|
|
51
|
+
) {}
|
|
55
52
|
|
|
56
53
|
// ============================================================================
|
|
57
54
|
// PUBLIC RESOLUTION METHODS
|
|
@@ -63,24 +60,31 @@ export class InstanceResolver {
|
|
|
63
60
|
*
|
|
64
61
|
* @param token The injection token
|
|
65
62
|
* @param args Optional arguments
|
|
66
|
-
* @param contextContainer The container to use for creating
|
|
63
|
+
* @param contextContainer The container to use for creating context
|
|
64
|
+
* @param requestStorage Optional request storage (for scope upgrades)
|
|
65
|
+
* @param requestId Optional request ID (for scope upgrades)
|
|
67
66
|
*/
|
|
68
67
|
async resolveInstance(
|
|
69
68
|
token: AnyInjectableType,
|
|
70
69
|
args: any,
|
|
71
70
|
contextContainer: IContainer,
|
|
71
|
+
requestStorage?: IHolderStorage,
|
|
72
|
+
requestId?: string,
|
|
72
73
|
): Promise<[undefined, any] | [DIError]> {
|
|
73
74
|
return this.resolveWithStorage(
|
|
74
75
|
token,
|
|
75
76
|
args,
|
|
76
77
|
contextContainer,
|
|
77
|
-
this.
|
|
78
|
+
this.storage,
|
|
79
|
+
undefined,
|
|
80
|
+
requestStorage,
|
|
81
|
+
requestId,
|
|
78
82
|
)
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
/**
|
|
82
86
|
* Resolves a request-scoped instance for a ScopedContainer.
|
|
83
|
-
* The service will be stored in the ScopedContainer's request
|
|
87
|
+
* The service will be stored in the ScopedContainer's request storage.
|
|
84
88
|
*
|
|
85
89
|
* @param token The injection token
|
|
86
90
|
* @param args Optional arguments
|
|
@@ -91,13 +95,14 @@ export class InstanceResolver {
|
|
|
91
95
|
args: any,
|
|
92
96
|
scopedContainer: ScopedContainer,
|
|
93
97
|
): Promise<[undefined, any] | [DIError]> {
|
|
94
|
-
// Use the cached storage from ScopedContainer
|
|
95
98
|
return this.resolveWithStorage(
|
|
96
99
|
token,
|
|
97
100
|
args,
|
|
101
|
+
scopedContainer.getParent(),
|
|
102
|
+
scopedContainer.getParent().getStorage(),
|
|
98
103
|
scopedContainer,
|
|
99
|
-
scopedContainer.
|
|
100
|
-
scopedContainer,
|
|
104
|
+
scopedContainer.getStorage(),
|
|
105
|
+
scopedContainer.getRequestId(),
|
|
101
106
|
)
|
|
102
107
|
}
|
|
103
108
|
|
|
@@ -114,9 +119,11 @@ export class InstanceResolver {
|
|
|
114
119
|
*
|
|
115
120
|
* @param token The injection token
|
|
116
121
|
* @param args Optional arguments
|
|
117
|
-
* @param contextContainer The container for
|
|
122
|
+
* @param contextContainer The container for context
|
|
118
123
|
* @param storage The storage strategy to use
|
|
119
124
|
* @param scopedContainer Optional scoped container for request-scoped services
|
|
125
|
+
* @param requestStorage Optional request storage (for scope upgrades)
|
|
126
|
+
* @param requestId Optional request ID (for scope upgrades)
|
|
120
127
|
*/
|
|
121
128
|
private async resolveWithStorage(
|
|
122
129
|
token: AnyInjectableType,
|
|
@@ -124,28 +131,56 @@ export class InstanceResolver {
|
|
|
124
131
|
contextContainer: IContainer,
|
|
125
132
|
storage: IHolderStorage,
|
|
126
133
|
scopedContainer?: ScopedContainer,
|
|
134
|
+
requestStorage?: IHolderStorage,
|
|
135
|
+
requestId?: string,
|
|
127
136
|
): Promise<[undefined, any] | [DIError]> {
|
|
128
137
|
// Step 1: Resolve token and prepare instance name
|
|
129
138
|
const [err, data] = await this.resolveTokenAndPrepareInstanceName(
|
|
130
139
|
token,
|
|
131
140
|
args,
|
|
132
141
|
contextContainer,
|
|
142
|
+
requestId,
|
|
143
|
+
scopedContainer,
|
|
133
144
|
)
|
|
134
145
|
if (err) {
|
|
135
146
|
return [err]
|
|
136
147
|
}
|
|
137
148
|
|
|
138
|
-
const { instanceName, validatedArgs, realToken } = data!
|
|
149
|
+
const { instanceName, validatedArgs, realToken, scope } = data!
|
|
139
150
|
|
|
140
151
|
// Step 2: Check for existing holder SYNCHRONOUSLY (no await between check and store)
|
|
141
152
|
// This is critical for preventing race conditions with concurrent resolution
|
|
142
|
-
const getResult =
|
|
153
|
+
const getResult =
|
|
154
|
+
storage.get(instanceName) ?? requestStorage?.get(instanceName) ?? null
|
|
155
|
+
|
|
156
|
+
// Create getHolder function for circular dependency detection
|
|
157
|
+
const getHolder = (name: string): InstanceHolder | undefined => {
|
|
158
|
+
// Check both storages
|
|
159
|
+
const result = storage.get(name)
|
|
160
|
+
if (result && result[0] === undefined && result[1]) {
|
|
161
|
+
return result[1]
|
|
162
|
+
}
|
|
163
|
+
if (requestStorage) {
|
|
164
|
+
const reqResult = requestStorage.get(name)
|
|
165
|
+
if (reqResult && reqResult[0] === undefined && reqResult[1]) {
|
|
166
|
+
return reqResult[1]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return undefined
|
|
170
|
+
}
|
|
143
171
|
|
|
144
172
|
if (getResult !== null) {
|
|
145
173
|
const [error, holder] = getResult
|
|
146
174
|
if (!error && holder) {
|
|
147
175
|
// Found existing holder - wait for it to be ready
|
|
148
|
-
|
|
176
|
+
// Try to get waiterHolder from resolution context if available
|
|
177
|
+
const resolutionCtx = getCurrentResolutionContext()
|
|
178
|
+
const waiterHolder = resolutionCtx?.waiterHolder
|
|
179
|
+
const readyResult = await this.waitForInstanceReady(
|
|
180
|
+
holder,
|
|
181
|
+
waiterHolder,
|
|
182
|
+
getHolder,
|
|
183
|
+
)
|
|
149
184
|
if (readyResult[0]) {
|
|
150
185
|
return [readyResult[0]]
|
|
151
186
|
}
|
|
@@ -174,6 +209,9 @@ export class InstanceResolver {
|
|
|
174
209
|
contextContainer,
|
|
175
210
|
storage,
|
|
176
211
|
scopedContainer,
|
|
212
|
+
requestStorage,
|
|
213
|
+
requestId,
|
|
214
|
+
scope,
|
|
177
215
|
)
|
|
178
216
|
if (createError) {
|
|
179
217
|
return [createError]
|
|
@@ -182,6 +220,81 @@ export class InstanceResolver {
|
|
|
182
220
|
return [undefined, holder!.instance]
|
|
183
221
|
}
|
|
184
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Internal method to resolve token args and create instance name.
|
|
225
|
+
* Handles factory token resolution and validation.
|
|
226
|
+
*/
|
|
227
|
+
private async resolveTokenAndPrepareInstanceName(
|
|
228
|
+
token: AnyInjectableType,
|
|
229
|
+
args: any,
|
|
230
|
+
contextContainer: IContainer,
|
|
231
|
+
requestId?: string,
|
|
232
|
+
scopedContainer?: ScopedContainer,
|
|
233
|
+
): Promise<
|
|
234
|
+
| [
|
|
235
|
+
undefined,
|
|
236
|
+
{
|
|
237
|
+
instanceName: string
|
|
238
|
+
validatedArgs: any
|
|
239
|
+
actualToken: InjectionTokenType
|
|
240
|
+
realToken: InjectionToken<any, any>
|
|
241
|
+
scope: InjectableScope
|
|
242
|
+
},
|
|
243
|
+
]
|
|
244
|
+
| [DIError]
|
|
245
|
+
> {
|
|
246
|
+
const [err, { actualToken, validatedArgs }] =
|
|
247
|
+
this.tokenResolver.validateAndResolveTokenArgs(token, args)
|
|
248
|
+
if (
|
|
249
|
+
err instanceof DIError &&
|
|
250
|
+
err.code === DIErrorCode.TokenValidationError
|
|
251
|
+
) {
|
|
252
|
+
return [err]
|
|
253
|
+
} else if (
|
|
254
|
+
err instanceof DIError &&
|
|
255
|
+
err.code === DIErrorCode.FactoryTokenNotResolved &&
|
|
256
|
+
actualToken instanceof FactoryInjectionToken
|
|
257
|
+
) {
|
|
258
|
+
this.logger?.log(
|
|
259
|
+
`[InstanceResolver]#resolveTokenAndPrepareInstanceName() Factory token not resolved, resolving it`,
|
|
260
|
+
)
|
|
261
|
+
// Create a simple factory context for resolving the factory token
|
|
262
|
+
const factoryCtx = {
|
|
263
|
+
inject: async (t: any, a?: any) =>
|
|
264
|
+
(scopedContainer ?? contextContainer).get(t, a),
|
|
265
|
+
container: scopedContainer ?? contextContainer,
|
|
266
|
+
addDestroyListener: () => {},
|
|
267
|
+
}
|
|
268
|
+
await actualToken.resolve(factoryCtx as any)
|
|
269
|
+
return this.resolveTokenAndPrepareInstanceName(
|
|
270
|
+
token,
|
|
271
|
+
undefined,
|
|
272
|
+
contextContainer,
|
|
273
|
+
requestId,
|
|
274
|
+
scopedContainer,
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Get the real token for registry lookup
|
|
279
|
+
const realToken = this.tokenResolver.getRealToken(actualToken)
|
|
280
|
+
// Get scope from registry
|
|
281
|
+
const record = this.registry.get(realToken)
|
|
282
|
+
const scope = record.scope
|
|
283
|
+
|
|
284
|
+
// Generate instance name with requestId if needed
|
|
285
|
+
const instanceName = this.nameResolver.generateInstanceName(
|
|
286
|
+
actualToken,
|
|
287
|
+
validatedArgs,
|
|
288
|
+
requestId,
|
|
289
|
+
scope,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
return [
|
|
293
|
+
undefined,
|
|
294
|
+
{ instanceName, validatedArgs, actualToken, realToken, scope },
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
|
|
185
298
|
/**
|
|
186
299
|
* Handles storage error states (destroying, error, etc.).
|
|
187
300
|
* Returns a result if handled, null if should proceed with creation.
|
|
@@ -204,7 +317,18 @@ export class InstanceResolver {
|
|
|
204
317
|
// Re-check after destruction
|
|
205
318
|
const newResult = storage.get(instanceName)
|
|
206
319
|
if (newResult !== null && !newResult[0]) {
|
|
207
|
-
|
|
320
|
+
// Create getHolder for circular dependency detection
|
|
321
|
+
const getHolder = (name: string): InstanceHolder | undefined => {
|
|
322
|
+
const result = storage.get(name)
|
|
323
|
+
return result && result[0] === undefined && result[1]
|
|
324
|
+
? result[1]
|
|
325
|
+
: undefined
|
|
326
|
+
}
|
|
327
|
+
const readyResult = await this.waitForInstanceReady(
|
|
328
|
+
newResult[1]!,
|
|
329
|
+
undefined,
|
|
330
|
+
getHolder,
|
|
331
|
+
)
|
|
208
332
|
if (readyResult[0]) {
|
|
209
333
|
return [readyResult[0]]
|
|
210
334
|
}
|
|
@@ -237,6 +361,9 @@ export class InstanceResolver {
|
|
|
237
361
|
contextContainer: IContainer,
|
|
238
362
|
storage: IHolderStorage,
|
|
239
363
|
scopedContainer?: ScopedContainer,
|
|
364
|
+
requestStorage?: IHolderStorage,
|
|
365
|
+
requestId?: string,
|
|
366
|
+
scope?: InjectableScope,
|
|
240
367
|
): Promise<[undefined, InstanceHolder<Instance>] | [DIError]> {
|
|
241
368
|
this.logger?.log(
|
|
242
369
|
`[InstanceResolver]#createAndStoreInstance() Creating instance for ${instanceName}`,
|
|
@@ -246,64 +373,117 @@ export class InstanceResolver {
|
|
|
246
373
|
return [DIError.factoryNotFound(realToken.name.toString())]
|
|
247
374
|
}
|
|
248
375
|
|
|
249
|
-
const ctx = this.createFactoryContext(contextContainer)
|
|
250
376
|
const record = this.registry.get<Instance, any>(realToken)
|
|
251
|
-
const {
|
|
377
|
+
const { type, scope: recordScope } = record
|
|
378
|
+
const serviceScope = scope || recordScope
|
|
252
379
|
|
|
253
380
|
// For transient services, don't use storage locking - create directly
|
|
254
|
-
if (
|
|
255
|
-
return this.createTransientInstance(
|
|
381
|
+
if (serviceScope === InjectableScope.Transient) {
|
|
382
|
+
return this.createTransientInstance(
|
|
383
|
+
instanceName,
|
|
384
|
+
record,
|
|
385
|
+
args,
|
|
386
|
+
contextContainer,
|
|
387
|
+
scopedContainer,
|
|
388
|
+
requestStorage,
|
|
389
|
+
requestId,
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
if (serviceScope === InjectableScope.Request && !requestStorage) {
|
|
393
|
+
return [
|
|
394
|
+
DIError.initializationError(
|
|
395
|
+
`Request storage is required for request-scoped services`,
|
|
396
|
+
instanceName,
|
|
397
|
+
),
|
|
398
|
+
]
|
|
256
399
|
}
|
|
257
400
|
|
|
258
|
-
|
|
259
|
-
|
|
401
|
+
let storageToUse: IHolderStorage
|
|
402
|
+
if (serviceScope === InjectableScope.Request) {
|
|
403
|
+
storageToUse = requestStorage!
|
|
404
|
+
} else {
|
|
405
|
+
storageToUse = storage
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Create holder in "Creating" state
|
|
409
|
+
const [deferred, holder] = storageToUse.createHolder<Instance>(
|
|
260
410
|
instanceName,
|
|
261
411
|
type,
|
|
262
|
-
|
|
263
|
-
ctx.deps,
|
|
412
|
+
new Set(),
|
|
264
413
|
)
|
|
265
|
-
|
|
266
414
|
// Store holder immediately (for lock mechanism)
|
|
267
|
-
|
|
415
|
+
storageToUse.set(instanceName, holder)
|
|
416
|
+
|
|
417
|
+
// Create context for service initialization
|
|
418
|
+
const ctx = this.createServiceInitializationContext(
|
|
419
|
+
scopedContainer ?? contextContainer,
|
|
420
|
+
instanceName,
|
|
421
|
+
serviceScope,
|
|
422
|
+
holder.deps,
|
|
423
|
+
realToken,
|
|
424
|
+
requestStorage,
|
|
425
|
+
requestId,
|
|
426
|
+
)
|
|
268
427
|
|
|
269
|
-
|
|
428
|
+
holder.destroyListeners = ctx.getDestroyListeners()
|
|
429
|
+
|
|
430
|
+
// Create getHolder function for resolution context
|
|
270
431
|
const getHolder = (name: string): InstanceHolder | undefined => {
|
|
271
|
-
//
|
|
272
|
-
const
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
|
|
432
|
+
// Check both storages
|
|
433
|
+
const result = storage.get(name)
|
|
434
|
+
if (result && result[0] === undefined && result[1]) {
|
|
435
|
+
return result[1]
|
|
436
|
+
}
|
|
437
|
+
if (requestStorage) {
|
|
438
|
+
const reqResult = requestStorage.get(name)
|
|
439
|
+
if (reqResult && reqResult[0] === undefined && reqResult[1]) {
|
|
440
|
+
return reqResult[1]
|
|
441
|
+
}
|
|
276
442
|
}
|
|
277
|
-
|
|
278
|
-
const [, managerHolder] = this.manager.get(name)
|
|
279
|
-
return managerHolder
|
|
443
|
+
return undefined
|
|
280
444
|
}
|
|
281
445
|
|
|
282
|
-
// Start async instantiation within
|
|
283
|
-
// This allows circular dependency detection to track the waiter
|
|
446
|
+
// Start async instantiation within resolution context for circular dependency detection
|
|
284
447
|
withResolutionContext(holder, getHolder, () => {
|
|
285
|
-
this.
|
|
448
|
+
this.serviceInitializer
|
|
286
449
|
.instantiateService(ctx, record, args)
|
|
287
450
|
.then(async (result: [undefined, Instance] | [DIError]) => {
|
|
288
451
|
const [error, instance] =
|
|
289
452
|
result.length === 2 ? result : [result[0], undefined]
|
|
453
|
+
const newScope = record.scope
|
|
454
|
+
const newName = this.nameResolver.generateInstanceName(
|
|
455
|
+
realToken,
|
|
456
|
+
args,
|
|
457
|
+
requestId,
|
|
458
|
+
newScope,
|
|
459
|
+
)
|
|
290
460
|
await this.handleInstantiationResult(
|
|
291
|
-
|
|
461
|
+
newName,
|
|
292
462
|
holder,
|
|
293
463
|
ctx,
|
|
294
464
|
deferred,
|
|
295
|
-
|
|
465
|
+
newScope,
|
|
296
466
|
error,
|
|
297
467
|
instance,
|
|
298
468
|
scopedContainer,
|
|
469
|
+
requestStorage,
|
|
470
|
+
requestId,
|
|
299
471
|
)
|
|
300
472
|
})
|
|
301
473
|
.catch(async (error: Error) => {
|
|
474
|
+
const newScope = record.scope
|
|
475
|
+
const newName = this.nameResolver.generateInstanceName(
|
|
476
|
+
realToken,
|
|
477
|
+
args,
|
|
478
|
+
requestId,
|
|
479
|
+
newScope,
|
|
480
|
+
)
|
|
481
|
+
|
|
302
482
|
await this.handleInstantiationError(
|
|
303
|
-
|
|
483
|
+
newName,
|
|
304
484
|
holder,
|
|
305
485
|
deferred,
|
|
306
|
-
|
|
486
|
+
newScope,
|
|
307
487
|
error,
|
|
308
488
|
)
|
|
309
489
|
})
|
|
@@ -316,7 +496,10 @@ export class InstanceResolver {
|
|
|
316
496
|
})
|
|
317
497
|
|
|
318
498
|
// Wait for instance to be ready
|
|
319
|
-
|
|
499
|
+
// Use resolution context to get waiterHolder if available
|
|
500
|
+
const resolutionCtx = getCurrentResolutionContext()
|
|
501
|
+
const waiterHolder = resolutionCtx?.waiterHolder
|
|
502
|
+
return this.waitForInstanceReady(holder, waiterHolder, getHolder)
|
|
320
503
|
}
|
|
321
504
|
|
|
322
505
|
/**
|
|
@@ -327,49 +510,38 @@ export class InstanceResolver {
|
|
|
327
510
|
instanceName: string,
|
|
328
511
|
record: any,
|
|
329
512
|
args: any,
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
513
|
+
contextContainer: IContainer,
|
|
514
|
+
scopedContainer?: ScopedContainer,
|
|
515
|
+
requestStorage?: IHolderStorage,
|
|
516
|
+
requestId?: string,
|
|
334
517
|
): Promise<[undefined, InstanceHolder<Instance>] | [DIError]> {
|
|
335
518
|
this.logger?.log(
|
|
336
519
|
`[InstanceResolver]#createTransientInstance() Creating transient instance for ${instanceName}`,
|
|
337
520
|
)
|
|
338
521
|
|
|
339
522
|
// Create a temporary holder for resolution context (transient instances can still have deps)
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
destroyListeners: [],
|
|
350
|
-
createdAt: Date.now(),
|
|
351
|
-
waitingFor: new Set(),
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Create a getHolder function for resolution context
|
|
355
|
-
const getHolder = (name: string): InstanceHolder | undefined => {
|
|
356
|
-
const [, managerHolder] = this.manager.get(name)
|
|
357
|
-
return managerHolder
|
|
358
|
-
}
|
|
523
|
+
const ctx = this.createServiceInitializationContext(
|
|
524
|
+
scopedContainer ?? contextContainer,
|
|
525
|
+
instanceName,
|
|
526
|
+
InjectableScope.Transient,
|
|
527
|
+
new Set(),
|
|
528
|
+
record.originalToken,
|
|
529
|
+
requestStorage,
|
|
530
|
+
requestId,
|
|
531
|
+
)
|
|
359
532
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
() => this.instantiator.instantiateService(ctx, record, args),
|
|
533
|
+
const [error, instance] = await this.serviceInitializer.instantiateService(
|
|
534
|
+
ctx,
|
|
535
|
+
record,
|
|
536
|
+
args,
|
|
365
537
|
)
|
|
366
538
|
|
|
367
539
|
if (error) {
|
|
368
|
-
return [error
|
|
540
|
+
return [error]
|
|
369
541
|
}
|
|
370
542
|
|
|
371
|
-
// Create a holder for the
|
|
372
|
-
const
|
|
543
|
+
// Create a temporary holder for the result
|
|
544
|
+
const tempHolder: InstanceHolder<Instance> = {
|
|
373
545
|
status: InstanceStatus.Created,
|
|
374
546
|
name: instanceName,
|
|
375
547
|
instance: instance as Instance,
|
|
@@ -377,150 +549,85 @@ export class InstanceResolver {
|
|
|
377
549
|
destroyPromise: null,
|
|
378
550
|
type: record.type,
|
|
379
551
|
scope: InjectableScope.Transient,
|
|
380
|
-
deps: ctx.
|
|
552
|
+
deps: ctx.dependencies,
|
|
381
553
|
destroyListeners: ctx.getDestroyListeners(),
|
|
382
554
|
createdAt: Date.now(),
|
|
383
555
|
waitingFor: new Set(),
|
|
384
556
|
}
|
|
385
557
|
|
|
386
|
-
return [undefined,
|
|
558
|
+
return [undefined, tempHolder]
|
|
387
559
|
}
|
|
388
560
|
|
|
389
561
|
/**
|
|
390
|
-
*
|
|
562
|
+
* Handles successful service instantiation.
|
|
391
563
|
*/
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
const [err, { actualToken, validatedArgs }] =
|
|
405
|
-
this.tokenProcessor.validateAndResolveTokenArgs(token, args)
|
|
406
|
-
if (err) {
|
|
407
|
-
return null
|
|
408
|
-
}
|
|
409
|
-
const instanceName = this.tokenProcessor.generateInstanceName(
|
|
410
|
-
actualToken,
|
|
411
|
-
validatedArgs,
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
// Check if this is a ScopedContainer and the service is request-scoped
|
|
415
|
-
if ('getRequestInstance' in contextContainer) {
|
|
416
|
-
const scopedContainer = contextContainer as ScopedContainer
|
|
417
|
-
const requestHolder = scopedContainer.getRequestInstance(instanceName)
|
|
418
|
-
if (requestHolder) {
|
|
419
|
-
return requestHolder.instance as Instance
|
|
420
|
-
}
|
|
421
|
-
}
|
|
564
|
+
private async handleInstantiationSuccess(
|
|
565
|
+
instanceName: string,
|
|
566
|
+
holder: InstanceHolder<any>,
|
|
567
|
+
ctx: ServiceInitializationContext,
|
|
568
|
+
deferred: any,
|
|
569
|
+
instance: any,
|
|
570
|
+
_scopedContainer?: ScopedContainer,
|
|
571
|
+
requestStorage?: IHolderStorage,
|
|
572
|
+
_requestId?: string,
|
|
573
|
+
): Promise<void> {
|
|
574
|
+
holder.instance = instance
|
|
575
|
+
holder.status = InstanceStatus.Created
|
|
422
576
|
|
|
423
|
-
//
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
return null
|
|
427
|
-
}
|
|
428
|
-
return holder.instance as Instance
|
|
429
|
-
}
|
|
577
|
+
// Set up dependency subscriptions for event-based invalidation
|
|
578
|
+
// Determine which storage to use for subscriptions
|
|
579
|
+
const storageForSubscriptions = requestStorage || this.storage
|
|
430
580
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
contextContainer: IContainer,
|
|
439
|
-
): Promise<
|
|
440
|
-
| [
|
|
441
|
-
undefined,
|
|
442
|
-
{
|
|
443
|
-
instanceName: string
|
|
444
|
-
validatedArgs: any
|
|
445
|
-
actualToken: InjectionTokenType
|
|
446
|
-
realToken: InjectionToken<any, any>
|
|
447
|
-
},
|
|
448
|
-
]
|
|
449
|
-
| [DIError]
|
|
450
|
-
> {
|
|
451
|
-
const [err, { actualToken, validatedArgs }] =
|
|
452
|
-
this.tokenProcessor.validateAndResolveTokenArgs(token, args)
|
|
453
|
-
if (err instanceof DIError && err.code === DIErrorCode.UnknownError) {
|
|
454
|
-
return [err]
|
|
455
|
-
} else if (
|
|
456
|
-
err instanceof DIError &&
|
|
457
|
-
err.code === DIErrorCode.FactoryTokenNotResolved &&
|
|
458
|
-
actualToken instanceof FactoryInjectionToken
|
|
459
|
-
) {
|
|
460
|
-
this.logger?.log(
|
|
461
|
-
`[InstanceResolver]#resolveTokenAndPrepareInstanceName() Factory token not resolved, resolving it`,
|
|
462
|
-
)
|
|
463
|
-
await actualToken.resolve(this.createFactoryContext(contextContainer))
|
|
464
|
-
return this.resolveTokenAndPrepareInstanceName(
|
|
465
|
-
token,
|
|
466
|
-
undefined,
|
|
467
|
-
contextContainer,
|
|
581
|
+
// Set up subscriptions via ServiceInvalidator
|
|
582
|
+
if (ctx.dependencies.size > 0) {
|
|
583
|
+
this.serviceInvalidator.setupDependencySubscriptions(
|
|
584
|
+
instanceName,
|
|
585
|
+
ctx.dependencies,
|
|
586
|
+
storageForSubscriptions,
|
|
587
|
+
holder,
|
|
468
588
|
)
|
|
469
589
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
590
|
+
|
|
591
|
+
this.logger?.log(
|
|
592
|
+
`[InstanceResolver] Instance ${instanceName} created successfully`,
|
|
473
593
|
)
|
|
474
|
-
|
|
475
|
-
const realToken =
|
|
476
|
-
actualToken instanceof BoundInjectionToken ||
|
|
477
|
-
actualToken instanceof FactoryInjectionToken
|
|
478
|
-
? actualToken.token
|
|
479
|
-
: actualToken
|
|
480
|
-
return [undefined, { instanceName, validatedArgs, actualToken, realToken }]
|
|
594
|
+
deferred.resolve([undefined, instance])
|
|
481
595
|
}
|
|
482
596
|
|
|
483
|
-
// ============================================================================
|
|
484
|
-
// INSTANTIATION HANDLERS
|
|
485
|
-
// ============================================================================
|
|
486
|
-
|
|
487
597
|
/**
|
|
488
|
-
*
|
|
489
|
-
* Uses the shared utility from BaseHolderManager.
|
|
490
|
-
* Passes the current resolution context for circular dependency detection.
|
|
598
|
+
* Handles service instantiation errors.
|
|
491
599
|
*/
|
|
492
|
-
private
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
600
|
+
private async handleInstantiationError(
|
|
601
|
+
instanceName: string,
|
|
602
|
+
holder: InstanceHolder<any>,
|
|
603
|
+
deferred: any,
|
|
604
|
+
scope: InjectableScope,
|
|
605
|
+
error: any,
|
|
606
|
+
): Promise<void> {
|
|
607
|
+
holder.status = InstanceStatus.Error
|
|
608
|
+
holder.instance = error instanceof DIError ? error : DIError.unknown(error)
|
|
609
|
+
this.logger?.error(
|
|
610
|
+
`[InstanceResolver] Instance ${instanceName} creation failed:`,
|
|
611
|
+
error,
|
|
502
612
|
)
|
|
613
|
+
deferred.reject(error instanceof DIError ? error : DIError.unknown(error))
|
|
503
614
|
}
|
|
504
615
|
|
|
505
616
|
/**
|
|
506
|
-
* Handles
|
|
617
|
+
* Handles instantiation result (success or error).
|
|
507
618
|
*/
|
|
508
619
|
private async handleInstantiationResult(
|
|
509
620
|
instanceName: string,
|
|
510
621
|
holder: InstanceHolder<any>,
|
|
511
|
-
ctx:
|
|
512
|
-
deps: Set<string>
|
|
513
|
-
getDestroyListeners: () => (() => void)[]
|
|
514
|
-
},
|
|
622
|
+
ctx: ServiceInitializationContext,
|
|
515
623
|
deferred: any,
|
|
516
624
|
scope: InjectableScope,
|
|
517
625
|
error: any,
|
|
518
626
|
instance: any,
|
|
519
627
|
scopedContainer?: ScopedContainer,
|
|
628
|
+
requestStorage?: IHolderStorage,
|
|
629
|
+
requestId?: string,
|
|
520
630
|
): Promise<void> {
|
|
521
|
-
holder.destroyListeners = ctx.getDestroyListeners()
|
|
522
|
-
holder.creationPromise = null
|
|
523
|
-
|
|
524
631
|
if (error) {
|
|
525
632
|
await this.handleInstantiationError(
|
|
526
633
|
instanceName,
|
|
@@ -537,123 +644,174 @@ export class InstanceResolver {
|
|
|
537
644
|
deferred,
|
|
538
645
|
instance,
|
|
539
646
|
scopedContainer,
|
|
647
|
+
requestStorage,
|
|
648
|
+
requestId,
|
|
540
649
|
)
|
|
541
650
|
}
|
|
542
651
|
}
|
|
543
652
|
|
|
544
653
|
/**
|
|
545
|
-
*
|
|
654
|
+
* Waits for an instance holder to be ready and returns the appropriate result.
|
|
655
|
+
*
|
|
656
|
+
* @param holder The holder to wait for
|
|
657
|
+
* @param waiterHolder Optional holder that is doing the waiting (for circular dependency detection)
|
|
658
|
+
* @param getHolder Optional function to retrieve holders by name (required if waiterHolder is provided)
|
|
546
659
|
*/
|
|
547
|
-
private async
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
660
|
+
private async waitForInstanceReady<T>(
|
|
661
|
+
holder: InstanceHolder<T>,
|
|
662
|
+
waiterHolder?: InstanceHolder,
|
|
663
|
+
getHolder?: (name: string) => InstanceHolder | undefined,
|
|
664
|
+
): Promise<[undefined, InstanceHolder<T>] | [DIError]> {
|
|
665
|
+
switch (holder.status) {
|
|
666
|
+
case InstanceStatus.Creating: {
|
|
667
|
+
// Check for circular dependency before waiting
|
|
668
|
+
if (waiterHolder && getHolder) {
|
|
669
|
+
const cycle = CircularDetector.detectCycle(
|
|
670
|
+
waiterHolder.name,
|
|
671
|
+
holder.name,
|
|
672
|
+
getHolder,
|
|
673
|
+
)
|
|
674
|
+
if (cycle) {
|
|
675
|
+
return [DIError.circularDependency(cycle)]
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
679
|
+
// Track the waiting relationship
|
|
680
|
+
waiterHolder.waitingFor.add(holder.name)
|
|
681
|
+
}
|
|
682
|
+
}
|
|
560
683
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
684
|
+
try {
|
|
685
|
+
await holder.creationPromise
|
|
686
|
+
} finally {
|
|
687
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
688
|
+
// Clean up the waiting relationship
|
|
689
|
+
if (waiterHolder) {
|
|
690
|
+
waiterHolder.waitingFor.delete(holder.name)
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Recursively check after creation completes
|
|
696
|
+
return this.waitForInstanceReady(holder, waiterHolder, getHolder)
|
|
569
697
|
}
|
|
570
|
-
}
|
|
571
698
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
ctx.deps.forEach((dependency: string) => {
|
|
575
|
-
holder.destroyListeners.push(
|
|
576
|
-
this.serviceLocator.getEventBus().on(dependency, 'destroy', () => {
|
|
577
|
-
this.logger?.log(
|
|
578
|
-
`[InstanceResolver] Dependency ${dependency} destroyed, invalidating ${instanceName}`,
|
|
579
|
-
)
|
|
580
|
-
this.serviceLocator.getInvalidator().invalidate(instanceName)
|
|
581
|
-
}),
|
|
582
|
-
)
|
|
699
|
+
case InstanceStatus.Destroying:
|
|
700
|
+
return [DIError.instanceDestroying(holder.name)]
|
|
583
701
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const prefixedDependency =
|
|
587
|
-
scopedContainer.getPrefixedEventName(dependency)
|
|
588
|
-
holder.destroyListeners.push(
|
|
589
|
-
this.serviceLocator
|
|
590
|
-
.getEventBus()
|
|
591
|
-
.on(prefixedDependency, 'destroy', () => {
|
|
592
|
-
this.logger?.log(
|
|
593
|
-
`[InstanceResolver] Request-scoped dependency ${dependency} destroyed, invalidating ${instanceName}`,
|
|
594
|
-
)
|
|
595
|
-
// For request-scoped, we need to invalidate within the scoped container
|
|
596
|
-
scopedContainer.invalidate(instance)
|
|
597
|
-
}),
|
|
598
|
-
)
|
|
599
|
-
}
|
|
600
|
-
})
|
|
601
|
-
}
|
|
702
|
+
case InstanceStatus.Error:
|
|
703
|
+
return [holder.instance as unknown as DIError]
|
|
602
704
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
705
|
+
case InstanceStatus.Created:
|
|
706
|
+
return [undefined, holder]
|
|
707
|
+
|
|
708
|
+
default:
|
|
709
|
+
// @ts-expect-error Maybe we will use this in the future
|
|
710
|
+
return [DIError.instanceNotFound(holder?.name ?? 'unknown')]
|
|
711
|
+
}
|
|
608
712
|
}
|
|
609
713
|
|
|
610
714
|
/**
|
|
611
|
-
*
|
|
715
|
+
* Creates a ServiceInitializationContext for service instantiation.
|
|
612
716
|
*/
|
|
613
|
-
private
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
deferred: any,
|
|
717
|
+
private createServiceInitializationContext(
|
|
718
|
+
container: IContainer,
|
|
719
|
+
serviceName: string,
|
|
617
720
|
scope: InjectableScope,
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
)
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
721
|
+
deps: Set<string>,
|
|
722
|
+
serviceToken: InjectionToken<any, any>,
|
|
723
|
+
requestStorage?: IHolderStorage,
|
|
724
|
+
requestId?: string,
|
|
725
|
+
): ServiceInitializationContext {
|
|
726
|
+
const destroyListeners: Array<() => void> = []
|
|
727
|
+
|
|
728
|
+
return {
|
|
729
|
+
inject: async (token: any, args?: any) => {
|
|
730
|
+
// Track dependency and check for scope upgrade
|
|
731
|
+
const actualToken =
|
|
732
|
+
typeof token === 'function'
|
|
733
|
+
? this.tokenResolver.normalizeToken(token)
|
|
734
|
+
: token
|
|
735
|
+
const realToken = this.tokenResolver.getRealToken(actualToken)
|
|
736
|
+
const depRecord = this.registry.get(realToken)
|
|
737
|
+
const depScope = depRecord.scope
|
|
738
|
+
|
|
739
|
+
// Generate dependency name - if dependency is Request-scoped and we have requestId, use it
|
|
740
|
+
const dependencyRequestId =
|
|
741
|
+
depScope === InjectableScope.Request ? requestId : undefined
|
|
742
|
+
const finalDepName = this.nameResolver.generateInstanceName(
|
|
743
|
+
actualToken,
|
|
744
|
+
args,
|
|
745
|
+
dependencyRequestId,
|
|
746
|
+
depScope,
|
|
747
|
+
)
|
|
642
748
|
|
|
643
|
-
|
|
644
|
-
|
|
749
|
+
// Check if current service needs scope upgrade
|
|
750
|
+
// If current service is Singleton and dependency is Request, upgrade current service
|
|
751
|
+
if (
|
|
752
|
+
scope === InjectableScope.Singleton &&
|
|
753
|
+
depScope === InjectableScope.Request &&
|
|
754
|
+
requestStorage &&
|
|
755
|
+
requestId
|
|
756
|
+
) {
|
|
757
|
+
// Check and perform scope upgrade for current service
|
|
758
|
+
// Use the dependency name with requestId for the check
|
|
759
|
+
const [needsUpgrade, newServiceName] =
|
|
760
|
+
this.scopeTracker.checkAndUpgradeScope(
|
|
761
|
+
serviceName,
|
|
762
|
+
scope,
|
|
763
|
+
finalDepName,
|
|
764
|
+
depScope,
|
|
765
|
+
serviceToken,
|
|
766
|
+
this.storage,
|
|
767
|
+
requestStorage,
|
|
768
|
+
requestId,
|
|
769
|
+
)
|
|
645
770
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
771
|
+
if (needsUpgrade && newServiceName) {
|
|
772
|
+
// Service was upgraded - update the service name in context
|
|
773
|
+
// The holder will be moved to request storage by ScopeTracker
|
|
774
|
+
// For now, we continue with the current resolution
|
|
775
|
+
// Future resolutions will use the new name
|
|
776
|
+
}
|
|
777
|
+
}
|
|
649
778
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
779
|
+
// Track dependency
|
|
780
|
+
deps.add(finalDepName)
|
|
781
|
+
|
|
782
|
+
// Resolve dependency
|
|
783
|
+
// Resolution context is automatically used by the injectors system for circular dependency detection
|
|
784
|
+
return container.get(token, args)
|
|
785
|
+
},
|
|
786
|
+
container,
|
|
787
|
+
addDestroyListener: (listener: () => void) => {
|
|
788
|
+
destroyListeners.push(listener)
|
|
789
|
+
},
|
|
790
|
+
getDestroyListeners: () => destroyListeners,
|
|
791
|
+
serviceName,
|
|
792
|
+
dependencies: deps,
|
|
793
|
+
scope,
|
|
794
|
+
trackDependency: (name: string, depScope: InjectableScope) => {
|
|
795
|
+
deps.add(name)
|
|
796
|
+
// Check for scope upgrade
|
|
797
|
+
if (
|
|
798
|
+
scope === InjectableScope.Singleton &&
|
|
799
|
+
depScope === InjectableScope.Request &&
|
|
800
|
+
requestStorage &&
|
|
801
|
+
requestId
|
|
802
|
+
) {
|
|
803
|
+
this.scopeTracker.checkAndUpgradeScope(
|
|
804
|
+
serviceName,
|
|
805
|
+
scope,
|
|
806
|
+
name,
|
|
807
|
+
depScope,
|
|
808
|
+
serviceToken,
|
|
809
|
+
this.storage,
|
|
810
|
+
requestStorage,
|
|
811
|
+
requestId,
|
|
812
|
+
)
|
|
813
|
+
}
|
|
814
|
+
},
|
|
815
|
+
}
|
|
658
816
|
}
|
|
659
817
|
}
|