@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.
Files changed (62) hide show
  1. package/README.md +301 -39
  2. package/docs/README.md +122 -49
  3. package/docs/api-reference.md +763 -0
  4. package/docs/container.md +274 -0
  5. package/docs/examples/basic-usage.mts +97 -0
  6. package/docs/examples/factory-pattern.mts +318 -0
  7. package/docs/examples/injection-tokens.mts +225 -0
  8. package/docs/examples/request-scope-example.mts +254 -0
  9. package/docs/examples/service-lifecycle.mts +359 -0
  10. package/docs/factory.md +584 -0
  11. package/docs/getting-started.md +308 -0
  12. package/docs/injectable.md +496 -0
  13. package/docs/injection-tokens.md +400 -0
  14. package/docs/lifecycle.md +539 -0
  15. package/docs/scopes.md +749 -0
  16. package/lib/_tsup-dts-rollup.d.mts +495 -150
  17. package/lib/_tsup-dts-rollup.d.ts +495 -150
  18. package/lib/index.d.mts +26 -12
  19. package/lib/index.d.ts +26 -12
  20. package/lib/index.js +993 -462
  21. package/lib/index.js.map +1 -1
  22. package/lib/index.mjs +983 -453
  23. package/lib/index.mjs.map +1 -1
  24. package/package.json +2 -2
  25. package/project.json +10 -2
  26. package/src/__tests__/container.spec.mts +1301 -0
  27. package/src/__tests__/factory.spec.mts +137 -0
  28. package/src/__tests__/injectable.spec.mts +32 -88
  29. package/src/__tests__/injection-token.spec.mts +333 -17
  30. package/src/__tests__/request-scope.spec.mts +263 -0
  31. package/src/__type-tests__/factory.spec-d.mts +65 -0
  32. package/src/__type-tests__/inject.spec-d.mts +27 -28
  33. package/src/__type-tests__/injectable.spec-d.mts +42 -206
  34. package/src/container.mts +167 -0
  35. package/src/decorators/factory.decorator.mts +79 -0
  36. package/src/decorators/index.mts +1 -0
  37. package/src/decorators/injectable.decorator.mts +6 -56
  38. package/src/enums/injectable-scope.enum.mts +5 -1
  39. package/src/event-emitter.mts +18 -20
  40. package/src/factory-context.mts +2 -10
  41. package/src/index.mts +3 -2
  42. package/src/injection-token.mts +24 -9
  43. package/src/injector.mts +8 -20
  44. package/src/interfaces/factory.interface.mts +3 -3
  45. package/src/interfaces/index.mts +2 -0
  46. package/src/interfaces/on-service-destroy.interface.mts +3 -0
  47. package/src/interfaces/on-service-init.interface.mts +3 -0
  48. package/src/registry.mts +7 -16
  49. package/src/request-context-holder.mts +145 -0
  50. package/src/service-instantiator.mts +158 -0
  51. package/src/service-locator-event-bus.mts +0 -28
  52. package/src/service-locator-instance-holder.mts +27 -16
  53. package/src/service-locator-manager.mts +84 -0
  54. package/src/service-locator.mts +550 -395
  55. package/src/utils/defer.mts +73 -0
  56. package/src/utils/get-injectors.mts +93 -80
  57. package/src/utils/index.mts +2 -0
  58. package/src/utils/types.mts +52 -0
  59. package/docs/concepts/injectable.md +0 -182
  60. package/docs/concepts/injection-token.md +0 -145
  61. package/src/proxy-service-locator.mts +0 -83
  62. 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 { InjectableScope, InjectableType } from '../enums/index.mjs'
6
- import { Registry, ServiceLocator, syncInject } from '../index.mjs'
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 inject(Test)
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 = inject(Test)
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 inject(Test2)
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 inject(token)
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 = inject(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 identifier = getGlobalServiceLocator().getInstanceIdentifier(
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 inject(Test2)
79
+ const inst2 = await container.get(Test2)
133
80
  expect(inst1).toBe(inst2)
134
81
  const result2 = await inst2.makeFoo()
135
- await getGlobalServiceLocator().invalidate(identifier)
82
+ await container.invalidate(inst1)
136
83
  await new Promise((resolve) => setTimeout(resolve, 10))
137
- const inst3 = await inject(Test2)
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 syncInject', async () => {
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 = syncInject(Test)
100
+ test = inject(Test)
154
101
 
155
102
  makeFoo() {
156
103
  return this.test.value
157
104
  }
158
105
  }
159
- const inst1 = await inject(Test2)
106
+ const inst1 = await container.get(Test2)
160
107
  expect(inst1).toBeInstanceOf(Test2)
161
108
  const result1 = inst1.makeFoo()
162
- const inst2 = await inject(Test2)
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 inject(token, { foo: 'bar' })
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 inject(boundToken)
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 newServiceLocator = new ServiceLocator(registry)
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 = syncInject(boundToken)
166
+ foo = inject(boundToken)
223
167
  }
224
168
 
225
- const value = await inject(TestOuter)
169
+ const value = await newContainer.get(TestOuter)
226
170
  expect(value).toBeInstanceOf(TestOuter)
227
171
  expect(value.foo).toBeInstanceOf(TestInner)
228
172
  })