@di-framework/di-framework 2.0.4
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/LICENSE +21 -0
- package/README.md +472 -0
- package/dist/container.d.ts +136 -0
- package/dist/container.js +402 -0
- package/dist/decorators.d.ts +93 -0
- package/dist/decorators.js +128 -0
- package/dist/types.d.ts +242 -0
- package/dist/types.js +140 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Geoff Seemueller
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
# di-framework
|
|
2
|
+
|
|
3
|
+
[](https://github.com/geoffsee/di-framework/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/di-framework)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
A lightweight, type-safe Dependency Injection framework for TypeScript using decorators. This framework automatically manages service instantiation, dependency resolution, and lifecycle management.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
No external dependencies required! The framework works with SWC and TypeScript's native decorator support.
|
|
13
|
+
|
|
14
|
+
Just ensure you have:
|
|
15
|
+
- TypeScript 5.0+
|
|
16
|
+
- SWC or TypeScript compiler with `experimentalDecorators` and `emitDecoratorMetadata` enabled
|
|
17
|
+
|
|
18
|
+
The decorators are fully integrated with SWC's native support - no need for `reflect-metadata` or any other polyfill.
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Basic Service
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { Container } from 'di-framework/decorators';
|
|
26
|
+
|
|
27
|
+
@Container()
|
|
28
|
+
export class DatabaseService {
|
|
29
|
+
connect(): void {
|
|
30
|
+
console.log('Connected to database');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Service with Dependencies
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { Container, Component } from 'di-framework/decorators';
|
|
39
|
+
import { DatabaseService } from './services/DatabaseService';
|
|
40
|
+
|
|
41
|
+
@Container()
|
|
42
|
+
export class UserService {
|
|
43
|
+
@Component(DatabaseService)
|
|
44
|
+
private db!: DatabaseService;
|
|
45
|
+
|
|
46
|
+
constructor() {}
|
|
47
|
+
|
|
48
|
+
getUser(id: string) {
|
|
49
|
+
return this.db.query(`SELECT * FROM users WHERE id = '${id}'`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Note: Property injection is used for all dependencies. This works seamlessly with SWC and TypeScript's native decorator support.
|
|
55
|
+
|
|
56
|
+
### 3. Resolve Services
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { useContainer } from 'di-framework/container';
|
|
60
|
+
import { UserService } from './services/UserService';
|
|
61
|
+
|
|
62
|
+
const container = useContainer();
|
|
63
|
+
const userService = container.resolve<UserService>(UserService);
|
|
64
|
+
|
|
65
|
+
// All dependencies are automatically injected!
|
|
66
|
+
userService.getUser('123');
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## API Reference
|
|
70
|
+
|
|
71
|
+
### `@Container(options?)`
|
|
72
|
+
|
|
73
|
+
Marks a class as injectable and automatically registers it with the DI container.
|
|
74
|
+
|
|
75
|
+
**Options:**
|
|
76
|
+
- `singleton?: boolean` (default: `true`) - Create a new instance each time or reuse the same instance
|
|
77
|
+
- `container?: DIContainer` - Specify a custom container (defaults to global container)
|
|
78
|
+
- Note: Import as `import { Container as DIContainer } from 'di-framework/container'` to avoid name collision with the `@Container` decorator.
|
|
79
|
+
|
|
80
|
+
**Example:**
|
|
81
|
+
```typescript
|
|
82
|
+
@Container({ singleton: false })
|
|
83
|
+
export class RequestScopedService {
|
|
84
|
+
// New instance created for each resolution
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `@Component(target)`
|
|
89
|
+
|
|
90
|
+
Marks a constructor parameter or property for dependency injection.
|
|
91
|
+
|
|
92
|
+
**Parameters:**
|
|
93
|
+
- `target` - The class to inject or a string identifier for factory-registered services
|
|
94
|
+
|
|
95
|
+
**Example - Constructor Parameter:**
|
|
96
|
+
```typescript
|
|
97
|
+
@Container()
|
|
98
|
+
export class OrderService {
|
|
99
|
+
constructor(@Component(DatabaseService) private db: DatabaseService) {}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Example - Property Injection:**
|
|
104
|
+
```typescript
|
|
105
|
+
@Container()
|
|
106
|
+
export class ReportService {
|
|
107
|
+
@Component(DatabaseService)
|
|
108
|
+
private db: DatabaseService;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `@Telemetry(options?)`
|
|
113
|
+
|
|
114
|
+
Marks a method for telemetry tracking. When called, it emits a `telemetry` event on the container. Works with both synchronous and asynchronous methods.
|
|
115
|
+
|
|
116
|
+
**Options:**
|
|
117
|
+
- `logging?: boolean` (default: `false`) - If true, logs the method execution details (status and duration) to the console.
|
|
118
|
+
|
|
119
|
+
**Example:**
|
|
120
|
+
```typescript
|
|
121
|
+
@Container()
|
|
122
|
+
export class ApiService {
|
|
123
|
+
@Telemetry({ logging: true })
|
|
124
|
+
async fetchData(id: string) {
|
|
125
|
+
// ...
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `@TelemetryListener()`
|
|
131
|
+
|
|
132
|
+
Marks a method as a listener for telemetry events. The method will be automatically registered to the container's `telemetry` event when the service is instantiated.
|
|
133
|
+
|
|
134
|
+
**Example:**
|
|
135
|
+
```typescript
|
|
136
|
+
@Container()
|
|
137
|
+
export class MonitoringService {
|
|
138
|
+
@TelemetryListener()
|
|
139
|
+
onTelemetry(event: any) {
|
|
140
|
+
console.log(`Method ${event.className}.${event.methodName} took ${event.endTime - event.startTime}ms`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### `useContainer()`
|
|
146
|
+
|
|
147
|
+
Returns the global DI container instance.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { useContainer } from 'di-framework/container';
|
|
151
|
+
|
|
152
|
+
const container = useContainer();
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### `container.register(serviceClass, options?)`
|
|
156
|
+
|
|
157
|
+
Manually register a service class.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
container.register(UserService, { singleton: true });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### `container.registerFactory(name, factory, options?)`
|
|
164
|
+
|
|
165
|
+
Register a service using a factory function.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
container.registerFactory('config', () => ({
|
|
169
|
+
apiKey: process.env.API_KEY,
|
|
170
|
+
dbUrl: process.env.DATABASE_URL
|
|
171
|
+
}), { singleton: true });
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### `container.resolve(serviceClass)`
|
|
175
|
+
|
|
176
|
+
Resolve and get an instance of a service.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const userService = container.resolve<UserService>(UserService);
|
|
180
|
+
// or by name
|
|
181
|
+
const config = container.resolve('config');
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### `container.has(serviceClass)`
|
|
185
|
+
|
|
186
|
+
Check if a service is registered.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
if (container.has(UserService)) {
|
|
190
|
+
const service = container.resolve(UserService);
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### `container.getServiceNames()`
|
|
195
|
+
|
|
196
|
+
Get all registered service names.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
const names = container.getServiceNames();
|
|
200
|
+
console.log(names); // ['DatabaseService', 'UserService', ...]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### `container.on(event, listener)`
|
|
204
|
+
|
|
205
|
+
Subscribe to DI container lifecycle events (observer pattern).
|
|
206
|
+
|
|
207
|
+
**Events:**
|
|
208
|
+
- `registered` - fired when a class or factory is registered
|
|
209
|
+
- `resolved` - fired whenever a service is resolved (cached or fresh)
|
|
210
|
+
- `constructed` - fired when `construct()` creates a new instance
|
|
211
|
+
- `cleared` - fired when the container is cleared
|
|
212
|
+
|
|
213
|
+
**Example:**
|
|
214
|
+
```typescript
|
|
215
|
+
const unsubscribe = container.on('resolved', ({ key, fromCache }) => {
|
|
216
|
+
console.log(`Resolved ${typeof key === 'string' ? key : key.name} (fromCache=${fromCache})`);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
unsubscribe(); // stop listening
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### `container.construct(serviceClass, overrides?)`
|
|
223
|
+
|
|
224
|
+
Create a fresh instance without registering it, while still honoring dependency injection. Useful for constructor-pattern scenarios where you need to supply specific primitives/config values.
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { Component } from 'di-framework/decorators';
|
|
228
|
+
import { LoggerService } from 'di-framework/services/LoggerService';
|
|
229
|
+
|
|
230
|
+
class Greeter {
|
|
231
|
+
constructor(@Component(LoggerService) private logger: LoggerService, private greeting: string) {}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const greeter = container.construct(Greeter, { 1: 'hello world' });
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### `container.fork(options?)`
|
|
238
|
+
|
|
239
|
+
Clone the container registrations (prototype pattern) into a new container. Pass `{ carrySingletons: true }` to reuse existing singleton instances; default is to start with fresh instances.
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
const testContainer = container.fork({ carrySingletons: false });
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Advanced Examples
|
|
246
|
+
|
|
247
|
+
### Multiple Dependencies
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
@Container()
|
|
251
|
+
export class ApplicationContext {
|
|
252
|
+
constructor(
|
|
253
|
+
@Component(DatabaseService) private db: DatabaseService,
|
|
254
|
+
@Component(LoggerService) private logger: LoggerService,
|
|
255
|
+
@Component(AuthService) private auth: AuthService
|
|
256
|
+
) {}
|
|
257
|
+
|
|
258
|
+
async initialize() {
|
|
259
|
+
this.logger.log('Initializing application...');
|
|
260
|
+
await this.db.connect();
|
|
261
|
+
this.auth.setup();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Transient (Non-Singleton) Services
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
@Container({ singleton: false })
|
|
270
|
+
export class RequestContext {
|
|
271
|
+
id = Math.random().toString();
|
|
272
|
+
|
|
273
|
+
constructor(@Component(LoggerService) private logger: LoggerService) {
|
|
274
|
+
this.logger.log(`Request context created: ${this.id}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Each resolve creates a new instance
|
|
279
|
+
const ctx1 = container.resolve(RequestContext); // new instance
|
|
280
|
+
const ctx2 = container.resolve(RequestContext); // different instance
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Lifecycle Methods
|
|
284
|
+
|
|
285
|
+
Services can optionally implement lifecycle methods:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
@Container()
|
|
289
|
+
export class DatabaseService {
|
|
290
|
+
private connected = false;
|
|
291
|
+
|
|
292
|
+
setEnv(env: Record<string, any>) {
|
|
293
|
+
// Called to initialize environment-specific config
|
|
294
|
+
console.log('DB URL:', env.DATABASE_URL);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
setCtx(context: any) {
|
|
298
|
+
// Called to set execution context
|
|
299
|
+
console.log('Context:', context);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
connect() {
|
|
303
|
+
this.connected = true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Calling lifecycle methods
|
|
308
|
+
const db = container.resolve(DatabaseService);
|
|
309
|
+
db.setEnv(process.env);
|
|
310
|
+
db.setCtx({ userId: '123' });
|
|
311
|
+
db.connect();
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Factory Functions
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
container.registerFactory('apiClient', () => {
|
|
318
|
+
return new HttpClient({
|
|
319
|
+
baseUrl: process.env.API_URL,
|
|
320
|
+
timeout: 5000
|
|
321
|
+
});
|
|
322
|
+
}, { singleton: true });
|
|
323
|
+
|
|
324
|
+
// Use in services
|
|
325
|
+
@Container()
|
|
326
|
+
export class UserService {
|
|
327
|
+
constructor(@Component('apiClient') private api: any) {}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## How It Works
|
|
332
|
+
|
|
333
|
+
1. **Decoration**: When you decorate a class with `@Container()`, the decorator registers it with the global container
|
|
334
|
+
2. **Registration**: The class is stored in the container with metadata about its dependencies
|
|
335
|
+
3. **Resolution**: When you call `container.resolve(ServiceClass)`:
|
|
336
|
+
- The container creates a new instance (or returns existing singleton)
|
|
337
|
+
- It examines the constructor parameters and their types
|
|
338
|
+
- It recursively resolves each dependency
|
|
339
|
+
- Dependencies are injected into the constructor
|
|
340
|
+
- The configured instance is returned
|
|
341
|
+
4. **Caching**: Singleton instances are cached and reused
|
|
342
|
+
|
|
343
|
+
## Comparison with SAMPLE.ts
|
|
344
|
+
|
|
345
|
+
### Before (Manual - SAMPLE.ts)
|
|
346
|
+
```typescript
|
|
347
|
+
const createServerContext = (env, ctx) => {
|
|
348
|
+
if(!instanceState.member) {
|
|
349
|
+
const contextInstance = Context.create({
|
|
350
|
+
contactService: ContactService.create({}),
|
|
351
|
+
assetService: AssetService.create({}),
|
|
352
|
+
transactionService: TransactionService.create({}),
|
|
353
|
+
// ... 20+ more services manually created and wired
|
|
354
|
+
chatService: ChatService.create({
|
|
355
|
+
openAIApiKey: env.OPENAI_API_KEY,
|
|
356
|
+
// ... manual configuration
|
|
357
|
+
}),
|
|
358
|
+
});
|
|
359
|
+
instanceState.member = contextInstance;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
instanceState.member.setEnv(env);
|
|
363
|
+
instanceState.member.setCtx(ctx);
|
|
364
|
+
// ... manual dependency wiring
|
|
365
|
+
instanceState.member.knowledgeService.setAttachmentService(
|
|
366
|
+
instanceState.member.attachmentService
|
|
367
|
+
);
|
|
368
|
+
return instanceState.member;
|
|
369
|
+
};
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### After (DI Framework)
|
|
373
|
+
```typescript
|
|
374
|
+
@Container()
|
|
375
|
+
export class ApplicationContext {
|
|
376
|
+
constructor(
|
|
377
|
+
@Component(ContactService) private contactService: ContactService,
|
|
378
|
+
@Component(AssetService) private assetService: AssetService,
|
|
379
|
+
@Component(TransactionService) private transactionService: TransactionService,
|
|
380
|
+
// ... all services automatically injected
|
|
381
|
+
@Component(ChatService) private chatService: ChatService,
|
|
382
|
+
) {}
|
|
383
|
+
|
|
384
|
+
setEnv(env: Record<string, any>) {
|
|
385
|
+
// Services are already available via constructor injection
|
|
386
|
+
this.chatService.initialize(env.OPENAI_API_KEY);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
setCtx(ctx: any) {
|
|
390
|
+
// All services have access to context
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Usage
|
|
395
|
+
const container = useContainer();
|
|
396
|
+
const appContext = container.resolve(ApplicationContext);
|
|
397
|
+
appContext.setEnv(env);
|
|
398
|
+
appContext.setCtx(ctx);
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Benefits:**
|
|
402
|
+
- No manual service instantiation
|
|
403
|
+
- No manual dependency wiring
|
|
404
|
+
- Automatic singleton management
|
|
405
|
+
- Type-safe dependency resolution
|
|
406
|
+
- Easier to test (mock services simply by registering test implementations)
|
|
407
|
+
- Scales better as services grow
|
|
408
|
+
|
|
409
|
+
## Error Handling
|
|
410
|
+
|
|
411
|
+
### Circular Dependencies
|
|
412
|
+
```typescript
|
|
413
|
+
// This will be detected and throw an error:
|
|
414
|
+
@Container()
|
|
415
|
+
class ServiceA {
|
|
416
|
+
constructor(@Component(ServiceB) private b: ServiceB) {}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
@Container()
|
|
420
|
+
class ServiceB {
|
|
421
|
+
constructor(@Component(ServiceA) private a: ServiceA) {}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Error: Circular dependency detected while resolving ServiceA
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Unregistered Services
|
|
428
|
+
```typescript
|
|
429
|
+
@Container()
|
|
430
|
+
class MyService {
|
|
431
|
+
constructor(@Component(UnregisteredService) private s: UnregisteredService) {}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Error: Service 'UnregisteredService' is not registered in the DI container
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## Best Practices
|
|
438
|
+
|
|
439
|
+
1. **Mark all services with `@Container()`** - Makes dependency management explicit
|
|
440
|
+
2. **Use constructor injection** - Preferred over property injection for mandatory dependencies
|
|
441
|
+
3. **Use property injection for optional dependencies** - Keep it minimal
|
|
442
|
+
4. **No need to import reflect-metadata** - This framework uses a lightweight metadata store
|
|
443
|
+
5. **Separate service interfaces from implementations** - For easier testing
|
|
444
|
+
6. **Use singletons for stateless services** - Most services should be singletons
|
|
445
|
+
7. **Use transient (non-singleton) for stateful services** - Request/session scoped services
|
|
446
|
+
|
|
447
|
+
## Testing
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
// Create a test container
|
|
451
|
+
import { Container as DIContainer } from 'di-framework/container';
|
|
452
|
+
|
|
453
|
+
const testContainer = new DIContainer();
|
|
454
|
+
|
|
455
|
+
// Register mock implementations
|
|
456
|
+
class MockDatabaseService {
|
|
457
|
+
query() { return { mock: true }; }
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
testContainer.register(MockDatabaseService);
|
|
461
|
+
|
|
462
|
+
// Register dependencies
|
|
463
|
+
testContainer.register(UserService);
|
|
464
|
+
|
|
465
|
+
// Test the service with mocked dependencies
|
|
466
|
+
const userService = testContainer.resolve(UserService);
|
|
467
|
+
expect(userService.getUser('1')).toEqual({ mock: true });
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
## License
|
|
471
|
+
|
|
472
|
+
MIT
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Injection Container
|
|
3
|
+
*
|
|
4
|
+
* Manages service registration, dependency resolution, and instance lifecycle.
|
|
5
|
+
* Supports singleton pattern and automatic dependency injection via decorators.
|
|
6
|
+
* Works with SWC and TypeScript's native decorator support.
|
|
7
|
+
*/
|
|
8
|
+
type Constructor<T = any> = new (...args: any[]) => T;
|
|
9
|
+
type ServiceFactory<T = any> = () => T;
|
|
10
|
+
type ContainerEventPayloads = {
|
|
11
|
+
registered: {
|
|
12
|
+
key: string | Constructor;
|
|
13
|
+
singleton: boolean;
|
|
14
|
+
kind: 'class' | 'factory';
|
|
15
|
+
};
|
|
16
|
+
resolved: {
|
|
17
|
+
key: string | Constructor;
|
|
18
|
+
instance: any;
|
|
19
|
+
singleton: boolean;
|
|
20
|
+
fromCache: boolean;
|
|
21
|
+
};
|
|
22
|
+
constructed: {
|
|
23
|
+
key: Constructor;
|
|
24
|
+
instance: any;
|
|
25
|
+
overrides: Record<number, any>;
|
|
26
|
+
};
|
|
27
|
+
cleared: {
|
|
28
|
+
count: number;
|
|
29
|
+
};
|
|
30
|
+
telemetry: {
|
|
31
|
+
className: string;
|
|
32
|
+
methodName: string;
|
|
33
|
+
args: any[];
|
|
34
|
+
startTime: number;
|
|
35
|
+
endTime?: number;
|
|
36
|
+
result?: any;
|
|
37
|
+
error?: any;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
type Listener<T> = (payload: T) => void;
|
|
41
|
+
export declare const TELEMETRY_METADATA_KEY = "di:telemetry";
|
|
42
|
+
export declare const TELEMETRY_LISTENER_METADATA_KEY = "di:telemetry-listener";
|
|
43
|
+
declare function defineMetadata(key: string | symbol, value: any, target: any): void;
|
|
44
|
+
declare function getMetadata(key: string | symbol, target: any): any;
|
|
45
|
+
declare function hasMetadata(key: string | symbol, target: any): boolean;
|
|
46
|
+
declare function getOwnMetadata(key: string | symbol, target: any): any;
|
|
47
|
+
export declare class Container {
|
|
48
|
+
private services;
|
|
49
|
+
private resolutionStack;
|
|
50
|
+
private listeners;
|
|
51
|
+
/**
|
|
52
|
+
* Register a service class as injectable
|
|
53
|
+
*/
|
|
54
|
+
register<T>(serviceClass: Constructor<T>, options?: {
|
|
55
|
+
singleton?: boolean;
|
|
56
|
+
}): this;
|
|
57
|
+
/**
|
|
58
|
+
* Register a service using a factory function
|
|
59
|
+
*/
|
|
60
|
+
registerFactory<T>(name: string, factory: ServiceFactory<T>, options?: {
|
|
61
|
+
singleton?: boolean;
|
|
62
|
+
}): this;
|
|
63
|
+
/**
|
|
64
|
+
* Get or create a service instance
|
|
65
|
+
*/
|
|
66
|
+
resolve<T>(serviceClass: Constructor<T> | string): T;
|
|
67
|
+
/**
|
|
68
|
+
* Construct a new instance without registering it in the container.
|
|
69
|
+
* Supports constructor overrides for primitives/config (constructor pattern).
|
|
70
|
+
* Always returns a fresh instance (no caching).
|
|
71
|
+
*/
|
|
72
|
+
construct<T>(serviceClass: Constructor<T>, overrides?: Record<number, any>): T;
|
|
73
|
+
/**
|
|
74
|
+
* Check if a service is registered
|
|
75
|
+
*/
|
|
76
|
+
has(serviceClass: Constructor | string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Clear all registered services
|
|
79
|
+
*/
|
|
80
|
+
clear(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Get all registered service names
|
|
83
|
+
*/
|
|
84
|
+
getServiceNames(): string[];
|
|
85
|
+
/**
|
|
86
|
+
* Fork the container (prototype pattern): clone registrations into a new container.
|
|
87
|
+
* Optionally carry over existing singleton instances.
|
|
88
|
+
*/
|
|
89
|
+
fork(options?: {
|
|
90
|
+
carrySingletons?: boolean;
|
|
91
|
+
}): Container;
|
|
92
|
+
/**
|
|
93
|
+
* Subscribe to container lifecycle events (observer pattern).
|
|
94
|
+
* Returns an unsubscribe function.
|
|
95
|
+
*/
|
|
96
|
+
on<K extends keyof ContainerEventPayloads>(event: K, listener: Listener<ContainerEventPayloads[K]>): () => void;
|
|
97
|
+
/**
|
|
98
|
+
* Remove a previously registered listener
|
|
99
|
+
*/
|
|
100
|
+
off<K extends keyof ContainerEventPayloads>(event: K, listener: Listener<ContainerEventPayloads[K]>): void;
|
|
101
|
+
private emit;
|
|
102
|
+
/**
|
|
103
|
+
* Private method to instantiate a service
|
|
104
|
+
*/
|
|
105
|
+
private instantiate;
|
|
106
|
+
/**
|
|
107
|
+
* Apply telemetry tracking and listeners to an instance
|
|
108
|
+
*/
|
|
109
|
+
private applyTelemetry;
|
|
110
|
+
/**
|
|
111
|
+
* Check if a function is a class constructor
|
|
112
|
+
*/
|
|
113
|
+
private isClass;
|
|
114
|
+
/**
|
|
115
|
+
* Extract parameter names from constructor
|
|
116
|
+
*/
|
|
117
|
+
private getConstructorParamNames;
|
|
118
|
+
/**
|
|
119
|
+
* Extract parameter types from TypeScript compiled code
|
|
120
|
+
* Looks for type annotations in the compiled constructor signature
|
|
121
|
+
*/
|
|
122
|
+
private extractParamTypesFromSource;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Global DI container instance
|
|
126
|
+
*/
|
|
127
|
+
export declare const container: Container;
|
|
128
|
+
/**
|
|
129
|
+
* Get the global DI container
|
|
130
|
+
*/
|
|
131
|
+
export declare function useContainer(): Container;
|
|
132
|
+
/**
|
|
133
|
+
* Export metadata functions for use in decorators
|
|
134
|
+
* These provide a simple, reflect-metadata-free way to store and access metadata
|
|
135
|
+
*/
|
|
136
|
+
export { defineMetadata, getMetadata, hasMetadata, getOwnMetadata };
|