@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,293 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { Injectable } from '../decorators/injectable.decorator.mjs'
|
|
4
|
+
import { InjectableScope } from '../enums/index.mjs'
|
|
5
|
+
import { TestContainer } from '../testing/test-container.mjs'
|
|
6
|
+
import { InjectionToken } from '../token/injection-token.mjs'
|
|
7
|
+
|
|
8
|
+
describe('TestContainer', () => {
|
|
9
|
+
let container: TestContainer
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
container = new TestContainer()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
await container.dispose()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('Binding API', () => {
|
|
20
|
+
it('should bind a value to a token', async () => {
|
|
21
|
+
const TOKEN = InjectionToken.create<string>('test-token')
|
|
22
|
+
const testValue = 'hello world'
|
|
23
|
+
|
|
24
|
+
container.bind(TOKEN).toValue(testValue)
|
|
25
|
+
|
|
26
|
+
const result = await container.get(TOKEN)
|
|
27
|
+
expect(result).toBe(testValue)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('should bind a class to a token', async () => {
|
|
31
|
+
const TOKEN = InjectionToken.create<{ getValue(): string }>(
|
|
32
|
+
'service-token',
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
class MockService {
|
|
36
|
+
getValue(): string {
|
|
37
|
+
return 'mock value'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
container.bind(TOKEN).toClass(MockService)
|
|
42
|
+
|
|
43
|
+
const result = await container.get(TOKEN)
|
|
44
|
+
expect(result.getValue()).toBe('mock value')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should bind a factory to a token', async () => {
|
|
48
|
+
const TOKEN = InjectionToken.create<{ id: number }>('factory-token')
|
|
49
|
+
|
|
50
|
+
let counter = 0
|
|
51
|
+
container.bind(TOKEN).toFactory(() => ({ id: ++counter }))
|
|
52
|
+
|
|
53
|
+
const result = await container.get(TOKEN)
|
|
54
|
+
expect(result.id).toBe(1)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should clear all bindings', async () => {
|
|
58
|
+
const TOKEN = InjectionToken.create<string>('clear-token')
|
|
59
|
+
container.bind(TOKEN).toValue('test')
|
|
60
|
+
|
|
61
|
+
await container.get(TOKEN)
|
|
62
|
+
container.expectResolved(TOKEN)
|
|
63
|
+
|
|
64
|
+
await container.clear()
|
|
65
|
+
|
|
66
|
+
container.expectNotResolved(TOKEN)
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('Assertion Helpers', () => {
|
|
71
|
+
@Injectable()
|
|
72
|
+
class SimpleService {
|
|
73
|
+
getValue(): string {
|
|
74
|
+
return 'simple'
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
it('should assert service is resolved', async () => {
|
|
79
|
+
await container.get(SimpleService)
|
|
80
|
+
|
|
81
|
+
expect(() => container.expectResolved(SimpleService)).not.toThrow()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('should assert service is NOT resolved', () => {
|
|
85
|
+
@Injectable()
|
|
86
|
+
class UnresolvedService {}
|
|
87
|
+
|
|
88
|
+
expect(() => container.expectNotResolved(UnresolvedService)).not.toThrow()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should throw when expecting resolved but not resolved', () => {
|
|
92
|
+
@Injectable()
|
|
93
|
+
class NeverResolved {}
|
|
94
|
+
|
|
95
|
+
expect(() => container.expectResolved(NeverResolved)).toThrow(
|
|
96
|
+
/to be resolved/,
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should throw when expecting not resolved but was resolved', async () => {
|
|
101
|
+
await container.get(SimpleService)
|
|
102
|
+
|
|
103
|
+
expect(() => container.expectNotResolved(SimpleService)).toThrow(
|
|
104
|
+
/to NOT be resolved/,
|
|
105
|
+
)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should assert singleton scope', async () => {
|
|
109
|
+
await container.get(SimpleService)
|
|
110
|
+
|
|
111
|
+
expect(() => container.expectSingleton(SimpleService)).not.toThrow()
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('should assert transient scope', async () => {
|
|
115
|
+
@Injectable({ scope: InjectableScope.Transient })
|
|
116
|
+
class TransientService {}
|
|
117
|
+
|
|
118
|
+
await container.get(TransientService)
|
|
119
|
+
|
|
120
|
+
expect(() => container.expectTransient(TransientService)).not.toThrow()
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('should assert request scope', async () => {
|
|
124
|
+
@Injectable({ scope: InjectableScope.Request })
|
|
125
|
+
class RequestService {}
|
|
126
|
+
|
|
127
|
+
expect(() => container.expectRequestScoped(RequestService)).not.toThrow()
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe('Instance Assertions', () => {
|
|
132
|
+
it('should assert same instance for singleton', async () => {
|
|
133
|
+
@Injectable()
|
|
134
|
+
class SingletonService {}
|
|
135
|
+
|
|
136
|
+
await expect(
|
|
137
|
+
container.expectSameInstance(SingletonService),
|
|
138
|
+
).resolves.not.toThrow()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should assert different instances for transient', async () => {
|
|
142
|
+
@Injectable({ scope: InjectableScope.Transient })
|
|
143
|
+
class TransientService {}
|
|
144
|
+
|
|
145
|
+
await expect(
|
|
146
|
+
container.expectDifferentInstances(TransientService),
|
|
147
|
+
).resolves.not.toThrow()
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
describe('Method Call Tracking', () => {
|
|
152
|
+
it('should record method calls', async () => {
|
|
153
|
+
const TOKEN = InjectionToken.create<{ doWork(x: number): number }>(
|
|
154
|
+
'work-token',
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
class MockWorker {
|
|
158
|
+
doWork(x: number): number {
|
|
159
|
+
return x * 2
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
container.bind(TOKEN).toClass(MockWorker)
|
|
164
|
+
const worker = await container.get(TOKEN)
|
|
165
|
+
|
|
166
|
+
// Record the call manually (TestContainer requires manual recording)
|
|
167
|
+
const result = worker.doWork(5)
|
|
168
|
+
container.recordMethodCall(TOKEN, 'doWork', [5], result)
|
|
169
|
+
|
|
170
|
+
expect(() => container.expectCalled(TOKEN, 'doWork')).not.toThrow()
|
|
171
|
+
expect(() =>
|
|
172
|
+
container.expectCalledWith(TOKEN, 'doWork', [5]),
|
|
173
|
+
).not.toThrow()
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('should track call count', async () => {
|
|
177
|
+
const TOKEN = InjectionToken.create<{ process(): void }>('process-token')
|
|
178
|
+
|
|
179
|
+
class MockProcessor {
|
|
180
|
+
process(): void {}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
container.bind(TOKEN).toClass(MockProcessor)
|
|
184
|
+
const processor = await container.get(TOKEN)
|
|
185
|
+
|
|
186
|
+
processor.process()
|
|
187
|
+
container.recordMethodCall(TOKEN, 'process', [])
|
|
188
|
+
processor.process()
|
|
189
|
+
container.recordMethodCall(TOKEN, 'process', [])
|
|
190
|
+
processor.process()
|
|
191
|
+
container.recordMethodCall(TOKEN, 'process', [])
|
|
192
|
+
|
|
193
|
+
expect(() => container.expectCallCount(TOKEN, 'process', 3)).not.toThrow()
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('should get all method calls', async () => {
|
|
197
|
+
const TOKEN = InjectionToken.create<{ action(s: string): void }>(
|
|
198
|
+
'action-token',
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
class MockAction {
|
|
202
|
+
// oxlint-disable-next-line no-unused-vars
|
|
203
|
+
action(s: string): void {}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
container.bind(TOKEN).toClass(MockAction)
|
|
207
|
+
const action = await container.get(TOKEN)
|
|
208
|
+
|
|
209
|
+
action.action('first')
|
|
210
|
+
container.recordMethodCall(TOKEN, 'action', ['first'])
|
|
211
|
+
action.action('second')
|
|
212
|
+
container.recordMethodCall(TOKEN, 'action', ['second'])
|
|
213
|
+
|
|
214
|
+
const calls = container.getMethodCalls(TOKEN)
|
|
215
|
+
expect(calls).toHaveLength(2)
|
|
216
|
+
expect(calls[0].args).toEqual(['first'])
|
|
217
|
+
expect(calls[1].args).toEqual(['second'])
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('should clear method calls', async () => {
|
|
221
|
+
const TOKEN = InjectionToken.create<{ foo(): void }>('foo-token')
|
|
222
|
+
|
|
223
|
+
class MockFoo {
|
|
224
|
+
foo(): void {}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
container.bind(TOKEN).toClass(MockFoo)
|
|
228
|
+
await container.get(TOKEN)
|
|
229
|
+
container.recordMethodCall(TOKEN, 'foo', [])
|
|
230
|
+
|
|
231
|
+
container.clearMethodCalls()
|
|
232
|
+
|
|
233
|
+
expect(container.getMethodCalls(TOKEN)).toHaveLength(0)
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
describe('Lifecycle Tracking', () => {
|
|
238
|
+
it('should track lifecycle events for value bindings', async () => {
|
|
239
|
+
const TOKEN = InjectionToken.create<object>('lifecycle-token')
|
|
240
|
+
|
|
241
|
+
// Value bindings record 'created' event automatically
|
|
242
|
+
container.bind(TOKEN).toValue({ name: 'test' })
|
|
243
|
+
|
|
244
|
+
const stats = container.getServiceStats(TOKEN)
|
|
245
|
+
expect(stats.lifecycleEvents.some((e) => e.event === 'created')).toBe(
|
|
246
|
+
true,
|
|
247
|
+
)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('should get service stats with method calls', async () => {
|
|
251
|
+
const TOKEN = InjectionToken.create<{ method(): void }>('stats-token')
|
|
252
|
+
|
|
253
|
+
const mockService = { method: () => {} }
|
|
254
|
+
container.bind(TOKEN).toValue(mockService)
|
|
255
|
+
|
|
256
|
+
const service = await container.get(TOKEN)
|
|
257
|
+
service.method()
|
|
258
|
+
container.recordMethodCall(TOKEN, 'method', [])
|
|
259
|
+
|
|
260
|
+
const stats = container.getServiceStats(TOKEN)
|
|
261
|
+
expect(stats.instanceCount).toBe(1)
|
|
262
|
+
expect(stats.methodCalls).toHaveLength(1)
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
describe('Dependency Graph', () => {
|
|
267
|
+
it('should get dependency graph', async () => {
|
|
268
|
+
const TOKEN_A = InjectionToken.create<object>('service-a')
|
|
269
|
+
const TOKEN_B = InjectionToken.create<object>('service-b')
|
|
270
|
+
|
|
271
|
+
container.bind(TOKEN_A).toValue({ name: 'A' })
|
|
272
|
+
container.bind(TOKEN_B).toValue({ name: 'B' })
|
|
273
|
+
|
|
274
|
+
await container.get(TOKEN_A)
|
|
275
|
+
await container.get(TOKEN_B)
|
|
276
|
+
|
|
277
|
+
const graph = container.getDependencyGraph()
|
|
278
|
+
expect(graph.nodes).toBeDefined()
|
|
279
|
+
expect(Object.keys(graph.nodes).length).toBeGreaterThan(0)
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it('should get simplified dependency graph', async () => {
|
|
283
|
+
const TOKEN = InjectionToken.create<object>('independent-service')
|
|
284
|
+
|
|
285
|
+
container.bind(TOKEN).toValue({ independent: true })
|
|
286
|
+
await container.get(TOKEN)
|
|
287
|
+
|
|
288
|
+
const graph = container.getSimplifiedDependencyGraph()
|
|
289
|
+
expect(graph).toBeDefined()
|
|
290
|
+
expect(typeof graph).toBe('object')
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
})
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { z } from 'zod/v4'
|
|
3
|
+
|
|
4
|
+
import { Injectable } from '../decorators/index.mjs'
|
|
5
|
+
import { DIError, DIErrorCode } from '../errors/index.mjs'
|
|
6
|
+
import { TokenResolver } from '../internal/core/token-resolver.mjs'
|
|
7
|
+
import {
|
|
8
|
+
BoundInjectionToken,
|
|
9
|
+
FactoryInjectionToken,
|
|
10
|
+
InjectionToken,
|
|
11
|
+
} from '../token/injection-token.mjs'
|
|
12
|
+
import { getInjectableToken } from '../utils/index.mjs'
|
|
13
|
+
|
|
14
|
+
describe('TokenResolver', () => {
|
|
15
|
+
let resolver: TokenResolver
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
resolver = new TokenResolver()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('normalizeToken', () => {
|
|
22
|
+
it('should return InjectionToken as-is', () => {
|
|
23
|
+
const token = InjectionToken.create<string>('test')
|
|
24
|
+
const result = resolver.normalizeToken(token)
|
|
25
|
+
expect(result).toBe(token)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should return BoundInjectionToken as-is', () => {
|
|
29
|
+
const schema = z.object({ value: z.string() })
|
|
30
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
31
|
+
const bound = new BoundInjectionToken(token, { value: 'test' })
|
|
32
|
+
const result = resolver.normalizeToken(bound)
|
|
33
|
+
expect(result).toBe(bound)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should return FactoryInjectionToken as-is', () => {
|
|
37
|
+
const schema = z.object({ value: z.string() })
|
|
38
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
39
|
+
const factory = new FactoryInjectionToken(token, async () => ({
|
|
40
|
+
value: 'test',
|
|
41
|
+
}))
|
|
42
|
+
const result = resolver.normalizeToken(factory)
|
|
43
|
+
expect(result).toBe(factory)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should extract token from decorated class', () => {
|
|
47
|
+
@Injectable()
|
|
48
|
+
class TestService {}
|
|
49
|
+
|
|
50
|
+
const result = resolver.normalizeToken(TestService)
|
|
51
|
+
expect(result).toBeInstanceOf(InjectionToken)
|
|
52
|
+
expect(result).toBe(getInjectableToken(TestService))
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
describe('getRealToken', () => {
|
|
57
|
+
it('should return InjectionToken as-is', () => {
|
|
58
|
+
const token = InjectionToken.create<string>('test')
|
|
59
|
+
const result = resolver.getRealToken(token)
|
|
60
|
+
expect(result).toBe(token)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should unwrap BoundInjectionToken', () => {
|
|
64
|
+
const schema = z.object({ value: z.string() })
|
|
65
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
66
|
+
const bound = new BoundInjectionToken(token, { value: 'test' })
|
|
67
|
+
const result = resolver.getRealToken(bound)
|
|
68
|
+
expect(result).toBe(token)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should unwrap FactoryInjectionToken', () => {
|
|
72
|
+
const schema = z.object({ value: z.string() })
|
|
73
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
74
|
+
const factory = new FactoryInjectionToken(token, async () => ({
|
|
75
|
+
value: 'test',
|
|
76
|
+
}))
|
|
77
|
+
const result = resolver.getRealToken(factory)
|
|
78
|
+
expect(result).toBe(token)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('getRegistryToken', () => {
|
|
83
|
+
it('should normalize and unwrap class to InjectionToken', () => {
|
|
84
|
+
@Injectable()
|
|
85
|
+
class TestService {}
|
|
86
|
+
|
|
87
|
+
const result = resolver.getRegistryToken(TestService)
|
|
88
|
+
expect(result).toBeInstanceOf(InjectionToken)
|
|
89
|
+
expect(result).toBe(getInjectableToken(TestService))
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should normalize and unwrap BoundInjectionToken to InjectionToken', () => {
|
|
93
|
+
const schema = z.object({ value: z.string() })
|
|
94
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
95
|
+
const bound = new BoundInjectionToken(token, { value: 'test' })
|
|
96
|
+
|
|
97
|
+
const result = resolver.getRegistryToken(bound)
|
|
98
|
+
expect(result).toBe(token)
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
describe('validateAndResolveTokenArgs', () => {
|
|
103
|
+
it('should pass through token without schema', () => {
|
|
104
|
+
const token = InjectionToken.create<string>('test')
|
|
105
|
+
const [error, data] = resolver.validateAndResolveTokenArgs(token)
|
|
106
|
+
|
|
107
|
+
expect(error).toBeUndefined()
|
|
108
|
+
expect(data.actualToken).toBe(token)
|
|
109
|
+
expect(data.validatedArgs).toBeUndefined()
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should pass through token with valid args and schema', () => {
|
|
113
|
+
const schema = z.object({ value: z.string() })
|
|
114
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
115
|
+
const args = { value: 'hello' }
|
|
116
|
+
|
|
117
|
+
const [error, data] = resolver.validateAndResolveTokenArgs(token, args)
|
|
118
|
+
|
|
119
|
+
expect(error).toBeUndefined()
|
|
120
|
+
expect(data.actualToken).toBe(token)
|
|
121
|
+
expect(data.validatedArgs).toEqual({ value: 'hello' })
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should return error for invalid args', () => {
|
|
125
|
+
const schema = z.object({ value: z.string() })
|
|
126
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
127
|
+
const args = { value: 123 } // Invalid: should be string
|
|
128
|
+
|
|
129
|
+
const [error] = resolver.validateAndResolveTokenArgs(token, args)
|
|
130
|
+
|
|
131
|
+
expect(error).toBeInstanceOf(DIError)
|
|
132
|
+
expect(error?.code).toBe(DIErrorCode.TokenValidationError)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should extract args from BoundInjectionToken', () => {
|
|
136
|
+
const schema = z.object({ value: z.string() })
|
|
137
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
138
|
+
const bound = new BoundInjectionToken(token, { value: 'bound-value' })
|
|
139
|
+
|
|
140
|
+
const [error, data] = resolver.validateAndResolveTokenArgs(bound)
|
|
141
|
+
|
|
142
|
+
expect(error).toBeUndefined()
|
|
143
|
+
expect(data.actualToken).toBe(bound)
|
|
144
|
+
expect(data.validatedArgs).toEqual({ value: 'bound-value' })
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('should return error for unresolved FactoryInjectionToken', () => {
|
|
148
|
+
const schema = z.object({ value: z.string() })
|
|
149
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
150
|
+
const factory = new FactoryInjectionToken(token, async () => ({
|
|
151
|
+
value: 'test',
|
|
152
|
+
}))
|
|
153
|
+
|
|
154
|
+
const [error] = resolver.validateAndResolveTokenArgs(factory)
|
|
155
|
+
|
|
156
|
+
expect(error).toBeInstanceOf(DIError)
|
|
157
|
+
expect(error?.code).toBe(DIErrorCode.FactoryTokenNotResolved)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should extract args from resolved FactoryInjectionToken', async () => {
|
|
161
|
+
const schema = z.object({ value: z.string() })
|
|
162
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
163
|
+
const factory = new FactoryInjectionToken(token, async () => ({
|
|
164
|
+
value: 'factory-value',
|
|
165
|
+
}))
|
|
166
|
+
|
|
167
|
+
// Resolve the factory first
|
|
168
|
+
await factory.resolve({
|
|
169
|
+
inject: vi.fn(),
|
|
170
|
+
container: {} as any,
|
|
171
|
+
addDestroyListener: vi.fn(),
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
const [error, data] = resolver.validateAndResolveTokenArgs(factory)
|
|
175
|
+
|
|
176
|
+
expect(error).toBeUndefined()
|
|
177
|
+
expect(data.actualToken).toBe(factory)
|
|
178
|
+
expect(data.validatedArgs).toEqual({ value: 'factory-value' })
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should handle class constructor token', () => {
|
|
182
|
+
@Injectable()
|
|
183
|
+
class TestService {}
|
|
184
|
+
|
|
185
|
+
const [error, data] = resolver.validateAndResolveTokenArgs(TestService)
|
|
186
|
+
|
|
187
|
+
expect(error).toBeUndefined()
|
|
188
|
+
expect(data.actualToken).toBeInstanceOf(InjectionToken)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('should log validation errors when logger is provided', () => {
|
|
192
|
+
const mockLogger = {
|
|
193
|
+
error: vi.fn(),
|
|
194
|
+
log: vi.fn(),
|
|
195
|
+
warn: vi.fn(),
|
|
196
|
+
} as unknown as Console
|
|
197
|
+
|
|
198
|
+
const resolverWithLogger = new TokenResolver(mockLogger)
|
|
199
|
+
const schema = z.object({ value: z.string() })
|
|
200
|
+
const token = InjectionToken.create<string, typeof schema>('test', schema)
|
|
201
|
+
|
|
202
|
+
resolverWithLogger.validateAndResolveTokenArgs(token, { value: 123 })
|
|
203
|
+
|
|
204
|
+
expect(mockLogger.error).toHaveBeenCalled()
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
})
|