@khapu2906/treasure-chest 1.0.4 → 1.2.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 CHANGED
@@ -1,6 +1,43 @@
1
1
  # Treasure Chest
2
2
 
3
- A simple and powerful TypeScript dependency injection container.
3
+ [![npm version](https://badge.fury.io/js/@khapu2906%2Ftreasure-chest.svg)](https://www.npmjs.com/package/@khapu2906/treasure-chest)
4
+ [![CI](https://github.com/khapu2906/treasure-chest/actions/workflows/ci.yml/badge.svg)](https://github.com/khapu2906/treasure-chest/actions/workflows/ci.yml)
5
+ [![License: DIB](https://img.shields.io/badge/License-DIB-blue.svg)](./LICENSE)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
7
+ [![Node.js](https://img.shields.io/badge/Node.js-18%2B-green.svg)](https://nodejs.org/)
8
+
9
+ A lightweight and powerful TypeScript dependency injection container for managing service dependencies with support for transient, singleton, scoped, lazy, conditional, and contextual bindings.
10
+
11
+ ## Features
12
+
13
+ ### Core Features
14
+
15
+ - **Lightweight**: Zero runtime dependencies, minimal footprint
16
+ - **TypeScript First**: Full TypeScript support with complete type safety
17
+ - **Flexible Lifecycle**: Transient, singleton, and scoped bindings
18
+ - **Lazy Loading**: Deferred initialization for performance optimization
19
+ - **Container Hierarchy**: Child containers with inheritance
20
+ - **Nested Dependencies**: Automatic resolution of dependency chains
21
+ - **Circular Detection**: Automatic circular dependency detection
22
+ - **Conditional & Contextual**: Environment and context-aware bindings
23
+ - **Alias Support**: Multiple names for the same service
24
+
25
+ ### Performance (v1.2.0)
26
+
27
+ - **Map-based Storage**: O(1) lookup complexity (10-100x faster)
28
+ - **Binding Cache**: Memoized results for non-conditional bindings
29
+ - **High-Speed Resolution**: 3.67M ops/sec (worst case), 24.7M ops/sec (cached)
30
+ - **Auto-Dispose**: IDisposable interface with zero overhead
31
+ - **Modular Architecture**: Better tree-shaking and code splitting
32
+
33
+ ### Developer Experience
34
+
35
+ - **Intuitive API**: Fluent API with comprehensive JSDoc
36
+ - **Well Tested**: 62+ test cases with full coverage
37
+ - **Dual Module Support**: Both CommonJS and ES Modules (ESM)
38
+ - **Type Safe**: Full TypeScript declarations
39
+ - **Production Ready**: Battle-tested patterns
40
+ - **Modular Codebase**: Clean separation of concerns
4
41
 
5
42
  ## Installation
6
43
 
@@ -8,7 +45,7 @@ A simple and powerful TypeScript dependency injection container.
8
45
  npm install @khapu2906/treasure-chest
9
46
  ```
10
47
 
11
- ## Basic Usage
48
+ ## Quick Start
12
49
 
13
50
  ```typescript
14
51
  import { Container } from '@khapu2906/treasure-chest';
@@ -17,7 +54,7 @@ const container = new Container();
17
54
 
18
55
  // Register a service
19
56
  container.bind('logger', () => ({
20
- log: (message: string) => console.log(`[LOG] ${message}`)
57
+ log: (message: string) => console.log(`[LOG] ${message}`),
21
58
  }));
22
59
 
23
60
  // Use the service
@@ -28,6 +65,7 @@ logger.log('Hello World!');
28
65
  ## Key Features
29
66
 
30
67
  ### 1. Bind (Transient)
68
+
31
69
  Creates a new instance on each resolve:
32
70
 
33
71
  ```typescript
@@ -39,6 +77,7 @@ console.log(instance1.id !== instance2.id); // true
39
77
  ```
40
78
 
41
79
  ### 2. Singleton
80
+
42
81
  Creates only one instance:
43
82
 
44
83
  ```typescript
@@ -50,6 +89,7 @@ console.log(cache1 === cache2); // true
50
89
  ```
51
90
 
52
91
  ### 3. Alias
92
+
53
93
  Create aliases for services:
54
94
 
55
95
  ```typescript
@@ -60,49 +100,306 @@ const logger = container.resolve('appLogger'); // Same as 'logger'
60
100
  ```
61
101
 
62
102
  ### 4. Conditional Binding
103
+
63
104
  Register services based on conditions:
64
105
 
65
106
  ```typescript
66
107
  const env = 'production';
67
108
 
68
- container.bind('storage', () => ({ type: 'local' }), () => env === 'development');
69
- container.bind('storage', () => ({ type: 's3' }), () => env === 'production');
109
+ container.bind(
110
+ 'storage',
111
+ () => ({ type: 'local' }),
112
+ () => env === 'development'
113
+ );
114
+ container.bind(
115
+ 'storage',
116
+ () => ({ type: 's3' }),
117
+ () => env === 'production'
118
+ );
70
119
 
71
120
  const storage = container.resolve('storage'); // { type: 's3' }
72
121
  ```
73
122
 
74
123
  ### 5. Contextual Binding
124
+
75
125
  Register different implementations for different contexts:
76
126
 
77
127
  ```typescript
78
- container.when('UserService').needs('repository').give(() => new UserRepository());
79
- container.when('AdminService').needs('repository').give(() => new AdminRepository());
128
+ container
129
+ .when('UserService')
130
+ .needs('repository')
131
+ .give(() => new UserRepository());
132
+ container
133
+ .when('AdminService')
134
+ .needs('repository')
135
+ .give(() => new AdminRepository());
80
136
 
81
137
  const userRepo = container.resolve('repository', 'UserService'); // UserRepository
82
138
  const adminRepo = container.resolve('repository', 'AdminService'); // AdminRepository
83
139
  ```
84
140
 
141
+ ### 6. Scoped Lifecycle (NEW in v1.2.0)
142
+
143
+ Per-scope instances with automatic cleanup:
144
+
145
+ ```typescript
146
+ import { Container } from '@khapu2906/treasure-chest';
147
+
148
+ const container = new Container();
149
+
150
+ // Register scoped service with cleanup
151
+ container.scoped(
152
+ 'dbConnection',
153
+ () => new DbConnection(),
154
+ function () {
155
+ this.close();
156
+ } // Cleanup function
157
+ );
158
+
159
+ // Create a scope (e.g., per HTTP request)
160
+ const scope = container.createScope();
161
+
162
+ const conn1 = container.resolve('dbConnection');
163
+ const conn2 = container.resolve('dbConnection');
164
+ console.log(conn1 === conn2); // true - same instance within scope
165
+
166
+ // Cleanup when done
167
+ await scope.dispose(); // Calls cleanup functions
168
+ ```
169
+
170
+ ### 7. Lazy Loading (NEW in v1.2.0)
171
+
172
+ Defer expensive initialization:
173
+
174
+ ```typescript
175
+ import { Container, Lazy } from '@khapu2906/treasure-chest';
176
+
177
+ const container = new Container();
178
+
179
+ // Register lazy service
180
+ container.lazy('heavyService', () => new HeavyMLModel());
181
+
182
+ // Resolve as Lazy wrapper
183
+ const lazy = container.resolve<Lazy<HeavyMLModel>>('heavyService');
184
+
185
+ console.log(lazy.isInitialized); // false - not loaded yet
186
+
187
+ // Access when needed
188
+ const model = lazy.value; // NOW it's initialized
189
+
190
+ console.log(lazy.isInitialized); // true
191
+ ```
192
+
193
+ ### 8. Child Containers (NEW in v1.2.0)
194
+
195
+ Container hierarchy for multi-tenant apps:
196
+
197
+ ```typescript
198
+ const parent = new Container();
199
+ parent.singleton('config', () => new Config());
200
+
201
+ // Create child container
202
+ const tenant1 = parent.createChild();
203
+ tenant1.singleton('logger', () => new TenantLogger('tenant1'));
204
+
205
+ const tenant2 = parent.createChild();
206
+ tenant2.singleton('logger', () => new TenantLogger('tenant2'));
207
+
208
+ // Each tenant has its own logger but shares config
209
+ tenant1.resolve('config'); // From parent
210
+ tenant1.resolve('logger'); // From tenant1
211
+ ```
212
+
213
+ ### 9. Circular Dependency Detection (NEW in v1.2.0)
214
+
215
+ Automatic detection with clear error messages:
216
+
217
+ ```typescript
218
+ container.bind('A', (c) => {
219
+ const b = c.resolve('B');
220
+ return { name: 'A', dep: b };
221
+ });
222
+
223
+ container.bind('B', (c) => {
224
+ const a = c.resolve('A'); // Circular!
225
+ return { name: 'B', dep: a };
226
+ });
227
+
228
+ try {
229
+ container.resolve('A');
230
+ } catch (error) {
231
+ console.log(error.message);
232
+ // "Circular dependency detected: A -> B -> A"
233
+ }
234
+ ```
235
+
236
+ ### 10. IDisposable Interface & Auto-Dispose (NEW in v1.2.0)
237
+
238
+ Automatic cleanup detection with zero overhead:
239
+
240
+ ```typescript
241
+ import { Container, IDisposable } from '@khapu2906/treasure-chest';
242
+
243
+ // Implement IDisposable for auto-cleanup
244
+ class DbConnection implements IDisposable {
245
+ async connect() {
246
+ console.log('Connected');
247
+ }
248
+
249
+ async dispose() {
250
+ console.log('Disconnected');
251
+ }
252
+ }
253
+
254
+ const container = new Container();
255
+
256
+ // Auto-detection: No need to pass dispose function!
257
+ container.scoped('db', () => new DbConnection());
258
+
259
+ // Using withScope() - C# using pattern
260
+ await container.withScope(async (scope) => {
261
+ const db = container.resolve<DbConnection>('db');
262
+ await db.connect();
263
+ // Auto-disposed when scope exits
264
+ });
265
+ // Dispose called automatically here
266
+ ```
267
+
268
+ ### 11. Web Framework Integration Pattern
269
+
270
+ Pattern for automatic per-request scope management. See `examples/10-middleware.ts` for full implementation.
271
+
272
+ > **Note:** Middleware helpers are not exported from the core package to keep it framework-agnostic.
273
+ > For production use, consider creating separate adapter packages.
274
+
275
+ **Pattern Example:**
276
+
277
+ ```typescript
278
+ import { Container } from '@khapu2906/treasure-chest';
279
+
280
+ const container = new Container();
281
+ container.scoped('db', () => new DbConnection());
282
+
283
+ // Express pattern
284
+ function expressScope(container: Container) {
285
+ return (req, res, next) => {
286
+ const scope = container.createScope();
287
+ req.scope = scope;
288
+ res.on('finish', () => scope.dispose());
289
+ next();
290
+ };
291
+ }
292
+
293
+ app.use(expressScope(container));
294
+ ```
295
+
296
+ **Supported Patterns:**
297
+
298
+ - ✅ Express middleware
299
+ - ✅ Fastify hooks (onRequest/onResponse)
300
+ - ✅ Koa middleware
301
+
302
+ See [examples/10-middleware.ts](./examples/10-middleware.ts) for complete implementations.
303
+
85
304
  ## API Reference
86
305
 
87
306
  ### Container Class
88
307
 
89
- #### `bind<T>(key: string, factory: FactoryFn<T>, condition?: ConditionFn)`
90
- Register a transient service.
308
+ #### Lifecycle Methods
309
+
310
+ **`bind<T>(key: string, factory: FactoryFn<T>, condition?: ConditionFn)`**
91
311
 
92
- #### `singleton<T>(key: string, factory: FactoryFn<T>, condition?: ConditionFn)`
93
- Register a singleton service.
312
+ - Register a transient service (new instance each time)
94
313
 
95
- #### `alias(aliasKey: string, originalKey: string)`
96
- Create an alias for a service.
314
+ **`singleton<T>(key: string, factory: FactoryFn<T>, condition?: ConditionFn)`**
97
315
 
98
- #### `resolve<T>(key: string, context?: string): T`
99
- Resolve a service from the container.
316
+ - Register a singleton service (single instance shared)
100
317
 
101
- #### `when(context: string)`
102
- Start a contextual binding chain.
318
+ **`scoped<T>(key: string, factory: FactoryFn<T>, dispose?: DisposeFn)` ⭐ NEW**
103
319
 
104
- #### `reset()`
105
- Clear all bindings and instances (useful for testing).
320
+ - Register a scoped service (instance per scope)
321
+ - Optional cleanup function called on dispose
322
+
323
+ **`lazy<T>(key: string, factory: FactoryFn<T>, lifecycle?: Lifecycle)` ⭐ NEW**
324
+
325
+ - Register a lazy service (deferred initialization)
326
+ - Returns `Lazy<T>` wrapper
327
+ - Default lifecycle: singleton
328
+
329
+ #### Resolution Methods
330
+
331
+ **`resolve<T>(key: string, context?: string): T`**
332
+
333
+ - Resolve a service from the container
334
+ - Supports circular dependency detection
335
+
336
+ **`has(key: string): boolean` ⭐ NEW**
337
+
338
+ - Check if a binding exists for a key
339
+
340
+ **`keys(): string[]` ⭐ NEW**
341
+
342
+ - Get all registered service keys
343
+
344
+ #### Contextual Binding
345
+
346
+ **`when(context: string).needs(key: string).give(factory: FactoryFn)`**
347
+
348
+ - Create context-specific bindings
349
+ - Fluent API for readability
350
+
351
+ **`alias(aliasKey: string, originalKey: string)`**
352
+
353
+ - Create an alias for a service
354
+
355
+ #### Container Hierarchy
356
+
357
+ **`createChild(): Container` ⭐ NEW**
358
+
359
+ - Create a child container that inherits from parent
360
+ - Children can override parent bindings
361
+
362
+ **`createScope(): Scope` ⭐ NEW**
363
+
364
+ - Create a new scope for scoped instances
365
+ - Returns a `Scope` object
366
+
367
+ **`withScope<T>(callback: (scope: Scope) => T | Promise<T>): Promise<T>` ⭐ NEW v1.2.0**
368
+
369
+ - Execute callback with auto-managed scope
370
+ - C#-style `using` statement pattern
371
+ - Automatic disposal even on errors
372
+ - Exception-safe cleanup
373
+
374
+ #### Cleanup
375
+
376
+ **`reset()`**
377
+
378
+ - Clear all bindings and instances
379
+
380
+ **`dispose(): Promise<void>` ⭐ NEW**
381
+
382
+ - Dispose container and all scoped instances
383
+ - Calls cleanup functions
384
+
385
+ ### Exported Types
386
+
387
+ ```typescript
388
+ import {
389
+ Container,
390
+ Lazy,
391
+ Scope,
392
+ Lifecycle,
393
+ IDisposable,
394
+ DisposeFn,
395
+ } from '@khapu2906/treasure-chest';
396
+
397
+ // Lifecycle: 'transient' | 'singleton' | 'scoped'
398
+ // Lazy<T>: Wrapper with .value and .isInitialized
399
+ // Scope: Scope management with .dispose()
400
+ // IDisposable: Interface for auto-cleanup detection ⭐ NEW v1.2.0
401
+ // DisposeFn: Type for cleanup functions
402
+ ```
106
403
 
107
404
  ### Global Container
108
405
 
@@ -145,31 +442,161 @@ const container = new Container();
145
442
 
146
443
  container.singleton('db', () => new Database());
147
444
  container.singleton('userRepo', (c) => new UserRepository(c.resolve('db')));
148
- container.singleton('userService', (c) => new UserService(c.resolve('userRepo')));
445
+ container.singleton(
446
+ 'userService',
447
+ (c) => new UserService(c.resolve('userRepo'))
448
+ );
149
449
 
150
450
  // Contextual binding
151
- container.when('AdminService').needs('userRepo').give((c) => ({
152
- ...c.resolve('userRepo'),
153
- adminAccess: true
154
- }));
451
+ container
452
+ .when('AdminService')
453
+ .needs('userRepo')
454
+ .give((c) => ({
455
+ ...c.resolve('userRepo'),
456
+ adminAccess: true,
457
+ }));
155
458
 
156
459
  // Usage
157
460
  const userService = container.resolve<UserService>('userService');
158
461
  console.log(userService.getUser(1)); // { id: 1, name: 'John Doe' }
159
462
  ```
160
463
 
161
- ## Testing
464
+ ## Examples
465
+
466
+ Check out the [examples](./examples) directory for comprehensive real-world usage patterns:
467
+
468
+ ### Basic Features
469
+
470
+ 1. **Basic Usage**: Transient and singleton bindings
471
+ 2. **Dependency Injection**: Nested dependency resolution
472
+ 3. **Conditional Binding**: Environment-based configuration
473
+ 4. **Contextual Binding**: Context-aware service resolution
474
+ 5. **Alias**: Multiple names for the same service
475
+
476
+ ### Advanced Features (v1.2.0)
477
+
478
+ 6. **Scoped Lifecycle**: Per-request services with cleanup
479
+ 7. **Lazy Loading**: Performance optimization with deferred init
480
+ 8. **Child Containers**: Multi-tenant and plugin systems
481
+ 9. **Circular Dependency**: Detection and best practices
482
+
483
+ ### Performance & Integration (v1.2.0) ⭐ NEW
484
+
485
+ 10. **Web Framework Middleware**: Express, Fastify, Koa integration
486
+ - Auto-scoping per HTTP request
487
+ - IDisposable auto-detection
488
+ - withScope() pattern demos
489
+
490
+ Each example includes detailed comments and runnable code.
491
+
492
+ ## Development
493
+
494
+ ### Install Dependencies
495
+
496
+ ```bash
497
+ npm install
498
+ ```
499
+
500
+ ### Running Tests
162
501
 
163
502
  ```bash
503
+ # Run all tests
164
504
  npm test
505
+
506
+ # Run tests in watch mode
507
+ npm run test:watch
508
+
509
+ # Run tests with coverage
510
+ npm run test:coverage
511
+
512
+ # Run tests with UI
513
+ npm run test:ui
514
+ ```
515
+
516
+ ### Linting and Formatting
517
+
518
+ ```bash
519
+ # Run ESLint
520
+ npm run lint
521
+
522
+ # Fix linting issues
523
+ npm run lint:fix
524
+
525
+ # Format code with Prettier
526
+ npm run format
527
+
528
+ # Check formatting
529
+ npm run format:check
165
530
  ```
166
531
 
167
- ## Build
532
+ ### Building
168
533
 
169
534
  ```bash
535
+ # Build for production (generates CJS, ESM, and type declarations)
170
536
  npm run build
537
+
538
+ # Clean build artifacts
539
+ npm run clean
540
+ ```
541
+
542
+ ### Running Benchmarks ⭐ NEW v1.2.0
543
+
544
+ ```bash
545
+ # Run performance benchmarks
546
+ npm run benchmark
547
+
548
+ # Results auto-saved to:
549
+ # - benchmarks/results/run-YYYY-MM-DD-HH-MM-SS.json (timestamped)
550
+ # - benchmarks/results/history.jsonl (append-only log)
551
+ # - benchmarks/results/LATEST.md (human-readable report)
552
+ ```
553
+
554
+ See [benchmarks/README.md](./benchmarks/README.md) for detailed documentation on:
555
+
556
+ - Benchmark categories
557
+ - Result formats
558
+ - Comparing performance over time
559
+ - Expected performance metrics
560
+
561
+ ### Running Examples
562
+
563
+ ```bash
564
+ # Run a specific example
565
+ npm run example:01 # Basic usage
566
+ npm run example:02 # Dependency injection
567
+ npm run example:10 # Middleware (v1.2.0)
568
+
569
+ # Run all examples sequentially
570
+ npm run examples
171
571
  ```
172
572
 
573
+ ## Contributing
574
+
575
+ Contributions are welcome! Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct and development process.
576
+
577
+ ## Changelog
578
+
579
+ See [CHANGELOG.md](./CHANGELOG.md) for a list of changes and version history.
580
+
173
581
  ## License
174
582
 
175
- DIB
583
+ This project is licensed under the DIB License - see the [LICENSE](./LICENSE) file for details.
584
+
585
+ ## Author
586
+
587
+ **Kent Phung**
588
+
589
+ - GitHub: [@khapu2906](https://github.com/khapu2906)
590
+ - Package: [@khapu2906/treasure-chest](https://www.npmjs.com/package/@khapu2906/treasure-chest)
591
+
592
+ ## Support
593
+
594
+ If you encounter any issues or have questions:
595
+
596
+ - Open an issue on [GitHub Issues](https://github.com/khapu2906/treasure-chest/issues)
597
+ - Check the [examples](./examples) for usage patterns
598
+ - Read the [API Reference](#api-reference) above
599
+
600
+ ---
601
+
602
+ Made with ❤️ by Kent Phung