@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.
- 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 +490 -145
- package/lib/_tsup-dts-rollup.d.ts +490 -145
- 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 +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 +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 +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
package/docs/factory.md
ADDED
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
# Factory Decorator
|
|
2
|
+
|
|
3
|
+
The `@Factory` decorator is used to create factory classes that produce instances rather than being instances themselves. Factories are useful for complex object creation, configuration-based instantiation, and scenarios where you need to create multiple instances with different parameters.
|
|
4
|
+
|
|
5
|
+
## Basic Usage
|
|
6
|
+
|
|
7
|
+
### Simple Factory
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { Factory } from '@navios/di'
|
|
11
|
+
|
|
12
|
+
@Factory()
|
|
13
|
+
class DatabaseConnectionFactory {
|
|
14
|
+
create() {
|
|
15
|
+
return {
|
|
16
|
+
host: 'localhost',
|
|
17
|
+
port: 5432,
|
|
18
|
+
connected: true,
|
|
19
|
+
connect: () => console.log('Connected to database'),
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Usage
|
|
25
|
+
const container = new Container()
|
|
26
|
+
const connection = await container.get(DatabaseConnectionFactory)
|
|
27
|
+
console.log(connection) // { host: 'localhost', port: 5432, connected: true, connect: [Function] }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Factory with Configuration
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import type { FactoryContext } from '@navios/di'
|
|
34
|
+
|
|
35
|
+
import { Factory, InjectionToken } from '@navios/di'
|
|
36
|
+
|
|
37
|
+
import { z } from 'zod'
|
|
38
|
+
|
|
39
|
+
const configSchema = z.object({
|
|
40
|
+
host: z.string(),
|
|
41
|
+
port: z.number(),
|
|
42
|
+
database: z.string(),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const DB_CONFIG_TOKEN = InjectionToken.create<
|
|
46
|
+
DatabaseConfig,
|
|
47
|
+
typeof configSchema
|
|
48
|
+
>('DB_CONFIG', configSchema)
|
|
49
|
+
|
|
50
|
+
@Factory({ token: DB_CONFIG_TOKEN })
|
|
51
|
+
class DatabaseConnectionFactory {
|
|
52
|
+
create(ctx: FactoryContext, config: z.infer<typeof configSchema>) {
|
|
53
|
+
return {
|
|
54
|
+
host: config.host,
|
|
55
|
+
port: config.port,
|
|
56
|
+
database: config.database,
|
|
57
|
+
connected: false,
|
|
58
|
+
connect: () => {
|
|
59
|
+
console.log(
|
|
60
|
+
`Connecting to ${config.host}:${config.port}/${config.database}`,
|
|
61
|
+
)
|
|
62
|
+
return Promise.resolve()
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Usage
|
|
69
|
+
const connection = await container.get(DB_CONFIG_TOKEN, {
|
|
70
|
+
host: 'localhost',
|
|
71
|
+
port: 5432,
|
|
72
|
+
database: 'myapp',
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Factory vs Injectable
|
|
77
|
+
|
|
78
|
+
### Injectable Service
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { Injectable } from '@navios/di'
|
|
82
|
+
|
|
83
|
+
@Injectable()
|
|
84
|
+
class EmailService {
|
|
85
|
+
sendEmail(to: string, subject: string) {
|
|
86
|
+
return `Email sent to ${to}: ${subject}`
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Usage - returns the service instance
|
|
91
|
+
const emailService = await container.get(EmailService)
|
|
92
|
+
await emailService.sendEmail('user@example.com', 'Hello')
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Factory Service
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { Factory } from '@navios/di'
|
|
99
|
+
|
|
100
|
+
@Factory()
|
|
101
|
+
class EmailServiceFactory {
|
|
102
|
+
create() {
|
|
103
|
+
return {
|
|
104
|
+
sendEmail: (to: string, subject: string) => {
|
|
105
|
+
return `Email sent to ${to}: ${subject}`
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Usage - returns the result of create() method
|
|
112
|
+
const emailService = await container.get(EmailServiceFactory)
|
|
113
|
+
await emailService.sendEmail('user@example.com', 'Hello')
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Advanced Patterns
|
|
117
|
+
|
|
118
|
+
### Factory with Dependencies
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { Factory, inject, Injectable } from '@navios/di'
|
|
122
|
+
|
|
123
|
+
@Injectable()
|
|
124
|
+
class LoggerService {
|
|
125
|
+
log(message: string) {
|
|
126
|
+
console.log(`[LOG] ${message}`)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@Injectable()
|
|
131
|
+
class ConfigService {
|
|
132
|
+
getDatabaseUrl() {
|
|
133
|
+
return 'postgresql://localhost:5432/myapp'
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@Factory()
|
|
138
|
+
class DatabaseConnectionFactory {
|
|
139
|
+
private readonly logger = inject(LoggerService)
|
|
140
|
+
private readonly config = inject(ConfigService)
|
|
141
|
+
|
|
142
|
+
create() {
|
|
143
|
+
const url = this.config.getDatabaseUrl()
|
|
144
|
+
this.logger.log(`Creating database connection to ${url}`)
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
url,
|
|
148
|
+
connected: false,
|
|
149
|
+
connect: async () => {
|
|
150
|
+
this.logger.log('Connecting to database...')
|
|
151
|
+
// Simulate connection
|
|
152
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
153
|
+
this.logger.log('Database connected successfully')
|
|
154
|
+
return { connected: true }
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Factory with Multiple Configurations
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { Factory, InjectionToken } from '@navios/di'
|
|
165
|
+
|
|
166
|
+
import { z } from 'zod'
|
|
167
|
+
|
|
168
|
+
const emailConfigSchema = z.object({
|
|
169
|
+
provider: z.enum(['smtp', 'sendgrid', 'ses']),
|
|
170
|
+
apiKey: z.string(),
|
|
171
|
+
fromEmail: z.string().email(),
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
const EMAIL_CONFIG_TOKEN = InjectionToken.create<
|
|
175
|
+
EmailConfig,
|
|
176
|
+
typeof emailConfigSchema
|
|
177
|
+
>('EMAIL_CONFIG', emailConfigSchema)
|
|
178
|
+
|
|
179
|
+
@Factory({ token: EMAIL_CONFIG_TOKEN })
|
|
180
|
+
class EmailServiceFactory {
|
|
181
|
+
create(config: z.infer<typeof emailConfigSchema>) {
|
|
182
|
+
switch (config.provider) {
|
|
183
|
+
case 'smtp':
|
|
184
|
+
return new SmtpEmailService(config)
|
|
185
|
+
case 'sendgrid':
|
|
186
|
+
return new SendGridEmailService(config)
|
|
187
|
+
case 'ses':
|
|
188
|
+
return new SesEmailService(config)
|
|
189
|
+
default:
|
|
190
|
+
throw new Error(`Unsupported email provider: ${config.provider}`)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
class SmtpEmailService {
|
|
196
|
+
constructor(private config: EmailConfig) {}
|
|
197
|
+
|
|
198
|
+
async sendEmail(to: string, subject: string) {
|
|
199
|
+
return `SMTP email sent to ${to}: ${subject}`
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
class SendGridEmailService {
|
|
204
|
+
constructor(private config: EmailConfig) {}
|
|
205
|
+
|
|
206
|
+
async sendEmail(to: string, subject: string) {
|
|
207
|
+
return `SendGrid email sent to ${to}: ${subject}`
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
class SesEmailService {
|
|
212
|
+
constructor(private config: EmailConfig) {}
|
|
213
|
+
|
|
214
|
+
async sendEmail(to: string, subject: string) {
|
|
215
|
+
return `SES email sent to ${to}: ${subject}`
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Factory with Transient Scope
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { Factory, InjectableScope } from '@navios/di'
|
|
224
|
+
|
|
225
|
+
@Factory({ scope: InjectableScope.Transient })
|
|
226
|
+
class RandomIdFactory {
|
|
227
|
+
create() {
|
|
228
|
+
return {
|
|
229
|
+
id: Math.random().toString(36).substr(2, 9),
|
|
230
|
+
createdAt: new Date(),
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Each call creates a new instance
|
|
236
|
+
const id1 = await container.get(RandomIdFactory)
|
|
237
|
+
const id2 = await container.get(RandomIdFactory)
|
|
238
|
+
|
|
239
|
+
console.log(id1.id) // Different random ID
|
|
240
|
+
console.log(id2.id) // Different random ID
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Factory Context
|
|
244
|
+
|
|
245
|
+
Factories have access to a `FactoryContext` that provides additional functionality:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { Factory, Injectable } from '@navios/di'
|
|
249
|
+
|
|
250
|
+
@Injectable()
|
|
251
|
+
class DatabaseService {
|
|
252
|
+
async query(sql: string) {
|
|
253
|
+
return `Query result: ${sql}`
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@Factory()
|
|
258
|
+
class UserRepositoryFactory {
|
|
259
|
+
create(ctx: FactoryContext) {
|
|
260
|
+
// Access the service locator
|
|
261
|
+
const locator = ctx.locator
|
|
262
|
+
|
|
263
|
+
// Inject dependencies within the factory
|
|
264
|
+
const dbService = await locator.getInstance(DatabaseService)
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
async getUser(id: string) {
|
|
268
|
+
const db = await dbService
|
|
269
|
+
return db.query(`SELECT * FROM users WHERE id = ${id}`)
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
async createUser(userData: any) {
|
|
273
|
+
const db = await dbService
|
|
274
|
+
return db.query(
|
|
275
|
+
`INSERT INTO users VALUES (${JSON.stringify(userData)})`,
|
|
276
|
+
)
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Real-World Examples
|
|
284
|
+
|
|
285
|
+
### HTTP Client Factory
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import type { FactoryContext } from '@navios/di'
|
|
289
|
+
|
|
290
|
+
import { Factory, InjectionToken } from '@navios/di'
|
|
291
|
+
|
|
292
|
+
import { z } from 'zod'
|
|
293
|
+
|
|
294
|
+
const httpConfigSchema = z.object({
|
|
295
|
+
baseUrl: z.string().url(),
|
|
296
|
+
timeout: z.number().default(5000),
|
|
297
|
+
retries: z.number().default(3),
|
|
298
|
+
headers: z.record(z.string()).optional(),
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
const HTTP_CONFIG_TOKEN = InjectionToken.create<
|
|
302
|
+
HttpClientFactory,
|
|
303
|
+
typeof httpConfigSchema
|
|
304
|
+
>('HTTP_CONFIG', httpConfigSchema)
|
|
305
|
+
|
|
306
|
+
@Factory({ token: HTTP_CONFIG_TOKEN })
|
|
307
|
+
class HttpClientFactory {
|
|
308
|
+
create(ctx: FactoryContext, config: z.infer<typeof httpConfigSchema>) {
|
|
309
|
+
return {
|
|
310
|
+
baseUrl: config.baseUrl,
|
|
311
|
+
timeout: config.timeout,
|
|
312
|
+
retries: config.retries,
|
|
313
|
+
headers: config.headers || {},
|
|
314
|
+
|
|
315
|
+
async get(path: string) {
|
|
316
|
+
console.log(`GET ${config.baseUrl}${path}`)
|
|
317
|
+
return { data: `Response from ${path}` }
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
async post(path: string, data: any) {
|
|
321
|
+
console.log(`POST ${config.baseUrl}${path}`, data)
|
|
322
|
+
return { data: `Created at ${path}` }
|
|
323
|
+
},
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Usage
|
|
329
|
+
const apiClient = await container.get(HTTP_CONFIG_TOKEN, {
|
|
330
|
+
baseUrl: 'https://api.example.com',
|
|
331
|
+
timeout: 10000,
|
|
332
|
+
retries: 5,
|
|
333
|
+
headers: {
|
|
334
|
+
Authorization: 'Bearer token123',
|
|
335
|
+
'Content-Type': 'application/json',
|
|
336
|
+
},
|
|
337
|
+
})
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Cache Factory
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
import { Factory, InjectableScope } from '@navios/di'
|
|
344
|
+
|
|
345
|
+
@Factory({ scope: InjectableScope.Transient })
|
|
346
|
+
class CacheFactory {
|
|
347
|
+
create() {
|
|
348
|
+
const cache = new Map()
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
set(key: string, value: any, ttl?: number) {
|
|
352
|
+
cache.set(key, {
|
|
353
|
+
value,
|
|
354
|
+
expires: ttl ? Date.now() + ttl : null,
|
|
355
|
+
})
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
get(key: string) {
|
|
359
|
+
const item = cache.get(key)
|
|
360
|
+
if (!item) return null
|
|
361
|
+
|
|
362
|
+
if (item.expires && Date.now() > item.expires) {
|
|
363
|
+
cache.delete(key)
|
|
364
|
+
return null
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return item.value
|
|
368
|
+
},
|
|
369
|
+
|
|
370
|
+
delete(key: string) {
|
|
371
|
+
return cache.delete(key)
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
clear() {
|
|
375
|
+
cache.clear()
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
size() {
|
|
379
|
+
return cache.size
|
|
380
|
+
},
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Database Connection Pool Factory
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
import { Factory, inject, Injectable } from '@navios/di'
|
|
390
|
+
|
|
391
|
+
@Injectable()
|
|
392
|
+
class DatabaseConfigService {
|
|
393
|
+
getConfig() {
|
|
394
|
+
return {
|
|
395
|
+
host: process.env.DB_HOST || 'localhost',
|
|
396
|
+
port: parseInt(process.env.DB_PORT || '5432'),
|
|
397
|
+
database: process.env.DB_NAME || 'myapp',
|
|
398
|
+
username: process.env.DB_USER || 'postgres',
|
|
399
|
+
password: process.env.DB_PASSWORD || 'password',
|
|
400
|
+
maxConnections: parseInt(process.env.DB_MAX_CONNECTIONS || '10'),
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
@Factory()
|
|
406
|
+
class DatabasePoolFactory {
|
|
407
|
+
private readonly config = inject(DatabaseConfigService)
|
|
408
|
+
|
|
409
|
+
create() {
|
|
410
|
+
const config = this.config.getConfig()
|
|
411
|
+
|
|
412
|
+
return {
|
|
413
|
+
config,
|
|
414
|
+
connections: new Map(),
|
|
415
|
+
connectionCount: 0,
|
|
416
|
+
|
|
417
|
+
async getConnection() {
|
|
418
|
+
if (this.connectionCount >= config.maxConnections) {
|
|
419
|
+
throw new Error('Connection pool exhausted')
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const connectionId = `conn_${Date.now()}_${Math.random()}`
|
|
423
|
+
const connection = {
|
|
424
|
+
id: connectionId,
|
|
425
|
+
host: config.host,
|
|
426
|
+
port: config.port,
|
|
427
|
+
database: config.database,
|
|
428
|
+
connected: true,
|
|
429
|
+
createdAt: new Date(),
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
this.connections.set(connectionId, connection)
|
|
433
|
+
this.connectionCount++
|
|
434
|
+
|
|
435
|
+
return connection
|
|
436
|
+
},
|
|
437
|
+
|
|
438
|
+
async releaseConnection(connectionId: string) {
|
|
439
|
+
if (this.connections.has(connectionId)) {
|
|
440
|
+
this.connections.delete(connectionId)
|
|
441
|
+
this.connectionCount--
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
getStats() {
|
|
446
|
+
return {
|
|
447
|
+
totalConnections: this.connectionCount,
|
|
448
|
+
maxConnections: config.maxConnections,
|
|
449
|
+
availableConnections: config.maxConnections - this.connectionCount,
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## Best Practices
|
|
458
|
+
|
|
459
|
+
### 1. Use Factories for Complex Object Creation
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
// ✅ Good: Use factory for complex configuration
|
|
463
|
+
@Factory()
|
|
464
|
+
class EmailServiceFactory {
|
|
465
|
+
create() {
|
|
466
|
+
return {
|
|
467
|
+
sendEmail: async (to: string, subject: string, body: string) => {
|
|
468
|
+
// Complex email sending logic
|
|
469
|
+
const template = await this.loadTemplate(subject)
|
|
470
|
+
const html = await this.renderTemplate(template, body)
|
|
471
|
+
return await this.sendViaProvider(to, html)
|
|
472
|
+
},
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### 2. Use Transient Scope for Stateful Objects
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
// ✅ Good: Transient factory for stateful objects
|
|
482
|
+
@Factory({ scope: InjectableScope.Transient })
|
|
483
|
+
class UserSessionFactory {
|
|
484
|
+
create() {
|
|
485
|
+
return {
|
|
486
|
+
userId: null,
|
|
487
|
+
sessionId: Math.random().toString(36),
|
|
488
|
+
loginTime: new Date(),
|
|
489
|
+
|
|
490
|
+
login(userId: string) {
|
|
491
|
+
this.userId = userId
|
|
492
|
+
this.loginTime = new Date()
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
logout() {
|
|
496
|
+
this.userId = null
|
|
497
|
+
},
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### 3. Use Injection Tokens for Interface-Based Factories
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
// ✅ Good: Interface-based factory
|
|
507
|
+
interface PaymentProcessor {
|
|
508
|
+
processPayment(amount: number): Promise<string>
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const PAYMENT_PROCESSOR_TOKEN =
|
|
512
|
+
InjectionToken.create<PaymentProcessor>('PaymentProcessor')
|
|
513
|
+
|
|
514
|
+
@Factory({ token: PAYMENT_PROCESSOR_TOKEN })
|
|
515
|
+
class StripePaymentProcessorFactory {
|
|
516
|
+
create(): PaymentProcessor {
|
|
517
|
+
return {
|
|
518
|
+
async processPayment(amount: number) {
|
|
519
|
+
return `Processed $${amount} via Stripe`
|
|
520
|
+
},
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### 4. Handle Errors in Factory Creation
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
// ✅ Good: Error handling in factory
|
|
530
|
+
@Factory()
|
|
531
|
+
class DatabaseConnectionFactory {
|
|
532
|
+
create() {
|
|
533
|
+
try {
|
|
534
|
+
return {
|
|
535
|
+
connect: async () => {
|
|
536
|
+
// Connection logic
|
|
537
|
+
return { connected: true }
|
|
538
|
+
},
|
|
539
|
+
}
|
|
540
|
+
} catch (error) {
|
|
541
|
+
throw new Error(`Failed to create database connection: ${error.message}`)
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
## API Reference
|
|
548
|
+
|
|
549
|
+
### Factory Options
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
interface FactoryOptions {
|
|
553
|
+
scope?: InjectableScope
|
|
554
|
+
token?: InjectionToken<any, any>
|
|
555
|
+
registry?: Registry
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### Factory Context
|
|
560
|
+
|
|
561
|
+
```typescript
|
|
562
|
+
interface FactoryContext {
|
|
563
|
+
inject<T>(token: T): Promise<T>
|
|
564
|
+
locator: ServiceLocator
|
|
565
|
+
on(event: string, listener: Function): void
|
|
566
|
+
getDependencies(): any[]
|
|
567
|
+
invalidate(): Promise<void>
|
|
568
|
+
addEffect(effect: Function): void
|
|
569
|
+
setTtl(ttl: number): void
|
|
570
|
+
getTtl(): number | null
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Factory Method Signature
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
interface Factorable<T> {
|
|
578
|
+
create(): T
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
interface FactorableWithArgs<T, S> {
|
|
582
|
+
create(ctx: FactoryContext, args: z.input<S>): T
|
|
583
|
+
}
|
|
584
|
+
```
|