awilix 9.0.0 → 10.0.1
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 +156 -99
- package/lib/awilix.browser.js +143 -82
- package/lib/awilix.d.ts +6 -6
- package/lib/awilix.js +20 -20
- package/lib/awilix.js.map +1 -1
- package/lib/awilix.module.mjs +135 -84
- package/lib/awilix.umd.js +143 -84
- package/lib/container.d.ts +16 -14
- package/lib/container.js +55 -31
- package/lib/container.js.map +1 -1
- package/lib/errors.d.ts +13 -0
- package/lib/errors.js +27 -8
- package/lib/errors.js.map +1 -1
- package/lib/function-tokenizer.js.map +1 -1
- package/lib/lifetime.d.ts +4 -0
- package/lib/lifetime.js +9 -1
- package/lib/lifetime.js.map +1 -1
- package/lib/list-modules.js.map +1 -1
- package/lib/load-modules.d.ts +2 -2
- package/lib/load-modules.js +1 -1
- package/lib/load-modules.js.map +1 -1
- package/lib/param-parser.js.map +1 -1
- package/lib/resolvers.d.ts +15 -27
- package/lib/resolvers.js +14 -14
- package/lib/resolvers.js.map +1 -1
- package/lib/utils.js.map +1 -1
- package/package.json +16 -15
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](http://standardjs.com/)
|
|
10
10
|
|
|
11
11
|
Extremely powerful, performant, & battle-tested **Dependency Injection** (DI) container for JavaScript/Node,
|
|
12
|
-
written in [TypeScript](http://typescriptlang.org).
|
|
12
|
+
written in [TypeScript](http://typescriptlang.org).
|
|
13
13
|
|
|
14
14
|
Awilix enables you to write **composable, testable software** using dependency injection **without special annotations**, which in turn decouples your core application code from the intricacies of the DI mechanism.
|
|
15
15
|
|
|
@@ -24,6 +24,7 @@ Awilix enables you to write **composable, testable software** using dependency i
|
|
|
24
24
|
- [Usage](#usage)
|
|
25
25
|
- [Lifetime management](#lifetime-management)
|
|
26
26
|
- [Scoped lifetime](#scoped-lifetime)
|
|
27
|
+
- [Strict mode](#strict-mode)
|
|
27
28
|
- [Injection modes](#injection-modes)
|
|
28
29
|
- [Auto-loading modules](#auto-loading-modules)
|
|
29
30
|
- [Per-module local injections](#per-module-local-injections)
|
|
@@ -39,6 +40,7 @@ Awilix enables you to write **composable, testable software** using dependency i
|
|
|
39
40
|
- [`aliasTo()`](#aliasto)
|
|
40
41
|
- [`listModules()`](#listmodules)
|
|
41
42
|
- [`AwilixResolutionError`](#awilixresolutionerror)
|
|
43
|
+
- [`AwilixRegistrationError`](#awilixregistrationerror)
|
|
42
44
|
- [The `AwilixContainer` object](#the-awilixcontainer-object)
|
|
43
45
|
- [`container.cradle`](#containercradle)
|
|
44
46
|
- [`container.registrations`](#containerregistrations)
|
|
@@ -74,9 +76,9 @@ yarn add awilix
|
|
|
74
76
|
You can also use the [UMD](https://github.com/umdjs/umd) build from `unpkg`
|
|
75
77
|
|
|
76
78
|
```html
|
|
77
|
-
<script src="https://unpkg.com/awilix/lib/awilix.umd.js"/>
|
|
79
|
+
<script src="https://unpkg.com/awilix/lib/awilix.umd.js" />
|
|
78
80
|
<script>
|
|
79
|
-
const container = Awilix.createContainer()
|
|
81
|
+
const container = Awilix.createContainer()
|
|
80
82
|
</script>
|
|
81
83
|
```
|
|
82
84
|
|
|
@@ -95,8 +97,10 @@ minimum, you need to do 3 things:
|
|
|
95
97
|
const awilix = require('awilix')
|
|
96
98
|
|
|
97
99
|
// Create the container and set the injectionMode to PROXY (which is also the default).
|
|
100
|
+
// Enable strict mode for extra correctness checks (highly recommended).
|
|
98
101
|
const container = awilix.createContainer({
|
|
99
|
-
injectionMode: awilix.InjectionMode.PROXY
|
|
102
|
+
injectionMode: awilix.InjectionMode.PROXY,
|
|
103
|
+
strict: true,
|
|
100
104
|
})
|
|
101
105
|
|
|
102
106
|
// This is our app code... We can use
|
|
@@ -118,7 +122,7 @@ class UserController {
|
|
|
118
122
|
container.register({
|
|
119
123
|
// Here we are telling Awilix how to resolve a
|
|
120
124
|
// userController: by instantiating a class.
|
|
121
|
-
userController: awilix.asClass(UserController)
|
|
125
|
+
userController: awilix.asClass(UserController),
|
|
122
126
|
})
|
|
123
127
|
|
|
124
128
|
// Let's try with a factory function.
|
|
@@ -126,16 +130,16 @@ const makeUserService = ({ db }) => {
|
|
|
126
130
|
// Notice how we can use destructuring
|
|
127
131
|
// to access dependencies
|
|
128
132
|
return {
|
|
129
|
-
getUser: id => {
|
|
133
|
+
getUser: (id) => {
|
|
130
134
|
return db.query(`select * from users where id=${id}`)
|
|
131
|
-
}
|
|
135
|
+
},
|
|
132
136
|
}
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
container.register({
|
|
136
140
|
// the `userService` is resolved by
|
|
137
141
|
// invoking the function.
|
|
138
|
-
userService: awilix.asFunction(makeUserService)
|
|
142
|
+
userService: awilix.asFunction(makeUserService),
|
|
139
143
|
})
|
|
140
144
|
|
|
141
145
|
// Alright, now we need a database.
|
|
@@ -149,7 +153,7 @@ function Database(connectionString, timeout) {
|
|
|
149
153
|
this.conn = connectToYourDatabaseSomehow(connectionString, timeout)
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
Database.prototype.query = function(sql) {
|
|
156
|
+
Database.prototype.query = function (sql) {
|
|
153
157
|
// blah....
|
|
154
158
|
return this.conn.rawSql(sql)
|
|
155
159
|
}
|
|
@@ -159,7 +163,7 @@ Database.prototype.query = function(sql) {
|
|
|
159
163
|
// We also want to use `CLASSIC` injection mode for this
|
|
160
164
|
// registration. Read more about injection modes below.
|
|
161
165
|
container.register({
|
|
162
|
-
db: awilix.asClass(Database).classic()
|
|
166
|
+
db: awilix.asClass(Database).classic(),
|
|
163
167
|
})
|
|
164
168
|
|
|
165
169
|
// Lastly we register the connection string and timeout values
|
|
@@ -169,7 +173,7 @@ container.register({
|
|
|
169
173
|
// limited to strings and numbers, it can be anything,
|
|
170
174
|
// really - they will be passed through directly.
|
|
171
175
|
connectionString: awilix.asValue(process.env.CONN_STR),
|
|
172
|
-
timeout: awilix.asValue(1000)
|
|
176
|
+
timeout: awilix.asValue(1000),
|
|
173
177
|
})
|
|
174
178
|
|
|
175
179
|
// We have now wired everything up!
|
|
@@ -219,17 +223,17 @@ const { asClass, asFunction, asValue } = awilix
|
|
|
219
223
|
class MailService {}
|
|
220
224
|
|
|
221
225
|
container.register({
|
|
222
|
-
mailService: asClass(MailService, { lifetime: Lifetime.SINGLETON })
|
|
226
|
+
mailService: asClass(MailService, { lifetime: Lifetime.SINGLETON }),
|
|
223
227
|
})
|
|
224
228
|
|
|
225
229
|
// or using the chaining configuration API..
|
|
226
230
|
container.register({
|
|
227
|
-
mailService: asClass(MailService).setLifetime(Lifetime.SINGLETON)
|
|
231
|
+
mailService: asClass(MailService).setLifetime(Lifetime.SINGLETON),
|
|
228
232
|
})
|
|
229
233
|
|
|
230
234
|
// or..
|
|
231
235
|
container.register({
|
|
232
|
-
mailService: asClass(MailService).singleton()
|
|
236
|
+
mailService: asClass(MailService).singleton(),
|
|
233
237
|
})
|
|
234
238
|
|
|
235
239
|
// or.......
|
|
@@ -260,7 +264,7 @@ class MessageService {
|
|
|
260
264
|
}
|
|
261
265
|
|
|
262
266
|
container.register({
|
|
263
|
-
messageService: asClass(MessageService).scoped()
|
|
267
|
+
messageService: asClass(MessageService).scoped(),
|
|
264
268
|
})
|
|
265
269
|
|
|
266
270
|
// imagine middleware in some web framework..
|
|
@@ -270,7 +274,7 @@ app.use((req, res, next) => {
|
|
|
270
274
|
|
|
271
275
|
// register some request-specific data..
|
|
272
276
|
req.scope.register({
|
|
273
|
-
currentUser: asValue(req.user)
|
|
277
|
+
currentUser: asValue(req.user),
|
|
274
278
|
})
|
|
275
279
|
|
|
276
280
|
next()
|
|
@@ -279,7 +283,7 @@ app.use((req, res, next) => {
|
|
|
279
283
|
app.get('/messages', (req, res) => {
|
|
280
284
|
// for each request we get a new message service!
|
|
281
285
|
const messageService = req.scope.resolve('messageService')
|
|
282
|
-
messageService.getMessages().then(messages => {
|
|
286
|
+
messageService.getMessages().then((messages) => {
|
|
283
287
|
res.send(200, messages)
|
|
284
288
|
})
|
|
285
289
|
})
|
|
@@ -289,18 +293,26 @@ app.get('/messages', (req, res) => {
|
|
|
289
293
|
```
|
|
290
294
|
|
|
291
295
|
**IMPORTANT!** If a singleton is resolved, and it depends on a scoped or
|
|
292
|
-
transient registration, those will remain in the singleton for
|
|
296
|
+
transient registration, those will remain in the singleton for its lifetime!
|
|
297
|
+
Similarly, if a scoped module is resolved, and it depends on a transient
|
|
298
|
+
registration, that remains in the scoped module for its lifetime.
|
|
299
|
+
In the example above, if `messageService` was a singleton, it would be cached
|
|
300
|
+
in the root container, and would always have the `currentUser` from the first
|
|
301
|
+
request. Modules should generally not have a longer lifetime than their
|
|
302
|
+
dependencies, as this can cause issues of stale data.
|
|
293
303
|
|
|
294
304
|
```js
|
|
295
|
-
const makePrintTime =
|
|
296
|
-
|
|
297
|
-
|
|
305
|
+
const makePrintTime =
|
|
306
|
+
({ time }) =>
|
|
307
|
+
() => {
|
|
308
|
+
console.log('Time:', time)
|
|
309
|
+
}
|
|
298
310
|
|
|
299
311
|
const getTime = () => new Date().toString()
|
|
300
312
|
|
|
301
313
|
container.register({
|
|
302
314
|
printTime: asFunction(makePrintTime).singleton(),
|
|
303
|
-
time: asFunction(getTime).transient()
|
|
315
|
+
time: asFunction(getTime).transient(),
|
|
304
316
|
})
|
|
305
317
|
|
|
306
318
|
// Resolving `time` 2 times will
|
|
@@ -315,9 +327,34 @@ container.resolve('printTime')()
|
|
|
315
327
|
container.resolve('printTime')()
|
|
316
328
|
```
|
|
317
329
|
|
|
330
|
+
If you want a mismatched configuration like this to error, set
|
|
331
|
+
`strict` in the container options. This will trigger
|
|
332
|
+
the following error at runtime when the singleton `printTime` is resolved:
|
|
333
|
+
`AwilixResolutionError: Could not resolve 'time'. Dependency 'time' has a shorter lifetime than its ancestor: 'printTime'`
|
|
334
|
+
|
|
335
|
+
In addition, registering a singleton on a scope other than the root container results in
|
|
336
|
+
unpredictable behavior. In particular, if two different singletons are registered on two different
|
|
337
|
+
scopes, they will share a cache entry and collide with each other. To throw a runtime error when a
|
|
338
|
+
singleton is registered on a scope other than the root container, enable [strict mode](#strict-mode).
|
|
339
|
+
|
|
318
340
|
Read the documentation for [`container.createScope()`](#containercreatescope)
|
|
319
341
|
for more examples.
|
|
320
342
|
|
|
343
|
+
# Strict mode
|
|
344
|
+
|
|
345
|
+
Strict mode is a new feature in Awilix 10. It enables additional correctness checks that can help
|
|
346
|
+
you catch bugs early.
|
|
347
|
+
|
|
348
|
+
In particular, strict mode enables the following checks:
|
|
349
|
+
|
|
350
|
+
- When a singleton or scoped registration depends on a transient non-value registration, an error is
|
|
351
|
+
thrown. This detects and prevents the issue where a shorter lifetime dependency can leak outside
|
|
352
|
+
its intended lifetime due to its preservation in a longer lifetime module.
|
|
353
|
+
- Singleton registrations on any scopes are disabled. This prevents the issue where a singleton is
|
|
354
|
+
registered on a scope other than the root container, which results in unpredictable behavior.
|
|
355
|
+
- Singleton resolution is performed using registrations from the root container only, which prevents
|
|
356
|
+
potential leaks in which scoped registrations are preserved in singletons.
|
|
357
|
+
|
|
321
358
|
# Injection modes
|
|
322
359
|
|
|
323
360
|
The injection mode determines how a function/constructor receives its
|
|
@@ -351,11 +388,11 @@ modes are available on `awilix.InjectionMode`
|
|
|
351
388
|
```
|
|
352
389
|
|
|
353
390
|
- `InjectionMode.CLASSIC`: Parses the function/constructor parameters, and
|
|
354
|
-
matches them with registrations in the container. `CLASSIC` mode has a
|
|
355
|
-
slightly higher initialization cost as it has to parse the function/class
|
|
356
|
-
to figure out the dependencies at the time of registration, however resolving
|
|
357
|
-
them will be **much faster** than when using `PROXY`. _Don't use `CLASSIC` if
|
|
358
|
-
you minify your code!_ We recommend using `CLASSIC` in Node and `PROXY` in
|
|
391
|
+
matches them with registrations in the container. `CLASSIC` mode has a
|
|
392
|
+
slightly higher initialization cost as it has to parse the function/class
|
|
393
|
+
to figure out the dependencies at the time of registration, however resolving
|
|
394
|
+
them will be **much faster** than when using `PROXY`. _Don't use `CLASSIC` if
|
|
395
|
+
you minify your code!_ We recommend using `CLASSIC` in Node and `PROXY` in
|
|
359
396
|
environments where minification is needed.
|
|
360
397
|
|
|
361
398
|
```js
|
|
@@ -423,8 +460,8 @@ container.register({
|
|
|
423
460
|
const container = createContainer()
|
|
424
461
|
container.loadModules(['services/**/*.js', 'repositories/**/*.js'], {
|
|
425
462
|
resolverOptions: {
|
|
426
|
-
injectionMode: InjectionMode.CLASSIC
|
|
427
|
-
}
|
|
463
|
+
injectionMode: InjectionMode.CLASSIC,
|
|
464
|
+
},
|
|
428
465
|
})
|
|
429
466
|
```
|
|
430
467
|
|
|
@@ -464,7 +501,7 @@ function database({ connectionString, timeout, logger }) {
|
|
|
464
501
|
const db = database({
|
|
465
502
|
logger: new LoggerMock(),
|
|
466
503
|
timeout: 4000,
|
|
467
|
-
connectionString: 'localhost:1337;user=123...'
|
|
504
|
+
connectionString: 'localhost:1337;user=123...',
|
|
468
505
|
})
|
|
469
506
|
```
|
|
470
507
|
|
|
@@ -521,35 +558,38 @@ const awilix = require('awilix')
|
|
|
521
558
|
const container = awilix.createContainer()
|
|
522
559
|
|
|
523
560
|
// Load our modules!
|
|
524
|
-
container.loadModules(
|
|
525
|
-
// Globs!
|
|
561
|
+
container.loadModules(
|
|
526
562
|
[
|
|
527
|
-
//
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
563
|
+
// Globs!
|
|
564
|
+
[
|
|
565
|
+
// To have different resolverOptions for specific modules.
|
|
566
|
+
'models/**/*.js',
|
|
567
|
+
{
|
|
568
|
+
register: awilix.asValue,
|
|
569
|
+
lifetime: Lifetime.SINGLETON,
|
|
570
|
+
},
|
|
571
|
+
],
|
|
572
|
+
'services/**/*.js',
|
|
573
|
+
'repositories/**/*.js',
|
|
533
574
|
],
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
})
|
|
575
|
+
{
|
|
576
|
+
// We want to register `UserService` as `userService` -
|
|
577
|
+
// by default loaded modules are registered with the
|
|
578
|
+
// name of the file (minus the extension)
|
|
579
|
+
formatName: 'camelCase',
|
|
580
|
+
// Apply resolver options to all modules.
|
|
581
|
+
resolverOptions: {
|
|
582
|
+
// We can give these auto-loaded modules
|
|
583
|
+
// the deal of a lifetime! (see what I did there?)
|
|
584
|
+
// By default it's `TRANSIENT`.
|
|
585
|
+
lifetime: Lifetime.SINGLETON,
|
|
586
|
+
// We can tell Awilix what to register everything as,
|
|
587
|
+
// instead of guessing. If omitted, will inspect the
|
|
588
|
+
// module to determine what to register as.
|
|
589
|
+
register: awilix.asClass,
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
)
|
|
553
593
|
|
|
554
594
|
// We are now ready! We now have a userService, userRepository and emailService!
|
|
555
595
|
container.resolve('userService').getUser(1)
|
|
@@ -575,10 +615,10 @@ export default function userRepository({ db, timeout }) {
|
|
|
575
615
|
return Promise.race([
|
|
576
616
|
db.query('select * from users'),
|
|
577
617
|
Promise.delay(timeout).then(() =>
|
|
578
|
-
Promise.reject(new Error('Timed out'))
|
|
579
|
-
)
|
|
618
|
+
Promise.reject(new Error('Timed out')),
|
|
619
|
+
),
|
|
580
620
|
])
|
|
581
|
-
}
|
|
621
|
+
},
|
|
582
622
|
}
|
|
583
623
|
}
|
|
584
624
|
```
|
|
@@ -597,22 +637,22 @@ const container = createContainer()
|
|
|
597
637
|
// Provide an injection function that returns an object with locals.
|
|
598
638
|
// The function is called once per resolve of the registration
|
|
599
639
|
// it is attached to.
|
|
600
|
-
.inject(() => ({ timeout: 2000 }))
|
|
640
|
+
.inject(() => ({ timeout: 2000 })),
|
|
601
641
|
})
|
|
602
642
|
|
|
603
643
|
// Shorthand variants
|
|
604
644
|
.register({
|
|
605
645
|
userRepository: asFunction(createUserRepository, {
|
|
606
|
-
injector: () => ({ timeout: 2000 })
|
|
607
|
-
})
|
|
646
|
+
injector: () => ({ timeout: 2000 }),
|
|
647
|
+
}),
|
|
608
648
|
})
|
|
609
649
|
|
|
610
650
|
// Stringly-typed shorthand
|
|
611
651
|
.register(
|
|
612
652
|
'userRepository',
|
|
613
653
|
asFunction(createUserRepository, {
|
|
614
|
-
injector: () => ({ timeout: 2000 })
|
|
615
|
-
})
|
|
654
|
+
injector: () => ({ timeout: 2000 }),
|
|
655
|
+
}),
|
|
616
656
|
)
|
|
617
657
|
|
|
618
658
|
// with `loadModules`
|
|
@@ -645,7 +685,7 @@ export default class AwesomeService {
|
|
|
645
685
|
// `RESOLVER` is a Symbol.
|
|
646
686
|
AwesomeService[RESOLVER] = {
|
|
647
687
|
lifetime: Lifetime.SCOPED,
|
|
648
|
-
injectionMode: InjectionMode.CLASSIC
|
|
688
|
+
injectionMode: InjectionMode.CLASSIC,
|
|
649
689
|
}
|
|
650
690
|
```
|
|
651
691
|
|
|
@@ -656,7 +696,7 @@ import { createContainer, asClass } from 'awilix'
|
|
|
656
696
|
import AwesomeService from './services/awesome-service.js'
|
|
657
697
|
|
|
658
698
|
const container = createContainer().register({
|
|
659
|
-
awesomeService: asClass(AwesomeService)
|
|
699
|
+
awesomeService: asClass(AwesomeService),
|
|
660
700
|
})
|
|
661
701
|
|
|
662
702
|
console.log(container.registrations.awesomeService.lifetime) // 'SCOPED'
|
|
@@ -720,7 +760,7 @@ function configureContainer() {
|
|
|
720
760
|
.singleton()
|
|
721
761
|
// This is called when the pool is going to be disposed.
|
|
722
762
|
// If it returns a Promise, it will be awaited by `dispose`.
|
|
723
|
-
.disposer(pool => pool.end())
|
|
763
|
+
.disposer((pool) => pool.end()),
|
|
724
764
|
})
|
|
725
765
|
}
|
|
726
766
|
|
|
@@ -796,18 +836,20 @@ pass in an object with the following props:
|
|
|
796
836
|
[Per-module local injections](#per-module-local-injections)
|
|
797
837
|
- `register`: Only used in `loadModules`, determines how to register a loaded
|
|
798
838
|
module explicitly
|
|
839
|
+
- `isLeakSafe`: true if this resolver should be excluded from lifetime-leak checking performed in
|
|
840
|
+
[strict mode](#strict-mode). Defaults to false.
|
|
799
841
|
|
|
800
842
|
**Examples of usage:**
|
|
801
843
|
|
|
802
844
|
```js
|
|
803
845
|
container.register({
|
|
804
|
-
stuff: asClass(MyClass, { injectionMode: InjectionMode.CLASSIC })
|
|
846
|
+
stuff: asClass(MyClass, { injectionMode: InjectionMode.CLASSIC }),
|
|
805
847
|
})
|
|
806
848
|
|
|
807
849
|
container.loadModules([['some/path/to/*.js', { register: asClass }]], {
|
|
808
850
|
resolverOptions: {
|
|
809
|
-
lifetime: Lifetime.SCOPED
|
|
810
|
-
}
|
|
851
|
+
lifetime: Lifetime.SCOPED,
|
|
852
|
+
},
|
|
811
853
|
})
|
|
812
854
|
```
|
|
813
855
|
|
|
@@ -830,6 +872,7 @@ Args:
|
|
|
830
872
|
**_must_** be named exactly like they are in the registration. For
|
|
831
873
|
example, a dependency registered as `repository` cannot be referenced in a
|
|
832
874
|
class constructor as `repo`.
|
|
875
|
+
- `options.strict`: Enables [strict mode](#strict-mode). Defaults to `false`.
|
|
833
876
|
|
|
834
877
|
## `asFunction()`
|
|
835
878
|
|
|
@@ -869,7 +912,7 @@ Resolves the dependency specified.
|
|
|
869
912
|
```js
|
|
870
913
|
container.register({
|
|
871
914
|
val: asValue(123),
|
|
872
|
-
aliasVal: aliasTo('val')
|
|
915
|
+
aliasVal: aliasTo('val'),
|
|
873
916
|
})
|
|
874
917
|
|
|
875
918
|
container.resolve('aliasVal') === container.resolve('val')
|
|
@@ -913,6 +956,11 @@ This is a special error thrown when Awilix is unable to resolve all dependencies
|
|
|
913
956
|
`err instanceof AwilixResolutionError` if you wish. It will tell you what
|
|
914
957
|
dependencies it could not find or which ones caused a cycle.
|
|
915
958
|
|
|
959
|
+
## `AwilixRegistrationError`
|
|
960
|
+
|
|
961
|
+
This is a special error thrown when Awilix is unable to register a dependency due to a strict mode
|
|
962
|
+
violation. You can catch this error and use `err instanceof AwilixRegistrationError` if you wish.
|
|
963
|
+
|
|
916
964
|
## The `AwilixContainer` object
|
|
917
965
|
|
|
918
966
|
The container returned from `createContainer` has some methods and properties.
|
|
@@ -942,7 +990,7 @@ Each scope has it's own cache, and checks the cache of it's ancestors.
|
|
|
942
990
|
```js
|
|
943
991
|
let counter = 1
|
|
944
992
|
container.register({
|
|
945
|
-
count: asFunction(() => counter++).singleton()
|
|
993
|
+
count: asFunction(() => counter++).singleton(),
|
|
946
994
|
})
|
|
947
995
|
|
|
948
996
|
container.cradle.count === 1
|
|
@@ -958,7 +1006,7 @@ Options passed to `createContainer` are stored here.
|
|
|
958
1006
|
|
|
959
1007
|
```js
|
|
960
1008
|
const container = createContainer({
|
|
961
|
-
injectionMode: InjectionMode.CLASSIC
|
|
1009
|
+
injectionMode: InjectionMode.CLASSIC,
|
|
962
1010
|
})
|
|
963
1011
|
|
|
964
1012
|
console.log(container.options.injectionMode) // 'CLASSIC'
|
|
@@ -974,7 +1022,7 @@ Resolves the registration with the given name. Used by the cradle.
|
|
|
974
1022
|
|
|
975
1023
|
```js
|
|
976
1024
|
container.register({
|
|
977
|
-
leet: asFunction(() => 1337)
|
|
1025
|
+
leet: asFunction(() => 1337),
|
|
978
1026
|
})
|
|
979
1027
|
|
|
980
1028
|
container.resolve('leet') === 1337
|
|
@@ -1022,14 +1070,14 @@ container.register('context', asClass(SessionContext))
|
|
|
1022
1070
|
container.register({
|
|
1023
1071
|
connectionString: asValue('localhost:1433;user=...'),
|
|
1024
1072
|
mailService: asFunction(makeMailService, { lifetime: Lifetime.SINGLETON }),
|
|
1025
|
-
context: asClass(SessionContext, { lifetime: Lifetime.SCOPED })
|
|
1073
|
+
context: asClass(SessionContext, { lifetime: Lifetime.SCOPED }),
|
|
1026
1074
|
})
|
|
1027
1075
|
|
|
1028
1076
|
// `asClass` and `asFunction` also supports a fluid syntax.
|
|
1029
1077
|
// This...
|
|
1030
1078
|
container.register(
|
|
1031
1079
|
'mailService',
|
|
1032
|
-
asFunction(makeMailService).setLifetime(Lifetime.SINGLETON)
|
|
1080
|
+
asFunction(makeMailService).setLifetime(Lifetime.SINGLETON),
|
|
1033
1081
|
)
|
|
1034
1082
|
// .. is the same as this:
|
|
1035
1083
|
container.register('context', asClass(SessionContext).singleton())
|
|
@@ -1070,9 +1118,9 @@ Args:
|
|
|
1070
1118
|
pass the name through as-is. The 2nd parameter is a full module descriptor.
|
|
1071
1119
|
- `opts.resolverOptions`: An `object` passed to the resolvers. Used to configure
|
|
1072
1120
|
the lifetime, injection mode and more of the loaded modules.
|
|
1073
|
-
- `opts.esModules`: Loads modules using Node's native ES modules.
|
|
1074
|
-
**This makes `container.loadModules` asynchronous, and will therefore return a `Promise`!**
|
|
1075
|
-
This is only
|
|
1121
|
+
- `opts.esModules`: Loads modules using Node's native ES modules.
|
|
1122
|
+
**This makes `container.loadModules` asynchronous, and will therefore return a `Promise`!**
|
|
1123
|
+
This is only supported on Node 14.0+ and should only be used if you're using
|
|
1076
1124
|
the [Native Node ES modules](https://nodejs.org/api/esm.html)
|
|
1077
1125
|
|
|
1078
1126
|
Example:
|
|
@@ -1149,7 +1197,7 @@ inside a scope. A scope is basically a "child" container.
|
|
|
1149
1197
|
// Increments the counter every time it is resolved.
|
|
1150
1198
|
let counter = 1
|
|
1151
1199
|
container.register({
|
|
1152
|
-
counterValue: asFunction(() => counter++).scoped()
|
|
1200
|
+
counterValue: asFunction(() => counter++).scoped(),
|
|
1153
1201
|
})
|
|
1154
1202
|
const scope1 = container.createScope()
|
|
1155
1203
|
const scope2 = container.createScope()
|
|
@@ -1169,7 +1217,7 @@ A _Scope_ maintains it's own cache of `Lifetime.SCOPED` registrations, meaning i
|
|
|
1169
1217
|
```js
|
|
1170
1218
|
let counter = 1
|
|
1171
1219
|
container.register({
|
|
1172
|
-
counterValue: asFunction(() => counter++).scoped()
|
|
1220
|
+
counterValue: asFunction(() => counter++).scoped(),
|
|
1173
1221
|
})
|
|
1174
1222
|
const scope1 = container.createScope()
|
|
1175
1223
|
const scope2 = container.createScope()
|
|
@@ -1195,13 +1243,13 @@ that scope and it's children.
|
|
|
1195
1243
|
// that returns the value of the scope-provided dependency.
|
|
1196
1244
|
// For this example we could also use scoped lifetime.
|
|
1197
1245
|
container.register({
|
|
1198
|
-
scopedValue: asFunction(cradle => 'Hello ' + cradle.someValue)
|
|
1246
|
+
scopedValue: asFunction((cradle) => 'Hello ' + cradle.someValue),
|
|
1199
1247
|
})
|
|
1200
1248
|
|
|
1201
1249
|
// Create a scope and register a value.
|
|
1202
1250
|
const scope = container.createScope()
|
|
1203
1251
|
scope.register({
|
|
1204
|
-
someValue: asValue('scope')
|
|
1252
|
+
someValue: asValue('scope'),
|
|
1205
1253
|
})
|
|
1206
1254
|
|
|
1207
1255
|
scope.cradle.scopedValue === 'Hello scope'
|
|
@@ -1211,27 +1259,36 @@ container.cradle.someValue
|
|
|
1211
1259
|
// of the resolver.
|
|
1212
1260
|
```
|
|
1213
1261
|
|
|
1214
|
-
Things registered in the scope take precedence over
|
|
1262
|
+
Things registered in the scope take precedence over registrations in the parent scope(s). This
|
|
1263
|
+
applies to both the registration directly requested from the scope container, and any dependencies
|
|
1264
|
+
that the registration uses.
|
|
1215
1265
|
|
|
1216
1266
|
```js
|
|
1217
1267
|
// It does not matter when the scope is created,
|
|
1218
1268
|
// it will still have anything that is registered
|
|
1219
|
-
// in
|
|
1269
|
+
// in its parent.
|
|
1220
1270
|
const scope = container.createScope()
|
|
1221
1271
|
|
|
1222
1272
|
container.register({
|
|
1223
1273
|
value: asValue('root'),
|
|
1224
|
-
usedValue: asFunction(cradle => cradle.value)
|
|
1274
|
+
usedValue: asFunction((cradle) => `hello from ${cradle.value}`),
|
|
1225
1275
|
})
|
|
1226
1276
|
|
|
1227
1277
|
scope.register({
|
|
1228
|
-
value: asValue('scope')
|
|
1278
|
+
value: asValue('scope'),
|
|
1229
1279
|
})
|
|
1230
1280
|
|
|
1231
|
-
container.cradle.
|
|
1232
|
-
scope.cradle.
|
|
1281
|
+
container.cradle.value === 'root'
|
|
1282
|
+
scope.cradle.value === 'scope'
|
|
1283
|
+
container.cradle.usedValue === 'hello from root'
|
|
1284
|
+
scope.cradle.usedValue === 'hello from scope'
|
|
1233
1285
|
```
|
|
1234
1286
|
|
|
1287
|
+
Registering singletons in a scope results in unpredictable behavior and should be avoided. Having
|
|
1288
|
+
more than one singleton with the same name in different scopes will result in them sharing a cache
|
|
1289
|
+
entry and colliding with each other. To disallow such registrations, enable
|
|
1290
|
+
[strict mode](#strict-mode) in the container options.
|
|
1291
|
+
|
|
1235
1292
|
### `container.build()`
|
|
1236
1293
|
|
|
1237
1294
|
Builds an instance of a class (or a function) by injecting dependencies, but
|
|
@@ -1266,11 +1323,11 @@ class MyClass {
|
|
|
1266
1323
|
}
|
|
1267
1324
|
|
|
1268
1325
|
const createMyFunc = ({ ping }) => ({
|
|
1269
|
-
pong: () => ping
|
|
1326
|
+
pong: () => ping,
|
|
1270
1327
|
})
|
|
1271
1328
|
|
|
1272
1329
|
container.register({
|
|
1273
|
-
ping: asValue('pong')
|
|
1330
|
+
ping: asValue('pong'),
|
|
1274
1331
|
})
|
|
1275
1332
|
|
|
1276
1333
|
// Shorthand
|
|
@@ -1304,9 +1361,9 @@ const pg = require('pg')
|
|
|
1304
1361
|
|
|
1305
1362
|
container.register({
|
|
1306
1363
|
pool: asFunction(() => new pg.Pool())
|
|
1307
|
-
.disposer(pool => pool.end())
|
|
1364
|
+
.disposer((pool) => pool.end())
|
|
1308
1365
|
// IMPORTANT! Must be either singleton or scoped!
|
|
1309
|
-
.singleton()
|
|
1366
|
+
.singleton(),
|
|
1310
1367
|
})
|
|
1311
1368
|
|
|
1312
1369
|
const pool = container.resolve('pool')
|
|
@@ -1348,11 +1405,11 @@ because they depend on Node-specific packages.
|
|
|
1348
1405
|
|
|
1349
1406
|
# Ecosystem
|
|
1350
1407
|
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1408
|
+
- [`awilix-manager`](https://github.com/kibertoad/awilix-manager): Wrapper that allows eager injection, asynchronous init methods and dependency lookup by tags.
|
|
1409
|
+
- [`awilix-express`](https://github.com/jeffijoe/awilix-express): Bindings for the Express HTTP library.
|
|
1410
|
+
- [`awilix-koa`](https://github.com/jeffijoe/awilix-koa): Bindings for the Koa HTTP library.
|
|
1411
|
+
- [`awilix-router-core`](https://github.com/jeffijoe/awilix-router-core): Library for building HTTP bindings for Awilix with routing.
|
|
1412
|
+
- [`fastify-awilix`](https://github.com/fastify/fastify-awilix): Bindings for the Fastify framework.
|
|
1356
1413
|
|
|
1357
1414
|
# Contributing
|
|
1358
1415
|
|