@navios/di 0.2.1 → 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 +299 -38
  2. package/docs/README.md +121 -48
  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 +490 -145
  17. package/lib/_tsup-dts-rollup.d.ts +490 -145
  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 +19 -4
  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 +548 -393
  55. package/src/utils/defer.mts +73 -0
  56. package/src/utils/get-injectors.mts +91 -78
  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
package/README.md CHANGED
@@ -1,6 +1,17 @@
1
1
  # Navios DI
2
2
 
3
- Navios DI is a library that implements the Dependency Injection pattern. It provides you with basic tools to create and manage your services
3
+ A powerful, type-safe dependency injection library for TypeScript applications. Navios DI provides a modern, decorator-based approach to dependency injection with support for singletons, transients, factories, injection tokens, and service lifecycle management.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Type-safe**: Full TypeScript support with compile-time type checking
8
+ - 🏗️ **Decorator-based**: Clean, declarative syntax using decorators
9
+ - 🔄 **Lifecycle Management**: Built-in support for service initialization and cleanup
10
+ - 🏭 **Factory Pattern**: Create instances using factory classes
11
+ - 🎫 **Injection Tokens**: Flexible token-based dependency resolution
12
+ - 📦 **Scoped Instances**: Singleton and transient scopes
13
+ - ⚡ **Async/Sync Injection**: Both synchronous and asynchronous dependency resolution
14
+ - 🔧 **Container API**: Simple container-based API for dependency management
4
15
 
5
16
  ## Installation
6
17
 
@@ -10,72 +21,322 @@ npm install @navios/di
10
21
  yarn add @navios/di
11
22
  ```
12
23
 
13
- ## Simple Usage example
24
+ ## Quick Start
14
25
 
15
- ```ts
16
- import { inject, Injectable, syncInject } from '@navios/di'
26
+ ### Basic Usage
27
+
28
+ ```typescript
29
+ import { Container, inject, Injectable } from '@navios/di'
17
30
 
18
31
  @Injectable()
19
- class GreeterService {
20
- getFoo(user: string): string {
21
- return `Hello ${user}`
32
+ class DatabaseService {
33
+ async connect() {
34
+ return 'Connected to database'
22
35
  }
23
36
  }
24
37
 
25
38
  @Injectable()
26
39
  class UserService {
27
- private readonly greeterService = syncInject(GreeterService)
40
+ private readonly db = inject(DatabaseService)
28
41
 
29
- greet(user: string): string {
30
- return this.greeterService.getFoo(user)
42
+ async getUsers() {
43
+ const connection = await this.db.connect()
44
+ return `Users from ${connection}`
31
45
  }
32
46
  }
33
47
 
34
- const userService = await inject(UserService)
48
+ // Using Container
49
+ const container = new Container()
50
+ const userService = await container.get(UserService)
51
+ console.log(await userService.getUsers()) // "Users from Connected to database"
52
+ ```
53
+
54
+ ## Core Concepts
55
+
56
+ ### Container
57
+
58
+ The `Container` class provides a simplified API for dependency injection:
35
59
 
36
- console.log(userService.greet('World')) // Hello World
60
+ ```typescript
61
+ import { Container } from '@navios/di'
62
+
63
+ const container = new Container()
64
+
65
+ // Get instances
66
+ const service = await container.get(MyService)
67
+
68
+ // Invalidate services and their dependencies
69
+ await container.invalidate(service)
70
+
71
+ // Wait for all pending operations
72
+ await container.ready()
37
73
  ```
38
74
 
39
- ## Usage with Injection Token
75
+ ### Injectable Decorator
40
76
 
41
- ```ts
42
- import { inject, Injectable, InjectionToken, syncInject } from '@navios/di'
77
+ The `@Injectable` decorator marks a class as injectable:
43
78
 
44
- import { z } from 'zod/v4'
79
+ ```typescript
80
+ import { Injectable, InjectableScope } from '@navios/di'
45
81
 
46
- const schema = z.object({
47
- user: z.string(),
48
- })
82
+ // Singleton (default)
83
+ @Injectable()
84
+ class SingletonService {}
85
+
86
+ // Transient (new instance each time)
87
+ @Injectable({ scope: InjectableScope.Transient })
88
+ class TransientService {}
49
89
 
