@khapu2906/treasure-chest 1.0.4 → 1.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 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,341 @@ 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. Container Composition (NEW in v1.3.0)
142
+
143
+ Mix services from different domains without inheritance:
144
+
145
+ ```typescript
146
+ import { Container } from '@khapu2906/treasure-chest';
147
+
148
+ // Create domain-specific containers
149
+ const infra = new Container();
150
+ infra.singleton('db', () => new Database());
151
+
152
+ const services = new Container();
153
+ services.singleton('userService', () => new UserService());
154
+
155
+ const controllers = new Container();
156
+ controllers.singleton('userController', () => new UserController());
157
+
158
+ // Compose them together
159
+ const app = Container.compose([infra, services, controllers]);
160
+
161
+ // All services available in one container
162
+ const db = app.resolve('db');
163
+ const userService = app.resolve('userService');
164
+ const userController = app.resolve('userController');
165
+ ```
166
+
167
+ ### 7. Scoped Lifecycle (NEW in v1.2.0)
168
+
169
+ Per-scope instances with automatic cleanup:
170
+
171
+ ```typescript
172
+ import { Container } from '@khapu2906/treasure-chest';
173
+
174
+ const container = new Container();
175
+
176
+ // Register scoped service with cleanup
177
+ container.scoped(
178
+ 'dbConnection',
179
+ () => new DbConnection(),
180
+ function () {
181
+ this.close();
182
+ } // Cleanup function
183
+ );
184
+
185
+ // Create a scope (e.g., per HTTP request)
186
+ const scope = container.createScope();
187
+
188
+ const conn1 = container.resolve('dbConnection');
189
+ const conn2 = container.resolve('dbConnection');
190
+ console.log(conn1 === conn2); // true - same instance within scope
191
+
192
+ // Cleanup when done
193
+ await scope.dispose(); // Calls cleanup functions
194
+ ```
195
+
196
+ ### 7. Lazy Loading (NEW in v1.2.0)
197
+
198
+ Defer expensive initialization:
199
+
200
+ ```typescript
201
+ import { Container, Lazy } from '@khapu2906/treasure-chest';
202
+
203
+ const container = new Container();
204
+
205
+ // Register lazy service
206
+ container.lazy('heavyService', () => new HeavyMLModel());
207
+
208
+ // Resolve as Lazy wrapper
209
+ const lazy = container.resolve<Lazy<HeavyMLModel>>('heavyService');
210
+
211
+ console.log(lazy.isInitialized); // false - not loaded yet
212
+
213
+ // Access when needed
214
+ const model = lazy.value; // NOW it's initialized
215
+
216
+ console.log(lazy.isInitialized); // true
217
+ ```
218
+
219
+ ### 8. Child Containers (NEW in v1.2.0)
220
+
221
+ Container hierarchy for multi-tenant apps:
222
+
223
+ ```typescript
224
+ const parent = new Container();
225
+ parent.singleton('config', () => new Config());
226
+
227
+ // Create child container
228
+ const tenant1 = parent.createChild();
229
+ tenant1.singleton('logger', () => new TenantLogger('tenant1'));
230
+
231
+ const tenant2 = parent.createChild();
232
+ tenant2.singleton('logger', () => new TenantLogger('tenant2'));
233
+
234
+ // Each tenant has its own logger but shares config
235
+ tenant1.resolve('config'); // From parent
236
+ tenant1.resolve('logger'); // From tenant1
237
+ ```
238
+
239
+ ### 9. Circular Dependency Detection (NEW in v1.2.0)
240
+
241
+ Automatic detection with clear error messages:
242
+
243
+ ```typescript
244
+ container.bind('A', (c) => {
245
+ const b = c.resolve('B');
246
+ return { name: 'A', dep: b };
247
+ });
248
+
249
+ container.bind('B', (c) => {
250
+ const a = c.resolve('A'); // Circular!
251
+ return { name: 'B', dep: a };
252
+ });
253
+
254
+ try {
255
+ container.resolve('A');
256
+ } catch (error) {
257
+ console.log(error.message);
258
+ // "Circular dependency detected: A -> B -> A"
259
+ }
260
+ ```
261
+
262
+ ### 10. IDisposable Interface & Auto-Dispose (NEW in v1.2.0)
263
+
264
+ Automatic cleanup detection with zero overhead:
265
+
266
+ ```typescript
267
+ import { Container, IDisposable } from '@khapu2906/treasure-chest';
268
+
269
+ // Implement IDisposable for auto-cleanup
270
+ class DbConnection implements IDisposable {
271
+ async connect() {
272
+ console.log('Connected');
273
+ }
274
+
275
+ async dispose() {
276
+ console.log('Disconnected');
277
+ }
278
+ }
279
+
280
+ const container = new Container();
281
+
282
+ // Auto-detection: No need to pass dispose function!
283
+ container.scoped('db', () => new DbConnection());
284
+
285
+ // Using withScope() - C# using pattern
286
+ await container.withScope(async (scope) => {
287
+ const db = container.resolve<DbConnection>('db');
288
+ await db.connect();
289
+ // Auto-disposed when scope exits
290
+ });
291
+ // Dispose called automatically here
292
+ ```
293
+
294
+ ### 11. Web Framework Integration Pattern
295
+
296
+ Pattern for automatic per-request scope management. See `examples/10-middleware.ts` for full implementation.
297
+
298
+ > **Note:** Middleware helpers are not exported from the core package to keep it framework-agnostic.
299
+ > For production use, consider creating separate adapter packages.
300
+
301
+ **Pattern Example:**
302
+
303
+ ```typescript
304
+ import { Container } from '@khapu2906/treasure-chest';
305
+
306
+ const container = new Container();
307
+ container.scoped('db', () => new DbConnection());
308
+
309
+ // Express pattern
310
+ function expressScope(container: Container) {
311
+ return (req, res, next) => {
312
+ const scope = container.createScope();
313
+ req.scope = scope;
314
+ res.on('finish', () => scope.dispose());
315
+ next();
316
+ };
317
+ }
318
+
319
+ app.use(expressScope(container));
320
+ ```
321
+
322
+ **Supported Patterns:**
323
+
324
+ - ✅ Express middleware
325
+ - ✅ Fastify hooks (onRequest/onResponse)
326
+ - ✅ Koa middleware
327
+
328
+ See [examples/10-middleware.ts](./examples/10-middleware.ts) for complete implementations.
329
+
85
330
  ## API Reference
