@navios/di 0.2.0 → 0.3.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/README.md +301 -39
- package/docs/README.md +122 -49
- package/docs/api-reference.md +763 -0
- package/docs/container.md +274 -0
- package/docs/examples/basic-usage.mts +97 -0
- package/docs/examples/factory-pattern.mts +318 -0
- package/docs/examples/injection-tokens.mts +225 -0
- package/docs/examples/request-scope-example.mts +254 -0
- package/docs/examples/service-lifecycle.mts +359 -0
- package/docs/factory.md +584 -0
- package/docs/getting-started.md +308 -0
- package/docs/injectable.md +496 -0
- package/docs/injection-tokens.md +400 -0
- package/docs/lifecycle.md +539 -0
- package/docs/scopes.md +749 -0
- package/lib/_tsup-dts-rollup.d.mts +495 -150
- package/lib/_tsup-dts-rollup.d.ts +495 -150
- package/lib/index.d.mts +26 -12
- package/lib/index.d.ts +26 -12
- package/lib/index.js +993 -462
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +983 -453
- package/lib/index.mjs.map +1 -1
- package/package.json +2 -2
- package/project.json +10 -2
- package/src/__tests__/container.spec.mts +1301 -0
- package/src/__tests__/factory.spec.mts +137 -0
- package/src/__tests__/injectable.spec.mts +32 -88
- package/src/__tests__/injection-token.spec.mts +333 -17
- package/src/__tests__/request-scope.spec.mts +263 -0
- package/src/__type-tests__/factory.spec-d.mts +65 -0
- package/src/__type-tests__/inject.spec-d.mts +27 -28
- package/src/__type-tests__/injectable.spec-d.mts +42 -206
- package/src/container.mts +167 -0
- package/src/decorators/factory.decorator.mts +79 -0
- package/src/decorators/index.mts +1 -0
- package/src/decorators/injectable.decorator.mts +6 -56
- package/src/enums/injectable-scope.enum.mts +5 -1
- package/src/event-emitter.mts +18 -20
- package/src/factory-context.mts +2 -10
- package/src/index.mts +3 -2
- package/src/injection-token.mts +24 -9
- package/src/injector.mts +8 -20
- package/src/interfaces/factory.interface.mts +3 -3
- package/src/interfaces/index.mts +2 -0
- package/src/interfaces/on-service-destroy.interface.mts +3 -0
- package/src/interfaces/on-service-init.interface.mts +3 -0
- package/src/registry.mts +7 -16
- package/src/request-context-holder.mts +145 -0
- package/src/service-instantiator.mts +158 -0
- package/src/service-locator-event-bus.mts +0 -28
- package/src/service-locator-instance-holder.mts +27 -16
- package/src/service-locator-manager.mts +84 -0
- package/src/service-locator.mts +550 -395
- package/src/utils/defer.mts +73 -0
- package/src/utils/get-injectors.mts +93 -80
- package/src/utils/index.mts +2 -0
- package/src/utils/types.mts +52 -0
- package/docs/concepts/injectable.md +0 -182
- package/docs/concepts/injection-token.md +0 -145
- package/src/proxy-service-locator.mts +0 -83
- package/src/resolve-service.mts +0 -41
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest'
|
|
2
|
+
import { z } from 'zod/v4'
|
|
3
|
+
|
|
4
|
+
import { Factory } from '../decorators/index.mjs'
|
|
5
|
+
import { InjectableScope } from '../enums/index.mjs'
|
|
6
|
+
import { Container, Registry } from '../index.mjs'
|
|
7
|
+
import { InjectionToken } from '../injection-token.mjs'
|
|
8
|
+
|
|
9
|
+
describe('Factory decorator', () => {
|
|
10
|
+
let container: Container
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
container = new Container()
|
|
13
|
+
})
|
|
14
|
+
it('should work with factory', async () => {
|
|
15
|
+
@Factory()
|
|
16
|
+
class Test {
|
|
17
|
+
create() {
|
|
18
|
+
return 'foo'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const value = await container.get(Test)
|
|
23
|
+
expect(value).toBe('foo')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should work with request scope', async () => {
|
|
27
|
+
@Factory({
|
|
28
|
+
scope: InjectableScope.Transient,
|
|
29
|
+
})
|
|
30
|
+
class Test {
|
|
31
|
+
create() {
|
|
32
|
+
return Date.now()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const value = await container.get(Test)
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
38
|
+
const value2 = await container.get(Test)
|
|
39
|
+
expect(value).not.toBe(value2)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should work with factory injection token and schema', async () => {
|
|
43
|
+
class TestFoo {
|
|
44
|
+
constructor(public readonly foo: string) {}
|
|
45
|
+
}
|
|
46
|
+
const token = InjectionToken.create(
|
|
47
|
+
TestFoo,
|
|
48
|
+
z.object({
|
|
49
|
+
foo: z.string(),
|
|
50
|
+
}),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@Factory({ token })
|
|
54
|
+
// oxlint-disable-next-line no-unused-vars
|
|
55
|
+
class Test {
|
|
56
|
+
create(ctx: any, args: { foo: string }) {
|
|
57
|
+
return new TestFoo(args.foo)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const value = await container.get(token, { foo: 'bar' })
|
|
62
|
+
const sameValue = await container.get(token, { foo: 'bar' })
|
|
63
|
+
const differentValue = await container.get(token, { foo: 'baz' })
|
|
64
|
+
// await new Promise((resolve) => setTimeout(resolve, 10))
|
|
65
|
+
expect(value).toBeInstanceOf(TestFoo)
|
|
66
|
+
expect(value.foo).toBe('bar')
|
|
67
|
+
expect(differentValue).toBeInstanceOf(TestFoo)
|
|
68
|
+
expect(differentValue.foo).toBe('baz')
|
|
69
|
+
expect(value).not.toBe(differentValue)
|
|
70
|
+
expect(value).toBe(sameValue)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('should work with factory and inner inject', async () => {
|
|
74
|
+
@Factory()
|
|
75
|
+
class TestFactory {
|
|
76
|
+
create() {
|
|
77
|
+
return {
|
|
78
|
+
makeFoo: () => 'foo',
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Factory()
|
|
84
|
+
class Test2Factory {
|
|
85
|
+
create() {
|
|
86
|
+
return {
|
|
87
|
+
async makeFoo() {
|
|
88
|
+
const instance = await container.get(TestFactory)
|
|
89
|
+
return instance.makeFoo()
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const instance = await container.get(Test2Factory)
|
|
96
|
+
const result = await instance.makeFoo()
|
|
97
|
+
expect(result).toBe('foo')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should work with factory and custom registry', async () => {
|
|
101
|
+
const registry = new Registry()
|
|
102
|
+
const newContainer = new Container(registry)
|
|
103
|
+
|
|
104
|
+
@Factory({ registry })
|
|
105
|
+
class TestFactory {
|
|
106
|
+
create() {
|
|
107
|
+
return 'custom-registry-foo'
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const value = await newContainer.get(TestFactory)
|
|
112
|
+
expect(value).toBe('custom-registry-foo')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should work with factory and invalidation', async () => {
|
|
116
|
+
@Factory()
|
|
117
|
+
class TestFactory {
|
|
118
|
+
create() {
|
|
119
|
+
return Date.now()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const factory = await container.get(TestFactory)
|
|
124
|
+
const result1 = factory
|
|
125
|
+
const inst2 = await container.get(TestFactory)
|
|
126
|
+
expect(factory).toBe(inst2)
|
|
127
|
+
const result2 = inst2
|
|
128
|
+
await container.invalidate(factory)
|
|
129
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
130
|
+
const inst3 = await container.get(TestFactory)
|
|
131
|
+
expect(factory).not.toBe(inst3)
|
|
132
|
+
const result3 = inst3
|
|
133
|
+
expect(result1).not.toBe(result3)
|
|
134
|
+
expect(result2).not.toBe(result3)
|
|
135
|
+
expect(result1).toBe(result2)
|
|
136
|
+
})
|
|
137
|
+
})
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest'
|
|
2
2
|
import { z } from 'zod/v4'
|
|
3
3
|
|
|
4
4
|
import { Injectable } from '../decorators/index.mjs'
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
asyncInject,
|
|
7
|
+
Container,
|
|
8
|
+
inject,
|
|
9
|
+
InjectableScope,
|
|
10
|
+
Registry,
|
|
11
|
+
} from '../index.mjs'
|
|
7
12
|
import { InjectionToken } from '../injection-token.mjs'
|
|
8
|
-
import { getGlobalServiceLocator, inject } from '../injector.mjs'
|
|
9
|
-
import { getInjectableToken, getInjectors } from '../utils/index.mjs'
|
|
10
13
|
|
|
11
14
|
describe('Injectable decorator', () => {
|
|
15
|
+
let container: Container
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
container = new Container()
|
|
18
|
+
})
|
|
12
19
|
it('should work with class', async () => {
|
|
13
20
|
@Injectable()
|
|
14
21
|
class Test {}
|
|
15
22
|
|
|
16
|
-
const value = await
|
|
23
|
+
const value = await container.get(Test)
|
|
17
24
|
expect(value).toBeInstanceOf(Test)
|
|
18
25
|
})
|
|
19
26
|
|
|
@@ -27,7 +34,7 @@ describe('Injectable decorator', () => {
|
|
|
27
34
|
|
|
28
35
|
@Injectable()
|
|
29
36
|
class Test2 {
|
|
30
|
-
fooMaker =
|
|
37
|
+
fooMaker = asyncInject(Test)
|
|
31
38
|
|
|
32
39
|
async makeFoo() {
|
|
33
40
|
const fooMaker = await this.fooMaker
|
|
@@ -35,106 +42,46 @@ describe('Injectable decorator', () => {
|
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
const value = await
|
|
45
|
+
const value = await container.get(Test2)
|
|
39
46
|
expect(value).toBeInstanceOf(Test2)
|
|
40
47
|
const result = await value.makeFoo()
|
|
41
48
|
expect(result).toBe('foo')
|
|
42
49
|
})
|
|
43
50
|
|
|
44
|
-
it('should work with factory', async () => {
|
|
45
|
-
@Injectable({ type: InjectableType.Factory })
|
|
46
|
-
class Test {
|
|
47
|
-
create() {
|
|
48
|
-
return 'foo'
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const value = await inject(Test)
|
|
53
|
-
expect(value).toBe('foo')
|
|
54
|
-
})
|
|
55
|
-
it('should work with request scope', async () => {
|
|
56
|
-
@Injectable({
|
|
57
|
-
scope: InjectableScope.Instance,
|
|
58
|
-
type: InjectableType.Factory,
|
|
59
|
-
})
|
|
60
|
-
class Test {
|
|
61
|
-
create() {
|
|
62
|
-
return Date.now()
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const value = await inject(Test)
|
|
67
|
-
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
68
|
-
const value2 = await inject(Test)
|
|
69
|
-
expect(value).not.toBe(value2)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
51
|
it('should work with injection token', async () => {
|
|
73
52
|
const token = InjectionToken.create('Test')
|
|
74
53
|
|
|
75
54
|
@Injectable({ token })
|
|
76
55
|
class Test {}
|
|
77
56
|
|
|
78
|
-
const value = await
|
|
57
|
+
const value = await container.get(token)
|
|
79
58
|
expect(value).toBeInstanceOf(Test)
|
|
80
59
|
})
|
|
81
60
|
|
|
82
|
-
it('should work with factory injection token and schema', async () => {
|
|
83
|
-
class TestFoo {
|
|
84
|
-
constructor(public readonly foo: string) {}
|
|
85
|
-
}
|
|
86
|
-
const token = InjectionToken.create(
|
|
87
|
-
TestFoo,
|
|
88
|
-
z.object({
|
|
89
|
-
foo: z.string(),
|
|
90
|
-
}),
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
@Injectable({ token, type: InjectableType.Factory })
|
|
94
|
-
class Test {
|
|
95
|
-
create(ctx: any, args: { foo: string }) {
|
|
96
|
-
return new TestFoo(args.foo)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const value = await inject(token, { foo: 'bar' })
|
|
101
|
-
const differentValue = await inject(token, { foo: 'baz' })
|
|
102
|
-
const sameValue = await inject(token, { foo: 'bar' })
|
|
103
|
-
expect(value).toBeInstanceOf(TestFoo)
|
|
104
|
-
expect(value.foo).toBe('bar')
|
|
105
|
-
expect(differentValue).toBeInstanceOf(TestFoo)
|
|
106
|
-
expect(differentValue.foo).toBe('baz')
|
|
107
|
-
expect(value).not.toBe(differentValue)
|
|
108
|
-
expect(value).toBe(sameValue)
|
|
109
|
-
})
|
|
110
61
|
it('should work with invalidation', async () => {
|
|
111
|
-
@Injectable()
|
|
62
|
+
@Injectable({ scope: InjectableScope.Transient })
|
|
112
63
|
class Test {
|
|
113
64
|
value = Date.now()
|
|
114
65
|
}
|
|
115
66
|
|
|
116
67
|
@Injectable()
|
|
117
68
|
class Test2 {
|
|
118
|
-
test =
|
|
69
|
+
test = asyncInject(Test)
|
|
119
70
|
|
|
120
71
|
async makeFoo() {
|
|
121
72
|
const test = await this.test
|
|
122
73
|
return test.value
|
|
123
74
|
}
|
|
124
75
|
}
|
|
125
|
-
const
|
|
126
|
-
getInjectableToken(Test),
|
|
127
|
-
undefined,
|
|
128
|
-
)
|
|
129
|
-
const inst1 = await inject(Test2)
|
|
76
|
+
const inst1 = await container.get(Test2)
|
|
130
77
|
expect(inst1).toBeInstanceOf(Test2)
|
|
131
78
|
const result1 = await inst1.makeFoo()
|
|
132
|
-
const inst2 = await
|
|
79
|
+
const inst2 = await container.get(Test2)
|
|
133
80
|
expect(inst1).toBe(inst2)
|
|
134
81
|
const result2 = await inst2.makeFoo()
|
|
135
|
-
await
|
|
82
|
+
await container.invalidate(inst1)
|
|
136
83
|
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
137
|
-
const inst3 = await
|
|
84
|
+
const inst3 = await container.get(Test2)
|
|
138
85
|
expect(inst1).not.toBe(inst3)
|
|
139
86
|
const result3 = await inst3.makeFoo()
|
|
140
87
|
expect(result1).not.toBe(result3)
|
|
@@ -142,7 +89,7 @@ describe('Injectable decorator', () => {
|
|
|
142
89
|
expect(result1).toBe(result2)
|
|
143
90
|
})
|
|
144
91
|
|
|
145
|
-
it('should work with
|
|
92
|
+
it('should work with inject', async () => {
|
|
146
93
|
@Injectable()
|
|
147
94
|
class Test {
|
|
148
95
|
value = Date.now()
|
|
@@ -150,16 +97,16 @@ describe('Injectable decorator', () => {
|
|
|
150
97
|
|
|
151
98
|
@Injectable()
|
|
152
99
|
class Test2 {
|
|
153
|
-
test =
|
|
100
|
+
test = inject(Test)
|
|
154
101
|
|
|
155
102
|
makeFoo() {
|
|
156
103
|
return this.test.value
|
|
157
104
|
}
|
|
158
105
|
}
|
|
159
|
-
const inst1 = await
|
|
106
|
+
const inst1 = await container.get(Test2)
|
|
160
107
|
expect(inst1).toBeInstanceOf(Test2)
|
|
161
108
|
const result1 = inst1.makeFoo()
|
|
162
|
-
const inst2 = await
|
|
109
|
+
const inst2 = await container.get(Test2)
|
|
163
110
|
expect(inst1).toBe(inst2)
|
|
164
111
|
const result2 = await inst2.makeFoo()
|
|
165
112
|
expect(result1).toBe(result2)
|
|
@@ -177,7 +124,7 @@ describe('Injectable decorator', () => {
|
|
|
177
124
|
constructor(public readonly arg: z.output<typeof schema>) {}
|
|
178
125
|
}
|
|
179
126
|
|
|
180
|
-
const value = await
|
|
127
|
+
const value = await container.get(token, { foo: 'bar' })
|
|
181
128
|
expect(value).toBeInstanceOf(Test)
|
|
182
129
|
// @ts-expect-error It's a test
|
|
183
130
|
expect(value.arg).toEqual({ foo: 'bar' })
|
|
@@ -196,16 +143,13 @@ describe('Injectable decorator', () => {
|
|
|
196
143
|
foo: 'bar',
|
|
197
144
|
})
|
|
198
145
|
|
|
199
|
-
const value = await
|
|
146
|
+
const value = await container.get(boundToken)
|
|
200
147
|
expect(value).toBeInstanceOf(Test)
|
|
201
148
|
})
|
|
202
149
|
|
|
203
|
-
it('should work with bound injection token', async () => {
|
|
150
|
+
it('should work with bound injection token and custom registry', async () => {
|
|
204
151
|
const registry = new Registry()
|
|
205
|
-
const
|
|
206
|
-
const { inject, syncInject } = getInjectors({
|
|
207
|
-
baseLocator: newServiceLocator,
|
|
208
|
-
})
|
|
152
|
+
const newContainer = new Container(registry, console)
|
|
209
153
|
const schema = z.object({
|
|
210
154
|
foo: z.string(),
|
|
211
155
|
})
|
|
@@ -219,10 +163,10 @@ describe('Injectable decorator', () => {
|
|
|
219
163
|
|
|
220
164
|
@Injectable({ registry })
|
|
221
165
|
class TestOuter {
|
|
222
|
-
foo =
|
|
166
|
+
foo = inject(boundToken)
|
|
223
167
|
}
|
|
224
168
|
|
|
225
|
-
const value = await
|
|
169
|
+
const value = await newContainer.get(TestOuter)
|
|
226
170
|
expect(value).toBeInstanceOf(TestOuter)
|
|
227
171
|
expect(value.foo).toBeInstanceOf(TestInner)
|
|
228
172
|
})
|