Browse Source

Merge branch 'monorepo'

Michael Bromley 6 years ago
parent
commit
5b71bceff0
100 changed files with 3723 additions and 1151 deletions
  1. 22 31
      .gitignore
  2. 2 2
      .lintstagedrc.yml
  3. 5 4
      .travis.yml
  4. 1 1
      CONTRIBUTING.md
  5. 27 112
      README.md
  6. 1 1
      admin-ui/src/environments/environment.prod.ts
  7. 1 1
      admin-ui/src/environments/environment.ts
  8. 1 1
      admin-ui/tsconfig.json
  9. 0 587
      codegen/generate-config-docs.ts
  10. 6 0
      docs/assets/styles/_shortcodes.scss
  11. 3 1
      docs/assets/styles/main.scss
  12. 0 26
      docs/content/docs/configuration/_index.md
  13. 1 1
      docs/content/docs/plugins/_index.md
  14. 0 19
      docs/content/docs/plugins/admin-ui-plugin.md
  15. 0 67
      docs/content/docs/plugins/default-asset-server-plugin.md
  16. 0 67
      docs/content/docs/plugins/default-email-plugin.md
  17. 13 0
      docs/content/docs/typescript-api/_index.md
  18. 2 1
      docs/layouts/docs/baseof.html
  19. 2 1
      docs/layouts/shortcodes/generation-info.html
  20. 8 0
      lerna.json
  21. 22 10
      package.json
  22. 1 0
      packages/admin-ui-plugin/.gitignore
  23. 0 0
      packages/admin-ui-plugin/.npmignore
  24. 5 0
      packages/admin-ui-plugin/README.md
  25. 21 0
      packages/admin-ui-plugin/build.js
  26. 1 0
      packages/admin-ui-plugin/index.ts
  27. 29 0
      packages/admin-ui-plugin/package.json
  28. 46 15
      packages/admin-ui-plugin/src/plugin.ts
  29. 9 0
      packages/admin-ui-plugin/tsconfig.build.json
  30. 11 0
      packages/admin-ui-plugin/tsconfig.json
  31. 1207 0
      packages/admin-ui-plugin/yarn.lock
  32. 1 0
      packages/asset-server-plugin/.gitignore
  33. 0 0
      packages/asset-server-plugin/.npmignore
  34. 7 0
      packages/asset-server-plugin/README.md
  35. 2 0
      packages/asset-server-plugin/index.ts
  36. 30 0
      packages/asset-server-plugin/package.json
  37. 0 0
      packages/asset-server-plugin/src/file-icon.png
  38. 0 0
      packages/asset-server-plugin/src/file-icon.psd
  39. 116 23
      packages/asset-server-plugin/src/plugin.ts
  40. 3 5
      packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts
  41. 1 1
      packages/asset-server-plugin/src/transform-image.ts
  42. 9 0
      packages/asset-server-plugin/tsconfig.build.json
  43. 11 0
      packages/asset-server-plugin/tsconfig.json
  44. 1755 0
      packages/asset-server-plugin/yarn.lock
  45. 1 0
      packages/common/.gitignore
  46. 0 0
      packages/common/.npmignore
  47. 3 0
      packages/common/README.md
  48. 13 0
      packages/common/jest.config.js
  49. 17 0
      packages/common/package.json
  50. 0 0
      packages/common/src/filter-async.spec.ts
  51. 0 0
      packages/common/src/filter-async.ts
  52. 3 2
      packages/common/src/generated-shop-types.ts
  53. 3 3
      packages/common/src/generated-types.ts
  54. 0 0
      packages/common/src/normalize-string.spec.ts
  55. 0 0
      packages/common/src/normalize-string.ts
  56. 0 0
      packages/common/src/omit.spec.ts
  57. 1 1
      packages/common/src/omit.ts
  58. 0 0
      packages/common/src/pick.spec.ts
  59. 0 0
      packages/common/src/pick.ts
  60. 0 0
      packages/common/src/shared-constants.ts
  61. 18 1
      packages/common/src/shared-types.ts
  62. 0 2
      packages/common/src/shared-utils.spec.ts
  63. 0 0
      packages/common/src/shared-utils.ts
  64. 0 0
      packages/common/src/simple-deep-clone.ts
  65. 0 0
      packages/common/src/unique.spec.ts
  66. 0 0
      packages/common/src/unique.ts
  67. 12 0
      packages/common/tsconfig.build.json
  68. 11 0
      packages/common/tsconfig.json
  69. 5 0
      packages/common/tslint.json
  70. 99 0
      packages/common/yarn.lock
  71. 5 0
      packages/core/.gitignore
  72. 1 1
      packages/core/README.md
  73. 17 0
      packages/core/build/gulpfile.ts
  74. 3 2
      packages/core/build/tsconfig.build.json
  75. 3 3
      packages/core/build/tsconfig.cli.json
  76. 0 0
      packages/core/cli/cli-utils.ts
  77. 38 25
      packages/core/cli/populate.ts
  78. 1 27
      packages/core/cli/vendure-cli.ts
  79. 0 0
      packages/core/e2e/__data__/.gitkeep
  80. 0 0
      packages/core/e2e/__snapshots__/administrator.e2e-spec.ts.snap
  81. 0 0
      packages/core/e2e/__snapshots__/collection.e2e-spec.ts.snap
  82. 0 0
      packages/core/e2e/__snapshots__/facet.e2e-spec.ts.snap
  83. 0 0
      packages/core/e2e/__snapshots__/import.e2e-spec.ts.snap
  84. 0 0
      packages/core/e2e/__snapshots__/product.e2e-spec.ts.snap
  85. 0 0
      packages/core/e2e/__snapshots__/promotion.e2e-spec.ts.snap
  86. 0 0
      packages/core/e2e/__snapshots__/role.e2e-spec.ts.snap
  87. 9 9
      packages/core/e2e/administrator.e2e-spec.ts
  88. 12 12
      packages/core/e2e/auth.e2e-spec.ts
  89. 21 25
      packages/core/e2e/collection.e2e-spec.ts
  90. 0 3
      packages/core/e2e/config/jest-e2e.json
  91. 1 7
      packages/core/e2e/config/test-config.ts
  92. 0 0
      packages/core/e2e/config/testing-asset-preview-strategy.ts
  93. 0 0
      packages/core/e2e/config/testing-asset-storage-strategy.ts
  94. 0 0
      packages/core/e2e/config/testing-entity-id-strategy.ts
  95. 2 1
      packages/core/e2e/config/tsconfig.e2e.json
  96. 9 9
      packages/core/e2e/country.e2e-spec.ts
  97. 4 12
      packages/core/e2e/customer.e2e-spec.ts
  98. 10 10
      packages/core/e2e/default-search-plugin.e2e-spec.ts
  99. 2 2
      packages/core/e2e/entity-id-strategy.e2e-spec.ts
  100. 17 17
      packages/core/e2e/facet.e2e-spec.ts

+ 22 - 31
.gitignore

@@ -7,6 +7,27 @@
 ../.idea/workspace.xml
 ../.idea/workspace.xml
 .idea/**/tasks.xml
 .idea/**/tasks.xml
 .idea/dictionaries
 .idea/dictionaries
+docker-compose.yml
+.env
+lerna-debug.log
+dist
+e2e/__data__/*
+docs/resources/_gen/*
+docs/static/main.js*
+docs/static/main.css*
+docs/static/intro.js*
+docs/static/intro.css*
+docs/public
+docs/content/docs/typescript-api/*
+!docs/content/docs/typescript-api/_index.md
+docs/content/docs/graphql-api/*
+docs/content/docs/plugins/*
+!docs/content/docs/plugins/*.md
+!docs/content/docs/graphql-api/_index.md
+!docs/content/docs/graphql-api/shop/_index.md
+!docs/content/docs/graphql-api/admin/_index.md
+.vscode/
+yarn-error.log
 
 
 # Sensitive or high-churn files:
 # Sensitive or high-churn files:
 .idea/**/dataSources/
 .idea/**/dataSources/
@@ -120,7 +141,7 @@ artifacts/
 *.tlh
 *.tlh
 *.tmp
 *.tmp
 *.tmp_proj
 *.tmp_proj
-server/yarn-error.log
+packages/core/yarn-error.log
 *.vspscc
 *.vspscc
 *.vssscc
 *.vssscc
 .builds
 .builds
@@ -222,10 +243,6 @@ PublishScripts/
 
 
 # NuGet Packages
 # NuGet Packages
 *.nupkg
 *.nupkg
-# The packages folder can be ignored because of Package Restore
-**/[Pp]ackages/*
-# except build/, which is used as an MSBuild target.
-!**/[Pp]ackages/build/
 # Uncomment if necessary however generally it will be regenerated when needed
 # Uncomment if necessary however generally it will be regenerated when needed
 #!**/[Pp]ackages/repositories.config
 #!**/[Pp]ackages/repositories.config
 # NuGet v3's project.json files produces more ignorable files
 # NuGet v3's project.json files produces more ignorable files
@@ -381,29 +398,3 @@ Icon
 Network Trash Folder
 Network Trash Folder
 Temporary Items
 Temporary Items
 .apdisk
 .apdisk
-
-=======
-# Local
-docker-compose.yml
-.env
-dist
-server/e2e/__data__/*
-server/assets
-server/dist
-server/*.sqlite
-!server/e2e/__data__/.gitkeep
-server/test-emails
-server/src/email/preview/output
-docs/resources/_gen/*
-docs/static/main.js*
-docs/static/main.css*
-docs/static/intro.js*
-docs/static/intro.css*
-docs/public
-docs/content/docs/configuration/*
-!docs/content/docs/configuration/_index.md
-docs/content/docs/graphql-api/*
-!docs/content/docs/graphql-api/_index.md
-!docs/content/docs/graphql-api/shop/_index.md
-!docs/content/docs/graphql-api/admin/_index.md
-.vscode/

+ 2 - 2
.lintstagedrc.yml

@@ -6,7 +6,7 @@ linters:
   admin-ui/**/*.html:
   admin-ui/**/*.html:
     - yarn format
     - yarn format
     - git add
     - git add
