@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
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,71 +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 { Injectable, inject, 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);
28
-
29
- greet(user: string): string {
30
- return this.greeterService.getFoo(user);
40
+ private readonly db = inject(DatabaseService)
41
+
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
35
57
 
36
- console.log(userService.greet('World')); // Hello World
58
+ The `Container` class provides a simplified API for dependency injection:
59
+
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 { Injectable, inject, syncInject, InjectionToken } from '@navios/di';
43
- import { z } from 'zod';
77
+ The `@Injectable` decorator marks a class as injectable:
44
78
 
45
- const schema = z.object({
46
- user: z.string(),
47
- })
79
+ ```typescript
80
+ import { Injectable, InjectableScope } from '@navios/di'
48
81
 
49
- interface GreeterInterface {
50
- getFoo(): string;
82
+ // Singleton (default)
83
+ @Injectable()
84
+ class SingletonService {}
85
+
86
+ // Transient (new instance each time)
87
+ @Injectable({ scope: InjectableScope.Transient })
88
+ class TransientService {}
89
+
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
+ }
51
107
  }
52
108
 
53
- const token = new InjectionToken.create<GreeterInterface, typeof schema>(
54
- Symbol.for('user'),
55
- schema,
56
- )
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
+ ```
118
+
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
+ }
57
180
 
58
- @Injectable({
59
- token,
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(),
60
202
  })
61
- class GreeterService {
62
- constructor(private readonly config: z.infer<typeof schema>) {}
63
203
 
64
- getFoo(): string {
65
- 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
66
215
  }
67
216
  }
68
217
 
69
- const greetWorld = await inject(token, {
70
- 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,
71
223
  })
72
- const BoundGreeterService = InjectionToken.bound(token, {
73
- user: 'Earth'
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,
74
234
  })
75
235
 
76
- const greetEarth = await inject(BoundGreeterService);
236
+ // No need to provide arguments
237
+ const container = new Container()
77
238
 
78
- console.log(greetWorld.getFoo()); // Hello World
79
- console.log(greetEarth.getFoo()); // Hello Earth
239
+ const config = await container.get(BoundConfig)
80
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
+ }
253
+ })
254
+
255
+ const config = await container.get(FactoryConfig)
256
+ ```
257
+
258
+ ## Advanced Usage
259
+
260
+ ### Custom Registry
261
+
262
+ ```typescript
263
+ import { Container, Registry } from '@navios/di'
264
+
265
+ const customRegistry = new Registry()
266
+ const container = new Container(customRegistry)
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 { Injectable, inject, 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);
28
-
29
- greet(user: string): string {
30
- return this.greeterService.getFoo(user);
49
+ private readonly db = inject(DatabaseService)
50
+
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 { Injectable, inject, syncInject, InjectionToken } from '@navios/di';
43
- import { z } from 'zod';
65
+ Navios DI follows a modern, decorator-based architecture:
44
66
 
45
- const schema = z.object({
46
- user: z.string(),
47
- })
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
48
72
 
49
- interface GreeterInterface {
50
- getFoo(): string;
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
80
+
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
+ }
51
91
  }
52
92
 
53
- const token = new InjectionToken<GreeterInterface, typeof schema>(
54
- Symbol.for('user'),
55
- schema,
56
- )
93
+ @Injectable()
94
+ class NotificationService {
95
+ private readonly emailService = inject(EmailService)
57
96
 
58
- @Injectable({
59
- token,
97
+ notify(user: string, message: string) {
98
+ return this.emailService.sendEmail(user, `Notification: ${message}`)
99
+ }
100
+ }
101
+ ```
102
+
103
+ ### Configuration with Injection Tokens
104
+
105
+ ```typescript
106
+ import { z } from 'zod'
107
+
108
+ const configSchema = z.object({
109
+ apiUrl: z.string(),
110
+ timeout: z.number(),
60
111
  })
61
- class GreeterService {
62
- constructor(private readonly config: z.infer<typeof schema>) {}
63
112
 
64
- getFoo(): string {
65
- 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
66
124
  }
67
125
  }
126
+ ```
68
127
 
69
- const greetWorld = await inject(token, {
70
- user: 'World'
71
- })
72
- const BoundGreeterService = InjectionToken.bound(token, {
73
- user: 'Earth'
74
- })
128
+ ### Service Lifecycle
75
129
 
76
- const greetEarth = await inject(BoundGreeterService);
130
+ ```typescript
131
+ @Injectable()
132
+ class DatabaseService implements OnServiceInit, OnServiceDestroy {
133
+ private connection: any = null
77
134
 
78
- console.log(greetWorld.getFoo()); // Hello World
79
- console.log(greetEarth.getFoo()); // Hello Earth
135
+ async onServiceInit() {
136
+ console.log('Initializing database...')
137
+ this.connection = await this.connect()
138
+ }
80
139
 
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