86
331
 
87
332
  ### Container Class
88
333
 
89
- #### `bind<T>(key: string, factory: FactoryFn<T>, condition?: ConditionFn)`
90
- Register a transient service.
334
+ #### Lifecycle Methods
335
+
336
+ **`bind<T>(key: string, factory: FactoryFn<T>, condition?: ConditionFn)`**
337
+
338
+ - Register a transient service (new instance each time)
339
+
340
+ **`singleton<T>(key: string, factory: FactoryFn<T>, condition?: ConditionFn)`**
341
+
342
+ - Register a singleton service (single instance shared)
343
+
344
+ **`scoped<T>(key: string, factory: FactoryFn<T>, dispose?: DisposeFn)` ⭐ NEW**
345
+
346
+ - Register a scoped service (instance per scope)
347
+ - Optional cleanup function called on dispose
348
+
349
+ **`lazy<T>(key: string, factory: FactoryFn<T>, lifecycle?: Lifecycle)` ⭐ NEW**
350
+
351
+ - Register a lazy service (deferred initialization)
352
+ - Returns `Lazy<T>` wrapper
353
+ - Default lifecycle: singleton
354
+
355
+ #### Resolution Methods
356
+
357
+ **`resolve<T>(key: string, context?: string): T`**
358
+
359
+ - Resolve a service from the container
360
+ - Supports circular dependency detection
361
+
362
+ **`has(key: string): boolean` ⭐ NEW**
363
+
364
+ - Check if a binding exists for a key
365
+
366
+ **`keys(): string[]` ⭐ NEW**
367
+
368
+ - Get all registered service keys
369
+
370
+ #### Contextual Binding
371
+
372
+ **`when(context: string).needs(key: string).give(factory: FactoryFn)`**
373
+
374
+ - Create context-specific bindings
375
+ - Fluent API for readability
376
+
377
+ **`alias(aliasKey: string, originalKey: string)`**
91
378
 