-  server/**/*.ts:
-    - yarn lint:server
+  packages/core/*.ts:
+    - yarn lint:core
     - yarn format
     - yarn format
     - git add
     - git add

+ 5 - 4
.travis.yml

@@ -12,9 +12,10 @@ cache: yarn
 
 
 install:
 install:
   - yarn install
   - yarn install
+  - yarn bootstrap
+  - cd admin-ui && yarn install && cd ..
 
 
 script:
 script:
-  - cd admin-ui && yarn && yarn test --watch=false --browsers=ChromeHeadlessCI --progress=false
-  - cd ../server && yarn && yarn test
-  - yarn test:e2e
-  - cd ../admin-ui && yarn build --prod
+  - yarn lerna run build
+  - yarn test:all
+  - cd admin-ui && yarn build --prod

+ 1 - 1
CONTRIBUTING.md

@@ -22,4 +22,4 @@ type(scope): Message in present tense
 * **test** (adding missing tests, refactoring tests; no production code change)
 * **test** (adding missing tests, refactoring tests; no production code change)
 * **chore** (updating build tasks etc; no production code change)
 * **chore** (updating build tasks etc; no production code change)
 
 
-`scope` indicates the module affected (server, admin-ui, docs etc.)
+`scope` indicates the package (core, asset-server-plugin, docs etc.)

+ 27 - 112
README.md

@@ -1,25 +1,23 @@
-# Vendure [![Build Status](https://travis-ci.org/vendure-ecommerce/vendure.svg?branch=master)](https://travis-ci.org/vendure-ecommerce/vendure)
+# Vendure
 
 
 A headless [GraphQL](https://graphql.org/) ecommerce framework built on [Node.js](https://nodejs.org) with [Nest](https://nestjs.com/) with [TypeScript](http://www.typescriptlang.org/).
 A headless [GraphQL](https://graphql.org/) ecommerce framework built on [Node.js](https://nodejs.org) with [Nest](https://nestjs.com/) with [TypeScript](http://www.typescriptlang.org/).
 
 
+[![Build Status](https://travis-ci.org/vendure-ecommerce/vendure.svg?branch=master)](https://travis-ci.org/vendure-ecommerce/vendure) [![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/)
+
 ### [www.vendure.io](https://www.vendure.io/)
 ### [www.vendure.io](https://www.vendure.io/)
 
 
 ## Structure
 ## Structure
 
 
-Vendure is a headless framework, which means that it is just an API serving JSON via a GraphQL endpoint. The code for
-the server is located in the `server` directory.
-
-We ship with an administration UI which is a stand-alone web application which can be used to perform tasks such
-as inventory, order and customer management. The code for this is located in the `admin-ui` directory.
+This project is a monorepo managed with [Lerna](https://github.com/lerna/lerna). Several npm packages are published from this repo, which can be found in the `packages/` directory.
 
 
 ```
 ```
 vendure/
 vendure/
 ├── admin-ui/       # Source of the admin ui app (an Angular CLI project)
 ├── admin-ui/       # Source of the admin ui app (an Angular CLI project)
-├── codegen/        # Scripts used to generate TypeScript code and docs from source
 ├── docs/           # Documentation source
 ├── docs/           # Documentation source
-├── server/         # Source for the Vendure server
-├── shared/         # Types and utils shared by the server & admin ui
-
+├── packages/       # Source for the Vendure server & plugin packages
+├── scripts/
+    ├── codegen/    # Scripts used to generate TypeScript code from the GraphQL APIs
+    ├── docs/       # Scripts used to generate documentation markdown from the source
 ```
 ```
 
 
 ## Development
 ## Development
@@ -34,18 +32,23 @@ The root directory has a `package.json` which contains build-related dependencie
 * Generating TypeScript types from the GraphQL schema
 * Generating TypeScript types from the GraphQL schema
 * Linting, formatting & testing tasks to run on git commit & push
 * Linting, formatting & testing tasks to run on git commit & push
 
 
+### 2. Bootstrap the packages
+
+`yarn bootstrap`
+
+This runs the Lerna "bootstrap" command, which installs dependencies for all packages.
+
 ### 2. Set up the server
 ### 2. Set up the server
 
 
 The server requires an SQL database to be available. I am currently using [bitnami-docker-phpmyadmin](https://github.com/bitnami/bitnami-docker-phpmyadmin) Docker image,
 The server requires an SQL database to be available. I am currently using [bitnami-docker-phpmyadmin](https://github.com/bitnami/bitnami-docker-phpmyadmin) Docker image,
-which is MariaDB including phpMyAdmin.
+which is MariaDB including phpMyAdmin. However, the simplest option is to use SQLite.
 
 
 Vendure uses [TypeORM](http://typeorm.io), so it compatible will any database which works with TypeORM.
 Vendure uses [TypeORM](http://typeorm.io), so it compatible will any database which works with TypeORM.
 
 
-1. Configure the [dev config](./server/dev-config.ts)
+1. Configure the [dev config](./packages/dev-server/dev-config.ts)
 2. Create the database using your DB admin tool of choice (e.g. phpMyAdmin if you are using the docker image suggested above). Name it according to the config ("vendure-dev").
 2. Create the database using your DB admin tool of choice (e.g. phpMyAdmin if you are using the docker image suggested above). Name it according to the config ("vendure-dev").
-3. `cd server && yarn`
-4. Populate mock data with `yarn populate`
-5. `yarn start:dev` to start the server
+3. Populate mock data with `yarn dev-server:populate`
+4. `yarn dev-server` to start the server
 
 
 ### 3. Set up the admin ui
 ### 3. Set up the admin ui
 
 
@@ -58,38 +61,29 @@ Vendure uses [TypeORM](http://typeorm.io), so it compatible will any database wh
 [graphql-code-generator](https://github.com/dotansimha/graphql-code-generator) is used to automatically create TypeScript interfaces
 [graphql-code-generator](https://github.com/dotansimha/graphql-code-generator) is used to automatically create TypeScript interfaces
 for all GraphQL server operations and admin ui queries. These generated interfaces are used in both the admin ui and the server.
 for all GraphQL server operations and admin ui queries. These generated interfaces are used in both the admin ui and the server.
 
 
-Run `yarn generate-gql-types` to generate TypeScript interfaces based on these queries. The generated
-types are located at [`./shared/generated-types.ts`](./shared/generated-types.ts).
+Run `yarn codegen` to generate TypeScript interfaces based on these queries. The generated
+types are located at [`packages/common/src/generated-types.ts`](./packages/common/src/generated-types.ts) & [`packages/common/src/generated-shop-types.ts`](./packages/common/src/generated-shop-types.ts).
 
 
 ### Testing
 ### Testing
 
 
 #### Server Unit Tests
 #### Server Unit Tests
 
 
-The server has unit tests which are run with the following scripts in the `server` directory:
-
-* `yarn test` - Run unit tests once
-* `yarn test:watch` - Run unit tests in watch mode
+The server has unit tests which are run with `yarn test:common` and `yarn test:core`.
 
 
 Unit tests are co-located with the files which they test, and have the suffix `.spec.ts`.
 Unit tests are co-located with the files which they test, and have the suffix `.spec.ts`.
 
 
 #### Server e2e Tests
 #### Server e2e Tests
 
 
 The server has e2e tests which test the API with real http calls and against a real Sqlite database powered by [sql.js](https://github.com/kripken/sql.js/). 
 The server has e2e tests which test the API with real http calls and against a real Sqlite database powered by [sql.js](https://github.com/kripken/sql.js/). 
-The tests are run with the following scripts in the `server` directory:
-
-* `yarn test:e2e` - Run e2e tests once
-* `yarn test:e2e:watch` - Run e2e tests in watch mode
+The tests are run with `yarn test:e2e`
 
 
-The e2e tests are located in [`/server/e2e`](./server/e2e). Each test suite (file) has the suffix `.e2e-spec.ts`. The first time the e2e tests are run,
+The e2e tests are located in [`/packages/core/e2e`](./packages/core/e2e). Each test suite (file) has the suffix `.e2e-spec.ts`. The first time the e2e tests are run,
 sqlite files will be generated in the `__data__` directory. These files are used to speed up subsequent runs of the e2e tests. They can be freely deleted
 sqlite files will be generated in the `__data__` directory. These files are used to speed up subsequent runs of the e2e tests. They can be freely deleted
 and will be re-created the next time the e2e tests are run.
 and will be re-created the next time the e2e tests are run.
 
 
 #### Admin UI Unit Tests
 #### Admin UI Unit Tests
 
 
-The Admin UI has unit tests which are run with the following scripts in the `admin-ui` directory:
-
-* `yarn test --watch=false` - Run unit tests once.
-* `yarn test` - Run unit tests in watch mode.
+The Admin UI has unit tests which are run with `yarn test:admin-ui`.
 
 
 Unit tests are co-located with the files which they test, and have the suffix `.spec.ts`.
 Unit tests are co-located with the files which they test, and have the suffix `.spec.ts`.
 
 
@@ -102,98 +96,19 @@ This can be overridden by appending a `lang` query parameter to the url (e.g. `h
 
 
 All locales in Vendure are represented by 2-character [ISO 639-1 language codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
 All locales in Vendure are represented by 2-character [ISO 639-1 language codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
 
 
-Translations for localized strings are located in the [i18n/messages](./server/src/i18n/messages) directory.
+Translations for localized strings are located in the [i18n/messages](./packages/core/src/i18n/messages) directory.
 
 
 ### Errors
 ### Errors
 
 
-All errors thrown by the Vendure server can be found in the [errors.ts file](./server/src/common/error/errors.ts). 
+All errors thrown by the Vendure server can be found in the [errors.ts file](./packages/core/src/common/error/errors.ts). 
 
 
 All errors extend from `I18nError`, which means that the error messages are localized as described above. Each error type
 All errors extend from `I18nError`, which means that the error messages are localized as described above. Each error type
 has a distinct code which can be used by the front-end client in order to correctly handle the error.
 has a distinct code which can be used by the front-end client in order to correctly handle the error.
 
 
-### Custom Fields
-
-The developer may add custom fields to most of the entities in Vendure, which may contain any data specific to their
-business requirements. For example, attaching extra login data to a User or some boolean flag to a Product.
-
-Custom fields can currently be added to the following entities:
-
-* Address
-* Customer
-* Facet
-* FacetValue
-* GlobalSettings
-* Product
-* ProductCategory
-* ProductOption
-* ProductOptionGroup
-
-Custom fields are configured by means of a `customFields` property in the VendureConfig object passed to the `bootstrap()` function.
-
-#### Example
-
-```TypeScript
-bootstrap({
-    // ...
-    customFields: {
-        Product: [
-            { name: 'infoUrl', type: 'string' },
-            { name: 'downloadable', type: 'boolean' },
-            { name: 'shortName', type: 'localeString' },
-        ],
-        User: [
-            { name: 'socialLoginToken', type: 'string' },
-        ],
-    },
-})
-```
-
-When Vendure runs, the specified properties will be added to a `customFields` property of the Product and User entities, and
-the GraphQL schema will be updated to reflect the availability of these fields.
-
-In the admin UI, there will be form inputs which allow these fields to be updated by an administrator.
-
-#### Custom Field Types
-
-Currently the supported types are:
-
-| type          | corresponding type in DB  | corresponding GraphQL type    |
-| ----          | ------------------------  | --------------------------    |
-| string        | varchar                   | String                        |
-| localeString  | varchar                   | String                        |
-| int           | int                       | Int                           |
-| float         | double                    | Float                         |
-| boolean       | bool / tinyint            | Boolean                       | 
-| datetime      | datetime / timestamp      | String                        |
-
-Note that the "localeString" type can be localized into any languages supported by your instance.
-
-The following types are under consideration:
-
-* blob
-* text
-
-#### Planned extensions
-
-Currently a custom field configuration has only a "name" and "type". We will be adding further configuration
-options in the future as the framework matures. Currently-planned options are:
-
-* **Enum-like options for strings**. This would display a `<select>` control with pre-defined values.
-* **Access control based on user permissions**. So that read / update access to a given custom field can be
-restricted e.g. only authenticated users / admins / not exposed via the GraphQL API at all (in the case where)
-the custom field will be used solely programatically by business logic contained in custom plugins).
-* **Config for the controls in the admin UI**. For example, an "int" field could have its "step", "max" and "min" values
-specified, which would add corresponding constraints in the admin UI.
-* **Validation logic**. It may be useful to allow the developer to pass a validation function for that field in the config,
-so that any specific constraints can be imposed on the inputted data in a consistent manner.
-* **Custom UI Widget**. For certain specialized custom fields, it may be desirable for the administrator to be able to use
-a custom-made form input to set the value in the admin UI. For example, a "location" field could use a visual map interface
-to set the coordiantes of a point. This would probably be a post-1.0 feature.
-
 ### Orders Process
 ### Orders Process
 
 
 The orders process is governed by a finite state machine which allows each Order to transition only from one valid state
 The orders process is governed by a finite state machine which allows each Order to transition only from one valid state
-to another, as defined by the [OrderState definitions](server/src/service/helpers/order-state-machine/order-state.ts):
+to another, as defined by the [OrderState definitions](packages/core/src/service/helpers/order-state-machine/order-state.ts):
 
 
 ```TypeScript
 ```TypeScript
 export type OrderState =
 export type OrderState =

+ 1 - 1
admin-ui/src/environments/environment.prod.ts

@@ -1,5 +1,5 @@
 declare function require(path: string): any;
 declare function require(path: string): any;
 export const environment = {
 export const environment = {
     production: true,
     production: true,
-    version: require('../../../server/package.json').version,
+    version: require('../../../package.json').version,
 };
 };

+ 1 - 1
admin-ui/src/environments/environment.ts

@@ -4,7 +4,7 @@
 declare function require(path: string): any;
 declare function require(path: string): any;
 export const environment = {
 export const environment = {
     production: false,
     production: false,
-    version: require('../../../server/package.json').version,
+    version: require('../../../package.json').version,
 };
 };
 
 
 /*
 /*

+ 1 - 1
admin-ui/tsconfig.json

@@ -24,7 +24,7 @@
       "esnext.asynciterable"
       "esnext.asynciterable"
     ],
     ],
     "paths": {
     "paths": {
-      "shared/*": ["../shared/*"]
+      "shared/*": ["../packages/common/lib/*"]
     }
     }
   }
   }
 }
 }

+ 0 - 587
codegen/generate-config-docs.ts

@@ -1,587 +0,0 @@
-import fs from 'fs';
-import klawSync from 'klaw-sync';
-import path from 'path';
-import ts from 'typescript';
-
-import { assertNever, notNullOrUndefined } from '../shared/shared-utils';
-
-import { deleteGeneratedDocs, generateFrontMatter } from './docgen-utils';
-
-// The absolute URL to the generated docs section
-const docsUrl = '/docs/configuration/';
-// The directory in which the markdown files will be saved
-const outputPath = path.join(__dirname, '../docs/content/docs/configuration');
-// The directories to scan for TypeScript source files
-const tsSourceDirs = ['/server/src/', '/shared/'];
-
-// tslint:disable:no-console
-interface MethodParameterInfo {
-    name: string;
-    type: string;
-}
-
-interface MemberInfo {
-    name: string;
-    description: string;
-    type: string;
-    fullText: string;
-}
-
-interface PropertyInfo extends MemberInfo {
-    kind: 'property';
-    defaultValue: string;
-}
-
-interface MethodInfo extends MemberInfo {
-    kind: 'method';
-    parameters: MethodParameterInfo[];
-}
-
-interface DeclarationInfo {
-    sourceFile: string;
-    sourceLine: number;
-    title: string;
-    fullText: string;
-    weight: number;
-    category: string;
-    description: string;
-    fileName: string;
-}
-
-interface InterfaceInfo extends DeclarationInfo {
-    kind: 'interface';
-    extends?: string;
-    members: Array<PropertyInfo | MethodInfo>;
-}
-
-interface ClassInfo extends DeclarationInfo {
-    kind: 'class';
-    implements?: string;
-    extends?: string;
-    members: Array<PropertyInfo | MethodInfo>;
-}
-
-interface TypeAliasInfo extends DeclarationInfo {
-    kind: 'typeAlias';
-    members?: Array<PropertyInfo | MethodInfo>;
-    type: ts.TypeNode;
-}
-
-type ValidDeclaration = ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.ClassDeclaration;
-type TypeMap = Map<string, string>;
-
-/**
- * This map is used to cache types and their corresponding Hugo path. It is used to enable
- * hyperlinking from a member's "type" to the definition of that type.
- */
-const globalTypeMap: TypeMap = new Map();
-
-const tsFiles = tsSourceDirs
-    .map(scanPath =>
-        klawSync(path.join(__dirname, '../', scanPath), {
-            nodir: true,
-            filter: item => path.extname(item.path) === '.ts',
-            traverseAll: true,
-        }),
-    )
-    .reduce((allFiles, files) => [...allFiles, ...files], [])
-    .map(item => item.path);
-
-deleteGeneratedDocs(outputPath);
-generateConfigDocs(tsFiles, outputPath, globalTypeMap);
-const watchMode = !!process.argv.find(arg => arg === '--watch' || arg === '-w');
-if (watchMode) {
-    console.log(`Watching for changes to source files...`);
-    tsFiles.forEach(file => {
-        fs.watchFile(file, { interval: 1000 }, () => {
-            generateConfigDocs([file], outputPath, globalTypeMap);
-        });
-    });
-}
-
-/**
- * Uses the TypeScript compiler API to parse the given files and extract out the documentation
- * into markdown files
- */
-function generateConfigDocs(filePaths: string[], hugoOutputPath: string, typeMap: TypeMap) {
-    const timeStart = +new Date();
-    let generatedCount = 0;
-    const sourceFiles = filePaths.map(filePath => {
-        return ts.createSourceFile(
-            filePath,
-            fs.readFileSync(filePath).toString(),
-            ts.ScriptTarget.ES2015,
-            true,
-        );
-    });
-
-    const statements = getStatementsWithSourceLocation(sourceFiles);
-
-    const declarationInfos = statements
-        .map(statement => {
-            const info = parseDeclaration(statement.statement, statement.sourceFile, statement.sourceLine);
-            if (info) {
-                typeMap.set(info.title, info.category + '/' + info.fileName);
-            }
-            return info;
-        })
-        .filter(notNullOrUndefined);
-
-    for (const info of declarationInfos) {
-        let markdown = '';
-        switch (info.kind) {
-            case 'interface':
-                markdown = renderInterfaceOrClass(info, typeMap);
-                break;
-            case 'typeAlias':
-                markdown = renderTypeAlias(info, typeMap);
-                break;
-            case 'class':
-                markdown = renderInterfaceOrClass(info as any, typeMap);
-                break;
-            default:
-                assertNever(info);
-        }
-
-        const categoryDir = path.join(hugoOutputPath, info.category);
-        const indexFile = path.join(categoryDir, '_index.md');
-        if (!fs.existsSync(categoryDir)) {
-            fs.mkdirSync(categoryDir);
-        }
-        if (!fs.existsSync(indexFile)) {
-            const indexFileContent = generateFrontMatter(info.category, 10, false) + `\n\n# ${info.category}`;
-            fs.writeFileSync(indexFile, indexFileContent);
-            generatedCount++;
-        }
-
-        fs.writeFileSync(path.join(categoryDir, info.fileName + '.md'), markdown);
-        generatedCount++;
-    }
-
-    if (declarationInfos.length) {
-        console.log(`Generated ${generatedCount} configuration docs in ${+new Date() - timeStart}ms`);
-    }
-}
-
-/**
- * Maps an array of parsed SourceFiles into statements, including a reference to the original file each statement
- * came from.
- */
-function getStatementsWithSourceLocation(
-    sourceFiles: ts.SourceFile[],
-): Array<{ statement: ts.Statement; sourceFile: string; sourceLine: number }> {
-    return sourceFiles.reduce(
-        (st, sf) => {
-            const statementsWithSources = sf.statements.map(statement => {
-                const sourceFile = path.relative(path.join(__dirname, '..'), sf.fileName).replace(/\\/g, '/');
-                const sourceLine = sf.getLineAndCharacterOfPosition(statement.getStart()).line + 1;
-                return { statement, sourceFile, sourceLine };
-            });
-            return [...st, ...statementsWithSources];
-        },
-        [] as Array<{ statement: ts.Statement; sourceFile: string; sourceLine: number }>,
-    );
-}
-
-/**
- * Parses an InterfaceDeclaration into a simple object which can be rendered into markdown.
- */
-function parseDeclaration(
-    statement: ts.Statement,
-    sourceFile: string,
-    sourceLine: number,
-): InterfaceInfo | TypeAliasInfo | ClassInfo | undefined {
-    if (!isValidDeclaration(statement)) {
-        return;
-    }
-    const category = getDocsCategory(statement);
-    if (category === undefined) {
-        return;
-    }
-    const title = statement.name ? statement.name.getText() : 'anonymous';
-    const fullText = getDeclarationFullText(statement);
-    const weight = getDeclarationWeight(statement);
-    const description = getDeclarationDescription(statement);
-    const fileName = title
-        .split(/(?=[A-Z])/)
-        .join('-')
-        .toLowerCase();
-
-    const info = {
-        sourceFile,
-        sourceLine,
-        fullText,
-        title,
-        weight,
-        category,
-        description,
-        fileName,
-    };
-
-    if (ts.isInterfaceDeclaration(statement)) {
-        return {
-            ...info,
-            kind: 'interface',
-            extends: getHeritageClauseText(statement, ts.SyntaxKind.ExtendsKeyword),
-            members: parseMembers(statement.members),
-        };
-    } else if (ts.isTypeAliasDeclaration(statement)) {
-        return {
-            ...info,
-            type: statement.type,
-            kind: 'typeAlias',
-            members: ts.isTypeLiteralNode(statement.type) ? parseMembers(statement.type.members) : undefined,
-        };
-    } else if (ts.isClassDeclaration(statement)) {
-        return {
-            ...info,
-            kind: 'class',
-            members: parseMembers(statement.members),
-            extends: getHeritageClauseText(statement, ts.SyntaxKind.ExtendsKeyword),
-            implements: getHeritageClauseText(statement, ts.SyntaxKind.ImplementsKeyword),
-        };
-    }
-}
-
-/**
- * Returns the text of any "extends" or "implements" clause of a class or interface.
- */
-function getHeritageClauseText(
-    statement: ts.ClassDeclaration | ts.InterfaceDeclaration,
-    kind: ts.SyntaxKind.ExtendsKeyword | ts.SyntaxKind.ImplementsKeyword,
-): string | undefined {
-    const { heritageClauses } = statement;
-    if (!heritageClauses) {
-        return;
-    }
-    const clause = heritageClauses.find(cl => cl.token === kind);
-    if (!clause) {
-        return;
-    }
-    return clause.getText();
-}
-
-/**
- * Returns the declaration name plus any type parameters.
- */
-function getDeclarationFullText(declaration: ValidDeclaration): string {
-    const name = declaration.name ? declaration.name.getText() : 'anonymous';
-    let typeParams = '';
-    if (declaration.typeParameters) {
-        typeParams = '<' + declaration.typeParameters.map(tp => tp.getText()).join(', ') + '>';
-    }
-    return name + typeParams;
-}
-
-/**
- * Parses an array of inteface members into a simple object which can be rendered into markdown.
- */
-function parseMembers(
-    members: ts.NodeArray<ts.TypeElement | ts.ClassElement>,
-): Array<PropertyInfo | MethodInfo> {
-    const result: Array<PropertyInfo | MethodInfo> = [];
-
-    for (const member of members) {
-        const modifiers = member.modifiers ? member.modifiers.map(m => m.getText()) : [];
-        const isPrivate = modifiers.includes('private');
-        if (
-            !isPrivate &&
-            (ts.isPropertySignature(member) ||
-                ts.isMethodSignature(member) ||
-                ts.isPropertyDeclaration(member) ||
-                ts.isMethodDeclaration(member) ||
-                ts.isConstructorDeclaration(member))
-        ) {
-            const name = member.name ? member.name.getText() : 'constructor';
-            let description = '';
-            let type = '';
-            let defaultValue = '';
-            let parameters: MethodParameterInfo[] = [];
-            let fullText = '';
-            if (ts.isConstructorDeclaration(member)) {
-                fullText = 'constructor';
-            } else if (ts.isMethodDeclaration(member)) {
-                fullText = member.name.getText();
-            } else {
-                fullText = member.getText();
-            }
-            parseTags(member, {
-                description: tag => (description += tag.comment || ''),
-                example: tag => (description += formatExampleCode(tag.comment)),
-                default: tag => (defaultValue = tag.comment || ''),
-            });
-            if (member.type) {
-                type = member.type.getText();
-            }
-            const memberInfo: MemberInfo = {
-                fullText,
-                name,
-                description,
-                type,
-            };
-            if (
-                ts.isMethodSignature(member) ||
-                ts.isMethodDeclaration(member) ||
-                ts.isConstructorDeclaration(member)
-            ) {
-                parameters = member.parameters.map(p => ({
-                    name: p.name.getText(),
-                    type: p.type ? p.type.getText() : '',
-                }));
-                result.push({
-                    ...memberInfo,
-                    kind: 'method',
-                    parameters,
-                });
-            } else {
-                result.push({
-                    ...memberInfo,
-                    kind: 'property',
-                    defaultValue,
-                });
-            }
-        }
-    }
-
-    return result;
-}
-
-/**
- * Render the interface to a markdown string.
- */
-function renderInterfaceOrClass(info: InterfaceInfo | ClassInfo, knownTypeMap: Map<string, string>): string {
-    const { title, weight, category, description, members } = info;
-    let output = '';
-    output += generateFrontMatter(title, weight);
-    output += `\n\n# ${title}\n\n`;
-    output += renderGenerationInfoShortcode(info);
-    output += `${renderDescription(description, knownTypeMap)}\n\n`;
-    output += `## Signature\n\n`;
-    output += info.kind === 'interface' ? renderInterfaceSignature(info) : renderClassSignature(info);
-    output += `## Members\n\n`;
-    output += `${renderMembers(info, knownTypeMap)}\n`;
-    return output;
-}
-
-/**
- * Render the type alias to a markdown string.
- */
-function renderTypeAlias(typeAliasInfo: TypeAliasInfo, knownTypeMap: Map<string, string>): string {
-    const { title, weight, description, type, fullText } = typeAliasInfo;
-    let output = '';
-    output += generateFrontMatter(title, weight);
-    output += `\n\n# ${title}\n\n`;
-    output += renderGenerationInfoShortcode(typeAliasInfo);
-    output += `${renderDescription(description, knownTypeMap)}\n\n`;
-    output += `## Signature\n\n`;
-    output += renderTypeAliasSignature(typeAliasInfo);
-    if (typeAliasInfo.members) {
-        output += `## Members\n\n`;
-        output += `${renderMembers(typeAliasInfo, knownTypeMap)}\n`;
-    }
-    return output;
-}
-
-/**
- * Generates a markdown code block string for the interface signature.
- */
-function renderInterfaceSignature(interfaceInfo: InterfaceInfo): string {
-    const { fullText, members } = interfaceInfo;
-    let output = '';
-    output += `\`\`\`TypeScript\n`;
-    output += `interface ${fullText} `;
-    if (interfaceInfo.extends) {
-        output += interfaceInfo.extends + ' ';
-    }
-    output += `{\n`;
-    output += members.map(member => `  ${member.fullText}`).join(`\n`);
-    output += `\n}\n`;
-    output += `\`\`\`\n`;
-
-    return output;
-}
-
-function renderClassSignature(classInfo: ClassInfo): string {
-    const { fullText, members } = classInfo;
-    let output = '';
-    output += `\`\`\`TypeScript\n`;
-    output += `class ${fullText} `;
-    if (classInfo.extends) {
-        output += classInfo.extends + ' ';
-    }
-    if (classInfo.implements) {
-        output += classInfo.implements + ' ';
-    }
-    output += `{\n`;
-    output += members
-        .map(member => {
-            if (member.kind === 'method') {
-                const args = member.parameters
-                    .map(p => {
-                        return `${p.name}: ${p.type}`;
-                    })
-                    .join(', ');
-                if (member.fullText === 'constructor') {
-                    return `  constructor(${args})`;
-                } else {
-                    return `  ${member.fullText}(${args}) => ${member.type};`;
-                }
-            } else {
-                return `  ${member.fullText}`;
-            }
-        })
-        .join(`\n`);
-    output += `\n}\n`;
-    output += `\`\`\`\n`;
-
-    return output;
-}
-
-function renderTypeAliasSignature(typeAliasInfo: TypeAliasInfo): string {
-    const { fullText, members, type } = typeAliasInfo;
-    let output = '';
-    output += `\`\`\`TypeScript\n`;
-    output += `type ${fullText} = `;
-    if (members) {
-        output += `{\n`;
-        output += members.map(member => `  ${member.fullText}`).join(`\n`);
-        output += `\n}\n`;
-    } else {
-        output += type.getText() + `\n`;
-    }
-    output += `\`\`\`\n`;
-    return output;
-}
-
-function renderMembers(info: InterfaceInfo | ClassInfo | TypeAliasInfo, knownTypeMap: TypeMap): string {
-    const { members, title } = info;
-    let output = '';
-    for (const member of members || []) {
-        let defaultParam = '';
-        let type = '';
-        if (member.kind === 'property') {
-            type = renderType(member.type, knownTypeMap);
-            defaultParam = member.defaultValue
-                ? `default="${renderType(member.defaultValue, knownTypeMap)}" `
-                : '';
-        } else {
-            const args = member.parameters
-                .map(p => {
-                    return `${p.name}: ${renderType(p.type, knownTypeMap)}`;
-                })
-                .join(', ');
-            if (member.fullText === 'constructor') {
-                type = `(${args}) => ${title}`;
-            } else {
-                type = `(${args}) => ${renderType(member.type, knownTypeMap)}`;
-            }
-        }
-        output += `### ${member.name}\n\n`;
-        output += `{{< member-info kind="${member.kind}" type="${type}" ${defaultParam}>}}\n\n`;
-        output += `${renderDescription(member.description, knownTypeMap)}\n\n`;
-    }
-    return output;
-}
-
-function renderGenerationInfoShortcode(info: DeclarationInfo): string {
-    return `{{< generation-info sourceFile="${info.sourceFile}" sourceLine="${info.sourceLine}">}}\n\n`;
-}
-
-/**
- * Extracts the "@docsCategory" value from the JSDoc comments if present.
- */
-function getDocsCategory(statement: ValidDeclaration): string | undefined {
-    let category: string | undefined;
-    parseTags(statement, {
-        docsCategory: tag => (category = tag.comment || ''),
-    });
-    return category;
-}
-
-/**
- * Parses the Node's JSDoc tags and invokes the supplied functions against any matching tag names.
- */
-function parseTags<T extends ts.Node>(
-    node: T,
-    tagMatcher: { [tagName: string]: (tag: ts.JSDocTag) => void },
-): void {
-    const jsDocTags = ts.getJSDocTags(node);
-    for (const tag of jsDocTags) {
-        const tagName = tag.tagName.text;
-        if (tagMatcher[tagName]) {
-            tagMatcher[tagName](tag);
-        }
-    }
-}
-
-/**
- * This function takes a string representing a type (e.g. "Array<ShippingMethod>") and turns
- * and known types (e.g. "ShippingMethod") into hyperlinks.
- */
-function renderType(type: string, knownTypeMap: TypeMap): string {
-    let typeText = type
-        .trim()
-        // encode HTML entities
-        .replace(/[\u00A0-\u9999<>\&]/gim, i => '&#' + i.charCodeAt(0) + ';')
-        // remove newlines
-        .replace(/\n/g, ' ');
-
-    for (const [key, val] of knownTypeMap) {
-        const re = new RegExp(`\\b${key}\\b`, 'g');
-        typeText = typeText.replace(re, `<a href='${docsUrl}/${val}/'>${key}</a>`);
-    }
-    return typeText;
-}
-
-/**
- * Replaces any `{@link Foo}` references in the description with hyperlinks.
- */
-function renderDescription(description: string, knownTypeMap: TypeMap): string {
-    for (const [key, val] of knownTypeMap) {
-        const re = new RegExp(`{@link\\s*${key}}`, 'g');
-        description = description.replace(re, `<a href='${docsUrl}/${val}/'>${key}</a>`);
-    }
-    return description;
-}
-
-/**
- * Reads the @docsWeight JSDoc tag from the interface.
- */
-function getDeclarationWeight(statement: ValidDeclaration): number {
-    let weight = 10;
-    parseTags(statement, {
-        docsWeight: tag => (weight = Number.parseInt(tag.comment || '10', 10)),
-    });
-    return weight;
-}
-
-/**
- * Reads the @description JSDoc tag from the interface.
- */
-function getDeclarationDescription(statement: ValidDeclaration): string {
-    let description = '';
-    parseTags(statement, {
-        description: tag => (description += tag.comment),
-        example: tag => (description += formatExampleCode(tag.comment)),
-    });
-    return description;
-}
-
-/**
- * Cleans up a JSDoc "@example" block by removing leading whitespace and asterisk (TypeScript has an open issue
- * wherein the asterisks are not stripped as they should be, see https://github.com/Microsoft/TypeScript/issues/23517)
- */
-function formatExampleCode(example: string = ''): string {
-    return '\n\n*Example*\n\n' + example.replace(/\n\s+\*\s/g, '\n');
-}
-
-/**
- * Type guard for the types of statement which can ge processed by the doc generator.
- */
-function isValidDeclaration(statement: ts.Statement): statement is ValidDeclaration {
-    return (
-        ts.isInterfaceDeclaration(statement) ||
-        ts.isTypeAliasDeclaration(statement) ||
-        ts.isClassDeclaration(statement)
-    );
-}

