Procházet zdrojové kódy

Merge branch 'master' into dashboard-image-cropping

Artur Paikin před 4 roky
rodič
revize
5c761b8f36
100 změnil soubory, kde provedl 5520 přidání a 555 odebrání
  1. 1 0
      CHANGELOG.md
  2. 56 52
      README.md
  3. 2 2
      examples/cdn-example/index.html
  4. 14 0
      examples/custom-provider/babel.config.js
  5. 14 58
      examples/custom-provider/client/MyCustomProvider.js
  6. 3954 0
      examples/custom-provider/package-lock.json
  7. 85 21
      examples/custom-provider/server/customprovider.js
  8. binární
      examples/custom-provider/server/fixtures/image.jpg
  9. 11 12
      examples/custom-provider/server/index.js
  10. 5 0
      examples/dev/Dashboard.js
  11. 1 1
      examples/transloadit-textarea/index.html
  12. 2 2
      examples/uppy-with-companion/client/index.html
  13. 12 6
      package-lock.json
  14. 1 1
      packages/@uppy/aws-s3-multipart/package.json
  15. 117 26
      packages/@uppy/aws-s3-multipart/src/MultipartUploader.js
  16. 9 7
      packages/@uppy/aws-s3-multipart/src/index.js
  17. 1 0
      packages/@uppy/aws-s3-multipart/types/index.d.ts
  18. 1 1
      packages/@uppy/aws-s3/package.json
  19. 1 1
      packages/@uppy/companion-client/package.json
  20. 0 1
      packages/@uppy/companion-client/src/Provider.js
  21. 0 1
      packages/@uppy/companion-client/types/index.d.ts
  22. 8 4
      packages/@uppy/companion/infra/kube/gcloud-deploy.sh
  23. 1 1
      packages/@uppy/companion/package.json
  24. 10 0
      packages/@uppy/companion/src/companion.js
  25. 2 0
      packages/@uppy/companion/src/config/grant.js
  26. 1 0
      packages/@uppy/companion/src/server/Uploader.js
  27. 4 3
      packages/@uppy/companion/src/server/controllers/callback.js
  28. 10 16
      packages/@uppy/companion/src/server/provider/index.js
  29. 2 2
      packages/@uppy/companion/src/standalone/helper.js
  30. 1 1
      packages/@uppy/companion/src/standalone/index.js
  31. 1 1
      packages/@uppy/core/package.json
  32. 7 0
      packages/@uppy/core/src/index.js
  33. 42 0
      packages/@uppy/core/src/index.test.js
  34. 1 1
      packages/@uppy/dashboard/package.json
  35. 1 1
      packages/@uppy/drag-drop/package.json
  36. 1 1
      packages/@uppy/dropbox/package.json
  37. 1 1
      packages/@uppy/facebook/package.json
  38. 1 1
      packages/@uppy/file-input/package.json
  39. 1 1
      packages/@uppy/form/package.json
  40. 1 1
      packages/@uppy/golden-retriever/package.json
  41. 1 1
      packages/@uppy/google-drive/package.json
  42. 0 1
      packages/@uppy/google-drive/src/index.js
  43. 1 1
      packages/@uppy/informer/package.json
  44. 1 1
      packages/@uppy/instagram/package.json
  45. 0 1
      packages/@uppy/instagram/src/index.js
  46. 1 1
      packages/@uppy/locales/package.json
  47. 1 1
      packages/@uppy/onedrive/package.json
  48. 1 1
      packages/@uppy/progress-bar/package.json
  49. 4 0
      packages/@uppy/provider-views/README.md
  50. 1 1
      packages/@uppy/provider-views/package.json
  51. 22 0
      packages/@uppy/provider-views/types/index.d.ts
  52. 1 1
      packages/@uppy/react/package.json
  53. 2 2
      packages/@uppy/robodog/README.md
  54. 1 1
      packages/@uppy/robodog/package.json
  55. 1 1
      packages/@uppy/screen-capture/package.json
  56. 1 1
      packages/@uppy/status-bar/package.json
  57. 1 1
      packages/@uppy/thumbnail-generator/package.json
  58. 1 1
      packages/@uppy/transloadit/package.json
  59. 1 1
      packages/@uppy/tus/package.json
  60. 1 0
      packages/@uppy/tus/src/index.js
  61. 1 1
      packages/@uppy/url/package.json
  62. 2 1
      packages/@uppy/utils/package.json
  63. 20 0
      packages/@uppy/utils/src/AbortController.js
  64. 51 0
      packages/@uppy/utils/src/AbortController.test.js
  65. 36 0
      packages/@uppy/utils/src/delay.js
  66. 33 0
      packages/@uppy/utils/src/delay.test.js
  67. 1 1
      packages/@uppy/webcam/package.json
  68. 1 1
      packages/@uppy/xhr-upload/package.json
  69. 1 1
      packages/uppy/package.json
  70. 575 0
      website/src/_posts/2020-03-custom-providers.md
  71. 15 9
      website/src/docs/aws-s3-multipart.md
  72. 58 8
      website/src/docs/companion.md
  73. 35 1
      website/src/docs/dropbox.md
  74. 31 0
      website/src/docs/google-drive.md
  75. 5 5
      website/src/docs/index.md
  76. 2 2
      website/src/docs/locales.md
  77. 12 29
      website/src/docs/react-dashboard-modal.md
  78. 21 41
      website/src/docs/react-dashboard.md
  79. 2 28
      website/src/docs/react-dragdrop.md
  80. 73 0
      website/src/docs/react-initializing.md
  81. 1 46
      website/src/docs/react-native.md
  82. 3 28
      website/src/docs/react-progressbar.md
  83. 3 28
      website/src/docs/react-statusbar.md
  84. 4 4
      website/src/docs/robodog-form.md
  85. 2 2
      website/src/docs/robodog.md
  86. 1 1
      website/src/docs/uppy.md
  87. 1 1
      website/src/examples/dashboard/app.es6
  88. 3 3
      website/src/examples/i18n/app.html
  89. 1 1
      website/src/examples/markdown-snippets/app.es6
  90. 1 1
      website/src/examples/markdown-snippets/app.html
  91. binární
      website/src/images/blog/custom-providers/dashboard-preview.png
  92. binární
      website/src/images/blog/custom-providers/unsplash-api-dashboard.png
  93. binární
      website/src/images/blog/custom-providers/unsplashicon.png
  94. 2 2
      website/themes/uppy/layout/index.ejs
  95. 10 5
      website/themes/uppy/layout/layout.ejs
  96. 74 9
      website/themes/uppy/source/css/_common.scss
  97. 0 41
      website/themes/uppy/source/css/_index.scss
  98. 10 9
      website/themes/uppy/source/css/_page.scss
  99. 2 1
      website/themes/uppy/source/css/_settings.scss
  100. 1 0
      website/themes/uppy/source/images/transloadit-white-glyph.svg

+ 1 - 0
CHANGELOG.md

@@ -105,6 +105,7 @@ PRs are welcome! Please do open an issue to discuss first if it's a big feature,
 - [ ] core: add maxTotalFileSize restriction #514 (@arturi)
 - [ ] companion: what happens if access token expires during/between an download & upload (@ife)
 - [ ] providers: Provider Browser don't handle uppy restrictions, can we hide things that don't match the restrictions in Google Drive and Instagram? #1827 (@arturi)