92
- #### `singleton<T>(key: string, factory: FactoryFn<T>, condition?: ConditionFn)`
93
- Register a singleton service.
379
+ - Create an alias for a service
94
380
 
95
- #### `alias(aliasKey: string, originalKey: string)`
96
- Create an alias for a service.
381
+ #### Container Composition
97
382
 
98
- #### `resolve<T>(key: string, context?: string): T`
99
- Resolve a service from the container.
383
+ **`Container.compose(containers: Container[]): Container` ⭐ NEW v1.2.0**
100
384
 
101
- #### `when(context: string)`
102
- Start a contextual binding chain.
385
+ - Combine multiple containers into one
386
+ - Allows mixing services from different domains
387
+ - First-wins conflict resolution
388
+ - Maintains container isolation
103
389
 
104
- #### `reset()`
105
- Clear all bindings and instances (useful for testing).
390
+ #### Container Hierarchy
391
+
392
+ **`createChild(): Container` ⭐ NEW**
393
+
394
+ - Create a child container that inherits from parent
395
+ - Children can override parent bindings
396
+
397
+ **`createScope(): Scope` ⭐ NEW**
398
+
399
+ - Create a new scope for scoped instances
400
+ - Returns a `Scope` object
401
+
402
+ **`withScope<T>(callback: (scope: Scope) => T | Promise<T>): Promise<T>` ⭐ NEW v1.2.0**
403
+
404
+ - Execute callback with auto-managed scope
405
+ - C#-style `using` statement pattern
406
+ - Automatic disposal even on errors
407
+ - Exception-safe cleanup
408
+
409
+ #### Cleanup
410
+
411
+ **`reset()`**
412
+
413
+ - Clear all bindings and instances
414
+
415
+ **`dispose(): Promise<void>` ⭐ NEW**
416
+
417
+ - Dispose container and all scoped instances
418
+ - Calls cleanup functions
419
+
420
+ ### Exported Types
421
+
422
+ ```typescript
423
+ import {
424
+ Container,
425
+ Lazy,
426
+ Scope,
427
+ Lifecycle,
428
+ IDisposable,
429
+ DisposeFn,
430
+ } from '@khapu2906/treasure-chest';
431
+
432
+ // Lifecycle: 'transient' | 'singleton' | 'scoped'
433
+ // Lazy<T>: Wrapper with .value and .isInitialized
434
+ // Scope: Scope management with .dispose()
435
+ // IDisposable: Interface for auto-cleanup detection ⭐ NEW v1.2.0
436
+ // DisposeFn: Type for cleanup functions
437
+ ```
106
438
 
107
439
  ### Global Container
108
440
 
@@ -145,31 +477,162 @@ const container = new Container();
145
477
 
146
478
  container.singleton('db', () => new Database());
147
479
  container.singleton('userRepo', (c) => new UserRepository(c.resolve('db')));
148
- container.singleton('userService', (c) => new UserService(c.resolve('userRepo')));
480
+ container.singleton(
481
+ 'userService',
482
+ (c) => new UserService(c.resolve('userRepo'))
483
+ );
149
484
 
150
485
  // Contextual binding
151
- container.when('AdminService').needs('userRepo').give((c) => ({
152
- ...c.resolve('userRepo'),
153
- adminAccess: true
154
- }));
486
+ container
487
+ .when('AdminService')
488
+ .needs('userRepo')
489
+ .give((c) => ({
490
+ ...c.resolve('userRepo'),
491
+ adminAccess: true,
492
+ }));
155
493
 
156
494
  // Usage
157
495
  const userService = container.resolve<UserService>('userService');
158
496
  console.log(userService.getUser(1)); // { id: 1, name: 'John Doe' }