+ 6 - 0
docs/assets/styles/_shortcodes.scss

@@ -56,6 +56,12 @@
 .generation-info {
 .generation-info {
     font-size: $font-size-12;
     font-size: $font-size-12;
     border-bottom: 1px dashed $gray-400;
     border-bottom: 1px dashed $gray-400;
+    .label {
+        color: $gray-600;
+    }
+    .file {
+        margin-left: 12px;
+    }
 }
 }
 
 
 /**
 /**

+ 3 - 1
docs/assets/styles/main.scss

@@ -179,8 +179,10 @@ ul.contents-list {
 
 
 .book-footer {
 .book-footer {
     height: 200px;
     height: 200px;
-    // background-color: $gray-100;
+    text-align: center;
+    color: $gray-600;
     margin-top: 60px;
     margin-top: 60px;
+    font-size: 12px;
 }
 }
 
 
 .book-posts {
 .book-posts {

+ 0 - 26
docs/content/docs/configuration/_index.md

@@ -1,26 +0,0 @@
----
-title: "Configuration"
-weight: 9
-showtoc: false
----
-
-# Vendure Configuration Docs
-
-All configuration is done by way of the [`VendureConfig`]({{< ref "vendure-config" >}}) object which is passed to Vendure's `bootstrap()` function.
-
-```TypeScript
-bootstrap({
-    authOptions: {
-        sessionSecret: 'BD95F861369DCD684AA668A926E86F8B',
-    },
-    port: 3000,
-    apiPath: 'api',
-    // ...
-});
-```
-
-This section contains a description of all available configuration options for Vendure.
-
-{{% alert %}}
-All documentation in this section is auto-generated from the TypeScript source of the Vendure server.
-{{% /alert %}}

+ 1 - 1
docs/content/docs/plugins/_index.md

@@ -15,4 +15,4 @@ Plugins in Vendure allow one to:
 
 
 These abilities make plugins a very versatile and powerful means of implementing custom business requirements.
 These abilities make plugins a very versatile and powerful means of implementing custom business requirements.
 
 
-This section details the built-in plugins which ship with Vendure as well as a guide to writing your own plugins.
+This section details the official Vendure plugins included in the main Vendure repo, as well as a guide on writing your own plugins for Vendure.

+ 0 - 19
docs/content/docs/plugins/admin-ui-plugin.md

@@ -1,19 +0,0 @@
----
-title: "AdminUiPlugin"
----
-
-# AdminUiPlugin
-
-This plugin starts a static server for the Admin UI app, and proxies it via the `/admin/` path of the main Vendure server.
-
-The Admin UI allows you to administer all aspects of your store, from inventory management to order tracking. It is the tool used by store administrators on a day-to-day basis for the management of the store.
-
-
-```ts 
-const config: VendureConfig = {
-  // Add an instance of the plugin to the plugins array
-  plugins: [
-    new AdminUiPlugin({ port: 3002 }),
-  ],
-};
-```

+ 0 - 67
docs/content/docs/plugins/default-asset-server-plugin.md

@@ -1,67 +0,0 @@
----
-title: "DefaultAssetServerPlugin"
----
-
-# DefaultAssetServerPlugin
-
-The `DefaultAssetServerPlugin` serves assets (images and other files) from the local file system. It can also perform on-the-fly image transformations and caches the results for subsequent calls.
-
-```ts
-const config: VendureConfig = {
-  // Add an instance of the plugin to the plugins array
-  plugins: [
-    new DefaultAssetServerPlugin({
-      route: 'assets',
-      assetUploadDir: path.join(__dirname, 'assets'),
-      port: 4000,
-    }),
-  ],
-};
-```
-
-The full configuration is documented at [DefaultAssetServerOptions]({{< relref "default-asset-server-options" >}})
-
-## Image transformation
-
-Asset preview images can be transformed (resized & cropped) on the fly by appending query parameters to the url:
-
-`http://localhost:3000/assets/some-asset.jpg?w=500&h=300&mode=resize`
-
-The above URL will return `some-asset.jpg`, resized to fit in the bounds of a 500px x 300px rectangle.
-
-### Preview mode
-
-The `mode` parameter can be either `crop` or `resize`. See the [ImageTransformMode]({{< relref "image-transform-mode" >}}) docs for details.
-
-### Transform presets
-
-Presets can be defined which allow a single preset name to be used instead of specifying the width, height and mode. Presets are configured via the DefaultAssetServerOptions [presets property]({{< relref "default-asset-server-options" >}}#presets).
-
-For example, defining the following preset:
-
-```ts
-new DefaultAssetServerPlugin({
-  // ...
-  presets: [
-    { name: 'my-preset', width: 85, height: 85, mode: 'crop' },
-  ],
-}),
-```
-
-means that a request to:
-
-`http://localhost:3000/assets/some-asset.jpg?preset=my-preset`
-
-is equivalent to:
-
-`http://localhost:3000/assets/some-asset.jpg?w=85&h=85&mode=crop`
-
-The DefaultAssetServerPlugin comes pre-configured with the following presets:
-
-name | width | height | mode
------|-------|--------|-----
-tiny | 50px | 50px | crop
-thumb | 150px | 150px | crop
-small | 300px | 300px | resize
-medium | 500px | 500px | resize
-large | 800px | 800px | resize

+ 0 - 67
docs/content/docs/plugins/default-email-plugin.md

@@ -1,67 +0,0 @@
----
-title: "DefaultEmailPlugin"
----
-
-# DefaultEmailPlugin
-
-The DefaultEmailPlugin configures the the [EmailOptions]({{< relref "email-options" >}}) to use an [MJML](https://mjml.io/)-based email generator and presents a simplified interface for typical email requirements.
-
-```ts 
-const config: VendureConfig = {
-  // Add an instance of the plugin to the plugins array
-  plugins: [
-    new DefaultEmailPlugin({
-      templatePath: path.join(__dirname, 'vendure/email/templates'),
-      transport: {
-        type: 'smtp',
-        host: 'smtp.example.com',
-        port: 587,
-        auth: {
-          user: 'username',
-          pass: 'password',
-        }
-      },
-    }),
-  ],
-};
-```
-
-## Customizing templates
-
-Emails are generated from templates which use [MJML](https://mjml.io/) syntax. MJML is an open-source HTML-like markup language which makes the task of creating responsive email markup simple. By default, the templates are installed to `<project root>/vendure/email/templates` and can be freely edited.
-
-Dynamic data such as the recipient's name or order items are specified using [Handlebars syntax](https://handlebarsjs.com/):
-
-```HTML
-<p>Dear {{ order.customer.firstName }} {{ order.customer.lastName }},</p>
-
-<p>Thank you for your order!</p>
-
-<mj-table cellpadding="6px">
-  {{#each order.lines }}
-    <tr class="order-row">
-      <td>{{ quantity }} x {{ productVariant.name }}</td>
-      <td>{{ productVariant.quantity }}</td>
-      <td>{{ formatMoney totalPrice }}</td>
-    </tr>
-  {{/each}}
-</mj-table>
-```
-
-### Handlebars helpers
-
-The following helper functions are available for use in email templates:
-
-* `formatMoney`: Formats an amount of money (which are always stored as integers in Vendure) as a decimal, e.g. `123` => `1.23`
-* `formatDate`: Formats a Date value with the [dateformat](https://www.npmjs.com/package/dateformat) package.
-
-## Dev mode
-
-For development, the `transport` option can be replaced by `devMode: true`. Doing so configures Vendure to use the [file transport]({{< relref "file-transport-options" >}}) and outputs emails as rendered HTML files in a directory named "test-emails" which is located adjacent to the directory configured in the `templatePath`.
-
-```ts 
-new DefaultEmailPlugin({
-  templatePath: path.join(__dirname, 'vendure/email/templates'),
-  devMode: true,
-})
-```

+ 13 - 0
docs/content/docs/typescript-api/_index.md

@@ -0,0 +1,13 @@
+---
+title: "TypeScript API"
+weight: 9
+showtoc: false
+---
+
+# Vendure TypeScript API Docs
+
+The Vendure TypeScript API is used when configuring the server (via the [`VendureConfig`]({{< ref "vendure-config" >}}) object) and when writing plugins that extend the core functionality of Vendure core.
+
+{{% alert %}}
+All documentation in this section is auto-generated from the TypeScript source of the Vendure server.
+{{% /alert %}}

+ 2 - 1
docs/layouts/docs/baseof.html

@@ -23,8 +23,9 @@
     <div class="book-page">
     <div class="book-page">
         {{ partial "docs/mobile-header" . }}
         {{ partial "docs/mobile-header" . }}
         {{ template "main" . }}
         {{ template "main" . }}
-        <div class="book-footer">
 
 
+        <div class="book-footer">
+            Generated on {{ dateFormat "Jan 2 2006 at 15:04" $.Page.Lastmod }}
         </div>
         </div>
     </div>
     </div>
 
 

+ 2 - 1
docs/layouts/shortcodes/generation-info.html

@@ -1,3 +1,4 @@
 <div class="generation-info">
 <div class="generation-info">
-    Documentation generated from <a href="https://github.com/vendure-ecommerce/vendure/blob/master/{{ .Get "sourceFile" }}#L{{ .Get "sourceLine" }}">{{  index (last 1 (split (.Get "sourceFile") "/")) 0 }}</a> on {{ dateFormat "Jan 2 2006 at 15:04" $.Page.Lastmod }}
+    <span class="label">Package:</span> <a href="https://www.npmjs.com/package/{{ .Get "packageName" }}">{{ .Get "packageName" }}</a>
+    <span class="label file">File:</span> <a href="https://github.com/vendure-ecommerce/vendure/blob/master/{{ .Get "sourceFile" }}#L{{ .Get "sourceLine" }}">{{  index (last 1 (split (.Get "sourceFile") "/")) 0 }}</a>
 </div>
 </div>

+ 8 - 0
lerna.json

@@ -0,0 +1,8 @@
+{
+  "packages": [
+    "packages/*"
+  ],
+  "version": "independent",
+  "npmClient": "yarn",
+  "useWorkspaces": true
+}

+ 22 - 10
package.json

@@ -1,21 +1,29 @@
 {
 {
   "name": "vendure",
   "name": "vendure",
-  "version": "0.1.0",
+  "version": "0.1.0-alpha.17",
   "private": true,
   "private": true,
   "scripts": {
   "scripts": {
-    "docs:watch": "concurrently -n docgen,hugo,webpack -c green,blue,cyan \"yarn generate-api-docs && yarn generate-config-docs -w\" \"cd docs && hugo server\" \"cd docs && yarn webpack -w\"",
-    "docs:build": "yarn generate-api-docs && yarn generate-config-docs && cd docs && yarn webpack --prod && hugo",
+    "bootstrap": "lerna bootstrap",
+    "docs:watch": "concurrently -n docgen,hugo,webpack -c green,blue,cyan \"yarn generate-graphql-docs && yarn generate-typescript-docs -w\" \"cd docs && hugo server\" \"cd docs && yarn webpack -w\"",
+    "docs:build": "yarn generate-graphql-docs && yarn generate-typescript-docs && cd docs && yarn webpack --prod && hugo",
     "docs:deploy": "cd docs && yarn && cd .. && yarn docs:build",
     "docs:deploy": "cd docs && yarn && cd .. && yarn docs:build",
-    "generate-gql-types": "ts-node ./codegen/generate-graphql-types.ts",
-    "generate-config-docs": "ts-node ./codegen/generate-config-docs.ts",
-    "generate-api-docs": "ts-node ./codegen/generate-api-docs.ts --api=shop && ts-node ./codegen/generate-api-docs.ts --api=admin",
-    "test": "cd admin-ui && yarn test --watch=false --browsers=ChromeHeadlessCI --progress=false && cd ../server && yarn test && yarn test:e2e",
+    "codegen": "ts-node scripts/codegen/generate-graphql-types.ts",
+    "generate-typescript-docs": "ts-node scripts/docs/generate-typescript-docs.ts",
+    "generate-graphql-docs": "ts-node scripts/docs/generate-graphql-docs.ts --api=shop && ts-node scripts/docs/generate-graphql-docs.ts --api=admin",
     "format": "prettier --write --html-whitespace-sensitivity ignore",
     "format": "prettier --write --html-whitespace-sensitivity ignore",
-    "lint:server": "cd server && yarn lint --fix",
+    "lint:core": "cd packages/core && yarn lint --fix",
     "lint:admin-ui": "cd admin-ui && yarn lint --fix",
     "lint:admin-ui": "cd admin-ui && yarn lint --fix",
     "precommit": "lint-staged",
     "precommit": "lint-staged",
     "postcommit": "git update-index --again",
     "postcommit": "git update-index --again",
-    "prepush": "yarn test && cd admin-ui && yarn build --prod"
+    "prepush": "yarn test:all && cd admin-ui && yarn build --prod",
+    "dev-server": "cd packages/dev-server && yarn start",
+    "dev-server:populate": "cd packages/dev-server && yarn populate",
+    "test:all": "cd admin-ui && yarn test --watch=false --browsers=ChromeHeadlessCI --progress=false && cd ../ && yarn test:common && yarn test:core && yarn test:e2e",
+    "test:common": "jest --config packages/common/jest.config.js",
+    "test:core": "jest --config packages/core/jest.config.js",
+    "test:e2e": "jest --config packages/core/e2e/config/jest-e2e.json --runInBand",
+    "test:admin-ui": "cd admin-ui && yarn test --watch=false --browsers=ChromeHeadlessCI --progress=false",
+    "build": "lerna run build"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@types/graphql": "^14.0.5",
     "@types/graphql": "^14.0.5",
@@ -31,11 +39,15 @@
     "graphql-codegen-typescript-server": "^0.16.0",
     "graphql-codegen-typescript-server": "^0.16.0",
     "graphql-tools": "^4.0.0",
     "graphql-tools": "^4.0.0",
     "husky": "^0.14.3",
     "husky": "^0.14.3",
+    "jest": "^24.5.0",
     "klaw-sync": "^6.0.0",
     "klaw-sync": "^6.0.0",
+    "lerna": "^3.13.1",
     "lint-staged": "^7.2.0",
     "lint-staged": "^7.2.0",
     "prettier": "^1.15.2",
     "prettier": "^1.15.2",
+    "ts-jest": "^24.0.0",
     "ts-node": "^7.0.1",
     "ts-node": "^7.0.1",
     "tslint": "^5.11.0",
     "tslint": "^5.11.0",
     "typescript": "^3.2.4"
     "typescript": "^3.2.4"
-  }
+  },
+  "workspaces": ["packages/*"]
 }
 }

+ 1 - 0
packages/admin-ui-plugin/.gitignore

@@ -0,0 +1 @@
+lib

+ 0 - 0
packages/admin-ui-plugin/.npmignore


+ 5 - 0
packages/admin-ui-plugin/README.md

@@ -0,0 +1,5 @@
+# Vendure AdminUiPlugin
+
+`npm install @vendure/admin-ui-plugin`
+
+For documentation, see [www.vendure.io/docs/plugins/admin-ui-plugin](www.vendure.io/docs/plugins/admin-ui-plugin)

+ 21 - 0
packages/admin-ui-plugin/build.js

@@ -0,0 +1,21 @@
+/* tslint:disable:no-console */
+const path = require ('path');
+const fs = require ('fs-extra');
+const { exec } = require('child_process');
+
+console.log('Building admin-ui from source...');
+exec(
+    'yarn build --prod=true',
+    {
+        cwd: path.join(__dirname, '../../admin-ui'),
+    },
+    async error => {
+        if (error) {
+            console.log(error);
+            process.exit(1);
+        }
+        console.log('done!');
+        await fs.copy('../../admin-ui/dist/vendure-admin', 'lib/admin-ui');
+        process.exit(0);
+    },
+);

+ 1 - 0
packages/admin-ui-plugin/index.ts

@@ -0,0 +1 @@
+export * from './src/plugin';

+ 29 - 0
packages/admin-ui-plugin/package.json

