@noego/ioc 0.0.2

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.
@@ -0,0 +1,16 @@
1
+ import 'reflect-metadata';
2
+ import { createContainer } from "./framework/implementation/Container";
3
+ import { Container } from "./framework/implementation/Container";
4
+ import { LoadAs } from "./framework/implementation/LoadAs";
5
+ import { Parameter } from "./framework/implementation/Parameter";
6
+ import { IContainer } from "./framework/interface/Container";
7
+ import { NotAClassDefinitionError, ParameterNotFoundError, DependencyNotFoundError, InstanceNotCreatedError, CircularDependencyError } from "./framework/errors/ContainerErrors";
8
+ import { Component, Inject } from "./framework/decorators/index";
9
+ export { IContainer };
10
+ export { LoadAs };
11
+ export { Parameter };
12
+ export { Container };
13
+ export { NotAClassDefinitionError, ParameterNotFoundError, DependencyNotFoundError, InstanceNotCreatedError, CircularDependencyError };
14
+ export { Component, Inject };
15
+ export { createContainer } from "./framework/implementation/Container";
16
+ export default createContainer;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createContainer = exports.Inject = exports.Component = exports.CircularDependencyError = exports.InstanceNotCreatedError = exports.DependencyNotFoundError = exports.ParameterNotFoundError = exports.NotAClassDefinitionError = exports.Container = exports.Parameter = exports.LoadAs = void 0;
4
+ require("reflect-metadata");
5
+ const Container_1 = require("./framework/implementation/Container");
6
+ const Container_2 = require("./framework/implementation/Container");
7
+ Object.defineProperty(exports, "Container", { enumerable: true, get: function () { return Container_2.Container; } });
8
+ const LoadAs_1 = require("./framework/implementation/LoadAs");
9
+ Object.defineProperty(exports, "LoadAs", { enumerable: true, get: function () { return LoadAs_1.LoadAs; } });
10
+ const Parameter_1 = require("./framework/implementation/Parameter");
11
+ Object.defineProperty(exports, "Parameter", { enumerable: true, get: function () { return Parameter_1.Parameter; } });
12
+ const ContainerErrors_1 = require("./framework/errors/ContainerErrors");
13
+ Object.defineProperty(exports, "NotAClassDefinitionError", { enumerable: true, get: function () { return ContainerErrors_1.NotAClassDefinitionError; } });
14
+ Object.defineProperty(exports, "ParameterNotFoundError", { enumerable: true, get: function () { return ContainerErrors_1.ParameterNotFoundError; } });
15
+ Object.defineProperty(exports, "DependencyNotFoundError", { enumerable: true, get: function () { return ContainerErrors_1.DependencyNotFoundError; } });
16
+ Object.defineProperty(exports, "InstanceNotCreatedError", { enumerable: true, get: function () { return ContainerErrors_1.InstanceNotCreatedError; } });
17
+ Object.defineProperty(exports, "CircularDependencyError", { enumerable: true, get: function () { return ContainerErrors_1.CircularDependencyError; } });
18
+ const index_1 = require("./framework/decorators/index");
19
+ Object.defineProperty(exports, "Component", { enumerable: true, get: function () { return index_1.Component; } });
20
+ Object.defineProperty(exports, "Inject", { enumerable: true, get: function () { return index_1.Inject; } });
21
+ var Container_3 = require("./framework/implementation/Container");
22
+ Object.defineProperty(exports, "createContainer", { enumerable: true, get: function () { return Container_3.createContainer; } });
23
+ exports.default = Container_1.createContainer;
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":";;;AAAA,4BAA0B;AAC1B,oEAAsE;AACtE,oEAAgE;AAUvD,0FAVA,qBAAS,OAUA;AATlB,8DAA0D;AAOjD,uFAPA,eAAM,OAOA;AANf,oEAAgE;AAOvD,0FAPA,qBAAS,OAOA;AALlB,wEAAgL;AAOvK,yGAPA,0CAAwB,OAOA;AAAE,uGAPA,wCAAsB,OAOA;AAAE,wGAPA,yCAAuB,OAOA;AAAE,wGAPA,yCAAuB,OAOA;AAAE,wGAPA,yCAAuB,OAOA;AANpI,wDAAgE;AAOvD,0FAPA,iBAAS,OAOA;AAAE,uFAPA,cAAM,OAOA;AAC1B,kEAAsE;AAA7D,4GAAA,eAAe,OAAA;AACxB,kBAAe,2BAAe,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { JestConfigWithTsJest } from 'ts-jest';
2
+ declare const jestConfig: JestConfigWithTsJest;
3
+ export default jestConfig;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jestConfig = {
4
+ preset: 'ts-jest',
5
+ testEnvironment: 'node',
6
+ testPathIgnorePatterns: ['/node_modules/', '/dist/'],
7
+ };
8
+ exports.default = jestConfig;
9
+ //# sourceMappingURL=jest.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest.config.js","sourceRoot":"","sources":["../../jest.config.ts"],"names":[],"mappings":";;AAEA,MAAM,UAAU,GAAyB;IACvC,MAAM,EAAE,SAAS;IACjB,eAAe,EAAE,MAAM;IACvB,sBAAsB,EAAE,CAAC,gBAAgB,EAAE,QAAQ,CAAC;CACrD,CAAC;AAEF,kBAAe,UAAU,CAAC"}
@@ -0,0 +1,16 @@
1
+ import 'reflect-metadata';
2
+ import { createContainer } from "./framework/implementation/Container";
3
+ import { Container } from "./framework/implementation/Container";
4
+ import { LoadAs } from "./framework/implementation/LoadAs";
5
+ import { Parameter } from "./framework/implementation/Parameter";
6
+ import { IContainer } from "./framework/interface/Container";
7
+ import { NotAClassDefinitionError, ParameterNotFoundError, DependencyNotFoundError, InstanceNotCreatedError, CircularDependencyError } from "./framework/errors/ContainerErrors";
8
+ import { Component, Inject } from "./framework/decorators/index";
9
+ export { IContainer };
10
+ export { LoadAs };
11
+ export { Parameter };
12
+ export { Container };
13
+ export { NotAClassDefinitionError, ParameterNotFoundError, DependencyNotFoundError, InstanceNotCreatedError, CircularDependencyError };
14
+ export { Component, Inject };
15
+ export { createContainer } from "./framework/implementation/Container";
16
+ export default createContainer;
@@ -0,0 +1,15 @@
1
+ import 'reflect-metadata';
2
+ import { createContainer } from "./framework/implementation/Container";
3
+ import { Container } from "./framework/implementation/Container";
4
+ import { LoadAs } from "./framework/implementation/LoadAs";
5
+ import { Parameter } from "./framework/implementation/Parameter";
6
+ import { NotAClassDefinitionError, ParameterNotFoundError, DependencyNotFoundError, InstanceNotCreatedError, CircularDependencyError } from "./framework/errors/ContainerErrors";
7
+ import { Component, Inject } from "./framework/decorators/index";
8
+ export { LoadAs };
9
+ export { Parameter };
10
+ export { Container };
11
+ export { NotAClassDefinitionError, ParameterNotFoundError, DependencyNotFoundError, InstanceNotCreatedError, CircularDependencyError };
12
+ export { Component, Inject };
13
+ export { createContainer } from "./framework/implementation/Container";
14
+ export default createContainer;
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAA;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAA;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAA;AAEhE,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAA;AAChL,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;AAGhE,OAAO,EAAE,MAAM,EAAE,CAAA;AACjB,OAAO,EAAE,SAAS,EAAE,CAAA;AACpB,OAAO,EAAE,SAAS,EAAE,CAAA;AACpB,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,CAAA;AACtI,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAA;AACtE,eAAe,eAAe,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { JestConfigWithTsJest } from 'ts-jest';
2
+ declare const jestConfig: JestConfigWithTsJest;
3
+ export default jestConfig;
@@ -0,0 +1,7 @@
1
+ const jestConfig = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ testPathIgnorePatterns: ['/node_modules/', '/dist/'],
5
+ };
6
+ export default jestConfig;
7
+ //# sourceMappingURL=jest.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest.config.js","sourceRoot":"","sources":["../../jest.config.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAyB;IACvC,MAAM,EAAE,SAAS;IACjB,eAAe,EAAE,MAAM;IACvB,sBAAsB,EAAE,CAAC,gBAAgB,EAAE,QAAQ,CAAC;CACrD,CAAC;AAEF,eAAe,UAAU,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@noego/ioc",
3
+ "version": "0.0.2",
4
+ "description": "A self contained IoC container for Node.js",
5
+ "main": "dist/cjs/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "test": "jest --silent",
9
+ "build:cjs": "tsc -p tsconfig.cjs.json",
10
+ "build:esm": "tsc -p tsconfig.esm.json",
11
+ "build": "npm run build:cjs && npm run build:esm",
12
+ "typecheck": "tsc -p tsconfig.cjs.json --noEmit"
13
+ },
14
+ "exports":{
15
+ ".": {
16
+ "require": "./dist/cjs/index.js",
17
+ "import": "./dist/esm/index.js"
18
+ },
19
+ "./package.json": "./package.json",
20
+ "./readme.md": "./readme.md"
21
+ },
22
+ "author": "Shavauhn Gabay",
23
+ "license": "ISC",
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "dependencies": {
28
+ "@types/node": "^20.10.4",
29
+ "@types/randomstring": "^1.1.11",
30
+ "@types/reflect-metadata": "^0.0.5",
31
+ "randomstring": "^1.3.0",
32
+ "reflect-metadata": "^0.2.2"
33
+ },
34
+ "devDependencies": {
35
+ "@types/jest": "^29.5.14",
36
+ "jest": "^29.7.0",
37
+ "ts-jest": "^29.3.2",
38
+ "ts-node": "^10.9.2",
39
+ "typescript": "^5.8.3"
40
+ }
41
+ }
package/readme.md ADDED
@@ -0,0 +1,566 @@
1
+ # @noego/ioc
2
+
3
+ A lightweight, flexible Inversion of Control (IoC) container for Node.js and TypeScript applications, providing support for multiple dependency lifetime scopes, parameter injection, and both class and function registration with full type safety.
4
+
5
+ ## Features
6
+
7
+ **Dual Module Support**: Compatible with CommonJS and ES Modules
8
+ **TypeScript & Typings**: Built in TypeScript with bundled declaration files
9
+
10
+ - **Multiple Lifetime Scopes**: Support for Singleton, Transient, and Scoped dependencies
11
+ - **Class & Function Registration**: Register both classes and functions as dependencies
12
+ - **Parameter Injection**: Inject parameter values at resolution time
13
+ - **Container Extension**: Create child containers that inherit parent registrations
14
+ - **Decorator Support**: Use @Component and @Inject decorators for clean, declarative DI
15
+ - **TypeScript Support**: Built with full TypeScript support for type safety
16
+ - **Async Resolution**: Support for asynchronous dependency resolution
17
+ - **Lightweight**: Small footprint with minimal external dependencies
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @noego/ioc
23
+ # or
24
+ yarn add @noego/ioc
25
+ ```
26
+
27
+ If you want to use decorators, also install reflect-metadata:
28
+
29
+ ```bash
30
+ npm install reflect-metadata
31
+ # or
32
+ yarn add reflect-metadata
33
+ ```
34
+
35
+ And configure TypeScript for decorator support in tsconfig.json:
36
+
37
+ ```json
38
+ {
39
+ "compilerOptions": {
40
+ "experimentalDecorators": true,
41
+ "emitDecoratorMetadata": true,
42
+ "module": "ESNext",
43
+ "moduleResolution": "node",
44
+ "target": "ESNext"
45
+ }
46
+ }
47
+ ```
48
+
49
+ ### Quick Start
50
+ Follow these minimal steps to get @noego/ioc working in a TypeScript project:
51
+
52
+ 1. Install packages:
53
+ ```bash
54
+ npm install @noego/ioc reflect-metadata
55
+ ```
56
+
57
+ 2. Configure `tsconfig.json`:
58
+ ```json
59
+ {
60
+ "compilerOptions": {
61
+ "experimentalDecorators": true,
62
+ "emitDecoratorMetadata": true,
63
+ "module": "ESNext",
64
+ "moduleResolution": "node",
65
+ "target": "ESNext"
66
+ },
67
+ "include": ["src"]
68
+ }
69
+ ```
70
+
71
+ 3. Create an entry file (`index.ts`):
72
+ ```typescript
73
+ import 'reflect-metadata';
74
+ import createContainer, { Component } from '@noego/ioc';
75
+
76
+ @Component()
77
+ class ExampleService {}
78
+
79
+ async function bootstrap() {
80
+ const container = createContainer();
81
+ container.registerClass(ExampleService);
82
+ const svc = await container.instance(ExampleService);
83
+ console.log('Service instance:', svc);
84
+ }
85
+
86
+ bootstrap();
87
+ ```
88
+
89
+ 4. Run the entry file:
90
+ ```bash
91
+ npx ts-node index.ts
92
+ ```
93
+
94
+ ## Getting Started
95
+
96
+ ### Using manual registration:
97
+
98
+ ```typescript
99
+ import createContainer from "@noego/ioc";
100
+
101
+ // Create a container
102
+ const container = createContainer();
103
+
104
+ // Register your dependencies
105
+ container.registerClass(Database);
106
+ container.registerClass(UserRepository, { param: [Database] });
107
+
108
+ // Resolve and use
109
+ async function main() {
110
+ const repo = await container.instance(UserRepository);
111
+ const users = repo.getUsers();
112
+ }
113
+
114
+ main();
115
+ ```
116
+
117
+ ### Using decorators:
118
+
119
+ ```typescript
120
+ import createContainer, { Component, Inject } from "@noego/ioc";
121
+ import 'reflect-metadata'; // Required when using decorators
122
+
123
+ @Component({ scope: LoadAs.Singleton })
124
+ class Database {
125
+ connect() {
126
+ return "Connected to DB";
127
+ }
128
+ }
129
+
130
+ @Component()
131
+ class UserRepository {
132
+ constructor(private db: Database) {}
133
+
134
+ getUsers() {
135
+ this.db.connect();
136
+ return ["User1", "User2"];
137
+ }
138
+ }
139
+
140
+ // Create container and register classes
141
+ const container = createContainer();
142
+ container.registerClass(Database);
143
+ container.registerClass(UserRepository);
144
+
145
+ // Resolve and use
146
+ async function main() {
147
+ const repo = await container.instance(UserRepository);
148
+ const users = repo.getUsers();
149
+ }
150
+
151
+ main();
152
+ ```
153
+
154
+ ## Usage
155
+
156
+ ### Basic Usage
157
+
158
+ ```typescript
159
+ import createContainer from "@noego/ioc";
160
+
161
+ // Create a container
162
+ const container = createContainer();
163
+
164
+ // Define classes
165
+ class Database {
166
+ connect() {
167
+ return "Connected to DB";
168
+ }
169
+ }
170
+
171
+ class UserRepository {
172
+ constructor(private db: Database) {}
173
+
174
+ getUsers() {
175
+ this.db.connect();
176
+ return ["User1", "User2"];
177
+ }
178
+ }
179
+
180
+ class UserService {
181
+ constructor(private repo: UserRepository) {}
182
+
183
+ getAllUsers() {
184
+ return this.repo.getUsers();
185
+ }
186
+ }
187
+
188
+ // Register dependencies
189
+ container.registerClass(Database);
190
+ container.registerClass(UserRepository, { param: [Database] });
191
+ container.registerClass(UserService, { param: [UserRepository] });
192
+
193
+ // Resolve dependencies
194
+ async function run() {
195
+ const userService = await container.instance(UserService);
196
+ const users = userService.getAllUsers();
197
+ console.log(users); // ["User1", "User2"]
198
+ }
199
+
200
+ run();
201
+ ```
202
+
203
+ ### Lifetime Scopes
204
+
205
+ The container supports three different lifetime scopes:
206
+
207
+ 1. **Transient** (default): A new instance is created every time the dependency is resolved
208
+ 2. **Singleton**: Only one instance is created and reused throughout the application
209
+ 3. **Scoped**: A single instance is created per container scope
210
+
211
+ ```typescript
212
+ import { LoadAs } from "@noego/ioc";
213
+
214
+ // Register a singleton
215
+ container.registerClass(Database, { loadAs: LoadAs.Singleton });
216
+
217
+ // Register a scoped dependency
218
+ container.registerClass(UserRepository, {
219
+ param: [Database],
220
+ loadAs: LoadAs.Scoped
221
+ });
222
+ ```
223
+
224
+ ### Parameter Injection
225
+
226
+ You can inject parameter values at resolution time:
227
+
228
+ ```typescript
229
+ import { Parameter } from "@noego/ioc";
230
+
231
+ class User {
232
+ constructor(public id: number, public name: string) {}
233
+ }
234
+
235
+ // Create parameters
236
+ const USER_ID = Parameter.create();
237
+ const USER_NAME = Parameter.create();
238
+
239
+ // Register with parameters
240
+ container.registerClass(User, { param: [USER_ID, USER_NAME] });
241
+
242
+ // Resolve with parameter values
243
+ async function createUser() {
244
+ const user = await container.instance(User, [
245
+ USER_ID.value(1),
246
+ USER_NAME.value("John")
247
+ ]);
248
+
249
+ console.log(user.id, user.name); // 1, "John"
250
+ }
251
+ ```
252
+
253
+ ### Function Registration
254
+
255
+ You can also register functions as dependencies:
256
+
257
+ ```typescript
258
+ function createLogger(prefix: string) {
259
+ return {
260
+ log: (message: string) => console.log(`${prefix}: ${message}`)
261
+ };
262
+ }
263
+
264
+ const PREFIX = Parameter.create();
265
+
266
+ // Register function
267
+ container.registerFunction("logger", createLogger, {
268
+ param: [PREFIX]
269
+ });
270
+
271
+ // Resolve function
272
+ async function useLogger() {
273
+ const logger = await container.get("logger", [PREFIX.value("APP")]);
274
+ logger.log("Application started"); // "APP: Application started"
275
+ }
276
+ ```
277
+
278
+ ### Using Decorators
279
+
280
+ The container supports decorator-based dependency injection using `@Component` and `@Inject`.
281
+
282
+ #### Setup
283
+
284
+ First, ensure TypeScript is configured to support decorators:
285
+
286
+ ```json
287
+ // tsconfig.json
288
+ {
289
+ "compilerOptions": {
290
+ "experimentalDecorators": true,
291
+ "emitDecoratorMetadata": true,
292
+ // other options...
293
+ }
294
+ }
295
+ ```
296
+
297
+ Also, import `reflect-metadata` once at your application's entry point:
298
+
299
+ ```typescript
300
+ // index.ts or main.ts
301
+ import 'reflect-metadata';
302
+ // ... rest of your code
303
+ ```
304
+
305
+ #### Component Decorator
306
+
307
+ Use `@Component` to mark a class as a component with an optional scope:
308
+
309
+ ```typescript
310
+ import { Component, LoadAs } from '@noego/ioc';
311
+
312
+ @Component() // Default is Transient
313
+ class UserService {
314
+ // ...
315
+ }
316
+
317
+ @Component({ scope: LoadAs.Singleton })
318
+ class DatabaseService {
319
+ // ...
320
+ }
321
+
322
+ @Component({ scope: LoadAs.Scoped })
323
+ class RequestContext {
324
+ // ...
325
+ }
326
+ ```
327
+
328
+ #### Inject Decorator
329
+
330
+ Use `@Inject` to specify tokens for interface dependencies or to override constructor parameter types:
331
+
332
+ ```typescript
333
+ import { Component, Inject } from '@noego/ioc';
334
+
335
+ // Define an interface
336
+ interface ILogger {
337
+ log(message: string): void;
338
+ }
339
+
340
+ // Create a token for the interface
341
+ const LoggerToken = Symbol('ILogger');
342
+
343
+ // Implement the interface
344
+ @Component({ scope: LoadAs.Singleton })
345
+ class ConsoleLogger implements ILogger {
346
+ log(message: string) {
347
+ console.log(message);
348
+ }
349
+ }
350
+
351
+ // Use @Inject to specify which implementation to use
352
+ @Component()
353
+ class UserService {
354
+ constructor(
355
+ // Use @Inject with a token
356
+ @Inject(LoggerToken) private logger: ILogger,
357
+
358
+ // Regular parameter - resolved by type
359
+ private database: DatabaseService
360
+ ) {}
361
+
362
+ createUser() {
363
+ this.logger.log('Creating user...');
364
+ // ...
365
+ }
366
+ }
367
+
368
+ // Register
369
+ const container = createContainer();
370
+ container.registerClass(DatabaseService);
371
+ container.registerClass(ConsoleLogger);
372
+ container.registerFunction(LoggerToken, () => container.instance(ConsoleLogger));
373
+ container.registerClass(UserService);
374
+
375
+ // Resolve
376
+ const service = await container.instance(UserService);
377
+ ```
378
+
379
+ #### Override Priority
380
+
381
+ Manual registration options take precedence over decorators:
382
+
383
+ 1. Manually defined parameters in `registerClass({ param: [...] })` override constructor parameter types and `@Inject` annotations.
384
+ 2. Manually defined scope in `registerClass({ loadAs: ... })` overrides `@Component({ scope: ... })`.
385
+
386
+ This allows you to change behavior at registration time without modifying the decorated class.
387
+
388
+ ### Extending Containers
389
+
390
+ You can create a child container that inherits all the registrations from the parent but allows overriding:
391
+
392
+ ```typescript
393
+ // Create parent container
394
+ const parentContainer = createContainer();
395
+ parentContainer.registerClass(Database);
396
+
397
+ // Create child container
398
+ const childContainer = parentContainer.extend();
399
+
400
+ // Override in child container
401
+ childContainer.registerClass(Database, { /* different configuration */ });
402
+
403
+ // Parent container still uses the original registration
404
+ // Child container uses the new registration
405
+ ```
406
+
407
+ ## API Reference
408
+
409
+ ### Container
410
+
411
+ - `createContainer()`: Creates a new IoC container
412
+ - `registerClass<T>(classDefinition, options?)`: Register a class
413
+ - `registerFunction(label, function, options?)`: Register a function
414
+ - `instance<T>(classDefinition, params?)`: Resolve a class instance
415
+ - `get<T>(label, params?)`: Resolve a dependency by key
416
+ - `extend()`: Create a child container
417
+
418
+ ### Decorators
419
+
420
+ - `@Component(options?)`: Mark a class as container-managed
421
+ - `@Inject(token)`: Specify a token for a constructor parameter
422
+
423
+ ### Options
424
+
425
+ ```typescript
426
+ interface ContainerOptions {
427
+ param?: any[]; // Dependencies or parameters
428
+ loadAs?: LoadAs; // Lifetime scope
429
+ }
430
+ ```
431
+
432
+ ### LoadAs Enum
433
+
434
+ ```typescript
435
+ enum LoadAs {
436
+ Singleton, // Single instance throughout application
437
+ Scoped, // Single instance per container scope
438
+ Transient // New instance each time
439
+ }
440
+ ```
441
+
442
+ ### Parameter
443
+
444
+ - `Parameter.create()`: Create a new parameter
445
+ - `parameter.value(value)`: Create a parameter value
446
+
447
+ ### Component Options
448
+
449
+ ```typescript
450
+ interface ComponentOptions {
451
+ scope?: LoadAs; // Lifetime scope
452
+ }
453
+ ```
454
+
455
+ ## Real-World Use Cases
456
+
457
+ ### Express Application
458
+
459
+ ```typescript
460
+ import express from 'express';
461
+ import createContainer, { LoadAs } from '@noego/ioc';
462
+
463
+ // Create services
464
+ class ConfigService {
465
+ getConfig() {
466
+ return { port: 3000 };
467
+ }
468
+ }
469
+
470
+ class DatabaseService {
471
+ constructor(private config: ConfigService) {}
472
+
473
+ connect() {
474
+ console.log('Connected to database');
475
+ return {};
476
+ }
477
+ }
478
+
479
+ class UserRepository {
480
+ constructor(private db: DatabaseService) {}
481
+
482
+ findAll() {
483
+ return [{ id: 1, name: 'User 1' }];
484
+ }
485
+ }
486
+
487
+ class UserController {
488
+ constructor(private repo: UserRepository) {}
489
+
490
+ getUsers(req, res) {
491
+ const users = this.repo.findAll();
492
+ res.json(users);
493
+ }
494
+ }
495
+
496
+ // Setup container
497
+ const container = createContainer();
498
+ container.registerClass(ConfigService, { loadAs: LoadAs.Singleton });
499
+ container.registerClass(DatabaseService, { param: [ConfigService], loadAs: LoadAs.Singleton });
500
+ container.registerClass(UserRepository, { param: [DatabaseService] });
501
+ container.registerClass(UserController, { param: [UserRepository] });
502
+
503
+ // Create express app
504
+ const app = express();
505
+
506
+ // Setup routes using the container
507
+ app.get('/users', async (req, res) => {
508
+ const controller = await container.instance(UserController);
509
+ controller.getUsers(req, res);
510
+ });
511
+
512
+ // Start server
513
+ async function bootstrap() {
514
+ const config = await container.instance(ConfigService);
515
+ app.listen(config.getConfig().port, () => {
516
+ console.log(`Server running on port ${config.getConfig().port}`);
517
+ });
518
+ }
519
+
520
+ bootstrap();
521
+ ```
522
+
523
+ ## Running Tests
524
+
525
+ The project uses Jest for testing. To run tests:
526
+
527
+ ```bash
528
+ npm test
529
+ ```
530
+
531
+ ## License
532
+
533
+ ISC
534
+
535
+ ## Contributing
536
+
537
+ Contributions are welcome! Here's how you can contribute to this project:
538
+
539
+ 1. Fork the repository
540
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
541
+ 3. Install dependencies (`npm install`)
542
+ 4. Make your changes
543
+ 5. Run tests to ensure everything works (`npm test`)
544
+ 6. Commit your changes (`git commit -m 'Add some amazing feature'`)
545
+ 7. Push to the branch (`git push origin feature/amazing-feature`)
546
+ 8. Open a Pull Request
547
+
548
+ ### Development Setup
549
+
550
+ 1. Clone the repository:
551
+ ```bash
552
+ git clone <repository-url>
553
+ cd ioc
554
+ ```
555
+
556
+ 2. Install dependencies:
557
+ ```bash
558
+ npm install
559
+ ```
560
+
561
+ 3. Run tests:
562
+ ```bash
563
+ npm test
564
+ ```
565
+
566
+ Please make sure to update tests as appropriate and follow the existing code style.