@navios/di 0.2.1 → 0.3.1
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 +299 -38
- package/docs/README.md +121 -48
- 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 +494 -145
- package/lib/_tsup-dts-rollup.d.ts +494 -145
- package/lib/index.d.mts +26 -12
- package/lib/index.d.ts +26 -12
- package/lib/index.js +1021 -470
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1011 -461
- 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 +427 -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 +19 -4
- 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 +174 -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 +548 -393
- package/src/utils/defer.mts +73 -0
- package/src/utils/get-injectors.mts +91 -78
- 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,274 @@
|
|
|
1
|
+
# Container
|
|
2
|
+
|
|
3
|
+
The `Container` class is the main entry point for dependency injection in Navios DI. It provides a simplified, high-level API for managing services and their dependencies.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Container wraps a `ServiceLocator` instance and provides convenient methods for:
|
|
8
|
+
|
|
9
|
+
- Getting service instances
|
|
10
|
+
- Invalidating services and their dependencies
|
|
11
|
+
- Managing service lifecycle
|
|
12
|
+
- Accessing the underlying service locator for advanced usage
|
|
13
|
+
|
|
14
|
+
## Basic Usage
|
|
15
|
+
|
|
16
|
+
### Creating a Container
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { Container } from '@navios/di'
|
|
20
|
+
|
|
21
|
+
// Create with default registry
|
|
22
|
+
const container = new Container()
|
|
23
|
+
|
|
24
|
+
// Create with custom registry
|
|
25
|
+
const customRegistry = new Registry()
|
|
26
|
+
const container = new Container(customRegistry)
|
|
27
|
+
|
|
28
|
+
// Create with custom registry and logger
|
|
29
|
+
const mockLogger = {
|
|
30
|
+
log: console.log,
|
|
31
|
+
error: console.error,
|
|
32
|
+
warn: console.warn,
|
|
33
|
+
info: console.info,
|
|
34
|
+
debug: console.debug,
|
|
35
|
+
}
|
|
36
|
+
const container = new Container(customRegistry, mockLogger)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Getting Service Instances
|
|
40
|
+
|
|
41
|
+
The `get` method is the primary way to retrieve service instances:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { Container, Injectable } from '@navios/di'
|
|
45
|
+
|
|
46
|
+
@Injectable()
|
|
47
|
+
class UserService {
|
|
48
|
+
getUsers() {
|
|
49
|
+
return ['Alice', 'Bob', 'Charlie']
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const container = new Container()
|
|
54
|
+
const userService = await container.get(UserService)
|
|
55
|
+
console.log(userService.getUsers()) // ['Alice', 'Bob', 'Charlie']
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Type-Safe Service Resolution
|
|
59
|
+
|
|
60
|
+
The Container provides full type safety for service resolution:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// Simple class injection
|
|
64
|
+
const service: UserService = await container.get(UserService)
|
|
65
|
+
|
|
66
|
+
// Injection token with schema
|
|
67
|
+
const config = await container.get(CONFIG_TOKEN, {
|
|
68
|
+
apiUrl: 'https://api.example.com',
|
|
69
|
+
timeout: 5000,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Bound injection token
|
|
73
|
+
const boundService = await container.get(BoundConfigToken)
|
|
74
|
+
|
|
75
|
+
// Factory injection token
|
|
76
|
+
const factoryService = await container.get(FactoryConfigToken)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Advanced Usage
|
|
80
|
+
|
|
81
|
+
### Service Invalidation
|
|
82
|
+
|
|
83
|
+
The `invalidate` method allows you to remove a service and its dependencies from the container:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
@Injectable()
|
|
87
|
+
class CacheService {
|
|
88
|
+
private cache = new Map()
|
|
89
|
+
|
|
90
|
+
set(key: string, value: any) {
|
|
91
|
+
this.cache.set(key, value)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get(key: string) {
|
|
95
|
+
return this.cache.get(key)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const container = new Container()
|
|
100
|
+
const cacheService = await container.get(CacheService)
|
|
101
|
+
|
|
102
|
+
// Use the service
|
|
103
|
+
cacheService.set('user:123', { name: 'Alice' })
|
|
104
|
+
|
|
105
|
+
// Invalidate the service
|
|
106
|
+
await container.invalidate(cacheService)
|
|
107
|
+
|
|
108
|
+
// Next access will create a new instance
|
|
109
|
+
const newCacheService = await container.get(CacheService)
|
|
110
|
+
console.log(newCacheService.get('user:123')) // undefined (new instance)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Waiting for Operations
|
|
114
|
+
|
|
115
|
+
The `ready` method waits for all pending operations to complete:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const container = new Container()
|
|
119
|
+
|
|
120
|
+
// Start multiple async operations
|
|
121
|
+
const promises = [
|
|
122
|
+
container.get(UserService),
|
|
123
|
+
container.get(EmailService),
|
|
124
|
+
container.get(DatabaseService),
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
// Wait for all to complete
|
|
128
|
+
await container.ready()
|
|
129
|
+
|
|
130
|
+
// All services are now ready
|
|
131
|
+
const [userService, emailService, dbService] = await Promise.all(promises)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Accessing the Service Locator
|
|
135
|
+
|
|
136
|
+
For advanced usage, you can access the underlying `ServiceLocator`:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const container = new Container()
|
|
140
|
+
const serviceLocator = container.getServiceLocator()
|
|
141
|
+
|
|
142
|
+
// Use advanced ServiceLocator methods
|
|
143
|
+
const instance = await serviceLocator.getOrThrowInstance(UserService)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Best Practices
|
|
147
|
+
|
|
148
|
+
### 1. Use Container for Application Setup
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// app.ts
|
|
152
|
+
import { Container } from '@navios/di'
|
|
153
|
+
|
|
154
|
+
async function bootstrap() {
|
|
155
|
+
const container = new Container()
|
|
156
|
+
|
|
157
|
+
// Initialize core services
|
|
158
|
+
await container.get(DatabaseService)
|
|
159
|
+
await container.get(CacheService)
|
|
160
|
+
await container.get(EmailService)
|
|
161
|
+
|
|
162
|
+
// Start the application
|
|
163
|
+
const app = await container.get(ApplicationService)
|
|
164
|
+
await app.start()
|
|
165
|
+
|
|
166
|
+
return container
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const container = await bootstrap()
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 2. Invalidate Services When Needed
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// When configuration changes
|
|
176
|
+
await container.invalidate(configService)
|
|
177
|
+
|
|
178
|
+
// When cache needs clearing
|
|
179
|
+
await container.invalidate(cacheService)
|
|
180
|
+
|
|
181
|
+
// When user logs out
|
|
182
|
+
await container.invalidate(userSessionService)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 3. Handle Errors Gracefully
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
try {
|
|
189
|
+
const service = await container.get(NonExistentService)
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (error instanceof InstanceNotFoundError) {
|
|
192
|
+
console.error('Service not registered:', error.message)
|
|
193
|
+
} else {
|
|
194
|
+
console.error('Unexpected error:', error)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 4. Use Custom Registries for Modularity
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
// Create separate registries for different modules
|
|
203
|
+
const userRegistry = new Registry()
|
|
204
|
+
const paymentRegistry = new Registry()
|
|
205
|
+
|
|
206
|
+
// Register services in their respective registries
|
|
207
|
+
@Injectable({ registry: userRegistry })
|
|
208
|
+
class UserService {}
|
|
209
|
+
|
|
210
|
+
@Injectable({ registry: paymentRegistry })
|
|
211
|
+
class PaymentService {}
|
|
212
|
+
|
|
213
|
+
// Create containers with specific registries
|
|
214
|
+
const userContainer = new Container(userRegistry)
|
|
215
|
+
const paymentContainer = new Container(paymentRegistry)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## API Reference
|
|
219
|
+
|
|
220
|
+
### Constructor
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
constructor(registry?: Registry, logger?: Console | null)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
- `registry`: Optional registry instance (defaults to global registry)
|
|
227
|
+
- `logger`: Optional logger instance for debugging
|
|
228
|
+
|
|
229
|
+
### Methods
|
|
230
|
+
|
|
231
|
+
#### `get<T>(token: T): Promise<InstanceType<T>>`
|
|
232
|
+
|
|
233
|
+
Gets a service instance from the container.
|
|
234
|
+
|
|
235
|
+
**Overloads:**
|
|
236
|
+
|
|
237
|
+
- `get<T extends ClassType>(token: T): Promise<InstanceType<T>>`
|
|
238
|
+
- `get<T, S extends InjectionTokenSchemaType>(token: InjectionToken<T, S>, args: z.input<S>): Promise<T>`
|
|
239
|
+
- `get<T>(token: InjectionToken<T, undefined>): Promise<T>`
|
|
240
|
+
- `get<T>(token: BoundInjectionToken<T, any>): Promise<T>`
|
|
241
|
+
- `get<T>(token: FactoryInjectionToken<T, any>): Promise<T>`
|
|
242
|
+
|
|
243
|
+
#### `invalidate(service: unknown): Promise<void>`
|
|
244
|
+
|
|
245
|
+
Invalidates a service and its dependencies.
|
|
246
|
+
|
|
247
|
+
#### `ready(): Promise<void>`
|
|
248
|
+
|
|
249
|
+
Waits for all pending operations to complete.
|
|
250
|
+
|
|
251
|
+
#### `getServiceLocator(): ServiceLocator`
|
|
252
|
+
|
|
253
|
+
Returns the underlying ServiceLocator instance.
|
|
254
|
+
|
|
255
|
+
## Error Handling
|
|
256
|
+
|
|
257
|
+
The Container can throw various errors:
|
|
258
|
+
|
|
259
|
+
- `InstanceNotFoundError`: When a service is not registered
|
|
260
|
+
- `InstanceExpiredError`: When a service instance has expired
|
|
261
|
+
- `InstanceDestroyingError`: When trying to access a service being destroyed
|
|
262
|
+
- `UnknownError`: For unexpected errors
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import { InstanceNotFoundError } from '@navios/di'
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const service = await container.get(UnregisteredService)
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (error instanceof InstanceNotFoundError) {
|
|
271
|
+
console.error('Service not found:', error.message)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic Usage Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates the fundamental concepts of Navios DI:
|
|
5
|
+
* - Service registration with @Injectable
|
|
6
|
+
* - Dependency injection with asyncInject and inject
|
|
7
|
+
* - Container usage
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { asyncInject, Container, inject, Injectable } from '@navios/di'
|
|
11
|
+
|
|
12
|
+
// 1. Create a simple service
|
|
13
|
+
@Injectable()
|
|
14
|
+
class LoggerService {
|
|
15
|
+
log(message: string) {
|
|
16
|
+
console.log(`[LOG] ${new Date().toISOString()} - ${message}`)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 2. Create a service that depends on LoggerService
|
|
21
|
+
@Injectable()
|
|
22
|
+
class UserService {
|
|
23
|
+
private readonly logger = inject(LoggerService)
|
|
24
|
+
|
|
25
|
+
createUser(name: string, email: string) {
|
|
26
|
+
this.logger.log(`Creating user: ${name}`)
|
|
27
|
+
return { id: Math.random().toString(36), name, email }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 3. Create a service that uses async injection
|
|
32
|
+
@Injectable()
|
|
33
|
+
class EmailService {
|
|
34
|
+
private readonly logger = asyncInject(LoggerService)
|
|
35
|
+
|
|
36
|
+
async sendEmail(to: string, subject: string, body: string) {
|
|
37
|
+
const logger = await this.logger
|
|
38
|
+
logger.log(`Sending email to ${to}`)
|
|
39
|
+
|
|
40
|
+
// Simulate email sending
|
|
41
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
42
|
+
|
|
43
|
+
return { success: true, messageId: Math.random().toString(36) }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 4. Create a service that orchestrates other services
|
|
48
|
+
@Injectable()
|
|
49
|
+
class UserRegistrationService {
|
|
50
|
+
private readonly userService = inject(UserService)
|
|
51
|
+
private readonly emailService = inject(EmailService)
|
|
52
|
+
private readonly logger = inject(LoggerService)
|
|
53
|
+
|
|
54
|
+
async registerUser(name: string, email: string) {
|
|
55
|
+
this.logger.log(`Starting user registration for ${name}`)
|
|
56
|
+
|
|
57
|
+
// Create user
|
|
58
|
+
const user = this.userService.createUser(name, email)
|
|
59
|
+
|
|
60
|
+
// Send welcome email
|
|
61
|
+
await this.emailService.sendEmail(
|
|
62
|
+
email,
|
|
63
|
+
'Welcome!',
|
|
64
|
+
`Hello ${name}, welcome to our platform!`,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
this.logger.log(`User registration completed for ${name}`)
|
|
68
|
+
|
|
69
|
+
return user
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 5. Usage example
|
|
74
|
+
async function main() {
|
|
75
|
+
console.log('=== Basic Usage Example ===\n')
|
|
76
|
+
|
|
77
|
+
// Create container
|
|
78
|
+
const container = new Container()
|
|
79
|
+
|
|
80
|
+
// Get the registration service (all dependencies will be automatically injected)
|
|
81
|
+
const registrationService = await container.get(UserRegistrationService)
|
|
82
|
+
|
|
83
|
+
// Register a user
|
|
84
|
+
const user = await registrationService.registerUser(
|
|
85
|
+
'Alice',
|
|
86
|
+
'alice@example.com',
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
console.log('\nRegistered user:', user)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Run the example
|
|
93
|
+
if (require.main === module) {
|
|
94
|
+
main().catch(console.error)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { LoggerService, UserService, EmailService, UserRegistrationService }
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import type { Factorable, FactorableWithArgs, FactoryContext } from '@navios/di'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Container,
|
|
5
|
+
Factory,
|
|
6
|
+
inject,
|
|
7
|
+
Injectable,
|
|
8
|
+
InjectableScope,
|
|
9
|
+
InjectionToken,
|
|
10
|
+
} from '@navios/di'
|
|
11
|
+
|
|
12
|
+
import { z } from 'zod'
|
|
13
|
+
|
|
14
|
+
const container = new Container()
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Factory Pattern Example
|
|
18
|
+
*
|
|
19
|
+
* This example demonstrates:
|
|
20
|
+
* - Using @Factory decorator
|
|
21
|
+
* - Factory with dependencies
|
|
22
|
+
* - Factory with configuration
|
|
23
|
+
* - Factory with different scopes
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// 1. Simple factory without dependencies
|
|
27
|
+
@Factory()
|
|
28
|
+
class RandomIdFactory {
|
|
29
|
+
create() {
|
|
30
|
+
return {
|
|
31
|
+
id: Math.random().toString(36).substr(2, 9),
|
|
32
|
+
createdAt: new Date(),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 2. Factory with dependencies
|
|
38
|
+
@Injectable()
|
|
39
|
+
class LoggerService {
|
|
40
|
+
log(message: string) {
|
|
41
|
+
console.log(`[LOG] ${message}`)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Factory()
|
|
46
|
+
class DatabaseConnectionFactory {
|
|
47
|
+
private readonly logger = inject(LoggerService)
|
|
48
|
+
|
|
49
|
+
create() {
|
|
50
|
+
this.logger.log('Creating database connection...')
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
host: 'localhost',
|
|
54
|
+
port: 5432,
|
|
55
|
+
connected: false,
|
|
56
|
+
connect: async () => {
|
|
57
|
+
this.logger.log('Connecting to database...')
|
|
58
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
59
|
+
this.logger.log('Database connected successfully')
|
|
60
|
+
return { connected: true }
|
|
61
|
+
},
|
|
62
|
+
disconnect: async () => {
|
|
63
|
+
this.logger.log('Disconnecting from database...')
|
|
64
|
+
return { connected: false }
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 3. Factory with configuration schema
|
|
71
|
+
const emailConfigSchema = z.object({
|
|
72
|
+
provider: z.enum(['smtp', 'sendgrid', 'ses']),
|
|
73
|
+
apiKey: z.string(),
|
|
74
|
+
fromEmail: z.string().email(),
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
type EmailConfig = z.infer<typeof emailConfigSchema>
|
|
78
|
+
|
|
79
|
+
interface EmailService {
|
|
80
|
+
sendEmail(
|
|
81
|
+
to: string,
|
|
82
|
+
subject: string,
|
|
83
|
+
body: string,
|
|
84
|
+
): Promise<{ success: boolean; provider: string }>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const EMAIL_CONFIG_TOKEN = InjectionToken.create<
|
|
88
|
+
EmailService,
|
|
89
|
+
typeof emailConfigSchema
|
|
90
|
+
>('EMAIL_CONFIG', emailConfigSchema)
|
|
91
|
+
|
|
92
|
+
@Factory({ token: EMAIL_CONFIG_TOKEN })
|
|
93
|
+
class EmailServiceFactory
|
|
94
|
+
implements FactorableWithArgs<EmailService, typeof emailConfigSchema>
|
|
95
|
+
{
|
|
96
|
+
create(ctx: FactoryContext, config: z.infer<typeof emailConfigSchema>) {
|
|
97
|
+
switch (config.provider) {
|
|
98
|
+
case 'smtp':
|
|
99
|
+
return new SmtpEmailService(config)
|
|
100
|
+
case 'sendgrid':
|
|
101
|
+
return new SendGridEmailService(config)
|
|
102
|
+
case 'ses':
|
|
103
|
+
return new SesEmailService(config)
|
|
104
|
+
default:
|
|
105
|
+
throw new Error(`Unsupported email provider: ${config.provider}`)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Email service implementations
|
|
111
|
+
class SmtpEmailService implements EmailService {
|
|
112
|
+
constructor(private config: EmailConfig) {}
|
|
113
|
+
|
|
114
|
+
async sendEmail(to: string, subject: string, body: string) {
|
|
115
|
+
console.log(`SMTP email sent to ${to}: ${subject}`)
|
|
116
|
+
return { success: true, provider: 'smtp' }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
class SendGridEmailService {
|
|
121
|
+
constructor(private config: EmailConfig) {}
|
|
122
|
+
|
|
123
|
+
async sendEmail(to: string, subject: string, body: string) {
|
|
124
|
+
console.log(`SendGrid email sent to ${to}: ${subject}`)
|
|
125
|
+
return { success: true, provider: 'sendgrid' }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
class SesEmailService {
|
|
130
|
+
constructor(private config: EmailConfig) {}
|
|
131
|
+
|
|
132
|
+
async sendEmail(to: string, subject: string, body: string) {
|
|
133
|
+
console.log(`SES email sent to ${to}: ${subject}`)
|
|
134
|
+
return { success: true, provider: 'ses' }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 4. Factory with transient scope
|
|
139
|
+
|
|
140
|
+
interface UserSession {
|
|
141
|
+
sessionId: string
|
|
142
|
+
userId: string | null
|
|
143
|
+
loginTime: Date
|
|
144
|
+
isActive: boolean
|
|
145
|
+
login(userId: string): void
|
|
146
|
+
logout(): void
|
|
147
|
+
getSessionInfo(): Pick<
|
|
148
|
+
UserSession,
|
|
149
|
+
'sessionId' | 'userId' | 'loginTime' | 'isActive'
|
|
150
|
+
>
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@Factory({ scope: InjectableScope.Transient })
|
|
154
|
+
class UserSessionFactory implements Factorable<UserSession> {
|
|
155
|
+
create() {
|
|
156
|
+
return {
|
|
157
|
+
sessionId: Math.random().toString(36),
|
|
158
|
+
userId: null,
|
|
159
|
+
loginTime: new Date(),
|
|
160
|
+
isActive: false,
|
|
161
|
+
|
|
162
|
+
login(userId: string) {
|
|
163
|
+
this.userId = userId
|
|
164
|
+
this.isActive = true
|
|
165
|
+
this.loginTime = new Date()
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
logout() {
|
|
169
|
+
this.userId = null
|
|
170
|
+
this.isActive = false
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
getSessionInfo() {
|
|
174
|
+
return {
|
|
175
|
+
sessionId: this.sessionId,
|
|
176
|
+
userId: this.userId,
|
|
177
|
+
loginTime: this.loginTime,
|
|
178
|
+
isActive: this.isActive,
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
} satisfies UserSession
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 5. Factory with complex object creation
|
|
186
|
+
@Injectable()
|
|
187
|
+
class ConfigService {
|
|
188
|
+
getDatabaseConfig() {
|
|
189
|
+
return {
|
|
190
|
+
host: 'localhost',
|
|
191
|
+
port: 5432,
|
|
192
|
+
database: 'myapp',
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
getCacheConfig() {
|
|
197
|
+
return {
|
|
198
|
+
host: 'localhost',
|
|
199
|
+
port: 6379,
|
|
200
|
+
ttl: 3600,
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@Factory()
|
|
206
|
+
class ApplicationContextFactory {
|
|
207
|
+
private readonly config = inject(ConfigService)
|
|
208
|
+
|
|
209
|
+
create() {
|
|
210
|
+
const dbConfig = this.config.getDatabaseConfig()
|
|
211
|
+
const cacheConfig = this.config.getCacheConfig()
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
database: {
|
|
215
|
+
...dbConfig,
|
|
216
|
+
connectionString: `postgresql://${dbConfig.host}:${dbConfig.port}/${dbConfig.database}`,
|
|
217
|
+
},
|
|
218
|
+
cache: {
|
|
219
|
+
...cacheConfig,
|
|
220
|
+
connectionString: `redis://${cacheConfig.host}:${cacheConfig.port}`,
|
|
221
|
+
},
|
|
222
|
+
environment: process.env.NODE_ENV || 'development',
|
|
223
|
+
version: '1.0.0',
|
|
224
|
+
initializedAt: new Date(),
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 6. Usage examples
|
|
230
|
+
async function demonstrateSimpleFactory() {
|
|
231
|
+
console.log('=== Simple Factory Example ===\n')
|
|
232
|
+
|
|
233
|
+
const idFactory = await container.get(RandomIdFactory)
|
|
234
|
+
console.log('Generated ID:', idFactory)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function demonstrateFactoryWithDependencies() {
|
|
238
|
+
console.log('\n=== Factory with Dependencies Example ===\n')
|
|
239
|
+
|
|
240
|
+
const dbFactory = await container.get(DatabaseConnectionFactory)
|
|
241
|
+
console.log('Database connection:', dbFactory)
|
|
242
|
+
|
|
243
|
+
const connection = await dbFactory.connect()
|
|
244
|
+
console.log('Connection result:', connection)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function demonstrateFactoryWithConfiguration() {
|
|
248
|
+
console.log('\n=== Factory with Configuration Example ===\n')
|
|
249
|
+
|
|
250
|
+
// Create different email services based on configuration
|
|
251
|
+
const smtpEmail = await container.get(EMAIL_CONFIG_TOKEN, {
|
|
252
|
+
provider: 'smtp',
|
|
253
|
+
apiKey: 'smtp_key',
|
|
254
|
+
fromEmail: 'noreply@example.com',
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
const sendgridEmail = await container.get(EMAIL_CONFIG_TOKEN, {
|
|
258
|
+
provider: 'sendgrid',
|
|
259
|
+
apiKey: 'sg_key',
|
|
260
|
+
fromEmail: 'noreply@example.com',
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
await smtpEmail.sendEmail('user@example.com', 'Test', 'Hello from SMTP')
|
|
264
|
+
await sendgridEmail.sendEmail(
|
|
265
|
+
'user@example.com',
|
|
266
|
+
'Test',
|
|
267
|
+
'Hello from SendGrid',
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function demonstrateTransientFactory() {
|
|
272
|
+
console.log('\n=== Transient Factory Example ===\n')
|
|
273
|
+
|
|
274
|
+
// Create multiple sessions
|
|
275
|
+
const session1 = await container.get(UserSessionFactory)
|
|
276
|
+
const session2 = await container.get(UserSessionFactory)
|
|
277
|
+
|
|
278
|
+
session1.login('user1')
|
|
279
|
+
session2.login('user2')
|
|
280
|
+
|
|
281
|
+
console.log('Session 1:', session1.getSessionInfo())
|
|
282
|
+
console.log('Session 2:', session2.getSessionInfo())
|
|
283
|
+
|
|
284
|
+
// Verify they are different instances
|
|
285
|
+
console.log('Different sessions:', session1.sessionId !== session2.sessionId)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async function demonstrateComplexFactory() {
|
|
289
|
+
console.log('\n=== Complex Factory Example ===\n')
|
|
290
|
+
|
|
291
|
+
const appContext = await container.get(ApplicationContextFactory)
|
|
292
|
+
console.log('Application context:', JSON.stringify(appContext, null, 2))
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Main function
|
|
296
|
+
async function main() {
|
|
297
|
+
await demonstrateSimpleFactory()
|
|
298
|
+
await demonstrateFactoryWithDependencies()
|
|
299
|
+
await demonstrateFactoryWithConfiguration()
|
|
300
|
+
await demonstrateTransientFactory()
|
|
301
|
+
await demonstrateComplexFactory()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Run the example
|
|
305
|
+
if (require.main === module) {
|
|
306
|
+
main().catch(console.error)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export {
|
|
310
|
+
RandomIdFactory,
|
|
311
|
+
DatabaseConnectionFactory,
|
|
312
|
+
EmailServiceFactory,
|
|
313
|
+
UserSessionFactory,
|
|
314
|
+
ApplicationContextFactory,
|
|
315
|
+
SmtpEmailService,
|
|
316
|
+
SendGridEmailService,
|
|
317
|
+
SesEmailService,
|
|
318
|
+
}
|