@@ -0,0 +1,29 @@
+{
+  "name": "@vendure/admin-ui-plugin",
+  "version": "0.1.0-alpha.1",
+  "main": "lib/index.js",
+  "types": "lib/index.d.ts",
+  "files": ["lib/**/*"],
+  "license": "MIT",
+  "scripts": {
+    "build": "rimraf lib && node build.js && yarn compile",
+    "compile": "tsc -p ./tsconfig.build.json"
+  },
+  "devDependencies": {
+    "@types/express": "^4.0.39",
+    "@types/fs-extra": "^5.0.4",
+    "@types/http-proxy-middleware": "^0.19.2",
+    "@vendure/common": ">=0.1.0-alpha.1",
+    "@vendure/core": ">=0.1.0-alpha.1",
+    "express": "^4.16.4",
+    "rimraf": "^2.6.3"
+  },
+  "dependencies": {
+    "fs-extra": "^7.0.1",
+    "http-proxy-middleware": "^0.19.1",
+    "typescript": "^3.3.4000"
+  },
+  "peerDependencies": {
+    "@vendure/core": ">=0.1.0-alpha.1"
+  }
+}

+ 46 - 15
server/src/plugin/admin-ui-plugin/admin-ui-plugin.ts → packages/admin-ui-plugin/src/plugin.ts

@@ -1,18 +1,16 @@
+import { AdminUiConfig } from '@vendure/common/lib/shared-types';
+import { InjectorFn, VendureConfig, VendurePlugin } from '@vendure/core';
 import express from 'express';
 import express from 'express';
 import fs from 'fs-extra';
 import fs from 'fs-extra';
 import { Server } from 'http';
 import { Server } from 'http';
+import proxy from 'http-proxy-middleware';
 import path from 'path';
 import path from 'path';
 
 
-import { AdminUiConfig } from '../../../../shared/shared-types';
-import { VendureConfig } from '../../config/vendure-config';
-import { InjectorFn, VendurePlugin } from '../../config/vendure-plugin/vendure-plugin';
-import { createProxyHandler } from '../plugin-utils';
-
 /**
 /**
  * @description
  * @description
  * Configuration options for the {@link AdminUiPlugin}.
  * Configuration options for the {@link AdminUiPlugin}.
  *
  *
- * @docsCategory plugin
+ * @docsCategory AdminUiPlugin
  */
  */
 export interface AdminUiOptions {
 export interface AdminUiOptions {
     /**
     /**
@@ -49,10 +47,21 @@ export interface AdminUiOptions {
 
 
 /**
 /**
  * @description
  * @description
- * This plugin starts a static server for the Admin UI app, and proxies it via the `/admin/` path
- * of the main Vendure server.
+ * This plugin starts a static server for the Admin UI app, and proxies it via the `/admin/` path of the main Vendure server.
+ *
+ * The Admin UI allows you to administer all aspects of your store, from inventory management to order tracking. It is the tool used by store administrators on a day-to-day basis for the management of the store.
+ *
+ * @example
+ * ```ts
+ * const config: VendureConfig = {
+ *   // Add an instance of the plugin to the plugins array
+ *   plugins: [
+ *     new AdminUiPlugin({ port: 3002 }),
+ *   ],
+ * };
+ * ```
  *
  *
- * @docsCategory plugin
+ * @docsCategory AdminUiPlugin
  */
  */
 export class AdminUiPlugin implements VendurePlugin {
 export class AdminUiPlugin implements VendurePlugin {
     private server: Server;
     private server: Server;
@@ -81,7 +90,7 @@ export class AdminUiPlugin implements VendurePlugin {
     }
     }
 
 
     onClose(): Promise<void> {
     onClose(): Promise<void> {
-        return new Promise(resolve => this.server.close(resolve));
+        return new Promise(resolve => this.server.close(() => resolve()));
     }
     }
 
 
     /**
     /**
@@ -99,17 +108,39 @@ export class AdminUiPlugin implements VendurePlugin {
     }
     }
 
 
     private getAdminUiPath(): string {
     private getAdminUiPath(): string {
-        // attempt to read the index.html file from the Vendure dist bundle (as when installed
-        // in an end-user project)
-        const prodPath = path.join(__dirname, '../../../../admin-ui');
+        // attempt to read from the path location on a production npm install
+        const prodPath = path.join(__dirname, '../admin-ui');
         if (fs.existsSync(path.join(prodPath, 'index.html'))) {
         if (fs.existsSync(path.join(prodPath, 'index.html'))) {
             return prodPath;
             return prodPath;
         }
         }
-        // attempt to read from the built admin-ui in the /server/dist/ folder when developing
-        const devPath = path.join(__dirname, '../../../dist/admin-ui');
+        // attempt to read from the path on a development install
+        const devPath = path.join(__dirname, '../lib/admin-ui');
         if (fs.existsSync(path.join(devPath, 'index.html'))) {
         if (fs.existsSync(path.join(devPath, 'index.html'))) {
             return devPath;
             return devPath;
         }
         }
         throw new Error(`AdminUiPlugin: admin-ui app not found`);
         throw new Error(`AdminUiPlugin: admin-ui app not found`);
     }
     }
 }
 }
+
+export interface ProxyOptions {
+    route: string;
+    port: number;
+    hostname?: string;
+}
+
+/**
+ * Configures the proxy middleware which will be passed to the main Vendure server. This
+ * will proxy all asset requests to the dedicated asset server.
+ */
+function createProxyHandler(options: ProxyOptions, logging: boolean) {
+    const route = options.route.charAt(0) === '/' ? options.route : '/' + options.route;
+    const proxyHostname = options.hostname || 'localhost';
+    return proxy({
+        // TODO: how do we detect https?
+        target: `http://${proxyHostname}:${options.port}`,
+        pathRewrite: {
+            [`^${route}`]: `/`,
+        },
+        logLevel: logging ? 'info' : 'silent',
+    });
+}

+ 9 - 0
packages/admin-ui-plugin/tsconfig.build.json

@@ -0,0 +1,9 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "./lib"
+  },
+  "files": [
+    "./index.ts"
+  ]
+}

+ 11 - 0
packages/admin-ui-plugin/tsconfig.json

@@ -0,0 +1,11 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "declaration": true,
+    "removeComments": true,
+    "noLib": false,
+    "skipLibCheck": true,
+    "sourceMap": true,
+    "newLine": "LF"
+  }
+}

+ 1207 - 0
packages/admin-ui-plugin/yarn.lock

@@ -0,0 +1,1207 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@types/body-parser@*":
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c"
+  integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==
+  dependencies:
+    "@types/connect" "*"
+    "@types/node" "*"
+
+"@types/connect@*":
+  version "3.4.32"
+  resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28"
+  integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==
+  dependencies:
+    "@types/node" "*"
+
+"@types/express-serve-static-core@*":
+  version "4.16.2"
+  resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.2.tgz#5ee8a22e602005be6767df6b2cba9879df3f75aa"
+  integrity sha512-qgc8tjnDrc789rAQed8NoiFLV5VGcItA4yWNFphqGU0RcuuQngD00g3LHhWIK3HQ2XeDgVCmlNPDlqi3fWBHnQ==
+  dependencies:
+    "@types/node" "*"
+    "@types/range-parser" "*"
+
+"@types/express@^4.0.39":
+  version "4.16.1"
+  resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.1.tgz#d756bd1a85c34d87eaf44c888bad27ba8a4b7cf0"
+  integrity sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==
+  dependencies:
+    "@types/body-parser" "*"
+    "@types/express-serve-static-core" "*"
+    "@types/serve-static" "*"
+
+"@types/fs-extra@^5.0.4":
+  version "5.0.5"
+  resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.5.tgz#080d90a792f3fa2c5559eb44bd8ef840aae9104b"
+  integrity sha512-w7iqhDH9mN8eLClQOYTkhdYUOSpp25eXxfc6VbFOGtzxW34JcvctH2bKjj4jD4++z4R5iO5D+pg48W2e03I65A==
+  dependencies:
+    "@types/node" "*"
+
+"@types/http-proxy-middleware@^0.19.2":
+  version "0.19.2"
+  resolved "https://registry.yarnpkg.com/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz#1c44b96487cb2f333102b762c56a8f02241e85bd"
+  integrity sha512-aXcAs2VEaiHwlFlEqMJ+sNSFCO+wuWXcvdBk5Un7f0tUv1eTIIAmkd4S5D/Yi5JI0xofPpm9h3017TngbrLh7A==
+  dependencies:
+    "@types/connect" "*"
+    "@types/http-proxy" "*"
+    "@types/node" "*"
+
+"@types/http-proxy@*":
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.0.tgz#baf82ff6aa2723fd29f90e3ba1384e665006863e"
+  integrity sha512-l+s0IoxSHqhLFJPDHRfO235kgrCkvFD8JmdV/T9C4BKBYPIjrQopGFH4r7h2e3jQqgJRCthRCAZIxDoFnj1zwQ==
+  dependencies:
+    "@types/node" "*"
+
+"@types/mime@*":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
+  integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==
+
+"@types/node@*":
+  version "11.12.2"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-11.12.2.tgz#d7f302e74b10e9801d52852137f652d9ee235da8"
+  integrity sha512-c82MtnqWB/CqqK7/zit74Ob8H1dBdV7bK+BcErwtXbe0+nUGkgzq5NTDmRW/pAv2lFtmeNmW95b0zK2hxpeklg==
+
+"@types/range-parser@*":
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
+  integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
+
+"@types/serve-static@*":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48"
+  integrity sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==
+  dependencies:
+    "@types/express-serve-static-core" "*"
+    "@types/mime" "*"
+
+accepts@~1.3.5:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
+  integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I=
+  dependencies:
+    mime-types "~2.1.18"
+    negotiator "0.6.1"
+
+arr-diff@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+  integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+  integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+  integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-flatten@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+  integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
+
+array-unique@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+  integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+assign-symbols@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+  integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+atob@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base@^0.11.1:
+  version "0.11.2"
+  resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+  integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+  dependencies:
+    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"
+
+body-parser@1.18.3:
+  version "1.18.3"
+  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4"
+  integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=
+  dependencies:
+    bytes "3.0.0"
+    content-type "~1.0.4"
+    debug "2.6.9"
+    depd "~1.1.2"
+    http-errors "~1.6.3"
+    iconv-lite "0.4.23"
+    on-finished "~2.3.0"
+    qs "6.5.2"
+    raw-body "2.3.3"
+    type-is "~1.6.16"
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+braces@^2.3.1:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
+  integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+  dependencies:
+    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"
+
+bytes@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+  integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
+
+cache-base@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+  integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+  dependencies:
+    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"
+
+class-utils@^0.3.5:
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+  integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+  dependencies:
+    arr-union "^3.1.0"
+    define-property "^0.2.5"
+    isobject "^3.0.0"
+    static-extend "^0.1.1"
+
+collection-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+  integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+  dependencies:
+    map-visit "^1.0.0"
+    object-visit "^1.0.0"
+
+component-emitter@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
+  integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+content-disposition@0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
+  integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ=
+
+content-type@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
+  integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+
+cookie-signature@1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+  integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
+
+cookie@0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
+  integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=
+
+copy-descriptor@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+  integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+  dependencies:
+    ms "2.0.0"
+
+debug@^3.2.6:
+  version "3.2.6"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+  integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+  dependencies:
+    ms "^2.1.1"
+
+decode-uri-component@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+  integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+define-property@^0.2.5:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+  integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+  dependencies:
+    is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+  integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+  dependencies:
+    is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+  integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+  dependencies:
+    is-descriptor "^1.0.2"
+    isobject "^3.0.1"
+
+depd@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+  integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
+
+destroy@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
+  integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
+
+ee-first@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+  integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
+
+encodeurl@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+  integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+
+escape-html@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
+
+etag@~1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+  integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
+
+eventemitter3@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
+  integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==
+
+expand-brackets@^2.1.4:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+  integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+  dependencies:
+    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"
+
+express@^4.16.4:
+  version "4.16.4"
+  resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
+  integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==
+  dependencies:
+    accepts "~1.3.5"
+    array-flatten "1.1.1"
+    body-parser "1.18.3"
+    content-disposition "0.5.2"
+    content-type "~1.0.4"
+    cookie "0.3.1"
+    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.1"
+    fresh "0.5.2"
+    merge-descriptors "1.0.1"
+    methods "~1.1.2"
+    on-finished "~2.3.0"
+    parseurl "~1.3.2"
+    path-to-regexp "0.1.7"
+    proxy-addr "~2.0.4"
+    qs "6.5.2"
+    range-parser "~1.2.0"
+    safe-buffer "5.1.2"
+    send "0.16.2"
+    serve-static "1.13.2"
+    setprototypeof "1.1.0"
+    statuses "~1.4.0"
+    type-is "~1.6.16"
+    utils-merge "1.0.1"
+    vary "~1.1.2"
+
+extend-shallow@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+  integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+  dependencies:
+    is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+  integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+  dependencies:
+    assign-symbols "^1.0.0"
+    is-extendable "^1.0.1"
+
+extglob@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+  integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+  dependencies:
+    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"
+
+fill-range@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+  integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+    to-regex-range "^2.1.0"
+
+finalhandler@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
+  integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==
+  dependencies:
+    debug "2.6.9"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    on-finished "~2.3.0"
+    parseurl "~1.3.2"
+    statuses "~1.4.0"
+    unpipe "~1.0.0"
+
+follow-redirects@^1.0.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76"
+  integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==
+  dependencies:
+    debug "^3.2.6"
+
+for-in@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+  integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+forwarded@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
+  integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
+
+fragment-cache@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+  integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+  dependencies:
+    map-cache "^0.2.2"
+
+fresh@0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+  integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
+
+fs-extra@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
+  integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+get-value@^2.0.3, get-value@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+  integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+glob@^7.1.3:
+  version "7.1.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+  integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
+  dependencies:
+    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"
+
+graceful-fs@^4.1.2, graceful-fs@^4.1.6:
+  version "4.1.15"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
+  integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
+
+has-value@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+  integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+  dependencies:
+    get-value "^2.0.3"
+    has-values "^0.1.4"
+    isobject "^2.0.0"
+
+has-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+  integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+  dependencies:
+    get-value "^2.0.6"
+    has-values "^1.0.0"
+    isobject "^3.0.0"
+
+has-values@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+  integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+  integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+  dependencies:
+    is-number "^3.0.0"
+    kind-of "^4.0.0"
+
+http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
+  version "1.6.3"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
+  integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.3"
+    setprototypeof "1.1.0"
+    statuses ">= 1.4.0 < 2"
+
+http-proxy-middleware@^0.19.1:
+  version "0.19.1"
+  resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a"
+  integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==
+  dependencies:
+    http-proxy "^1.17.0"
+    is-glob "^4.0.0"
+    lodash "^4.17.11"
+    micromatch "^3.1.10"
+
+http-proxy@^1.17.0:
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a"
+  integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==
+  dependencies:
+    eventemitter3 "^3.0.0"
+    follow-redirects "^1.0.0"
+    requires-port "^1.0.0"
+
+iconv-lite@0.4.23:
+  version "0.4.23"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
+  integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+  integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
+ipaddr.js@1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e"
+  integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4=
+
+is-accessor-descriptor@^0.1.6:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+  integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+  integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-buffer@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+  integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-data-descriptor@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+  integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+  integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-descriptor@^0.1.0:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+  integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+  dependencies:
+    is-accessor-descriptor "^0.1.6"
+    is-data-descriptor "^0.1.4"
+    kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+  integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+  dependencies:
+    is-accessor-descriptor "^1.0.0"
+    is-data-descriptor "^1.0.0"
+    kind-of "^6.0.2"
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+  integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+  integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+  dependencies:
+    is-plain-object "^2.0.4"
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-glob@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+  integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-number@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+  integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+  integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+  dependencies:
+    isobject "^3.0.1"
+
+is-windows@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+  integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+isarray@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+  integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+  dependencies:
+    isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+  integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+  integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+  integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+  integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
+  integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
+
+lodash@^4.17.11:
+  version "4.17.11"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
+  integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
+
+map-cache@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+  integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+  integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+  dependencies:
+    object-visit "^1.0.0"
+
+media-typer@0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+  integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
+
+merge-descriptors@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+  integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
+
+methods@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+  integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
+
+micromatch@^3.1.10:
+  version "3.1.10"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+  integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+  dependencies:
+    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"
+
+mime-db@~1.38.0:
+  version "1.38.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad"
+  integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==
+
+mime-types@~2.1.18:
+  version "2.1.22"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd"
+  integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==
+  dependencies:
+    mime-db "~1.38.0"
+
+mime@1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
+  integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
+
+minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+mixin-deep@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
+  integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==
+  dependencies:
+    for-in "^1.0.2"
+    is-extendable "^1.0.1"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+  integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
+
+nanomatch@^1.2.9:
+  version "1.2.13"
+  resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
+  integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+  dependencies:
+    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@0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
+  integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
+
+object-copy@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+  integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+  dependencies:
+    copy-descriptor "^0.1.0"
+    define-property "^0.2.5"
+    kind-of "^3.0.3"
+
+object-visit@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+  integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+  dependencies:
+    isobject "^3.0.0"
+
+object.pick@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+  integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+  dependencies:
+    isobject "^3.0.1"
+
+on-finished@~2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+  integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
+  dependencies:
+    ee-first "1.1.1"
+
+once@^1.3.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
+parseurl@~1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
+  integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=
+
+pascalcase@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+  integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-to-regexp@0.1.7:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
+  integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
+
+posix-character-classes@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+  integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+proxy-addr@~2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
+  integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==
+  dependencies:
+    forwarded "~0.1.2"
+    ipaddr.js "1.8.0"
+
+qs@6.5.2:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+  integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+
+range-parser@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
+  integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=
+
+raw-body@2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
+  integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==
+  dependencies:
+    bytes "3.0.0"
+    http-errors "1.6.3"
+    iconv-lite "0.4.23"
+    unpipe "1.0.0"
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+  integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+  dependencies:
+    extend-shallow "^3.0.2"
+    safe-regex "^1.1.0"
+
+repeat-element@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+  integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+  integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+requires-port@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+  integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
+
+resolve-url@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+  integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+ret@~0.1.10:
+  version "0.1.15"
+  resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+  integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rimraf@^2.6.3:
+  version "2.6.3"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+  integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+  dependencies:
+    glob "^7.1.3"
+
+safe-buffer@5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+  integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+  dependencies:
+    ret "~0.1.10"
+
+"safer-buffer@>= 2.1.2 < 3":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+send@0.16.2:
+  version "0.16.2"
+  resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
+  integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==
+  dependencies:
+    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.6.2"
+    mime "1.4.1"
+    ms "2.0.0"
+    on-finished "~2.3.0"
+    range-parser "~1.2.0"
+    statuses "~1.4.0"
+
+serve-static@1.13.2:
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1"
+  integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==
+  dependencies:
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    parseurl "~1.3.2"
+    send "0.16.2"
+
+set-value@^0.4.3:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1"
+  integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.1"
+    to-object-path "^0.3.0"
+
+set-value@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274"
+  integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.3"
+    split-string "^3.0.1"
+
+setprototypeof@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
+  integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
+
+snapdragon-node@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+  integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+  dependencies:
+    define-property "^1.0.0"
+    isobject "^3.0.0"
+    snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+  integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+  dependencies:
+    kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+  integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+  dependencies:
+    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"
+
+source-map-resolve@^0.5.0:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
+  integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
+  dependencies:
+    atob "^2.1.1"
+    decode-uri-component "^0.2.0"
+    resolve-url "^0.2.1"
+    source-map-url "^0.4.0"
+    urix "^0.1.0"
+
+source-map-url@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+  integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@^0.5.6:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+  integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+split-string@^3.0.1, split-string@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+  integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+  dependencies:
+    extend-shallow "^3.0.0"
+
+static-extend@^0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+  integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+  dependencies:
+    define-property "^0.2.5"
+    object-copy "^0.1.0"
+
+"statuses@>= 1.4.0 < 2":
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+  integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+
+statuses@~1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
+  integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==
+
+to-object-path@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+  integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+  dependencies:
+    kind-of "^3.0.2"
+
+to-regex-range@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+  integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+  dependencies:
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+  integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+  dependencies:
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    regex-not "^1.0.2"
+    safe-regex "^1.1.0"
+
+type-is@~1.6.16:
+  version "1.6.16"
+  resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194"
+  integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==
+  dependencies:
+    media-typer "0.3.0"
+    mime-types "~2.1.18"
+
+typescript@^3.3.4000:
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6"
+  integrity sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q==
+
+union-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
+  integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=
+  dependencies:
+    arr-union "^3.1.0"
+    get-value "^2.0.6"
+    is-extendable "^0.1.1"
+    set-value "^0.4.3"
+
+universalify@^0.1.0:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+  integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+unpipe@1.0.0, unpipe@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+  integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
+
+unset-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+  integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+  dependencies:
+    has-value "^0.3.1"
+    isobject "^3.0.0"
+
+urix@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+  integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+use@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+  integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+utils-merge@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+  integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
+
+vary@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+  integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=

