@adimm/x-injection 0.8.0 → 1.1.6
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 +517 -441
- package/dist/index.cjs +347 -320
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +384 -390
- package/dist/index.d.ts +384 -390
- package/dist/index.js +324 -298
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,441 +1,517 @@
|
|
|
1
|
-
<h1 align="center">
|
|
2
|
-
xInjection <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
|
-
<a href="https://app.codecov.io/gh/AdiMarianMutu/x-injection" target="__blank"><img src="https://badgen.net/codecov/c/github/AdiMarianMutu/x-injection"></a>
|
|
5
|
-
</h1>
|
|
6
|
-
|
|
7
|
-
<p align="center">
|
|
8
|
-
<a href="https://github.com/AdiMarianMutu/x-injection/actions/workflows/ci.yml?query=branch%3Amain" target="__blank"><img src="https://github.com/AdiMarianMutu/x-injection/actions/workflows/ci.yml/badge.svg?branch=main"></a>
|
|
9
|
-
<a href="https://github.com/AdiMarianMutu/x-injection/actions/workflows/publish.yml" target="__blank"><img src="https://github.com/AdiMarianMutu/x-injection/actions/workflows/publish.yml/badge.svg"></a>
|
|
10
|
-
<br>
|
|
11
|
-
<img src="https://badgen.net/bundlephobia/minzip/@adimm/x-injection">
|
|
12
|
-
<a href="https://www.npmjs.com/package/@adimm/x-injection" target="__blank"><img src="https://badgen.net/npm/dm/@adimm/x-injection"></a>
|
|
13
|
-
</p>
|
|
14
|
-
|
|
15
|
-
## Table of Contents
|
|
16
|
-
|
|
17
|
-
- [Table of Contents](#table-of-contents)
|
|
18
|
-
- [Overview](#overview)
|
|
19
|
-
- [Features](#features)
|
|
20
|
-
- [Installation](#installation)
|
|
21
|
-
- [TypeScript Configuration](#typescript-configuration)
|
|
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)
|
|
30
|
-
- [
|
|
31
|
-
- [
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
35
|
-
- [
|
|
36
|
-
- [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
AppModule
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
2
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
1
|
+
<h1 align="center">
|
|
2
|
+
xInjection <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
|
+
<a href="https://app.codecov.io/gh/AdiMarianMutu/x-injection" target="__blank"><img src="https://badgen.net/codecov/c/github/AdiMarianMutu/x-injection"></a>
|
|
5
|
+
</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/AdiMarianMutu/x-injection/actions/workflows/ci.yml?query=branch%3Amain" target="__blank"><img src="https://github.com/AdiMarianMutu/x-injection/actions/workflows/ci.yml/badge.svg?branch=main"></a>
|
|
9
|
+
<a href="https://github.com/AdiMarianMutu/x-injection/actions/workflows/publish.yml" target="__blank"><img src="https://github.com/AdiMarianMutu/x-injection/actions/workflows/publish.yml/badge.svg"></a>
|
|
10
|
+
<br>
|
|
11
|
+
<img src="https://badgen.net/bundlephobia/minzip/@adimm/x-injection">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@adimm/x-injection" target="__blank"><img src="https://badgen.net/npm/dm/@adimm/x-injection"></a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Table of Contents](#table-of-contents)
|
|
18
|
+
- [Overview](#overview)
|
|
19
|
+
- [Features](#features)
|
|
20
|
+
- [Installation](#installation)
|
|
21
|
+
- [TypeScript Configuration](#typescript-configuration)
|
|
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)
|
|
30
|
+
- [Provider Modules](#provider-modules)
|
|
31
|
+
- [ProviderModuleDefinition](#providermoduledefinition)
|
|
32
|
+
- [Feed to `new ProviderModule`](#feed-to-new-providermodule)
|
|
33
|
+
- [Feed to `lazyImport`](#feed-to-lazyimport)
|
|
34
|
+
- [Why not just use the `ProviderModuleOptions` interface?](#why-not-just-use-the-providermoduleoptions-interface)
|
|
35
|
+
- [Lazy `imports` and `exports`](#lazy-imports-and-exports)
|
|
36
|
+
- [Imports](#imports)
|
|
37
|
+
- [Exports](#exports)
|
|
38
|
+
- [Advanced Usage](#advanced-usage)
|
|
39
|
+
- [ProviderModuleNaked Interface](#providermodulenaked-interface)
|
|
40
|
+
- [Strict Mode](#strict-mode)
|
|
41
|
+
- [Why you should not turn it off:](#why-you-should-not-turn-it-off)
|
|
42
|
+
- [MarkAsGlobal](#markasglobal)
|
|
43
|
+
- [Unit Tests](#unit-tests)
|
|
44
|
+
- [Documentation](#documentation)
|
|
45
|
+
- [ReactJS Implementation](#reactjs-implementation)
|
|
46
|
+
- [Contributing](#contributing)
|
|
47
|
+
|
|
48
|
+
## Overview
|
|
49
|
+
|
|
50
|
+
**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.
|
|
51
|
+
|
|
52
|
+
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.
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- **NestJS-inspired module system:** Import and export providers between modules.
|
|
57
|
+
- **Granular dependency encapsulation:** Each module manages its own container.
|
|
58
|
+
- **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.
|
|
59
|
+
- **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.
|
|
60
|
+
- **Advanced container access:** Directly interact with the underlying [InversifyJS containers](https://inversify.io/docs/api/container/) if needed.
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
First, ensure you have [`reflect-metadata`](https://www.npmjs.com/package/reflect-metadata) installed:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
npm i reflect-metadata
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Then install `xInjection`:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
npm i @adimm/x-injection
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### TypeScript Configuration
|
|
77
|
+
|
|
78
|
+
Add the following options to your `tsconfig.json` to enable decorator metadata:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"compilerOptions": {
|
|
83
|
+
"experimentalDecorators": true,
|
|
84
|
+
"emitDecoratorMetadata": true
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Getting Started
|
|
90
|
+
|
|
91
|
+
### Bootstrapping the AppModule
|
|
92
|
+
|
|
93
|
+
In your application's entry point, import and register the global `AppModule`:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { AppModule } from '@adimm/x-injection';
|
|
97
|
+
|
|
98
|
+
AppModule.register({});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
> **Note:** You must call `AppModule.register()` even if you have no global providers. Passing an empty object `{}` is valid.
|
|
102
|
+
|
|
103
|
+
### Registering Global Providers
|
|
104
|
+
|
|
105
|
+
To make services available throughout your application, register them as global providers:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { AppModule, Injectable } from '@adimm/x-injection';
|
|
109
|
+
|
|
110
|
+
@Injectable()
|
|
111
|
+
class LoggerService {}
|
|
112
|
+
|
|
113
|
+
@Injectable()
|
|
114
|
+
class ConfigService {
|
|
115
|
+
constructor(private readonly logger: LoggerService) {}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
AppModule.register({
|
|
119
|
+
providers: [LoggerService, ConfigService],
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Now, `LoggerService` and `ConfigService` can be injected anywhere in your app, including inside all `ProviderModules`.
|
|
124
|
+
|
|
125
|
+
### Registering Global Modules
|
|
126
|
+
|
|
127
|
+
You can also import entire modules into the `AppModule` like so:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const SECRET_TOKEN_PROVIDER = { provide: 'SECRET_TOKEN', useValue: '123' };
|
|
131
|
+
const SECRET_TOKEN_2_PROVIDER = { provide: 'SECRET_TOKEN_2', useValue: 123 };
|
|
132
|
+
|
|
133
|
+
const ConfigModule = new ProviderModule({
|
|
134
|
+
identifier: Symbol('ConfigModule'),
|
|
135
|
+
markAsGlobal: true,
|
|
136
|
+
providers: [SECRET_TOKEN_PROVIDER, SECRET_TOKEN_2_PROVIDER],
|
|
137
|
+
exports: [SECRET_TOKEN_PROVIDER, SECRET_TOKEN_2_PROVIDER],
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
AppModule.register({
|
|
141
|
+
imports: [ConfigModule],
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
> **Note:** _All modules which are imported into the `AppModule` must have the [markAsGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#markasglobal) option set to `true`, otherwise the [InjectionProviderModuleGlobalMarkError](https://adimarianmutu.github.io/x-injection/classes/InjectionProviderModuleGlobalMarkError.html) exception will be thrown!_
|
|
146
|
+
>
|
|
147
|
+
> **Note2:** _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 [markAsGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#markasglobal) flag option!_
|
|
148
|
+
|
|
149
|
+
### Injection Scope
|
|
150
|
+
|
|
151
|
+
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.
|
|
152
|
+
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.
|
|
153
|
+
|
|
154
|
+
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):
|
|
155
|
+
```ts
|
|
156
|
+
const USER_PROVIDER: ProviderToken<UserService> = {
|
|
157
|
+
scope: InjectionScope.Request,
|
|
158
|
+
provide: UserService,
|
|
159
|
+
useClass: UserService,
|
|
160
|
+
};
|
|
161
|
+
```
|
|
162
|
+
2. Within the [@Injectable](https://adimarianmutu.github.io/x-injection/functions/Injectable.html) decorator:
|
|
163
|
+
```ts
|
|
164
|
+
@Injectable(InjectionScope.Transient)
|
|
165
|
+
class Transaction {}
|
|
166
|
+
```
|
|
167
|
+
3. By providing the [defaultScope](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#defaultscope) property when initializing a `ProviderModule`:
|
|
168
|
+
```ts
|
|
169
|
+
const RainModule = new ProviderModule({
|
|
170
|
+
identifier: Symbol('RainModule'),
|
|
171
|
+
defaultScope: InjectionScope.Transient,
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
> **Note:** _Imported modules/providers retain their original `InjectionScope`!_
|
|
176
|
+
|
|
177
|
+
#### Singleton
|
|
178
|
+
|
|
179
|
+
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.
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
expect(MyModule.get(MyProvider)).toBe(MyModule.get(MyProvider));
|
|
185
|
+
// true
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### Transient
|
|
189
|
+
|
|
190
|
+
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.
|
|
191
|
+
|
|
192
|
+
Example:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
expect(MyModule.get(MyProvider)).toBe(MyModule.get(MyProvider));
|
|
196
|
+
// false
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### Request
|
|
200
|
+
|
|
201
|
+
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.
|
|
202
|
+
|
|
203
|
+
Example:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
@Injectable(InjectionScope.Transient)
|
|
207
|
+
class Book {
|
|
208
|
+
author: string;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@Injectable(InjectionScope.Request)
|
|
212
|
+
class Metro2033 extends Book {
|
|
213
|
+
override author = 'Dmitry Alekseyevich Glukhovsky';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@Injectable(InjectionScope.Transient)
|
|
217
|
+
class Library {
|
|
218
|
+
constructor(
|
|
219
|
+
public readonly metro2033: Metro2033,
|
|
220
|
+
public readonly metro2033_reference: Metro2033
|
|
221
|
+
) {}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const winstonLibrary = MyModule.get(Library);
|
|
225
|
+
const londonLibrary = MyModule.get(Library);
|
|
226
|
+
|
|
227
|
+
expect(winstonLibrary.metro2033).toBe(winstonLibrary.metro2033_reference);
|
|
228
|
+
expect(londonLibrary.metro2033).toBe(londonLibrary.metro2033_reference);
|
|
229
|
+
// true
|
|
230
|
+
|
|
231
|
+
expect(winstonLibrary.metro2033).toBe(londonLibrary.metro2033);
|
|
232
|
+
// false
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Provider Modules
|
|
236
|
+
|
|
237
|
+
You can define `modules` to encapsulate related providers and manage their scope:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { Injectable, InjectionScope, ProviderModule } from '@adimm/x-injection';
|
|
241
|
+
|
|
242
|
+
@Injectable()
|
|
243
|
+
export class DatabaseService {
|
|
244
|
+
// Implementation...
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
@Injectable()
|
|
248
|
+
export class SessionService {
|
|
249
|
+
constructor(public readonly userService: UserService) {}
|
|
250
|
+
|
|
251
|
+
// Implementation...
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export const DatabaseModule = new ProviderModule({
|
|
255
|
+
identifier: Symbol('DatabaseModule'),
|
|
256
|
+
// or: identifier: 'DatabaseModule',
|
|
257
|
+
providers: [DatabaseService],
|
|
258
|
+
exports: [DatabaseService],
|
|
259
|
+
onReady: async (module) => {
|
|
260
|
+
const databaseService = module.get(DatabaseService);
|
|
261
|
+
|
|
262
|
+
// Additional initialization...
|
|
263
|
+
},
|
|
264
|
+
onDispose: async (module) => {
|
|
265
|
+
const databaseService = module.get(DatabaseService);
|
|
266
|
+
|
|
267
|
+
databaseService.closeConnection();
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
export const SessionModule = new ProviderModule({
|
|
272
|
+
identifier: Symbol('SessionModule'),
|
|
273
|
+
defaultScope: InjectionScope.Request,
|
|
274
|
+
providers: [SessionService],
|
|
275
|
+
exports: [SessionService],
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Register these modules in your `AppModule`:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
AppModule.register({
|
|
283
|
+
imports: [DatabaseModule, SessionModule],
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
> **Note:** The `AppModule.register` method can be invoked only _once_! _(You may re-invoke it only after the module has been disposed)_ Preferably during your application bootstrapping process.
|
|
288
|
+
|
|
289
|
+
From now on, the `AppModule` container has the references of the `DatabaseService` and the `SessionService`.
|
|
290
|
+
|
|
291
|
+
**Inject multiple dependencies:**
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
const [serviceA, serviceB] = BigModule.getMany(ServiceA, ServiceB);
|
|
295
|
+
// or
|
|
296
|
+
const [serviceC, serviceD] = BigModule.getMany<[ServiceC, ServiceD]>(SERVICE_TOKEN, 'SERVICE_ID');
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### ProviderModuleDefinition
|
|
300
|
+
|
|
301
|
+
When you do:
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
const MyModule = new ProviderModule({...});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
The `MyModule` will be eagerly instantiated, therefore creating under-the-hood an unique container for the `MyModule` instance.
|
|
308
|
+
|
|
309
|
+
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.
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
const GarageModuleDefinition = new ProviderModuleDefinition({ identifier: 'GarageModuleDefinition' });
|
|
313
|
+
|
|
314
|
+
// You can always edit all the properties of the definition.
|
|
315
|
+
|
|
316
|
+
GarageModuleDefinition.imports = [...GarageModuleDefinition.imports, PorscheModule, FerrariModuleDefinition];
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Feed to `new ProviderModule`
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
const GarageModule = new ProviderModule(GarageModuleDefinition);
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
#### Feed to `lazyImport`
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
ExistingModule.lazyImport(GarageModuleDefinition);
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
> **Note:** _Providing it to the `lazyImport` method will automatically instantiate a new `ProviderModule` on-the-fly!_
|
|
332
|
+
|
|
333
|
+
#### Why not just use the `ProviderModuleOptions` interface?
|
|
334
|
+
|
|
335
|
+
That's a very good question! It means that you understood that the `ProviderModuleDefinition` is actually a `class` wrapper of the `ProviderModuleOptions`.
|
|
336
|
+
|
|
337
|
+
Theoretically you _can_ use a _plain_ `object` having the `ProviderModuleOptions` interface, however, the `ProviderModuleOptions` interface purpose is solely to _expose/shape_ the options with which a module can be instantiated, while the `ProviderModuleDefinition` purpose is to _define_ the actual `ProviderModule` _blueprint_.
|
|
338
|
+
|
|
339
|
+
### Lazy `imports` and `exports`
|
|
340
|
+
|
|
341
|
+
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.
|
|
342
|
+
|
|
343
|
+
> The lazy nature defers the actual module resolution, this may help in breaking immediate circular reference chain under some circumstances.
|
|
344
|
+
|
|
345
|
+
#### Imports
|
|
346
|
+
|
|
347
|
+
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.
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
const GarageModule = new ProviderModule({
|
|
351
|
+
identifier: 'GarageModule',
|
|
352
|
+
// Eager imports happen at module initialization
|
|
353
|
+
imports: [FerrariModule, PorscheModule, ...]
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Later in your code
|
|
357
|
+
|
|
358
|
+
GarageModule.lazyImport(LamborghiniModule, BugattiModule, ...);
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### Exports
|
|
362
|
+
|
|
363
|
+
You can lazily `export` a `provider` or `module` by providing a `callback` _(it can also be an `async` callback)_ as shown below:
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
const SecureBankBranchModule = new ProviderModule({
|
|
367
|
+
identifier: 'SecureBankBranchModule',
|
|
368
|
+
providers: [BankBranchService],
|
|
369
|
+
exports: [BankBranchService],
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const BankModule = new ProviderModule({
|
|
373
|
+
identifier: 'BankModule',
|
|
374
|
+
imports: [SecureBankBranchModule],
|
|
375
|
+
exports: [..., (importerModule) => {
|
|
376
|
+
// When the module having the identifier `UnknownBankModule` imports the `BankModule`
|
|
377
|
+
// it'll not be able to also import the `SecureBankBranchModule` as we are not returning it here.
|
|
378
|
+
if (importerModule.toString() === 'UnknownBankModule') return;
|
|
379
|
+
|
|
380
|
+
// Otherwise we safely export it
|
|
381
|
+
return SecureBankBranchModule;
|
|
382
|
+
}]
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Advanced Usage
|
|
387
|
+
|
|
388
|
+
### ProviderModuleNaked Interface
|
|
389
|
+
|
|
390
|
+
Each `ProviderModule` instance implements the `IProviderModule` interface for simplicity, but can be cast to `IProviderModuleNaked` for advanced operations:
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
const nakedModule = ProviderModuleInstance.toNaked();
|
|
394
|
+
// or: nakedModule = ProviderModuleInstance as IproviderModuleNaked;
|
|
395
|
+
const inversifyContainer = nakedModule.container;
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
You can also access the global `InversifyJS` container directly:
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
import { AppModule, GlobalContainer } from '@adimm/x-injection';
|
|
402
|
+
|
|
403
|
+
const globalContainer = GlobalContainer || AppModule.toNaked().container;
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
For advanced scenarios, `IProviderModuleNaked` exposes additional methods (prefixed with `__`) that wrap InversifyJS APIs, supporting native `xInjection` provider tokens and more.
|
|
407
|
+
|
|
408
|
+
### Strict Mode
|
|
409
|
+
|
|
410
|
+
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.
|
|
411
|
+
|
|
412
|
+
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.
|
|
413
|
+
|
|
414
|
+
> **Note:** _Do not open an `issue` if a bug or edge-case is caused by having the `strict` property disabled!_
|
|
415
|
+
|
|
416
|
+
#### Why you should not turn it off:
|
|
417
|
+
|
|
418
|
+
##### MarkAsGlobal
|
|
419
|
+
|
|
420
|
+
The [markAsGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#markasglobal) flag property is used to make sure that `modules` which should be registered directly into the `AppModule` are indeed provided to the the `imports` array of the `AppModule` and the the other way around, if a `module` is imported into the `AppModule` without having the `markAsGlobal` flag property set, it'll throw an error.
|
|
421
|
+
|
|
422
|
+
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`.
|
|
423
|
+
|
|
424
|
+
Imagine the following scenario:
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
const ScopedModule = new ProviderModule({
|
|
428
|
+
identifier: 'ScopedModule',
|
|
429
|
+
providers: [...],
|
|
430
|
+
exports: [...],
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
const AnotherScopedModule = new ProviderModule({
|
|
434
|
+
identifier: 'AnotherScopedModule',
|
|
435
|
+
imports: [ScopedModule],
|
|
436
|
+
providers: [...],
|
|
437
|
+
exports: [...],
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const GlobalModule = new ProviderModule({
|
|
441
|
+
identifier: 'GlobalModule',
|
|
442
|
+
markAsGlobal: true,
|
|
443
|
+
imports: [AnotherScopedModule],
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
AppModule.register({
|
|
447
|
+
imports: [GlobalModule],
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
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!
|
|
452
|
+
|
|
453
|
+
Disabling `strict` mode removes this safeguard, allowing any module to be imported into the `AppModule` regardless of `markAsGlobal`, increasing risk of bugs by exposing yourself to the above example.
|
|
454
|
+
|
|
455
|
+
## Unit Tests
|
|
456
|
+
|
|
457
|
+
It is very easy to create mock modules so you can use them in your unit tests.
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
class ApiService {
|
|
461
|
+
constructor(private readonly userService: UserService) {}
|
|
462
|
+
|
|
463
|
+
async sendRequest<T>(location: LocationParams): Promise<T> {
|
|
464
|
+
// Pseudo Implementation
|
|
465
|
+
return this.sendToLocation(user, location);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private async sendToLocation(user: User, location: any): Promise<any> {}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const ApiModule = new ProviderModule({
|
|
472
|
+
identifier: Symbol('ApiModule'),
|
|
473
|
+
providers: [UserService, ApiService],
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
const ApiModuleMocked = new ProviderModule({
|
|
477
|
+
identifier: Symbol('ApiModule_MOCK'),
|
|
478
|
+
providers: [
|
|
479
|
+
{
|
|
480
|
+
provide: UserService,
|
|
481
|
+
useClass: UserService_Mock,
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
provide: ApiService,
|
|
485
|
+
useValue: {
|
|
486
|
+
sendRequest: async (location) => {
|
|
487
|
+
console.log(location);
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
});
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Now what you have to do is just to provide the `ApiModuleMocked` instead of the `ApiModule` 😎
|
|
496
|
+
|
|
497
|
+
## Documentation
|
|
498
|
+
|
|
499
|
+
Comprehensive, auto-generated documentation is available at:
|
|
500
|
+
|
|
501
|
+
👉 [https://adimarianmutu.github.io/x-injection/index.html](https://adimarianmutu.github.io/x-injection/index.html)
|
|
502
|
+
|
|
503
|
+
## ReactJS Implementation
|
|
504
|
+
|
|
505
|
+
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 ⚛️
|
|
506
|
+
|
|
507
|
+
For more details check out the [GitHub Repository](https://github.com/AdiMarianMutu/x-injection-reactjs).
|
|
508
|
+
|
|
509
|
+
## Contributing
|
|
510
|
+
|
|
511
|
+
Pull requests are warmly welcomed! 😃
|
|
512
|
+
|
|
513
|
+
Please ensure your contributions adhere to the project's code style. See the repository for more details.
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
> For questions, feature requests, or bug reports, feel free to open an [issue](https://github.com/AdiMarianMutu/x-injection/issues) on GitHub!
|