+- [x] aws-s3-multipart: retry uploading failed parts (#2312 / @goto-bus-stop)
 
 ## 1.16.1
 

+ 56 - 52
README.md

@@ -65,7 +65,7 @@ $ npm install @uppy/core @uppy/dashboard @uppy/tus
 
 We recommend installing from npm and then using a module bundler such as [Webpack](https://webpack.js.org/), [Browserify](http://browserify.org/) or [Rollup.js](http://rollupjs.org/).
 
-Add CSS [uppy.min.css](https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.css), either to your HTML page's `<head>` or include in JS, if your bundler of choice supports it — transforms and plugins are available for Browserify and Webpack.
+Add CSS [uppy.min.css](https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.css), either to your HTML page's `<head>` or include in JS, if your bundler of choice supports it — transforms and plugins are available for Browserify and Webpack.
 
 Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object.
 
@@ -73,10 +73,10 @@ Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly
 
 ```html
 <!-- 1. Add CSS to `<head>` -->
-<link href="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.css" rel="stylesheet">
+<link href="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.css" rel="stylesheet">
 
 <!-- 2. Add JS before the closing `</body>` -->
-<script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
 
 <!-- 3. Initialize -->
 <div class="UppyDragDrop"></div>
@@ -174,7 +174,7 @@ const Uppy = require('@uppy/core')
 If you're using Uppy from CDN, `es6-promise` and `whatwg-fetch` are already included in the bundle, so no need to include anything additionally:
 
 ```html
-<script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
 ```
 
 ## FAQ
@@ -243,41 +243,41 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu
 :---: |:---: |:---: |:---: |:---: |:---: |
 [zcallan](https://github.com/zcallan) |[tim-kos](https://github.com/tim-kos) |[janko](https://github.com/janko) |[wilkoklak](https://github.com/wilkoklak) |[oliverpool](https://github.com/oliverpool) |[mcallistertyler95](https://github.com/mcallistertyler95) |
 
-[<img alt="DJWassink" src="https://avatars3.githubusercontent.com/u/1822404?v=4&s=117" width="117">](https://github.com/DJWassink) |[<img alt="taoqf" src="https://avatars3.githubusercontent.com/u/15901911?v=4&s=117" width="117">](https://github.com/taoqf) |[<img alt="tuoxiansp" src="https://avatars1.githubusercontent.com/u/3960056?v=4&s=117" width="117">](https://github.com/tuoxiansp) |[<img alt="tranvansang" src="https://avatars1.githubusercontent.com/u/13043196?v=4&s=117" width="117">](https://github.com/tranvansang) |[<img alt="ap--" src="https://avatars1.githubusercontent.com/u/1463443?v=4&s=117" width="117">](https://github.com/ap--) |[<img alt="dependabot[bot]" src="https://avatars0.githubusercontent.com/in/29110?v=4&s=117" width="117">](https://github.com/apps/dependabot) |
+[<img alt="DJWassink" src="https://avatars3.githubusercontent.com/u/1822404?v=4&s=117" width="117">](https://github.com/DJWassink) |[<img alt="taoqf" src="https://avatars3.githubusercontent.com/u/15901911?v=4&s=117" width="117">](https://github.com/taoqf) |[<img alt="dependabot[bot]" src="https://avatars0.githubusercontent.com/in/29110?v=4&s=117" width="117">](https://github.com/apps/dependabot) |[<img alt="tuoxiansp" src="https://avatars1.githubusercontent.com/u/3960056?v=4&s=117" width="117">](https://github.com/tuoxiansp) |[<img alt="gavboulton" src="https://avatars0.githubusercontent.com/u/3900826?v=4&s=117" width="117">](https://github.com/gavboulton) |[<img alt="bertho-zero" src="https://avatars0.githubusercontent.com/u/8525267?v=4&s=117" width="117">](https://github.com/bertho-zero) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[DJWassink](https://github.com/DJWassink) |[taoqf](https://github.com/taoqf) |[tuoxiansp](https://github.com/tuoxiansp) |[tranvansang](https://github.com/tranvansang) |[ap--](https://github.com/ap--) |[dependabot[bot]](https://github.com/apps/dependabot) |
+[DJWassink](https://github.com/DJWassink) |[taoqf](https://github.com/taoqf) |[dependabot[bot]](https://github.com/apps/dependabot) |[tuoxiansp](https://github.com/tuoxiansp) |[gavboulton](https://github.com/gavboulton) |[bertho-zero](https://github.com/bertho-zero) |
 
-[<img alt="bambii7" src="https://avatars3.githubusercontent.com/u/1826459?v=4&s=117" width="117">](https://github.com/bambii7) |[<img alt="bertho-zero" src="https://avatars0.githubusercontent.com/u/8525267?v=4&s=117" width="117">](https://github.com/bertho-zero) |[<img alt="MikeKovarik" src="https://avatars0.githubusercontent.com/u/3995401?v=4&s=117" width="117">](https://github.com/MikeKovarik) |[<img alt="pauln" src="https://avatars3.githubusercontent.com/u/574359?v=4&s=117" width="117">](https://github.com/pauln) |[<img alt="toadkicker" src="https://avatars1.githubusercontent.com/u/523330?v=4&s=117" width="117">](https://github.com/toadkicker) |[<img alt="gavboulton" src="https://avatars0.githubusercontent.com/u/3900826?v=4&s=117" width="117">](https://github.com/gavboulton) |
+[<img alt="tranvansang" src="https://avatars1.githubusercontent.com/u/13043196?v=4&s=117" width="117">](https://github.com/tranvansang) |[<img alt="ap--" src="https://avatars1.githubusercontent.com/u/1463443?v=4&s=117" width="117">](https://github.com/ap--) |[<img alt="bambii7" src="https://avatars3.githubusercontent.com/u/1826459?v=4&s=117" width="117">](https://github.com/bambii7) |[<img alt="mrbatista" src="https://avatars0.githubusercontent.com/u/6544817?v=4&s=117" width="117">](https://github.com/mrbatista) |[<img alt="MikeKovarik" src="https://avatars0.githubusercontent.com/u/3995401?v=4&s=117" width="117">](https://github.com/MikeKovarik) |[<img alt="pauln" src="https://avatars3.githubusercontent.com/u/574359?v=4&s=117" width="117">](https://github.com/pauln) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[bambii7](https://github.com/bambii7) |[bertho-zero](https://github.com/bertho-zero) |[MikeKovarik](https://github.com/MikeKovarik) |[pauln](https://github.com/pauln) |[toadkicker](https://github.com/toadkicker) |[gavboulton](https://github.com/gavboulton) |
+[tranvansang](https://github.com/tranvansang) |[ap--](https://github.com/ap--) |[bambii7](https://github.com/bambii7) |[mrbatista](https://github.com/mrbatista) |[MikeKovarik](https://github.com/MikeKovarik) |[pauln](https://github.com/pauln) |
 
-[<img alt="mrbatista" src="https://avatars0.githubusercontent.com/u/6544817?v=4&s=117" width="117">](https://github.com/mrbatista) |[<img alt="manuelkiessling" src="https://avatars2.githubusercontent.com/u/206592?v=4&s=117" width="117">](https://github.com/manuelkiessling) |[<img alt="nndevstudio" src="https://avatars2.githubusercontent.com/u/22050968?v=4&s=117" width="117">](https://github.com/nndevstudio) |[<img alt="ogtfaber" src="https://avatars2.githubusercontent.com/u/320955?v=4&s=117" width="117">](https://github.com/ogtfaber) |[<img alt="dargmuesli" src="https://avatars2.githubusercontent.com/u/4778485?v=4&s=117" width="117">](https://github.com/dargmuesli) |[<img alt="sunil-shrestha" src="https://avatars3.githubusercontent.com/u/2129058?v=4&s=117" width="117">](https://github.com/sunil-shrestha) |
+[<img alt="toadkicker" src="https://avatars1.githubusercontent.com/u/523330?v=4&s=117" width="117">](https://github.com/toadkicker) |[<img alt="dargmuesli" src="https://avatars2.githubusercontent.com/u/4778485?v=4&s=117" width="117">](https://github.com/dargmuesli) |[<img alt="manuelkiessling" src="https://avatars2.githubusercontent.com/u/206592?v=4&s=117" width="117">](https://github.com/manuelkiessling) |[<img alt="nndevstudio" src="https://avatars2.githubusercontent.com/u/22050968?v=4&s=117" width="117">](https://github.com/nndevstudio) |[<img alt="ogtfaber" src="https://avatars2.githubusercontent.com/u/320955?v=4&s=117" width="117">](https://github.com/ogtfaber) |[<img alt="sksavant" src="https://avatars1.githubusercontent.com/u/1040701?v=4&s=117" width="117">](https://github.com/sksavant) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[mrbatista](https://github.com/mrbatista) |[manuelkiessling](https://github.com/manuelkiessling) |[nndevstudio](https://github.com/nndevstudio) |[ogtfaber](https://github.com/ogtfaber) |[dargmuesli](https://github.com/dargmuesli) |[sunil-shrestha](https://github.com/sunil-shrestha) |
+[toadkicker](https://github.com/toadkicker) |[dargmuesli](https://github.com/dargmuesli) |[manuelkiessling](https://github.com/manuelkiessling) |[nndevstudio](https://github.com/nndevstudio) |[ogtfaber](https://github.com/ogtfaber) |[sksavant](https://github.com/sksavant) |
 
-[<img alt="yonahforst" src="https://avatars3.githubusercontent.com/u/1440796?v=4&s=117" width="117">](https://github.com/yonahforst) |[<img alt="stephentuso" src="https://avatars2.githubusercontent.com/u/11889560?v=4&s=117" width="117">](https://github.com/stephentuso) |[<img alt="mskelton" src="https://avatars3.githubusercontent.com/u/25914066?v=4&s=117" width="117">](https://github.com/mskelton) |[<img alt="sksavant" src="https://avatars1.githubusercontent.com/u/1040701?v=4&s=117" width="117">](https://github.com/sksavant) |[<img alt="btrice" src="https://avatars2.githubusercontent.com/u/4358225?v=4&s=117" width="117">](https://github.com/btrice) |[<img alt="Burkes" src="https://avatars2.githubusercontent.com/u/9220052?v=4&s=117" width="117">](https://github.com/Burkes) |
+[<img alt="sunil-shrestha" src="https://avatars3.githubusercontent.com/u/2129058?v=4&s=117" width="117">](https://github.com/sunil-shrestha) |[<img alt="yonahforst" src="https://avatars3.githubusercontent.com/u/1440796?v=4&s=117" width="117">](https://github.com/yonahforst) |[<img alt="stephentuso" src="https://avatars2.githubusercontent.com/u/11889560?v=4&s=117" width="117">](https://github.com/stephentuso) |[<img alt="mskelton" src="https://avatars3.githubusercontent.com/u/25914066?v=4&s=117" width="117">](https://github.com/mskelton) |[<img alt="btrice" src="https://avatars2.githubusercontent.com/u/4358225?v=4&s=117" width="117">](https://github.com/btrice) |[<img alt="behnammodi" src="https://avatars0.githubusercontent.com/u/1549069?v=4&s=117" width="117">](https://github.com/behnammodi) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[yonahforst](https://github.com/yonahforst) |[stephentuso](https://github.com/stephentuso) |[mskelton](https://github.com/mskelton) |[sksavant](https://github.com/sksavant) |[btrice](https://github.com/btrice) |[Burkes](https://github.com/Burkes) |
+[sunil-shrestha](https://github.com/sunil-shrestha) |[yonahforst](https://github.com/yonahforst) |[stephentuso](https://github.com/stephentuso) |[mskelton](https://github.com/mskelton) |[btrice](https://github.com/btrice) |[behnammodi](https://github.com/behnammodi) |
 
-[<img alt="craigjennings11" src="https://avatars2.githubusercontent.com/u/1683368?v=4&s=117" width="117">](https://github.com/craigjennings11) |[<img alt="davekiss" src="https://avatars2.githubusercontent.com/u/1256071?v=4&s=117" width="117">](https://github.com/davekiss) |[<img alt="frobinsonj" src="https://avatars3.githubusercontent.com/u/16726902?v=4&s=117" width="117">](https://github.com/frobinsonj) |[<img alt="geertclerx" src="https://avatars0.githubusercontent.com/u/1381327?v=4&s=117" width="117">](https://github.com/geertclerx) |[<img alt="jasonbosco" src="https://avatars3.githubusercontent.com/u/458383?v=4&s=117" width="117">](https://github.com/jasonbosco) |[<img alt="jedwood" src="https://avatars0.githubusercontent.com/u/369060?v=4&s=117" width="117">](https://github.com/jedwood) |
+[<img alt="Burkes" src="https://avatars2.githubusercontent.com/u/9220052?v=4&s=117" width="117">](https://github.com/Burkes) |[<img alt="craigjennings11" src="https://avatars2.githubusercontent.com/u/1683368?v=4&s=117" width="117">](https://github.com/craigjennings11) |[<img alt="davekiss" src="https://avatars2.githubusercontent.com/u/1256071?v=4&s=117" width="117">](https://github.com/davekiss) |[<img alt="frobinsonj" src="https://avatars3.githubusercontent.com/u/16726902?v=4&s=117" width="117">](https://github.com/frobinsonj) |[<img alt="geertclerx" src="https://avatars0.githubusercontent.com/u/1381327?v=4&s=117" width="117">](https://github.com/geertclerx) |[<img alt="jasonbosco" src="https://avatars3.githubusercontent.com/u/458383?v=4&s=117" width="117">](https://github.com/jasonbosco) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[craigjennings11](https://github.com/craigjennings11) |[davekiss](https://github.com/davekiss) |[frobinsonj](https://github.com/frobinsonj) |[geertclerx](https://github.com/geertclerx) |[jasonbosco](https://github.com/jasonbosco) |[jedwood](https://github.com/jedwood) |
+[Burkes](https://github.com/Burkes) |[craigjennings11](https://github.com/craigjennings11) |[davekiss](https://github.com/davekiss) |[frobinsonj](https://github.com/frobinsonj) |[geertclerx](https://github.com/geertclerx) |[jasonbosco](https://github.com/jasonbosco) |
 
-[<img alt="Mactaivsh" src="https://avatars0.githubusercontent.com/u/12948083?v=4&s=117" width="117">](https://github.com/Mactaivsh) |[<img alt="maferland" src="https://avatars3.githubusercontent.com/u/5889721?v=4&s=117" width="117">](https://github.com/maferland) |[<img alt="Martin005" src="https://avatars0.githubusercontent.com/u/10096404?v=4&s=117" width="117">](https://github.com/Martin005) |[<img alt="martiuslim" src="https://avatars2.githubusercontent.com/u/17944339?v=4&s=117" width="117">](https://github.com/martiuslim) |[<img alt="MatthiasKunnen" src="https://avatars3.githubusercontent.com/u/16807587?v=4&s=117" width="117">](https://github.com/MatthiasKunnen) |[<img alt="behnammodi" src="https://avatars0.githubusercontent.com/u/1549069?v=4&s=117" width="117">](https://github.com/behnammodi) |
+[<img alt="jedwood" src="https://avatars0.githubusercontent.com/u/369060?v=4&s=117" width="117">](https://github.com/jedwood) |[<img alt="Mactaivsh" src="https://avatars0.githubusercontent.com/u/12948083?v=4&s=117" width="117">](https://github.com/Mactaivsh) |[<img alt="maferland" src="https://avatars3.githubusercontent.com/u/5889721?v=4&s=117" width="117">](https://github.com/maferland) |[<img alt="Martin005" src="https://avatars0.githubusercontent.com/u/10096404?v=4&s=117" width="117">](https://github.com/Martin005) |[<img alt="martiuslim" src="https://avatars2.githubusercontent.com/u/17944339?v=4&s=117" width="117">](https://github.com/martiuslim) |[<img alt="MatthiasKunnen" src="https://avatars3.githubusercontent.com/u/16807587?v=4&s=117" width="117">](https://github.com/MatthiasKunnen) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[Mactaivsh](https://github.com/Mactaivsh) |[maferland](https://github.com/maferland) |[Martin005](https://github.com/Martin005) |[martiuslim](https://github.com/martiuslim) |[MatthiasKunnen](https://github.com/MatthiasKunnen) |[behnammodi](https://github.com/behnammodi) |
+[jedwood](https://github.com/jedwood) |[Mactaivsh](https://github.com/Mactaivsh) |[maferland](https://github.com/maferland) |[Martin005](https://github.com/Martin005) |[martiuslim](https://github.com/martiuslim) |[MatthiasKunnen](https://github.com/MatthiasKunnen) |
 
-[<img alt="richartkeil" src="https://avatars0.githubusercontent.com/u/8680858?v=4&s=117" width="117">](https://github.com/richartkeil) |[<img alt="richmeij" src="https://avatars0.githubusercontent.com/u/9741858?v=4&s=117" width="117">](https://github.com/richmeij) |[<img alt="rosenfeld" src="https://avatars1.githubusercontent.com/u/32246?v=4&s=117" width="117">](https://github.com/rosenfeld) |[<img alt="ThomasG77" src="https://avatars2.githubusercontent.com/u/642120?v=4&s=117" width="117">](https://github.com/ThomasG77) |[<img alt="zhuangya" src="https://avatars2.githubusercontent.com/u/499038?v=4&s=117" width="117">](https://github.com/zhuangya) |[<img alt="allenfantasy" src="https://avatars1.githubusercontent.com/u/1009294?v=4&s=117" width="117">](https://github.com/allenfantasy) |
+[<img alt="msand" src="https://avatars2.githubusercontent.com/u/1131362?v=4&s=117" width="117">](https://github.com/msand) |[<img alt="richartkeil" src="https://avatars0.githubusercontent.com/u/8680858?v=4&s=117" width="117">](https://github.com/richartkeil) |[<img alt="richmeij" src="https://avatars0.githubusercontent.com/u/9741858?v=4&s=117" width="117">](https://github.com/richmeij) |[<img alt="rosenfeld" src="https://avatars1.githubusercontent.com/u/32246?v=4&s=117" width="117">](https://github.com/rosenfeld) |[<img alt="ThomasG77" src="https://avatars2.githubusercontent.com/u/642120?v=4&s=117" width="117">](https://github.com/ThomasG77) |[<img alt="zhuangya" src="https://avatars2.githubusercontent.com/u/499038?v=4&s=117" width="117">](https://github.com/zhuangya) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[richartkeil](https://github.com/richartkeil) |[richmeij](https://github.com/richmeij) |[rosenfeld](https://github.com/rosenfeld) |[ThomasG77](https://github.com/ThomasG77) |[zhuangya](https://github.com/zhuangya) |[allenfantasy](https://github.com/allenfantasy) |
+[msand](https://github.com/msand) |[richartkeil](https://github.com/richartkeil) |[richmeij](https://github.com/richmeij) |[rosenfeld](https://github.com/rosenfeld) |[ThomasG77](https://github.com/ThomasG77) |[zhuangya](https://github.com/zhuangya) |
 
-[<img alt="Zyclotrop-j" src="https://avatars0.githubusercontent.com/u/4939546?v=4&s=117" width="117">](https://github.com/Zyclotrop-j) |[<img alt="fortrieb" src="https://avatars0.githubusercontent.com/u/4126707?v=4&s=117" width="117">](https://github.com/fortrieb) |[<img alt="jarey" src="https://avatars1.githubusercontent.com/u/5025224?v=4&s=117" width="117">](https://github.com/jarey) |[<img alt="muhammadInam" src="https://avatars1.githubusercontent.com/u/7801708?v=4&s=117" width="117">](https://github.com/muhammadInam) |[<img alt="rettgerst" src="https://avatars2.githubusercontent.com/u/11684948?v=4&s=117" width="117">](https://github.com/rettgerst) |[<img alt="jukakoski" src="https://avatars0.githubusercontent.com/u/52720967?v=4&s=117" width="117">](https://github.com/jukakoski) |
+[<img alt="allenfantasy" src="https://avatars1.githubusercontent.com/u/1009294?v=4&s=117" width="117">](https://github.com/allenfantasy) |[<img alt="Zyclotrop-j" src="https://avatars0.githubusercontent.com/u/4939546?v=4&s=117" width="117">](https://github.com/Zyclotrop-j) |[<img alt="fortrieb" src="https://avatars0.githubusercontent.com/u/4126707?v=4&s=117" width="117">](https://github.com/fortrieb) |[<img alt="jarey" src="https://avatars1.githubusercontent.com/u/5025224?v=4&s=117" width="117">](https://github.com/jarey) |[<img alt="muhammadInam" src="https://avatars1.githubusercontent.com/u/7801708?v=4&s=117" width="117">](https://github.com/muhammadInam) |[<img alt="rettgerst" src="https://avatars2.githubusercontent.com/u/11684948?v=4&s=117" width="117">](https://github.com/rettgerst) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[Zyclotrop-j](https://github.com/Zyclotrop-j) |[fortrieb](https://github.com/fortrieb) |[jarey](https://github.com/jarey) |[muhammadInam](https://github.com/muhammadInam) |[rettgerst](https://github.com/rettgerst) |[jukakoski](https://github.com/jukakoski) |
+[allenfantasy](https://github.com/allenfantasy) |[Zyclotrop-j](https://github.com/Zyclotrop-j) |[fortrieb](https://github.com/fortrieb) |[jarey](https://github.com/jarey) |[muhammadInam](https://github.com/muhammadInam) |[rettgerst](https://github.com/rettgerst) |
 
-[<img alt="msand" src="https://avatars2.githubusercontent.com/u/1131362?v=4&s=117" width="117">](https://github.com/msand) |[<img alt="ajschmidt8" src="https://avatars0.githubusercontent.com/u/7400326?v=4&s=117" width="117">](https://github.com/ajschmidt8) |[<img alt="superhawk610" src="https://avatars1.githubusercontent.com/u/18172185?v=4&s=117" width="117">](https://github.com/superhawk610) |[<img alt="adamelmore" src="https://avatars2.githubusercontent.com/u/2363879?v=4&s=117" width="117">](https://github.com/adamelmore) |[<img alt="adamvigneault" src="https://avatars2.githubusercontent.com/u/18236120?v=4&s=117" width="117">](https://github.com/adamvigneault) |[<img alt="asmt3" src="https://avatars1.githubusercontent.com/u/1777709?v=4&s=117" width="117">](https://github.com/asmt3) |
+[<img alt="jukakoski" src="https://avatars0.githubusercontent.com/u/52720967?v=4&s=117" width="117">](https://github.com/jukakoski) |[<img alt="ajschmidt8" src="https://avatars0.githubusercontent.com/u/7400326?v=4&s=117" width="117">](https://github.com/ajschmidt8) |[<img alt="superhawk610" src="https://avatars1.githubusercontent.com/u/18172185?v=4&s=117" width="117">](https://github.com/superhawk610) |[<img alt="adamelmore" src="https://avatars2.githubusercontent.com/u/2363879?v=4&s=117" width="117">](https://github.com/adamelmore) |[<img alt="adamvigneault" src="https://avatars2.githubusercontent.com/u/18236120?v=4&s=117" width="117">](https://github.com/adamvigneault) |[<img alt="asmt3" src="https://avatars1.githubusercontent.com/u/1777709?v=4&s=117" width="117">](https://github.com/asmt3) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[msand](https://github.com/msand) |[ajschmidt8](https://github.com/ajschmidt8) |[superhawk610](https://github.com/superhawk610) |[adamelmore](https://github.com/adamelmore) |[adamvigneault](https://github.com/adamvigneault) |[asmt3](https://github.com/asmt3) |
+[jukakoski](https://github.com/jukakoski) |[ajschmidt8](https://github.com/ajschmidt8) |[superhawk610](https://github.com/superhawk610) |[adamelmore](https://github.com/adamelmore) |[adamvigneault](https://github.com/adamvigneault) |[asmt3](https://github.com/asmt3) |
 
 [<img alt="alexnj" src="https://avatars0.githubusercontent.com/u/683500?v=4&s=117" width="117">](https://github.com/alexnj) |[<img alt="amitport" src="https://avatars1.githubusercontent.com/u/1131991?v=4&s=117" width="117">](https://github.com/amitport) |[<img alt="functino" src="https://avatars0.githubusercontent.com/u/415498?v=4&s=117" width="117">](https://github.com/functino) |[<img alt="Botz" src="https://avatars3.githubusercontent.com/u/2706678?v=4&s=117" width="117">](https://github.com/Botz) |[<img alt="radarhere" src="https://avatars2.githubusercontent.com/u/3112309?v=4&s=117" width="117">](https://github.com/radarhere) |[<img alt="superandrew213" src="https://avatars3.githubusercontent.com/u/13059204?v=4&s=117" width="117">](https://github.com/superandrew213) |
 :---: |:---: |:---: |:---: |:---: |:---: |
@@ -295,61 +295,65 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu
 :---: |:---: |:---: |:---: |:---: |:---: |
 [Aarbel](https://github.com/Aarbel) |[cbush06](https://github.com/cbush06) |[czj](https://github.com/czj) |[ardeois](https://github.com/ardeois) |[sercraig](https://github.com/sercraig) |[danmichaelo](https://github.com/danmichaelo) |
 
-[<img alt="mrboomer" src="https://avatars0.githubusercontent.com/u/5942912?v=4&s=117" width="117">](https://github.com/mrboomer) |[<img alt="akizor" src="https://avatars1.githubusercontent.com/u/1052439?v=4&s=117" width="117">](https://github.com/akizor) |[<img alt="davilima6" src="https://avatars0.githubusercontent.com/u/422130?v=4&s=117" width="117">](https://github.com/davilima6) |[<img alt="DennisKofflard" src="https://avatars2.githubusercontent.com/u/8669129?v=4&s=117" width="117">](https://github.com/DennisKofflard) |[<img alt="olitomas" src="https://avatars0.githubusercontent.com/u/6918659?v=4&s=117" width="117">](https://github.com/olitomas) |[<img alt="efbautista" src="https://avatars3.githubusercontent.com/u/35430671?v=4&s=117" width="117">](https://github.com/efbautista) |
+[<img alt="mrboomer" src="https://avatars0.githubusercontent.com/u/5942912?v=4&s=117" width="117">](https://github.com/mrboomer) |[<img alt="akizor" src="https://avatars1.githubusercontent.com/u/1052439?v=4&s=117" width="117">](https://github.com/akizor) |[<img alt="davilima6" src="https://avatars0.githubusercontent.com/u/422130?v=4&s=117" width="117">](https://github.com/davilima6) |[<img alt="DennisKofflard" src="https://avatars2.githubusercontent.com/u/8669129?v=4&s=117" width="117">](https://github.com/DennisKofflard) |[<img alt="dominiceden" src="https://avatars2.githubusercontent.com/u/6367692?v=4&s=117" width="117">](https://github.com/dominiceden) |[<img alt="efbautista" src="https://avatars3.githubusercontent.com/u/35430671?v=4&s=117" width="117">](https://github.com/efbautista) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[mrboomer](https://github.com/mrboomer) |[akizor](https://github.com/akizor) |[davilima6](https://github.com/davilima6) |[DennisKofflard](https://github.com/DennisKofflard) |[olitomas](https://github.com/olitomas) |[efbautista](https://github.com/efbautista) |
+[mrboomer](https://github.com/mrboomer) |[akizor](https://github.com/akizor) |[davilima6](https://github.com/davilima6) |[DennisKofflard](https://github.com/DennisKofflard) |[dominiceden](https://github.com/dominiceden) |[efbautista](https://github.com/efbautista) |
 
-[<img alt="yoldar" src="https://avatars3.githubusercontent.com/u/1597578?v=4&s=117" width="117">](https://github.com/yoldar) |[<img alt="eliOcs" src="https://avatars1.githubusercontent.com/u/1283954?v=4&s=117" width="117">](https://github.com/eliOcs) |[<img alt="lowsprofile" src="https://avatars1.githubusercontent.com/u/11029687?v=4&s=117" width="117">](https://github.com/lowsprofile) |[<img alt="fgallinari" src="https://avatars1.githubusercontent.com/u/6473638?v=4&s=117" width="117">](https://github.com/fgallinari) |[<img alt="dtrucs" src="https://avatars2.githubusercontent.com/u/1926041?v=4&s=117" width="117">](https://github.com/dtrucs) |[<img alt="geoffappleford" src="https://avatars2.githubusercontent.com/u/731678?v=4&s=117" width="117">](https://github.com/geoffappleford) |
+[<img alt="yoldar" src="https://avatars3.githubusercontent.com/u/1597578?v=4&s=117" width="117">](https://github.com/yoldar) |[<img alt="elenalape" src="https://avatars2.githubusercontent.com/u/22844059?v=4&s=117" width="117">](https://github.com/elenalape) |[<img alt="eliOcs" src="https://avatars1.githubusercontent.com/u/1283954?v=4&s=117" width="117">](https://github.com/eliOcs) |[<img alt="lowsprofile" src="https://avatars1.githubusercontent.com/u/11029687?v=4&s=117" width="117">](https://github.com/lowsprofile) |[<img alt="fgallinari" src="https://avatars1.githubusercontent.com/u/6473638?v=4&s=117" width="117">](https://github.com/fgallinari) |[<img alt="dtrucs" src="https://avatars2.githubusercontent.com/u/1926041?v=4&s=117" width="117">](https://github.com/dtrucs) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[yoldar](https://github.com/yoldar) |[eliOcs](https://github.com/eliOcs) |[lowsprofile](https://github.com/lowsprofile) |[fgallinari](https://github.com/fgallinari) |[dtrucs](https://github.com/dtrucs) |[geoffappleford](https://github.com/geoffappleford) |
+[yoldar](https://github.com/yoldar) |[elenalape](https://github.com/elenalape) |[eliOcs](https://github.com/eliOcs) |[lowsprofile](https://github.com/lowsprofile) |[fgallinari](https://github.com/fgallinari) |[dtrucs](https://github.com/dtrucs) |
 
-[<img alt="gjungb" src="https://avatars0.githubusercontent.com/u/3391068?v=4&s=117" width="117">](https://github.com/gjungb) |[<img alt="roenschg" src="https://avatars2.githubusercontent.com/u/9590236?v=4&s=117" width="117">](https://github.com/roenschg) |[<img alt="HughbertD" src="https://avatars0.githubusercontent.com/u/1580021?v=4&s=117" width="117">](https://github.com/HughbertD) |[<img alt="HussainAlkhalifah" src="https://avatars1.githubusercontent.com/u/43642162?v=4&s=117" width="117">](https://github.com/HussainAlkhalifah) |[<img alt="huydod" src="https://avatars2.githubusercontent.com/u/37580530?v=4&s=117" width="117">](https://github.com/huydod) |[<img alt="ishendyweb" src="https://avatars1.githubusercontent.com/u/10582418?v=4&s=117" width="117">](https://github.com/ishendyweb) |
+[<img alt="geoffappleford" src="https://avatars2.githubusercontent.com/u/731678?v=4&s=117" width="117">](https://github.com/geoffappleford) |[<img alt="gjungb" src="https://avatars0.githubusercontent.com/u/3391068?v=4&s=117" width="117">](https://github.com/gjungb) |[<img alt="roenschg" src="https://avatars2.githubusercontent.com/u/9590236?v=4&s=117" width="117">](https://github.com/roenschg) |[<img alt="HughbertD" src="https://avatars0.githubusercontent.com/u/1580021?v=4&s=117" width="117">](https://github.com/HughbertD) |[<img alt="HussainAlkhalifah" src="https://avatars1.githubusercontent.com/u/43642162?v=4&s=117" width="117">](https://github.com/HussainAlkhalifah) |[<img alt="huydod" src="https://avatars2.githubusercontent.com/u/37580530?v=4&s=117" width="117">](https://github.com/huydod) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[gjungb](https://github.com/gjungb) |[roenschg](https://github.com/roenschg) |[HughbertD](https://github.com/HughbertD) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) |[huydod](https://github.com/huydod) |[ishendyweb](https://github.com/ishendyweb) |
+[geoffappleford](https://github.com/geoffappleford) |[gjungb](https://github.com/gjungb) |[roenschg](https://github.com/roenschg) |[HughbertD](https://github.com/HughbertD) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) |[huydod](https://github.com/huydod) |
 
-[<img alt="NaxYo" src="https://avatars3.githubusercontent.com/u/1963876?v=4&s=117" width="117">](https://github.com/NaxYo) |[<img alt="intenzive" src="https://avatars1.githubusercontent.com/u/11055931?v=4&s=117" width="117">](https://github.com/intenzive) |[<img alt="JacobMGEvans" src="https://avatars1.githubusercontent.com/u/27247160?v=4&s=117" width="117">](https://github.com/JacobMGEvans) |[<img alt="jdssem" src="https://avatars0.githubusercontent.com/u/978944?v=4&s=117" width="117">](https://github.com/jdssem) |[<img alt="jcjmcclean" src="https://avatars3.githubusercontent.com/u/1822574?v=4&s=117" width="117">](https://github.com/jcjmcclean) |[<img alt="janklimo" src="https://avatars1.githubusercontent.com/u/7811733?v=4&s=117" width="117">](https://github.com/janklimo) |
+[<img alt="ishendyweb" src="https://avatars1.githubusercontent.com/u/10582418?v=4&s=117" width="117">](https://github.com/ishendyweb) |[<img alt="NaxYo" src="https://avatars3.githubusercontent.com/u/1963876?v=4&s=117" width="117">](https://github.com/NaxYo) |[<img alt="intenzive" src="https://avatars1.githubusercontent.com/u/11055931?v=4&s=117" width="117">](https://github.com/intenzive) |[<img alt="JacobMGEvans" src="https://avatars1.githubusercontent.com/u/27247160?v=4&s=117" width="117">](https://github.com/JacobMGEvans) |[<img alt="jdssem" src="https://avatars0.githubusercontent.com/u/978944?v=4&s=117" width="117">](https://github.com/jdssem) |[<img alt="jcjmcclean" src="https://avatars3.githubusercontent.com/u/1822574?v=4&s=117" width="117">](https://github.com/jcjmcclean) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[NaxYo](https://github.com/NaxYo) |[intenzive](https://github.com/intenzive) |[JacobMGEvans](https://github.com/JacobMGEvans) |[jdssem](https://github.com/jdssem) |[jcjmcclean](https://github.com/jcjmcclean) |[janklimo](https://github.com/janklimo) |
+[ishendyweb](https://github.com/ishendyweb) |[NaxYo](https://github.com/NaxYo) |[intenzive](https://github.com/intenzive) |[JacobMGEvans](https://github.com/JacobMGEvans) |[jdssem](https://github.com/jdssem) |[jcjmcclean](https://github.com/jcjmcclean) |
 
-[<img alt="vith" src="https://avatars1.githubusercontent.com/u/3265539?v=4&s=117" width="117">](https://github.com/vith) |[<img alt="jessica-coursera" src="https://avatars1.githubusercontent.com/u/35155465?v=4&s=117" width="117">](https://github.com/jessica-coursera) |[<img alt="theJoeBiz" src="https://avatars1.githubusercontent.com/u/189589?v=4&s=117" width="117">](https://github.com/theJoeBiz) |[<img alt="jderrough" src="https://avatars3.githubusercontent.com/u/1108358?v=4&s=117" width="117">](https://github.com/jderrough) |[<img alt="jonathanly" src="https://avatars3.githubusercontent.com/u/13286473?v=4&s=117" width="117">](https://github.com/jonathanly) |[<img alt="jorgeepc" src="https://avatars3.githubusercontent.com/u/3879892?v=4&s=117" width="117">](https://github.com/jorgeepc) |
+[<img alt="janklimo" src="https://avatars1.githubusercontent.com/u/7811733?v=4&s=117" width="117">](https://github.com/janklimo) |[<img alt="vith" src="https://avatars1.githubusercontent.com/u/3265539?v=4&s=117" width="117">](https://github.com/vith) |[<img alt="jessica-coursera" src="https://avatars1.githubusercontent.com/u/35155465?v=4&s=117" width="117">](https://github.com/jessica-coursera) |[<img alt="theJoeBiz" src="https://avatars1.githubusercontent.com/u/189589?v=4&s=117" width="117">](https://github.com/theJoeBiz) |[<img alt="jderrough" src="https://avatars3.githubusercontent.com/u/1108358?v=4&s=117" width="117">](https://github.com/jderrough) |[<img alt="jonathanly" src="https://avatars3.githubusercontent.com/u/13286473?v=4&s=117" width="117">](https://github.com/jonathanly) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[vith](https://github.com/vith) |[jessica-coursera](https://github.com/jessica-coursera) |[theJoeBiz](https://github.com/theJoeBiz) |[jderrough](https://github.com/jderrough) |[jonathanly](https://github.com/jonathanly) |[jorgeepc](https://github.com/jorgeepc) |
+[janklimo](https://github.com/janklimo) |[vith](https://github.com/vith) |[jessica-coursera](https://github.com/jessica-coursera) |[theJoeBiz](https://github.com/theJoeBiz) |[jderrough](https://github.com/jderrough) |[jonathanly](https://github.com/jonathanly) |
 
-[<img alt="julianocomg" src="https://avatars1.githubusercontent.com/u/7483557?v=4&s=117" width="117">](https://github.com/julianocomg) |[<img alt="dogrocker" src="https://avatars0.githubusercontent.com/u/8379027?v=4&s=117" width="117">](https://github.com/dogrocker) |[<img alt="firesharkstudios" src="https://avatars1.githubusercontent.com/u/17069637?v=4&s=117" width="117">](https://github.com/firesharkstudios) |[<img alt="kyleparisi" src="https://avatars0.githubusercontent.com/u/1286753?v=4&s=117" width="117">](https://github.com/kyleparisi) |[<img alt="lafe" src="https://avatars1.githubusercontent.com/u/4070008?v=4&s=117" width="117">](https://github.com/lafe) |[<img alt="larowlan" src="https://avatars2.githubusercontent.com/u/555254?v=4&s=117" width="117">](https://github.com/larowlan) |
+[<img alt="jorgeepc" src="https://avatars3.githubusercontent.com/u/3879892?v=4&s=117" width="117">](https://github.com/jorgeepc) |[<img alt="julianocomg" src="https://avatars1.githubusercontent.com/u/7483557?v=4&s=117" width="117">](https://github.com/julianocomg) |[<img alt="dogrocker" src="https://avatars0.githubusercontent.com/u/8379027?v=4&s=117" width="117">](https://github.com/dogrocker) |[<img alt="firesharkstudios" src="https://avatars1.githubusercontent.com/u/17069637?v=4&s=117" width="117">](https://github.com/firesharkstudios) |[<img alt="kyleparisi" src="https://avatars0.githubusercontent.com/u/1286753?v=4&s=117" width="117">](https://github.com/kyleparisi) |[<img alt="lafe" src="https://avatars1.githubusercontent.com/u/4070008?v=4&s=117" width="117">](https://github.com/lafe) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[julianocomg](https://github.com/julianocomg) |[dogrocker](https://github.com/dogrocker) |[firesharkstudios](https://github.com/firesharkstudios) |[kyleparisi](https://github.com/kyleparisi) |[lafe](https://github.com/lafe) |[larowlan](https://github.com/larowlan) |
+[jorgeepc](https://github.com/jorgeepc) |[julianocomg](https://github.com/julianocomg) |[dogrocker](https://github.com/dogrocker) |[firesharkstudios](https://github.com/firesharkstudios) |[kyleparisi](https://github.com/kyleparisi) |[lafe](https://github.com/lafe) |
 
-[<img alt="dviry" src="https://avatars3.githubusercontent.com/u/1230260?v=4&s=117" width="117">](https://github.com/dviry) |[<img alt="galli-leo" src="https://avatars3.githubusercontent.com/u/5339762?v=4&s=117" width="117">](https://github.com/galli-leo) |[<img alt="leods92" src="https://avatars0.githubusercontent.com/u/879395?v=4&s=117" width="117">](https://github.com/leods92) |[<img alt="louim" src="https://avatars2.githubusercontent.com/u/923718?v=4&s=117" width="117">](https://github.com/louim) |[<img alt="lucaperret" src="https://avatars1.githubusercontent.com/u/1887122?v=4&s=117" width="117">](https://github.com/lucaperret) |[<img alt="mperrando" src="https://avatars2.githubusercontent.com/u/525572?v=4&s=117" width="117">](https://github.com/mperrando) |
+[<img alt="larowlan" src="https://avatars2.githubusercontent.com/u/555254?v=4&s=117" width="117">](https://github.com/larowlan) |[<img alt="dviry" src="https://avatars3.githubusercontent.com/u/1230260?v=4&s=117" width="117">](https://github.com/dviry) |[<img alt="galli-leo" src="https://avatars3.githubusercontent.com/u/5339762?v=4&s=117" width="117">](https://github.com/galli-leo) |[<img alt="leods92" src="https://avatars0.githubusercontent.com/u/879395?v=4&s=117" width="117">](https://github.com/leods92) |[<img alt="louim" src="https://avatars2.githubusercontent.com/u/923718?v=4&s=117" width="117">](https://github.com/louim) |[<img alt="lucaperret" src="https://avatars1.githubusercontent.com/u/1887122?v=4&s=117" width="117">](https://github.com/lucaperret) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[dviry](https://github.com/dviry) |[galli-leo](https://github.com/galli-leo) |[leods92](https://github.com/leods92) |[louim](https://github.com/louim) |[lucaperret](https://github.com/lucaperret) |[mperrando](https://github.com/mperrando) |
+[larowlan](https://github.com/larowlan) |[dviry](https://github.com/dviry) |[galli-leo](https://github.com/galli-leo) |[leods92](https://github.com/leods92) |[louim](https://github.com/louim) |[lucaperret](https://github.com/lucaperret) |
 
-[<img alt="marcusforsberg" src="https://avatars0.githubusercontent.com/u/1009069?v=4&s=117" width="117">](https://github.com/marcusforsberg) |[<img alt="mattfik" src="https://avatars2.githubusercontent.com/u/1638028?v=4&s=117" width="117">](https://github.com/mattfik) |[<img alt="hrsh" src="https://avatars3.githubusercontent.com/u/1929359?v=4&s=117" width="117">](https://github.com/hrsh) |[<img alt="mhulet" src="https://avatars0.githubusercontent.com/u/293355?v=4&s=117" width="117">](https://github.com/mhulet) |[<img alt="mkabatek" src="https://avatars0.githubusercontent.com/u/1764486?v=4&s=117" width="117">](https://github.com/mkabatek) |[<img alt="achmiral" src="https://avatars0.githubusercontent.com/u/10906059?v=4&s=117" width="117">](https://github.com/achmiral) |
+[<img alt="mperrando" src="https://avatars2.githubusercontent.com/u/525572?v=4&s=117" width="117">](https://github.com/mperrando) |[<img alt="marcusforsberg" src="https://avatars0.githubusercontent.com/u/1009069?v=4&s=117" width="117">](https://github.com/marcusforsberg) |[<img alt="Acconut" src="https://avatars3.githubusercontent.com/u/1375043?v=4&s=117" width="117">](https://github.com/Acconut) |[<img alt="mattfik" src="https://avatars2.githubusercontent.com/u/1638028?v=4&s=117" width="117">](https://github.com/mattfik) |[<img alt="matthewhartstonge" src="https://avatars2.githubusercontent.com/u/6119549?v=4&s=117" width="117">](https://github.com/matthewhartstonge) |[<img alt="hrsh" src="https://avatars3.githubusercontent.com/u/1929359?v=4&s=117" width="117">](https://github.com/hrsh) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[marcusforsberg](https://github.com/marcusforsberg) |[mattfik](https://github.com/mattfik) |[hrsh](https://github.com/hrsh) |[mhulet](https://github.com/mhulet) |[mkabatek](https://github.com/mkabatek) |[achmiral](https://github.com/achmiral) |
+[mperrando](https://github.com/mperrando) |[marcusforsberg](https://github.com/marcusforsberg) |[Acconut](https://github.com/Acconut) |[mattfik](https://github.com/mattfik) |[matthewhartstonge](https://github.com/matthewhartstonge) |[hrsh](https://github.com/hrsh) |
 
-[<img alt="mnafees" src="https://avatars1.githubusercontent.com/u/1763885?v=4&s=117" width="117">](https://github.com/mnafees) |[<img alt="shahimclt" src="https://avatars3.githubusercontent.com/u/8318002?v=4&s=117" width="117">](https://github.com/shahimclt) |[<img alt="naveed-ahmad" src="https://avatars2.githubusercontent.com/u/701567?v=4&s=117" width="117">](https://github.com/naveed-ahmad) |[<img alt="nicojones" src="https://avatars2.githubusercontent.com/u/6078915?v=4&s=117" width="117">](https://github.com/nicojones) |[<img alt="olemoign" src="https://avatars3.githubusercontent.com/u/11632871?v=4&s=117" width="117">](https://github.com/olemoign) |[<img alt="leftdevel" src="https://avatars3.githubusercontent.com/u/843337?v=4&s=117" width="117">](https://github.com/leftdevel) |
+[<img alt="mhulet" src="https://avatars0.githubusercontent.com/u/293355?v=4&s=117" width="117">](https://github.com/mhulet) |[<img alt="mkabatek" src="https://avatars0.githubusercontent.com/u/1764486?v=4&s=117" width="117">](https://github.com/mkabatek) |[<img alt="achmiral" src="https://avatars0.githubusercontent.com/u/10906059?v=4&s=117" width="117">](https://github.com/achmiral) |[<img alt="mnafees" src="https://avatars1.githubusercontent.com/u/1763885?v=4&s=117" width="117">](https://github.com/mnafees) |[<img alt="shahimclt" src="https://avatars3.githubusercontent.com/u/8318002?v=4&s=117" width="117">](https://github.com/shahimclt) |[<img alt="naveed-ahmad" src="https://avatars2.githubusercontent.com/u/701567?v=4&s=117" width="117">](https://github.com/naveed-ahmad) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[mnafees](https://github.com/mnafees) |[shahimclt](https://github.com/shahimclt) |[naveed-ahmad](https://github.com/naveed-ahmad) |[nicojones](https://github.com/nicojones) |[olemoign](https://github.com/olemoign) |[leftdevel](https://github.com/leftdevel) |
+[mhulet](https://github.com/mhulet) |[mkabatek](https://github.com/mkabatek) |[achmiral](https://github.com/achmiral) |[mnafees](https://github.com/mnafees) |[shahimclt](https://github.com/shahimclt) |[naveed-ahmad](https://github.com/naveed-ahmad) |
 
-[<img alt="cryptic022" src="https://avatars2.githubusercontent.com/u/18145703?v=4&s=117" width="117">](https://github.com/cryptic022) |[<img alt="phillipalexander" src="https://avatars0.githubusercontent.com/u/1577682?v=4&s=117" width="117">](https://github.com/phillipalexander) |[<img alt="Pzoco" src="https://avatars0.githubusercontent.com/u/3101348?v=4&s=117" width="117">](https://github.com/Pzoco) |[<img alt="eman8519" src="https://avatars2.githubusercontent.com/u/2380804?v=4&s=117" width="117">](https://github.com/eman8519) |[<img alt="luarmr" src="https://avatars3.githubusercontent.com/u/817416?v=4&s=117" width="117">](https://github.com/luarmr) |[<img alt="phobos101" src="https://avatars2.githubusercontent.com/u/7114944?v=4&s=117" width="117">](https://github.com/phobos101) |
+[<img alt="nicojones" src="https://avatars2.githubusercontent.com/u/6078915?v=4&s=117" width="117">](https://github.com/nicojones) |[<img alt="olemoign" src="https://avatars3.githubusercontent.com/u/11632871?v=4&s=117" width="117">](https://github.com/olemoign) |[<img alt="leftdevel" src="https://avatars3.githubusercontent.com/u/843337?v=4&s=117" width="117">](https://github.com/leftdevel) |[<img alt="cryptic022" src="https://avatars2.githubusercontent.com/u/18145703?v=4&s=117" width="117">](https://github.com/cryptic022) |[<img alt="pedrofs" src="https://avatars0.githubusercontent.com/u/56484?v=4&s=117" width="117">](https://github.com/pedrofs) |[<img alt="phillipalexander" src="https://avatars0.githubusercontent.com/u/1577682?v=4&s=117" width="117">](https://github.com/phillipalexander) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[cryptic022](https://github.com/cryptic022) |[phillipalexander](https://github.com/phillipalexander) |[Pzoco](https://github.com/Pzoco) |[eman8519](https://github.com/eman8519) |[luarmr](https://github.com/luarmr) |[phobos101](https://github.com/phobos101) |
+[nicojones](https://github.com/nicojones) |[olemoign](https://github.com/olemoign) |[leftdevel](https://github.com/leftdevel) |[cryptic022](https://github.com/cryptic022) |[pedrofs](https://github.com/pedrofs) |[phillipalexander](https://github.com/phillipalexander) |
 
-[<img alt="romain-preston" src="https://avatars3.githubusercontent.com/u/1517040?v=4&s=117" width="117">](https://github.com/romain-preston) |[<img alt="scherroman" src="https://avatars3.githubusercontent.com/u/7923938?v=4&s=117" width="117">](https://github.com/scherroman) |[<img alt="fortunto2" src="https://avatars1.githubusercontent.com/u/1236751?v=4&s=117" width="117">](https://github.com/fortunto2) |[<img alt="jrschumacher" src="https://avatars1.githubusercontent.com/u/46549?v=4&s=117" width="117">](https://github.com/jrschumacher) |[<img alt="samuelcolburn" src="https://avatars2.githubusercontent.com/u/9741902?v=4&s=117" width="117">](https://github.com/samuelcolburn) |[<img alt="sergei-zelinsky" src="https://avatars2.githubusercontent.com/u/19428086?v=4&s=117" width="117">](https://github.com/sergei-zelinsky) |
+[<img alt="Pzoco" src="https://avatars0.githubusercontent.com/u/3101348?v=4&s=117" width="117">](https://github.com/Pzoco) |[<img alt="eman8519" src="https://avatars2.githubusercontent.com/u/2380804?v=4&s=117" width="117">](https://github.com/eman8519) |[<img alt="luarmr" src="https://avatars3.githubusercontent.com/u/817416?v=4&s=117" width="117">](https://github.com/luarmr) |[<img alt="phobos101" src="https://avatars2.githubusercontent.com/u/7114944?v=4&s=117" width="117">](https://github.com/phobos101) |[<img alt="romain-preston" src="https://avatars3.githubusercontent.com/u/1517040?v=4&s=117" width="117">](https://github.com/romain-preston) |[<img alt="scherroman" src="https://avatars3.githubusercontent.com/u/7923938?v=4&s=117" width="117">](https://github.com/scherroman) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[romain-preston](https://github.com/romain-preston) |[scherroman](https://github.com/scherroman) |[fortunto2](https://github.com/fortunto2) |[jrschumacher](https://github.com/jrschumacher) |[samuelcolburn](https://github.com/samuelcolburn) |[sergei-zelinsky](https://github.com/sergei-zelinsky) |
+[Pzoco](https://github.com/Pzoco) |[eman8519](https://github.com/eman8519) |[luarmr](https://github.com/luarmr) |[phobos101](https://github.com/phobos101) |[romain-preston](https://github.com/romain-preston) |[scherroman](https://github.com/scherroman) |
 
-[<img alt="SpazzMarticus" src="https://avatars0.githubusercontent.com/u/5716457?v=4&s=117" width="117">](https://github.com/SpazzMarticus) |[<img alt="suchoproduction" src="https://avatars3.githubusercontent.com/u/6931349?v=4&s=117" width="117">](https://github.com/suchoproduction) |[<img alt="waptik" src="https://avatars1.githubusercontent.com/u/1687551?v=4&s=117" width="117">](https://github.com/waptik) |[<img alt="steverob" src="https://avatars2.githubusercontent.com/u/1220480?v=4&s=117" width="117">](https://github.com/steverob) |[<img alt="tajchumber" src="https://avatars3.githubusercontent.com/u/16062635?v=4&s=117" width="117">](https://github.com/tajchumber) |[<img alt="Tashows" src="https://avatars2.githubusercontent.com/u/16656928?v=4&s=117" width="117">](https://github.com/Tashows) |
+[<img alt="fortunto2" src="https://avatars1.githubusercontent.com/u/1236751?v=4&s=117" width="117">](https://github.com/fortunto2) |[<img alt="jrschumacher" src="https://avatars1.githubusercontent.com/u/46549?v=4&s=117" width="117">](https://github.com/jrschumacher) |[<img alt="samuelcolburn" src="https://avatars2.githubusercontent.com/u/9741902?v=4&s=117" width="117">](https://github.com/samuelcolburn) |[<img alt="sergei-zelinsky" src="https://avatars2.githubusercontent.com/u/19428086?v=4&s=117" width="117">](https://github.com/sergei-zelinsky) |[<img alt="SpazzMarticus" src="https://avatars0.githubusercontent.com/u/5716457?v=4&s=117" width="117">](https://github.com/SpazzMarticus) |[<img alt="suchoproduction" src="https://avatars3.githubusercontent.com/u/6931349?v=4&s=117" width="117">](https://github.com/suchoproduction) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[SpazzMarticus](https://github.com/SpazzMarticus) |[suchoproduction](https://github.com/suchoproduction) |[waptik](https://github.com/waptik) |[steverob](https://github.com/steverob) |[tajchumber](https://github.com/tajchumber) |[Tashows](https://github.com/Tashows) |
+[fortunto2](https://github.com/fortunto2) |[jrschumacher](https://github.com/jrschumacher) |[samuelcolburn](https://github.com/samuelcolburn) |[sergei-zelinsky](https://github.com/sergei-zelinsky) |[SpazzMarticus](https://github.com/SpazzMarticus) |[suchoproduction](https://github.com/suchoproduction) |
 
-[<img alt="twarlop" src="https://avatars3.githubusercontent.com/u/2856082?v=4&s=117" width="117">](https://github.com/twarlop) |[<img alt="tmaier" src="https://avatars0.githubusercontent.com/u/350038?v=4&s=117" width="117">](https://github.com/tmaier) |[<img alt="tomsaleeba" src="https://avatars0.githubusercontent.com/u/1773838?v=4&s=117" width="117">](https://github.com/tomsaleeba) |[<img alt="tvaliasek" src="https://avatars2.githubusercontent.com/u/8644946?v=4&s=117" width="117">](https://github.com/tvaliasek) |[<img alt="vially" src="https://avatars1.githubusercontent.com/u/433598?v=4&s=117" width="117">](https://github.com/vially) |[<img alt="nagyv" src="https://avatars2.githubusercontent.com/u/126671?v=4&s=117" width="117">](https://github.com/nagyv) |
+[<img alt="waptik" src="https://avatars1.githubusercontent.com/u/1687551?v=4&s=117" width="117">](https://github.com/waptik) |[<img alt="steverob" src="https://avatars2.githubusercontent.com/u/1220480?v=4&s=117" width="117">](https://github.com/steverob) |[<img alt="tajchumber" src="https://avatars3.githubusercontent.com/u/16062635?v=4&s=117" width="117">](https://github.com/tajchumber) |[<img alt="Tashows" src="https://avatars2.githubusercontent.com/u/16656928?v=4&s=117" width="117">](https://github.com/Tashows) |[<img alt="twarlop" src="https://avatars3.githubusercontent.com/u/2856082?v=4&s=117" width="117">](https://github.com/twarlop) |[<img alt="tmaier" src="https://avatars0.githubusercontent.com/u/350038?v=4&s=117" width="117">](https://github.com/tmaier) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[twarlop](https://github.com/twarlop) |[tmaier](https://github.com/tmaier) |[tomsaleeba](https://github.com/tomsaleeba) |[tvaliasek](https://github.com/tvaliasek) |[vially](https://github.com/vially) |[nagyv](https://github.com/nagyv) |
+[waptik](https://github.com/waptik) |[steverob](https://github.com/steverob) |[tajchumber](https://github.com/tajchumber) |[Tashows](https://github.com/Tashows) |[twarlop](https://github.com/twarlop) |[tmaier](https://github.com/tmaier) |
 
-[<img alt="willycamargo" src="https://avatars1.githubusercontent.com/u/5041887?v=4&s=117" width="117">](https://github.com/willycamargo) |[<img alt="xhocquet" src="https://avatars2.githubusercontent.com/u/8116516?v=4&s=117" width="117">](https://github.com/xhocquet) |[<img alt="YehudaKremer" src="https://avatars3.githubusercontent.com/u/946652?v=4&s=117" width="117">](https://github.com/YehudaKremer) |[<img alt="zachconner" src="https://avatars0.githubusercontent.com/u/11339326?v=4&s=117" width="117">](https://github.com/zachconner) |[<img alt="zacharylawson" src="https://avatars3.githubusercontent.com/u/7375444?v=4&s=117" width="117">](https://github.com/zacharylawson) |[<img alt="agreene-coursera" src="https://avatars0.githubusercontent.com/u/30501355?v=4&s=117" width="117">](https://github.com/agreene-coursera) |
+[<img alt="tomsaleeba" src="https://avatars0.githubusercontent.com/u/1773838?v=4&s=117" width="117">](https://github.com/tomsaleeba) |[<img alt="tvaliasek" src="https://avatars2.githubusercontent.com/u/8644946?v=4&s=117" width="117">](https://github.com/tvaliasek) |[<img alt="sparanoid" src="https://avatars0.githubusercontent.com/u/96356?v=4&s=117" width="117">](https://github.com/sparanoid) |[<img alt="vially" src="https://avatars1.githubusercontent.com/u/433598?v=4&s=117" width="117">](https://github.com/vially) |[<img alt="nagyv" src="https://avatars2.githubusercontent.com/u/126671?v=4&s=117" width="117">](https://github.com/nagyv) |[<img alt="willycamargo" src="https://avatars1.githubusercontent.com/u/5041887?v=4&s=117" width="117">](https://github.com/willycamargo) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[willycamargo](https://github.com/willycamargo) |[xhocquet](https://github.com/xhocquet) |[YehudaKremer](https://github.com/YehudaKremer) |[zachconner](https://github.com/zachconner) |[zacharylawson](https://github.com/zacharylawson) |[agreene-coursera](https://github.com/agreene-coursera) |
+[tomsaleeba](https://github.com/tomsaleeba) |[tvaliasek](https://github.com/tvaliasek) |[sparanoid](https://github.com/sparanoid) |[vially](https://github.com/vially) |[nagyv](https://github.com/nagyv) |[willycamargo](https://github.com/willycamargo) |
+
+[<img alt="xhocquet" src="https://avatars2.githubusercontent.com/u/8116516?v=4&s=117" width="117">](https://github.com/xhocquet) |[<img alt="yaegor" src="https://avatars2.githubusercontent.com/u/3315?v=4&s=117" width="117">](https://github.com/yaegor) |[<img alt="YehudaKremer" src="https://avatars3.githubusercontent.com/u/946652?v=4&s=117" width="117">](https://github.com/YehudaKremer) |[<img alt="zachconner" src="https://avatars0.githubusercontent.com/u/11339326?v=4&s=117" width="117">](https://github.com/zachconner) |[<img alt="zacharylawson" src="https://avatars3.githubusercontent.com/u/7375444?v=4&s=117" width="117">](https://github.com/zacharylawson) |[<img alt="agreene-coursera" src="https://avatars0.githubusercontent.com/u/30501355?v=4&s=117" width="117">](https://github.com/agreene-coursera) |
+:---: |:---: |:---: |:---: |:---: |:---: |
+[xhocquet](https://github.com/xhocquet) |[yaegor](https://github.com/yaegor) |[YehudaKremer](https://github.com/YehudaKremer) |[zachconner](https://github.com/zachconner) |[zacharylawson](https://github.com/zacharylawson) |[agreene-coursera](https://github.com/agreene-coursera) |
 
 [<img alt="alfatv" src="https://avatars2.githubusercontent.com/u/62238673?v=4&s=117" width="117">](https://github.com/alfatv) |[<img alt="arggh" src="https://avatars3.githubusercontent.com/u/17210302?v=4&s=117" width="117">](https://github.com/arggh) |[<img alt="avalla" src="https://avatars1.githubusercontent.com/u/986614?v=4&s=117" width="117">](https://github.com/avalla) |[<img alt="c0b41" src="https://avatars1.githubusercontent.com/u/2834954?v=4&s=117" width="117">](https://github.com/c0b41) |[<img alt="canvasbh" src="https://avatars3.githubusercontent.com/u/44477734?v=4&s=117" width="117">](https://github.com/canvasbh) |[<img alt="craigcbrunner" src="https://avatars3.githubusercontent.com/u/2780521?v=4&s=117" width="117">](https://github.com/craigcbrunner) |
 :---: |:---: |:---: |:---: |:---: |:---: |
@@ -363,9 +367,9 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu
 :---: |:---: |:---: |:---: |:---: |:---: |
 [ninesalt](https://github.com/ninesalt) |[luntta](https://github.com/luntta) |[rhymes](https://github.com/rhymes) |[rlebosse](https://github.com/rlebosse) |[rtaieb](https://github.com/rtaieb) |[thanhthot](https://github.com/thanhthot) |
 
-[<img alt="tinny77" src="https://avatars2.githubusercontent.com/u/1872936?v=4&s=117" width="117">](https://github.com/tinny77) |[<img alt="yoann-hellopret" src="https://avatars3.githubusercontent.com/u/46525558?v=4&s=117" width="117">](https://github.com/yoann-hellopret) |[<img alt="dominiceden" src="https://avatars2.githubusercontent.com/u/6367692?v=4&s=117" width="117">](https://github.com/dominiceden) |
+[<img alt="tinny77" src="https://avatars2.githubusercontent.com/u/1872936?v=4&s=117" width="117">](https://github.com/tinny77) |[<img alt="yoann-hellopret" src="https://avatars3.githubusercontent.com/u/46525558?v=4&s=117" width="117">](https://github.com/yoann-hellopret) |[<img alt="olitomas" src="https://avatars0.githubusercontent.com/u/6918659?v=4&s=117" width="117">](https://github.com/olitomas) |
 :---: |:---: |:---: |
-[tinny77](https://github.com/tinny77) |[yoann-hellopret](https://github.com/yoann-hellopret) |[dominiceden](https://github.com/dominiceden) |
+[tinny77](https://github.com/tinny77) |[yoann-hellopret](https://github.com/yoann-hellopret) |[olitomas](https://github.com/olitomas) |
 
 
 <!--/contributors-->

+ 2 - 2
examples/cdn-example/index.html

@@ -4,11 +4,11 @@
     <title></title>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link href="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.css" rel="stylesheet">
+    <link href="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.css" rel="stylesheet">
   </head>
   <body>
     <button id="uppyModalOpener">Open Modal</button>
-    <script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
+    <script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
     <script>
       const uppy = Uppy.Core({debug: true, autoProceed: false})
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })

+ 14 - 0
examples/custom-provider/babel.config.js

@@ -0,0 +1,14 @@
+module.exports = (api) => {
+  api.env('test')
+  return {
+    presets: [
+      ['@babel/preset-env', {
+        modules: false,
+        loose: true
+      }]
+    ],
+    plugins: [
+      ['@babel/plugin-transform-react-jsx', { pragma: 'h' }]
+    ].filter(Boolean)
+  }
+}

+ 14 - 58
examples/custom-provider/client/MyCustomProvider.js

@@ -10,20 +10,22 @@ module.exports = class MyCustomProvider extends Plugin {
     this.id = this.opts.id || 'MyCustomProvider'
     Provider.initPlugin(this, opts)
 
-    this.title = 'MyCustomProvider'
+    this.title = 'MyUnsplash'
     this.icon = () => (
-      <img src="https://uppy.io/images/logos/uppy-dog-head-arrow.svg" width="23" />
+      <svg width="32" height="32" xmlns="http://www.w3.org/2000/svg">
+        <path d="M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z" fill="#000000" fill-rule="nonzero" />
+      </svg>
     )
 
-    // writing out the key explicitly for readability the key used to store
-    // the provider instance must be equal to this.id.
-    this[this.id] = new Provider(uppy, {
+    this.provider = new Provider(uppy, {
       companionUrl: this.opts.companionUrl,
-      provider: 'mycustomprovider'
+      companionHeaders: this.opts.companionHeaders || this.opts.serverHeaders,
+      provider: 'myunsplash',
+      pluginId: this.id
     })
 
     this.files = []
-    this.onAuth = this.onAuth.bind(this)
+    this.onFirstRender = this.onFirstRender.bind(this)
     this.render = this.render.bind(this)
 
     // merge default options with the ones set by user
@@ -31,7 +33,9 @@ module.exports = class MyCustomProvider extends Plugin {
   }
 
   install () {
-    this.view = new ProviderViews(this)
+    this.view = new ProviderViews(this, {
+      provider: this.provider
+    })
     // Set default state
     this.setPluginState({
       authenticated: false,
@@ -54,56 +58,8 @@ module.exports = class MyCustomProvider extends Plugin {
     this.unmount()
   }
 
-  onAuth (authenticated) {
-    this.setPluginState({ authenticated })
-    if (authenticated) {
-      this.view.getFolder()
-    }
-  }
-
-  isFolder (item) {
-    return false
-  }
-
-  getItemData (item) {
-    return item
-  }
-
-  getItemIcon (item) {
-    return 'https://uppy.io/images/logos/uppy-dog-head-arrow.svg'
-  }
-
-  getItemSubList (item) {
-    return item.entries
-  }
-
-  getItemName (item) {
-    return item.name
-  }
-
-  getMimeType (item) {
-    // mime types aren't supported.
-    return null
-  }
-
-  getItemId (item) {
-    return item.name
-  }
-
-  getItemRequestPath (item) {
-    return encodeURIComponent(item.name)
-  }
-
-  getItemModifiedDate (item) {
-    return Date.now()
-  }
-
-  getItemThumbnailUrl (item) {
-    return 'https://uppy.io/images/logos/uppy-dog-head-arrow.svg'
-  }
-
-  getUsername () {
-    return 'Cool Dog'
+  onFirstRender () {
+    return this.view.getFolder()
   }
 
   render (state) {

+ 3954 - 0
examples/custom-provider/package-lock.json

@@ -0,0 +1,3954 @@
+{
+  "name": "@uppy-example/custom-provider",
+  "requires": true,
+  "lockfileVersion": 1,
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz",
+      "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==",
+      "requires": {
+        "@babel/highlight": "^7.10.1"
+      }
+    },
+    "@babel/core": {
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz",
+      "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==",
+      "requires": {
+        "@babel/code-frame": "^7.8.3",
+        "@babel/generator": "^7.9.0",
+        "@babel/helper-module-transforms": "^7.9.0",
+        "@babel/helpers": "^7.9.0",
+        "@babel/parser": "^7.9.0",
+        "@babel/template": "^7.8.6",
+        "@babel/traverse": "^7.9.0",
+        "@babel/types": "^7.9.0",
+        "convert-source-map": "^1.7.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.1",
+        "json5": "^2.1.2",
+        "lodash": "^4.17.13",
+        "resolve": "^1.3.2",
+        "semver": "^5.4.1",
+        "source-map": "^0.5.0"
+      }
+    },
+    "@babel/generator": {
+      "version": "7.10.2",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.2.tgz",
+      "integrity": "sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==",
+      "requires": {
+        "@babel/types": "^7.10.2",
+        "jsesc": "^2.5.1",
+        "lodash": "^4.17.13",
+        "source-map": "^0.5.0"
+      }
+    },
+    "@babel/helper-function-name": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz",
+      "integrity": "sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==",
+      "requires": {
+        "@babel/helper-get-function-arity": "^7.10.1",
+        "@babel/template": "^7.10.1",
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/helper-get-function-arity": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz",
+      "integrity": "sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==",
+      "requires": {
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/helper-member-expression-to-functions": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz",
+      "integrity": "sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g==",
+      "requires": {
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/helper-module-imports": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz",
+      "integrity": "sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==",
+      "requires": {
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/helper-module-transforms": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz",
+      "integrity": "sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg==",
+      "requires": {
+        "@babel/helper-module-imports": "^7.10.1",
+        "@babel/helper-replace-supers": "^7.10.1",
+        "@babel/helper-simple-access": "^7.10.1",
+        "@babel/helper-split-export-declaration": "^7.10.1",
+        "@babel/template": "^7.10.1",
+        "@babel/types": "^7.10.1",
+        "lodash": "^4.17.13"
+      }
+    },
+    "@babel/helper-optimise-call-expression": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz",
+      "integrity": "sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg==",
+      "requires": {
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/helper-replace-supers": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz",
+      "integrity": "sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A==",
+      "requires": {
+        "@babel/helper-member-expression-to-functions": "^7.10.1",
+        "@babel/helper-optimise-call-expression": "^7.10.1",
+        "@babel/traverse": "^7.10.1",
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/helper-simple-access": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz",
+      "integrity": "sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw==",
+      "requires": {
+        "@babel/template": "^7.10.1",
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/helper-split-export-declaration": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz",
+      "integrity": "sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==",
+      "requires": {
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz",
+      "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw=="
+    },
+    "@babel/helpers": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.1.tgz",
+      "integrity": "sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==",
+      "requires": {
+        "@babel/template": "^7.10.1",
+        "@babel/traverse": "^7.10.1",
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/highlight": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz",
+      "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==",
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.10.1",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      }
+    },
+    "@babel/parser": {
+      "version": "7.10.2",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.2.tgz",
+      "integrity": "sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ=="
+    },
+    "@babel/template": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.1.tgz",
+      "integrity": "sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig==",
+      "requires": {
+        "@babel/code-frame": "^7.10.1",
+        "@babel/parser": "^7.10.1",
+        "@babel/types": "^7.10.1"
+      }
+    },
+    "@babel/traverse": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.1.tgz",
+      "integrity": "sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ==",
+      "requires": {
+        "@babel/code-frame": "^7.10.1",
+        "@babel/generator": "^7.10.1",
+        "@babel/helper-function-name": "^7.10.1",
+        "@babel/helper-split-export-declaration": "^7.10.1",
+        "@babel/parser": "^7.10.1",
+        "@babel/types": "^7.10.1",
+        "debug": "^4.1.0",
+        "globals": "^11.1.0",
+        "lodash": "^4.17.13"
+      }
+    },
+    "@babel/types": {
+      "version": "7.10.2",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz",
+      "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==",
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.10.1",
+        "lodash": "^4.17.13",
+        "to-fast-properties": "^2.0.0"
+      }
+    },
+    "JSONStream": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+      "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+      "requires": {
+        "jsonparse": "^1.2.0",
+        "through": ">=2.2.7 <3"
+      }
+    },
+    "accepts": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+      "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+      "requires": {
+        "mime-types": "~2.1.24",
+        "negotiator": "0.6.2"
+      }
+    },
+    "acorn": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz",
+      "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ=="
+    },
+    "acorn-node": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
+      "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
+      "requires": {
+        "acorn": "^7.0.0",
+        "acorn-walk": "^7.0.0",
+        "xtend": "^4.0.2"
+      }
+    },
+    "acorn-walk": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz",
+      "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ=="
+    },
+    "ansi-regex": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
+      "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk="
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "anymatch": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+      "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+      "requires": {
+        "micromatch": "^3.1.4",
+        "normalize-path": "^2.1.1"
+      },
+      "dependencies": {
+        "normalize-path": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+          "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+          "requires": {
+            "remove-trailing-separator": "^1.0.1"
+          }
+        }
+      }
+    },
+    "arr-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
+    },
+    "arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
+    },
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+    },
+    "array-unique": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
+    },
+    "asn1.js": {
+      "version": "4.10.1",
+      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+      "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+      "requires": {
+        "bn.js": "^4.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
+        }
+      }
+    },
+    "assert": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
+      "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+      "requires": {
+        "object-assign": "^4.1.1",
+        "util": "0.10.3"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
+        },
+        "util": {
+          "version": "0.10.3",
+          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+          "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+          "requires": {
+            "inherits": "2.0.1"
+          }
+        }
+      }
+    },
+    "assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
+    },
+    "async-each": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ=="
+    },
+    "async-limiter": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
+    },
+    "atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
+    },
+    "babelify": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz",
+      "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg=="
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+    },
+    "base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "requires": {
+        "cache-base": "^1.0.1",
+        "class-utils": "^0.3.5",
+        "component-emitter": "^1.2.1",
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.1",
+        "mixin-deep": "^1.2.0",
+        "pascalcase": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "base64-js": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
+    },
+    "binary-extensions": {
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+      "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
+    },
+    "bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "optional": true,
+      "requires": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
+    "bn.js": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz",
+      "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA=="
+    },
+    "body-parser": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+      "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+      "requires": {
+        "bytes": "3.1.0",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "on-finished": "~2.3.0",
+        "qs": "6.7.0",
+        "raw-body": "2.4.0",
+        "type-is": "~1.6.17"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "bole": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/bole/-/bole-2.0.0.tgz",
+      "integrity": "sha1-2KocaQRnv7T+Ebh0rLLoOH44JhU=",
+      "requires": {
+        "core-util-is": ">=1.0.1 <1.1.0-0",
+        "individual": ">=3.0.0 <3.1.0-0",
+        "json-stringify-safe": ">=5.0.0 <5.1.0-0"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+      "requires": {
+        "arr-flatten": "^1.1.0",
+        "array-unique": "^0.3.2",
+        "extend-shallow": "^2.0.1",
+        "fill-range": "^4.0.0",
+        "isobject": "^3.0.1",
+        "repeat-element": "^1.1.2",
+        "snapdragon": "^0.8.1",
+        "snapdragon-node": "^2.0.1",
+        "split-string": "^3.0.2",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
+    },
+    "browser-pack": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz",
+      "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==",
+      "requires": {
+        "JSONStream": "^1.0.3",
+        "combine-source-map": "~0.8.0",
+        "defined": "^1.0.0",
+        "safe-buffer": "^5.1.1",
+        "through2": "^2.0.0",
+        "umd": "^3.0.0"
+      }
+    },
+    "browser-resolve": {
+      "version": "1.11.3",
+      "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
+      "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
+      "requires": {
+        "resolve": "1.1.7"
+      },
+      "dependencies": {
+        "resolve": {
+          "version": "1.1.7",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs="
+        }
+      }
+    },
+    "browserify": {
+      "version": "16.5.1",
+      "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.5.1.tgz",
+      "integrity": "sha512-EQX0h59Pp+0GtSRb5rL6OTfrttlzv+uyaUVlK6GX3w11SQ0jKPKyjC/54RhPR2ib2KmfcELM06e8FxcI5XNU2A==",
+      "requires": {
+        "JSONStream": "^1.0.3",
+        "assert": "^1.4.0",
+        "browser-pack": "^6.0.1",
+        "browser-resolve": "^1.11.0",
+        "browserify-zlib": "~0.2.0",
+        "buffer": "~5.2.1",
+        "cached-path-relative": "^1.0.0",
+        "concat-stream": "^1.6.0",
+        "console-browserify": "^1.1.0",
+        "constants-browserify": "~1.0.0",
+        "crypto-browserify": "^3.0.0",
+        "defined": "^1.0.0",
+        "deps-sort": "^2.0.0",
+        "domain-browser": "^1.2.0",
+        "duplexer2": "~0.1.2",
+        "events": "^2.0.0",
+        "glob": "^7.1.0",
+        "has": "^1.0.0",
+        "htmlescape": "^1.1.0",
+        "https-browserify": "^1.0.0",
+        "inherits": "~2.0.1",
+        "insert-module-globals": "^7.0.0",
+        "labeled-stream-splicer": "^2.0.0",
+        "mkdirp-classic": "^0.5.2",
+        "module-deps": "^6.0.0",
+        "os-browserify": "~0.3.0",
+        "parents": "^1.0.1",
+        "path-browserify": "~0.0.0",
+        "process": "~0.11.0",
+        "punycode": "^1.3.2",
+        "querystring-es3": "~0.2.0",
+        "read-only-stream": "^2.0.0",
+        "readable-stream": "^2.0.2",
+        "resolve": "^1.1.4",
+        "shasum": "^1.0.0",
+        "shell-quote": "^1.6.1",
+        "stream-browserify": "^2.0.0",
+        "stream-http": "^3.0.0",
+        "string_decoder": "^1.1.1",
+        "subarg": "^1.0.0",
+        "syntax-error": "^1.1.1",
+        "through2": "^2.0.0",
+        "timers-browserify": "^1.0.1",
+        "tty-browserify": "0.0.1",
+        "url": "~0.11.0",
+        "util": "~0.10.1",
+        "vm-browserify": "^1.0.0",
+        "xtend": "^4.0.0"
+      },
+      "dependencies": {
+        "events": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz",
+          "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg=="
+        }
+      }
+    },
+    "browserify-aes": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+      "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+      "requires": {
+        "buffer-xor": "^1.0.3",
+        "cipher-base": "^1.0.0",
+        "create-hash": "^1.1.0",
+        "evp_bytestokey": "^1.0.3",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "browserify-cipher": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+      "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+      "requires": {
+        "browserify-aes": "^1.0.4",
+        "browserify-des": "^1.0.0",
+        "evp_bytestokey": "^1.0.0"
+      }
+    },
+    "browserify-des": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+      "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+      "requires": {
+        "cipher-base": "^1.0.1",
+        "des.js": "^1.0.0",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      }
+    },
+    "browserify-rsa": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+      "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+      "requires": {
+        "bn.js": "^4.1.0",
+        "randombytes": "^2.0.1"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
+        }
+      }
+    },
+    "browserify-sign": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz",
+      "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==",
+      "requires": {
+        "bn.js": "^5.1.1",
+        "browserify-rsa": "^4.0.1",
+        "create-hash": "^1.2.0",
+        "create-hmac": "^1.1.7",
+        "elliptic": "^6.5.2",
+        "inherits": "^2.0.4",
+        "parse-asn1": "^5.1.5",
+        "readable-stream": "^3.6.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.4",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+          "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+        }
+      }
+    },
+    "browserify-zlib": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+      "requires": {
+        "pako": "~1.0.5"
+      }
+    },
+    "budo": {
+      "version": "11.6.3",
+      "resolved": "https://registry.npmjs.org/budo/-/budo-11.6.3.tgz",
+      "integrity": "sha512-U9pV6SoSxGduY/wnoIlDwEEUhxtTFqYqoyWvi3B5nJ/abSxuNmolfAfzgOQIEXqtHhPEA4FlM+VNzdEDOjpIjw==",
+      "requires": {
+        "bole": "^2.0.0",
+        "browserify": "^16.2.3",
+        "chokidar": "^2.0.4",
+        "connect-pushstate": "^1.1.0",
+        "escape-html": "^1.0.3",
+        "events": "^1.0.2",
+        "garnish": "^5.0.0",
+        "get-ports": "^1.0.2",
+        "inject-lr-script": "^2.1.0",
+        "internal-ip": "^3.0.1",
+        "micromatch": "^3.1.10",
+        "on-finished": "^2.3.0",
+        "on-headers": "^1.0.1",
+        "once": "^1.3.2",
+        "opn": "^3.0.2",
+        "path-is-absolute": "^1.0.1",
+        "pem": "^1.13.2",
+        "reload-css": "^1.0.0",
+        "resolve": "^1.1.6",
+        "serve-static": "^1.10.0",
+        "simple-html-index": "^1.4.0",
+        "stacked": "^1.1.1",
+        "stdout-stream": "^1.4.0",
+        "strip-ansi": "^3.0.0",
+        "subarg": "^1.0.0",
+        "term-color": "^1.0.1",
+        "url-trim": "^1.0.0",
+        "watchify-middleware": "^1.8.2",
+        "ws": "^6.2.1",
+        "xtend": "^4.0.0"
+      }
+    },
+    "buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
+      "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
+      "requires": {
+        "base64-js": "^1.0.2",
+        "ieee754": "^1.1.4"
+      }
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+    },
+    "buffer-xor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
+    },
+    "builtin-status-codes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+      "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug="
+    },
+    "bytes": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+    },
+    "cache-base": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+      "requires": {
+        "collection-visit": "^1.0.0",
+        "component-emitter": "^1.2.1",
+        "get-value": "^2.0.6",
+        "has-value": "^1.0.0",
+        "isobject": "^3.0.1",
+        "set-value": "^2.0.0",
+        "to-object-path": "^0.3.0",
+        "union-value": "^1.0.0",
+        "unset-value": "^1.0.0"
+      }
+    },
+    "cached-path-relative": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz",
+      "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg=="
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      }
+    },
+    "charenc": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+      "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
+    },
+    "chokidar": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+      "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+      "requires": {
+        "anymatch": "^2.0.0",
+        "async-each": "^1.0.1",
+        "braces": "^2.3.2",
+        "fsevents": "^1.2.7",
+        "glob-parent": "^3.1.0",
+        "inherits": "^2.0.3",
+        "is-binary-path": "^1.0.0",
+        "is-glob": "^4.0.0",
+        "normalize-path": "^3.0.0",
+        "path-is-absolute": "^1.0.0",
+        "readdirp": "^2.2.1",
+        "upath": "^1.1.1"
+      }
+    },
+    "cipher-base": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+      "requires": {
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "class-utils": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+      "requires": {
+        "arr-union": "^3.1.0",
+        "define-property": "^0.2.5",
+        "isobject": "^3.0.0",
+        "static-extend": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "collection-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "requires": {
+        "map-visit": "^1.0.0",
+        "object-visit": "^1.0.0"
+      }
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+    },
+    "combine-source-map": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz",
+      "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=",
+      "requires": {
+        "convert-source-map": "~1.1.0",
+        "inline-source-map": "~0.6.0",
+        "lodash.memoize": "~3.0.3",
+        "source-map": "~0.5.3"
+      },
+      "dependencies": {
+        "convert-source-map": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
+          "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA="
+        }
+      }
+    },
+    "component-emitter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "connect-pushstate": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/connect-pushstate/-/connect-pushstate-1.1.0.tgz",
+      "integrity": "sha1-vKsiQnHEOWBKD7D2FMCl9WPojiQ="
+    },
+    "console-browserify": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+      "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA=="
+    },
+    "constants-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
+    },
+    "content-disposition": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+      "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+      "requires": {
+        "safe-buffer": "5.1.2"
+      }
+    },
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+    },
+    "convert-source-map": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+      "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+      "requires": {
+        "safe-buffer": "~5.1.1"
+      }
+    },
+    "cookie": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+      "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+    },
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "create-ecdh": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
+      "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+      "requires": {
+        "bn.js": "^4.1.0",
+        "elliptic": "^6.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
+        }
+      }
+    },
+    "create-hash": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+      "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+      "requires": {
+        "cipher-base": "^1.0.1",
+        "inherits": "^2.0.1",
+        "md5.js": "^1.3.4",
+        "ripemd160": "^2.0.1",
+        "sha.js": "^2.4.0"
+      }
+    },
+    "create-hmac": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+      "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+      "requires": {
+        "cipher-base": "^1.0.3",
+        "create-hash": "^1.1.0",
+        "inherits": "^2.0.1",
+        "ripemd160": "^2.0.0",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      }
+    },
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+      "requires": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      }
+    },
+    "crypt": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+      "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
+    },
+    "crypto-browserify": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+      "requires": {
+        "browserify-cipher": "^1.0.0",
+        "browserify-sign": "^4.0.0",
+        "create-ecdh": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "create-hmac": "^1.1.0",
+        "diffie-hellman": "^5.0.0",
+        "inherits": "^2.0.1",
+        "pbkdf2": "^3.0.3",
+        "public-encrypt": "^4.0.0",
+        "randombytes": "^2.0.0",
+        "randomfill": "^1.0.3"
+      }
+    },
+    "dash-ast": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz",
+      "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA=="
+    },
+    "debounce": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz",
+      "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
+    },
+    "debug": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+      "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+      "requires": {
+        "ms": "^2.1.1"
+      }
+    },
+    "decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
+    },
+    "default-gateway": {
+      "version": "2.7.2",
+      "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz",
+      "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==",
+      "requires": {
+        "execa": "^0.10.0",
+        "ip-regex": "^2.1.0"
+      }
+    },
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "requires": {
+        "object-keys": "^1.0.12"
+      }
+    },
+    "define-property": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+      "requires": {
+        "is-descriptor": "^1.0.2",
+        "isobject": "^3.0.1"
+      },
+      "dependencies": {
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "defined": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+      "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM="
+    },
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "deps-sort": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz",
+      "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==",
+      "requires": {
+        "JSONStream": "^1.0.3",
+        "shasum-object": "^1.0.0",
+        "subarg": "^1.0.0",
+        "through2": "^2.0.0"
+      }
+    },
+    "des.js": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
+      "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
+      "requires": {
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0"
+      }
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "detective": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz",
+      "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==",
+      "requires": {
+        "acorn-node": "^1.6.1",
+        "defined": "^1.0.0",
+        "minimist": "^1.1.1"
+      }
+    },
+    "diffie-hellman": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+      "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+      "requires": {
+        "bn.js": "^4.1.0",
+        "miller-rabin": "^4.0.0",
+        "randombytes": "^2.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
+        }
+      }
+    },
+    "domain-browser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+      "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA=="
+    },
+    "duplexer2": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+      "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+      "requires": {
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "elliptic": {
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
+      "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
+      "requires": {
+        "bn.js": "^4.4.0",
+        "brorand": "^1.0.1",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
+        }
+      }
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "es-abstract": {
+      "version": "1.17.5",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+      "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+      "requires": {
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1",
+        "is-callable": "^1.1.5",
+        "is-regex": "^1.0.5",
+        "object-inspect": "^1.7.0",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.0",
+        "string.prototype.trimleft": "^2.1.1",
+        "string.prototype.trimright": "^2.1.1"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "requires": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "es6-promisify": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.1.1.tgz",
+      "integrity": "sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg=="
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
+    "events": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+      "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
+    },
+    "evp_bytestokey": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+      "requires": {
+        "md5.js": "^1.3.4",
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "execa": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
+      "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
+      "requires": {
+        "cross-spawn": "^6.0.0",
+        "get-stream": "^3.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
+      }
+    },
+    "expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+      "requires": {
+        "debug": "^2.3.3",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "posix-character-classes": "^0.1.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "express": {
+      "version": "4.17.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+      "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+      "requires": {
+        "accepts": "~1.3.7",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.19.0",
+        "content-disposition": "0.5.3",
+        "content-type": "~1.0.4",
+        "cookie": "0.4.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "~1.1.2",
+        "fresh": "0.5.2",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.5",
+        "qs": "6.7.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.1.2",
+        "send": "0.17.1",
+        "serve-static": "1.14.1",
+        "setprototypeof": "1.1.1",
+        "statuses": "~1.5.0",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "express-session": {
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz",
+      "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==",
+      "requires": {
+        "cookie": "0.4.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "~2.0.0",
+        "on-headers": "~1.0.2",
+        "parseurl": "~1.3.3",
+        "safe-buffer": "5.2.0",
+        "uid-safe": "~2.1.5"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "depd": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+          "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "safe-buffer": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
+          "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
+        }
+      }
+    },
+    "extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "requires": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "requires": {
+        "array-unique": "^0.3.2",
+        "define-property": "^1.0.0",
+        "expand-brackets": "^2.1.4",
+        "extend-shallow": "^2.0.1",
+        "fragment-cache": "^0.2.1",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "fast-safe-stringify": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
+      "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
+    },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "optional": true
+    },
+    "fill-range": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1",
+        "to-regex-range": "^2.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "finalhandler": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+      "requires": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "statuses": "~1.5.0",
+        "unpipe": "~1.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
+    },
+    "forwarded": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+    },
+    "fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "requires": {
+        "map-cache": "^0.2.2"
+      }
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
+    "from2": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+      "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+      "requires": {
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0"
+      }
+    },
+    "from2-string": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/from2-string/-/from2-string-1.1.0.tgz",
+      "integrity": "sha1-GCgrJ9CKJnyzAwzSuLSw8hKvdSo=",
+      "requires": {
+        "from2": "^2.0.3"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+    },
+    "fsevents": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+      "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+      "optional": true,
+      "requires": {
+        "bindings": "^1.5.0",
+        "nan": "^2.12.1"
+      }
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "garnish": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/garnish/-/garnish-5.2.0.tgz",
+      "integrity": "sha1-vtQ2WTguSxmOM8eTiXvnxwHmVXc=",
+      "requires": {
+        "chalk": "^0.5.1",
+        "minimist": "^1.1.0",
+        "pad-left": "^2.0.0",
+        "pad-right": "^0.2.2",
+        "prettier-bytes": "^1.0.3",
+        "pretty-ms": "^2.1.0",
+        "right-now": "^1.0.0",
+        "split2": "^0.2.1",
+        "stdout-stream": "^1.4.0",
+        "url-trim": "^1.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
+          "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94="
+        },
+        "chalk": {
+          "version": "0.5.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
+          "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
+          "requires": {
+            "ansi-styles": "^1.1.0",
+            "escape-string-regexp": "^1.0.0",
+            "has-ansi": "^0.1.0",
+            "strip-ansi": "^0.3.0",
+            "supports-color": "^0.2.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "0.3.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
+          "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
+          "requires": {
+            "ansi-regex": "^0.2.1"
+          }
+        },
+        "supports-color": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
+          "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo="
+        }
+      }
+    },
+    "gensync": {
+      "version": "1.0.0-beta.1",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
+      "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg=="
+    },
+    "get-assigned-identifiers": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz",
+      "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ=="
+    },
+    "get-ports": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/get-ports/-/get-ports-1.0.3.tgz",
+      "integrity": "sha1-9AvVgKyn7A77e5bL/L6wPviUteg=",
+      "requires": {
+        "map-limit": "0.0.1"
+      }
+    },
+    "get-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+      "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+    },
+    "get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
+    },
+    "glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+      "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+      "requires": {
+        "is-glob": "^3.1.0",
+        "path-dirname": "^1.0.0"
+      },
+      "dependencies": {
+        "is-glob": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+          "requires": {
+            "is-extglob": "^2.1.0"
+          }
+        }
+      }
+    },
+    "globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
+    },
+    "graceful-fs": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+      "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-ansi": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
+      "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
+      "requires": {
+        "ansi-regex": "^0.2.0"
+      }
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+    },
+    "has-symbols": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+      "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+    },
+    "has-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+      "requires": {
+        "get-value": "^2.0.6",
+        "has-values": "^1.0.0",
+        "isobject": "^3.0.0"
+      }
+    },
+    "has-values": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+      "requires": {
+        "is-number": "^3.0.0",
+        "kind-of": "^4.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "hash-base": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+      "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+      "requires": {
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.6.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.4",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+          "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+        }
+      }
+    },
+    "hash.js": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+      "requires": {
+        "inherits": "^2.0.3",
+        "minimalistic-assert": "^1.0.1"
+      }
+    },
+    "hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+      "requires": {
+        "hash.js": "^1.0.3",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "hosted-git-info": {
+      "version": "2.8.8",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+      "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
+    },
+    "htmlescape": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
+      "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E="
+    },
+    "http-errors": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+      "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+      "requires": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.1",
+        "statuses": ">= 1.5.0 < 2",
+        "toidentifier": "1.0.0"
+      }
+    },
+    "https-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+      "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
+    },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "ieee754": {
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+    },
+    "individual": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/individual/-/individual-3.0.0.tgz",
+      "integrity": "sha1-58pPhfiVewGHNPKFdQ3CLsL5hi0="
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+    },
+    "inject-lr-script": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/inject-lr-script/-/inject-lr-script-2.2.0.tgz",
+      "integrity": "sha512-lFLjCOg2XP8233AiET5vFePo910vhNIkKHDzUptNhc+4Y7dsp/TNBiusUUpaxzaGd6UDHy0Lozfl9AwmteK6DQ==",
+      "requires": {
+        "resp-modifier": "^6.0.0"
+      }
+    },
+    "inline-source-map": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz",
+      "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=",
+      "requires": {
+        "source-map": "~0.5.3"
+      }
+    },
+    "insert-module-globals": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz",
+      "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==",
+      "requires": {
+        "JSONStream": "^1.0.3",
+        "acorn-node": "^1.5.2",
+        "combine-source-map": "^0.8.0",
+        "concat-stream": "^1.6.1",
+        "is-buffer": "^1.1.0",
+        "path-is-absolute": "^1.0.1",
+        "process": "~0.11.0",
+        "through2": "^2.0.0",
+        "undeclared-identifiers": "^1.1.2",
+        "xtend": "^4.0.0"
+      }
+    },
+    "internal-ip": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz",
+      "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==",
+      "requires": {
+        "default-gateway": "^2.6.0",
+        "ipaddr.js": "^1.5.2"
+      }
+    },
+    "ip-regex": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+      "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
+    },
+    "ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+    },
+    "is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+    },
+    "is-binary-path": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+      "requires": {
+        "binary-extensions": "^1.0.0"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
+    "is-callable": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+      "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
+    },
+    "is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-date-object": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+      "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
+    },
+    "is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "requires": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+        }
+      }
+    },
+    "is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
+    },
+    "is-finite": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
+      "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w=="
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
+    "is-regex": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+      "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+      "requires": {
+        "has": "^1.0.3"
+      }
+    },
+    "is-stream": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+    },
+    "is-symbol": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+      "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+      "requires": {
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+    },
+    "isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+    },
+    "jsesc": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
+    },
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
+    },
+    "json-stable-stringify": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz",
+      "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=",
+      "requires": {
+        "jsonify": "~0.0.0"
+      }
+    },
+    "json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+    },
+    "json5": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+      "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
+      "requires": {
+        "minimist": "^1.2.5"
+      }
+    },
+    "jsonify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
+    },
+    "jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
+    },
+    "kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
+    },
+    "labeled-stream-splicer": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz",
+      "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==",
+      "requires": {
+        "inherits": "^2.0.1",
+        "stream-splicer": "^2.0.0"
+      }
+    },
+    "load-json-file": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+      "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^4.0.0",
+        "pify": "^3.0.0",
+        "strip-bom": "^3.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.15",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+    },
+    "lodash.memoize": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
+      "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8="
+    },
+    "map-cache": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="
+    },
+    "map-limit": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
+      "integrity": "sha1-63lhAxwPDo0AG/LVb6toXViCLzg=",
+      "requires": {
+        "once": "~1.3.0"
+      },
+      "dependencies": {
+        "once": {
+          "version": "1.3.3",
+          "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+          "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
+          "requires": {
+            "wrappy": "1"
+          }
+        }
+      }
+    },
+    "map-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "requires": {
+        "object-visit": "^1.0.0"
+      }
+    },
+    "md5": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
+      "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
+      "requires": {
+        "charenc": "~0.0.1",
+        "crypt": "~0.0.1",
+        "is-buffer": "~1.1.1"
+      }
+    },
+    "md5.js": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+      "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+      "requires": {
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      }
+    },
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+    },
+    "memorystream": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+      "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI="
+    },
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+    },
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+    },
+    "micromatch": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "braces": "^2.3.1",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "extglob": "^2.0.4",
+        "fragment-cache": "^0.2.1",
+        "kind-of": "^6.0.2",
+        "nanomatch": "^1.2.9",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.2"
+      }
+    },
+    "miller-rabin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+      "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+      "requires": {
+        "bn.js": "^4.0.0",
+        "brorand": "^1.0.1"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
+        }
+      }
+    },
+    "mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+    },
+    "mime-db": {
+      "version": "1.44.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+      "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
+    },
+    "mime-types": {
+      "version": "2.1.27",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+      "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+      "requires": {
+        "mime-db": "1.44.0"
+      }
+    },
+    "minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+    },
+    "minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+    },
+    "mixin-deep": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+      "requires": {
+        "for-in": "^1.0.2",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "mkdirp-classic": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+      "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
+    },
+    "module-deps": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.2.tgz",
+      "integrity": "sha512-a9y6yDv5u5I4A+IPHTnqFxcaKr4p50/zxTjcQJaX2ws9tN/W6J6YXnEKhqRyPhl494dkcxx951onSKVezmI+3w==",
+      "requires": {
+        "JSONStream": "^1.0.3",
+        "browser-resolve": "^1.7.0",
+        "cached-path-relative": "^1.0.2",
+        "concat-stream": "~1.6.0",
+        "defined": "^1.0.0",
+        "detective": "^5.2.0",
+        "duplexer2": "^0.1.2",
+        "inherits": "^2.0.1",
+        "parents": "^1.0.0",
+        "readable-stream": "^2.0.2",
+        "resolve": "^1.4.0",
+        "stream-combiner2": "^1.1.1",
+        "subarg": "^1.0.0",
+        "through2": "^2.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+    },
+    "nan": {
+      "version": "2.14.1",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
+      "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
+      "optional": true
+    },
+    "nanomatch": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "fragment-cache": "^0.2.1",
+        "is-windows": "^1.0.2",
+        "kind-of": "^6.0.2",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      }
+    },
+    "negotiator": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+    },
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
+    },
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+    },
+    "npm-run-all": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
+      "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "chalk": "^2.4.1",
+        "cross-spawn": "^6.0.5",
+        "memorystream": "^0.3.1",
+        "minimatch": "^3.0.4",
+        "pidtree": "^0.3.0",
+        "read-pkg": "^3.0.0",
+        "shell-quote": "^1.6.1",
+        "string.prototype.padend": "^3.0.0"
+      }
+    },
+    "npm-run-path": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+      "requires": {
+        "path-key": "^2.0.0"
+      }
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+    },
+    "object-copy": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+      "requires": {
+        "copy-descriptor": "^0.1.0",
+        "define-property": "^0.2.5",
+        "kind-of": "^3.0.3"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "object-inspect": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+      "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+    },
+    "object-visit": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+      "requires": {
+        "isobject": "^3.0.0"
+      }
+    },
+    "object.assign": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+      "requires": {
+        "define-properties": "^1.1.2",
+        "function-bind": "^1.1.1",
+        "has-symbols": "^1.0.0",
+        "object-keys": "^1.0.11"
+      }
+    },
+    "object.pick": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "on-headers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+      "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "opn": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/opn/-/opn-3.0.3.tgz",
+      "integrity": "sha1-ttmec5n3jWXDuq/+8fsojpuFJDo=",
+      "requires": {
+        "object-assign": "^4.0.1"
+      }
+    },
+    "os-browserify": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+      "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc="
+    },
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+    },
+    "outpipe": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/outpipe/-/outpipe-1.1.1.tgz",
+      "integrity": "sha1-UM+GFjZeh+Ax4ppeyTOaPaRyX6I=",
+      "requires": {
+        "shell-quote": "^1.4.2"
+      }
+    },
+    "p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
+    },
+    "pad-left": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/pad-left/-/pad-left-2.1.0.tgz",
+      "integrity": "sha1-FuajstRKjhOMsIOMx8tAOk/J6ZQ=",
+      "requires": {
+        "repeat-string": "^1.5.4"
+      }
+    },
+    "pad-right": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz",
+      "integrity": "sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=",
+      "requires": {
+        "repeat-string": "^1.5.2"
+      }
+    },
+    "pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
+    "parents": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
+      "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=",
+      "requires": {
+        "path-platform": "~0.11.15"
+      }
+    },
+    "parse-asn1": {
+      "version": "5.1.5",
+      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
+      "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
+      "requires": {
+        "asn1.js": "^4.0.0",
+        "browserify-aes": "^1.0.0",
+        "create-hash": "^1.1.0",
+        "evp_bytestokey": "^1.0.0",
+        "pbkdf2": "^3.0.3",
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+      "requires": {
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1"
+      }
+    },
+    "parse-ms": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz",
+      "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0="
+    },
+    "parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+    },
+    "pascalcase": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
+    },
+    "path-browserify": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+      "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ=="
+    },
+    "path-dirname": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+    },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
+    },
+    "path-platform": {
+      "version": "0.11.15",
+      "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz",
+      "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I="
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "path-type": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+      "requires": {
+        "pify": "^3.0.0"
+      }
+    },
+    "pbkdf2": {
+      "version": "3.0.17",
+      "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
+      "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+      "requires": {
+        "create-hash": "^1.1.2",
+        "create-hmac": "^1.1.4",
+        "ripemd160": "^2.0.1",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      }
+    },
+    "pem": {
+      "version": "1.14.4",
+      "resolved": "https://registry.npmjs.org/pem/-/pem-1.14.4.tgz",
+      "integrity": "sha512-v8lH3NpirgiEmbOqhx0vwQTxwi0ExsiWBGYh0jYNq7K6mQuO4gI6UEFlr6fLAdv9TPXRt6GqiwE37puQdIDS8g==",
+      "requires": {
+        "es6-promisify": "^6.0.0",
+        "md5": "^2.2.1",
+        "os-tmpdir": "^1.0.1",
+        "which": "^2.0.2"
+      },
+      "dependencies": {
+        "which": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+          "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+          "requires": {
+            "isexe": "^2.0.0"
+          }
+        }
+      }
+    },
+    "pidtree": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
+      "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA=="
+    },
+    "pify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
+    },
+    "plur": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz",
+      "integrity": "sha1-24XGgU9eXlo7Se/CjWBP7GKXUVY="
+    },
+    "posix-character-classes": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
+    },
+    "prettier-bytes": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/prettier-bytes/-/prettier-bytes-1.0.4.tgz",
+      "integrity": "sha1-mUsCqkb2mcULYle1+qp/4lV+YtY="
+    },
+    "pretty-ms": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz",
+      "integrity": "sha1-QlfCVt8/sLRR1q/6qwIYhBJpgdw=",
+      "requires": {
+        "is-finite": "^1.0.1",
+        "parse-ms": "^1.0.0",
+        "plur": "^1.0.0"
+      }
+    },
+    "process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
+    "proxy-addr": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+      "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+      "requires": {
+        "forwarded": "~0.1.2",
+        "ipaddr.js": "1.9.1"
+      }
+    },
+    "public-encrypt": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+      "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+      "requires": {
+        "bn.js": "^4.1.0",
+        "browserify-rsa": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "parse-asn1": "^5.0.0",
+        "randombytes": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
+        }
+      }
+    },
+    "punycode": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+    },
+    "qs": {
+      "version": "6.7.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+      "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+    },
+    "query-string": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+      "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+      "requires": {
+        "object-assign": "^4.1.0",
+        "strict-uri-encode": "^1.0.0"
+      }
+    },
+    "querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+    },
+    "querystring-es3": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+      "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
+    },
+    "random-bytes": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+      "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
+    },
+    "randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "requires": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "randomfill": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+      "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+      "requires": {
+        "randombytes": "^2.0.5",
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+    },
+    "raw-body": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+      "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+      "requires": {
+        "bytes": "3.1.0",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      }
+    },
+    "read-only-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
+      "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=",
+      "requires": {
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "read-pkg": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+      "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+      "requires": {
+        "load-json-file": "^4.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^3.0.0"
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.7",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      },
+      "dependencies": {
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
+    "readdirp": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+      "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+      "requires": {
+        "graceful-fs": "^4.1.11",
+        "micromatch": "^3.1.10",
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "regex-not": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "requires": {
+        "extend-shallow": "^3.0.2",
+        "safe-regex": "^1.1.0"
+      }
+    },
+    "reload-css": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/reload-css/-/reload-css-1.0.2.tgz",
+      "integrity": "sha1-avsRFi4jFP7M2tbcX96CH9cxgzE=",
+      "requires": {
+        "query-string": "^4.2.3"
+      }
+    },
+    "remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
+    },
+    "repeat-element": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g=="
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+    },
+    "resolve": {
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+      "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
+    },
+    "resp-modifier": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz",
+      "integrity": "sha1-sSTeXE+6/LpUH0j/pzlw9KpFa08=",
+      "requires": {
+        "debug": "^2.2.0",
+        "minimatch": "^3.0.2"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "ret": {
+      "version": "0.1.15",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
+    },
+    "right-now": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/right-now/-/right-now-1.0.0.tgz",
+      "integrity": "sha1-bolgne69fc2vja7Mmuo5z1haCRg="
+    },
+    "ripemd160": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+      "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+      "requires": {
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "safe-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "requires": {
+        "ret": "~0.1.10"
+      }
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+    },
+    "send": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+      "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+      "requires": {
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "destroy": "~1.0.4",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "~1.7.2",
+        "mime": "1.6.0",
+        "ms": "2.1.1",
+        "on-finished": "~2.3.0",
+        "range-parser": "~1.2.1",
+        "statuses": "~1.5.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          },
+          "dependencies": {
+            "ms": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+              "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+            }
+          }
+        },
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+        }
+      }
+    },
+    "serve-static": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+      "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+      "requires": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.17.1"
+      }
+    },
+    "set-value": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-extendable": "^0.1.1",
+        "is-plain-object": "^2.0.3",
+        "split-string": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "setprototypeof": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+      "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+    },
+    "sha.js": {
+      "version": "2.4.11",
+      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+      "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+      "requires": {
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "shasum": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz",
+      "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=",
+      "requires": {
+        "json-stable-stringify": "~0.0.0",
+        "sha.js": "~2.4.4"
+      }
+    },
+    "shasum-object": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz",
+      "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==",
+      "requires": {
+        "fast-safe-stringify": "^2.0.7"
+      }
+    },
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "requires": {
+        "shebang-regex": "^1.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+    },
+    "shell-quote": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
+      "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg=="
+    },
+    "signal-exit": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+      "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
+    },
+    "simple-concat": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
+      "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
+    },
+    "simple-html-index": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/simple-html-index/-/simple-html-index-1.5.0.tgz",
+      "integrity": "sha1-LJPurrrAAdihNfwAIr1K3o9YmW8=",
+      "requires": {
+        "from2-string": "^1.1.0"
+      }
+    },
+    "snapdragon": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+      "requires": {
+        "base": "^0.11.1",
+        "debug": "^2.2.0",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "map-cache": "^0.2.2",
+        "source-map": "^0.5.6",
+        "source-map-resolve": "^0.5.0",
+        "use": "^3.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
+    "snapdragon-node": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+      "requires": {
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.0",
+        "snapdragon-util": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "snapdragon-util": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+      "requires": {
+        "kind-of": "^3.2.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "source-map": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+    },
+    "source-map-resolve": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+      "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+      "requires": {
+        "atob": "^2.1.2",
+        "decode-uri-component": "^0.2.0",
+        "resolve-url": "^0.2.1",
+        "source-map-url": "^0.4.0",
+        "urix": "^0.1.0"
+      }
+    },
+    "source-map-url": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
+    },
+    "spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+      "requires": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
+    },
+    "split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "requires": {
+        "extend-shallow": "^3.0.0"
+      }
+    },
+    "split2": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-0.2.1.tgz",
+      "integrity": "sha1-At2smtwD7Au3jBKC7Aecpuha6QA=",
+      "requires": {
+        "through2": "~0.6.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        },
+        "readable-stream": {
+          "version": "1.0.34",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "0.0.1",
+            "string_decoder": "~0.10.x"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+        },
+        "through2": {
+          "version": "0.6.5",
+          "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
+          "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
+          "requires": {
+            "readable-stream": ">=1.0.33-1 <1.1.0-0",
+            "xtend": ">=4.0.0 <4.1.0-0"
+          }
+        }
+      }
+    },
+    "stacked": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/stacked/-/stacked-1.1.1.tgz",
+      "integrity": "sha1-LH+jjMfjejQRp3zY55LeRI+faXU="
+    },
+    "static-extend": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+      "requires": {
+        "define-property": "^0.2.5",
+        "object-copy": "^0.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+    },
+    "stdout-stream": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
+      "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
+      "requires": {
+        "readable-stream": "^2.0.1"
+      }
+    },
+    "stream-browserify": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+      "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+      "requires": {
+        "inherits": "~2.0.1",
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "stream-combiner2": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
+      "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=",
+      "requires": {
+        "duplexer2": "~0.1.0",
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "stream-http": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.1.tgz",
+      "integrity": "sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg==",
+      "requires": {
+        "builtin-status-codes": "^3.0.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.6.0",
+        "xtend": "^4.0.2"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.4",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+          "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+        },
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
+      }
+    },
+    "stream-splicer": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz",
+      "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==",
+      "requires": {
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "strict-uri-encode": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+      "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
+    },
+    "string.prototype.padend": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz",
+      "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==",
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1"
+      }
+    },
+    "string.prototype.trimend": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+      "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "string.prototype.trimleft": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
+      "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5",
+        "string.prototype.trimstart": "^1.0.0"
+      }
+    },
+    "string.prototype.trimright": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
+      "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5",
+        "string.prototype.trimend": "^1.0.0"
+      }
+    },
+    "string.prototype.trimstart": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+      "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "requires": {
+        "safe-buffer": "~5.2.0"
+      },
+      "dependencies": {
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+        }
+      }
+    },
+    "strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "requires": {
+        "ansi-regex": "^2.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+        }
+      }
+    },
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
+    },
+    "strip-eof": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
+    },
+    "subarg": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
+      "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=",
+      "requires": {
+        "minimist": "^1.1.0"
+      }
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "syntax-error": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz",
+      "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==",
+      "requires": {
+        "acorn-node": "^1.2.0"
+      }
+    },
+    "term-color": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/term-color/-/term-color-1.0.1.tgz",
+      "integrity": "sha1-OOGSVTpHPjXkFgT/UZmEa/gRejo=",
+      "requires": {
+        "ansi-styles": "2.0.1",
+        "supports-color": "1.3.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.0.1.tgz",
+          "integrity": "sha1-sDP1f5Pi0oreuLwRE4+hPaD9IKM="
+        },
+        "supports-color": {
+          "version": "1.3.1",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.3.1.tgz",
+          "integrity": "sha1-FXWN8J2P87SswwdTn6vicJXhBC0="
+        }
+      }
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+    },
+    "through2": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+      "requires": {
+        "readable-stream": "~2.3.6",
+        "xtend": "~4.0.1"
+      }
+    },
+    "timers-browserify": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
+      "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
+      "requires": {
+        "process": "~0.11.0"
+      }
+    },
+    "to-fast-properties": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
+    },
+    "to-object-path": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "to-regex": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+      "requires": {
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "regex-not": "^1.0.2",
+        "safe-regex": "^1.1.0"
+      }
+    },
+    "to-regex-range": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+      "requires": {
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1"
+      }
+    },
+    "toidentifier": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+      "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+    },
+    "tty-browserify": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",
+      "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw=="
+    },
+    "type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "requires": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      }
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+    },
+    "uid-safe": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+      "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+      "requires": {
+        "random-bytes": "~1.0.0"
+      }
+    },
+    "umd": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz",
+      "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow=="
+    },
+    "undeclared-identifiers": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz",
+      "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==",
+      "requires": {
+        "acorn-node": "^1.3.0",
+        "dash-ast": "^1.0.0",
+        "get-assigned-identifiers": "^1.2.0",
+        "simple-concat": "^1.0.0",
+        "xtend": "^4.0.1"
+      }
+    },
+    "union-value": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+      "requires": {
+        "arr-union": "^3.1.0",
+        "get-value": "^2.0.6",
+        "is-extendable": "^0.1.1",
+        "set-value": "^2.0.1"
+      }
+    },
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+    },
+    "unset-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+      "requires": {
+        "has-value": "^0.3.1",
+        "isobject": "^3.0.0"
+      },
+      "dependencies": {
+        "has-value": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+          "requires": {
+            "get-value": "^2.0.3",
+            "has-values": "^0.1.4",
+            "isobject": "^2.0.0"
+          },
+          "dependencies": {
+            "isobject": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+              "requires": {
+                "isarray": "1.0.0"
+              }
+            }
+          }
+        },
+        "has-values": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E="
+        }
+      }
+    },
+    "upath": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+      "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="
+    },
+    "urix": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
+    },
+    "url": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+      "requires": {
+        "punycode": "1.3.2",
+        "querystring": "0.2.0"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+        }
+      }
+    },
+    "url-trim": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/url-trim/-/url-trim-1.0.0.tgz",
+      "integrity": "sha1-QAV+LxZLiOXaynJp2kfm0d2Detw="
+    },
+    "use": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
+    },
+    "util": {
+      "version": "0.10.4",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+      "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+      "requires": {
+        "inherits": "2.0.3"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+    },
+    "vm-browserify": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+      "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
+    },
+    "watchify": {
+      "version": "3.11.1",
+      "resolved": "https://registry.npmjs.org/watchify/-/watchify-3.11.1.tgz",
+      "integrity": "sha512-WwnUClyFNRMB2NIiHgJU9RQPQNqVeFk7OmZaWf5dC5EnNa0Mgr7imBydbaJ7tGTuPM2hz1Cb4uiBvK9NVxMfog==",
+      "requires": {
+        "anymatch": "^2.0.0",
+        "browserify": "^16.1.0",
+        "chokidar": "^2.1.1",
+        "defined": "^1.0.0",
+        "outpipe": "^1.1.0",
+        "through2": "^2.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "watchify-middleware": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/watchify-middleware/-/watchify-middleware-1.8.2.tgz",
+      "integrity": "sha512-A+x5K0mHVEK2WSLOEbazcXDFnSlralMZzk364Ea39F4xFl2jGl4VQLLN5HwrnRzpF5/Ggf1Q2he0HpJtflUiHg==",
+      "requires": {
+        "concat-stream": "^1.5.0",
+        "debounce": "^1.0.0",
+        "events": "^1.0.2",
+        "object-assign": "^4.0.1",
+        "strip-ansi": "^3.0.0",
+        "watchify": "^3.11.1"
+      }
+    },
+    "which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "ws": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
+      "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
+      "requires": {
+        "async-limiter": "~1.0.0"
+      }
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+    }
+  }
+}

+ 85 - 21
examples/custom-provider/server/customprovider.js

@@ -1,41 +1,105 @@
-const fs = require('fs')
-const path = require('path')
-const DUMM_FILE = path.join(__dirname, 'fixtures/image.jpg')
+const request = require('request')
+const BASE_URL = 'https://api.unsplash.com'
 
 /**
  * an example of a custom provider module. It implements @uppy/companion's Provider interface
  */
 class MyCustomProvider {
   constructor (options) {
-    this.authProvider = MyCustomProvider.authProvider
+    this.authProvider = 'myunsplash'
   }
 
-  static get authProvider () {
-    return 'mycustomprovider'
-  }
-
-  list (options, done) {
-    const response = {
-      body: {
-        entries: [
-          { name: 'file1.jpg' },
-          { name: 'file2.jpg' },
-          { name: 'file3.jpg' }
-        ]
+  list ({ token, directory }, done) {
+    const path = directory ? `/${directory}/photos` : ''
+    const options = {
+      url: `${BASE_URL}/collections${path}`,
+      method: 'GET',
+      json: true,
+      headers: {
+        Authorization: `Bearer ${token}`
       }
     }
-    return done(null, response, response.body)
+
+    request(options, (err, resp, body) => {
+      if (err) {
+        console.log(err)
+        done(err)
+        return
+      }
+
+      done(null, this._adaptData(body))
+    })
   }
 
   download ({ id, token }, onData) {
-    return fs.readFile(DUMM_FILE, (err, data) => {
-      if (err) console.error(err)
-      onData(data)
+    const options = {
+      url: `${BASE_URL}/photos/${id}`,
+      method: 'GET',
+      json: true,
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    }
+
+    request(options, (err, resp, body) => {
+      if (err) {
+        console.log(err)
+        return
+      }
+
+      const url = body.links.download
+      request.get(url)
+        .on('data', (chunk) => onData(null, chunk))
+        .on('end', () => onData(null, null))
+        .on('error', (err) => console.log(err))
     })
   }
 
   size ({ id, token }, done) {
-    return done(fs.statSync(DUMM_FILE).size)
+    const options = {
+      url: `${BASE_URL}/photos/${id}`,
+      method: 'GET',
+      json: true,
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    }
+
+    request(options, (err, resp, body) => {
+      if (err) {
+        console.log(err)
+        done(err)
+        return
+      }
+
+      done(null, body.width * body.height)
+    })
+  }
+
+  _adaptData (res) {
+    const data = {
+      username: null,
+      items: [],
+      nextPagePath: null
+    }
+
+    const items = res
+    items.forEach((item) => {
+      const isFolder = !!item.published_at
+      data.items.push({
+        isFolder: isFolder,
+        icon: isFolder ? item.cover_photo.urls.thumb : item.urls.thumb,
+        name: item.title || item.description,
+        mimeType: isFolder ? null : 'image/jpeg',
+        id: item.id,
+        thumbnail: isFolder ? item.cover_photo.urls.thumb : item.urls.thumb,
+        requestPath: item.id,
+        modifiedDate: item.updated_at,
+        size: null
+      })
+    })
+
+    return data
   }
 }
 

binární
examples/custom-provider/server/fixtures/image.jpg


+ 11 - 12
examples/custom-provider/server/index.js

@@ -33,6 +33,10 @@ app.get('/', (req, res) => {
   res.send('Welcome to my uppy companion service')
 })
 
+// source https://unsplash.com/documentation#user-authentication
+const AUTHORIZE_URL = 'https://unsplash.com/oauth/authorize'
+const ACCESS_URL = 'https://unsplash.com/oauth/token'
+
 // initialize uppy
 const uppyOptions = {
   providerOptions: {
@@ -42,15 +46,16 @@ const uppyOptions = {
     }
   },
   customProviders: {
-    mycustomprovider: {
+    myunsplash: {
       config: {
         // your oauth handlers
-        authorize_url: 'http://localhost:3020/oauth/authorize',
-        access_url: 'http://localhost:3020/oauth/token',
+        authorize_url: AUTHORIZE_URL,
+        access_url: ACCESS_URL,
         oauth: 2,
-        key: '***',
-        secret: '**',
-        scope: ['read', 'write']
+        key: 'your unsplash key here',
+        secret: 'your unsplash secret here',
+        callback: '/myunsplash/callback',
+        transport: 'session'
       },
       // you provider module
       module: require('./customprovider')
@@ -65,12 +70,6 @@ const uppyOptions = {
   debug: true
 }
 
-app.get('/oauth/authorize', (req, res) => {
-  // skips the default oauth process.
-  // ideally this endpoint should handle the actual oauth process
-  res.redirect(`http://localhost:3020/mycustomprovider/callback?state=${req.query.state}&access_token=randombytes`)
-})
-
 app.use(uppy.app(uppyOptions))
 
 // handle 404