+ 1 - 0
packages/asset-server-plugin/.gitignore

@@ -0,0 +1 @@
+lib

+ 0 - 0
packages/asset-server-plugin/.npmignore


+ 7 - 0
packages/asset-server-plugin/README.md

@@ -0,0 +1,7 @@
+# Vendure AssetServerPlugin
+
+The `AssetServerPlugin` serves assets (images and other files) from the local file system. It can also perform on-the-fly image transformations and caches the results for subsequent calls.
+
+`npm install @vendure/asset-server-plugin`
+
+For documentation, see [www.vendure.io/docs/plugins/asset-server-plugin](www.vendure.io/docs/plugins/asset-server-plugin)

+ 2 - 0
packages/asset-server-plugin/index.ts

@@ -0,0 +1,2 @@
+export * from './src/plugin';
+export * from './src/sharp-asset-preview-strategy';

+ 30 - 0
packages/asset-server-plugin/package.json

@@ -0,0 +1,30 @@
+{
+  "name": "@vendure/asset-server-plugin",
+  "version": "0.1.0-alpha.1",
+  "main": "lib/index.js",
+  "types": "lib/index.d.ts",
+  "files": ["lib/**/*"],
+  "license": "MIT",
+  "scripts": {
+    "build": "rimraf lib && tsc -p ./tsconfig.build.json"
+  },
+  "devDependencies": {
+    "@types/express": "^4.0.39",
+    "@types/fs-extra": "^5.0.4",
+    "@types/http-proxy-middleware": "^0.19.2",
+    "@types/sharp": "^0.22.1",
+    "@vendure/common": ">=0.1.0-alpha.1",
+    "@vendure/core": ">=0.1.0-alpha.1",
+    "express": "^4.16.4",
+    "rimraf": "^2.6.3"
+  },
+  "dependencies": {
+    "fs-extra": "^7.0.1",
+    "http-proxy-middleware": "^0.19.1",
+    "sharp": "^0.22.0",
+    "typescript": "^3.3.4000"
+  },
+  "peerDependencies": {
+    "@vendure/core": ">=0.1.0-alpha.1"
+  }
+}

+ 0 - 0
server/src/plugin/default-asset-server-plugin/file-icon.png → packages/asset-server-plugin/src/file-icon.png


+ 0 - 0
server/src/plugin/default-asset-server-plugin/file-icon.psd → packages/asset-server-plugin/src/file-icon.psd


+ 116 - 23
server/src/plugin/default-asset-server-plugin/default-asset-server-plugin.ts → packages/asset-server-plugin/src/plugin.ts

@@ -1,14 +1,10 @@
+import { AssetStorageStrategy, InjectorFn, LocalAssetStorageStrategy, VendureConfig, VendurePlugin } from '@vendure/core';
 import express, { NextFunction, Request, Response } from 'express';
 import express, { NextFunction, Request, Response } from 'express';
 import { Server } from 'http';
 import { Server } from 'http';
+import proxy from 'http-proxy-middleware';
 import path from 'path';
 import path from 'path';
 
 