50
- interface GreeterInterface {
51
- getFoo(): string
90
+ // With custom injection token
91
+ @Injectable({ token: MyToken })
92
+ class TokenizedService {}
93
+ ```
94
+
95
+ ### Injection Methods
96
+
97
+ #### `inject` - Synchronous Injection
98
+
99
+ Use `inject` for immediate access to dependencies:
100
+
101
+ ```typescript
102
+ @Injectable()
103
+ class EmailService {
104
+ sendEmail(message: string) {
105
+ return `Email sent: ${message}`
106
+ }
52
107
  }
53
108
 
54
- const token = new InjectionToken.create<GreeterInterface, typeof schema>(
55
- Symbol.for('user'),
56
- schema,
57
- )
109
+ @Injectable()
110
+ class NotificationService {
111
+ private readonly emailService = inject(EmailService)
112
+
113
+ notify(message: string) {
114
+ return this.emailService.sendEmail(message)
115
+ }
116
+ }
117
+ ```
58
118
 
59
- @Injectable({
60
- token,
119
+ #### `asyncInject` - Asynchronous Injection
120
+
121
+ Use `asyncInject` for async dependency resolution:
122
+
123
+ ```typescript
124
+ @Injectable()
125
+ class AsyncService {
126
+ private readonly emailService = asyncInject(EmailService)
127
+
128
+ async notify(message: string) {
129
+ const emailService = await this.emailService
130
+ return emailService.sendEmail(message)
131
+ }
132
+ }
133
+ ```
134
+
135
+ ### Factory Decorator
136
+
137
+ Create instances using factory classes:
138
+
139
+ ```typescript
140
+ import { Factory } from '@navios/di'
141
+
142
+ @Factory()
143
+ class DatabaseConnectionFactory {
144
+ create() {
145
+ return {
146
+ host: 'localhost',
147
+ port: 5432,
148
+ connected: true,
149
+ }
150
+ }
151
+ }
152
+
153
+ // Usage
154
+ const connection = await container.get(DatabaseConnectionFactory)
155
+ console.log(connection) // { host: 'localhost', port: 5432, connected: true }
156
+ ```
157
+
158
+ ### Service Lifecycle
159
+
160
+ Implement lifecycle hooks for initialization and cleanup:
161
+
162
+ ```typescript
163
+ import { Injectable, OnServiceDestroy, OnServiceInit } from '@navios/di'
164
+
165
+ @Injectable()
166
+ class DatabaseService implements OnServiceInit, OnServiceDestroy {
167
+ private connection: any = null
168
+
169
+ async onServiceInit() {
170
+ console.log('Initializing database connection...')
171
+ this.connection = await this.connect()
172
+ }
173
+
174
+ async onServiceDestroy() {
175
+ console.log('Closing database connection...')
176
+ if (this.connection) {
177
+ await this.connection.close()
178
+ }
179
+ }
180
+
181
+ private async connect() {
182
+ // Database connection logic
183
+ return { connected: true }
184
+ }
185
+ }
186
+ ```
187
+
188
+ ### Injection Tokens
189
+
190
+ Use injection tokens for flexible dependency resolution:
191
+
192
+ #### Basic Injection Token
193
+
194
+ ```typescript
195
+ import { Container, Injectable, InjectionToken } from '@navios/di'
196
+
197
+ import { z } from 'zod'
198
+
199
+ const configSchema = z.object({
200
+ apiUrl: z.string(),
201
+ timeout: z.number(),
61
202
  })
62
- class GreeterService {
63
- constructor(private readonly config: z.infer<typeof schema>) {}
64
203
 
65
- getFoo(): string {
66
- return `Hello ${this.config.user}`
204
+ const CONFIG_TOKEN = InjectionToken.create<Config, typeof configSchema>(
205
+ 'APP_CONFIG',
206
+ configSchema,
207
+ )
208
+
209
+ @Injectable({ token: CONFIG_TOKEN })
210
+ class ConfigService {
211
+ constructor(private config: z.infer<typeof configSchema>) {}
212
+
213
+ getApiUrl() {
214
+ return this.config.apiUrl
67
215
  }
68
216
  }
69
217
 
70
- const greetWorld = await inject(token, {
71
- user: 'World',
218
+ // Usage
219
+ const container = new Container()
220
+ const config = await container.get(CONFIG_TOKEN, {
221
+ apiUrl: 'https://api.example.com',
222
+ timeout: 5000,
223
+ })
224
+ ```
225
+
226
+ #### Bound Injection Token
227
+
228
+ Pre-bind values to injection tokens:
229
+
230
+ ```typescript
231
+ const BoundConfig = InjectionToken.bound(CONFIG_TOKEN, {
232
+ apiUrl: 'https://api.example.com',
233
+ timeout: 5000,
72
234
  })
73
- const BoundGreeterService = InjectionToken.bound(token, {
74
- user: 'Earth',
235
+
236
+ // No need to provide arguments
237
+ const container = new Container()
238
+
239
+ const config = await container.get(BoundConfig)
240
+ ```
241
+
242
+ #### Factory Injection Token
243
+
244
+ Use factories to resolve token values:
245
+
246
+ ```typescript
247
+ const FactoryConfig = InjectionToken.factory(CONFIG_TOKEN, async () => {
248
+ // Load config from environment or external source
249
+ return {
250
+ apiUrl: process.env.API_URL || 'https://api.example.com',
251
+ timeout: parseInt(process.env.TIMEOUT || '5000'),
252
+ }
75
253
  })
76
254
 
77
- const greetEarth = await inject(BoundGreeterService)
255
+ const config = await container.get(FactoryConfig)
256
+ ```
257
+
258
+ ## Advanced Usage
259
+
260
+ ### Custom Registry
78
261
 
79
- console.log(greetWorld.getFoo()) // Hello World
80
- console.log(greetEarth.getFoo()) // Hello Earth
262
+ ```typescript
263
+ import { Container, Registry } from '@navios/di'
264
+
265
+ const customRegistry = new Registry()
266
+ const container = new Container(customRegistry)
81
267
  ```
268
+
269
+ ### Error Handling
270
+
271
+ ```typescript
272
+ try {
273
+ const service = await container.get(NonExistentService)
274
+ } catch (error) {
275
+ console.error('Service not found:', error.message)
276
+ }
277
+ ```
278
+
279
+ ### Service Invalidation
280
+
281
+ ```typescript
282
+ // Invalidate a specific service and its dependencies
283
+ await container.invalidate(myService)
284
+
285
+ // The service will be recreated on next access
286
+ const newService = await container.get(MyService)
287
+ ```
288
+
289
+ ## API Reference
290
+
291
+ ### Container
292
+
293
+ - `get<T>(token: T): Promise<InstanceType<T>>` - Get an instance
294
+ - `invalidate(service: unknown): Promise<void>` - Invalidate a service
295
+ - `ready(): Promise<void>` - Wait for pending operations
296
+ - `getServiceLocator(): ServiceLocator` - Get underlying service locator
297
+
298
+ ### Injectable Decorator
299
+
300
+ - `@Injectable(options?: InjectableOptions)` - Mark class as injectable
301
+ - Options:
302
+ - `scope?: InjectableScope` - Service scope (Singleton | Transient)
303
+ - `token?: InjectionToken` - Custom injection token
304
+ - `registry?: Registry` - Custom registry
305
+
306
+ ### Factory Decorator
307
+
308
+ - `@Factory(options?: FactoryOptions)` - Mark class as factory
309
+ - Options:
310
+ - `scope?: InjectableScope` - Factory scope
311
+ - `token?: InjectionToken` - Custom injection token
312
+ - `registry?: Registry` - Custom registry
313
+
314
+ ### Injection Methods
315
+
316
+ - `inject<T>(token: T): T` - Synchronous injection
317
+ - `asyncInject<T>(token: T): Promise<T>` - Asynchronous injection
318
+
319
+ ### Injection Tokens
320
+
321
+ - `InjectionToken.create<T>(name: string | symbol): InjectionToken<T>`
322
+ - `InjectionToken.create<T, S>(name: string | symbol, schema: S): InjectionToken<T, S>`
323
+ - `InjectionToken.bound<T, S>(token: InjectionToken<T, S>, value: z.input<S>): BoundInjectionToken<T, S>`
324
+ - `InjectionToken.factory<T, S>(token: InjectionToken<T, S>, factory: () => Promise<z.input<S>>): FactoryInjectionToken<T, S>`
325
+
326
+ ### Lifecycle Interfaces
327
+
328
+ - `OnServiceInit` - Implement `onServiceInit(): Promise<void> | void`
329
+ - `OnServiceDestroy` - Implement `onServiceDestroy(): Promise<void> | void`
330
+
331
+ ## Best Practices
332
+
333
+ 1. **Use `inject` for immediate dependencies** - When you need the dependency right away
334
+ 2. **Use `asyncInject` for async dependencies** - When the dependency might not be ready yet
335
+ 3. **Implement lifecycle hooks** - For proper resource management
336
+ 4. **Use injection tokens** - For configuration and interface-based dependencies
337
+ 5. **Prefer singletons** - Unless you specifically need new instances each time
338
+ 6. **Use factories** - For complex object creation logic
339
+
340
+ ## License
341
+
342
+ MIT
package/docs/README.md CHANGED
@@ -1,81 +1,154 @@
1
- # Navios DI
1
+ # Navios DI Documentation
2
2
 
3
- Navios DI is a library that implements the Dependency Injection pattern. It provides you with basic tools to create and manage your services
3
+ Welcome to the comprehensive documentation for Navios DI, a powerful dependency injection library for TypeScript applications.
4
4
 
5
- ## Installation
5
+ ## Table of Contents
6
6
 
7
- ```bash
8
- npm install @navios/di
9
- # or
10
- yarn add @navios/di
11
- ```
7
+ - [Getting Started](./getting-started.md) - Installation and basic setup
8
+ - [Container](./container.md) - Container API and usage patterns
9
+ - [Injectable Decorator](./injectable.md) - Service registration and configuration
10
+ - [Factory Decorator](./factory.md) - Factory pattern implementation
11
+ - [Injection Tokens](./injection-tokens.md) - Token-based dependency resolution
12
+ - [Service Lifecycle](./lifecycle.md) - Initialization and cleanup hooks
13
+ - [Scopes](./scopes.md) - Singleton and transient service scopes
14
+ - [Advanced Patterns](./advanced-patterns.md) - Complex usage scenarios
15
+ - [API Reference](./api-reference.md) - Complete API documentation
16
+ - [Migration Guide](./migration.md) - Upgrading from older versions
17
+
18
+ ## Quick Links
19
+
20
+ ### Core Concepts
21
+
22
+ - **[Container](./container.md)** - The main entry point for dependency injection
23
+ - **[Injectable](./injectable.md)** - Decorator for marking classes as injectable services
24
+ - **[Factory](./factory.md)** - Decorator for creating factory classes
25
+ - **[Injection Tokens](./injection-tokens.md)** - Flexible token-based dependency resolution
26
+
27
+ ### Key Features
12
28
 
13
- ## Simple Usage example
29
+ - **Type Safety** - Full TypeScript support with compile-time checking
30
+ - **Lifecycle Management** - Built-in hooks for service initialization and cleanup
31
+ - **Multiple Scopes** - Singleton and transient service lifetimes
32
+ - **Async/Sync Injection** - Both synchronous and asynchronous dependency resolution
33
+ - **Factory Pattern** - Complex object creation with factory classes
14
34
 
15
- ```ts
16
- import { inject, Injectable, syncInject } from '@navios/di'
35
+ ### Getting Started
36
+
37
+ ```typescript
38
+ import { Container, inject, Injectable } from '@navios/di'
17
39
 
18
40
  @Injectable()
19
- class GreeterService {
20
- getFoo(user: string): string {
21
- return `Hello ${user}`
41
+ class DatabaseService {
42
+ async connect() {
43
+ return 'Connected to database'
22
44
  }
23
45
  }
24
46
 
25
47
  @Injectable()
26
48
  class UserService {
27
- private readonly greeterService = syncInject(GreeterService)
49
+ private readonly db = inject(DatabaseService)
28
50
 
29
- greet(user: string): string {
30
- return this.greeterService.getFoo(user)
51
+ async getUsers() {
52
+ const connection = await this.db.connect()
53
+ return `Users from ${connection}`
31
54
  }
32
55
  }
33
56
 
34
- const userService = await inject(UserService)
35
-
36
- console.log(userService.greet('World')) // Hello World
57
+ // Using Container
58
+ const container = new Container()
59
+ const userService = await container.get(UserService)
60
+ console.log(await userService.getUsers())
37
61
  ```
38
62
 
39
- ## Usage with Injection Token
63
+ ## Architecture Overview
40
64
 
41
- ```ts
42
- import { inject, Injectable, InjectionToken, syncInject } from '@navios/di'
65
+ Navios DI follows a modern, decorator-based architecture:
43
66
 
44
- import { z } from 'zod/v4'
67
+ 1. **Services** are marked with `@Injectable()` decorator
68
+ 2. **Dependencies** are injected using `asyncInject()` or `inject()`
69
+ 3. **Container** manages service instances and their lifecycle
70
+ 4. **Injection Tokens** provide flexible dependency resolution
71
+ 5. **Factories** handle complex object creation
45
72
 
46
- const schema = z.object({
47
- user: z.string(),
48
- })
73
+ ## Design Principles
74
+
75
+ - **Type Safety First** - Leverage TypeScript's type system for compile-time safety
76
+ - **Declarative Configuration** - Use decorators for clean, readable service definitions
77
+ - **Flexible Resolution** - Support both class-based and token-based injection
78
+ - **Lifecycle Awareness** - Built-in support for service initialization and cleanup
79
+ - **Performance Optimized** - Efficient instance management and caching
49
80
 
50
- interface GreeterInterface {
51
- getFoo(): string
81
+ ## Examples
82
+
83
+ ### Basic Service Injection
84
+
85
+ ```typescript
86
+ @Injectable()
87
+ class EmailService {
88
+ sendEmail(to: string, subject: string) {
89
+ return `Email sent to ${to}: ${subject}`
90
+ }
52
91
  }
53
92
 
54
- const token = new InjectionToken<GreeterInterface, typeof schema>(
55
- Symbol.for('user'),
56
- schema,
57
- )
93
+ @Injectable()
94
+ class NotificationService {
95
+ private readonly emailService = inject(EmailService)
96
+
97
+ notify(user: string, message: string) {
98
+ return this.emailService.sendEmail(user, `Notification: ${message}`)
99
+ }
100
+ }
101
+ ```
102
+
103
+ ### Configuration with Injection Tokens
58
104
 
59
- @Injectable({
60
- token,
105
+ ```typescript
106
+ import { z } from 'zod'
107
+
108
+ const configSchema = z.object({
109
+ apiUrl: z.string(),
110
+ timeout: z.number(),
61
111
  })
62
- class GreeterService {
63
- constructor(private readonly config: z.infer<typeof schema>) {}
64
112
 
65
- getFoo(): string {
66
- return `Hello ${this.config.user}`
113
+ const CONFIG_TOKEN = InjectionToken.create<Config, typeof configSchema>(
114
+ 'APP_CONFIG',
115
+ configSchema,
116
+ )
117
+
118
+ @Injectable({ token: CONFIG_TOKEN })
119
+ class ConfigService {
120
+ constructor(private config: z.infer<typeof configSchema>) {}
121
+
122
+ getApiUrl() {
123
+ return this.config.apiUrl
67
124
  }
68
125
  }
126
+ ```
69
127
 
70
- const greetWorld = await inject(token, {
71
- user: 'World',
72
- })
73
- const BoundGreeterService = InjectionToken.bound(token, {
74
- user: 'Earth',
75
- })
128
+ ### Service Lifecycle
129
+
130
+ ```typescript
131
+ @Injectable()
132
+ class DatabaseService implements OnServiceInit, OnServiceDestroy {
133
+ private connection: any = null
76
134
 
77
- const greetEarth = await inject(BoundGreeterService)
135
+ async onServiceInit() {
136
+ console.log('Initializing database...')
137
+ this.connection = await this.connect()
138
+ }
78
139
 
79
- console.log(greetWorld.getFoo()) // Hello World
80
- console.log(greetEarth.getFoo()) // Hello Earth
140
+ async onServiceDestroy() {
141
+ console.log('Closing database...')
142
+ if (this.connection) {
143
+ await this.connection.close()
144
+ }
145
+ }
146
+ }
81
147
  ```
148
+
149
+ ## Need Help?
150
+
151
+ - Check the [API Reference](./api-reference.md) for detailed method documentation
152
+ - Look at [Advanced Patterns](./advanced-patterns.md) for complex scenarios
153
+ - Review the [Migration Guide](./migration.md) if upgrading from older versions
154
+ - See the [Examples](./examples/) folder for complete working examples