@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,351 @@
|
|
|
1
|
+
import { z } from 'zod/v4'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { DIError, DIErrorCode } from '../errors/index.mjs'
|
|
5
|
+
import { InjectableScope, InjectableType } from '../enums/index.mjs'
|
|
6
|
+
import { InjectionToken } from '../token/injection-token.mjs'
|
|
7
|
+
import type { FactoryRecord } from '../token/registry.mjs'
|
|
8
|
+
|
|
9
|
+
describe('DIError', () => {
|
|
10
|
+
describe('constructor', () => {
|
|
11
|
+
it('should create error with code and message', () => {
|
|
12
|
+
const error = new DIError(DIErrorCode.FactoryNotFound, 'Test message')
|
|
13
|
+
|
|
14
|
+
expect(error.code).toBe(DIErrorCode.FactoryNotFound)
|
|
15
|
+
expect(error.message).toBe('Test message')
|
|
16
|
+
expect(error.name).toBe('DIError')
|
|
17
|
+
expect(error.context).toBeUndefined()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should create error with context', () => {
|
|
21
|
+
const context = { key: 'value', num: 42 }
|
|
22
|
+
const error = new DIError(
|
|
23
|
+
DIErrorCode.InstanceNotFound,
|
|
24
|
+
'Test message',
|
|
25
|
+
context,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
expect(error.context).toEqual(context)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should extend Error class', () => {
|
|
32
|
+
const error = new DIError(DIErrorCode.UnknownError, 'Test')
|
|
33
|
+
expect(error).toBeInstanceOf(Error)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('factoryNotFound', () => {
|
|
38
|
+
it('should create FactoryNotFound error', () => {
|
|
39
|
+
const error = DIError.factoryNotFound('MyService')
|
|
40
|
+
|
|
41
|
+
expect(error.code).toBe(DIErrorCode.FactoryNotFound)
|
|
42
|
+
expect(error.message).toBe('Factory MyService not found')
|
|
43
|
+
expect(error.context).toEqual({ name: 'MyService' })
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('factoryTokenNotResolved', () => {
|
|
48
|
+
it('should create FactoryTokenNotResolved error with string token', () => {
|
|
49
|
+
const error = DIError.factoryTokenNotResolved('test-token')
|
|
50
|
+
|
|
51
|
+
expect(error.code).toBe(DIErrorCode.FactoryTokenNotResolved)
|
|
52
|
+
expect(error.message).toBe('Factory token not resolved: test-token')
|
|
53
|
+
expect(error.context).toEqual({ token: 'test-token' })
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should create FactoryTokenNotResolved error with symbol token', () => {
|
|
57
|
+
const sym = Symbol('test')
|
|
58
|
+
const error = DIError.factoryTokenNotResolved(sym)
|
|
59
|
+
|
|
60
|
+
expect(error.code).toBe(DIErrorCode.FactoryTokenNotResolved)
|
|
61
|
+
expect(error.message).toContain('Symbol(test)')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should handle null/undefined token', () => {
|
|
65
|
+
const error = DIError.factoryTokenNotResolved(null)
|
|
66
|
+
|
|
67
|
+
expect(error.code).toBe(DIErrorCode.FactoryTokenNotResolved)
|
|
68
|
+
expect(error.message).toBe('Factory token not resolved: unknown')
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('instanceNotFound', () => {
|
|
73
|
+
it('should create InstanceNotFound error', () => {
|
|
74
|
+
const error = DIError.instanceNotFound('MyInstance')
|
|
75
|
+
|
|
76
|
+
expect(error.code).toBe(DIErrorCode.InstanceNotFound)
|
|
77
|
+
expect(error.message).toBe('Instance MyInstance not found')
|
|
78
|
+
expect(error.context).toEqual({ name: 'MyInstance' })
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('instanceDestroying', () => {
|
|
83
|
+
it('should create InstanceDestroying error', () => {
|
|
84
|
+
const error = DIError.instanceDestroying('MyInstance')
|
|
85
|
+
|
|
86
|
+
expect(error.code).toBe(DIErrorCode.InstanceDestroying)
|
|
87
|
+
expect(error.message).toBe('Instance MyInstance destroying')
|
|
88
|
+
expect(error.context).toEqual({ name: 'MyInstance' })
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('unknown', () => {
|
|
93
|
+
it('should create UnknownError from string message', () => {
|
|
94
|
+
const error = DIError.unknown('Something went wrong')
|
|
95
|
+
|
|
96
|
+
expect(error.code).toBe(DIErrorCode.UnknownError)
|
|
97
|
+
expect(error.message).toBe('Something went wrong')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should create UnknownError from Error object', () => {
|
|
101
|
+
const originalError = new Error('Original error')
|
|
102
|
+
const error = DIError.unknown(originalError)
|
|
103
|
+
|
|
104
|
+
expect(error.code).toBe(DIErrorCode.UnknownError)
|
|
105
|
+
expect(error.message).toBe('Original error')
|
|
106
|
+
expect(error.context?.parent).toBe(originalError)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should include additional context', () => {
|
|
110
|
+
const error = DIError.unknown('Error', { extra: 'data' })
|
|
111
|
+
|
|
112
|
+
expect(error.context).toEqual({ extra: 'data' })
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should merge context with parent Error', () => {
|
|
116
|
+
const originalError = new Error('Original')
|
|
117
|
+
const error = DIError.unknown(originalError, { extra: 'data' })
|
|
118
|
+
|
|
119
|
+
expect(error.context?.parent).toBe(originalError)
|
|
120
|
+
expect(error.context?.extra).toBe('data')
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
describe('circularDependency', () => {
|
|
125
|
+
it('should create CircularDependency error', () => {
|
|
126
|
+
const cycle = ['ServiceA', 'ServiceB', 'ServiceC', 'ServiceA']
|
|
127
|
+
const error = DIError.circularDependency(cycle)
|
|
128
|
+
|
|
129
|
+
expect(error.code).toBe(DIErrorCode.CircularDependency)
|
|
130
|
+
expect(error.message).toBe(
|
|
131
|
+
'Circular dependency detected: ServiceA -> ServiceB -> ServiceC -> ServiceA',
|
|
132
|
+
)
|
|
133
|
+
expect(error.context).toEqual({ cycle })
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should handle self-referential cycle', () => {
|
|
137
|
+
const cycle = ['ServiceA', 'ServiceA']
|
|
138
|
+
const error = DIError.circularDependency(cycle)
|
|
139
|
+
|
|
140
|
+
expect(error.message).toBe(
|
|
141
|
+
'Circular dependency detected: ServiceA -> ServiceA',
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
describe('tokenValidationError', () => {
|
|
147
|
+
it('should create TokenValidationError', () => {
|
|
148
|
+
const schema = z.object({ name: z.string() })
|
|
149
|
+
const value = { name: 123 }
|
|
150
|
+
const error = DIError.tokenValidationError(
|
|
151
|
+
'Validation failed',
|
|
152
|
+
schema,
|
|
153
|
+
value,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
expect(error.code).toBe(DIErrorCode.TokenValidationError)
|
|
157
|
+
expect(error.message).toBe('Validation failed')
|
|
158
|
+
expect(error.context?.schema).toBe(schema)
|
|
159
|
+
expect(error.context?.value).toEqual(value)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('should handle undefined schema', () => {
|
|
163
|
+
const error = DIError.tokenValidationError(
|
|
164
|
+
'Validation failed',
|
|
165
|
+
undefined,
|
|
166
|
+
null,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
expect(error.context?.schema).toBeUndefined()
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
describe('tokenSchemaRequiredError', () => {
|
|
174
|
+
it('should create TokenSchemaRequiredError with string token', () => {
|
|
175
|
+
const error = DIError.tokenSchemaRequiredError('TestToken')
|
|
176
|
+
|
|
177
|
+
expect(error.code).toBe(DIErrorCode.TokenSchemaRequiredError)
|
|
178
|
+
expect(error.message).toContain('TestToken')
|
|
179
|
+
expect(error.message).toContain('requires schema arguments')
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should create TokenSchemaRequiredError with symbol token', () => {
|
|
183
|
+
const sym = Symbol('test')
|
|
184
|
+
const error = DIError.tokenSchemaRequiredError(sym)
|
|
185
|
+
|
|
186
|
+
expect(error.code).toBe(DIErrorCode.TokenSchemaRequiredError)
|
|
187
|
+
expect(error.message).toContain('Symbol(test)')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('should handle null/undefined token', () => {
|
|
191
|
+
const error = DIError.tokenSchemaRequiredError(null)
|
|
192
|
+
|
|
193
|
+
expect(error.message).toContain('unknown')
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
describe('classNotInjectable', () => {
|
|
198
|
+
it('should create ClassNotInjectable error', () => {
|
|
199
|
+
const error = DIError.classNotInjectable('MyClass')
|
|
200
|
+
|
|
201
|
+
expect(error.code).toBe(DIErrorCode.ClassNotInjectable)
|
|
202
|
+
expect(error.message).toBe(
|
|
203
|
+
'Class MyClass is not decorated with @Injectable.',
|
|
204
|
+
)
|
|
205
|
+
expect(error.context).toEqual({ className: 'MyClass' })
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe('scopeMismatchError', () => {
|
|
210
|
+
it('should create ScopeMismatchError', () => {
|
|
211
|
+
const error = DIError.scopeMismatchError(
|
|
212
|
+
'TestToken',
|
|
213
|
+
'Singleton',
|
|
214
|
+
'Request',
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
expect(error.code).toBe(DIErrorCode.ScopeMismatchError)
|
|
218
|
+
expect(error.message).toBe(
|
|
219
|
+
'Scope mismatch for TestToken: expected Singleton, got Request',
|
|
220
|
+
)
|
|
221
|
+
expect(error.context).toEqual({
|
|
222
|
+
token: 'TestToken',
|
|
223
|
+
expectedScope: 'Singleton',
|
|
224
|
+
actualScope: 'Request',
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
describe('priorityConflictError', () => {
|
|
230
|
+
it('should create PriorityConflictError', () => {
|
|
231
|
+
const token = InjectionToken.create<string>('test')
|
|
232
|
+
const records: FactoryRecord[] = [
|
|
233
|
+
{
|
|
234
|
+
scope: InjectableScope.Singleton,
|
|
235
|
+
originalToken: token,
|
|
236
|
+
target: class A {},
|
|
237
|
+
type: InjectableType.Class,
|
|
238
|
+
priority: 1,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
scope: InjectableScope.Singleton,
|
|
242
|
+
originalToken: token,
|
|
243
|
+
target: class B {},
|
|
244
|
+
type: InjectableType.Class,
|
|
245
|
+
priority: 1,
|
|
246
|
+
},
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
const error = DIError.priorityConflictError('TestToken', records)
|
|
250
|
+
|
|
251
|
+
expect(error.code).toBe(DIErrorCode.PriorityConflictError)
|
|
252
|
+
expect(error.message).toContain('Priority conflict')
|
|
253
|
+
expect(error.context?.records).toBe(records)
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
describe('storageError', () => {
|
|
258
|
+
it('should create StorageError with instance name', () => {
|
|
259
|
+
const error = DIError.storageError('Failed to store', 'set', 'MyInstance')
|
|
260
|
+
|
|
261
|
+
expect(error.code).toBe(DIErrorCode.StorageError)
|
|
262
|
+
expect(error.message).toBe('Storage error: Failed to store')
|
|
263
|
+
expect(error.context).toEqual({
|
|
264
|
+
operation: 'set',
|
|
265
|
+
instanceName: 'MyInstance',
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('should create StorageError without instance name', () => {
|
|
270
|
+
const error = DIError.storageError('Failed to clear', 'clear')
|
|
271
|
+
|
|
272
|
+
expect(error.context?.instanceName).toBeUndefined()
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
describe('initializationError', () => {
|
|
277
|
+
it('should create InitializationError from Error', () => {
|
|
278
|
+
const originalError = new Error('Constructor failed')
|
|
279
|
+
const error = DIError.initializationError('MyService', originalError)
|
|
280
|
+
|
|
281
|
+
expect(error.code).toBe(DIErrorCode.InitializationError)
|
|
282
|
+
expect(error.message).toBe(
|
|
283
|
+
'Service MyService initialization failed: Constructor failed',
|
|
284
|
+
)
|
|
285
|
+
expect(error.context?.error).toBe(originalError)
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
it('should create InitializationError from string', () => {
|
|
289
|
+
const error = DIError.initializationError(
|
|
290
|
+
'MyService',
|
|
291
|
+
'Missing dependency',
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
expect(error.message).toBe(
|
|
295
|
+
'Service MyService initialization failed: Missing dependency',
|
|
296
|
+
)
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
describe('dependencyResolutionError', () => {
|
|
301
|
+
it('should create DependencyResolutionError from Error', () => {
|
|
302
|
+
const originalError = new Error('Not found')
|
|
303
|
+
const error = DIError.dependencyResolutionError(
|
|
304
|
+
'MyService',
|
|
305
|
+
'DatabaseService',
|
|
306
|
+
originalError,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
expect(error.code).toBe(DIErrorCode.DependencyResolutionError)
|
|
310
|
+
expect(error.message).toBe(
|
|
311
|
+
'Failed to resolve dependency DatabaseService for service MyService: Not found',
|
|
312
|
+
)
|
|
313
|
+
expect(error.context).toEqual({
|
|
314
|
+
serviceName: 'MyService',
|
|
315
|
+
dependencyName: 'DatabaseService',
|
|
316
|
+
error: originalError,
|
|
317
|
+
})
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it('should create DependencyResolutionError from string', () => {
|
|
321
|
+
const error = DIError.dependencyResolutionError(
|
|
322
|
+
'MyService',
|
|
323
|
+
'DatabaseService',
|
|
324
|
+
'Connection failed',
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
expect(error.message).toContain('Connection failed')
|
|
328
|
+
})
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
describe('DIErrorCode enum', () => {
|
|
332
|
+
it('should have all expected error codes', () => {
|
|
333
|
+
expect(DIErrorCode.FactoryNotFound).toBe('FactoryNotFound')
|
|
334
|
+
expect(DIErrorCode.FactoryTokenNotResolved).toBe('FactoryTokenNotResolved')
|
|
335
|
+
expect(DIErrorCode.InstanceNotFound).toBe('InstanceNotFound')
|
|
336
|
+
expect(DIErrorCode.InstanceDestroying).toBe('InstanceDestroying')
|
|
337
|
+
expect(DIErrorCode.CircularDependency).toBe('CircularDependency')
|
|
338
|
+
expect(DIErrorCode.TokenValidationError).toBe('TokenValidationError')
|
|
339
|
+
expect(DIErrorCode.TokenSchemaRequiredError).toBe('TokenSchemaRequiredError')
|
|
340
|
+
expect(DIErrorCode.ClassNotInjectable).toBe('ClassNotInjectable')
|
|
341
|
+
expect(DIErrorCode.ScopeMismatchError).toBe('ScopeMismatchError')
|
|
342
|
+
expect(DIErrorCode.PriorityConflictError).toBe('PriorityConflictError')
|
|
343
|
+
expect(DIErrorCode.StorageError).toBe('StorageError')
|
|
344
|
+
expect(DIErrorCode.InitializationError).toBe('InitializationError')
|
|
345
|
+
expect(DIErrorCode.DependencyResolutionError).toBe(
|
|
346
|
+
'DependencyResolutionError',
|
|
347
|
+
)
|
|
348
|
+
expect(DIErrorCode.UnknownError).toBe('UnknownError')
|
|
349
|
+
})
|
|
350
|
+
})
|
|
351
|
+
})
|
|
@@ -424,13 +424,11 @@ describe('E2E Browser: Concurrent Requests', () => {
|
|
|
424
424
|
describe('E2E Browser: Service Lifecycle', () => {
|
|
425
425
|
let registry: Registry
|
|
426
426
|
let container: Container
|
|
427
|
-
let injectors: ReturnType<typeof getInjectors>
|
|
428
427
|
|
|
429
428
|
beforeEach(() => {
|
|
430
429
|
const setup = createTestSetup()
|
|
431
430
|
registry = setup.registry
|
|
432
431
|
container = setup.container
|
|
433
|
-
injectors = setup.injectors
|
|
434
432
|
})
|
|
435
433
|
|
|
436
434
|
afterEach(async () => {
|
|
@@ -654,13 +652,11 @@ describe('E2E Browser: Service Invalidation', () => {
|
|
|
654
652
|
describe('E2E Browser: Error Handling', () => {
|
|
655
653
|
let registry: Registry
|
|
656
654
|
let container: Container
|
|
657
|
-
let injectors: ReturnType<typeof getInjectors>
|
|
658
655
|
|
|
659
656
|
beforeEach(() => {
|
|
660
657
|
const setup = createTestSetup()
|
|
661
658
|
registry = setup.registry
|
|
662
659
|
container = setup.container
|
|
663
|
-
injectors = setup.injectors
|
|
664
660
|
})
|
|
665
661
|
|
|
666
662
|
afterEach(async () => {
|
|
@@ -11,12 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
13
13
|
|
|
14
|
+
import type { OnServiceDestroy, OnServiceInit } from '../index.mjs'
|
|
15
|
+
|
|
14
16
|
import { Container } from '../container/container.mjs'
|
|
15
17
|
import { Injectable } from '../decorators/injectable.decorator.mjs'
|
|
16
18
|
import { InjectableScope } from '../enums/index.mjs'
|
|
17
|
-
import { InjectionToken } from '../token/injection-token.mjs'
|
|
18
|
-
import type { OnServiceDestroy } from '../interfaces/on-service-destroy.interface.mjs'
|
|
19
|
-
import type { OnServiceInit } from '../interfaces/on-service-init.interface.mjs'
|
|
20
19
|
import { Registry } from '../token/registry.mjs'
|
|
21
20
|
import { getInjectors } from '../utils/get-injectors.mjs'
|
|
22
21
|
|
|
@@ -96,12 +95,16 @@ describe('E2E: Basic Setup', () => {
|
|
|
96
95
|
expect(instance1.id).not.toBe(instance2.id)
|
|
97
96
|
})
|
|
98
97
|
|
|
99
|
-
it('should resolve services with constructor arguments via schema', async () => {
|
|
98
|
+
it('should resolve services with constructor arguments via schema', async () => {
|
|
100
99
|
// Define schema first, then class, then token to avoid hoisting issues
|
|
101
100
|
const { z } = await import('zod/v4')
|
|
102
101
|
const configSchema = z.object({ port: z.number() })
|
|
103
102
|
|
|
104
|
-
@Injectable({
|
|
103
|
+
@Injectable({
|
|
104
|
+
scope: InjectableScope.Singleton,
|
|
105
|
+
registry,
|
|
106
|
+
schema: configSchema,
|
|
107
|
+
})
|
|
105
108
|
class ConfigService {
|
|
106
109
|
constructor(public config: { port: number }) {}
|
|
107
110
|
}
|
|
@@ -135,7 +138,7 @@ it('should resolve services with constructor arguments via schema', async () =>
|
|
|
135
138
|
expect(db.connect()).toBe('connected')
|
|
136
139
|
})
|
|
137
140
|
|
|
138
|
-
it('should resolve deep dependency chains', async () => {
|
|
141
|
+
it('should resolve deep dependency chains', async () => {
|
|
139
142
|
@Injectable({ scope: InjectableScope.Singleton, registry })
|
|
140
143
|
class ServiceLevel1 {
|
|
141
144
|
name = 'Level1'
|
|
@@ -415,7 +418,7 @@ describe('E2E: Concurrent Requests', () => {
|
|
|
415
418
|
await Promise.all(requests)
|
|
416
419
|
|
|
417
420
|
// Each request should have used the same counter instance (3 identical IDs)
|
|
418
|
-
for (const [
|
|
421
|
+
for (const [, ids] of Object.entries(instancesByRequest)) {
|
|
419
422
|
expect(ids.length).toBe(3)
|
|
420
423
|
expect(ids[0]).toBe(ids[1])
|
|
421
424
|
expect(ids[1]).toBe(ids[2])
|
|
@@ -1001,13 +1004,11 @@ describe('E2E: Service Invalidation', () => {
|
|
|
1001
1004
|
describe('E2E: Error Handling', () => {
|
|
1002
1005
|
let registry: Registry
|
|
1003
1006
|
let container: Container
|
|
1004
|
-
let injectors: ReturnType<typeof getInjectors>
|
|
1005
1007
|
|
|
1006
1008
|
beforeEach(() => {
|
|
1007
1009
|
const setup = createTestSetup()
|
|
1008
1010
|
registry = setup.registry
|
|
1009
1011
|
container = setup.container
|
|
1010
|
-
injectors = setup.injectors
|
|
1011
1012
|
})
|
|
1012
1013
|
|
|
1013
1014
|
afterEach(async () => {
|
|
@@ -1110,11 +1111,6 @@ describe('E2E: Advanced Scenarios', () => {
|
|
|
1110
1111
|
|
|
1111
1112
|
describe('Metadata handling', () => {
|
|
1112
1113
|
it('should pass metadata to request context', async () => {
|
|
1113
|
-
@Injectable({ scope: InjectableScope.Request, registry })
|
|
1114
|
-
class MetadataAwareService {
|
|
1115
|
-
constructor() {}
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
1114
|
const metadata = { userId: '123', roles: ['admin'] }
|
|
1119
1115
|
const scoped = container.beginRequest('request-1', metadata)
|
|
1120
1116
|
|
|
@@ -1125,11 +1121,6 @@ describe('E2E: Advanced Scenarios', () => {
|
|
|
1125
1121
|
})
|
|
1126
1122
|
|
|
1127
1123
|
it('should allow modifying metadata during request', async () => {
|
|
1128
|
-
@Injectable({ scope: InjectableScope.Request, registry })
|
|
1129
|
-
class RequestTracker {
|
|
1130
|
-
startTime = Date.now()
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
1124
|
const scoped = container.beginRequest('request-1')
|
|
1134
1125
|
scoped.setMetadata('stage', 'processing')
|
|
1135
1126
|
|