-import { AssetStorageStrategy } from '../../config/asset-storage-strategy/asset-storage-strategy';
-import { VendureConfig } from '../../config/vendure-config';
-import { InjectorFn, VendurePlugin } from '../../config/vendure-plugin/vendure-plugin';
-import { createProxyHandler } from '../plugin-utils';
-
-import { DefaultAssetPreviewStrategy } from './default-asset-preview-strategy';
-import { DefaultAssetStorageStrategy } from './default-asset-storage-strategy';
+import { SharpAssetPreviewStrategy } from './sharp-asset-preview-strategy';
 import { transformImage } from './transform-image';
 import { transformImage } from './transform-image';
 
 
 /**
 /**
@@ -20,13 +16,13 @@ import { transformImage } from './transform-image';
  * * resize: Preserving aspect ratio, resizes the image to be as large as possible
  * * resize: Preserving aspect ratio, resizes the image to be as large as possible
  * while ensuring its dimensions are less than or equal to both those specified.
  * while ensuring its dimensions are less than or equal to both those specified.
  *
  *
- * @docsCategory plugin
+ * @docsCategory AssetServerPlugin
  */
  */
 export type ImageTransformMode = 'crop' | 'resize';
 export type ImageTransformMode = 'crop' | 'resize';
 
 
 /**
 /**
  * @description
  * @description
- * A configuration option for an image size preset for the DefaultAssetServerPlugin.
+ * A configuration option for an image size preset for the AssetServerPlugin.
  *
  *
  * Presets allow a shorthand way to generate a thumbnail preview of an asset. For example,
  * Presets allow a shorthand way to generate a thumbnail preview of an asset. For example,
  * the built-in "tiny" preset generates a 50px x 50px cropped preview, which can be accessed
  * the built-in "tiny" preset generates a 50px x 50px cropped preview, which can be accessed
@@ -38,7 +34,7 @@ export type ImageTransformMode = 'crop' | 'resize';
  *
  *
  * `http://localhost:3000/assets/some-asset.jpg?w=50&h=50&mode=crop`
  * `http://localhost:3000/assets/some-asset.jpg?w=50&h=50&mode=crop`
  *
  *
- * @docsCategory plugin
+ * @docsCategory AssetServerPlugin
  */
  */
 export interface ImageTransformPreset {
 export interface ImageTransformPreset {
     name: string;
     name: string;
@@ -49,15 +45,15 @@ export interface ImageTransformPreset {
 
 
 /**
 /**
  * @description
  * @description
- * The configuration options for the DefaultAssetServerPlugin.
+ * The configuration options for the AssetServerPlugin.
  *
  *
- * @docsCategory plugin
+ * @docsCategory AssetServerPlugin
  */
  */
-export interface DefaultAssetServerOptions {
+export interface AssetServerOptions {
     hostname?: string;
     hostname?: string;
     /**
     /**
      * @description
      * @description
-     * The local port that the server will run on. Note that the DefaultAssetServerPlugin
+     * The local port that the server will run on. Note that the AssetServerPlugin
      * includes a proxy server which allows the asset server to be accessed on the same
      * includes a proxy server which allows the asset server to be accessed on the same
      * port as the main Vendure server.
      * port as the main Vendure server.
      */
      */
@@ -94,11 +90,75 @@ export interface DefaultAssetServerOptions {
 }
 }
 
 
 /**
 /**
- * The DefaultAssetServerPlugin instantiates a static Express server which is used to
- * serve the assets. It can also perform on-the-fly image transformations and caches the
- * results for subsequent calls.
+ * @description
+ * The `AssetServerPlugin` serves assets (images and other files) from the local file system. It can also perform on-the-fly image transformations
+ * and caches the results for subsequent calls.
+ *
+ * @example
+ * ```ts
+ * const config: VendureConfig = {
+ *   // Add an instance of the plugin to the plugins array
+ *   plugins: [
+ *     new AssetServerPlugin({
+ *       route: 'assets',
+ *       assetUploadDir: path.join(__dirname, 'assets'),
+ *       port: 4000,
+ *     }),
+ *   ],
+ * };
+ * ```
+ *
+ * The full configuration is documented at [AssetServerOptions]({{< relref "asset-server-options" >}})
+ *
+ * ## Image transformation
+ *
+ * Asset preview images can be transformed (resized & cropped) on the fly by appending query parameters to the url:
+ *
+ * `http://localhost:3000/assets/some-asset.jpg?w=500&h=300&mode=resize`
+ *
+ * The above URL will return `some-asset.jpg`, resized to fit in the bounds of a 500px x 300px rectangle.
+ *
+ * ### Preview mode
+ *
+ * The `mode` parameter can be either `crop` or `resize`. See the [ImageTransformMode]({{< relref "image-transform-mode" >}}) docs for details.
+ *
+ * ### Transform presets
+ *
+ * Presets can be defined which allow a single preset name to be used instead of specifying the width, height and mode. Presets are
+ * configured via the AssetServerOptions [presets property]({{< relref "asset-server-options" >}}#presets).
+ *
+ * For example, defining the following preset:
+ *
+ * ```ts
+ * new AssetServerPlugin({
+ *   // ...
+ *   presets: [
+ *     { name: 'my-preset', width: 85, height: 85, mode: 'crop' },
+ *   ],
+ * }),
+ * ```
+ *
+ * means that a request to:
+ *
+ * `http://localhost:3000/assets/some-asset.jpg?preset=my-preset`
+ *
+ * is equivalent to:
+ *
+ * `http://localhost:3000/assets/some-asset.jpg?w=85&h=85&mode=crop`
+ *
+ * The AssetServerPlugin comes pre-configured with the following presets:
+ *
+ * name | width | height | mode
+ * -----|-------|--------|-----
+ * tiny | 50px | 50px | crop
+ * thumb | 150px | 150px | crop
+ * small | 300px | 300px | resize
+ * medium | 500px | 500px | resize
+ * large | 800px | 800px | resize
+ *
+ * @docsCategory AssetServerPlugin
  */
  */
-export class DefaultAssetServerPlugin implements VendurePlugin {
+export class AssetServerPlugin implements VendurePlugin {
     private server: Server;
     private server: Server;
     private assetStorage: AssetStorageStrategy;
     private assetStorage: AssetStorageStrategy;
     private readonly cacheDir = 'cache';
     private readonly cacheDir = 'cache';
@@ -110,7 +170,7 @@ export class DefaultAssetServerPlugin implements VendurePlugin {
         { name: 'large', width: 800, height: 800, mode: 'resize' },
         { name: 'large', width: 800, height: 800, mode: 'resize' },
     ];
     ];
 
 
-    constructor(private options: DefaultAssetServerOptions) {
+    constructor(private options: AssetServerOptions) {
         if (options.presets) {
         if (options.presets) {
             for (const preset of options.presets) {
             for (const preset of options.presets) {
                 const existingIndex = this.presets.findIndex(p => p.name === preset.name);
                 const existingIndex = this.presets.findIndex(p => p.name === preset.name);
@@ -124,8 +184,8 @@ export class DefaultAssetServerPlugin implements VendurePlugin {
     }
     }
 
 
     configure(config: Required<VendureConfig>) {
     configure(config: Required<VendureConfig>) {
-        this.assetStorage = new DefaultAssetStorageStrategy(this.options.assetUploadDir, this.options.route);
-        config.assetOptions.assetPreviewStrategy = new DefaultAssetPreviewStrategy({
+        this.assetStorage = this.createAssetStorageStrategy();
+        config.assetOptions.assetPreviewStrategy = new SharpAssetPreviewStrategy({
             maxWidth: this.options.previewMaxWidth || 1600,
             maxWidth: this.options.previewMaxWidth || 1600,
             maxHeight: this.options.previewMaxHeight || 1600,
             maxHeight: this.options.previewMaxHeight || 1600,
         });
         });
@@ -142,7 +202,17 @@ export class DefaultAssetServerPlugin implements VendurePlugin {
     }
     }
 
 
     onClose(): Promise<void> {
     onClose(): Promise<void> {
-        return new Promise(resolve => this.server.close(resolve));
+        return new Promise(resolve => { this.server.close(() => resolve()); });
+    }
+
+    private createAssetStorageStrategy() {
+        const toAbsoluteUrlFn = (request: Request, identifier: string): string => {
+            if (!identifier) {
+                return '';
+            }
+            return `${request.protocol}://${request.get('host')}/${this.options.route}/${identifier}`;
+        };
+        return new LocalAssetStorageStrategy(this.options.assetUploadDir, toAbsoluteUrlFn);
     }
     }
 
 
     /**
     /**
@@ -170,7 +240,7 @@ export class DefaultAssetServerPlugin implements VendurePlugin {
      * transformed image, save it to cache, and serve the result as a response.
      * transformed image, save it to cache, and serve the result as a response.
      */
      */
     private generateTransformedImage() {
     private generateTransformedImage() {
-        return async (err, req: Request, res: Response, next: NextFunction) => {
+        return async (err: any, req: Request, res: Response, next: NextFunction) => {
             if (err && err.status === 404) {
             if (err && err.status === 404) {
                 if (req.query) {
                 if (req.query) {
                     let file: Buffer;
                     let file: Buffer;
@@ -212,3 +282,26 @@ export class DefaultAssetServerPlugin implements VendurePlugin {
         return `${baseName}${suffix}${ext}`;
         return `${baseName}${suffix}${ext}`;
     }
     }
 }
 }
+
+export interface ProxyOptions {
+    route: string;
+    port: number;
+    hostname?: string;
+}
+
+/**
+ * Configures the proxy middleware which will be passed to the main Vendure server. This
+ * will proxy all asset requests to the dedicated asset server.
+ */
+function createProxyHandler(options: ProxyOptions, logging: boolean) {
+    const route = options.route.charAt(0) === '/' ? options.route : '/' + options.route;
+    const proxyHostname = options.hostname || 'localhost';
+    return proxy({
+        // TODO: how do we detect https?
+        target: `http://${proxyHostname}:${options.port}`,
+        pathRewrite: {
+            [`^${route}`]: `/`,
+        },
+        logLevel: logging ? 'info' : 'silent',
+    });
+}

+ 3 - 5
server/src/plugin/default-asset-server-plugin/default-asset-preview-strategy.ts → packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts

@@ -1,11 +1,9 @@
+import { AssetType } from '@vendure/common/lib/generated-types';
+import { AssetPreviewStrategy, getAssetType } from '@vendure/core';
 import path from 'path';
 import path from 'path';
 import sharp from 'sharp';
 import sharp from 'sharp';
 
 
-import { AssetType } from '../../../../shared/generated-types';
-import { getAssetType } from '../../common/utils';
-import { AssetPreviewStrategy } from '../../config/asset-preview-strategy/asset-preview-strategy';
-
-export class DefaultAssetPreviewStrategy implements AssetPreviewStrategy {
+export class SharpAssetPreviewStrategy implements AssetPreviewStrategy {
     constructor(
     constructor(
         private config: {
         private config: {
             maxHeight: number;
             maxHeight: number;

+ 1 - 1
server/src/plugin/default-asset-server-plugin/transform-image.ts → packages/asset-server-plugin/src/transform-image.ts

@@ -1,7 +1,7 @@
 import sharp from 'sharp';
 import sharp from 'sharp';
 import { ResizeOptions } from 'sharp';
 import { ResizeOptions } from 'sharp';
 
 
-import { ImageTransformPreset } from './default-asset-server-plugin';
+import { ImageTransformPreset } from './plugin';
 
 
 /**
 /**
  * Applies transforms to the given image according to the query params passed.
  * Applies transforms to the given image according to the query params passed.

+ 9 - 0
packages/asset-server-plugin/tsconfig.build.json

@@ -0,0 +1,9 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "./lib"
+  },
+  "files": [
+    "./index.ts"
+  ]
+}

+ 11 - 0
packages/asset-server-plugin/tsconfig.json

@@ -0,0 +1,11 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "declaration": true,
+    "removeComments": true,
+    "noLib": false,
+    "skipLibCheck": true,
+    "sourceMap": true,
+    "newLine": "LF"
+  }
+}

+ 1755 - 0
packages/asset-server-plugin/yarn.lock

@@ -0,0 +1,1755 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@types/body-parser@*":
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c"
+  integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==
+  dependencies:
+    "@types/connect" "*"
+    "@types/node" "*"
+
+"@types/connect@*":
+  version "3.4.32"
+  resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28"
+  integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==
+  dependencies:
+    "@types/node" "*"
+
+"@types/express-serve-static-core@*":
+  version "4.16.2"
+  resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.2.tgz#5ee8a22e602005be6767df6b2cba9879df3f75aa"
+  integrity sha512-qgc8tjnDrc789rAQed8NoiFLV5VGcItA4yWNFphqGU0RcuuQngD00g3LHhWIK3HQ2XeDgVCmlNPDlqi3fWBHnQ==
+  dependencies:
+    "@types/node" "*"
+    "@types/range-parser" "*"
+
+"@types/express@^4.0.39":
+  version "4.16.1"
+  resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.1.tgz#d756bd1a85c34d87eaf44c888bad27ba8a4b7cf0"
+  integrity sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==
+  dependencies:
+    "@types/body-parser" "*"
+    "@types/express-serve-static-core" "*"
+    "@types/serve-static" "*"
+
+"@types/fs-extra@^5.0.4":
+  version "5.0.5"
+  resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.5.tgz#080d90a792f3fa2c5559eb44bd8ef840aae9104b"
+  integrity sha512-w7iqhDH9mN8eLClQOYTkhdYUOSpp25eXxfc6VbFOGtzxW34JcvctH2bKjj4jD4++z4R5iO5D+pg48W2e03I65A==
+  dependencies:
+    "@types/node" "*"
+
+"@types/http-proxy-middleware@^0.19.2":
+  version "0.19.2"
+  resolved "https://registry.yarnpkg.com/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz#1c44b96487cb2f333102b762c56a8f02241e85bd"
+  integrity sha512-aXcAs2VEaiHwlFlEqMJ+sNSFCO+wuWXcvdBk5Un7f0tUv1eTIIAmkd4S5D/Yi5JI0xofPpm9h3017TngbrLh7A==
+  dependencies:
+    "@types/connect" "*"
+    "@types/http-proxy" "*"
+    "@types/node" "*"
+
+"@types/http-proxy@*":
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.0.tgz#baf82ff6aa2723fd29f90e3ba1384e665006863e"
+  integrity sha512-l+s0IoxSHqhLFJPDHRfO235kgrCkvFD8JmdV/T9C4BKBYPIjrQopGFH4r7h2e3jQqgJRCthRCAZIxDoFnj1zwQ==
+  dependencies:
+    "@types/node" "*"
+
+"@types/mime@*":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
+  integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==
+
+"@types/node@*":
+  version "11.12.1"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-11.12.1.tgz#d90123f6c61fdf2f7cddd286ddae891586dd3488"
+  integrity sha512-sKDlqv6COJrR7ar0+GqqhrXQDzQlMcqMnF2iEU6m9hLo8kxozoAGUazwPyELHlRVmjsbvlnGXjnzyptSXVmceA==
+
+"@types/range-parser@*":
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
+  integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
+
+"@types/serve-static@*":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48"
+  integrity sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==
+  dependencies:
+    "@types/express-serve-static-core" "*"
+    "@types/mime" "*"
+
+"@types/sharp@^0.22.1":
+  version "0.22.1"
+  resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.22.1.tgz#4fccaa9cc580d859916bee693aa7ae7447d0e0ba"
+  integrity sha512-Reri4hhX77JBx6HWt2a2CLO0xjHMFHDaeBYMslUsTSHGRFQnk+VpoH25g7/L6QzyyNNmxTekK+j+lFFjK3OkqA==
+  dependencies:
+    "@types/node" "*"
+
+accepts@~1.3.5:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
+  integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I=
+  dependencies:
+    mime-types "~2.1.18"
+    negotiator "0.6.1"
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+  integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+aproba@^1.0.3:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+  integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
+
+are-we-there-yet@~1.1.2:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
+  integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^2.0.6"
+
+arr-diff@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+  integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+  integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+  integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-flatten@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+  integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
+
+array-unique@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+  integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+assign-symbols@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+  integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+atob@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base@^0.11.1:
+  version "0.11.2"
+  resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+  integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+  dependencies:
+    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"
+
+bindings@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
+  integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
+  dependencies:
+    file-uri-to-path "1.0.0"
+
+bl@^1.0.0:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
+  integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==
+  dependencies:
+    readable-stream "^2.3.5"
+    safe-buffer "^5.1.1"
+
+body-parser@1.18.3:
+  version "1.18.3"
+  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4"
+  integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=
+  dependencies:
+    bytes "3.0.0"
+    content-type "~1.0.4"
+    debug "2.6.9"
+    depd "~1.1.2"
+    http-errors "~1.6.3"
+    iconv-lite "0.4.23"
+    on-finished "~2.3.0"
+    qs "6.5.2"
+    raw-body "2.3.3"
+    type-is "~1.6.16"
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+braces@^2.3.1:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
+  integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+  dependencies:
+    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"
+
+buffer-alloc-unsafe@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
+  integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
+
+buffer-alloc@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
+  integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
+  dependencies:
+    buffer-alloc-unsafe "^1.1.0"
+    buffer-fill "^1.0.0"
+
+buffer-fill@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
+  integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
+
+bytes@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+  integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
+
+cache-base@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+  integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+  dependencies:
+    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"
+
+chownr@^1.0.1, chownr@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494"
+  integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==
+
+class-utils@^0.3.5:
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+  integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+  dependencies:
+    arr-union "^3.1.0"
+    define-property "^0.2.5"
+    isobject "^3.0.0"
+    static-extend "^0.1.1"
+
+code-point-at@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+  integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
+
+collection-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+  integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+  dependencies:
+    map-visit "^1.0.0"
+    object-visit "^1.0.0"
+
+color-convert@^1.9.1:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+color-name@^1.0.0:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-string@^1.5.2:
+  version "1.5.3"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
+  integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
+  dependencies:
+    color-name "^1.0.0"
+    simple-swizzle "^0.2.2"
+
+color@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/color/-/color-3.1.0.tgz#d8e9fb096732875774c84bf922815df0308d0ffc"
+  integrity sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg==
+  dependencies:
+    color-convert "^1.9.1"
+    color-string "^1.5.2"
+
+component-emitter@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
+  integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+  integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
+
+content-disposition@0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
+  integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ=
+
+content-type@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
+  integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+
+cookie-signature@1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+  integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
+
+cookie@0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
+  integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=
+
+copy-descriptor@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+  integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+  dependencies:
+    ms "2.0.0"
+
+debug@^3.2.6:
+  version "3.2.6"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+  integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+  dependencies:
+    ms "^2.1.1"
+
+decode-uri-component@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+  integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+decompress-response@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
+  integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
+  dependencies:
+    mimic-response "^1.0.0"
+
+deep-extend@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
+  integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+
+define-property@^0.2.5:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+  integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+  dependencies:
+    is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+  integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+  dependencies:
+    is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+  integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+  dependencies:
+    is-descriptor "^1.0.2"
+    isobject "^3.0.1"
+
+delegates@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+  integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
+
+depd@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+  integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
+
+destroy@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
+  integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
+
+detect-libc@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+  integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
+
+ee-first@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+  integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
+
+encodeurl@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+  integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+
+end-of-stream@^1.0.0, end-of-stream@^1.1.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
+  integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
+  dependencies:
+    once "^1.4.0"
+
+escape-html@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
+
+etag@~1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+  integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
+
+eventemitter3@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
+  integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==
+
+expand-brackets@^2.1.4:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+  integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+  dependencies:
+    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"
+
+expand-template@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
+  integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
+
+express@^4.16.4:
+  version "4.16.4"
+  resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
+  integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==
+  dependencies:
+    accepts "~1.3.5"
+    array-flatten "1.1.1"
+    body-parser "1.18.3"
+    content-disposition "0.5.2"
+    content-type "~1.0.4"
+    cookie "0.3.1"
+    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.1"
+    fresh "0.5.2"
+    merge-descriptors "1.0.1"
+    methods "~1.1.2"
+    on-finished "~2.3.0"
+    parseurl "~1.3.2"
+    path-to-regexp "0.1.7"
+    proxy-addr "~2.0.4"
+    qs "6.5.2"
+    range-parser "~1.2.0"
+    safe-buffer "5.1.2"
+    send "0.16.2"
+    serve-static "1.13.2"
+    setprototypeof "1.1.0"
+    statuses "~1.4.0"
+    type-is "~1.6.16"
+    utils-merge "1.0.1"
+    vary "~1.1.2"
+
+extend-shallow@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+  integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+  dependencies:
+    is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+  integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+  dependencies:
+    assign-symbols "^1.0.0"
+    is-extendable "^1.0.1"
+
+extglob@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+  integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+  dependencies:
+    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"
+
+file-uri-to-path@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+  integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
+
+fill-range@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+  integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+    to-regex-range "^2.1.0"
+
+finalhandler@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
+  integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==
+  dependencies:
+    debug "2.6.9"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    on-finished "~2.3.0"
+    parseurl "~1.3.2"
+    statuses "~1.4.0"
+    unpipe "~1.0.0"
+
+follow-redirects@^1.0.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76"
+  integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==
+  dependencies:
+    debug "^3.2.6"
+
+for-in@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+  integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+forwarded@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
+  integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
+
+fragment-cache@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+  integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+  dependencies:
+    map-cache "^0.2.2"
+
+fresh@0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+  integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
+
+fs-constants@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
+  integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
+
+fs-copy-file-sync@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/fs-copy-file-sync/-/fs-copy-file-sync-1.1.1.tgz#11bf32c096c10d126e5f6b36d06eece776062918"
+  integrity sha512-2QY5eeqVv4m2PfyMiEuy9adxNP+ajf+8AR05cEi+OAzPcOj90hvFImeZhTmKLBgSd9EvG33jsD7ZRxsx9dThkQ==
+
+fs-extra@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
+  integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
+fs-minipass@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
+  integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==
+  dependencies:
+    minipass "^2.2.1"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+gauge@~2.7.3:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+  integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
+  dependencies:
+    aproba "^1.0.3"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.0"
+    object-assign "^4.1.0"
+    signal-exit "^3.0.0"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wide-align "^1.1.0"
+
+get-value@^2.0.3, get-value@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+  integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+github-from-package@0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
+  integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
+
+glob@^7.1.3:
+  version "7.1.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+  integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
+  dependencies:
+    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"
+
+graceful-fs@^4.1.2, graceful-fs@^4.1.6:
+  version "4.1.15"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
+  integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
+
+has-unicode@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+  integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+
+has-value@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+  integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+  dependencies:
+    get-value "^2.0.3"
+    has-values "^0.1.4"
+    isobject "^2.0.0"
+
+has-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+  integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+  dependencies:
+    get-value "^2.0.6"
+    has-values "^1.0.0"
+    isobject "^3.0.0"
+
+has-values@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+  integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+  integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+  dependencies:
+    is-number "^3.0.0"
+    kind-of "^4.0.0"
+
+http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
+  version "1.6.3"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
+  integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.3"
+    setprototypeof "1.1.0"
+    statuses ">= 1.4.0 < 2"
+
+http-proxy-middleware@^0.19.1:
+  version "0.19.1"
+  resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a"
+  integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==
+  dependencies:
+    http-proxy "^1.17.0"
+    is-glob "^4.0.0"
+    lodash "^4.17.11"
+    micromatch "^3.1.10"
+
+http-proxy@^1.17.0:
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a"
+  integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==
+  dependencies:
+    eventemitter3 "^3.0.0"
+    follow-redirects "^1.0.0"
+    requires-port "^1.0.0"
+
+iconv-lite@0.4.23:
+  version "0.4.23"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
+  integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@2.0.3, inherits@~2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+  integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
+ini@~1.3.0:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+  integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+
+ipaddr.js@1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e"
+  integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4=
+
+is-accessor-descriptor@^0.1.6:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+  integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+  integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-arrayish@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+  integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
+
+is-buffer@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+  integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-data-descriptor@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+  integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+  integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-descriptor@^0.1.0:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+  integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+  dependencies:
+    is-accessor-descriptor "^0.1.6"
+    is-data-descriptor "^0.1.4"
+    kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+  integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+  dependencies:
+    is-accessor-descriptor "^1.0.0"
+    is-data-descriptor "^1.0.0"
+    kind-of "^6.0.2"
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+  integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+  integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+  dependencies:
+    is-plain-object "^2.0.4"
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+  integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+  integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-glob@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+  integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-number@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+  integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+  integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+  dependencies:
+    isobject "^3.0.1"
+
+is-windows@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+  integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+isarray@1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+  integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+  dependencies:
+    isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+  integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+  integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+  integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+  integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
+  integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
+
+lodash@^4.17.11:
+  version "4.17.11"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
+  integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
+
+map-cache@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+  integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+  integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+  dependencies:
+    object-visit "^1.0.0"
+
+media-typer@0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+  integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
+
+merge-descriptors@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+  integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
+
+methods@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+  integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
+
+micromatch@^3.1.10:
+  version "3.1.10"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+  integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+  dependencies:
+    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"
+
+mime-db@~1.38.0:
+  version "1.38.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad"
+  integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==
+
+mime-types@~2.1.18:
+  version "2.1.22"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd"
+  integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==
+  dependencies:
+    mime-db "~1.38.0"
+
+mime@1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
+  integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
+
+mimic-response@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
+  integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
+
+minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+  integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+
+minimist@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+
+minipass@^2.2.1, minipass@^2.3.4:
+  version "2.3.5"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
+  integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
+  dependencies:
+    safe-buffer "^5.1.2"
+    yallist "^3.0.0"
+
+minizlib@^1.1.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614"
+  integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
+  dependencies:
+    minipass "^2.2.1"
+
+mixin-deep@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
+  integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==
+  dependencies:
+    for-in "^1.0.2"
+    is-extendable "^1.0.1"
+
+mkdirp@^0.5.0, mkdirp@^0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+  integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+  dependencies:
+    minimist "0.0.8"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+  integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
+
+nan@^2.13.1:
+  version "2.13.2"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7"
+  integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==
+
+nanomatch@^1.2.9:
+  version "1.2.13"
+  resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
+  integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+  dependencies:
+    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"
+
+napi-build-utils@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508"
+  integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==
+
+negotiator@0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
+  integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
+
+node-abi@^2.7.0:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.7.1.tgz#a8997ae91176a5fbaa455b194976e32683cda643"
+  integrity sha512-OV8Bq1OrPh6z+Y4dqwo05HqrRL9YNF7QVMRfq1/pguwKLG+q9UB/Lk0x5qXjO23JjJg+/jqCHSTaG1P3tfKfuw==
+  dependencies:
+    semver "^5.4.1"
+
+noop-logger@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2"
+  integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=
+
+npmlog@^4.0.1, npmlog@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+  integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+  dependencies:
+    are-we-there-yet "~1.1.2"
+    console-control-strings "~1.1.0"
+    gauge "~2.7.3"
+    set-blocking "~2.0.0"
+
+number-is-nan@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+  integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
+
+object-assign@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-copy@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+  integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+  dependencies:
+    copy-descriptor "^0.1.0"
+    define-property "^0.2.5"
+    kind-of "^3.0.3"
+
+object-visit@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+  integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+  dependencies:
+    isobject "^3.0.0"
+
+object.pick@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+  integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+  dependencies:
+    isobject "^3.0.1"
+
+on-finished@~2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+  integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
+  dependencies:
+    ee-first "1.1.1"
+
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
+os-homedir@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+  integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
+
+parseurl@~1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
+  integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=
+
+pascalcase@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+  integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-to-regexp@0.1.7:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
+  integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
+
+posix-character-classes@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+  integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+prebuild-install@^5.2.5:
+  version "5.2.5"
+  resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.2.5.tgz#c7485911fe98950b7f7cd15bb9daee11b875cc44"
+  integrity sha512-6uZgMVg7yDfqlP5CPurVhtq3hUKBFNufiar4J5hZrlHTo59DDBEtyxw01xCdFss9j0Zb9+qzFVf/s4niayba3w==
+  dependencies:
+    detect-libc "^1.0.3"
+    expand-template "^2.0.3"
+    github-from-package "0.0.0"
+    minimist "^1.2.0"
+    mkdirp "^0.5.1"
+    napi-build-utils "^1.0.1"
+    node-abi "^2.7.0"
+    noop-logger "^0.1.1"
+    npmlog "^4.0.1"
+    os-homedir "^1.0.1"
+    pump "^2.0.1"
+    rc "^1.2.7"
+    simple-get "^2.7.0"
+    tar-fs "^1.13.0"
+    tunnel-agent "^0.6.0"
+    which-pm-runs "^1.0.0"
+
+process-nextick-args@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
+  integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==
+
+proxy-addr@~2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
+  integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==
+  dependencies:
+    forwarded "~0.1.2"
+    ipaddr.js "1.8.0"
+
+pump@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954"
+  integrity sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+pump@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
+  integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+qs@6.5.2:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+  integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+
+range-parser@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
+  integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=
+
+raw-body@2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
+  integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==
+  dependencies:
+    bytes "3.0.0"
+    http-errors "1.6.3"
+    iconv-lite "0.4.23"
+    unpipe "1.0.0"
+
+rc@^1.2.7:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
+  integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
+  dependencies:
+    deep-extend "^0.6.0"
+    ini "~1.3.0"
+    minimist "^1.2.0"
+    strip-json-comments "~2.0.1"
+
+readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.5:
+  version "2.3.6"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
+  integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
+  dependencies:
+    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"
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+  integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+  dependencies:
+    extend-shallow "^3.0.2"
+    safe-regex "^1.1.0"
+
+repeat-element@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+  integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+  integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+requires-port@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+  integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
+
+resolve-url@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+  integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+ret@~0.1.10:
+  version "0.1.15"
+  resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+  integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rimraf@^2.6.3:
+  version "2.6.3"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+  integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+  dependencies:
+    glob "^7.1.3"
+
+safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+  integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+  dependencies:
+    ret "~0.1.10"
+
+"safer-buffer@>= 2.1.2 < 3":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+semver@^5.4.1, semver@^5.6.0:
+  version "5.7.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
+  integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
+
+send@0.16.2:
+  version "0.16.2"
+  resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
+  integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==
+  dependencies:
+    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.6.2"
+    mime "1.4.1"
+    ms "2.0.0"
+    on-finished "~2.3.0"
+    range-parser "~1.2.0"
+    statuses "~1.4.0"
+
+serve-static@1.13.2:
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1"
+  integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==
+  dependencies:
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    parseurl "~1.3.2"
+    send "0.16.2"
+
+set-blocking@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+  integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+set-value@^0.4.3:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1"
+  integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.1"
+    to-object-path "^0.3.0"
+
+set-value@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274"
+  integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.3"
+    split-string "^3.0.1"
+
+setprototypeof@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
+  integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
+
+sharp@^0.22.0:
+  version "0.22.0"
+  resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.22.0.tgz#cf4cfcb019941fd06ac24555d9f5bc84536d29be"
+  integrity sha512-yInpiWYvVbE0hJylso2Q2A7QaYFBxGdSlVVHGeUf1F9JsQNAUpmaqdnX54TImgKbSCy9mQpEAoGm1pcKCZhCsQ==
+  dependencies:
+    bindings "^1.5.0"
+    color "^3.1.0"
+    detect-libc "^1.0.3"
+    fs-copy-file-sync "^1.1.1"
+    nan "^2.13.1"
+    npmlog "^4.1.2"
+    prebuild-install "^5.2.5"
+    semver "^5.6.0"
+    simple-get "^3.0.3"
+    tar "^4.4.8"
+    tunnel-agent "^0.6.0"
+
+signal-exit@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+
+simple-concat@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6"
+  integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=
+
+simple-get@^2.7.0:
+  version "2.8.1"
+  resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d"
+  integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==
+  dependencies:
+    decompress-response "^3.3.0"
+    once "^1.3.1"
+    simple-concat "^1.0.0"
+
+simple-get@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.0.3.tgz#924528ac3f9d7718ce5e9ec1b1a69c0be4d62efa"
+  integrity sha512-Wvre/Jq5vgoz31Z9stYWPLn0PqRqmBDpFSdypAnHu5AvRVCYPRYGnvryNLiXu8GOBNDH82J2FRHUGMjjHUpXFw==
+  dependencies:
+    decompress-response "^3.3.0"
+    once "^1.3.1"
+    simple-concat "^1.0.0"
+
+simple-swizzle@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
+  integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=
+  dependencies:
+    is-arrayish "^0.3.1"
+
+snapdragon-node@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+  integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+  dependencies:
+    define-property "^1.0.0"
+    isobject "^3.0.0"
+    snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+  integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+  dependencies:
+    kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+  integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+  dependencies:
+    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"
+
+source-map-resolve@^0.5.0:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
+  integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
+  dependencies:
+    atob "^2.1.1"
+    decode-uri-component "^0.2.0"
+    resolve-url "^0.2.1"
+    source-map-url "^0.4.0"
+    urix "^0.1.0"
+
+source-map-url@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+  integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@^0.5.6:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+  integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+split-string@^3.0.1, split-string@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+  integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+  dependencies:
+    extend-shallow "^3.0.0"
+
+static-extend@^0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+  integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+  dependencies:
+    define-property "^0.2.5"
+    object-copy "^0.1.0"
+
+"statuses@>= 1.4.0 < 2":
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+  integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+
+statuses@~1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
+  integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==
+
+string-width@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+  integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    strip-ansi "^3.0.0"
+
+"string-width@^1.0.2 || 2":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+  integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^4.0.0"
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+  integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+  integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+  dependencies:
+    ansi-regex "^3.0.0"
+
+strip-json-comments@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+  integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+
+tar-fs@^1.13.0:
+  version "1.16.3"
+  resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509"
+  integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==
+  dependencies:
+    chownr "^1.0.1"
+    mkdirp "^0.5.1"
+    pump "^1.0.0"
+    tar-stream "^1.1.2"
+
+tar-stream@^1.1.2:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
+  integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
+  dependencies:
+    bl "^1.0.0"
+    buffer-alloc "^1.2.0"
+    end-of-stream "^1.0.0"
+    fs-constants "^1.0.0"
+    readable-stream "^2.3.0"
+    to-buffer "^1.1.1"
+    xtend "^4.0.0"
+
+tar@^4.4.8:
+  version "4.4.8"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
+  integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
+  dependencies:
+    chownr "^1.1.1"
+    fs-minipass "^1.2.5"
+    minipass "^2.3.4"
+    minizlib "^1.1.1"
+    mkdirp "^0.5.0"
+    safe-buffer "^5.1.2"
+    yallist "^3.0.2"
+
+to-buffer@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
+  integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
+
+to-object-path@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+  integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+  dependencies:
+    kind-of "^3.0.2"
+
+to-regex-range@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+  integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+  dependencies:
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+  integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+  dependencies:
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    regex-not "^1.0.2"
+    safe-regex "^1.1.0"
+
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
+  dependencies:
+    safe-buffer "^5.0.1"
+
+type-is@~1.6.16:
+  version "1.6.16"
+  resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194"
+  integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==
+  dependencies:
+    media-typer "0.3.0"
+    mime-types "~2.1.18"
+
+typescript@^3.3.4000:
+  version "3.3.4000"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.4000.tgz#76b0f89cfdbf97827e1112d64f283f1151d6adf0"
+  integrity sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==
+
+union-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
+  integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=
+  dependencies:
+    arr-union "^3.1.0"
+    get-value "^2.0.6"
+    is-extendable "^0.1.1"
+    set-value "^0.4.3"
+
+universalify@^0.1.0:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+  integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+unpipe@1.0.0, unpipe@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+  integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
+
+unset-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+  integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+  dependencies:
+    has-value "^0.3.1"
+    isobject "^3.0.0"
+
+urix@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+  integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+use@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+  integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+utils-merge@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+  integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
+
+vary@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+  integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
+
+which-pm-runs@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
+  integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
+
+wide-align@^1.1.0:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
+  integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
+  dependencies:
+    string-width "^1.0.2 || 2"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+xtend@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
+  integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68=
+
+yallist@^3.0.0, yallist@^3.0.2:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
+  integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==

+ 1 - 0
packages/common/.gitignore

@@ -0,0 +1 @@
+lib

+ 0 - 0
packages/common/.npmignore


+ 3 - 0
packages/common/README.md

@@ -0,0 +1,3 @@
+# @vendure/common
+
+This package contains a set of common utility functions and TypeScript types used by multiple Vendure packages. It is not intended to be directly depended upon by an end-user project.

+ 13 - 0
packages/common/jest.config.js

@@ -0,0 +1,13 @@
+module.exports = {
+    coverageDirectory: "coverage",
+    moduleFileExtensions: [
+        "js",
+        "json",
+        "ts",
+    ],
+    preset: "ts-jest",
+    rootDir: __dirname,
+    transform: {
+        "^.+\\.(t|j)s$": "ts-jest",
+    },
+};

+ 17 - 0
packages/common/package.json

@@ -0,0 +1,17 @@
+{
+  "name": "@vendure/common",
+  "version": "0.1.0-alpha.1",
+  "main": "index.js",
+  "license": "MIT",
+  "scripts": {
+    "build": "rimraf dist && tsc -p ./tsconfig.build.json"
+  },
+  "files": ["lib/**/*"],
+  "dependencies": {
+    "@types/jest": "^24.0.11",
+    "typescript": "^3.3.4000"
+  },
+  "devDependencies": {
+    "rimraf": "^2.6.3"
+  }
+}

+ 0 - 0
shared/filter-async.spec.ts → packages/common/src/filter-async.spec.ts


+ 0 - 0
shared/filter-async.ts → packages/common/src/filter-async.ts


+ 3 - 2
shared/generated-shop-types.ts → packages/common/src/generated-shop-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
 // tslint:disable
-// Generated in 2019-03-25T13:48:49+01:00
+// Generated in 2019-04-01T11:03:11+02:00
 export type Maybe<T> = T | null;
 export type Maybe<T> = T | null;
 
 
 export interface OrderListOptions {
 export interface OrderListOptions {
@@ -1569,7 +1569,7 @@ export interface SearchResult {
     facetValueIds: string[];
     facetValueIds: string[];
     /** An array of ids of the Collections in which this result appears */
     /** An array of ids of the Collections in which this result appears */
     collectionIds: string[];
     collectionIds: string[];
-    /** A relevence score for the result. Differs between database implementations. */
+    /** A relevence score for the result. Differs between database implementations */
     score: number;
     score: number;
 }
 }
 
 
@@ -1899,4 +1899,5 @@ export interface ResetPasswordMutationArgs {
 // Unions
 // Unions
 // ====================================================
 // ====================================================
 
 
+/** The price of a search result product, either as a range or as a single price */
 export type SearchResultPrice = PriceRange | SinglePrice;
 export type SearchResultPrice = PriceRange | SinglePrice;

+ 3 - 3
shared/generated-types.ts → packages/common/src/generated-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
 // tslint:disable
-// Generated in 2019-03-25T13:48:50+01:00
+// Generated in 2019-04-01T11:03:13+02:00
 export type Maybe<T> = T | null;
 export type Maybe<T> = T | null;
 
 
 
 
@@ -5467,7 +5467,7 @@ export interface SearchResult {
   facetValueIds: string[];
   facetValueIds: string[];
   /** An array of ids of the Collections in which this result appears */
   /** An array of ids of the Collections in which this result appears */
   collectionIds: string[];
   collectionIds: string[];
-  /** A relevence score for the result. Differs between database implementations. */
+  /** A relevence score for the result. Differs between database implementations */
   score: number;
   score: number;
 }
 }
 
 
@@ -6248,7 +6248,7 @@ export interface SetUiLanguageMutationArgs {
 // ====================================================
 // ====================================================
 
 
 
 
-
+/** The price of a search result product, either as a range or as a single price */
 export type SearchResultPrice = PriceRange | SinglePrice;
 export type SearchResultPrice = PriceRange | SinglePrice;
 
 
 
 

+ 0 - 0
shared/normalize-string.spec.ts → packages/common/src/normalize-string.spec.ts


+ 0 - 0
shared/normalize-string.ts → packages/common/src/normalize-string.ts


+ 0 - 0
shared/omit.spec.ts → packages/common/src/omit.spec.ts


+ 1 - 1
shared/omit.ts → packages/common/src/omit.ts

@@ -45,7 +45,7 @@ function isObject(input: any): input is object {
 /**
 /**
  * When running in the Node environment, there is no native File object.
  * When running in the Node environment, there is no native File object.
  */
  */
-function isFileObject(input): boolean {
+function isFileObject(input: any): boolean {
     if (typeof File === 'undefined') {
     if (typeof File === 'undefined') {
         return false;
         return false;
     } else {
     } else {

+ 0 - 0
shared/pick.spec.ts → packages/common/src/pick.spec.ts


+ 0 - 0
shared/pick.ts → packages/common/src/pick.ts


+ 0 - 0
shared/shared-constants.ts → packages/common/src/shared-constants.ts


+ 18 - 1
shared/shared-types.ts → packages/common/src/shared-types.ts

@@ -59,9 +59,26 @@ export interface CustomFieldConfig {
 
 
 /**
 /**
  * @description
  * @description
- * The certain entities can have additional fields added to them by defining an array of {@link CustomFieldConfig}
+ * Most entities can have additional fields added to them by defining an array of {@link CustomFieldConfig}
  * objects on against the corresponding key.
  * objects on against the corresponding key.
  *
  *
+ * @example
+ * ```TypeScript
+ * bootstrap({
+ *     // ...
+ *     customFields: {
+ *         Product: [
+ *             { name: 'infoUrl', type: 'string' },
+ *             { name: 'downloadable', type: 'boolean' },
+ *             { name: 'shortName', type: 'localeString' },
+ *         ],
+ *         User: [
+ *             { name: 'socialLoginToken', type: 'string' },
+ *         ],
+ *     },
+ * })
+ * ```
+ *
  * @docsCategory custom-fields
  * @docsCategory custom-fields
  */
  */
 export interface CustomFields {
 export interface CustomFields {

+ 0 - 2
shared/shared-utils.spec.ts → packages/common/src/shared-utils.spec.ts

@@ -1,5 +1,3 @@
-/// <reference types="../server/node_modules/@types/jest" />
-
 import { generateAllCombinations } from './shared-utils';
 import { generateAllCombinations } from './shared-utils';
 
 
 describe('generateAllCombinations()', () => {
 describe('generateAllCombinations()', () => {

+ 0 - 0
shared/shared-utils.ts → packages/common/src/shared-utils.ts


+ 0 - 0
shared/simple-deep-clone.ts → packages/common/src/simple-deep-clone.ts


+ 0 - 0
shared/unique.spec.ts → packages/common/src/unique.spec.ts


+ 0 - 0
shared/unique.ts → packages/common/src/unique.ts


+ 12 - 0
packages/common/tsconfig.build.json

@@ -0,0 +1,12 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "./lib"
+  },
+  "include": [
+    "src/**/*.ts"
+  ],
+  "exclude": [
+    "**/*.spec.ts"
+  ]
+}

+ 11 - 0
packages/common/tsconfig.json

@@ -0,0 +1,11 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "declaration": true,
+    "removeComments": true,
+    "noLib": false,
+    "skipLibCheck": true,
+    "sourceMap": true,
+    "newLine": "LF"
+  }
+}

+ 5 - 0
packages/common/tslint.json

@@ -0,0 +1,5 @@
+{
+    "extends": [
+        "../../tslint.json"
+    ]
+}

+ 99 - 0
packages/common/yarn.lock

@@ -0,0 +1,99 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@types/jest-diff@*":
+  version "20.0.1"
+  resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89"
+  integrity sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==
+
+"@types/jest@^24.0.11":
+  version "24.0.11"
+  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.11.tgz#1f099bea332c228ea6505a88159bfa86a5858340"
+  integrity sha512-2kLuPC5FDnWIDvaJBzsGTBQaBbnDweznicvK7UGYzlIJP4RJR2a4A/ByLUXEyEgag6jz8eHdlWExGDtH3EYUXQ==
+  dependencies:
+    "@types/jest-diff" "*"
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+glob@^7.1.3:
+  version "7.1.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+  integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
+  dependencies:
+    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"
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+  integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
+minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+once@^1.3.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+rimraf@^2.6.3:
+  version "2.6.3"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+  integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+  dependencies:
+    glob "^7.1.3"
+
+typescript@^3.3.4000:
+  version "3.3.4000"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.4000.tgz#76b0f89cfdbf97827e1112d64f283f1151d6adf0"
+  integrity sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=

+ 5 - 0
packages/core/.gitignore

@@ -0,0 +1,5 @@
+assets
+dist
+e2e/__data__/*.sqlite
+!e2e/__data__/.gitkeep
+test-emails

+ 1 - 1
server/README.md → packages/core/README.md

@@ -3,7 +3,7 @@
 A headless [GraphQL](https://graphql.org/) ecommerce framework built on [Node.js](https://nodejs.org) with [Nest](https://nestjs.com/) with [TypeScript](http://www.typescriptlang.org/).
 A headless [GraphQL](https://graphql.org/) ecommerce framework built on [Node.js](https://nodejs.org) with [Nest](https://nestjs.com/) with [TypeScript](http://www.typescriptlang.org/).
 
 
 ```bash
 ```bash
-$ npm install @vendure/core@alpha
+$ npm install @vendure/core
 ```
 ```
 
 
 ### [www.vendure.io](https://www.vendure.io/)
 ### [www.vendure.io](https://www.vendure.io/)

+ 17 - 0
packages/core/build/gulpfile.ts

@@ -0,0 +1,17 @@
+import { exec } from 'child_process';
+import fs from 'fs-extra';
+import { dest, parallel, series, src } from 'gulp';
+import path from 'path';
+
+function copySchemas() {
+    return src(['../src/**/*.graphql']).pipe(dest('../dist'));
+}
+
+function copyI18nMessages() {
+    return src(['../src/i18n/messages/**/*']).pipe(dest('../dist/i18n/messages'));
+}
+
+export const build = parallel(
+    copySchemas,
+    copyI18nMessages,
+);

+ 3 - 2
server/build/tsconfig.build.json → packages/core/build/tsconfig.build.json

@@ -2,9 +2,10 @@
   "extends": "../tsconfig.json",
   "extends": "../tsconfig.json",
   "compilerOptions": {
   "compilerOptions": {
     "outDir": "../dist",
     "outDir": "../dist",
-    "rootDirs": ["../src", "../../../shared"]
+    "rootDirs": ["../src"]
   },
   },
   "files": [
   "files": [
-    "../src/index.ts"
+    "../src/index.ts",
+    "../typings.d.ts"
   ]
   ]
 }
 }

+ 3 - 3
server/build/tsconfig.cli.json → packages/core/build/tsconfig.cli.json

@@ -1,10 +1,10 @@
 {
 {
   "extends": "../tsconfig.json",
   "extends": "../tsconfig.json",
   "compilerOptions": {
   "compilerOptions": {
-    "outDir": "../dist/cli",
-    "declaration": false
+    "outDir": "../dist/cli"
   },
   },
   "files": [
   "files": [
-    "../cli/vendure-cli.ts"
+    "../cli/vendure-cli.ts",
+    "../typings.d.ts"
   ]
   ]
 }
 }

+ 0 - 0
server/cli/cli-utils.ts → packages/core/cli/cli-utils.ts


+ 38 - 25
server/cli/populate.ts → packages/core/cli/populate.ts

@@ -3,22 +3,46 @@ import fs from 'fs-extra';
 import path from 'path';
 import path from 'path';
 
 
 import { logColored } from './cli-utils';
 import { logColored } from './cli-utils';
-// tslint:disable-next-line:no-var-requires
-const { Populator, Importer } = require('@vendure/core');
+// tslint:disable:no-var-requires
+let Populator: any;
+let Importer: any;
+try {
+    Populator = require('@vendure/core').Populator;
+    Importer = require('@vendure/core').Importer;
+} catch (e) {
+    Populator = require('../src/data-import/providers/populator/populator').Populator;
+    Importer = require('../src/data-import/providers/importer/importer').Importer;
+}
 
 
 // tslint:disable:no-console
 // tslint:disable:no-console
-export async function populate() {
-    logColored('\nPopulating... (this may take a minute or two)\n');
-    const app = await getApplicationRef();
-    if (app) {
-        const initialData = require('./assets/initial-data.json');
-        await populateInitialData(app, initialData);
-        await populateProducts(app, initialData);
+export async function populate(
+    bootstrapFn: () => Promise<INestApplication | undefined>,
+    initialDataPath: string,
+): Promise<INestApplication>;
+export async function populate(
+    bootstrapFn: () => Promise<INestApplication | undefined>,
+    initialDataPath: string,
+    productsCsvPath: string,
+    imageSourcePath: string,
+): Promise<INestApplication>;
+export async function populate(
+    bootstrapFn: () => Promise<INestApplication | undefined>,
+    initialDataPath: string,
+    productsCsvPath?: string,
+    imageSourcePath?: string,
+): Promise<INestApplication> {
+    const app = await bootstrapFn();
+    if (!app) {
+        throw new Error('Could not bootstrap the Vendure app');
+    }
+    const initialData = require(initialDataPath);
+    await populateInitialData(app, initialData);
+    if (productsCsvPath && imageSourcePath) {
+        await importProductsFromFile(app, productsCsvPath, initialData.defaultLanguage);
         await populateCollections(app, initialData);
         await populateCollections(app, initialData);
-        logColored('\nDone!');
-        await app.close();
-        process.exit(0);
     }
     }
+    logColored('\nDone!');
+    return app;
 }
 }
 
 
 export async function importProducts(csvPath: string, languageCode: string) {
 export async function importProducts(csvPath: string, languageCode: string) {
@@ -88,7 +112,7 @@ async function getApplicationRef(): Promise<INestApplication | undefined> {
     return app;
     return app;
 }
 }
 
 
-async function populateInitialData(app: INestApplication, initialData: any) {
+async function populateInitialData(app: INestApplication, initialData: object) {
     const populator = app.get(Populator);
     const populator = app.get(Populator);
     try {
     try {
         await populator.populateInitialData(initialData);
         await populator.populateInitialData(initialData);
@@ -97,7 +121,7 @@ async function populateInitialData(app: INestApplication, initialData: any) {
     }
     }
 }
 }
 
 
-async function populateCollections(app: INestApplication, initialData: any) {
+async function populateCollections(app: INestApplication, initialData: object) {
     const populator = app.get(Populator);
     const populator = app.get(Populator);
     try {
     try {
         await populator.populateCollections(initialData);
         await populator.populateCollections(initialData);
@@ -106,17 +130,6 @@ async function populateCollections(app: INestApplication, initialData: any) {
     }
     }
 }
 }
 
 
-async function populateProducts(app: INestApplication, initialData: any) {
-    // copy the images to the import folder
-    const images = path.join(__dirname, 'assets', 'images');
-    const destination = path.join(process.cwd(), 'vendure', 'import-assets');
-    await fs.copy(images, destination);
-
-    // import the csv of same product data
-    const sampleProductsFile = path.join(__dirname, 'assets', 'products.csv');
-    await importProductsFromFile(app, sampleProductsFile, initialData.defaultLanguage);
-}
-
 async function importProductsFromFile(app: INestApplication, csvPath: string, languageCode: string) {
 async function importProductsFromFile(app: INestApplication, csvPath: string, languageCode: string) {
     // import the csv of same product data
     // import the csv of same product data
     const importer = app.get(Importer);
     const importer = app.get(Importer);

+ 1 - 27
server/cli/vendure-cli.ts → packages/core/cli/vendure-cli.ts

@@ -1,11 +1,9 @@
 #!/usr/bin/env node
 #!/usr/bin/env node
 import program from 'commander';
 import program from 'commander';
 import path from 'path';
 import path from 'path';
-import prompts from 'prompts';
 
 
 import { logColored } from './cli-utils';
 import { logColored } from './cli-utils';
-import { init } from './init';
-import { importProducts, populate } from './populate';
+import { importProducts } from './populate';
 // tslint:disable-next-line:no-var-requires
 // tslint:disable-next-line:no-var-requires
 const version = require('../../package.json').version;
 const version = require('../../package.json').version;
 
 
@@ -20,30 +18,6 @@ logColored(`
                                        `);
                                        `);
 
 
 program.version(`Vendure CLI v${version}`, '-v --version').name('vendure');
 program.version(`Vendure CLI v${version}`, '-v --version').name('vendure');
-program
-    .command('init')
-    .description('Initialize a new Vendure server application')
-    .action(async (command: any) => {
-        const indexFile = await init();
-        const answer = await prompts({
-            type: 'toggle',
-            name: 'populate',
-            message: 'Populate the database with some data to get you started (recommended)?',
-            active: 'yes',
-            inactive: 'no',
-            initial: true as any,
-        });
-        if (answer.populate) {
-            await populate();
-        }
-        logColored(`\nAll done! Run "${indexFile}" to start the server.`);
-    });
-program
-    .command('populate')
-    .description('Populate a new Vendure server instance with some initial data')
-    .action(async () => {
-        await populate();
-    });
 program
 program
     .command('import-products <csvFile>')
     .command('import-products <csvFile>')
     .option('-l, --language', 'Specify ISO 639-1 language code, e.g. "de", "es". Defaults to "en"')
     .option('-l, --language', 'Specify ISO 639-1 language code, e.g. "de", "es". Defaults to "en"')

+ 0 - 0
server/e2e/__data__/.gitkeep → packages/core/e2e/__data__/.gitkeep


+ 0 - 0
server/e2e/__snapshots__/administrator.e2e-spec.ts.snap → packages/core/e2e/__snapshots__/administrator.e2e-spec.ts.snap


+ 0 - 0
server/e2e/__snapshots__/collection.e2e-spec.ts.snap → packages/core/e2e/__snapshots__/collection.e2e-spec.ts.snap


+ 0 - 0
server/e2e/__snapshots__/facet.e2e-spec.ts.snap → packages/core/e2e/__snapshots__/facet.e2e-spec.ts.snap


+ 0 - 0
server/e2e/__snapshots__/import.e2e-spec.ts.snap → packages/core/e2e/__snapshots__/import.e2e-spec.ts.snap


+ 0 - 0
server/e2e/__snapshots__/product.e2e-spec.ts.snap → packages/core/e2e/__snapshots__/product.e2e-spec.ts.snap


+ 0 - 0
server/e2e/__snapshots__/promotion.e2e-spec.ts.snap → packages/core/e2e/__snapshots__/promotion.e2e-spec.ts.snap


+ 0 - 0
server/e2e/__snapshots__/role.e2e-spec.ts.snap → packages/core/e2e/__snapshots__/role.e2e-spec.ts.snap


+ 9 - 9
server/e2e/administrator.e2e-spec.ts → packages/core/e2e/administrator.e2e-spec.ts

@@ -1,3 +1,10 @@
+import {
+    Administrator,
+    CreateAdministrator,
+    GetAdministrator,
+    GetAdministrators,
+    UpdateAdministrator,
+} from '@vendure/common/lib/generated-types';
 import path from 'path';
 import path from 'path';
 
 
 import {
 import {
@@ -5,19 +12,12 @@ import {
     GET_ADMINISTRATOR,
     GET_ADMINISTRATOR,
     GET_ADMINISTRATORS,
     GET_ADMINISTRATORS,
     UPDATE_ADMINISTRATOR,
     UPDATE_ADMINISTRATOR,
-} from '../../admin-ui/src/app/data/definitions/administrator-definitions';
-import {
-    Administrator,
-    CreateAdministrator,
-    GetAdministrator,
-    GetAdministrators,
-    UpdateAdministrator,
-} from '../../shared/generated-types';
+} from '../../../admin-ui/src/app/data/definitions/administrator-definitions';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestAdminClient } from './test-client';
 import { TestAdminClient } from './test-client';
 import { TestServer } from './test-server';
 import { TestServer } from './test-server';
-import { assertThrowsWithMessage } from './test-utils';
+import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 
 
 describe('Administrator resolver', () => {
 describe('Administrator resolver', () => {
     const client = new TestAdminClient();
     const client = new TestAdminClient();

+ 12 - 12
server/e2e/auth.e2e-spec.ts → packages/core/e2e/auth.e2e-spec.ts

@@ -1,3 +1,12 @@
+import {
+    CreateAdministrator,
+    CreateProductMutationArgs,
+    CreateRole,
+    LoginMutationArgs,
+    Permission,
+    UpdateProductMutationArgs,
+} from '@vendure/common/lib/generated-types';
+import { SUPER_ADMIN_USER_IDENTIFIER, SUPER_ADMIN_USER_PASSWORD } from '@vendure/common/lib/shared-constants';
 import { DocumentNode } from 'graphql';
 import { DocumentNode } from 'graphql';
 import gql from 'graphql-tag';
 import gql from 'graphql-tag';
 import path from 'path';
 import path from 'path';
@@ -5,22 +14,13 @@ import path from 'path';
 import {
 import {
     CREATE_ADMINISTRATOR,
     CREATE_ADMINISTRATOR,
     CREATE_ROLE,
     CREATE_ROLE,
-} from '../../admin-ui/src/app/data/definitions/administrator-definitions';
-import { ATTEMPT_LOGIN } from '../../admin-ui/src/app/data/definitions/auth-definitions';
+} from '../../../admin-ui/src/app/data/definitions/administrator-definitions';
+import { ATTEMPT_LOGIN } from '../../../admin-ui/src/app/data/definitions/auth-definitions';
 import {
 import {
     CREATE_PRODUCT,
     CREATE_PRODUCT,
     GET_PRODUCT_LIST,
     GET_PRODUCT_LIST,
     UPDATE_PRODUCT,
     UPDATE_PRODUCT,
-} from '../../admin-ui/src/app/data/definitions/product-definitions';
-import {
-    CreateAdministrator,
-    CreateProductMutationArgs,
-    CreateRole,
-    LoginMutationArgs,
-    Permission,
-    UpdateProductMutationArgs,
-} from '../../shared/generated-types';
-import { SUPER_ADMIN_USER_IDENTIFIER, SUPER_ADMIN_USER_PASSWORD } from '../../shared/shared-constants';
+} from '../../../admin-ui/src/app/data/definitions/product-definitions';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestAdminClient } from './test-client';
 import { TestAdminClient } from './test-client';

+ 21 - 25
server/e2e/collection.e2e-spec.ts → packages/core/e2e/collection.e2e-spec.ts

@@ -1,19 +1,4 @@
 /* tslint:disable:no-non-null-assertion */
 /* tslint:disable:no-non-null-assertion */
-import gql from 'graphql-tag';
-import path from 'path';
-
-import {
-    CREATE_COLLECTION,
-    GET_COLLECTION,
-    MOVE_COLLECTION,
-    UPDATE_COLLECTION,
-} from '../../admin-ui/src/app/data/definitions/collection-definitions';
-import { FACET_VALUE_FRAGMENT } from '../../admin-ui/src/app/data/definitions/facet-definitions';
-import {
-    GET_ASSET_LIST,
-    UPDATE_PRODUCT,
-    UPDATE_PRODUCT_VARIANTS,
-} from '../../admin-ui/src/app/data/definitions/product-definitions';
 import {
 import {
     Collection,
     Collection,
     ConfigArgType,
     ConfigArgType,
@@ -29,14 +14,25 @@ import {
     UpdateCollection,
     UpdateCollection,
     UpdateProduct,
     UpdateProduct,
     UpdateProductVariants,
     UpdateProductVariants,
-} from '../../shared/generated-types';
-import { ROOT_COLLECTION_NAME } from '../../shared/shared-constants';
+} from '@vendure/common/lib/generated-types';
+import { ROOT_COLLECTION_NAME } from '@vendure/common/lib/shared-constants';
+import gql from 'graphql-tag';
+import path from 'path';
+
+import {
+    CREATE_COLLECTION,
+    GET_COLLECTION,
+    MOVE_COLLECTION,
+    UPDATE_COLLECTION,
+} from '../../../admin-ui/src/app/data/definitions/collection-definitions';
+import { FACET_VALUE_FRAGMENT } from '../../../admin-ui/src/app/data/definitions/facet-definitions';
+import { GET_ASSET_LIST, UPDATE_PRODUCT, UPDATE_PRODUCT_VARIANTS } from '../../../admin-ui/src/app/data/definitions/product-definitions';
 import { facetValueCollectionFilter } from '../src/config/collection/default-collection-filters';
 import { facetValueCollectionFilter } from '../src/config/collection/default-collection-filters';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestAdminClient } from './test-client';
 import { TestAdminClient } from './test-client';
 import { TestServer } from './test-server';
 import { TestServer } from './test-server';
-import { assertThrowsWithMessage } from './test-utils';
+import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 
 
 describe('Collection resolver', () => {
 describe('Collection resolver', () => {
     const client = new TestAdminClient();
     const client = new TestAdminClient();
@@ -227,7 +223,7 @@ describe('Collection resolver', () => {
             expect(result.moveCollection.parent!.id).toBe(electronicsCollection.id);
             expect(result.moveCollection.parent!.id).toBe(electronicsCollection.id);
 
 
             const positions = await getChildrenOf(electronicsCollection.id);
             const positions = await getChildrenOf(electronicsCollection.id);
-            expect(positions.map(i => i.id)).toEqual([pearCollection.id, computersCollection.id]);
+            expect(positions.map((i: any) => i.id)).toEqual([pearCollection.id, computersCollection.id]);
         });
         });
 
 
         it('re-evaluates Collection contents on move', async () => {
         it('re-evaluates Collection contents on move', async () => {
@@ -251,7 +247,7 @@ describe('Collection resolver', () => {
             });
             });
 
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
             const afterResult = await getChildrenOf(electronicsCollection.id);
-            expect(afterResult.map(i => i.id)).toEqual([computersCollection.id, pearCollection.id]);
+            expect(afterResult.map((i: any) => i.id)).toEqual([computersCollection.id, pearCollection.id]);
         });
         });
 
 
         it('corrects an out-of-bounds negative index value', async () => {
         it('corrects an out-of-bounds negative index value', async () => {
@@ -264,7 +260,7 @@ describe('Collection resolver', () => {
             });
             });
 
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
             const afterResult = await getChildrenOf(electronicsCollection.id);
-            expect(afterResult.map(i => i.id)).toEqual([pearCollection.id, computersCollection.id]);
+            expect(afterResult.map((i: any) => i.id)).toEqual([pearCollection.id, computersCollection.id]);
         });
         });
 
 
         it('corrects an out-of-bounds positive index value', async () => {
         it('corrects an out-of-bounds positive index value', async () => {
@@ -277,7 +273,7 @@ describe('Collection resolver', () => {
             });
             });
 
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
             const afterResult = await getChildrenOf(electronicsCollection.id);
-            expect(afterResult.map(i => i.id)).toEqual([computersCollection.id, pearCollection.id]);
+            expect(afterResult.map((i: any) => i.id)).toEqual([computersCollection.id, pearCollection.id]);
         });
         });
 
 
         it(
         it(
@@ -332,7 +328,7 @@ describe('Collection resolver', () => {
                 const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
                 const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: electronicsCollection.id,
                     id: electronicsCollection.id,
                 });
                 });
-                expect(result.collection.productVariants.items.map(i => i.name)).toEqual([
+                expect(result.collection.productVariants.items.map((i: any) => i.name)).toEqual([
                     'Laptop 13 inch 8GB',
                     'Laptop 13 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 13 inch 16GB',
                     'Laptop 13 inch 16GB',
@@ -361,7 +357,7 @@ describe('Collection resolver', () => {
                 const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
                 const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: computersCollection.id,
                     id: computersCollection.id,
                 });
                 });
-                expect(result.collection.productVariants.items.map(i => i.name)).toEqual([
+                expect(result.collection.productVariants.items.map((i: any) => i.name)).toEqual([
                     'Laptop 13 inch 8GB',
                     'Laptop 13 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 13 inch 16GB',
                     'Laptop 13 inch 16GB',
@@ -404,7 +400,7 @@ describe('Collection resolver', () => {
                         ],
                         ],
                     } as CreateCollectionInput,
                     } as CreateCollectionInput,
                 });
                 });
-                expect(result.createCollection.productVariants.items.map(i => i.name)).toEqual([
+                expect(result.createCollection.productVariants.items.map((i: any) => i.name)).toEqual([
                     'Instant Camera',
                     'Instant Camera',
                 ]);
                 ]);
             });
             });

+ 0 - 3
server/e2e/config/jest-e2e.json → packages/core/e2e/config/jest-e2e.json

@@ -1,8 +1,5 @@
 {
 {
   "moduleFileExtensions": ["js", "json", "ts"],
   "moduleFileExtensions": ["js", "json", "ts"],
-  "moduleNameMapper": {
-    "shared/(.*)": "<rootDir>/../../shared/$1.ts"
-  },
   "rootDir": "../",
   "rootDir": "../",
   "testRegex": ".e2e-spec.ts$",
   "testRegex": ".e2e-spec.ts$",
   "transform": {
   "transform": {

+ 1 - 7
server/e2e/config/test-config.ts → packages/core/e2e/config/test-config.ts

@@ -1,6 +1,6 @@
+import { ADMIN_API_PATH, SHOP_API_PATH } from '@vendure/common/lib/shared-constants';
 import path from 'path';
 import path from 'path';
 
 
-import { ADMIN_API_PATH, SHOP_API_PATH } from '../../../shared/shared-constants';
 import { DefaultAssetNamingStrategy } from '../../src/config/asset-naming-strategy/default-asset-naming-strategy';
 import { DefaultAssetNamingStrategy } from '../../src/config/asset-naming-strategy/default-asset-naming-strategy';
 import { VendureConfig } from '../../src/config/vendure-config';
 import { VendureConfig } from '../../src/config/vendure-config';
 
 
@@ -42,12 +42,6 @@ export const testConfig: VendureConfig = {
     paymentOptions: {
     paymentOptions: {
         paymentMethodHandlers: [],
         paymentMethodHandlers: [],
     },
     },
-    emailOptions: {
-        emailTemplatePath: __dirname,
-        transport: {
-            type: 'none',
-        },
-    },
     importExportOptions: {
     importExportOptions: {
         importAssetsDir: path.join(__dirname, '..', 'fixtures/assets'),
         importAssetsDir: path.join(__dirname, '..', 'fixtures/assets'),
     },
     },

+ 0 - 0
server/e2e/config/testing-asset-preview-strategy.ts → packages/core/e2e/config/testing-asset-preview-strategy.ts


+ 0 - 0
server/e2e/config/testing-asset-storage-strategy.ts → packages/core/e2e/config/testing-asset-storage-strategy.ts


+ 0 - 0
server/e2e/config/testing-entity-id-strategy.ts → packages/core/e2e/config/testing-entity-id-strategy.ts


+ 2 - 1
server/e2e/config/tsconfig.e2e.json → packages/core/e2e/config/tsconfig.e2e.json

@@ -4,7 +4,8 @@
     "types": ["jest", "node"],
     "types": ["jest", "node"],
     "lib": ["es2015"],
     "lib": ["es2015"],
     "skipLibCheck": false,
     "skipLibCheck": false,
-    "inlineSourceMap": true
+    "inlineSourceMap": true,
+    "allowJs": true
   },
   },
   "include": ["../**/*.e2e-spec.ts", "../**/*.d.ts"]
   "include": ["../**/*.e2e-spec.ts", "../**/*.d.ts"]
 }
 }

+ 9 - 9
server/e2e/country.e2e-spec.ts → packages/core/e2e/country.e2e-spec.ts

@@ -1,3 +1,11 @@
+import {
+    CreateCountry,
+    DeletionResult,
+    GetCountry,
+    GetCountryList,
+    LanguageCode,
+    UpdateCountry,
+} from '@vendure/common/lib/generated-types';
 import gql from 'graphql-tag';
 import gql from 'graphql-tag';
 import path from 'path';
 import path from 'path';
 
 
@@ -6,15 +14,7 @@ import {
     GET_COUNTRY,
     GET_COUNTRY,
     GET_COUNTRY_LIST,
     GET_COUNTRY_LIST,
     UPDATE_COUNTRY,
     UPDATE_COUNTRY,
-} from '../../admin-ui/src/app/data/definitions/settings-definitions';
-import {
-    CreateCountry,
-    DeletionResult,
-    GetCountry,
-    GetCountryList,
-    LanguageCode,
-    UpdateCountry,
-} from '../../shared/generated-types';
+} from '../../../admin-ui/src/app/data/definitions/settings-definitions';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestAdminClient } from './test-client';
 import { TestAdminClient } from './test-client';

+ 4 - 12
server/e2e/customer.e2e-spec.ts → packages/core/e2e/customer.e2e-spec.ts

@@ -1,3 +1,5 @@
+import { CreateCustomerAddress, DeletionResult, GetCustomer, GetCustomerList, UpdateCustomer } from '@vendure/common/lib/generated-types';
+import { omit } from '@vendure/common/lib/omit';
 import gql from 'graphql-tag';
 import gql from 'graphql-tag';
 import path from 'path';
 import path from 'path';
 
 
@@ -6,22 +8,12 @@ import {
     GET_CUSTOMER,
     GET_CUSTOMER,
     GET_CUSTOMER_LIST,
     GET_CUSTOMER_LIST,
     UPDATE_CUSTOMER,
     UPDATE_CUSTOMER,
-    UPDATE_CUSTOMER_ADDRESS,
-} from '../../admin-ui/src/app/data/definitions/customer-definitions';
-import {
-    CreateCustomerAddress,
-    DeletionResult,
-    GetCustomer,
-    GetCustomerList,
-    UpdateCustomer,
-    UpdateCustomerAddress,
-} from '../../shared/generated-types';
-import { omit } from '../../shared/omit';
+} from '../../../admin-ui/src/app/data/definitions/customer-definitions';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestAdminClient, TestShopClient } from './test-client';
 import { TestAdminClient, TestShopClient } from './test-client';
 import { TestServer } from './test-server';
 import { TestServer } from './test-server';
-import { assertThrowsWithMessage } from './test-utils';
+import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 
 
 // tslint:disable:no-non-null-assertion
 // tslint:disable:no-non-null-assertion
 
 

+ 10 - 10
server/e2e/default-search-plugin.e2e-spec.ts → packages/core/e2e/default-search-plugin.e2e-spec.ts

@@ -1,12 +1,3 @@
-import gql from 'graphql-tag';
-import path from 'path';
-
-import {
-    CREATE_COLLECTION,
-    UPDATE_COLLECTION,
-} from '../../admin-ui/src/app/data/definitions/collection-definitions';
-import { SEARCH_PRODUCTS, UPDATE_PRODUCT } from '../../admin-ui/src/app/data/definitions/product-definitions';
-import { UPDATE_TAX_RATE } from '../../admin-ui/src/app/data/definitions/settings-definitions';
 import {
 import {
     ConfigArgType,
     ConfigArgType,
     CreateCollection,
     CreateCollection,
@@ -16,7 +7,16 @@ import {
     UpdateCollection,
     UpdateCollection,
     UpdateProduct,
     UpdateProduct,
     UpdateTaxRate,
     UpdateTaxRate,
-} from '../../shared/generated-types';
+} from '@vendure/common/lib/generated-types';
+import gql from 'graphql-tag';
+import path from 'path';
+
+import {
+    CREATE_COLLECTION,
+    UPDATE_COLLECTION,
+} from '../../../admin-ui/src/app/data/definitions/collection-definitions';
+import { SEARCH_PRODUCTS, UPDATE_PRODUCT } from '../../../admin-ui/src/app/data/definitions/product-definitions';
+import { UPDATE_TAX_RATE } from '../../../admin-ui/src/app/data/definitions/settings-definitions';
 import { SimpleGraphQLClient } from '../mock-data/simple-graphql-client';
 import { SimpleGraphQLClient } from '../mock-data/simple-graphql-client';
 import { facetValueCollectionFilter } from '../src/config/collection/default-collection-filters';
 import { facetValueCollectionFilter } from '../src/config/collection/default-collection-filters';
 import { DefaultSearchPlugin } from '../src/plugin/default-search-plugin/default-search-plugin';
 import { DefaultSearchPlugin } from '../src/plugin/default-search-plugin/default-search-plugin';

+ 2 - 2
server/e2e/entity-id-strategy.e2e-spec.ts → packages/core/e2e/entity-id-strategy.e2e-spec.ts

@@ -1,8 +1,8 @@
+import { CreateFacet, LanguageCode } from '@vendure/common/lib/generated-types';
 import gql from 'graphql-tag';
 import gql from 'graphql-tag';
 import path from 'path';
 import path from 'path';
 
 
-import { CREATE_FACET } from '../../admin-ui/src/app/data/definitions/facet-definitions';
-import { CreateFacet, LanguageCode } from '../../shared/generated-types';
+import { CREATE_FACET } from '../../../admin-ui/src/app/data/definitions/facet-definitions';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestShopClient } from './test-client';
 import { TestShopClient } from './test-client';

+ 17 - 17
server/e2e/facet.e2e-spec.ts → packages/core/e2e/facet.e2e-spec.ts

@@ -1,3 +1,18 @@
+import {
+    CreateFacet,
+    CreateFacetValues,
+    DeletionResult,
+    FacetWithValues,
+    GetFacetList,
+    GetFacetWithValues,
+    GetProductList,
+    GetProductWithVariants,
+    LanguageCode,
+    UpdateFacet,
+    UpdateFacetValues,
+    UpdateProduct,
+    UpdateProductVariants,
+} from '@vendure/common/lib/generated-types';
 import gql from 'graphql-tag';
 import gql from 'graphql-tag';
 import path from 'path';
 import path from 'path';
 
 
@@ -8,28 +23,13 @@ import {
     GET_FACET_WITH_VALUES,
     GET_FACET_WITH_VALUES,
     UPDATE_FACET,
     UPDATE_FACET,
     UPDATE_FACET_VALUES,
     UPDATE_FACET_VALUES,
-} from '../../admin-ui/src/app/data/definitions/facet-definitions';
+} from '../../../admin-ui/src/app/data/definitions/facet-definitions';
 import {
 import {
     GET_PRODUCT_LIST,
     GET_PRODUCT_LIST,
     GET_PRODUCT_WITH_VARIANTS,
     GET_PRODUCT_WITH_VARIANTS,
     UPDATE_PRODUCT,
     UPDATE_PRODUCT,
     UPDATE_PRODUCT_VARIANTS,
     UPDATE_PRODUCT_VARIANTS,
-} from '../../admin-ui/src/app/data/definitions/product-definitions';
-import {
-    CreateFacet,
-    CreateFacetValues,
-    DeletionResult,
-    FacetWithValues,
-    GetFacetList,
-    GetFacetWithValues,
-    GetProductList,
-    GetProductWithVariants,
-    LanguageCode,
-    UpdateFacet,
-    UpdateFacetValues,
-    UpdateProduct,
-    UpdateProductVariants,
-} from '../../shared/generated-types';
+} from '../../../admin-ui/src/app/data/definitions/product-definitions';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestAdminClient } from './test-client';
 import { TestAdminClient } from './test-client';

Some files were not shown because too many files changed in this diff