159
497
  ```
160
498
 
161
- ## Testing
499
+ ## Examples
500
+
501
+ Check out the [examples](./examples) directory for comprehensive real-world usage patterns:
502
+
503
+ ### Basic Features
504
+
505
+ 1. **Basic Usage**: Transient and singleton bindings
506
+ 2. **Dependency Injection**: Nested dependency resolution
507
+ 3. **Conditional Binding**: Environment-based configuration
508
+ 4. **Contextual Binding**: Context-aware service resolution
509
+ 5. **Alias**: Multiple names for the same service
510
+
511
+ ### Advanced Features (v1.2.0)
512
+
513
+ 6. **Scoped Lifecycle**: Per-request services with cleanup
514
+ 7. **Lazy Loading**: Performance optimization with deferred init
515
+ 8. **Child Containers**: Multi-tenant and plugin systems
516
+ 9. **Circular Dependency**: Detection and best practices
517
+ 10. **Container Composition**: Mix services from different domains
518
+
519
+ ### Performance & Integration (v1.2.0) ⭐ NEW
520
+
521
+ 11. **Web Framework Middleware**: Express, Fastify, Koa integration
522
+ - Auto-scoping per HTTP request
523
+ - IDisposable auto-detection
524
+ - withScope() pattern demos
525
+
526
+ Each example includes detailed comments and runnable code.
527
+
528
+ ## Development
529
+
530
+ ### Install Dependencies
162
531
 
163
532
  ```bash
533
+ npm install
534
+ ```
535
+
536
+ ### Running Tests
537
+
538
+ ```bash
539
+ # Run all tests
164
540
  npm test
541
+
542
+ # Run tests in watch mode
543
+ npm run test:watch
544
+
545
+ # Run tests with coverage
546
+ npm run test:coverage
547
+
548
+ # Run tests with UI
549
+ npm run test:ui
165
550
  ```
166
551
 
167
- ## Build
552
+ ### Linting and Formatting
168
553
 
169
554
  ```bash
555
+ # Run ESLint
556
+ npm run lint
557
+
558
+ # Fix linting issues
559
+ npm run lint:fix
560
+
561
+ # Format code with Prettier
562
+ npm run format
563
+
564
+ # Check formatting
565
+ npm run format:check
566
+ ```
567
+
568
+ ### Building
569
+
570
+ ```bash
571
+ # Build for production (generates CJS, ESM, and type declarations)
170
572
  npm run build
573
+
574
+ # Clean build artifacts
575
+ npm run clean
171
576
  ```
172
577
 
578
+ ### Running Benchmarks ⭐ NEW v1.2.0
579
+
580
+ ```bash
581
+ # Run performance benchmarks
582
+ npm run benchmark
583
+
584
+ # Results auto-saved to:
585
+ # - benchmarks/results/run-YYYY-MM-DD-HH-MM-SS.json (timestamped)
586
+ # - benchmarks/results/history.jsonl (append-only log)
587
+ # - benchmarks/results/LATEST.md (human-readable report)
588
+ ```
589
+
590
+ See [benchmarks/README.md](./benchmarks/README.md) for detailed documentation on:
591
+
592
+ - Benchmark categories
593
+ - Result formats
594
+ - Comparing performance over time
595
+ - Expected performance metrics
596
+
597
+ ### Running Examples
598
+
599
+ ```bash
600
+ # Run a specific example
601
+ npm run example:01 # Basic usage
602
+ npm run example:02 # Dependency injection
603
+ npm run example:10 # Middleware (v1.2.0)
604
+
605
+ # Run all examples sequentially
606
+ npm run examples
607
+ ```
608
+
609
+ ## Contributing
610
+
611
+ Contributions are welcome! Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct and development process.
612
+
613
+ ## Changelog
614
+
615
+ See [CHANGELOG.md](./CHANGELOG.md) for a list of changes and version history.
616
+
173
617
  ## License
174
618
 
175
- DIB
619
+ This project is licensed under the DIB License - see the [LICENSE](./LICENSE) file for details.
620
+
621
+ ## Author
622
+
623
+ **Kent Phung**
624
+
625
+ - GitHub: [@khapu2906](https://github.com/khapu2906)
626
+ - Package: [@khapu2906/treasure-chest](https://www.npmjs.com/package/@khapu2906/treasure-chest)
627
+
628
+ ## Support
629
+
630
+ If you encounter any issues or have questions:
631
+
632
+ - Open an issue on [GitHub Issues](https://github.com/khapu2906/treasure-chest/issues)
633
+ - Check the [examples](./examples) for usage patterns
634
+ - Read the [API Reference](#api-reference) above
635
+
636
+ ---
637
+
638
+ Made with ❤️ by Kent Phung