@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
|
@@ -1,163 +1,286 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
|
+
import { Container } from '../container/container.mjs'
|
|
3
4
|
import { EventEmitter } from '../event-emitter.mjs'
|
|
4
5
|
|
|
5
6
|
describe('EventEmitter', () => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
let container: Container
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
container = new Container()
|
|
9
11
|
})
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
await container.dispose()
|
|
15
|
+
})
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
describe('basic operations', () => {
|
|
18
|
+
it('should be injectable as transient', async () => {
|
|
19
|
+
const emitter1 = await container.get(EventEmitter)
|
|
20
|
+
const emitter2 = await container.get(EventEmitter)
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
expect(emitter1).toBeInstanceOf(EventEmitter)
|
|
23
|
+
expect(emitter2).toBeInstanceOf(EventEmitter)
|
|
24
|
+
expect(emitter1).not.toBe(emitter2)
|
|
25
|
+
})
|
|
21
26
|
})
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
describe('on', () => {
|
|
29
|
+
it('should register event listener', async () => {
|
|
30
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
31
|
+
const listener = vi.fn()
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
await emitter.emit('test', 'hello')
|
|
33
|
+
emitter.on('test', listener)
|
|
34
|
+
await emitter.emit('test')
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
})
|
|
36
|
+
expect(listener).toHaveBeenCalledTimes(1)
|
|
37
|
+
})
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
it('should return unsubscribe function', async () => {
|
|
40
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
41
|
+
const listener = vi.fn()
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const unsubscribe = emitter.on('test', listener)
|
|
44
|
+
unsubscribe()
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
await emitter.emit('test')
|
|
47
|
+
expect(listener).not.toHaveBeenCalled()
|
|
48
|
+
})
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
it('should support multiple listeners for same event', async () => {
|
|
51
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
52
|
+
const listener1 = vi.fn()
|
|
53
|
+
const listener2 = vi.fn()
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
emitter.on('test', listener1)
|
|
56
|
+
emitter.on('test', listener2)
|
|
57
|
+
await emitter.emit('test')
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
expect(listener1).toHaveBeenCalledTimes(1)
|
|
60
|
+
expect(listener2).toHaveBeenCalledTimes(1)
|
|
61
|
+
})
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
it('should support different event types', async () => {
|
|
64
|
+
const emitter = await container.get(
|
|
65
|
+
EventEmitter<{ eventA: []; eventB: [] }>,
|
|
66
|
+
)
|
|
67
|
+
const listenerA = vi.fn()
|
|
68
|
+
const listenerB = vi.fn()
|
|
59
69
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
})
|
|
70
|
+
emitter.on('eventA', listenerA)
|
|
71
|
+
emitter.on('eventB', listenerB)
|
|
63
72
|
|
|
64
|
-
|
|
65
|
-
const emitter = new EventEmitter<{ test: [string] }>()
|
|
66
|
-
const mockListener = vi.fn()
|
|
73
|
+
await emitter.emit('eventA')
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
})
|
|
75
|
+
expect(listenerA).toHaveBeenCalledTimes(1)
|
|
76
|
+
expect(listenerB).not.toHaveBeenCalled()
|
|
77
|
+
})
|
|
71
78
|
})
|
|
72
79
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
describe('off', () => {
|
|
81
|
+
it('should remove specific listener', async () => {
|
|
82
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
83
|
+
const listener1 = vi.fn()
|
|
84
|
+
const listener2 = vi.fn()
|
|
77
85
|
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
emitter.on('test', listener1)
|
|
87
|
+
emitter.on('test', listener2)
|
|
88
|
+
emitter.off('test', listener1)
|
|
80
89
|
|
|
81
|
-
|
|
82
|
-
emitter.off('test', mockListener2)
|
|
90
|
+
await emitter.emit('test')
|
|
83
91
|
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
expect(listener1).not.toHaveBeenCalled()
|
|
93
|
+
expect(listener2).toHaveBeenCalledTimes(1)
|
|
94
|
+
})
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
it('should handle removing non-existent listener gracefully', async () => {
|
|
97
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
98
|
+
const listener = vi.fn()
|
|
90
99
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
// Should not throw
|
|
101
|
+
emitter.off('test', listener)
|
|
102
|
+
})
|
|
94
103
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
await emitter.emit('test', 'hello2')
|
|
104
|
+
it('should handle removing from non-existent event gracefully', async () => {
|
|
105
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
98
106
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
// Should not throw
|
|
108
|
+
emitter.off('test', () => {})
|
|
109
|
+
})
|
|
102
110
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
it('should clean up empty event sets', async () => {
|
|
112
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
113
|
+
const listener = vi.fn()
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
await emitter.emit('test', 'hello')
|
|
115
|
+
emitter.on('test', listener)
|
|
116
|
+
emitter.off('test', listener)
|
|
110
117
|
|
|
111
|
-
|
|
118
|
+
// After removing the last listener, the event should be cleaned up
|
|
119
|
+
// We can verify this by checking that emit doesn't call anything
|
|
120
|
+
await emitter.emit('test')
|
|
121
|
+
expect(listener).not.toHaveBeenCalled()
|
|
122
|
+
})
|
|
112
123
|
})
|
|
113
124
|
|
|
114
|
-
|
|
115
|
-
|
|
125
|
+
describe('once', () => {
|
|
126
|
+
it('should only trigger listener once', async () => {
|
|
127
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
128
|
+
const listener = vi.fn()
|
|
116
129
|
|
|
117
|
-
|
|
118
|
-
expect(result).toBeUndefined()
|
|
119
|
-
})
|
|
130
|
+
emitter.once('test', listener)
|
|
120
131
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
132
|
+
await emitter.emit('test')
|
|
133
|
+
await emitter.emit('test')
|
|
134
|
+
await emitter.emit('test')
|
|
124
135
|
|
|
125
|
-
|
|
126
|
-
|
|
136
|
+
expect(listener).toHaveBeenCalledTimes(1)
|
|
137
|
+
})
|
|
127
138
|
|
|
128
|
-
|
|
139
|
+
it('should return unsubscribe function', async () => {
|
|
140
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
141
|
+
const listener = vi.fn()
|
|
142
|
+
|
|
143
|
+
const unsubscribe = emitter.once('test', listener)
|
|
144
|
+
unsubscribe()
|
|
145
|
+
|
|
146
|
+
await emitter.emit('test')
|
|
147
|
+
expect(listener).not.toHaveBeenCalled()
|
|
148
|
+
})
|
|
129
149
|
})
|
|
130
150
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
151
|
+
describe('emit', () => {
|
|
152
|
+
it('should pass arguments to listeners', async () => {
|
|
153
|
+
const emitter = await container.get(
|
|
154
|
+
EventEmitter<{ test: [string, number] }>,
|
|
155
|
+
)
|
|
156
|
+
const listener = vi.fn()
|
|
157
|
+
|
|
158
|
+
emitter.on('test', listener)
|
|
159
|
+
await emitter.emit('test', 'hello', 42)
|
|
160
|
+
|
|
161
|
+
expect(listener).toHaveBeenCalledWith('hello', 42)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should return undefined for non-existent event', async () => {
|
|
165
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
166
|
+
|
|
167
|
+
const result = await emitter.emit('test')
|
|
168
|
+
expect(result).toBeUndefined()
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('should wait for async listeners', async () => {
|
|
172
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
173
|
+
let completed = false
|
|
134
174
|
|
|
135
|
-
|
|
136
|
-
|
|
175
|
+
emitter.on('test', async () => {
|
|
176
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
177
|
+
completed = true
|
|
178
|
+
})
|
|
137
179
|
|
|
138
|
-
|
|
139
|
-
|
|
180
|
+
await emitter.emit('test')
|
|
181
|
+
expect(completed).toBe(true)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should execute all listeners concurrently', async () => {
|
|
185
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
186
|
+
const order: number[] = []
|
|
187
|
+
|
|
188
|
+
emitter.on('test', async () => {
|
|
189
|
+
await new Promise((resolve) => setTimeout(resolve, 30))
|
|
190
|
+
order.push(1)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
emitter.on('test', async () => {
|
|
194
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
195
|
+
order.push(2)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
emitter.on('test', async () => {
|
|
199
|
+
order.push(3)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
await emitter.emit('test')
|
|
203
|
+
|
|
204
|
+
// All should complete, but order depends on timing
|
|
205
|
+
expect(order).toHaveLength(3)
|
|
206
|
+
// The sync one should complete first
|
|
207
|
+
expect(order[0]).toBe(3)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('should return array of results from Promise.all', async () => {
|
|
211
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
212
|
+
|
|
213
|
+
// @ts-expect-error - not a documented feature
|
|
214
|
+
emitter.on('test', async () => 'result1')
|
|
215
|
+
// @ts-expect-error - not a documented feature
|
|
216
|
+
emitter.on('test', async () => 'result2')
|
|
217
|
+
|
|
218
|
+
const results = await emitter.emit('test')
|
|
219
|
+
expect(results).toEqual(['result1', 'result2'])
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
describe('type safety', () => {
|
|
224
|
+
it('should enforce correct event argument types', async () => {
|
|
225
|
+
type Events = {
|
|
226
|
+
userCreated: [string, number]
|
|
227
|
+
orderPlaced: [{ id: string; total: number }]
|
|
228
|
+
empty: []
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const emitter = await container.get(EventEmitter<Events>)
|
|
232
|
+
const userListener = vi.fn()
|
|
233
|
+
const orderListener = vi.fn()
|
|
234
|
+
const emptyListener = vi.fn()
|
|
235
|
+
|
|
236
|
+
emitter.on('userCreated', userListener)
|
|
237
|
+
emitter.on('orderPlaced', orderListener)
|
|
238
|
+
emitter.on('empty', emptyListener)
|
|
239
|
+
|
|
240
|
+
await emitter.emit('userCreated', 'user123', 25)
|
|
241
|
+
await emitter.emit('orderPlaced', { id: 'order1', total: 100 })
|
|
242
|
+
await emitter.emit('empty')
|
|
243
|
+
|
|
244
|
+
expect(userListener).toHaveBeenCalledWith('user123', 25)
|
|
245
|
+
expect(orderListener).toHaveBeenCalledWith({ id: 'order1', total: 100 })
|
|
246
|
+
expect(emptyListener).toHaveBeenCalledWith()
|
|
247
|
+
})
|
|
140
248
|
})
|
|
141
249
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
250
|
+
describe('error handling', () => {
|
|
251
|
+
it('should propagate errors from sync listeners', async () => {
|
|
252
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
253
|
+
|
|
254
|
+
emitter.on('test', () => {
|
|
255
|
+
throw new Error('Sync error')
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
await expect(emitter.emit('test')).rejects.toThrow('Sync error')
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it('should propagate errors from async listeners', async () => {
|
|
262
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
146
263
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
264
|
+
emitter.on('test', async () => {
|
|
265
|
+
throw new Error('Async error')
|
|
266
|
+
})
|
|
150
267
|
|
|
151
|
-
|
|
268
|
+
await expect(emitter.emit('test')).rejects.toThrow('Async error')
|
|
269
|
+
})
|
|
152
270
|
})
|
|
153
271
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
272
|
+
describe('memory management', () => {
|
|
273
|
+
it('should allow garbage collection after unsubscribe', async () => {
|
|
274
|
+
const emitter = await container.get(EventEmitter<{ test: [] }>)
|
|
157
275
|
|
|
158
|
-
|
|
159
|
-
|
|
276
|
+
// Add and remove many listeners
|
|
277
|
+
for (let i = 0; i < 100; i++) {
|
|
278
|
+
const unsubscribe = emitter.on('test', () => {})
|
|
279
|
+
unsubscribe()
|
|
280
|
+
}
|
|
160
281
|
|
|
161
|
-
|
|
282
|
+
// The event should be cleaned up (empty listeners set removed)
|
|
283
|
+
await emitter.emit('test')
|
|
284
|
+
})
|
|
162
285
|
})
|
|
163
286
|
})
|