@adimm/x-injection 1.2.0 → 2.0.3

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,7 +1,7 @@
1
1
  <h1 align="center">
2
2
  xInjection&nbsp;<a href="https://www.npmjs.com/package/@adimm/x-injection" target="__blank"><img src="https://badgen.net/npm/v/@adimm/x-injection"></a>
3
- <img src="https://badgen.net/npm/license/@adimm/x-injection">
4
3
  <a href="https://app.codecov.io/gh/AdiMarianMutu/x-injection" target="__blank"><img src="https://badgen.net/codecov/c/github/AdiMarianMutu/x-injection"></a>
4
+ <img src="https://badgen.net/npm/license/@adimm/x-injection">
5
5
  </h1>
6
6
 
7
7
  <p align="center">
@@ -20,36 +20,47 @@ xInjection&nbsp;<a href="https://www.npmjs.com/package/@adimm/x-injection" targe
20
20
  - [Installation](#installation)
21
21
  - [TypeScript Configuration](#typescript-configuration)
22
22
  - [Getting Started](#getting-started)
23
- - [Bootstrapping the AppModule](#bootstrapping-the-appmodule)
24
- - [Registering Global Providers](#registering-global-providers)
25
- - [Registering Global Modules](#registering-global-modules)
26
- - [Injection Scope](#injection-scope)
27
- - [Singleton](#singleton)
28
- - [Transient](#transient)
29
- - [Request](#request)
23
+ - [Quick Start](#quick-start)
24
+ - [Glossary](#glossary)
25
+ - [ProviderModule](#providermodule)
26
+ - [AppModule](#appmodule)
27
+ - [Blueprint](#blueprint)
28
+ - [Definition](#definition)
29
+ - [Conventions](#conventions)
30
+ - [ProviderModule](#providermodule-1)
31
+ - [Blueprints](#blueprints)
32
+ - [ProviderToken](#providertoken)
33
+ - [AppModule](#appmodule-1)
34
+ - [ProviderModule API](#providermodule-api)
35
+ - [Injection Scope](#injection-scope)
36
+ - [Singleton](#singleton)
37
+ - [Transient](#transient)
38
+ - [Request](#request)
39
+ - [Provider Tokens](#provider-tokens)
30
40
  - [Provider Modules](#provider-modules)
31
- - [ProviderModuleDefinition](#providermoduledefinition)
32
- - [Feed to `new ProviderModule`](#feed-to-new-providermodule)
33
- - [Feed to `lazyImport`](#feed-to-lazyimport)
34
- - [Lazy `imports` and `exports`](#lazy-imports-and-exports)
35
- - [Imports](#imports)
36
- - [Exports](#exports)
41
+ - [Blueprints](#blueprints-1)
42
+ - [Import Behavior](#import-behavior)
43
+ - [isGlobal](#isglobal)
37
44
  - [Advanced Usage](#advanced-usage)
38
- - [ProviderModuleNaked Interface](#providermodulenaked-interface)
39
- - [Strict Mode](#strict-mode)
40
- - [Why you should not turn it off:](#why-you-should-not-turn-it-off)
41
- - [isGlobal](#isglobal)
45
+ - [Events](#events)
46
+ - [Middlewares](#middlewares)
47
+ - [Internals](#internals)
48
+ - [ProviderModule](#providermodule-2)
49
+ - [MiddlewaresManager](#middlewaresmanager)
50
+ - [ModuleContainer](#modulecontainer)
51
+ - [ImportedModuleContainer](#importedmodulecontainer)
52
+ - [DynamicModuleDefinition](#dynamicmoduledefinition)
53
+ - [ProviderModuleBlueprint](#providermoduleblueprint)
54
+ - [Set of Helpers](#set-of-helpers)
42
55
  - [Unit Tests](#unit-tests)
43
56
  - [Documentation](#documentation)
44
- - [Conventions](#conventions)
45
- - [ProviderModule](#providermodule)
46
- - [ProviderModuleDefinition](#providermoduledefinition-1)
47
57
  - [ReactJS Implementation](#reactjs-implementation)
48
58
  - [Contributing](#contributing)
59
+ - [Credits](#credits)
49
60
 
50
61
  ## Overview
51
62
 
52
- **xInjection** is a robust Inversion of Control [(IoC)](https://en.wikipedia.org/wiki/Inversion_of_control) library that extends [InversifyJS](https://github.com/inversify/InversifyJS) with a modular, [NestJS](https://github.com/nestjs/nest)-inspired Dependency Injection [(DI)](https://en.wikipedia.org/wiki/Dependency_injection) system. It enables you to **encapsulate** dependencies with fine-grained control using **[ProviderModule](https://adimarianmutu.github.io/x-injection/classes/ProviderModule.html)** classes, allowing for clean **separation** of concerns and **scalable** architecture.
63
+ **xInjection** is a robust Inversion of Control [(IoC)](https://en.wikipedia.org/wiki/Inversion_of_control) library that extends [InversifyJS](https://github.com/inversify/InversifyJS) with a modular, [NestJS](https://github.com/nestjs/nest)-inspired Dependency Injection [(DI)](https://en.wikipedia.org/wiki/Dependency_injection) system. It enables you to **encapsulate** dependencies with fine-grained control using **[ProviderModule](https://adimarianmutu.github.io/x-injection/classes/IProviderModule.html)** classes, allowing for clean **separation** of concerns and **scalable** architecture.
53
64
 
54
65
  Each `ProviderModule` manages its _own_ container, supporting easy **decoupling** and _explicit_ control over which providers are **exported** and **imported** across modules. The global **[AppModule](https://adimarianmutu.github.io/x-injection/variables/AppModule.html)** is always available, ensuring a seamless foundation for your application's DI needs.
55
66
 
@@ -58,8 +69,11 @@ Each `ProviderModule` manages its _own_ container, supporting easy **decoupling*
58
69
  - **NestJS-inspired module system:** Import and export providers between modules.
59
70
  - **Granular dependency encapsulation:** Each module manages its own container.
60
71
  - **Flexible provider scopes:** [Singleton](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#singleton), [Request](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#request), and [Transient](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#transient) lifecycles.
61
- - **Lifecycle hooks:** [onReady](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#onready) and [onDispose](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#ondispose) for _module_ initialization and cleanup.
62
- - **Advanced container access:** Directly interact with the underlying [InversifyJS containers](https://inversify.io/docs/api/container/) if needed.
72
+ - **Lifecycle hooks:** [onReady](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#onready), [onReset](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#onreset) and [onDispose](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#ondispose) for _module_ initialization and cleanup.
73
+ - **Middlewares:** Tap into the low-level implementation without any effort by just adding new `middlewares`.
74
+ - **Events:** Subscribe to internal events for maximum control.
75
+ - **Blueprints:** Plan ahead your `modules` without eagerly instantiating them.
76
+ - **Fully Agnostic:** It doesn't rely on any framework, just on [InversifyJS](https://inversify.io/) as it uses it under-the-hood to build the containers. It works the same both client side and server side.
63
77
 
64
78
  ## Installation
65
79
 
@@ -69,6 +83,10 @@ First, ensure you have [`reflect-metadata`](https://www.npmjs.com/package/reflec
69
83
  npm i reflect-metadata
70
84
  ```
71
85
 
86
+ > [!NOTE]
87
+ >
88
+ > You may have to add `import 'reflect-metadata'` at the entry point of your application.
89
+
72
90
  Then install `xInjection`:
73
91
 
74
92
  ```sh
@@ -90,113 +108,189 @@ Add the following options to your `tsconfig.json` to enable decorator metadata:
90
108
 
91
109
  ## Getting Started
92
110
 
93
- ### Bootstrapping the AppModule
111
+ ### Quick Start
112
+
113
+ ```ts
114
+ import { Injectable, ProviderModule } from '@adimm/x-injection';
115
+
116
+ @Injectable()
117
+ class HelloService {
118
+ sayHello() {
119
+ return 'Hello, world!';
120
+ }
121
+ }
122
+
123
+ const HelloModule = ProviderModule.create({
124
+ id: 'HelloModule',
125
+ providers: [HelloService],
126
+ exports: [HelloService],
127
+ });
128
+
129
+ const helloService = HelloModule.get(HelloService);
130
+
131
+ console.log(helloService.sayHello());
132
+ // => 'Hello, world!'
133
+ ```
134
+
135
+ ### Glossary
94
136
 
95
- In your application's entry point, import and register the global `AppModule`:
137
+ #### ProviderModule
138
+
139
+ The core class of `xInjection`, if you ever worked with [NestJS](https://nestjs.com/) _(or [Angular](https://angular.dev/))_, you'll find it very familiar.
96
140
 
97
141
  ```ts
98
- import { AppModule } from '@adimm/x-injection';
142
+ const GarageModule = ProviderModule.create({ id: 'GarageModule', imports: [], providers: [], exports: [] });
143
+ ```
99
144
 
100
- AppModule.register({});
145
+ #### AppModule
146
+
147
+ It is a special instance of the `ProviderModule` class which acts as the `root` of your `modules` graph, all _global_ modules will be automatically imported into the `AppModule` and shared across all your modules.
148
+
149
+ #### Blueprint
150
+
151
+ Another core class which most probably you'll end using a lot too, to keep it short, it allows you to plan ahead the `modules` without instantiating them.
152
+
153
+ ```ts
154
+ const CarModuleBlueprint = ProviderModule.blueprint({ id: 'CarModule', imports: [], providers: [], exports: [] });
101
155
  ```
102
156
 
103
- > [!WARNING]
104
- >
105
- > _You must invoke `AppModule.register()` even if you have no global providers. Passing an empty object `{}` to the method is valid._
157
+ #### Definition
106
158
 
107
- > [!NOTE]
108
- >
109
- > _You can import additional modules (later at run-time) into the `AppModule` by using the [lazyImport](https://adimarianmutu.github.io/x-injection/interfaces/IAppModule.html#lazyimport-1) `method`._
159
+ It is used to refer to the three main blocks of a module:
160
+
161
+ - [imports](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#imports)
162
+ - [providers](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#providers)
163
+ - [exports](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#exports)
110
164
 
111
- ### Registering Global Providers
165
+ ### Conventions
112
166
 
113
- To make services available throughout your application, register them as global providers:
167
+ The library has some opinionated _naming_ conventions which you should adopt too
168
+
169
+ #### ProviderModule
170
+
171
+ All variables holding an _instance_ of a `ProviderModule` should be written in [PascalCase](https://www.wikidata.org/wiki/Q9761807) and _suffixed_ with `Module`, like this:
114
172
 
115
173
  ```ts
116
- import { AppModule, Injectable } from '@adimm/x-injection';
174
+ const DatabaseModule = ProviderModule.create({...});
175
+ const UserModule = ProviderModule.create({...});
176
+ const CarPartsModule = ProviderModule.create({...});
177
+ ```
117
178
 
118
- @Injectable()
119
- class LoggerService {}
179
+ The `id` property of the `ProviderModule.options` should be the same as the `module` variable name.
120
180
 
121
- @Injectable()
122
- class ConfigService {
123
- constructor(private readonly logger: LoggerService) {}
124
- }
181
+ ```ts
182
+ const DatabaseModule = ProviderModule.create({ id: 'DatabaseModule' });
183
+ const UserModule = ProviderModule.create({ id: 'UserModule' });
184
+ const CarPartsModule = ProviderModule.create({ id: 'CarPartsModule' });
185
+ ```
125
186
 
126
- AppModule.register({
127
- providers: [LoggerService, ConfigService],
128
- });
187
+ If you are exporting a `module` from a designated file, then you should name that file as following:
188
+
189
+ ```
190
+ database.module.ts
191
+ user.module.ts
192
+ car-parts.module.ts
129
193
  ```
130
194
 
131
- Now, `LoggerService` and `ConfigService` can be injected anywhere in your app, including inside all `ProviderModules`.
195
+ > [!TIP]
196
+ >
197
+ > If you install/use the [Material Icon Theme](https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme) VS Code extension, you'll see the `*.module.ts` files with a specific icon.
132
198
 
133
- ### Registering Global Modules
199
+ #### Blueprints
134
200
 
135
- You can also import entire modules into the `AppModule` like so:
201
+ All variables holding an _instance_ of a `ProviderModuleBlueprint` should be written in [PascalCase](https://www.wikidata.org/wiki/Q9761807) too and _suffixed_ with `ModuleBp`, like this:
136
202
 
137
203
  ```ts
138
- const SECRET_TOKEN_PROVIDER = { provide: 'SECRET_TOKEN', useValue: '123' };
139
- const SECRET_TOKEN_2_PROVIDER = { provide: 'SECRET_TOKEN_2', useValue: 123 };
140
-
141
- const ConfigModuleDef = new ProviderModuleDefinition({
142
- identifier: 'ConfigModule',
143
- isGlobal: true,
144
- providers: [SECRET_TOKEN_PROVIDER, SECRET_TOKEN_2_PROVIDER],
145
- exports: [SECRET_TOKEN_PROVIDER, SECRET_TOKEN_2_PROVIDER],
146
- });
204
+ const DatabaseModuleBp = ProviderModule.blueprint({...});
205
+ const UserModuleBp = ProviderModule.blueprint({...});
206
+ const CarPartsModuleBp = ProviderModule.blueprint({...});
207
+ ```
147
208
 
148
- AppModule.register({
149
- imports: [ConfigModuleDef],
150
- });
209
+ The `id` property of the `ProviderModuleBlueprint.options` should **not** end with `Bp` because when you'll import that `blueprint` into a `module`, the exact provided `id` will be used!
210
+
211
+ ```ts
212
+ const DatabaseModuleBp = ProviderModule.create({ id: 'DatabaseModule' });
213
+ const UserModuleBp = ProviderModule.create({ id: 'UserModule' });
214
+ const CarPartsModuleBp = ProviderModule.create({ id: 'CarPartsModule' });
215
+ ```
216
+
217
+ If you are exporting a `blueprint` from a designated file, then you should name that file as following:
218
+
219
+ ```
220
+ database.module.bp.ts
221
+ user.module.bp.ts
222
+ car-parts.module.bp.ts
151
223
  ```
152
224
 
225
+ #### ProviderToken
226
+
227
+ All variables holding an _object_ representing a [ProviderToken](https://adimarianmutu.github.io/x-injection/types/ProviderToken.html) should be written in [SCREAMING_SNAKE_CASE](https://en.wikipedia.org/wiki/Snake_case) and _suffixed_ with `_PROVIDER`, like this:
228
+
229
+ ```ts
230
+ const USER_SERVICE_PROVIDER = UserService;
231
+ ```
232
+
233
+ If you are exporting a `provider token` from a designated file, then you should name that file as following:
234
+
235
+ ```
236
+ user-service.provider.ts
237
+ ```
238
+
239
+ ### AppModule
240
+
241
+ As explained above, it is the `root` module of your application, it is always available and eagerly bootstrapped.
242
+
243
+ Usually you'll not interact much with it as any `module` which is defined as _global_ will be automatically imported into it, therefore having its `exports` definition available across all your modules _out-of-the-box_. However, you can use it like any `ProviderModule` instance.
244
+
153
245
  > [!WARNING]
154
246
  >
155
- > _Importing a module marked as global into a scoped module will automatically import it into the `AppModule` rather than the scoped module itself!_
247
+ > Importing the `AppModule` into any `module` will throw an error!
156
248
 
157
- This means that you can also do the following:
249
+ You have 2 options to access it:
158
250
 
159
251
  ```ts
160
- const FancyModuleDefinition = new ProviderModuleDefinition({
161
- identifier: 'FancyModule',
162
- isGlobal: true,
163
- providers: [FancyService],
164
- exports: [FancyService],
165
- });
252
+ import { AppModule } from '@adimm/x-injection';
253
+ ```
166
254
 
167
- const ScopedModule = new ProviderModule({
168
- identifier: 'ScopedModule',
169
- // This `ScopedModule` will automatically "forward" the global `FancyModuleDefinition`
170
- // exports to the `AppModule`.
171
- imports: [FancyModuleDefinition],
172
- });
255
+ or
173
256
 
174
- AppModule.get(FancyService);
175
- // returns the global instance of the `FancyService` class
257
+ ```ts
258
+ import { ProviderModule } from '@adimm/x-injection';
259
+
260
+ ProviderModule.APP_MODULE_REF;
261
+
262
+ // This option is mostly used internally, but you can 100% safely use it as well.
176
263
  ```
177
264
 
178
- The above can also be written as:
265
+ Providing global services to the `AppModule`:
179
266
 
180
267
  ```ts
181
- const FancyModuleDefinition = new ProviderModuleDefinition({
182
- identifier: 'FancyModule',
183
- isGlobal: true,
184
- providers: [FancyService],
185
- exports: [FancyService],
186
- });
268
+ @Injectable()
269
+ class UserService {}
187
270
 
188
- AppModule.lazyImport(FancyModuleDefinition);
271
+ AppModule.update.addProvider(UserService);
189
272
  ```
190
273
 
191
274
  > [!NOTE]
192
275
  >
193
- > _All modules which are imported into the `AppModule` must have the [isGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#isGlobal) option set to `true`, otherwise the [InjectionProviderModuleGlobalMarkError](https://adimarianmutu.github.io/x-injection/classes/InjectionProviderModuleGlobalMarkError.html) exception will be thrown!_
194
- >
195
- > _An [InjectionProviderModuleGlobalMarkError](https://adimarianmutu.github.io/x-injection/classes/InjectionProviderModuleGlobalMarkError.html) exception will be thrown also when importing into the `AppModule` a module which does **not** have the [isGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#isGlobal) flag option!_
276
+ > All `providers` scope is set to [Singleton](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#singleton) by default if not provided.
277
+
278
+ Yes, that's it, now you have access to the `UserService` anywhere in your app across your `modules`, meaning that you can now do:
196
279
 
197
- ### Injection Scope
280
+ ```ts
281
+ const UnrelatedModule = ProviderModule.create({ id: 'UnrelatedModule' });
282
+
283
+ const userService = UnrelatedModule.get(UserService);
284
+ // returns the `userService` singleton instance.
285
+ ```
198
286
 
199
- There are mainly 3 first-class ways to set the [InjectionScope](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html) of a provider, and each one has an order priority.
287
+ ## ProviderModule API
288
+
289
+ You can see all the available `properties` and `methods` of the `ProviderModule` [here](https://adimarianmutu.github.io/x-injection/classes/IProviderModule.html).
290
+
291
+ ## Injection Scope
292
+
293
+ There are mainly 3 first-class ways to set the [InjectionScope](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html) of a `provider`, and each one has an order priority.
200
294
  The below list shows them in order of priority _(highest to lowest)_, meaning that if 2 _(or more)_ ways are used, the method with the highest priority will take precedence.
201
295
 
202
296
  1. By providing the [scope](https://adimarianmutu.github.io/x-injection/interfaces/ProviderScopeOption.html) property to the [ProviderToken](https://adimarianmutu.github.io/x-injection/types/ProviderToken.html):
@@ -224,7 +318,7 @@ The below list shows them in order of priority _(highest to lowest)_, meaning th
224
318
  >
225
319
  > _Imported modules/providers retain their original `InjectionScope`!_
226
320
 
227
- #### Singleton
321
+ ### Singleton
228
322
 
229
323
  The [Singleton](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#singleton) injection scope means that once a dependency has been resolved from within a module will be cached and further resolutions will use the value from the cache.
230
324
 
@@ -235,7 +329,7 @@ expect(MyModule.get(MyProvider)).toBe(MyModule.get(MyProvider));
235
329
  // true
236
330
  ```
237
331
 
238
- #### Transient
332
+ ### Transient
239
333
 
240
334
  The [Transient](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#transient) injection scope means that a _new_ instance of the dependency will be used whenever a resolution occurs.
241
335
 
@@ -246,7 +340,7 @@ expect(MyModule.get(MyProvider)).toBe(MyModule.get(MyProvider));
246
340
  // false
247
341
  ```
248
342
 
249
- #### Request
343
+ ### Request
250
344
 
251
345
  The [Request](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#request) injection scope means that the _same_ instance will be used when a resolution happens in the _same_ request scope.
252
346
 
@@ -282,235 +376,352 @@ expect(winstonLibrary.metro2033).toBe(londonLibrary.metro2033);
282
376
  // false
283
377
  ```
284
378
 
285
- ## Provider Modules
379
+ ## Provider Tokens
286
380
 
287
- You can define `modules` to encapsulate related providers and manage their scope:
381
+ A [ProviderToken](https://adimarianmutu.github.io/x-injection/types/ProviderToken.html) is another core block of `xInjection` _(and also many other IoC/DI libs)_ which is used to define a `token` which can then be used to resolve a `provider`.
288
382
 
289
- ```ts
290
- import { Injectable, InjectionScope, ProviderModule } from '@adimm/x-injection';
383
+ `xInjection` offers _4_ types of tokens:
291
384
 
292
- @Injectable()
293
- export class DatabaseService {
294
- // Implementation...
295
- }
385
+ - [ProviderIdentifier](https://adimarianmutu.github.io/x-injection/types/ProviderIdentifier.html)
386
+ - It allows you to bind a `value` to a specific _transparent_ token, like a `Class`, `Function`, `symbol` or `string`:
387
+ ```ts
388
+ const API_SERVICE_PROVIDER = ApiService;
389
+ // or
390
+ const CONSTANT_SECRET_PROVIDER = 'Shh';
391
+ ```
392
+ - [ProviderClassToken](https://adimarianmutu.github.io/x-injection/types/ProviderClassToken.html)
296
393
 
297
- @Injectable()
298
- export class SessionService {
299
- constructor(public readonly userService: UserService) {}
394
+ - It can be used define the token _and_ the provider:
300
395
 
301
- // Implementation...
302
- }
396
+ ```ts
397
+ const HUMAN_SERVICE_PROVIDER = { provide: HumanService, useClass: FemaleService };
303
398
 
304
- export const DatabaseModuleDef = new ProviderModuleDefinitionDef({
305
- identifier: 'DatabaseModule',
306
- providers: [DatabaseService],
307
- exports: [DatabaseService],
308
- onReady: async (module) => {
309
- const databaseService = module.get(DatabaseService);
399
+ // This will bind `HumanService` as the `token` and will resolve `FemaleService` from the container.
400
+ ```
310
401
 
311
- // Additional initialization...
312
- },
313
- onDispose: () => {
314
- return {
315
- before: async (module) => {
316
- // It is invoked right before the dispose process begins.
317
- // This means that the `module` container is still available to be used.
402
+ - [ProviderValueToken](https://adimarianmutu.github.io/x-injection/types/ProviderValueToken.html)
318
403
 
319
- const databaseService = module.get(DatabaseService);
404
+ - It can be used to easily bind _constant_ values, it can be anything, but once resolved it'll be cached and re-used upon further resolutions
320
405
 
321
- databaseService.closeConnection();
322
- },
323
- after: async (module) => {
324
- // It is invoked right after the dispose process ended.
325
- // This means that the `module` container is not available anymore.
326
- },
327
- };
328
- },
329
- });
406
+ ```ts
407
+ const THEY_DONT_KNOW_PROVIDER = { provide: CONSTANT_SECRET_PROVIDER, useValue: `They'll never know` };
408
+ const THEY_MAY_KNOW_PROVIDER = { provide: CONSTANT_SECRET_PROVIDER, useValue: 'Maybe they know?' };
330
409
 
331
- export const SessionModuleDef = new ProviderModuleDefinition({
332
- identifier: 'SessionModule',
333
- defaultScope: InjectionScope.Request,
334
- providers: [SessionService],
335
- exports: [SessionService],
336
- });
337
- ```
410
+ // As you can see we now have 2 different ProviderTokens which use the same `provide` key.
411
+ ```
338
412
 
339
- Register these modules in your `AppModule`:
413
+ - [ProviderFactoryToken](https://adimarianmutu.github.io/x-injection/types/ProviderFactoryToken.html)
340
414
 
341
- ```ts
342
- AppModule.register({
343
- imports: [DatabaseModuleDef, SessionModuleDef],
344
- });
345
- ```
415
+ - It can be used to bind a `factory` which is intended for more complex scenarios:
346
416
 
347
- > [!WARNING]
417
+ ```ts
418
+ const MAKE_PIZZA_PROVIDER = {
419
+ provide: 'MAKE_PIZZA',
420
+ useFactory: async (apiService: ApiService, pizzaService: PizzaService) => {
421
+ const typeOfPizza = await apiService.getTypeOfPizza();
422
+
423
+ if (typeOfPizza === 'margherita') return pizzaService.make.margherita;
424
+ if (typeOfPizza === 'quattro_stagioni') return pizzaService.make.quattroStagioni;
425
+ // and so on
426
+ },
427
+ // optional
428
+ inject: [API_SERVICE_PROVIDER, PizzaService],
429
+ };
430
+ ```
431
+
432
+ These are all the available `ProviderToken` you can use.
433
+
434
+ > [!NOTE]
348
435
  >
349
- > The `AppModule.register` method can be invoked only _once_, preferably during your application bootstrapping process. _(you may re-invoke it only after the module has been disposed)_
436
+ > In `NestJS` and `Angular` you can't use a `ProviderToken` to _get_ a value, `xInjection` allows this pattern, but you must understand that what it actually does, is to use the _value_ from the `provide` property.
437
+
438
+ ## Provider Modules
350
439
 
351
- From now on, the `AppModule` container has the references of the `DatabaseService` and the `SessionService`.
440
+ As you already saw till here, everything relies around the `ProviderModule` class, so let's dive a little more deep into understanding it.
352
441
 
353
- **Inject multiple dependencies:**
442
+ The most straight forward way to _create/instantiate_ a new `module` is:
354
443
 
355
444
  ```ts
356
- const [serviceA, serviceB] = BigModule.getMany(ServiceA, ServiceB);
357
- // or
358
- const [serviceC, serviceD] = BigModule.getMany<[ServiceC, ServiceD]>(SERVICE_TOKEN, 'SERVICE_ID');
445
+ const MyModule = ProviderModule.create({
446
+ id: 'MyModule',
447
+ imports: [AnotherModule, SecondModule, ThirdModule],
448
+ providers: [
449
+ { provide: CONSTANT_SECRET_PROVIDER, useValue: 'ultra secret' },
450
+ PizzaService,
451
+ { provide: HumanService, useClass: FemaleService },
452
+ ],
453
+ exports: [SecondModule, ThirdModule, PizzaService],
454
+ });
359
455
  ```
360
456
 
361
- ### ProviderModuleDefinition
457
+ From what we can see, the `MyModule` is importing into it 3 more modules, each of them may export one or more _(maybe nothing, that's valid too)_ providers, or even other `modules`.
458
+ Because we imported them into the `MyModule`, now we have access to any providers they may have chosen to export, and the same is true also for _their exported_ modules.
362
459
 
363
- When you do:
460
+ Then, we've chosen to _re-export_ from the `MyModule` the `SecondModule` and `ThirdModule`, meaning that if a different `module` imports `MyModule`, it'll automatically get access to those 2 modules as well. And in the end we also exported our own `PizzaService`, while the remaining other 2 providers, `CONSTANT_SECRET_PROVIDER` and `HumanService` can't be accessed when importing `MyModule`.
364
461
 
365
- ```ts
366
- const MyModule = new ProviderModule({...});
367
- ```
462
+ This is the _core_ feature of `xInjection` _(and `Angular`/`NestJS` DI system)_, being able to encapsulate the providers, so nothing can spill out without our explicit consent.
368
463
 
369
- The `MyModule` will be eagerly instantiated, therefore creating under-the-hood an unique container for the `MyModule` instance.
464
+ ---
370
465
 
371
- In some scenarios you may need/want to avoid that, you can achieve that by using the [ProviderModuleDefinition](https://adimarianmutu.github.io/x-injection/interfaces/IProviderModuleDefinition.html) `class`. It allows you to just define a _blueprint_ of the `ProviderModule` without all the overhead of instantiating the actual module.
466
+ We could also achieve the above by using the `ProviderModule` API like this:
372
467
 
373
468
  ```ts
374
- const GarageModuleDefinition = new ProviderModuleDefinition({ identifier: 'GarageModuleDefinition' });
375
-
376
- // You can always edit all the properties of the definition.
469
+ MyModule.update.addImport(AnotherModule);
470
+ MyModule.update.addImport(SecondModule, true); // `true` means "also add to the `exports` definition"
471
+ MyModule.update.addImport(ThirdModule, true);
377
472
 
378
- GarageModuleDefinition.imports = [...GarageModuleDefinition.imports, PorscheModule, FerrariModuleDefinition];
473
+ MyModule.update.addProvider({ provide: CONSTANT_SECRET_PROVIDER, useValue: 'ultra secret' });
474
+ MyModule.update.addProvider(PizzaService, true);
475
+ MyModule.update.addProvider({ provide: HumanService, useClass: FemaleService });
379
476
  ```
380
477
 
381
- #### Feed to `new ProviderModule`
478
+ Sometimes you may actually want to _lazy_ import a `module` from a _file_, this can be done very easily with `xInjection`:
382
479
 
383
480
  ```ts
384
- const GarageModule = new ProviderModule(GarageModuleDefinition);
481
+ (async () => {
482
+ await MyModule.update.addImportLazy(async () => (await import('./lazy.module')).LazyModule);
483
+
484
+ MyModule.isImportingModule('LazyModule');
485
+ // => true
486
+ })();
385
487
  ```
386
488
 
387
- #### Feed to `lazyImport`
489
+ > [!TIP]
490
+ >
491
+ > This design pattern is _extremely_ powerful and useful when you may have a lot of `modules` initializing during the app bootstrap process as you can defer their initialization, or even never load them if the user never needs those specific `modules` _(this is mostly applicable on the client-side rather than the server-side)_
388
492
 
389
- ```ts
390
- ExistingModule.lazyImport(GarageModuleDefinition);
391
- ```
493
+ Keep reading to understand how you can defer initialization of the modules _both_ `client-side` and `server-side`.
494
+
495
+ ## Blueprints
496
+
497
+ The [ProviderModuleBlueprint](https://adimarianmutu.github.io/x-injection/classes/ProviderModuleBlueprint.html) `class` main purpose is to encapsulate the `definitions` of a `Module`, when you do `ProviderModule.blueprint({...})` you are _not_ actually creating an instance of the `ProviderModule` class, but an instance of the `ProviderModuleBlueprint` class.
498
+
499
+ Before diving into some examples, let's first clarify some important aspects about the behavior of the `blueprint` class.
500
+
501
+ ### Import Behavior
502
+
503
+ Whenever you _import_ a `blueprint` into a `module`, it'll automatically be "transformed" to a `ProviderModule` instance by the engine, this step is crucial as a `blueprint` per se does not contain a _container_, just its _definitions_.
504
+
505
+ This has a "gotcha", because the conversion happens internally, you'll "not" be able to get a reference to that imported module, not directly at least _(I may change this in a future patch)_, this means that if you don't have the reference of the `ProviderModule`, you can't remove it from the _imported_ module _(if you'll ever need to remove it)_.
392
506
 
393
507
  > [!NOTE]
394
508
  >
395
- > _Providing it to the `lazyImport` method will automatically instantiate a new `ProviderModule` on-the-fly!_
509
+ > There's currently an workaround for this, you can `subscribe` to the `Import` event and get from there the `ProviderModule` instance, just make sure to check the `id` of the module via `module.toString()` when doing so.
396
510
 
397
- ### Lazy `imports` and `exports`
511
+ It is also important, to understand the _injection_ `scope` of an imported `blueprint`; we previously learned that when we import a `blueprint` into a `module` it automatically creates an instance of the `ProviderModule` from it, this means that all the `singleton` providers of the `blueprint` definition are now _scoped singleton_, where _scoped_ means _singleton in relation to their imported module_.
398
512
 
399
- You can also lazy import or export `providers`/`modules`, usually you don't need this feature, but there may be some advanced cases where you may want to be able to do so.
513
+ ### isGlobal
400
514
 
401
- > The lazy nature defers the actual module resolution, this may help in breaking immediate circular reference chain under some circumstances.
515
+ When you initialize a `blueprint` with the [isGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#isglobal) property set to `true`, the out-of-the-box behavior is to _automatically_ import the `blueprint` into the `AppModule`. You can disable this behavior by setting the [autoImportIntoAppModuleWhenGlobal](https://adimarianmutu.github.io/x-injectioninterfaces/ModuleBlueprintOptions.html#autoimportintoappmodulewhenglobal) property to `false`
516
+
517
+ ```ts
518
+ const GlobalModuleBp = ProviderModule.blueprint({..., isGlobal: true }, { autoImportIntoAppModuleWhenGlobal: false });
519
+ ```
402
520
 
403
- #### Imports
521
+ Now you can decide when to import it into the `AppModule` by doing `AppModule.addImport(GlobalModuleBp)`.
404
522
 
405
- You can lazily `import` a `module` by invoking the [lazyImport](https://adimarianmutu.github.io/x-injection/interfaces/IProviderModule.html#lazyimport) `method` at any time in your code.
523
+ ---
524
+
525
+ I highly recommend to take advantage of the `blueprints` nature in order to plan-ahead your `modules` and import them wherever you have to import only when needed;
526
+
527
+ Why?
528
+
529
+ - To _define module configurations upfront_ without incurring the cost of immediate initialization _(even if negligible)_.
530
+ - To reuse module _definitions across_ different parts of your application while maintaining isolated instances. _(when possible/applicable)_
531
+ - To _compose modules flexibly_, allowing you to adjust module dependencies dynamically before instantiation.
532
+
533
+ ## Advanced Usage
534
+
535
+ > [!WARNING]
536
+ >
537
+ > This section covers advanced features which may add additional complexity _(or even bugs)_ to your application if you misuse them, use these features only if truly needed and after evaluating the _pros_ and _cons_ of each.
538
+
539
+ ### Events
540
+
541
+ Each `module` will emit specific events through its life-cycle and you can intercept them by using the `Module.update.subscribe` method.
542
+
543
+ > [!TIP]
544
+ >
545
+ > [Here](https://adimarianmutu.github.io/x-injection/enums/DefinitionEventType.html) you can see all the available `events`
546
+
547
+ If you'd need to intercept a `get` request, you can achieve that by doing:
406
548
 
407
549
  ```ts
408
- const GarageModule = new ProviderModule({
409
- identifier: 'GarageModule',
410
- // Eager imports happen at module initialization
411
- imports: [FerrariModule, PorscheModule, ...]
550
+ const CarModule = ProviderModule.create({
551
+ id: 'CarModule',
552
+ providers: [CarService],
412
553
  });
413
554
 
414
- // Later in your code
555
+ CarModule.update.subscribe(({ type, change }) => {
556
+ // We are interested only in the `GetProvider` event.
557
+ if (type !== DefinitionEventType.GetProvider) return;
558
+
559
+ // As our `CarModule` has only one provider, it is safe to assume
560
+ // that the `change` will always be the `CarService` instance.
561
+ const carService = change as CarService;
562
+
563
+ console.log('CarService: ', carService);
564
+ });
415
565
 
416
- GarageModule.lazyImport(LamborghiniModule, BugattiModule, ...);
566
+ const carService = CarModule.get(CarService);
567
+ // logs => CarService: <instance_of_car_service_here>
417
568
  ```
418
569
 
419
- #### Exports
570
+ > [!WARNING]
571
+ >
572
+ > After subscribing to a `ProviderModule` signal emission, you should make sure to also `unsubscribe` if you don't need anymore to intercept the changes, not doing
573
+ > so may cause memory leaks if you have lots of `subscriptions` which do heavy computations!
420
574
 
421
- You can lazily `export` a `provider` or `module` by providing a `callback` _(it can also be an `async` callback)_ as shown below:
575
+ The `subscribe` method will _always_ return a `method` having the signature `() => void`, when you invoke it, it'll close the pipe which intercepts the signal emitted by the `module`:
422
576
 
423
577
  ```ts
424
- const SecureBankBranchModuleDef = new ProviderModuleDef({
425
- identifier: 'SecureBankBranchModule',
426
- providers: [BankBranchService],
427
- exports: [BankBranchService],
578
+ const unsubscribe = CarModule.update.subscribe(({ type, change }) => {
579
+ /* heavy computation here */
428
580
  });
429
581
 
430
- const BankModuleDef = new ProviderModuleDef({
431
- identifier: 'BankModule',
432
- imports: [SecureBankBranchModuleDef],
433
- exports: [..., (importerModule) => {
434
- // When the module having the identifier `UnknownBankModule` imports the `BankModule`
435
- // it'll not be able to also import the `SecureBankBranchModule` as we are not returning it here.
436
- if (importerModule.toString() === 'UnknownBankModule') return;
437
-
438
- // Otherwise we safely export it
439
- return SecureBankBranchModule;
440
- }]
441
- });
582
+ // later in your code
583
+
584
+ unsubscribe();
442
585
  ```
443
586
 
444
- ## Advanced Usage
587
+ > [!NOTE]
588
+ >
589
+ > Events are _always_ invoked _after_ middlewares
590
+
591
+ ### Middlewares
445
592
 
446
- ### ProviderModuleNaked Interface
593
+ Using middlewares is not encouraged as it allows you to tap into very deep low-level code which can cause unexpected bugs if not implemented carefully, however, `middlewares` are the perfect choice if you want to extend/alter the standard behavior of `module` as it allows you to decide what should happen with a resolved value _before_ it is returned to the `consumer`.
447
594
 
448
- Each `ProviderModule` instance implements the `IProviderModule` interface for simplicity, but can be cast to `IProviderModuleNaked` for advanced operations:
595
+ > [!TIP]
596
+ >
597
+ > [Here](https://adimarianmutu.github.io/x-injection/enums/MiddlewareType.html) you can see all the available `middlewares`
598
+
599
+ Let's say that you want to wrap all the returned values of a specific `module` within an object having this signature `{ timestamp: number; value: any }`. By using the `GetProvider` event will not do the trick because it _doesn't_ allow you to alter/change the actual returned value to the `consumer`, you can indeed alter the _content_ via reference, but not the _actual_ result.
600
+
601
+ So the easiest way to achieve that is by using the `BeforeGet` middleware as shown below:
449
602
 
450
603
  ```ts
451
- const nakedModule = ProviderModuleInstance.toNaked();
452
- // or: nakedModule = ProviderModuleInstance as IproviderModuleNaked;
453
- const inversifyContainer = nakedModule.container;
604
+ const TransactionModule = ProviderModule.create(TransactionModuleBp);
605
+
606
+ TransactionModule.middlewares.add(MiddlewareType.BeforeGet, (provider, providerToken, inject) => {
607
+ // We are interested only in the `providers` instances which are from the `Payment` class
608
+ if (!(provider instanceof Payment)) return true;
609
+ // or
610
+ if (providerToken !== 'LAST_TRANSACTION') return true;
611
+
612
+ // DON'T do this as you'll encounter an infinite loop
613
+ const transactionService = TransactionModule.get(TransactionService);
614
+ // If you have to inject into the middleware `context` from the `module`
615
+ // use the `inject` parameter
616
+ const transactionService = inject(TransactionService);
617
+
618
+ return {
619
+ timestamp: transactionService.getTimestamp(),
620
+ value: provider,
621
+ };
622
+ });
623
+
624
+ const transaction = TransactionModule.get('LAST_TRANSACTION');
625
+ // transaction => { timestamp: 1363952948, value: <Payment_instance> }
454
626
  ```
455
627
 
456
- You can also access the global `InversifyJS` container directly:
628
+ One more example is to add a `middleware` in order to _dynamically_ control which `modules` can import a specific `module` by using the [OnExportAccess](https://adimarianmutu.github.io/x-injection/enums/MiddlewareType.html#onexportaccess) flag.
457
629
 
458
630
  ```ts
459
- import { AppModule, GlobalContainer } from '@adimm/x-injection';
631
+ const UnauthorizedBranchBankModule = ProviderModule.create({ id: 'UnauthorizedBranchBankModule' });
632
+ const SensitiveBankDataModule = ProviderModule.create({
633
+ id: 'SensitiveBankDataModule',
634
+ providers: [SensitiveBankDataService, NonSensitiveBankDataService],
635
+ exports: [SensitiveBankDataService, NonSensitiveBankDataService],
636
+ });
460
637
 
461
- const globalContainer = GlobalContainer || AppModule.toNaked().container;
462
- ```
638
+ SensitiveBankDataModule.middlewares.add(MiddlewareType.OnExportAccess, (importerModule, currentExport) => {
639
+ // We want to deny access to our `SensitiveBankDataService` from the `exports` definition if the importer module is `UnauthorizedBranchBankModule`
640
+ if (importerModule.toString() === 'UnauthorizedBranchBankModule' && currentExport === SensitiveBankDataService)
641
+ return false;
463
642
 
464
- For advanced scenarios, `IProviderModuleNaked` exposes additional methods (prefixed with `__`) that wrap InversifyJS APIs, supporting native `xInjection` provider tokens and more.
643
+ // Remaining module are able to import all our `export` definition
644
+ // The `UnauthorizedBranchBankModule` is unable to import the `SensitiveBankDataService`
645
+ return true;
646
+ });
647
+ ```
465
648
 
466
- ### Strict Mode
649
+ > [!CAUTION]
650
+ >
651
+ > Returning `false` in a `middleware` will abort the chain, meaning that for the above example, no value would be returned.
652
+ > If you have to explicitly return a `false` boolean value, you may have to wrap your provider value as an workaround. _(`null` is accepted as a return value)_
653
+ >
654
+ > Meanwhile returning `true` means _"return the value without changing it"_.
655
+ >
656
+ > In the future this behavior may change, so if your business logic relies a lot on `middlewares` make sure to stay up-to-date with the latest changes.
467
657
 
468
- By default the `AppModule` runs in "strict mode", a built-in mode which enforces an _opinionated_ set of rules aiming to reduce common pitfalls and edge-case bugs.
658
+ It is also worth mentioning that you can apply _multiple_ middlewares by just invoking the `middlewares.add` method multiple times, they are executed in the same exact order as you applied them, meaning that the first invokation to `middlewares.add` will actually be the `root` of the chain.
469
659
 
470
- When invoking the [AppModule.register](https://adimarianmutu.github.io/x-injection/interfaces/IAppModule.html#register-1) `method` you can set the [\_strict](https://adimarianmutu.github.io/x-injection/interfaces/AppModuleOptions.html#_strict) property to `false` in order to permanentely disable those set of built-in rules.
660
+ If no error is thrown down the chain, all the registered middleware `callback` will be supplied with the necessary values.
471
661
 
472
662
  > [!WARNING]
473
663
  >
474
- > _Do not open an `issue` if a bug or edge-case is caused by having the `strict` property disabled!_
664
+ > It is the _developer_ responsability to catch any error down the `chain`!
475
665
 
476
- #### Why you should not turn it off:
666
+ ### Internals
477
667
 
478
- ##### isGlobal
668
+ If you are not interested in understanding how `xInjection` works under the hood, you can skip this section 😌
479
669
 
480
- When using the [isGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#isGlobal) property if a `module` is imported into the `AppModule` without having the `isGlobal` flag property set, it'll throw an error.
670
+ #### ProviderModule
481
671
 
482
- This may look redundant, but it may save you _(and your team)_ some hours of debugging in understanding why some `providers` are able to make their way into other `modules`. As those `providers` are now acting as _global_ `providers`.
672
+ It is the head of everything, a `ProviderModule` is actually composed by several classes, each with its own purpose.
483
673
 
484
- Imagine the following scenario:
674
+ > [!TIP]
675
+ >
676
+ > You can get access to _all_ the internal instances by doing `new ProviderModule({...})` instead of `ProviderModule.create({...})`
485
677
 
486
- ```ts
487
- const ScopedModule = new ProviderModule({
488
- identifier: 'ScopedModule',
489
- providers: [...],
490
- exports: [...],
491
- });
678
+ #### MiddlewaresManager
492
679
 
493
- const AnotherScopedModule = new ProviderModule({
494
- identifier: 'AnotherScopedModule',
495
- imports: [ScopedModule],
496
- providers: [...],
497
- exports: [...],
498
- });
680
+ It is the `class` which takes care of managing the registered `middlewares`, check it out [here](https://adimarianmutu.github.io/x-injection/classes/MiddlewaresManager.html).
499
681
 
500
- const GlobalModule = new ProviderModule({
501
- identifier: 'GlobalModule',
502
- isGlobal: true,
503
- imports: [AnotherScopedModule],
504
- });
682
+ Not much to say about it as its main role is to _register_ and _build_ the `middleware` chain.
505
683
 
506
- AppModule.register({
507
- imports: [GlobalModule],
508
- });
684
+ #### ModuleContainer
685
+
686
+ It is the `class` which takes care of managing the `inversify` container, check it out [here](https://adimarianmutu.github.io/x-injection/classes/ModuleContainer.html).
687
+
688
+ Its main purpose is to initialize the module raw _([InversifyJS Container](https://inversify.io/docs/api/container/))_ `class` and to _bind_ the providers to it.
689
+
690
+ #### ImportedModuleContainer
691
+
692
+ It is the `class` which takes care of managing the _imported_ modules, check it out [here](https://adimarianmutu.github.io/x-injection/classes/ImportedModuleContainer.html).
693
+
694
+ Because `modules` can be imported into other modules, therefore creating a _complex_ `graph` of modules, the purpose of this class is to keep track and sync the changes of the `exports` definition of the _imported_ module.
695
+
696
+ The `ProviderModule` API is simple yet very powerful, you may not realize that doing `addImport` will cause _(based on how deep is the imported module)_ a chain reaction which the `ImportedModuleContainer` must keep track of in order to make sure that the _consumer_ `module` which imported the _consumed_ `module` has access only to the `providers`/`modules` explicitly exported by the _consumed_ `module`.
697
+
698
+ Therefore it is encouraged to keep things mostly static, as each `addProvider`, `addImport`, `removeImport` and so on have a penality cost on your application performance. This cost in most cases is negligible, however it highly depends on how the _developer_ uses the feature `xInjection` offers.
699
+
700
+ > "With great power comes great responsibility."
701
+
702
+ #### DynamicModuleDefinition
703
+
704
+ It is the `class` which takes care of managing the _updates_ and _event_ emissions of the `module`, check it out [here](https://adimarianmutu.github.io/x-injection/classes/DynamicModuleDefinition.html).
705
+
706
+ This class is actually the "parent" of the `ImportedModuleContainer` instances, its purpose is to _build_ the _initial_ definition graph, and while doing so it also instantiate _for each_ imported module a new `ImportedModuleContainer`.
707
+
708
+ It also take care of managing the `events` bubbling by checking cirular references and so on.
709
+
710
+ #### ProviderModuleBlueprint
711
+
712
+ It's the "metadata" counterpart of the `ProviderModule` class, as its only purpose is to carry the definitions. Check it out [here](https://adimarianmutu.github.io/x-injection/classes/ProviderModuleBlueprint.html).
713
+
714
+ #### Set of Helpers
715
+
716
+ The library does also export a set of useful helpers in the case you may need it:
717
+
718
+ ```ts
719
+ import { ProviderModuleHelpers, ProviderTokenHelpers } from '@adimm/x-injection';
509
720
  ```
510
721
 
511
- At first glance you may not spot/understand the issue there, but because the `GlobalModule` _(which is then imported into the `AppModule`)_ is _directly_ importing the `AnotherScopedModule`, it means that _all_ the `providers` of the `AnotherScopedModule` and `ScopedModule` _(because `AnotherScopedModule` also imports `ScopedModule`)_ will become accessible through your entire app!
722
+ ---
512
723
 
513
- Disabling `strict` mode removes this safeguard, allowing any module to be imported into the `AppModule` regardless of `isGlobal`, increasing risk of bugs by exposing yourself to the above example.
724
+ This covers pretty much everything about how `xInjection` is built and how it works.
514
725
 
515
726
  ## Unit Tests
516
727
 
@@ -528,12 +739,13 @@ class ApiService {
528
739
  private async sendToLocation(user: User, location: any): Promise<any> {}
529
740
  }
530
741
 
531
- const ApiModuleDefinition = new ProviderModuleDefinition({
532
- identifier: 'ApiModule',
742
+ const ApiModuleBp = new ProviderModule.blueprint({
743
+ id: 'ApiModule',
533
744
  providers: [UserService, ApiService],
534
745
  });
535
746
 
536
- const ApiModuleDefinitionMocked = ApiModule.clone({
747
+ // Clone returns a `deep` clone and wraps all the `methods` to break their reference!
748
+ const ApiModuleBpMocked = ApiModuleBp.clone().updateDefinition({
537
749
  identifier: 'ApiModuleMocked',
538
750
  providers: [
539
751
  {
@@ -552,7 +764,7 @@ const ApiModuleDefinitionMocked = ApiModule.clone({
552
764
  });
553
765
  ```
554
766
 
555
- Now what you have to do is just to provide the `ApiModuleDefinitionMocked` instead of the `ApiModuleDefinition` 😎
767
+ Now what you have to do is just to provide the `ApiModuleBpMocked` instead of the `ApiModuleBp` 😎
556
768
 
557
769
  ## Documentation
558
770
 
@@ -560,34 +772,6 @@ Comprehensive, auto-generated documentation is available at:
560
772
 
561
773
  👉 [https://adimarianmutu.github.io/x-injection/index.html](https://adimarianmutu.github.io/x-injection/index.html)
562
774
 
563
- ## Conventions
564
-
565
- To create a stable experience across different projects, the following _conventions_ should be followed.
566
-
567
- ### ProviderModule
568
-
569
- When instantiating a new `ProviderModule` class you should append `Module` to the `variable` name and its `identifier`:
570
-
571
- ```ts
572
- const DatabaseModule = new ProviderModule({
573
- identifier: 'DatabaseModule',
574
- });
575
- ```
576
-
577
- ### ProviderModuleDefinition
578
-
579
- When instantiating a new `ProviderModuleDefinition` class you should append `ModuleDef` to the `variable` name:
580
-
581
- > [!NOTE]
582
- >
583
- > _Do not append `Def` to the `identifier` as that will be inherit by the actual `ProviderModule`!_
584
-
585
- ```ts
586
- const DatabaseModuleDef = new ProviderModuleDefinition({
587
- identifier: 'DatabaseModule', // No `Def` here!
588
- });
589
- ```
590
-
591
775
  ## ReactJS Implementation
592
776
 
593
777
  You want to use it within a [ReactJS](https://react.dev/) project? Don't worry, the library does already have an official implementation for React ⚛️
@@ -600,6 +784,11 @@ Pull requests are warmly welcomed! 😃
600
784
 
601
785
  Please ensure your contributions adhere to the project's code style. See the repository for more details.
602
786
 
787
+ ## Credits
788
+
789
+ - [Adi-Marian Mutu](https://www.linkedin.com/in/mutu-adi-marian/) - Author of `xInjection`
790
+ - [InversifyJS](https://github.com/inversify/monorepo) - Base lib
791
+
603
792
  ---
604
793
 
605
794
  > [!NOTE]