+ 5 - 0
examples/dev/Dashboard.js

@@ -11,6 +11,7 @@ const ScreenCapture = require('@uppy/screen-capture/src')
 const GoldenRetriever = require('@uppy/golden-retriever/src')
 const Tus = require('@uppy/tus/src')
 const AwsS3 = require('@uppy/aws-s3/src')
+const AwsS3Multipart = require('@uppy/aws-s3-multipart/src')
 const XHRUpload = require('@uppy/xhr-upload/src')
 const Transloadit = require('@uppy/transloadit/src')
 const Form = require('@uppy/form/src')
@@ -19,6 +20,7 @@ const Form = require('@uppy/form/src')
 
 const UPLOADER = 'tus'
 // const UPLOADER = 's3'
+// const UPLOADER = 's3-multipart'
 // const UPLOADER = 'xhr'
 // const UPLOADER = 'transloadit'
 
@@ -76,6 +78,9 @@ module.exports = () => {
     case 's3':
       uppyDashboard.use(AwsS3, { companionUrl: COMPANION_URL, limit: 6 })
       break
+    case 's3-multipart':
+      uppyDashboard.use(AwsS3Multipart, { companionUrl: COMPANION_URL, limit: 6 })
+      break
     case 'xhr':
       uppyDashboard.use(XHRUpload, { endpoint: XHR_ENDPOINT, limit: 6, bundle: true })
       break

+ 1 - 1
examples/transloadit-textarea/index.html

@@ -2,7 +2,7 @@
 <html>
   <head>
     <meta charset="utf-8">
-    <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.1/robodog.css">
+    <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.2/robodog.css">
     <style>
       body {
         font-family: Roboto, Open Sans;

+ 2 - 2
examples/uppy-with-companion/client/index.html

@@ -4,11 +4,11 @@
     <title></title>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link href="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.css" rel="stylesheet">
+    <link href="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.css" rel="stylesheet">
   </head>
   <body>
     <button id="uppyModalOpener">Open Modal</button>
-    <script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
+    <script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
     <script>
       const uppy = Uppy.Core({debug: true, autoProceed: false})
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })

