awilix 8.0.1 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,10 +8,12 @@
8
8
  [![node](https://img.shields.io/node/v/awilix.svg?maxAge=1000)](https://www.npmjs.com/package/awilix)
9
9
  [![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
10
10
 
11
- Extremely powerful **Dependency Injection** (DI) container for JavaScript/Node,
12
- written in [TypeScript](http://typescriptlang.org). **Make IoC great again!**
11
+ Extremely powerful, performant, & battle-tested **Dependency Injection** (DI) container for JavaScript/Node,
12
+ written in [TypeScript](http://typescriptlang.org).
13
13
 
14
- > Check out this
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
+
16
+ > 💡 Check out this
15
17
  > [intro to Dependency Injection with Awilix](https://medium.com/@Jeffijoe/dependency-injection-in-node-js-2016-edition-f2a88efdd427)
16
18
 
17
19
  # Table of Contents
@@ -22,6 +24,7 @@ written in [TypeScript](http://typescriptlang.org). **Make IoC great again!**
22
24
  - [Usage](#usage)
23
25
  - [Lifetime management](#lifetime-management)
24
26
  - [Scoped lifetime](#scoped-lifetime)
27
+ - [Strict mode](#strict-mode)
25
28
  - [Injection modes](#injection-modes)
26
29
  - [Auto-loading modules](#auto-loading-modules)
27
30
  - [Per-module local injections](#per-module-local-injections)
@@ -37,6 +40,7 @@ written in [TypeScript](http://typescriptlang.org). **Make IoC great again!**
37
40
  - [`aliasTo()`](#aliasto)
38
41
  - [`listModules()`](#listmodules)
39
42
  - [`AwilixResolutionError`](#awilixresolutionerror)
43
+ - [`AwilixRegistrationError`](#awilixregistrationerror)
40
44
  - [The `AwilixContainer` object](#the-awilixcontainer-object)
41
45
  - [`container.cradle`](#containercradle)
42
46
  - [`container.registrations`](#containerregistrations)
@@ -50,7 +54,8 @@ written in [TypeScript](http://typescriptlang.org). **Make IoC great again!**
50
54
  - [`container.build()`](#containerbuild)
51
55
  - [`container.dispose()`](#containerdispose)
52
56
  - [Universal Module (Browser Support)](#universal-module-browser-support)
53
- - [Contributing](#contributing)
57
+ - [Ecosystem](#ecosystem)
58
+ - [Contributing](#contributing)
54
59
  - [What's in a name?](#whats-in-a-name)
55
60
  - [Author](#author)
56
61
 
@@ -71,9 +76,9 @@ yarn add awilix
71
76
  You can also use the [UMD](https://github.com/umdjs/umd) build from `unpkg`
72
77
 
73
78
  ```html
74
- <script src="https://unpkg.com/awilix/lib/awilix.umd.js"/>
79
+ <script src="https://unpkg.com/awilix/lib/awilix.umd.js" />
75
80
  <script>
76
- const container = Awilix.createContainer()
81
+ const container = Awilix.createContainer()
77
82
  </script>
78
83
  ```
79
84
 
@@ -92,8 +97,10 @@ minimum, you need to do 3 things:
92
97
  const awilix = require('awilix')
93
98
 
94
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).
95
101
  const container = awilix.createContainer({
96
- injectionMode: awilix.InjectionMode.PROXY
102
+ injectionMode: awilix.InjectionMode.PROXY,
103
+ strict: true,
97
104
  })
98
105
 
99
106
  // This is our app code... We can use
@@ -115,7 +122,7 @@ class UserController {
115
122
  container.register({
116
123
  // Here we are telling Awilix how to resolve a
117
124
  // userController: by instantiating a class.
118
- userController: awilix.asClass(UserController)
125
+ userController: awilix.asClass(UserController),
119
126
  })
120
127
 
121
128
  // Let's try with a factory function.
@@ -123,16 +130,16 @@ const makeUserService = ({ db }) => {
123
130
  // Notice how we can use destructuring
124
131
  // to access dependencies
125
132
  return {
126
- getUser: id => {
133
+ getUser: (id) => {
127
134
  return db.query(`select * from users where id=${id}`)
128
- }
135
+ },
129
136
  }
130
137
  }
131
138
 
132
139
  container.register({
133
140
  // the `userService` is resolved by
134
141
  // invoking the function.
135
- userService: awilix.asFunction(makeUserService)
142
+ userService: awilix.asFunction(makeUserService),
136
143
  })
137
144
 
138
145
  // Alright, now we need a database.
@@ -146,7 +153,7 @@ function Database(connectionString, timeout) {
146
153
  this.conn = connectToYourDatabaseSomehow(connectionString, timeout)
147
154
  }
148
155
 
149
- Database.prototype.query = function(sql) {
156
+ Database.prototype.query = function (sql) {
150
157
  // blah....
151
158
  return this.conn.rawSql(sql)
152
159
  }
@@ -156,7 +163,7 @@ Database.prototype.query = function(sql) {
156
163
  // We also want to use `CLASSIC` injection mode for this
157
164
  // registration. Read more about injection modes below.
158
165
  container.register({
159
- db: awilix.asClass(Database).classic()
166
+ db: awilix.asClass(Database).classic(),
160
167
  })
161
168
 
162
169
  // Lastly we register the connection string and timeout values
@@ -166,7 +173,7 @@ container.register({
166
173
  // limited to strings and numbers, it can be anything,
167
174
  // really - they will be passed through directly.
168
175
  connectionString: awilix.asValue(process.env.CONN_STR),
169
- timeout: awilix.asValue(1000)
176
+ timeout: awilix.asValue(1000),
170
177
  })
171
178
 
172
179
  // We have now wired everything up!
@@ -216,17 +223,17 @@ const { asClass, asFunction, asValue } = awilix
216
223
  class MailService {}
217
224
 
218
225
  container.register({
219
- mailService: asClass(MailService, { lifetime: Lifetime.SINGLETON })
226
+ mailService: asClass(MailService, { lifetime: Lifetime.SINGLETON }),
220
227
  })
221
228
 
222
229
  // or using the chaining configuration API..
223
230
  container.register({
224
- mailService: asClass(MailService).setLifetime(Lifetime.SINGLETON)
231
+ mailService: asClass(MailService).setLifetime(Lifetime.SINGLETON),
225
232
  })
226
233
 
227
234
  // or..
228
235
  container.register({
229
- mailService: asClass(MailService).singleton()
236
+ mailService: asClass(MailService).singleton(),
230
237
  })
231
238
 
232
239
  // or.......
@@ -257,7 +264,7 @@ class MessageService {
257
264
  }
258
265
 
259
266
  container.register({
260
- messageService: asClass(MessageService).scoped()
267
+ messageService: asClass(MessageService).scoped(),
261
268
  })
262
269
 
263
270
  // imagine middleware in some web framework..
@@ -267,7 +274,7 @@ app.use((req, res, next) => {
267
274
 
268
275
  // register some request-specific data..
269
276
  req.scope.register({
270
- currentUser: asValue(req.user)
277
+ currentUser: asValue(req.user),
271
278
  })
272
279
 
273
280
  next()
@@ -276,7 +283,7 @@ app.use((req, res, next) => {
276
283
  app.get('/messages', (req, res) => {
277
284
  // for each request we get a new message service!
278
285
  const messageService = req.scope.resolve('messageService')
279
- messageService.getMessages().then(messages => {
286
+ messageService.getMessages().then((messages) => {
280
287
  res.send(200, messages)
281
288
  })
282
289
  })
@@ -286,18 +293,26 @@ app.get('/messages', (req, res) => {
286
293
  ```
287
294
 
288
295
  **IMPORTANT!** If a singleton is resolved, and it depends on a scoped or
289
- transient registration, those will remain in the singleton for it's lifetime!
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.
290
303
 
291
304
  ```js
292
- const makePrintTime = ({ time }) => () => {
293
- console.log('Time:', time)
294
- }
305
+ const makePrintTime =
306
+ ({ time }) =>
307
+ () => {
308
+ console.log('Time:', time)
309
+ }
295
310
 
296
311
  const getTime = () => new Date().toString()
297
312
 
298
313
  container.register({
299
314
  printTime: asFunction(makePrintTime).singleton(),
300
- time: asFunction(getTime).transient()
315
+ time: asFunction(getTime).transient(),
301
316
  })
302
317
 
303
318
  // Resolving `time` 2 times will
@@ -312,9 +327,34 @@ container.resolve('printTime')()
312
327
  container.resolve('printTime')()
313
328
  ```
314
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
+
315
340
  Read the documentation for [`container.createScope()`](#containercreatescope)
316
341
  for more examples.
317
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
+
318
358
  # Injection modes
319
359
 
320
360
  The injection mode determines how a function/constructor receives its
@@ -348,11 +388,11 @@ modes are available on `awilix.InjectionMode`
348
388
  ```
349
389
 
350
390
  - `InjectionMode.CLASSIC`: Parses the function/constructor parameters, and
351
- matches them with registrations in the container. `CLASSIC` mode has a
352
- slightly higher initialization cost as it has to parse the function/class
353
- to figure out the dependencies at the time of registration, however resolving
354
- them will be **much faster** than when using `PROXY`. _Don't use `CLASSIC` if
355
- 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
356
396
  environments where minification is needed.
357
397
 
358
398
  ```js
@@ -420,8 +460,8 @@ container.register({
420
460
  const container = createContainer()
421
461
  container.loadModules(['services/**/*.js', 'repositories/**/*.js'], {
422
462
  resolverOptions: {
423
- injectionMode: InjectionMode.CLASSIC
424
- }
463
+ injectionMode: InjectionMode.CLASSIC,
464
+ },
425
465
  })
426
466
  ```
427
467
 
@@ -461,7 +501,7 @@ function database({ connectionString, timeout, logger }) {
461
501
  const db = database({
462
502
  logger: new LoggerMock(),
463
503
  timeout: 4000,
464
- connectionString: 'localhost:1337;user=123...'
504
+ connectionString: 'localhost:1337;user=123...',
465
505
  })
466
506
  ```
467
507
 
@@ -518,34 +558,37 @@ const awilix = require('awilix')
518
558
  const container = awilix.createContainer()
519
559
 
520
560
  // Load our modules!
521
- container.loadModules([
522
- // Globs!
561
+ container.loadModules(
523
562
  [
524
- // To have different resolverOptions for specific modules.
525
- 'models/**/*.js',
526
- {
527
- register: awilix.asValue,
528
- lifetime: Lifetime.SINGLETON
529
- }
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',
530
574
  ],
531
- 'services/**/*.js',
532
- 'repositories/**/*.js'
533
- ], {
534
- // We want to register `UserService` as `userService` -
535
- // by default loaded modules are registered with the
536
- // name of the file (minus the extension)
537
- formatName: 'camelCase',
538
- // Apply resolver options to all modules.
539
- resolverOptions: {
540
- // We can give these auto-loaded modules
541
- // the deal of a lifetime! (see what I did there?)
542
- // By default it's `TRANSIENT`.
543
- lifetime: Lifetime.SINGLETON,
544
- // We can tell Awilix what to register everything as,
545
- // instead of guessing. If omitted, will inspect the
546
- // module to determine what to register as.
547
- register: awilix.asClass
548
- }
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
+ },
549
592
  )
550
593
 
551
594
  // We are now ready! We now have a userService, userRepository and emailService!
@@ -572,10 +615,10 @@ export default function userRepository({ db, timeout }) {
572
615
  return Promise.race([
573
616
  db.query('select * from users'),
574
617
  Promise.delay(timeout).then(() =>
575
- Promise.reject(new Error('Timed out'))
576
- )
618
+ Promise.reject(new Error('Timed out')),
619
+ ),
577
620
  ])
578
- }
621
+ },
579
622
  }
580
623
  }
581
624
  ```
@@ -594,22 +637,22 @@ const container = createContainer()
594
637
  // Provide an injection function that returns an object with locals.
595
638
  // The function is called once per resolve of the registration
596
639
  // it is attached to.
597
- .inject(() => ({ timeout: 2000 }))
640
+ .inject(() => ({ timeout: 2000 })),
598
641
  })
599
642
 
600
643
  // Shorthand variants
601
644
  .register({
602
645
  userRepository: asFunction(createUserRepository, {
603
- injector: () => ({ timeout: 2000 })
604
- })
646
+ injector: () => ({ timeout: 2000 }),
647
+ }),
605
648
  })
606
649
 
607
650
  // Stringly-typed shorthand
608
651
  .register(
609
652
  'userRepository',
610
653
  asFunction(createUserRepository, {
611
- injector: () => ({ timeout: 2000 })
612
- })
654
+ injector: () => ({ timeout: 2000 }),
655
+ }),
613
656
  )
614
657
 
615
658
  // with `loadModules`
@@ -642,7 +685,7 @@ export default class AwesomeService {
642
685
  // `RESOLVER` is a Symbol.
643
686
  AwesomeService[RESOLVER] = {
644
687
  lifetime: Lifetime.SCOPED,
645
- injectionMode: InjectionMode.CLASSIC
688
+ injectionMode: InjectionMode.CLASSIC,
646
689
  }
647
690
  ```
648
691
 
@@ -653,7 +696,7 @@ import { createContainer, asClass } from 'awilix'
653
696
  import AwesomeService from './services/awesome-service.js'
654
697
 
655
698
  const container = createContainer().register({
656
- awesomeService: asClass(AwesomeService)
699
+ awesomeService: asClass(AwesomeService),
657
700
  })
658
701
 
659
702
  console.log(container.registrations.awesomeService.lifetime) // 'SCOPED'
@@ -717,7 +760,7 @@ function configureContainer() {
717
760
  .singleton()
718
761
  // This is called when the pool is going to be disposed.
719
762
  // If it returns a Promise, it will be awaited by `dispose`.
720
- .disposer(pool => pool.end())
763
+ .disposer((pool) => pool.end()),
721
764
  })
722
765
  }
723
766
 
@@ -793,18 +836,20 @@ pass in an object with the following props:
793
836
  [Per-module local injections](#per-module-local-injections)
794
837
  - `register`: Only used in `loadModules`, determines how to register a loaded
795
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.
796
841
 
797
842
  **Examples of usage:**
798
843
 
799
844
  ```js
800
845
  container.register({
801
- stuff: asClass(MyClass, { injectionMode: InjectionMode.CLASSIC })
846
+ stuff: asClass(MyClass, { injectionMode: InjectionMode.CLASSIC }),
802
847
  })
803
848
 
804
849
  container.loadModules([['some/path/to/*.js', { register: asClass }]], {
805
850
  resolverOptions: {
806
- lifetime: Lifetime.SCOPED
807
- }
851
+ lifetime: Lifetime.SCOPED,
852
+ },
808
853
  })
809
854
  ```
810
855
 
@@ -827,6 +872,7 @@ Args:
827
872
  **_must_** be named exactly like they are in the registration. For
828
873
  example, a dependency registered as `repository` cannot be referenced in a
829
874
  class constructor as `repo`.
875
+ - `options.strict`: Enables [strict mode](#strict-mode). Defaults to `false`.
830
876
 
831
877
  ## `asFunction()`
832
878
 
@@ -866,7 +912,7 @@ Resolves the dependency specified.
866
912
  ```js
867
913
  container.register({
868
914
  val: asValue(123),
869
- aliasVal: aliasTo('val')
915
+ aliasVal: aliasTo('val'),
870
916
  })
871
917
 
872
918
  container.resolve('aliasVal') === container.resolve('val')
@@ -910,6 +956,11 @@ This is a special error thrown when Awilix is unable to resolve all dependencies
910
956
  `err instanceof AwilixResolutionError` if you wish. It will tell you what
911
957
  dependencies it could not find or which ones caused a cycle.
912
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
+
913
964
  ## The `AwilixContainer` object
914
965
 
915
966
  The container returned from `createContainer` has some methods and properties.
@@ -939,7 +990,7 @@ Each scope has it's own cache, and checks the cache of it's ancestors.
939
990
  ```js
940
991
  let counter = 1
941
992
  container.register({
942
- count: asFunction(() => counter++).singleton()
993
+ count: asFunction(() => counter++).singleton(),
943
994
  })
944
995
 
945
996
  container.cradle.count === 1
@@ -955,7 +1006,7 @@ Options passed to `createContainer` are stored here.
955
1006
 
956
1007
  ```js
957
1008
  const container = createContainer({
958
- injectionMode: InjectionMode.CLASSIC
1009
+ injectionMode: InjectionMode.CLASSIC,
959
1010
  })
960
1011
 
961
1012
  console.log(container.options.injectionMode) // 'CLASSIC'
@@ -971,7 +1022,7 @@ Resolves the registration with the given name. Used by the cradle.
971
1022
 
972
1023
  ```js
973
1024
  container.register({
974
- leet: asFunction(() => 1337)
1025
+ leet: asFunction(() => 1337),
975
1026
  })
976
1027
 
977
1028
  container.resolve('leet') === 1337
@@ -1019,14 +1070,14 @@ container.register('context', asClass(SessionContext))
1019
1070
  container.register({
1020
1071
  connectionString: asValue('localhost:1433;user=...'),
1021
1072
  mailService: asFunction(makeMailService, { lifetime: Lifetime.SINGLETON }),
1022
- context: asClass(SessionContext, { lifetime: Lifetime.SCOPED })
1073
+ context: asClass(SessionContext, { lifetime: Lifetime.SCOPED }),
1023
1074
  })
1024
1075
 
1025
1076
  // `asClass` and `asFunction` also supports a fluid syntax.
1026
1077
  // This...
1027
1078
  container.register(
1028
1079
  'mailService',
1029
- asFunction(makeMailService).setLifetime(Lifetime.SINGLETON)
1080
+ asFunction(makeMailService).setLifetime(Lifetime.SINGLETON),
1030
1081
  )
1031
1082
  // .. is the same as this:
1032
1083
  container.register('context', asClass(SessionContext).singleton())
@@ -1067,9 +1118,9 @@ Args:
1067
1118
  pass the name through as-is. The 2nd parameter is a full module descriptor.
1068
1119
  - `opts.resolverOptions`: An `object` passed to the resolvers. Used to configure
1069
1120
  the lifetime, injection mode and more of the loaded modules.
1070
- - `opts.esModules`: Loads modules using Node's native ES modules.
1071
- **This makes `container.loadModules` asynchronous, and will therefore return a `Promise`!**
1072
- This is only supported on Node 14.0+ and should only be used if you're using
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
1073
1124
  the [Native Node ES modules](https://nodejs.org/api/esm.html)
1074
1125
 
1075
1126
  Example:
@@ -1146,7 +1197,7 @@ inside a scope. A scope is basically a "child" container.
1146
1197
  // Increments the counter every time it is resolved.
1147
1198
  let counter = 1
1148
1199
  container.register({
1149
- counterValue: asFunction(() => counter++).scoped()
1200
+ counterValue: asFunction(() => counter++).scoped(),
1150
1201
  })
1151
1202
  const scope1 = container.createScope()
1152
1203
  const scope2 = container.createScope()
@@ -1166,7 +1217,7 @@ A _Scope_ maintains it's own cache of `Lifetime.SCOPED` registrations, meaning i
1166
1217
  ```js
1167
1218
  let counter = 1
1168
1219
  container.register({
1169
- counterValue: asFunction(() => counter++).scoped()
1220
+ counterValue: asFunction(() => counter++).scoped(),
1170
1221
  })
1171
1222
  const scope1 = container.createScope()
1172
1223
  const scope2 = container.createScope()
@@ -1192,13 +1243,13 @@ that scope and it's children.
1192
1243
  // that returns the value of the scope-provided dependency.
1193
1244
  // For this example we could also use scoped lifetime.
1194
1245
  container.register({
1195
- scopedValue: asFunction(cradle => 'Hello ' + cradle.someValue)
1246
+ scopedValue: asFunction((cradle) => 'Hello ' + cradle.someValue),
1196
1247
  })
1197
1248
 
1198
1249
  // Create a scope and register a value.
1199
1250
  const scope = container.createScope()
1200
1251
  scope.register({
1201
- someValue: asValue('scope')
1252
+ someValue: asValue('scope'),
1202
1253
  })
1203
1254
 
1204
1255
  scope.cradle.scopedValue === 'Hello scope'
@@ -1208,27 +1259,36 @@ container.cradle.someValue
1208
1259
  // of the resolver.
1209
1260
  ```
1210
1261
 
1211
- Things registered in the scope take precedence over it's parent.
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.
1212
1265
 
1213
1266
  ```js
1214
1267
  // It does not matter when the scope is created,
1215
1268
  // it will still have anything that is registered
1216
- // in it's parent.
1269
+ // in its parent.
1217
1270
  const scope = container.createScope()
1218
1271
 
1219
1272
  container.register({
1220
1273
  value: asValue('root'),
1221
- usedValue: asFunction(cradle => cradle.value)
1274
+ usedValue: asFunction((cradle) => `hello from ${cradle.value}`),
1222
1275
  })
1223
1276
 
1224
1277
  scope.register({
1225
- value: asValue('scope')
1278
+ value: asValue('scope'),
1226
1279
  })
1227
1280
 
1228
- container.cradle.usedValue === 'root'
1229
- scope.cradle.usedValue === 'scope'
1281
+ container.cradle.value === 'root'
1282
+ scope.cradle.value === 'scope'
1283
+ container.cradle.usedValue === 'hello from root'
1284
+ scope.cradle.usedValue === 'hello from scope'
1230
1285
  ```
1231
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
+
1232
1292
  ### `container.build()`
1233
1293
 
1234
1294
  Builds an instance of a class (or a function) by injecting dependencies, but
@@ -1263,11 +1323,11 @@ class MyClass {
1263
1323
  }
1264
1324
 
1265
1325
  const createMyFunc = ({ ping }) => ({
1266
- pong: () => ping
1326
+ pong: () => ping,
1267
1327
  })
1268
1328
 
1269
1329
  container.register({
1270
- ping: asValue('pong')
1330
+ ping: asValue('pong'),
1271
1331
  })
1272
1332
 
1273
1333
  // Shorthand
@@ -1301,9 +1361,9 @@ const pg = require('pg')
1301
1361
 
1302
1362
  container.register({
1303
1363
  pool: asFunction(() => new pg.Pool())
1304
- .disposer(pool => pool.end())
1364
+ .disposer((pool) => pool.end())
1305
1365
  // IMPORTANT! Must be either singleton or scoped!
1306
- .singleton()
1366
+ .singleton(),
1307
1367
  })
1308
1368
 
1309
1369
  const pool = container.resolve('pool')
@@ -1343,7 +1403,15 @@ because they depend on Node-specific packages.
1343
1403
  - Safari >= 10
1344
1404
  - Internet Explorer is not supported
1345
1405
 
1346
- ## Contributing
1406
+ # Ecosystem
1407
+
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.
1413
+
1414
+ # Contributing
1347
1415
 
1348
1416
  Please see our [contributing.md](./CONTRIBUTING.md)
1349
1417