+ 12 - 6
package-lock.json

@@ -7793,6 +7793,7 @@
     "@uppy/utils": {
       "version": "file:packages/@uppy/utils",
       "requires": {
+        "abortcontroller-polyfill": "^1.4.0",
         "lodash.throttle": "^4.1.1"
       }
     },
@@ -8980,6 +8981,11 @@
         }
       }
     },
+    "abortcontroller-polyfill": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.4.0.tgz",
+      "integrity": "sha512-3ZFfCRfDzx3GFjO6RAkYx81lPGpUS20ISxux9gLxuKnqafNcFQo59+IoZqpO2WvQlyc287B62HDnDdNYRmlvWA=="
+    },
     "absolute-path": {
       "version": "0.0.0",
       "resolved": "https://registry.npmjs.org/absolute-path/-/absolute-path-0.0.0.tgz",
@@ -28289,9 +28295,9 @@
       }
     },
     "npm-registry-fetch": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.3.tgz",
-      "integrity": "sha512-WGvUx0lkKFhu9MbiGFuT9nG2NpfQ+4dCJwRwwtK2HK5izJEvwDxMeUyqbuMS7N/OkpVCqDorV6rO5E4V9F8lJw==",
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.5.tgz",
+      "integrity": "sha512-yQ0/U4fYpCCqmueB2g8sc+89ckQ3eXpmU4+Yi2j5o/r0WkKvE2+Y0tK3DEILAtn2UaQTkjTHxIXe2/CSdit+/Q==",
       "dev": true,
       "requires": {
         "JSONStream": "^1.3.4",
@@ -28313,9 +28319,9 @@
           }
         },
         "safe-buffer": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
-          "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
           "dev": true
         },
         "yallist": {

+ 1 - 1
packages/@uppy/aws-s3-multipart/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/aws-s3-multipart",
   "description": "Upload to Amazon S3 with Uppy and S3's Multipart upload strategy",
-  "version": "1.7.1",
+  "version": "1.8.0",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 117 - 26
packages/@uppy/aws-s3-multipart/src/MultipartUploader.js

@@ -1,7 +1,11 @@
+const { AbortController, createAbortError } = require('@uppy/utils/lib/AbortController')
+const delay = require('@uppy/utils/lib/delay')
+
 const MB = 1024 * 1024
 
 const defaultOptions = {
   limit: 1,
+  retryDelays: [0, 1000, 3000, 5000],
   getChunkSize (file) {
     return Math.ceil(file.size / 10000)
   },
@@ -14,11 +18,6 @@ const defaultOptions = {
   }
 }
 
-function remove (arr, el) {
-  const i = arr.indexOf(el)
-  if (i !== -1) arr.splice(i, 1)
-}
-
 class MultipartUploader {
   constructor (file, options) {
     this.options = {
@@ -31,6 +30,7 @@ class MultipartUploader {
     }
 
     this.file = file
+    this.abortController = new AbortController()
 
     this.key = this.options.key || null
     this.uploadId = this.options.uploadId || null
@@ -45,15 +45,26 @@ class MultipartUploader {
     // aborting it immediately after it finishes.
     this.createdPromise = Promise.reject() // eslint-disable-line prefer-promise-reject-errors
     this.isPaused = false
+    this.partsInProgress = 0
     this.chunks = null
     this.chunkState = null
-    this.uploading = []
 
     this._initChunks()
 
     this.createdPromise.catch(() => {}) // silence uncaught rejection warning
   }
 
+  /**
+   * Was this upload aborted?
+   *
+   * If yes, we may need to throw an AbortError.
+   *
+   * @returns {boolean}
+   */
+  _aborted () {
+    return this.abortController.signal.aborted
+  }
+
   _initChunks () {
     const chunks = []
     const desiredChunkSize = this.options.getChunkSize(this.file)
@@ -79,6 +90,8 @@ class MultipartUploader {
       this.options.createMultipartUpload()
     )
     return this.createdPromise.then((result) => {
+      if (this._aborted()) throw createAbortError()
+
       const valid = typeof result === 'object' && result &&
         typeof result.uploadId === 'string' &&
         typeof result.key === 'string'
@@ -103,6 +116,8 @@ class MultipartUploader {
         key: this.key
       })
     ).then((parts) => {
+      if (this._aborted()) throw createAbortError()
+
       parts.forEach((part) => {
         const i = part.PartNumber - 1
         this.chunkState[i] = {
@@ -128,7 +143,7 @@ class MultipartUploader {
   _uploadParts () {
     if (this.isPaused) return
 
-    const need = this.options.limit - this.uploading.length
+    const need = this.options.limit - this.partsInProgress
     if (need === 0) return
 
     // All parts are uploaded.
@@ -149,7 +164,57 @@ class MultipartUploader {
     }
 
     candidates.forEach((index) => {
-      this._uploadPart(index)
+      this._uploadPartRetryable(index).catch((err) => {
+        this._onError(err)
+      })
+    })
+  }
+
+  _retryable ({ before, attempt, after }) {
+    const { retryDelays } = this.options
+    const { signal } = this.abortController
+
+    if (before) before()
+
+    function shouldRetry (err) {
+      if (err.source && typeof err.source.status === 'number') {
+        const { status } = err.source
+        // 0 probably indicates network failure
+        return status === 0 || status === 409 || status === 423 || (status >= 500 && status < 600)
+      }
+      return false
+    }
+
+    const doAttempt = (retryAttempt) =>
+      attempt().catch((err) => {
+        if (this._aborted()) throw createAbortError()
+
+        if (shouldRetry(err) && retryAttempt < retryDelays.length) {
+          return delay(retryDelays[retryAttempt], { signal })
+            .then(() => doAttempt(retryAttempt + 1))
+        } else {
+          throw err
+        }
+      })
+
+    return doAttempt(0).then((result) => {
+      if (after) after()
+      return result
+    }, (err) => {
+      if (after) after()
+      throw err
+    })
+  }
+
+  _uploadPartRetryable (index) {
+    return this._retryable({
+      before: () => {
+        this.partsInProgress += 1
+      },
+      attempt: () => this._uploadPart(index),
+      after: () => {
+        this.partsInProgress -= 1
+      }
     })
   }
 
@@ -170,11 +235,15 @@ class MultipartUploader {
       if (!valid) {
         throw new TypeError('AwsS3/Multipart: Got incorrect result from `prepareUploadPart()`, expected an object `{ url }`.')
       }
+
       return result
     }).then(({ url, headers }) => {
-      this._uploadPartBytes(index, url, headers)
-    }, (err) => {
-      this._onError(err)
+      if (this._aborted()) {
+        this.chunkState[index].busy = false
+        throw createAbortError()
+      }
+
+      return this._uploadPartBytes(index, url, headers)
     })
   }
 
@@ -202,6 +271,13 @@ class MultipartUploader {
 
   _uploadPartBytes (index, url, headers) {
     const body = this.chunks[index]
+    const { signal } = this.abortController
+
+    let defer
+    const promise = new Promise((resolve, reject) => {
+      defer = { resolve, reject }
+    })
+
     const xhr = new XMLHttpRequest()
     xhr.open('PUT', url, true)
     if (headers) {
@@ -211,7 +287,13 @@ class MultipartUploader {
     }
     xhr.responseType = 'text'
 
-    this.uploading.push(xhr)
+    function cleanup () {
+      signal.removeEventListener('abort', onabort)
+    }
+    function onabort () {
+      xhr.abort()
+    }
+    signal.addEventListener('abort', onabort)
 
     xhr.upload.addEventListener('progress', (ev) => {
       if (!ev.lengthComputable) return
@@ -220,16 +302,20 @@ class MultipartUploader {
     })
 
     xhr.addEventListener('abort', (ev) => {
-      remove(this.uploading, ev.target)
+      cleanup()
       this.chunkState[index].busy = false
+
+      defer.reject(createAbortError())
     })
 
     xhr.addEventListener('load', (ev) => {
-      remove(this.uploading, ev.target)
+      cleanup()
       this.chunkState[index].busy = false
 
       if (ev.target.status < 200 || ev.target.status >= 300) {
-        this._onError(new Error('Non 2xx'))
+        const error = new Error('Non 2xx')
+        error.source = ev.target
+        defer.reject(error)
         return
       }
 
@@ -238,23 +324,26 @@ class MultipartUploader {
       // NOTE This must be allowed by CORS.
       const etag = ev.target.getResponseHeader('ETag')
       if (etag === null) {
-        this._onError(new Error('AwsS3/Multipart: Could not read the ETag header. This likely means CORS is not configured correctly on the S3 Bucket. Seee https://uppy.io/docs/aws-s3-multipart#S3-Bucket-Configuration for instructions.'))
+        defer.reject(new Error('AwsS3/Multipart: Could not read the ETag header. This likely means CORS is not configured correctly on the S3 Bucket. Seee https://uppy.io/docs/aws-s3-multipart#S3-Bucket-Configuration for instructions.'))
         return
       }
 
       this._onPartComplete(index, etag)
+      defer.resolve()
     })
 
     xhr.addEventListener('error', (ev) => {
-      remove(this.uploading, ev.target)
+      cleanup()
       this.chunkState[index].busy = false
 
       const error = new Error('Unknown error')
       error.source = ev.target
-      this._onError(error)
+      defer.reject(error)
     })
 
     xhr.send(body)
+
+    return promise
   }
 
   _completeUpload () {
@@ -275,9 +364,8 @@ class MultipartUploader {
   }
 
   _abortUpload () {
-    this.uploading.slice().forEach(xhr => {
-      xhr.abort()
-    })
+    this.abortController.abort()
+
     this.createdPromise.then(() => {
       this.options.abortMultipartUpload({
         key: this.key,
@@ -286,10 +374,13 @@ class MultipartUploader {
     }, () => {
       // if the creation failed we do not need to abort
     })
-    this.uploading = []
   }
 
   _onError (err) {
+    if (err && err.name === 'AbortError') {
+      return
+    }
+
     this.options.onError(err)
   }
 
@@ -303,10 +394,10 @@ class MultipartUploader {
   }
 
   pause () {
-    const inProgress = this.uploading.slice()
-    inProgress.forEach((xhr) => {
-      xhr.abort()
-    })
+    this.abortController.abort()
+    // Swap it out for a new controller, because this instance may be resumed later.
+    this.abortController = new AbortController()
+
     this.isPaused = true
   }
 

+ 9 - 7
packages/@uppy/aws-s3-multipart/src/index.js

@@ -28,6 +28,7 @@ module.exports = class AwsS3Multipart extends Plugin {
     const defaultOptions = {
       timeout: 30 * 1000,
       limit: 0,
+      retryDelays: [0, 1000, 3000, 5000],
       createMultipartUpload: this.createMultipartUpload.bind(this),
       listParts: this.listParts.bind(this),
       prepareUploadPart: this.prepareUploadPart.bind(this),
@@ -68,14 +69,14 @@ module.exports = class AwsS3Multipart extends Plugin {
     }
   }
 
-  assertHost () {
+  assertHost (method) {
     if (!this.opts.companionUrl) {
-      throw new Error('Expected a `companionUrl` option containing a Companion address.')
+      throw new Error(`Expected a \`companionUrl\` option containing a Companion address, or if you are not using Companion, a custom \`${method}\` implementation.`)
     }
   }
 
   createMultipartUpload (file) {
-    this.assertHost()
+    this.assertHost('createMultipartUpload')
 
     const metadata = {}
 
@@ -93,7 +94,7 @@ module.exports = class AwsS3Multipart extends Plugin {
   }
 
   listParts (file, { key, uploadId }) {
-    this.assertHost()
+    this.assertHost('listParts')
 
     const filename = encodeURIComponent(key)
     return this.client.get(`s3/multipart/${uploadId}?key=${filename}`)
@@ -101,7 +102,7 @@ module.exports = class AwsS3Multipart extends Plugin {
   }
 
   prepareUploadPart (file, { key, uploadId, number }) {
-    this.assertHost()
+    this.assertHost('prepareUploadPart')
 
     const filename = encodeURIComponent(key)
     return this.client.get(`s3/multipart/${uploadId}/${number}?key=${filename}`)
@@ -109,7 +110,7 @@ module.exports = class AwsS3Multipart extends Plugin {
   }
 
   completeMultipartUpload (file, { key, uploadId, parts }) {
-    this.assertHost()
+    this.assertHost('completeMultipartUpload')
 
     const filename = encodeURIComponent(key)
     const uploadIdEnc = encodeURIComponent(uploadId)
@@ -118,7 +119,7 @@ module.exports = class AwsS3Multipart extends Plugin {
   }
 
   abortMultipartUpload (file, { key, uploadId }) {
-    this.assertHost()
+    this.assertHost('abortMultipartUpload')
 
     const filename = encodeURIComponent(key)
     const uploadIdEnc = encodeURIComponent(uploadId)
@@ -198,6 +199,7 @@ module.exports = class AwsS3Multipart extends Plugin {
         onPartComplete,
 
         limit: this.opts.limit || 5,
+        retryDelays: this.opts.retryDelays || [],
         ...file.s3Multipart
       })
 

+ 1 - 0
packages/@uppy/aws-s3-multipart/types/index.d.ts

@@ -33,6 +33,7 @@ declare module AwsS3Multipart {
     ) => MaybePromise<{ location?: string }>
     timeout?: number
     limit?: number
+    retryDelays?: number[] | null
   }
 }
 

+ 1 - 1
packages/@uppy/aws-s3/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/aws-s3",
   "description": "Upload to Amazon S3 with Uppy",
-  "version": "1.6.6",
+  "version": "1.6.7",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 1
packages/@uppy/companion-client/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/companion-client",
   "description": "Client library for communication with Companion. Intended for use in Uppy plugins.",
-  "version": "1.5.0",
+  "version": "1.5.1",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 0 - 1
packages/@uppy/companion-client/src/Provider.js

@@ -12,7 +12,6 @@ module.exports = class Provider extends RequestClient {
     super(uppy, opts)
     this.provider = opts.provider
     this.id = this.provider
-    this.authProvider = opts.authProvider || this.provider
     this.name = this.opts.name || _getName(this.id)
     this.pluginId = this.opts.pluginId
     this.tokenKey = `companion-${this.pluginId}-auth-token`

+ 0 - 1
packages/@uppy/companion-client/types/index.d.ts

@@ -37,7 +37,6 @@ export interface PublicProviderOptions extends RequestClientOptions {
  */
 export interface ProviderOptions extends PublicProviderOptions {
   provider: string
-  authProvider?: string
   name?: string
   pluginId: string
 }

+ 8 - 4
packages/@uppy/companion/infra/kube/gcloud-deploy.sh

@@ -20,12 +20,16 @@ mv ./kubectl ${HOME}/.local/bin/
 docker build -t transloadit/companion:latest -t transloadit/companion:$TRAVIS_COMMIT -f packages/@uppy/companion/Dockerfile packages/@uppy/companion;
 docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD";
 
-if [[ -z "${TRAVIS_TAG}" ]]; then
-  docker push transloadit/companion:$TRAVIS_COMMIT;
-else
+# Push the commit tagged docker image.
+docker push transloadit/companion:$TRAVIS_COMMIT;
+
+# If this build includes a git tag, tag the image with the git version tag and push the version.
+if [[ ! -z "${TRAVIS_TAG}" ]]; then
+  docker tag transloadit/companion:$TRAVIS_COMMIT transloadit/companion:$TRAVIS_TAG;
   docker push transloadit/companion:$TRAVIS_TAG;
 fi
 
+# Lastly, update the pointer to latest.
 docker push transloadit/companion:latest;
 
 
@@ -49,4 +53,4 @@ function cleanup {
     printf "Cleaning done."
 }
 
-trap cleanup EXIT
+trap cleanup EXIT

+ 1 - 1
packages/@uppy/companion/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@uppy/companion",
-  "version": "2.0.0-alpha.6",
+  "version": "2.0.0-alpha.7",
   "description": "OAuth helper and remote fetcher for Uppy's (https://uppy.io) extensible file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Dropbox and Google Drive, S3 and more :dog:",
   "main": "lib/companion.js",
   "types": "types/index.d.ts",

+ 10 - 0
packages/@uppy/companion/src/companion.js

@@ -335,4 +335,14 @@ const validateConfig = (companionOptions) => {
       `No access to "${companionOptions.filePath}". Please ensure the directory exists and with read/write permissions.`
     )
   }
+
+  const { providerOptions } = companionOptions
+  if (providerOptions) {
+    const deprecatedOptions = { microsoft: 'onedrive', google: 'drive' }
+    Object.keys(deprecatedOptions).forEach((deprected) => {
+      if (providerOptions[deprected]) {
+        throw new Error(`The Provider option "${deprected}" is no longer supported. Please use the option "${deprecatedOptions[deprected]}" instead.`)
+      }
+    })
+  }
 }

+ 2 - 0
packages/@uppy/companion/src/config/grant.js

@@ -1,6 +1,7 @@
 // oauth configuration for provider services that are used.
 module.exports = () => {
   return {
+    // for drive
     google: {
       transport: 'session',
       scope: [
@@ -23,6 +24,7 @@ module.exports = () => {
       scope: ['email', 'user_photos'],
       callback: '/facebook/callback'
     },
+    // for onedrive
     microsoft: {
       transport: 'session',
       scope: ['files.read.all', 'offline_access', 'User.Read'],

+ 1 - 0
packages/@uppy/companion/src/server/Uploader.js

@@ -420,6 +420,7 @@ class Uploader {
       uploadLengthDeferred: false,
       retryDelays: [0, 1000, 3000, 5000],
       uploadSize: this.bytesWritten,
+      headers: headerSanitize(this.options.headers),
       addRequestId: true,
       metadata: Object.assign(
         {

+ 4 - 3
packages/@uppy/companion/src/server/controllers/callback.js

@@ -17,14 +17,15 @@ module.exports = function callback (req, res, next) {
     req.companion.providerTokens = {}
   }
 
-  if (req.session.grant.response.access_token) {
-    req.companion.providerTokens[providerName] = req.session.grant.response.access_token
+  const grant = req.session.grant || {}
+  if (grant.response && grant.response.access_token) {
+    req.companion.providerTokens[providerName] = grant.response.access_token
     logger.debug(`Generating auth token for provider ${providerName}`, null, req.id)
     const uppyAuthToken = tokenService.generateToken(req.companion.providerTokens, req.companion.options.secret)
     return res.redirect(req.companion.buildURL(`/${providerName}/send-token?uppyAuthToken=${uppyAuthToken}`, true))
   }
 
   logger.debug(`Did not receive access token for provider ${providerName}`, null, req.id)
-  logger.debug(req.session.grant.response, 'callback.oauth.resp', req.id)
+  logger.debug(grant.response, 'callback.oauth.resp', req.id)
   return res.sendStatus(400)
 }

+ 10 - 16
packages/@uppy/companion/src/server/provider/index.js

@@ -100,17 +100,17 @@ module.exports.addProviderOptions = (companionOptions, grantConfig) => {
 
   const { oauthDomain } = server
   const keys = Object.keys(providerOptions).filter((key) => key !== 'server')
-  keys.forEach((authProvider) => {
-    if (grantConfig[authProvider]) {
+  keys.forEach((providerName) => {
+    const authProvider = providerNameToAuthName(providerName, companionOptions)
+    if (authProvider && grantConfig[authProvider]) {
       // explicitly add providerOptions so users don't override other providerOptions.
-      grantConfig[authProvider].key = providerOptions[authProvider].key
-      grantConfig[authProvider].secret = providerOptions[authProvider].secret
-      const { provider, name } = authNameToProvider(authProvider, companionOptions)
+      grantConfig[authProvider].key = providerOptions[providerName].key
+      grantConfig[authProvider].secret = providerOptions[providerName].secret
+      const provider = exports.getDefaultProviders(companionOptions)[providerName]
       Object.assign(grantConfig[authProvider], provider.getExtraConfig())
 
       // override grant.js redirect uri with companion's custom redirect url
       if (oauthDomain) {
-        const providerName = name
         const redirectPath = `/${providerName}/redirect`
         const isExternal = !!server.implicitPath
         const fullRedirectPath = getURLBuilder(companionOptions)(redirectPath, isExternal, true)
@@ -131,19 +131,13 @@ module.exports.addProviderOptions = (companionOptions, grantConfig) => {
 
 /**
  *
- * @param {string} authProvider
+ * @param {string} name of the provider
  * @param {{server: object, providerOptions: object}} options
- * @return {{name: string, provider: typeof Provider}}
+ * @return {string} the authProvider for this provider
  */
-const authNameToProvider = (authProvider, options) => {
+const providerNameToAuthName = (name, options) => {
   const providers = exports.getDefaultProviders(options)
-  const providerNames = Object.keys(providers)
-  for (const name of providerNames) {
-    const provider = providers[name]
-    if (provider.authProvider === authProvider) {
-      return { name, provider }
-    }
-  }
+  return (providers[name] || {}).authProvider
 }
 
 /**

+ 2 - 2
packages/@uppy/companion/src/standalone/helper.js

@@ -29,7 +29,7 @@ const getConfigFromEnv = () => {
 
   return {
     providerOptions: {
-      google: {
+      drive: {
         key: process.env.COMPANION_GOOGLE_KEY,
         secret: getSecret('COMPANION_GOOGLE_SECRET')
       },
@@ -45,7 +45,7 @@ const getConfigFromEnv = () => {
         key: process.env.COMPANION_FACEBOOK_KEY,
         secret: getSecret('COMPANION_FACEBOOK_SECRET')
       },
-      microsoft: {
+      onedrive: {
         key: process.env.COMPANION_ONEDRIVE_KEY,
         secret: getSecret('COMPANION_ONEDRIVE_SECRET')
       },

+ 1 - 1
packages/@uppy/companion/src/standalone/index.js

@@ -182,7 +182,7 @@ if (process.env.COMPANION_PATH) {
 // Only set COMPANION_ONEDRIVE_DOMAIN_VALIDATION if you are sure that you are setting the
 // correct value for COMPANION_ONEDRIVE_KEY (i.e application ID). If there's a slightest possiblilty
 // that you might have mixed the values for COMPANION_ONEDRIVE_KEY and COMPANION_ONEDRIVE_SECRET,
-// please do not set a value for COMPANION_ONEDRIVE_DOMAIN_VALIDATION
+// please DO NOT set any value for COMPANION_ONEDRIVE_DOMAIN_VALIDATION
 if (process.env.COMPANION_ONEDRIVE_DOMAIN_VALIDATION === 'true' && process.env.COMPANION_ONEDRIVE_KEY) {
   app.get('/.well-known/microsoft-identity-association.json', (req, res) => {
     const content = JSON.stringify({

+ 1 - 1
packages/@uppy/core/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/core",
   "description": "Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
-  "version": "1.11.0",
+  "version": "1.12.0",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 7 - 0
packages/@uppy/core/src/index.js

@@ -853,6 +853,13 @@ class Uppy {
 
     this.emit('retry-all', filesToRetry)
 
+    if (filesToRetry.length === 0) {
+      return Promise.resolve({
+        successful: [],
+        failed: []
+      })
+    }
+
     const uploadID = this._createUpload(filesToRetry, {
       forceAllowNewUpload: true // create new upload even if allowNewUpload: false
     })

+ 42 - 0
packages/@uppy/core/src/index.test.js

@@ -983,6 +983,48 @@ describe('src/Core', () => {
     })
   })
 
+  describe('retries', () => {
+    it('should start a new upload with failed files', async () => {
+      const onUpload = jest.fn()
+      const onRetryAll = jest.fn()
+
+      const core = new Core()
+      core.on('upload', onUpload)
+      core.on('retry-all', onRetryAll)
+
+      const id = core.addFile({
+        source: 'jest',
+        name: 'foo.jpg',
+        type: 'image/jpeg',
+        data: new File([sampleImage], { type: 'image/jpeg' })
+      })
+      core.setFileState(id, {
+        error: 'something went wrong'
+      })
+
+      await core.retryAll()
+      expect(onRetryAll).toHaveBeenCalled()
+      expect(onUpload).toHaveBeenCalled()
+    })
+
+    it('should not start a new upload if there are no failed files', async () => {
+      const onUpload = jest.fn()
+
+      const core = new Core()
+      core.on('upload', onUpload)
+
+      core.addFile({
+        source: 'jest',
+        name: 'foo.jpg',
+        type: 'image/jpeg',
+        data: new File([sampleImage], { type: 'image/jpeg' })
+      })
+
+      await core.retryAll()
+      expect(onUpload).not.toHaveBeenCalled()
+    })
+  })
+
   describe('restoring a file', () => {
     xit('should restore a file', () => {})
 

+ 1 - 1
packages/@uppy/dashboard/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/dashboard",
   "description": "Universal UI plugin for Uppy.",
-  "version": "1.10.1",
+  "version": "1.10.2",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 1 - 1
packages/@uppy/drag-drop/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/drag-drop",
   "description": "Droppable zone UI for Uppy. Drag and drop files into it to upload.",
-  "version": "1.4.15",
+  "version": "1.4.16",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 1 - 1
packages/@uppy/dropbox/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/dropbox",
   "description": "Import files from Dropbox, into Uppy.",
-  "version": "1.4.8",
+  "version": "1.4.9",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 1
packages/@uppy/facebook/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/facebook",
   "description": "Import files from Facebook, into Uppy.",
-  "version": "1.1.8",
+  "version": "1.1.9",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 1
packages/@uppy/file-input/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/file-input",
   "description": "Simple UI of a file input button that works with Uppy right out of the box",
-  "version": "1.4.13",
+  "version": "1.4.14",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 1 - 1
packages/@uppy/form/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/form",
   "description": "Connect Uppy to an existing HTML <form>.",
-  "version": "1.3.16",
+  "version": "1.3.17",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 1
packages/@uppy/golden-retriever/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/golden-retriever",
   "description": "The GoldenRetriever Uppy plugin saves selected files in browser cache to seamlessly resume uploding after browser crash or accidentally closed tab",
-  "version": "1.3.15",
+  "version": "1.3.16",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 1
packages/@uppy/google-drive/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/google-drive",
   "description": "The Google Drive plugin for Uppy lets users import files from their Google Drive account",
-  "version": "1.5.8",
+  "version": "1.5.9",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 0 - 1
packages/@uppy/google-drive/src/index.js

@@ -25,7 +25,6 @@ module.exports = class GoogleDrive extends Plugin {
       companionUrl: this.opts.companionUrl,
       companionHeaders: this.opts.companionHeaders || this.opts.serverHeaders,
       provider: 'drive',
-      authProvider: 'google',
       pluginId: this.id
     })
 

+ 1 - 1
packages/@uppy/informer/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/informer",
   "description": "A notification and error pop-up bar for Uppy.",
-  "version": "1.5.7",
+  "version": "1.5.8",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 1 - 1
packages/@uppy/instagram/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/instagram",
   "description": "Import photos and videos from Instagram, into Uppy.",
-  "version": "1.4.8",
+  "version": "1.4.9",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 0 - 1
packages/@uppy/instagram/src/index.js

@@ -24,7 +24,6 @@ module.exports = class Instagram extends Plugin {
       companionUrl: this.opts.companionUrl,
       companionHeaders: this.opts.companionHeaders || this.opts.serverHeaders,
       provider: 'instagram',
-      authProvider: 'instagram',
       pluginId: this.id
     })
 

+ 1 - 1
packages/@uppy/locales/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/locales",
   "description": "Uppy language packs",
-  "version": "1.15.0",
+  "version": "1.15.1",
   "license": "MIT",
   "keywords": [
     "uppy",

+ 1 - 1
packages/@uppy/onedrive/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/onedrive",
   "description": "Import files from OneDrive, into Uppy.",
-  "version": "1.1.8",
+  "version": "1.1.9",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 1
packages/@uppy/progress-bar/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/progress-bar",
   "description": "A progress bar UI for Uppy",
-  "version": "1.3.15",
+  "version": "1.3.16",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 4 - 0
packages/@uppy/provider-views/README.md

@@ -22,6 +22,10 @@ class GoogleDrive extends Plugin {
     // snip
   }
 
+  onFirstRender () {
+    return this.view.getFolder('root', '/')
+  }
+
   render (state) {
     return this.view.render(state)
   }

+ 1 - 1
packages/@uppy/provider-views/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/provider-views",
   "description": "View library for Uppy remote provider plugins.",
-  "version": "1.6.8",
+  "version": "1.7.0",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 22 - 0
packages/@uppy/provider-views/types/index.d.ts

@@ -0,0 +1,22 @@
+import CompanionClient = require('@uppy/companion-client')
+import Uppy = require('@uppy/core')
+
+interface ProviderViewOptions {
+    provider: CompanionClient.Provider
+    viewType?: 'list' | 'grid'
+    showTitles?: boolean
+    showFilter?: boolean
+    showBreadcrumbs?: boolean
+}
+
+interface OnFirstRenderer {
+    onFirstRender: () => any
+}
+
+
+declare class ProviderView {
+    constructor (plugin: Uppy.Plugin & OnFirstRenderer, opts: ProviderViewOptions)
+    // @todo add other provider view methods
+}
+
+export = ProviderView

+ 1 - 1
packages/@uppy/react/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/react",
   "description": "React component wrappers around Uppy's official UI plugins.",
-  "version": "1.8.1",
+  "version": "1.9.0",
   "license": "MIT",
   "main": "index.js",
   "module": "index.mjs",

+ 2 - 2
packages/@uppy/robodog/README.md

@@ -18,8 +18,8 @@ We recommend installing from npm and then using a module bundler such as [Webpac
 Alternatively, you can also use this package in a pre-built bundle from Transloadit's CDN: Edgly.
 
 ```html
-<link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/v1.16.1/robodog.min.css">
-<script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/robodog.min.js"></script>
+<link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/v1.17.0/robodog.min.css">
+<script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/robodog.min.js"></script>
 ```
 
 Then, a global `Robodog` variable will be available. For usage instructions, please see the [main Robodog documentation](https://uppy.io/docs/robodog).

+ 1 - 1
packages/@uppy/robodog/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/robodog",
   "description": "Transloadit SDK for browsers based on Uppy",
-  "version": "1.7.1",
+  "version": "1.7.2",
   "license": "MIT",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",

+ 1 - 1
packages/@uppy/screen-capture/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/screen-capture",
   "description": "Uppy plugin that captures video from display or application.",
-  "version": "1.0.3",
+  "version": "1.0.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 1 - 1
packages/@uppy/status-bar/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/status-bar",
   "description": "A progress bar for Uppy, with many bells and whistles.",
-  "version": "1.7.0",
+  "version": "1.7.1",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 1 - 1
packages/@uppy/thumbnail-generator/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/thumbnail-generator",
   "description": "Uppy plugin that generates small previews of images to show on your upload UI.",
-  "version": "1.6.2",
+  "version": "1.6.3",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 1
packages/@uppy/transloadit/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/transloadit",
   "description": "The Transloadit plugin can be used to upload files to Transloadit for all kinds of processing, such as transcoding video, resizing images, zipping/unzipping, and more",
-  "version": "1.6.1",
+  "version": "1.6.2",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 1
packages/@uppy/tus/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/tus",
   "description": "Resumable uploads for Uppy using Tus.io",
-  "version": "1.6.0",
+  "version": "1.7.0",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 0
packages/@uppy/tus/src/index.js

@@ -376,6 +376,7 @@ module.exports = class Tus extends Plugin {
         uploadUrl: opts.uploadUrl,
         protocol: 'tus',
         size: file.data.size,
+        headers: opts.headers,
         metadata: file.meta
       }).then((res) => {
         this.uppy.setFileState(file.id, { serverToken: res.token })

+ 1 - 1
packages/@uppy/url/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/url",
   "description": "The Url plugin lets users import files from the Internet. Paste any URL and it’ll be added!",
-  "version": "1.5.7",
+  "version": "1.5.8",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 2 - 1
packages/@uppy/utils/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/utils",
   "description": "Shared utility functions for Uppy Core and plugins maintained by the Uppy team.",
-  "version": "3.1.0",
+  "version": "3.2.0",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -18,6 +18,7 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
+    "abortcontroller-polyfill": "^1.4.0",
     "lodash.throttle": "^4.1.1"
   }
 }

+ 20 - 0
packages/@uppy/utils/src/AbortController.js

@@ -0,0 +1,20 @@
+/**
+ * Little AbortController proxy module so we can swap out the implementation easily later.
+ */
+
+const { AbortController, AbortSignal } = require('abortcontroller-polyfill/dist/abortcontroller')
+
+function createAbortError (message = 'Aborted') {
+  try {
+    return new DOMException(message, 'AbortError')
+  } catch {
+    // For Internet Explorer
+    const error = new Error(message)
+    error.name = 'AbortError'
+    return error
+  }
+}
+
+exports.AbortController = AbortController
+exports.AbortSignal = AbortSignal
+exports.createAbortError = createAbortError

+ 51 - 0
packages/@uppy/utils/src/AbortController.test.js

@@ -0,0 +1,51 @@
+const { AbortController, AbortSignal } = require('./AbortController')
+
+function flushInstantTimeouts () {
+  return new Promise(resolve => setTimeout(resolve, 0))
+}
+
+describe('AbortController', () => {
+  it('has the expected shape', () => {
+    const controller = new AbortController()
+    expect(typeof controller.abort).toBe('function')
+    expect(controller.signal).toBeInstanceOf(AbortSignal)
+  })
+
+  it('emits "abort" when abort() is called', async () => {
+    const controller = new AbortController()
+    const callback = jest.fn()
+
+    controller.signal.addEventListener('abort', callback)
+    controller.abort()
+
+    await flushInstantTimeouts()
+
+    expect(callback).toHaveBeenCalled()
+    expect(callback.mock.calls[0][0]).toBeInstanceOf(Event)
+  })
+
+  it('add and remove events', async () => {
+    const controller = new AbortController()
+    const callback = jest.fn()
+    const callback2 = jest.fn()
+
+    controller.signal.addEventListener('abort', callback)
+    controller.signal.addEventListener('abort', callback2)
+    controller.signal.removeEventListener('abort', callback)
+    controller.abort()
+
+    await flushInstantTimeouts()
+
+    expect(callback2).toHaveBeenCalled()
+    expect(callback2.mock.calls[0][0]).toBeInstanceOf(Event)
+    expect(callback).not.toHaveBeenCalled()
+  })
+
+  it('sets `signal.aborted` property when abort() is called', () => {
+    const controller = new AbortController()
+
+    expect(controller.signal.aborted).toBe(false)
+    controller.abort()
+    expect(controller.signal.aborted).toBe(true)
+  })
+})

+ 36 - 0
packages/@uppy/utils/src/delay.js

@@ -0,0 +1,36 @@
+const { createAbortError } = require('./AbortController')
+
+/**
+ * Return a Promise that resolves after `ms` milliseconds.
+ *
+ * @param {number} ms - Number of milliseconds to wait.
+ * @param {{ signal?: AbortSignal }} [opts] - An abort signal that can be used to cancel the delay early.
+ * @returns {Promise<void>} A Promise that resolves after the given amount of `ms`.
+ */
+module.exports = function delay (ms, opts) {
+  return new Promise((resolve, reject) => {
+    if (opts && opts.signal && opts.signal.aborted) {
+      return reject(createAbortError())
+    }
+
+    function onabort () {
+      clearTimeout(timeout)
+      cleanup()
+      reject(createAbortError())
+    }
+
+    const timeout = setTimeout(() => {
+      cleanup()
+      resolve()
+    }, ms)
+
+    if (opts && opts.signal) {
+      opts.signal.addEventListener('abort', onabort)
+    }
+    function cleanup () {
+      if (opts && opts.signal) {
+        opts.signal.removeEventListener('abort', onabort)
+      }
+    }
+  })
+}

+ 33 - 0
packages/@uppy/utils/src/delay.test.js

@@ -0,0 +1,33 @@
+const delay = require('./delay')
+const { AbortController } = require('./AbortController')
+
+describe('delay', () => {
+  it('should wait for the specified time', async () => {
+    const start = Date.now()
+    await delay(100)
+    expect(Date.now() - start).toBeGreaterThanOrEqual(100)
+  })
+
+  it('should reject if signal is already aborted', async () => {
+    const signal = { aborted: true }
+    const start = Date.now()
+    await expect(delay(100, { signal })).rejects.toHaveProperty('name', 'AbortError')
+    // should really be instant but using a very large range in case CI decides to be super busy and block the event loop for a while
+    expect(Date.now() - start).toBeLessThan(50)
+  })
+
+  it('should reject when signal is aborted', async () => {
+    const controller = new AbortController()
+    const start = Date.now()
+    const testDelay = delay(100, { signal: controller.signal })
+    await Promise.all([
+      delay(50).then(() => controller.abort()),
+      expect(testDelay).rejects.toHaveProperty('name', 'AbortError')
+    ])
+
+    // should have rejected before the timer is done
+    const time = Date.now() - start
+    expect(time).toBeGreaterThanOrEqual(50)
+    expect(time).toBeLessThan(100)
+  })
+})

+ 1 - 1
packages/@uppy/webcam/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/webcam",
   "description": "Uppy plugin that takes photos or records videos using the device's camera.",
-  "version": "1.6.7",
+  "version": "1.6.8",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",

+ 1 - 1
packages/@uppy/xhr-upload/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/xhr-upload",
   "description": "Plain and simple classic HTML multipart form uploads with Uppy, as well as uploads using the HTTP PUT method.",
-  "version": "1.6.0",
+  "version": "1.6.1",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 1 - 1
packages/uppy/package.json

@@ -1,7 +1,7 @@
 {
   "name": "uppy",
   "description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
-  "version": "1.16.1",
+  "version": "1.17.0",
   "license": "MIT",
   "main": "index.js",
   "module": "index.mjs",

+ 575 - 0
website/src/_posts/2020-03-custom-providers.md

@@ -0,0 +1,575 @@
+---
+title: "Adding Custom Providers: Step by Step Tutorial to add Custom Providers"
+date: 2020-06-08
+author: ife
+published: false
+---
+
+In this post we will be going through a step by step tutorial on how to develop, and add custom [providers](https://uppy.io/docs/providers/) to Uppy and Companion. For the purpose of this tutorial we will be building a custom provider for [Unsplash](https://unsplash.com/).
+
+<!--more-->
+
+## What are Provider Plugins?
+
+> Provider Plugins help you connect to your accounts with remote file storage providers such as Dropbox, Google Drive, Instagram and remote URLs.
+>
+> They work tightly with [Companion](https://www.npmjs.com/package/@uppy/companion) — Uppy's server-to-server communication handler between your server and file storage providers.
+
+Read more on [Provider Plugins](https://uppy.io/docs/providers/).
+
+
+Creating a custom provider composes of two parts; **Custom Companion Provider** and **Custom Uppy Plugin**
+
+## Creating a Custom Companion Provider
+
+On the Companion side, we need to create a Provider that receives the provider related requests that would come from Uppy.
+
+### Setting up Companion
+
+We'll start off by setting up a minimal express server.
+
+1. Create a folder named "custom-provider" and navigate into it
+
+```sh
+mkdir custom-provider
+cd custom-provider
+```
+2. Run `npm init` to setup your `package.json` file
+3. install express, express-session, body-parser, request, uppy and @uppy/companion
+
+```sh
+npm i -S express express-session body-parser request @uppy/companion uppy
+```
+
+with all that done, my package.json file looks something like this:
+
+```json
+{
+  "name": "custom-provider",
+  "dependencies": {
+    "body-parser": "^1.18.2",
+    "express": "^4.16.2",
+    "express-session": "^1.15.6",
+    "request": "^2.88.0",
+    "uppy": "^1.14.1",
+    "@uppy/companion": "^2.0.0"
+  },
+  "private": true,
+  "scripts": {}
+}
+```
+
+4. Create a `server/index.js` file within the project and add initiate your express server
+
+```js
+const express = require('express')
+const companion = require('@uppy/companion')
+const bodyParser = require('body-parser')
+const session = require('express-session')
+
+const app = express()
+
+app.use(bodyParser.json())
+app.use(session({
+  secret: 'some-secret',
+  resave: true,
+  saveUninitialized: true
+}))
+
+app.use((req, res, next) => {
+  res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
+  res.setHeader(
+    'Access-Control-Allow-Methods',
+    'GET, POST, OPTIONS, PUT, PATCH, DELETE'
+  )
+  res.setHeader(
+    'Access-Control-Allow-Headers',
+    'Authorization, Origin, Content-Type, Accept'
+  )
+  next()
+})
+
+// initialize uppy
+const companionOptions = {
+  providerOptions: {
+    dropbox: {
+      key: 'your Dropbox key',
+      secret: 'your Dropbox secret'
+    }
+  },
+  server: {
+    host: 'localhost:3020',
+    protocol: 'http'
+  },
+  filePath: './output',
+  secret: 'some-secret',
+  debug: true
+}
+
+app.use(companion.app(companionOptions))
+
+// handle 404
+app.use((req, res, next) => {
+  return res.status(404).json({ message: 'Not Found' })
+})
+
+companion.socket(app.listen(3020), companionOptions)
+
+console.log('Welcome to Companion!')
+console.log(`Listening on http://0.0.0.0:3020`)
+
+```
+
+The code snippet above sets up an express server and plugs Companion into it. However, the Companion setup doesn't include a custom provider yet. It only includes the Dropbox provider.
+
+To be sure everything is working correctly so far, you can start the server by running:
+
+`node server/index.js`
+
+It should log the following message in the console:
+
+```sh
+Welcome to Companion!
+Listening on http://0.0.0.0:3020
+```
+
+Now we need to proceed to add our custom provider.
+
+### Setting up Unsplash API
+
+Head to Unsplash's [developer platform](https://unsplash.com/oauth/applications) (you'd need to create an account if you don't already have one).
+
+Click "New Application" to create a new application. For the sake of this tutorial, I am naming my application "companion-provider". Once it is created, your page should look something like this:
+
+![](/images/blog/custom-providers/unsplash-api-dashboard.png)
+
+### Writing the custom provider
+
+Back to our project directory, we are going to create a `server/customprovider.js` file. This will contain the code that implements the Unsplash provider.
+
+According to [the docs](https://uppy.io/docs/companion/#Adding-custom-providers), we need to create a class that implements the methods `list`, `download`, and `size`.
+
+The constructor of our class will look something like this:
+
+```js
+class MyCustomProvider {
+  constructor (options) {
+    this.authProvider = 'myunsplash' // the name of our provider (lowercased)
+  }
+  ...
+}
+```
+
+Our `list` method would look something like this
+
+```js
+list ({ token, directory }, done) {
+  const path = directory ? `/${directory}/photos` : ''
+  const options = {
+    url: `${BASE_URL}/collections${path}`,
+    method: 'GET',
+    json: true,
+    headers: {
+      Authorization: `Bearer ${token}`
+    }
+  }
+
+  request(options, (err, resp, body) => {
+    if (err) {
+      console.log(err)
+      done(err)
+      return
+    }
+
+    done(null, this._adaptData(body))
+  })
+}
+
+_adaptData (res) {
+  const data = {
+    username: null,
+    items: [],
+    nextPagePath: null
+  }
+
+  const items = res
+  items.forEach((item) => {
+    const isFolder = !!item.published_at
+    data.items.push({
+      isFolder: isFolder,
+      icon: isFolder ? item.cover_photo.urls.thumb : item.urls.thumb,
+      name: item.title || item.description,
+      mimeType: isFolder ? null : 'image/jpeg',
+      id: item.id,
+      thumbnail: isFolder ? item.cover_photo.urls.thumb : item.urls.thumb,
+      requestPath: item.id,
+      modifiedDate: item.updated_at,
+      size: null
+    })
+  })
+
+  return data
+}
+```
+
+the method makes an http request to the unsplash API based on the documentation to [list collections](https://unsplash.com/documentation#list-collections) and [list collection's photos](https://unsplash.com/documentation#get-a-collections-photos). The `_adaptData` method exists to help us conform to [the list data](https://uppy.io/docs/companion/#list-data)
+
+Our `size` method will look something like this:
+
+```js
+size ({ id, token }, done) {
+  const options = {
+    url: `${BASE_URL}/photos/${id}`,
+    method: 'GET',
+    json: true,
+    headers: {
+      Authorization: `Bearer ${token}`
+    }
+  }
+
+  request(options, (err, resp, body) => {
+    if (err) {
+      console.log(err)
+      done(err)
+      return
+    }
+
+    done(null, body.width * body.height)
+  })
+}
+```
+
+Unsplash's API doesn't provide the file size, so we are estimating it by multiplying the width and height of the image.
+
+The `download` method will look like this:
+
+```js
+download ({ id, token }, onData) {
+  const options = {
+    url: `${BASE_URL}/photos/${id}`,
+    method: 'GET',
+    json: true,
+    headers: {
+      Authorization: `Bearer ${token}`
+    }
+  }
+
+  request(options, (err, resp, body) => {
+    if (err) {
+      console.log(err)
+      return
+    }
+
+    const url = body.links.download
+    request.get(url)
+      .on('data', (chunk) => onData(null, chunk))
+      .on('end', () => onData(null, null))
+      .on('error', (err) => console.log(err))
+  })
+}
+```
+With all of this put together the entire file would look something like this:
+
+```js
+const request = require('request')
+const BASE_URL = 'https://api.unsplash.com'
+
+class MyCustomProvider {
+  constructor (options) {
+    this.authProvider = 'myunsplash'
+  }
+
+  list ({ token, directory }, done) {
+    const path = directory ? `/${directory}/photos` : ''
+    const options = {
+      url: `${BASE_URL}/collections${path}`,
+      method: 'GET',
+      json: true,
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    }
+
+    request(options, (err, resp, body) => {
+      if (err) {
+        console.log(err)
+        done(err)
+        return
+      }
+
+      done(null, this._adaptData(body))
+    })
+  }
+
+  download ({ id, token }, onData) {
+    const options = {
+      url: `${BASE_URL}/photos/${id}`,
+      method: 'GET',
+      json: true,
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    }
+
+    request(options, (err, resp, body) => {
+      if (err) {
+        console.log(err)
+        return
+      }
+
+      const url = body.links.download
+      request.get(url)
+        .on('data', (chunk) => onData(null, chunk))
+        .on('end', () => onData(null, null))
+        .on('error', (err) => console.log(err))
+    })
+  }
+
+  size ({ id, token }, done) {
+    const options = {
+      url: `${BASE_URL}/photos/${id}`,
+      method: 'GET',
+      json: true,
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    }
+
+    request(options, (err, resp, body) => {
+      if (err) {
+        console.log(err)
+        done(err)
+        return
+      }
+
+      done(null, body.width * body.height)
+    })
+  }
+
+  _adaptData (res) {
+    const data = {
+      username: null,
+      items: [],
+      nextPagePath: null
+    }
+
+    const items = res
+    items.forEach((item) => {
+      const isFolder = !!item.published_at
+      data.items.push({
+        isFolder: isFolder,
+        icon: isFolder ? item.cover_photo.urls.thumb : item.urls.thumb,
+        name: item.title || item.description,
+        mimeType: isFolder ? null : 'image/jpeg',
+        id: item.id,
+        thumbnail: isFolder ? item.cover_photo.urls.thumb : item.urls.thumb,
+        requestPath: item.id,
+        modifiedDate: item.updated_at,
+        size: null
+      })
+    })
+
+    return data
+  }
+}
+
+module.exports = MyCustomProvider
+```
+
+Now we can go back to `server/index.js` to enable our custom provider:
+
+```js
+// initialize uppy
+const uppyOptions = {
+  providerOptions: {
+    dropbox: {
+      key: 'your Dropbox key',
+      secret: 'your Dropbox secret'
+    }
+  },
+  customProviders: {
+    myunsplash: {
+      config: {
+        // source https://unsplash.com/documentation#user-authentication
+        authorize_url: 'https://unsplash.com/oauth/authorize',
+        access_url: 'https://unsplash.com/oauth/token',
+        oauth: 2,
+        key: 'YOUR UNSPLASH API KEY',
+        secret: 'YOUR UNSPLASH API SECRET',
+        callback: '/myunsplash/callback',
+        transport: 'session',
+      },
+      // you provider module
+      module: require('./customprovider')
+    }
+  },
+  server: {
+    host: 'localhost:3020',
+    protocol: 'http'
+  },
+  filePath: './output',
+  secret: 'some-secret',
+  debug: true
+}
+```
+
+You can find your unsplash API key on your Unsplash [app dashboard](https://unsplash.com/oauth/applications)
+
+## Creating a Custom Uppy Plugin
+
+Now we need to implement the client part of this. To do this we need to implement an Uppy Plugin.
+
+First, we'll create a `client/MyCustomProvider.js` file. Following the instructions [here](https://uppy.io/docs/writing-plugins/), our Uppy Plugin (aka `client/MyCustomProvider.js` file) could look something like this:
+
+```js
+const { Plugin } = require('@uppy/core')
+const { Provider } = require('@uppy/companion-client')
+const ProviderViews = require('@uppy/provider-views')
+const { h } = require('preact')
+
+module.exports = class MyCustomProvider extends Plugin {
+  constructor (uppy, opts) {
+    super(uppy, opts)
+    this.type = 'acquirer'
+    this.id = this.opts.id || 'MyCustomProvider'
+    Provider.initPlugin(this, opts)
+
+    this.title = 'MyUnsplash'
+    this.icon = () => (
+      <svg width="32" height="32" xmlns="http://www.w3.org/2000/svg">
+        <path d="M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z" fill="#000000" fill-rule="nonzero" />
+      </svg>
+    )
+
+    this.provider = new Provider(uppy, {
+      companionUrl: this.opts.companionUrl,
+      companionHeaders: this.opts.companionHeaders || this.opts.serverHeaders,
+      provider: 'myunsplash',
+      pluginId: this.id
+    })
+
+    this.files = []
+    this.onFirstRender = this.onFirstRender.bind(this)
+    this.render = this.render.bind(this)
+
+    // merge default options with the ones set by user
+    this.opts = Object.assign({}, opts)
+  }
+
+  install () {
+    this.view = new ProviderViews(this, {
+      provider: this.provider
+    })
+    // Set default state
+    this.setPluginState({
+      authenticated: false,
+      files: [],
+      folders: [],
+      directories: [],
+      activeRow: -1,
+      filterInput: '',
+      isSearchVisible: false
+    })
+
+    const target = this.opts.target
+    if (target) {
+      this.mount(target, this)
+    }
+  }
+
+  uninstall () {
+    this.view.tearDown()
+    this.unmount()
+  }
+
+  onFirstRender () {
+    return this.view.getFolder()
+  }
+
+  render (state) {
+    return this.view.render(state)
+  }
+}
+```
+
+Asides from implementing the methods required by the Uppy Plugin, we are also implmeneting the method `onFirstRender`. This is because we are using the [@uppy/provider-views](https://www.npmjs.com/package/@uppy/provider-views) package to reuse its UI componenets. The [@uppy/provider-views](https://www.npmjs.com/package/@uppy/provider-views) package requires that our plugin implements an `onFirstRender` method.
+
+We are also pre-setting a default plugin with a default object structure which is used by the `@uppy/provider-views` package.
+
+With that done, we can now use our new plugin with Uppy. Create a file `client/main.js` and initiate Uppy in there like so:
+
+```js
+const Uppy = require('@uppy/core')
+const Dropbox = require('@uppy/dropbox')
+const Tus = require('@uppy/tus')
+const MyCustomProvider = require('./MyCustomProvider')
+const Dashboard = require('@uppy/dashboard')
+
+const uppy = Uppy({
+  debug: true
+})
+
+uppy.use(Dropbox, {
+  companionUrl: 'http://localhost:3020'
+})
+
+uppy.use(MyCustomProvider, {
+  companionUrl: 'http://localhost:3020'
+})
+
+uppy.use(Dashboard, {
+  inline: true,
+  target: 'body',
+  plugins: ['Dropbox', 'MyCustomProvider']
+})
+
+uppy.use(Tus, { endpoint: 'https://master.tus.io/files/' })
+```
+
+Now we need to bundle the file so it can be loaded on the browser. To do this, we'd install [budo](https://www.npmjs.com/package/budo):
+
+1. In the root project directory, run `npm install budo -g`.
+2. Create a `babel.config.js` file looking like so:
+
+```js
+module.exports = (api) => {
+  api.env('test')
+  return {
+    presets: [
+      ['@babel/preset-env', {
+        modules: false,
+        loose: true
+      }]
+    ],
+    plugins: [
+      ['@babel/plugin-transform-react-jsx', { pragma: 'h' }],
+    ].filter(Boolean)
+  }
+}
+```
+3. Add an `index.html` file (in the root of the project) that uses a bundle file like so:
+
+```html
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Uppy Custom provider Example</title>
+    <link href="https://transloadit.edgly.net/releases/uppy/v1.15.0/uppy.min.css" rel="stylesheet">
+  </head>
+  <body>
+    <script src="bundle.js"></script>
+  </body>
+</html>
+```
+
+4. Create the bundle file by running `budo client/main.js:bundle.js -- -t babelify`. This will bundle your `client` side work and have it running on a local server. Please check the console log to see on what port the server is running. As at the time of this writing, it defaults to `9966` (URL will be http://localhost:9966/).
+
+You can now load the URL of your client on your browser to see it running. When I run mine, it looks something like this:
+
+![](/images/blog/custom-providers/dashboard-preview.png)
+
+In order to use this with our Companion implementation, we need to also start our Companion server by running `node server/index.js`.
+
+Congrats! 🎉We have completed our custom plugin at this point. For an example source code on this tutorial, please see the [custom provider example](https://github.com/transloadit/uppy/tree/master/examples/custom-provider).

+ 15 - 9
website/src/docs/aws-s3-multipart.md

@@ -38,23 +38,29 @@ const AwsS3Multipart = Uppy.AwsS3Multipart
 
 The `@uppy/aws-s3-multipart` plugin has the following configurable options:
 
-### limit: 0
+### `limit: 0`
 
 The maximum amount of chunks to upload simultaneously. Set to `0` to disable limiting.
 
-### companionUrl: null
+### `retryDelays: [0, 1000, 3000, 5000]`
+
+When uploading a chunk fails, automatically try again after the millisecond intervals specified in this array. By default, we first retry instantly; if that fails, we retry after 1 second; if that fails, we retry after 3 seconds, etc.
+
+Set to `null` to disable automatic retries, and fail instantly if any chunk fails to upload.
+
+### `companionUrl: null`
 
 URL of the [Companion](/docs/companion) instance to use for proxying calls to the S3 Multipart API.
 
 This will be used by the default implementations of the upload-related functions below. If you provide your own implementations, a `companionUrl` is unnecessary.
 
-### companionHeaders: {}
+### `companionHeaders: {}`
 
 Custom headers that should be sent along to [Companion](/docs/companion) on every request.
 
 This will be used by the default implementations of the upload-related functions below. If you provide your own implementations, these headers are not sent automatically.
 
-### getChunkSize(file)
+### `getChunkSize(file)`
 
 A function that returns the minimum chunk size to use when uploading the given file.
 
@@ -62,7 +68,7 @@ The S3 Multipart plugin uploads files in chunks. Each chunk requires a signing r
 
 S3 requires a minimum chunk size of 5MB, and supports at most 10,000 chunks per multipart upload. If `getChunkSize()` returns a size that's too small, Uppy will increase it to S3's minimum requirements.
 
-### createMultipartUpload(file)
+### `createMultipartUpload(file)`
 
 A function that calls the S3 Multipart API to create a new upload. `file` is the file object from Uppy's state. The most relevant keys are `file.name` and `file.type`.
 
@@ -73,7 +79,7 @@ Return a Promise for an object with keys:
 
 The default implementation calls out to Companion's S3 signing endpoints.
 
-### listParts(file, { uploadId, key })
+### `listParts(file, { uploadId, key })`
 
 A function that calls the S3 Multipart API to list the parts of a file that have already been uploaded. Receives the `file` object from Uppy's state, and an object with keys:
 
@@ -88,7 +94,7 @@ Return a Promise for an array of S3 Part objects, as returned by the S3 Multipar
 
 The default implementation calls out to Companion's S3 signing endpoints.
 
-### prepareUploadPart(file, partData)
+### `prepareUploadPart(file, partData)`
 
 A function that generates a signed URL to upload a single part. Receives the `file` object from Uppy's state. The `partData` argument is an object with keys:
 
@@ -113,7 +119,7 @@ Return a Promise for an object with keys:
    ```
  - `headers` - **(Optional)** Custom headers that should be sent to the S3 presigned URL.
 
-### abortMultipartUpload(file, { uploadId, key })
+### `abortMultipartUpload(file, { uploadId, key })`
 
 A function that calls the S3 Multipart API to abort a Multipart upload, and delete all parts that have been uploaded so far. Receives the `file` object from Uppy's state, and an object with keys:
 
@@ -124,7 +130,7 @@ This is typically called when the user cancels an upload. Cancellation cannot fa
 
 The default implementation calls out to Companion's S3 signing endpoints.
 
-### completeMultipartUpload(file, { uploadId, key, parts })
+### `completeMultipartUpload(file, { uploadId, key, parts })`
 
 A function that calls the S3 Multipart API to complete a Multipart upload, combining all parts into a single object in the S3 bucket. Receives the `file` object from Uppy's state, and an object with keys:
 

+ 58 - 8
website/src/docs/companion.md

@@ -19,8 +19,8 @@ Companion handles the server-to-server communication between your server and fil
 
 As of now, Companion is integrated to work with:
 
-- Google Drive
-- Dropbox
+- Google Drive - [Set up instructions](/docs/google-drive/#Setting-Up)
+- Dropbox - [Set up instructions](/docs/dropbox/#Setting-Up)
 - Instagram
 - Facebook
 - OneDrive
@@ -286,7 +286,7 @@ See [env.example.sh](https://github.com/transloadit/uppy/blob/master/env.example
 
 6. **sendSelfEndpoint(optional)** - This is basically the same as the `server.host + server.path` attributes. The major reason for this attribute is that, when set, it adds the value as the `i-am` header of every request response.
 
-7. **customProviders(optional)** - This option enables you to add custom providers along with the already supported providers. See [Adding Custom Providers](#Adding-Custom-Providers) for more information.
+7. **customProviders(optional)** - This option enables you to add custom providers along with the already supported providers. See [Adding Custom Providers](#Adding-custom-providers) for more information.
 
 8. **uploadUrls(optional)** - An array of URLs (full paths). If specified, Companion will only accept uploads to these URLs (useful when you want to make sure a Companion instance is only allowed to upload to your servers, for example).
 
@@ -359,7 +359,7 @@ We have [a detailed guide on running Companion in Kubernetes](https://github.com
 
 ### Adding custom providers
 
-As of now, Companion supports **Google Drive**, **Dropbox**, **Instagram**, and **URL** (remote urls) out of the box, but you may also choose to add your own custom providers. You can do this by passing the `customProviders` option when calling the Uppy `app` method. The custom provider is expected to support Oauth 1 or 2 for authentication/authorization.
+As of now, Companion supports the [providers listed here](https://uppy.io/docs/companion/#Supported-providers) out of the box, but you may also choose to add your own custom providers. You can do this by passing the `customProviders` option when calling the Uppy `app` method. The custom provider is expected to support Oauth 1 or 2 for authentication/authorization.
 
 ```javascript
 let options = {
@@ -371,6 +371,7 @@ let options = {
                 oauth: 2,
                 key: "***",
                 secret: "***",
+                callback: '/myprovidername/callback'
                 scope: ["read", "write"]
             },
             module: require('/path/to/provider/module')
@@ -390,13 +391,62 @@ To work well with Companion, the **Module** must be a class with the following m
     - token - authorization token (retrieved from oauth process) to send along with your request
     - directory - the `id/name` of the directory from which data is to be retrieved. This may be ignored if it doesn't apply to your provider
     - query - expressjs query params object received by the server (just in case there is some data you need in there).
-  - `done (err, response, body)` - the callback that should be called when the request to your provider is made. As the signature indicates, the following data should be passed along to the callback `err`, `response`, and `body`.
-2. `download (options, onData, onResponse)` - downloads a particular file from the provider.
+  - `done (err, data)` - the callback that should be called when the request to your provider is made. As the signature indicates, the following data should be passed along to the callback `err`, and [`data`](#list-data).
+2. `download (options, onData)` - downloads a particular file from the provider.
   - `options` - is an object containing the following attributes:
     - token - authorization token (retrieved from oauth process) to send along with your request.
     - id - ID of the file being downloaded.
-  - `onData (chunk)` - a callback that should be called with each data chunk received on download. This is useful if the size of the downloaded file can be pre-determined. This would allow for pipelined upload of the file (to the desired destination), while the download is still going on.
-  - `onResponse (response)` - if the size of the downloaded file can not be pre-determined by Companion, then this callback should be called in place of the `onData` callback. This callback would be called after the download is done, and would take the downloaded data (response) as the argument.
+    - query - expressjs query params object received by the server (just in case there is some data you need in there).
+  - `onData (err, chunk)` - a callback that should be called with each data chunk received as download is happening. The `err` argument is an error that should be passed if an error occurs during download. It should be `null` if there's no error. Once the download is completed and there are no more chunks to receive, `onData` should be called with `null` values like so `onData(null, null)`
+3. `size (options, done)` - returns the byte size of the file that needs to be downloaded.
+  - `options` - is an object containing the following attributes:
+    - token - authorization token (retrieved from oauth process) to send along with your request.
+    - id - ID of the file being downloaded.
+  - `done (err, size)` - the callback that should be called after the request to your provider is completed. As the signature indicates, the following data should be passed along to the callback `err`, and `size` (number).
+
+The class must also have an `authProvider` string (lowercased) field which typically indicates the name of the provider (e.g "dropbox").
+
+#### list data
+
+```js
+{
+  // username or email of the user whose provider account is being accessed
+  username: 'johndoe',
+  // list of files and folders in the directory. An item is considered a folder
+  //  if it mainly exists as a collection to contain sub-items
+  items: [
+    {
+      // boolean value of whether or NOT it's a folder
+      isFolder: false,
+      // icon image URL
+      icon: 'https://random-api.url.com/fileicon.jpg',
+      // name of the item
+      name: 'myfile.jpg',
+      // the mime type of the item. Only relevant if the item is NOT a folder
+      mimeType: 'image/jpg',
+      // the id (in string) of the item
+      id: 'uniqueitemid',
+      // thumbnail image URL. Only relevant if the item is NOT a folder
+      thumbnail: 'https://random-api.url.com/filethumbnail.jpg',
+      // for folders this is typically the value that will be passed as "directory" in the list(...) method.
+      // For files, this is the value that will be passed as id in the download(...) method.
+      requestPath: 'file-or-folder-requestpath',
+      // datetime string (in ISO 8601 format) of when this item was last modified
+      modifiedDate: '2020-06-29T19:59:58Z',
+      // the size in bytes of the item. Only relevent if the item is NOT a folder
+      size: 278940,
+      custom: {
+        // an object that may contain some more custom fields that you may need to send to the client. Only add this object if you have a need for it.
+        customData1: 'the value',
+        customData2: 'the value',
+      },
+      // more items here
+    }
+  ]
+  // if the "items" list is paginated, this is the request path needed to fetch the next page.
+  nextPagePath: 'directory-name?cursor=cursor-to-next-page'
+}
+```
 
 ## Development
 

+ 35 - 1
website/src/docs/dropbox.md

@@ -39,13 +39,47 @@ In the [CDN package](/docs/#With-a-script-tag), it is available on the `Uppy` gl
 const Dropbox = Uppy.Dropbox
 ```
 
+## Setting Up
+
+To use the Dropbox provider, you need to configure the Dropbox keys that Companion should use. With the standalone Companion server, specify environment variables:
+```shell
+export COMPANION_DROPBOX_KEY="Dropbox API key"
+export COMPANION_DROPBOX_SECRET="Dropbox API secret"
+```
+
+When using the Companion Node.js API, configure these options:
+```js
+companion.app({
+  providerOptions: {
+    dropbox: {
+      key: 'Dropbox API key',
+      secret: 'Dropbox API secret'
+    }
+  }
+})
+```
+
+You can create a Dropbox App on the [Dropbox Developers site](https://www.dropbox.com/developers/apps/create).
+
+Things to note:
+- Choose the "Dropbox API", not the business variant.
+- Typically you'll want "Full Dropbox" access, unless you are very certain that you need the other one.
+
+You'll be redirected to the app page. This page lists the app key and app secret, which you should use to configure Companion as shown above.
+
+The app page has a "Redirect URIs" field. Here, add:
+```
+https://$YOUR_COMPANION_HOST_NAME/connect/dropbox/callback
+```
+
+You can only use the integration with your own account initially—make sure to apply for production status on the app page before you publish your app, or your users will not be able to sign in!
+
 ## CSS
 
 Dashboard plugin is recommended as a container to all Provider plugins, including Dropbox. If you are using Dashboard, it [comes with all the nessesary styles](/docs/dashboard/#CSS) for Dropbox as well.
 
 ⚠️ If you are feeling adventurous, and want to use Dropbox plugin separately, without Dashboard, make sure to include `@uppy/provider-views/dist/style.css` (or `style.min.css`) CSS file. This is experimental, not officially supported and not recommended.
 
-
 ## Options
 
 The `@uppy/dropbox` plugin has the following configurable options:

+ 31 - 0
website/src/docs/google-drive.md

@@ -39,6 +39,37 @@ In the [CDN package](/docs/#With-a-script-tag), it is available on the `Uppy` gl
 const GoogleDrive = Uppy.GoogleDrive
 ```
 
+## Setting  Up
+
+To use the Google Drive provider, you need to configure the Google Drive keys that Companion should use. With the standalone Companion server, specify environment variables:
+```shell
+export COMPANION_GOOGLE_KEY="Google Drive OAuth client ID"
+export COMPANION_GOOGLE_SECRET="Google Drive OAuth client secret"
+```
+
+When using the Companion Node.js API, configure these options:
+```js
+companion.app({
+  providerOptions: {
+    drive: {
+      key: 'Google Drive OAuth client ID',
+      secret: 'Google Drive OAuth client secret'
+    }
+  }
+})
+```
+
+To sign up for API keys, go to the [Google Developer Console](https://console.developers.google.com/).
+
+Create a project for your app if you don't have one yet.
+- On the project's dashboard, [enable the Google Drive API](https://developers.google.com/drive/api/v3/enable-drive-api).
+- [Set up OAuth authorization](https://developers.google.com/drive/api/v3/about-auth). Use this for an authorized redirect URI:
+   ```
+   https://$YOUR_COMPANION_HOST_NAME/connect/google/callback
+   ```
+
+Google will give you an OAuth client ID and client secret. Use them to configure Companion as shown above.
+
 ## CSS
 
 Dashboard plugin is recommended as a container to all Provider plugins, including Google Drive. If you are using Dashboard, it [comes with all the necessary styles](/docs/dashboard/#CSS) for Google Drive as well.

+ 5 - 5
website/src/docs/index.md

@@ -19,12 +19,12 @@ Here’s the simplest example html page with Uppy (it uses a CDN bundle, while w
   <head>
     <meta charset="utf-8">
     <title>Uppy</title>
-    <link href="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.css" rel="stylesheet">
+    <link href="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.css" rel="stylesheet">
   </head>
   <body>
     <div id="drag-drop-area"></div>
 
-    <script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
+    <script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
     <script>
       var uppy = Uppy.Core()
         .use(Uppy.Dashboard, {
@@ -113,12 +113,12 @@ You can also use a pre-built bundle from Transloadit's CDN: Edgly. `Uppy` will a
 1\. Add a script at the bottom of the closing `</body>` tag:
 
 ``` html
-<script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
 ```
 
 2\. Add CSS to `<head>`:
 ``` html
-<link href="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.css" rel="stylesheet">
+<link href="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.css" rel="stylesheet">
 ```
 
 3\. Initialize at the bottom of the closing `</body>` tag:
@@ -169,5 +169,5 @@ const Uppy = require('@uppy/core')
 If you're using Uppy from CDN, `es6-promise` and  `whatwg-fetch` are already included in the bundle, no need to include anything additionally:
 
 ```html
-<script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
 ```

+ 2 - 2
website/src/docs/locales.md

@@ -33,8 +33,8 @@ const uppy = Uppy({
 Add a `<script>` tag with Uppy bundle and the locale pack you’d like to use. You can copy/paste the link from the CDN column in the [locales table](#List-of-locale-packs). The locale will attach itself to the `Uppy.locales` object.
 
 ```html
-<script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
-<script src="https://transloadit.edgly.net/releases/uppy/locales/v1.15.0/de_DE.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/locales/v1.15.1/de_DE.min.js"></script>
 
 <script>
 var uppy = Uppy.Core({

+ 12 - 29
website/src/docs/react-dashboard-modal.md

@@ -3,7 +3,7 @@ title: "&lt;DashboardModal />"
 type: docs
 module: "@uppy/react"
 permalink: docs/react/dashboard-modal/
-order: 5
+order: 6
 category: "React"
 ---
 
@@ -39,35 +39,8 @@ Import general Core styles from `@uppy/core/dist/style.css` first, then add the
 
 Styles for Provider plugins, like Google Drive and Instagram, are also bundled with Dashboard styles. Styles for other plugins, such as `@uppy/url` and `@uppy/webcam`, are not inluded. If you are using those, please see their docs and make sure to include styles for them as well.
 
-## Initializing Uppy
-
-Your Uppy instance must be initialized before passing it to an `uppy={}` prop, and should be cleaned up using `uppy.close()` when you are done with it. A simple approach is to initialize it in your React component's `constructor()` and destroy it in `componentWillUnmount()`.
-
-> ⚠ Uppy instances are stateful, so the same instance must be used across different renders.
-> Do **NOT** initialize Uppy in a `render()` method!
-> Do **NOT** initialize Uppy in a function component!
-
-```js
-class MyComponent extends React.Component {
-  constructor (props) {
-    super(props)
-    this.uppy = Uppy()
-      .use(Transloadit, {})
-  }
-
-  componentWillUnmount () {
-    this.uppy.close()
-  }
-
-  render () {
-    return <DashboardModal uppy={this.uppy} />
-  }
-}
-```
-
 <!-- Make sure the old name of this section still works -->
 <a id="Options"></a>
-
 ## Props
 
 On top of all the [`@uppy/dashboard`][] options, the `<DashboardModal />` plugin adds two additional props:
@@ -75,6 +48,8 @@ On top of all the [`@uppy/dashboard`][] options, the `<DashboardModal />` plugin
  - `open` - Boolean true or false, setting this to `true` opens the modal and setting it to `false` closes it.
  - `onRequestClose` - Callback called when the user attempts to close the modal, either by clicking the close button or by clicking outside the modal (if the `closeModalOnClickOutside` prop is set).
 
+An Uppy instance must be provided in the `uppy={}` prop: see [Initializing Uppy](/docs/react/initializing) for details.
+
 To use other plugins like [`@uppy/webcam`][] with the `<DashboardModal />` component, add them to the Uppy instance and then specify their `id` in the [`plugins`](/docs/dashboard/#plugins) prop:
 
 ```js
@@ -97,10 +72,18 @@ class MusicUploadButton extends React.Component {
       modalOpen: false
     }
 
+    this.uppy = new Uppy()
+      .use(XHRUpload, { endpoint: '/api/songs/upload' })
+      .use(Webcam, { modes: ['audio-only'] })
+
     this.handleOpen = this.handleOpen.bind(this)
     this.handleClose = this.handleClose.bind(this)
   }
 
+  componentWillUnmount () {
+    this.uppy.close()
+  }
+
   handleOpen () {
     this.setState({
       modalOpen: true
@@ -118,7 +101,7 @@ class MusicUploadButton extends React.Component {
       <div>
         <button onClick={this.handleOpen}>Upload some music</button>
         <DashboardModal
-          uppy={this.props.uppy}
+          uppy={this.uppy}
           closeModalOnClickOutside
           open={this.state.modalOpen}
           onRequestClose={this.handleClose}

+ 21 - 41
website/src/docs/react-dashboard.md

@@ -3,7 +3,7 @@ title: "&lt;Dashboard />"
 type: docs
 module: "@uppy/react"
 permalink: docs/react/dashboard/
-order: 4
+order: 5
 category: "React"
 ---
 
@@ -39,52 +39,32 @@ Import general Core styles from `@uppy/core/dist/style.css` first, then add the
 
 Styles for Provider plugins, like Google Drive and Instagram, are also bundled with Dashboard styles. Styles for other plugins, such as `@uppy/url` and `@uppy/webcam`, are not inluded. If you are using those, please see their docs and make sure to include styles for them as well.
 
-## Initializing Uppy
-
-Your Uppy instance must be initialized before passing it to an `uppy={}` prop, and should be cleaned up using `uppy.close()` when you are done with it. A simple approach is to initialize it in your React component's `constructor()` and destroy it in `componentWillUnmount()`.
-
-> ⚠ Uppy instances are stateful, so the same instance must be used across different renders.
-> Do **NOT** initialize Uppy in a `render()` method!
-> Do **NOT** initialize Uppy in a function component!
-
-```js
-class MyComponent extends React.Component {
-  constructor (props) {
-    super(props)
-    this.uppy = Uppy()
-      .use(Transloadit, {})
-  }
-
-  componentWillUnmount () {
-    this.uppy.close()
-  }
-
-  render () {
-    return <Dashboard uppy={this.uppy} />
-  }
-}
-```
-
 ## Props
 
-The `<Dashboard />` component supports all [`@uppy/dashboard`][] options as props.
+The `<Dashboard />` component supports all [`@uppy/dashboard`][] options as props. Additionally, an Uppy instance must be provided in the `uppy={}` prop: see [Initializing Uppy](/docs/react/initializing) for details.
 
 The `<Dashboard />` cannot be passed to a `target:` option of a remote provider or plugins such as [`@uppy/webcam`][]. To use other plugins like [`@uppy/webcam`][] with the `<Dashboard />` component, first add them to the Uppy instance, and then specify their `id` in the [`plugins`](/docs/dashboard/#plugins) prop:
 
 ```js
-// Do this wherever you initialize Uppy, e.g., in a React component's constructor method.
-// Do NOT do it in `render()` or any other method that is called more than once!
-uppy.use(Webcam) // `id` defaults to "Webcam"
-uppy.use(Webcam, { id: 'MyWebcam' }) // `id` is… "MyWebcam"
-```
-
-Then add the following to `render()`:
-
-```js
-<Dashboard
-  plugins={['Webcam']}
-  {...props}
-/>
+function Uploader () {
+  const uppy = React.useMemo(() => {
+    return Uppy()
+      .use(Webcam) // `id` defaults to "Webcam"
+      // or
+      .use(Webcam, { id: 'MyWebcam' }) // `id` is… "MyWebcam"
+  }, [])
+  React.useEffect(() => {
+    return () => uppy.close()
+  }, [])
+
+  return (
+    <Dashboard
+      uppy={uppy}
+      plugins={['Webcam']}
+      {...props}
+    />
+  )
+}
 ```
 
 [`@uppy/dashboard`]: /docs/dashboard/

+ 2 - 28
website/src/docs/react-dragdrop.md

@@ -4,7 +4,7 @@ type: docs
 module: "@uppy/react"
 permalink: docs/react/drag-drop/
 alias: docs/react/dragdrop/
-order: 2
+order: 3
 category: "React"
 ---
 
@@ -36,35 +36,9 @@ import '@uppy/drag-drop/dist/style.css'
 
 Import general Core styles from `@uppy/core/dist/style.css` first, then add the Drag & Drop styles from `@uppy/drag-drop/dist/style.css`. A minified version is also available as `style.min.css` at the same path. The way to do import depends on your build system.
 
-## Initializing Uppy
-
-Your Uppy instance must be initialized before passing it to an `uppy={}` prop, and should be cleaned up using `uppy.close()` when you are done with it. A simple approach is to initialize it in your React component's `constructor()` and destroy it in `componentWillUnmount()`.
-
-> ⚠ Uppy instances are stateful, so the same instance must be used across different renders.
-> Do **NOT** initialize Uppy in a `render()` method!
-> Do **NOT** initialize Uppy in a function component!
-
-```js
-class MyComponent extends React.Component {
-  constructor (props) {
-    super(props)
-    this.uppy = Uppy()
-      .use(Transloadit, {})
-  }
-
-  componentWillUnmount () {
-    this.uppy.close()
-  }
-
-  render () {
-    return <DragDrop uppy={this.uppy} />
-  }
-}
-```
-
 ## Props
 
-The `<DragDrop />` component supports all [DragDrop](/docs/drag-drop/) options as props.
+The `<DragDrop />` component supports all [DragDrop](/docs/drag-drop/) options as props. Additionally, an Uppy instance must be provided in the `uppy={}` prop: see [Initializing Uppy](/docs/react/initializing) for details.
 
 ```js
 // assuming `this.uppy` contains an Uppy instance:

+ 73 - 0
website/src/docs/react-initializing.md

@@ -0,0 +1,73 @@
+---
+title: "Initializing Uppy"
+type: docs
+module: "@uppy/react"
+permalink: docs/react/initializing/
+alias: docs/react/initializing/
+order: 1
+category: "React"
+---
+
+When using Uppy's React components, an Uppy instance must be passed in to the `uppy={}` prop from the outside. This Uppy instance must be initialized before passing it to the desired component, and be cleaned up using `uppy.close()` when you are done with it.
+
+## Functional Components
+
+With React Hooks, the `useMemo` hook can be used to create an instance once and remember it for all rerenders. The `useEffect` hook can close the Uppy instance when the component unmounts.
+
+```js
+const MyComponent = () => {
+  const uppy = React.useMemo(() => {
+    // Do all the configuration here
+    return Uppy()
+      .use(Transloadit, {})
+  }, []);
+
+  React.useEffect(() => {
+    return () => uppy.close()
+  }, [])
+
+  return <DashboardModal uppy={uppy} />
+}
+```
+
+Both hooks must receive the `[]` dependency array parameter, or the Uppy instance will be recreated and/or destroyed every time the component rerenders. To make sure you never forget that, a custom hook could be used:
+```js
+function useUppy (factory) {
+  const uppy = React.useMemo(factory, [])
+  React.useEffect(() => {
+    return () => uppy.close()
+  }, [])
+  return uppy
+}
+
+// Then use it as:
+const uppy = useUppy(() => {
+  return Uppy()
+    .use(Tus, {})
+})
+```
+
+## Class Components
+
+A simple approach is to initialize it in your React component's `constructor()` and destroy it in `componentWillUnmount()`.
+
+> ⚠ Uppy instances are stateful, so the same instance must be used across different renders.
+> Do **NOT** initialize Uppy in a `render()` method!
+
+```js
+class MyComponent extends React.Component {
+  constructor (props) {
+    super(props)
+    this.uppy = Uppy()
+      .use(Transloadit, {})
+  }
+
+  componentWillUnmount () {
+    this.uppy.close()
+  }
+
+  render () {
+    return <DashboardModal uppy={this.uppy} />
+  }
+}
+```

+ 1 - 46
website/src/docs/react-native.md

@@ -3,7 +3,7 @@ title: "React Native"
 type: docs
 module: "@uppy/react-native"
 permalink: docs/react/native/
-order: 7
+order: 8
 category: "React"
 ---
 
@@ -35,51 +35,6 @@ render () {
 }
 ```
 
-## Initializing Uppy
-
-Your Uppy instance must be initialized before passing it to an `uppy={}` prop, and should be cleaned up using `uppy.close()` when you are done with it. A simple approach is to initialize it in your React component's `constructor()` and destroy it in `componentWillUnmount()`.
-
-> ⚠ Uppy instances are stateful, so the same instance must be used across different renders.
-> Do **NOT** initialize Uppy in a `render()` method!
-> Do **NOT** initialize Uppy in a function component!
-
-```js
-class MyComponent extends React.Component {
-  constructor (props) {
-    super(props)
-    this.state = {
-      isFilePickerVisible: false
-    }
-    this.uppy = Uppy()
-      .use(Transloadit, {})
-  }
-
-  componentWillUnmount () {
-    this.uppy.close()
-  }
-
-  showFilePicker () {
-    this.setState({
-      isFilePickerVisible: true
-    })
-  }
-
-  hideFilePicker () {
-    this.setState({
-      isFilePickerVisible: false
-    })
-  }
-
-  render () {
-    return <UppyFilePicker
-      show={this.state.isFilePickerVisible}
-      uppy={this.uppy}
-      onRequestClose={this.hideFilePicker}
-      companionUrl="https://companion.uppy.io" />
-  }
-}
-```
-
 ## Props
 
 The `<UppyFilePicker>` component supports the following props:

+ 3 - 28
website/src/docs/react-progressbar.md

@@ -4,7 +4,7 @@ type: docs
 module: "@uppy/react"
 permalink: docs/react/progress-bar/
 alias: docs/react/progressbar/
-order: 3
+order: 4
 category: "React"
 ---
 
@@ -36,38 +36,13 @@ import '@uppy/progress-bar/dist/style.css'
 
 Import general Core styles from `@uppy/core/dist/style.css` first, then add the Progress Bar styles from `@uppy/progress-bar/dist/style.css`. A minified version is also available as `style.min.css` at the same path. The way to do import depends on your build system.
 
-## Initializing Uppy
-
-Your Uppy instance must be initialized before passing it to an `uppy={}` prop, and should be cleaned up using `uppy.close()` when you are done with it. A simple approach is to initialize it in your React component's `constructor()` and destroy it in `componentWillUnmount()`.
-
-> ⚠ Uppy instances are stateful, so the same instance must be used across different renders.
-> Do **NOT** initialize Uppy in a `render()` method!
-> Do **NOT** initialize Uppy in a function component!
-
-```js
-class MyComponent extends React.Component {
-  constructor (props) {
-    super(props)
-    this.uppy = Uppy()
-      .use(Transloadit, {})
-  }
-
-  componentWillUnmount () {
-    this.uppy.close()
-  }
-
-  render () {
-    return <ProgressBar uppy={this.uppy} />
-  }
-}
-```
-
 ## Props
 
-The `<ProgressBar />` component supports all [`@uppy/progress-bar`][] options as props.
+The `<ProgressBar />` component supports all [`@uppy/progress-bar`][] options as props. Additionally, an Uppy instance must be provided in the `uppy={}` prop: see [Initializing Uppy](/docs/react/initializing) for details.
 
 ```js
 <ProgressBar
+  uppy={uppy}
   fixed
   hideAfterFinish
 />

+ 3 - 28
website/src/docs/react-statusbar.md

@@ -4,7 +4,7 @@ type: docs
 module: "@uppy/react"
 permalink: docs/react/status-bar/
 alias: docs/react/statusbar/
-order: 1
+order: 2
 category: "React"
 ---
 
@@ -36,38 +36,13 @@ import '@uppy/status-bar/dist/style.css'
 
 Import general Core styles from `@uppy/core/dist/style.css` first, then add the Status Bar styles from `@uppy/status-bar/dist/style.css`. A minified version is also available as `style.min.css` at the same path. The way to do import depends on your build system.
 
-## Initializing Uppy
-
-Your Uppy instance must be initialized before passing it to an `uppy={}` prop, and should be cleaned up using `uppy.close()` when you are done with it. A simple approach is to initialize it in your React component's `constructor()` and destroy it in `componentWillUnmount()`.
-
-> ⚠ Uppy instances are stateful, so the same instance must be used across different renders.
-> Do **NOT** initialize Uppy in a `render()` method!
-> Do **NOT** initialize Uppy in a function component!
-
-```js
-class MyComponent extends React.Component {
-  constructor (props) {
-    super(props)
-    this.uppy = Uppy()
-      .use(Transloadit, {})
-  }
-
-  componentWillUnmount () {
-    this.uppy.close()
-  }
-
-  render () {
-    return <StatusBar uppy={this.uppy} />
-  }
-}
-```
-
 ## Props
 
-The `<StatusBar />` component supports all [`@uppy/status-bar`][] options as props.
+The `<StatusBar />` component supports all [`@uppy/status-bar`][] options as props. Additionally, an Uppy instance must be provided in the `uppy={}` prop: see [Initializing Uppy](/docs/react/initializing) for details.
 
 ```js
 <StatusBar
+  uppy={uppy}
   hideUploadButton
   hideAfterFinish={false}
   showProgressDetails

+ 4 - 4
website/src/docs/robodog-form.md

@@ -128,7 +128,7 @@ $(selector).transloadit({
 ```
 ```html
 <!-- The new Robodog way! -->
-<script src="//transloadit.edgly.net/releases/uppy/robodog/v1.7.1/robodog.min.js"></script>
+<script src="//transloadit.edgly.net/releases/uppy/robodog/v1.7.2/robodog.min.js"></script>
 
 <script>
 window.Robodog.form(selector, {
@@ -140,7 +140,7 @@ window.Robodog.form(selector, {
 Make sure to also include the Uppy css file in your `<head>` tag in case you want to use the `modal: true` option:
 ```html
 <head>
-  <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.1/robodog.min.css">
+  <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.2/robodog.min.css">
 </head>
 ```
 
@@ -152,7 +152,7 @@ Notice how the form is submitted to the inexistant `/uploads` route once all tra
 <html>
   <head>
     <title>Testing Robodog</title>
-    <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.1/robodog.min.css">
+    <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.2/robodog.min.css">
   </head>
   <body>
     <form id="upload-form" action="/uploads" enctype="multipart/form-data" method="POST">
@@ -162,7 +162,7 @@ Notice how the form is submitted to the inexistant `/uploads` route once all tra
       <button type="submit">Upload</button>
     </form>
 
-    <script src="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.1/robodog.min.js"></script>
+    <script src="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.2/robodog.min.js"></script>
     <script type="text/javascript">
     window.Robodog.form('#upload-form', {
       waitForEncoding: true,

+ 2 - 2
website/src/docs/robodog.md

@@ -31,8 +31,8 @@ require('@uppy/robodog/dist/robodog.css')
 If you are not using a bundler, you can also import Robodog using an HTML script tag.
 
 ```html
-<link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.1/robodog.min.css">
-<script src="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.1/robodog.min.js"></script>
+<link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.2/robodog.min.css">
+<script src="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.2/robodog.min.js"></script>
 <!-- you can now use: window.Robodog.pick() -->
 ```
 

+ 1 - 1
website/src/docs/uppy.md

@@ -523,7 +523,7 @@ const updatedFile = Object.assign({}, updatedFiles[fileID], {
     bytesTotal: data.bytesTotal,
     percentage: Math.floor((data.bytesUploaded / data.bytesTotal * 100).toFixed(2))
   })
-))
+})
 updatedFiles[data.id] = updatedFile
 uppy.setState({files: updatedFiles})
 ```

+ 1 - 1
website/src/examples/dashboard/app.es6

@@ -170,7 +170,7 @@ function loadLocaleFromCDN (localeName) {
   var head = document.getElementsByTagName('head')[0]
   var js = document.createElement('script')
   js.type = 'text/javascript'
-  js.src = `https://transloadit.edgly.net/releases/uppy/locales/v1.15.0/${localeName}.min.js`
+  js.src = `https://transloadit.edgly.net/releases/uppy/locales/v1.15.1/${localeName}.min.js`
 
   head.appendChild(js)
 }

+ 3 - 3
website/src/examples/i18n/app.html

@@ -1,7 +1,7 @@
 <!-- Load Uppy CSS bundle. It is advisable to install Uppy 
   from npm/yarn instead, and pick and choose the plugins/styles you need.
   But for experimenting, you can use Transloadit’s CDN, Edgly: -->
-<link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.css">
+<link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.css">
 
 <div class="UppyDragDrop"></div>
 <div class="for-ProgressBar"></div>
@@ -12,8 +12,8 @@
 </div>
 
 <!-- Load Uppy JS bundle. -->
-<script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
-<script src="https://transloadit.edgly.net/releases/uppy/locales/v1.15.0/ru_RU.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/locales/v1.15.1/ru_RU.min.js"></script>
 <script>
   var uppy = Uppy.Core({ 
     debug: true, 

+ 1 - 1
website/src/examples/markdown-snippets/app.es6

@@ -3,7 +3,7 @@ const marked = require('marked')
 const dragdrop = require('drag-drop')
 // Add Robodog JS. It is advisable to install Robodog from npm/yarn.
 // But for experimenting, you can use also Transloadit’s CDN, Edgly:
-// <script src="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.1/robodog.min.js"></script>
+// <script src="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.2/robodog.min.js"></script>
 const robodog = require('@uppy/robodog')
 
 const TRANSLOADIT_EXAMPLE_KEY = '35c1aed03f5011e982b6afe82599b6a0'

+ 1 - 1
website/src/examples/markdown-snippets/app.html

@@ -1,6 +1,6 @@
 <!-- Add Robodog styles. It is advisable to install Robodog from npm/yarn.
   But for experimenting, you can use also Transloadit’s CDN, Edgly:
-  <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.1/robodog.min.css"> -->
+  <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/robodog/v1.7.2/robodog.min.css"> -->
   <link rel="stylesheet" href="/uppy/robodog.min.css">
   <form id="new" class="form-snippet">
     <h2>Create a new snippet</h2>

binární
website/src/images/blog/custom-providers/dashboard-preview.png


binární
website/src/images/blog/custom-providers/unsplash-api-dashboard.png


binární
website/src/images/blog/custom-providers/unsplashicon.png


+ 2 - 2
website/themes/uppy/layout/index.ejs

@@ -167,8 +167,8 @@
   <p>© <%- date(Date.now(), 'YYYY') %> <a href="https://transloadit.com" target="_blank">Transloadit</a></p>
 </footer>
 
-<link href="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.css" rel="stylesheet">
-<script src="https://transloadit.edgly.net/releases/uppy/v1.16.1/uppy.min.js"></script>
+<link href="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.css" rel="stylesheet">
+<script src="https://transloadit.edgly.net/releases/uppy/v1.17.0/uppy.min.js"></script>
 
 <script>
   var TUS_ENDPOINT = 'https://master.tus.io/files/'

+ 10 - 5
website/themes/uppy/layout/layout.ejs

@@ -39,11 +39,16 @@ if (page.series) {
   </head>
   <body class="<%= isIndex ? 'page-index' : 'page-inner' %> <%= page.body_class %>">
     <div class="page">
-      <div class="TransloaditBar">
-        <a class="TransloaditBar-logo" href="https://transloadit.com/" target="_blank" rel="noopener noreferrer">
-          <img class="TransloaditBar-logoImg" src="/images/transloadit.svg" width="82" height="19" alt="Transloadit">
-        </a>
-        <div class="TransloaditBar-about">Uppy is an open source project by <a class="TransloaditBar-link" href="https://transloadit.com/" target="_blank">Transloadit</a></div>
+      <div class="TransloaditBar js-TransloaditBar">
+        <div class="TransloaditBar-about">
+          <a class="TransloaditBar-logo" href="https://transloadit.com/" target="_blank" rel="noopener noreferrer">
+            <img class="TransloaditBar-logoImg" src="/images/transloadit-white-glyph.svg" width="20" height="20" alt="Transloadit">
+          </a>
+          <span>Uppy is an open source project by <a class="TransloaditBar-link" href="https://transloadit.com/" target="_blank">Transloadit</a></span>
+        </div>
+        <div class="TransloaditBar-info">
+          <b>New: </b><a href="https://transloadit.com/blog/2020/07/community-plan/" class="TransloaditBar-info-link">Transloadit Community Plan</a> is now free and includes hosted Companion &amp; tusd with unlimited uploading and 5GB/month encoding traffic!
+        </div>
       </div>
 
       <header id="header" class="MainHeader js-MainHeader">

+ 74 - 9
website/themes/uppy/source/css/_common.scss

@@ -174,7 +174,7 @@ hr {
   padding: 2em 1.4em 0;
 
   @media #{$screen-medium} {
-    padding: 60px 60px 30px;
+    padding: 0 60px 30px;
   }
 }
 
@@ -252,29 +252,94 @@ hr {
   // }
 }
 
+/**
+* TransloaditBar
+*/
+
+.TransloaditBar {
+  display: block;
+  padding: 5px 10px 8px;
+  background: $color-darkblue;
+  color: #fff;
+  font-size: 13px;
+  position: relative;
+  z-index: 11;
+  
+  @media #{$screen-medium} {
+    display: flex;
+    height: $size-transloaditBarHeight;
+    padding: 0 60px;
+    justify-content: space-between;
+    align-items: center;
+  }
+
+  &-link {
+    color: #fff;
+    font-weight: 700;
+
+    &:hover {
+      text-decoration: underline;
+    }
+  }
+
+  &-logo {
+    margin-right: 10px;
+  }
+
+  &-logoImg {
+		display: block;
+	}
+
+  &-about {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  &-info {
+    margin-top: 10px;
+    text-align: center;
+
+    @media #{$screen-medium} {
+      text-align: right;
+      margin-top: 0;
+    }
+  }
+
+  &-info-link {
+    color: #b3cbf2;
+    font-weight: 600;
+    
+    &:hover {
+      color: #b3cbf2;
+      text-decoration: underline;
+    }
+  }
+}
+
 /**
 * MainHeader
 */
 
 .MainHeader {
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100%;
-  z-index: 9;
   z-index: 2;
   padding-top: 15px;
   padding-bottom: 15px;
   background-color: $color-white;
   text-align: center;
-
+  position: relative;
+  border-bottom: 1px solid rgba($color-lightgray, 0.2);
+  
   @media #{$screen-medium} {
-    height: auto;
+    position: sticky;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: $size-headerHeight;
     display: flex;
     align-items: baseline;
     padding: 14px 60px 19px;
     z-index: 2;
-    border-bottom: 1px solid rgba($color-lightgray, 0.2);
     text-align: left;
 
     // &.fix-header {

+ 0 - 41
website/themes/uppy/source/css/_index.scss

@@ -1,44 +1,3 @@
-.TransloaditBar {
-  display: none;
-  height: 29px;
-  padding: 0 10px;
-  background: $color-darkblue;
-  color: #fff;
-  font-size: 11px;
-  justify-content: space-between;
-  align-items: center;
-
-  @media #{$screen-medium} {
-    padding: 0 60px;
-  }
-
-  &-link {
-    color: #fff;
-    font-weight: 700;
-
-    &:hover {
-      text-decoration: underline;
-    }
-  }
-
-  &-logo {
-    margin-right: 10px;
-  }
-
-  &-logoImg {
-		display: block;
-	}
-
-  &-about {
-    flex: 1;
-    text-align: right;
-  }
-
-  .page-index & {
-    display: flex;
-  }
-}
-
 .IndexHero {
   text-align: center;
   max-width: 680px;

+ 10 - 9
website/themes/uppy/source/css/_page.scss

@@ -13,7 +13,7 @@
   top: 0;
   right: 0;
   left: 0;
-  padding: 80px 30px 20px;
+  padding: 160px 30px 20px;
   transition: all .4s cubic-bezier(0.4, 0, 0, 1);
   transform: translate(-100%, 0);
   z-index: 10;
@@ -29,26 +29,26 @@
   // .fix-sidebar & { position: fixed; }
 
   @media #{$screen-medium} {
-    // position: absolute;
-    top: 0;
+    top: $size-headerHeight + $size-transloaditBarHeight;
     left: 0;
     right: auto;
     width: 280px;
-    // bottom: 0;
-    // padding: 2.2em 0;
-    padding-top: 95px;
+    padding-top: 30px;
     padding-left: 60px;
-    padding-bottom: 130px;
-    // width: 20px;
     margin-right: 20px;
     overflow-x: hidden;
     overflow-y: auto;
     -webkit-overflow-scrolling: touch;
     -ms-overflow-style: none;
+    transition: none;
     transform: none;
     background-color: transparent;
     box-shadow: none;
 
+    .list {
+      padding-bottom: 100px;
+    }
+
     .main-menu { display: none; }
     &::-webkit-scrollbar { width: 0 !important; }
     h2 { margin-top: .2em; }
@@ -137,12 +137,13 @@
 * Content
 */
 .Content {
-  padding: 2.2em 0;
+  padding: 0 0 2.2em;
   max-width: 600px;
   margin: 0 auto;
   font-size: 18px;
 
   @media #{$screen-medium} {
+    padding: 2.2em 0;
     &.with-sidebar {
       margin-left: 280px;
     }

+ 2 - 1
website/themes/uppy/source/css/_settings.scss

@@ -35,7 +35,8 @@ $color-primary    : $color-pink;
 
 // sizes
 $size-radius : 2px;
-$size-headerHeight: 40px;
+$size-headerHeight: 70px;
+$size-transloaditBarHeight: 37px;
 
 // Media queries
 

+ 1 - 0
website/themes/uppy/source/images/transloadit-white-glyph.svg

@@ -0,0 +1 @@
+<svg width="54" height="54" viewBox="0 0 54 54" xmlns="http://www.w3.org/2000/svg"><g fill="#FFFFFE" fill-rule="nonzero"><path d="M52.518 20.13c.656 3.56.908 12.505.908 21.278C53.426 49.028 40.005 54 26.713 54 13.414 54 0 49.027 0 41.408c0-8.773.245-17.719.908-21.277.613-3.323 14.678-5.778 23.885-6.094V7.662A3.999 3.999 0 0122.419 4C22.419 1.792 24.192 0 26.376 0c2.189 0 3.962 1.792 3.962 4 0 1.642-.981 3.05-2.38 3.662v6.356c9.176.217 23.928 2.709 24.56 6.113zM33.552 50.287c.767-.415 1.048-1.388.638-2.163a1.571 1.571 0 00-2.147-.645c-.024.006-2.116 1.103-5.349 1.103-3.202 0-5.256-1.078-5.311-1.103a1.57 1.57 0 00-2.141.633 1.61 1.61 0 00.625 2.175c.11.062 2.742 1.494 6.827 1.494 4.08 0 6.742-1.432 6.858-1.494zM5.772 37.1c0 4.973 3.987 9.002 8.9 9.002 4.92 0 8.907-4.03 8.907-9.002 0-4.971-3.987-9.002-8.907-9.002-4.913 0-8.9 4.03-8.9 9.002zm32.975 9.002c4.92 0 8.907-4.03 8.907-9.002 0-4.971-3.987-9.002-8.907-9.002s-8.906 4.03-8.906 9.002c0 4.973 3.987 9.002 8.906 9.002z"/><path d="M16.653 41.099a1.994 1.994 0 01-1.98-1.997c0-1.104.89-2.003 1.98-2.003 1.093 0 1.982.899 1.982 2.003a1.993 1.993 0 01-1.982 1.997zm-1.98-10c-3.27 0-5.932 2.69-5.932 6 0 3.311 2.662 6.002 5.931 6.002 3.275 0 5.938-2.691 5.938-6.002 0-3.31-2.663-6-5.938-6zM40.724 41.099c-1.09 0-1.98-.9-1.98-1.997 0-1.104.89-2.003 1.98-2.003 1.092 0 1.98.899 1.98 2.003a1.993 1.993 0 01-1.98 1.997zm-1.98-10c-3.268 0-5.929 2.69-5.929 6 0 3.311 2.66 6.002 5.929 6.002 3.274 0 5.936-2.691 5.936-6.002 0-3.31-2.662-6-5.936-6z"/></g></svg>

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů