diff --git a/.circleci/config.yml b/.circleci/config.yml index b92d5645532..ac168051274 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,7 +36,7 @@ common_settings: full_build: &full_build name: Full Build - command: 'cp packages/venia-concept/.env.dist packages/venia-concept/.env && npm run build && npm run clean:dist' + command: 'cp packages/venia-concept/example.env packages/venia-concept/.env && npm run build && npm run clean:dist' test_result_path: &test_result_path path: "test-results" diff --git a/.editorconfig b/.editorconfig index 3460da026f4..78026e36e90 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,9 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[{package.json,*.yml}] +[{package.json,*.yml,*.md}] indent_size = 2 indent_style = space + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore index acfc92aca54..fadcbee55e6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ __fixtures__ dist pwa-devdocs -packages/venia-concept/web diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..fd5994a6297 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +package-lock.json -diff diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 24ffc33cc8a..b054d07117c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -7,6 +7,8 @@ [ ] `peregrine` [ ] `pwa-module` [ ] `pwa-devdocs` +[ ] `upward-js` +[ ] `upward-spec` ## This issue is a: diff --git a/.gitignore b/.gitignore index 20a1bb52e07..af00b74a593 100644 --- a/.gitignore +++ b/.gitignore @@ -5,11 +5,9 @@ npm-debug.log coverage test-results dist -packages/venia-concept/web storybook-dist .idea test-report.xml test-results.json lerna-debug.log .env -packages/venia-concept/web diff --git a/.prettierignore b/.prettierignore index 9d57a7f4b6f..a63528f94fa 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,4 @@ coverage package-lock.json dist -web/js pwa-devdocs -packages/venia-concept/web diff --git a/README.md b/README.md index bb2d30b4bf0..5e7b0a780fe 100644 --- a/README.md +++ b/README.md @@ -44,39 +44,70 @@ All packages are versioned in a single repo, but released to `npm` as independen This repository includes the following packages: -* [venia-concept](packages/venia-concept) - Reference/Concept Theme +* [venia-concept](packages/venia-concept) - Reference/Concept Storefront * [pwa-buildpack](packages/pwa-buildpack/README.md) - Build tooling * [peregrine](packages/peregrine/README.md) - eCommerce Component Library * [pwa-module](packages/pwa-module) * [pwa-devdocs](packages/pwa-devdocs) - Project source for the [documentation site] +* [upward-js](packages/upward-js) - Reference implementation of the UPWARD specification +* [upward-spec](packages/upward-spec) - UPWARD specification and test suite -## Install project dependencies +## Quick Setup + +PWA Studio 2.0 requires much less setup than PWA Studio 1.0. The UPWARD architecture means that the Magento instance does not need to be configured to use your project as a theme. Instead, you connect to your Magento instance by simply specifying its URL in your environment. + +### Obtain Magento 2.3 + +1. Make sure the Magento instance you're using is set to development mode, and has the latest 2.3. + + * You need development mode for GraphQL introspection queries to work. + * The latest codebase will have the most up-to-date GraphQL schema. + +2. Ensure that the Venia sample data is installed on the Magento instance. (**TODO: painless instructions for the Composer commands to do that**) + +### Install Dependencies _**Note**: You must have a version of `node.js` >= `8.0.0`, and a version of `npm` >= `5.0.0`. The latest LTS versions of both are recommended._ Follow these steps to install the dependencies for all the packages in the project: -1. Clone the repository -2. Navigate to the root of the repository from the command line -3. Run `npm install` -4. Watch the bootstrapping take place. -5. To run the Venia theme development experience, run `npm run watch:venia` from package root. -6. To run the full PWA Studio deeloper experience, with Venia hot-reloading and concurrent Buildpack/Peregrine rebuilds, run `npm run watch:all` from package root. +1. Clone the repository. +2. Navigate to the root of the repository from the command line +3. Run `npm install` +4. Watch the bootstrapping take place. +5. Create a `packages/venia-concept/.env` file (or set environment variables manually) +6. Set the environment variable `MAGENTO_BACKEND_DOMAIN` to the URL of the backing Magento instance you are using +7. On your first install, run `npm run build` from package root. +8. To run the Venia storefront development experience, run `npm run watch:venia` from package root. +9. To run the full PWA Studio developer experience, with Venia hot-reloading and concurrent Buildpack/Peregrine rebuilds, run `npm run watch:all` from package root. +10. To run the staging environment, which uses more compressed assets and more closely reflects production, run `npm run stage:venia` from package root. (This requires that you first run `npm run build` to generate the artifacts being served.) -## Things not to do +## Troubleshooting -When using a monorepo and lerna, it's important that you break some common habits that are common when developing front-end packages. +### When I run the developer mode, I get validation errors + +Make sure you have created a `.env` file in `packages/venia-concept` which specifies variables for your local development environment. You can copy from the template `packages/venia-concept/example.env`. + +### Venia queries to GraphQL produce validation errors + +Venia and its GraphQL queries may be out of sync with the schema of your connected Magento instance. Make sure the Magento instance is up to date with the 2.3 development branch, and your copy of this repository (or your dependency on it) is up to date. + +**To test whether your queries are up to date, run `npm run validate:venia:gql` at project root.** + +## Things not to do -* Do _not_ run `npm install` to get `node_modules` up to date within any folder under `packages/`. - Instead, run `npm install` in the root of the repo, which will ensure all package's dependencies are up-to-date. -* When adding a new entry to `devDependencies` in a package's `package.json`, ask yourself whether that dependency will be used across multiple packages. - If the answer is "yes," the dependency should instead be installed in the root `package.json`. This will speed up runs of `lerna bootstrap`. +* Our monorepo is set up so that `npm install` can cross-link dependencies (such as Venia's dependency on Buildpack) without any extra tools. Do not run `lerna bootstrap`. +* All devDependencies are installed in the repository root. This means that **all scripts must be run from repository root**; otherwise, the locally installed CLI commands they use will not be available. +* Production dependencies are sometimes installed in child packages, but for some projects, such as Venia, it makes no sense to have production dependencies, because of bundling. +* Don't check in a big change to `package-lock.json`, and don't check in any `package-lock.json` files but the root one. +* Make sure to run `npm run prettier` and `npm run lint` before any commit you intend to push. You may want to set up a [Git hook] for this. [documentation site]: https://magento-research.github.io/pwa-studio/ [CircleCI]: https://circleci.com/gh/magento-research/pwa-studio.svg?style=svg [Coverage Status]: https://coveralls.io/repos/github/magento-research/pwa-studio/badge.svg?branch=master [Greenkeeper badge]: https://badges.greenkeeper.io/magento-research/pwa-studio.svg [Contribution guide]: .github/CONTRIBUTING.md +[Git hook]: [mage2pratik]: https://github.com/mage2pratik [mage2pratik-image]: https://avatars0.githubusercontent.com/u/33807558?s=120&v=4 diff --git a/greenkeeper.json b/greenkeeper.json deleted file mode 100644 index a85effe1549..00000000000 --- a/greenkeeper.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "groups": { - "default": { - "packages": [ - "package.json", - "packages/peregrine/package.json", - "packages/pwa-buildpack/package.json", - "packages/pwa-devdocs/package.json", - "packages/pwa-module/package.json", - "packages/venia-concept/package.json" - ] - } - } -} diff --git a/jest.config.js b/jest.config.js index 18b7540e6e8..73b22d0824c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,6 +4,7 @@ module.exports = { projects: [ 'packages/peregrine', 'packages/pwa-buildpack', + 'packages/upward-js', 'packages/venia-concept', 'scripts' ], diff --git a/lerna.json b/lerna.json index 69d022657de..6ae05056548 100644 --- a/lerna.json +++ b/lerna.json @@ -1,9 +1,11 @@ { - "version": "2.0.0-rc.1", + "version": "2.0.0-rc2.0.5", "packages": [ "packages/peregrine", "packages/pwa-buildpack", "packages/pwa-devdocs", + "packages/upward-js", + "packages/upward-spec", "packages/venia-concept" ], "npmClient": "npm", @@ -11,7 +13,7 @@ "publish": { "ignoreChanges": [ "__tests__/**", - "pwa-module" + "pwa-devdocs" ] } } diff --git a/package-lock.json b/package-lock.json index 177fc35db08..c7095e2493b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,21 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@apollographql/apollo-upload-server": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@apollographql/apollo-upload-server/-/apollo-upload-server-5.0.3.tgz", + "integrity": "sha512-tGAp3ULNyoA8b5o9LsU2Lq6SwgVPUOKAqKywu2liEtTvrFSGPrObwanhYwArq3GPeOqp2bi+JknSJCIU3oQN1Q==", + "requires": { + "@babel/runtime-corejs2": "^7.0.0-rc.1", + "busboy": "^0.2.14", + "object-path": "^0.11.4" + } + }, + "@apollographql/graphql-playground-html": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.0.tgz", + "integrity": "sha512-QAZIFrfVRkjvMkUHIQKZXZ3La0V5t12w5PWrhihYEabHwzIZV/txQd/kSYHgYPXC4s5OURxsXZop9f0BzI2QIQ==" + }, "@babel/code-frame": { "version": "7.0.0-beta.44", "resolved": "http://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz", @@ -31,6 +46,12 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz", "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -72,6 +93,14 @@ "chalk": "^2.0.0", "esutils": "^2.0.2", "js-tokens": "^3.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + } } }, "@babel/parser": { @@ -81,9 +110,9 @@ "dev": true }, "@babel/runtime-corejs2": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.0.0.tgz", - "integrity": "sha512-Yww0jXgolNtkhcK+Txo5JN+DjBpNmmAtD7G99HOebhEjBzjnACG09Tip9C8lSOF6PrhA56OeJWeOZduNJaKxBA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.1.2.tgz", + "integrity": "sha512-drxaPByExlcRDKW4ZLubUO4ZkI8/8ax9k9wve1aEthdLKFzjB7XRkOQ0xoTIWGxqdDnWDElkjYq77bt7yrcYJQ==", "requires": { "core-js": "^2.5.7", "regenerator-runtime": "^0.12.0" @@ -141,9 +170,9 @@ "dev": true }, "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", "dev": true } } @@ -160,15 +189,15 @@ } }, "@lerna/add": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.3.2.tgz", - "integrity": "sha512-nKRRRKb4wt/GAywi8P++NY1TUiyhMs2g2KHSb41I4/qiCFQnTj2zkeshPyNmtBGjKzFXnOqrmc/8Wa2vmHHZVg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.4.1.tgz", + "integrity": "sha512-Vf54B42jlD6G52qnv/cAGH70cVQIa+LX//lfsbkxHvzkhIqBl5J4KsnTOPkA9uq3R+zP58ayicCHB9ReiEWGJg==", "dev": true, "requires": { - "@lerna/bootstrap": "^3.3.2", + "@lerna/bootstrap": "^3.4.1", "@lerna/command": "^3.3.0", "@lerna/filter-options": "^3.3.2", - "@lerna/npm-conf": "^3.0.0", + "@lerna/npm-conf": "^3.4.1", "@lerna/validation-error": "^3.0.0", "dedent": "^0.7.0", "npm-package-arg": "^6.0.0", @@ -189,19 +218,19 @@ } }, "@lerna/bootstrap": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.3.2.tgz", - "integrity": "sha512-f0/FZ6iCXHNpHoUiM3wfmiJebHetrquP9mdNT7t//2iTGm1nz8iuKSLhfu9APazDXtqo3aDFx7JvuYKMg+GiXQ==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.4.1.tgz", + "integrity": "sha512-yZDJgNm/KDoRH2klzmQGmpWMg/XMzWgeWvauXkrfW/mj1wwmufOuh5pN4fBFxVmUUa/RFZdfMeaaJt3+W3PPBw==", "dev": true, "requires": { "@lerna/batch-packages": "^3.1.2", "@lerna/command": "^3.3.0", "@lerna/filter-options": "^3.3.2", "@lerna/has-npm-version": "^3.3.0", - "@lerna/npm-conf": "^3.0.0", + "@lerna/npm-conf": "^3.4.1", "@lerna/npm-install": "^3.3.0", "@lerna/rimraf-dir": "^3.3.0", - "@lerna/run-lifecycle": "^3.3.1", + "@lerna/run-lifecycle": "^3.4.1", "@lerna/run-parallel-batches": "^3.0.0", "@lerna/symlink-binary": "^3.3.0", "@lerna/symlink-dependencies": "^3.3.0", @@ -220,16 +249,16 @@ } }, "@lerna/changed": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.3.2.tgz", - "integrity": "sha512-wLH6RzYPQAryrsJakc9I3k0aFWE/cJyWoUD8dQy186jxwtLgeQdVc0+NegNyab7MIPi7Hsv9A3hx6lM1rPH94A==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.4.1.tgz", + "integrity": "sha512-gT7fhl4zQWyGETDO4Yy5wsFnqNlBSsezncS1nkMW1uO6jwnolwYqcr1KbrMR8HdmsZBn/00Y0mRnbtbpPPey8w==", "dev": true, "requires": { "@lerna/collect-updates": "^3.3.2", "@lerna/command": "^3.3.0", "@lerna/listable": "^3.0.0", "@lerna/output": "^3.0.0", - "@lerna/version": "^3.3.2" + "@lerna/version": "^3.4.1" } }, "@lerna/check-working-tree": { @@ -483,16 +512,15 @@ } }, "@lerna/conventional-commits": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.3.0.tgz", - "integrity": "sha512-nUFardc5G4jG5LI/Jlw0kblzlRLJ08ut6uJjHXTnUE/QJuKYaqBZm6goGG8OSxp/WltklndkQUOtThyZpefviA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.4.1.tgz", + "integrity": "sha512-3NETrA58aUkaEW3RdwdJ766Bg9NVpLzb26mtdlsJQcvB5sQBWH5dJSHIVQH1QsGloBeH2pE/mDUEVY8ZJXuR4w==", "dev": true, "requires": { "@lerna/validation-error": "^3.0.0", - "conventional-changelog-angular": "^1.6.6", - "conventional-changelog-core": "^2.0.5", - "conventional-recommended-bump": "^2.0.6", - "dedent": "^0.7.0", + "conventional-changelog-angular": "^5.0.1", + "conventional-changelog-core": "^3.1.0", + "conventional-recommended-bump": "^4.0.1", "fs-extra": "^7.0.0", "get-stream": "^4.0.0", "npm-package-arg": "^6.0.0", @@ -521,9 +549,9 @@ } }, "get-stream": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.0.0.tgz", - "integrity": "sha512-FneLKMENeOR7wOK0/ZXCh+lwqtnPwkeunJjRN28LPqzGvNAhYvrTAhXv6xDm4vsJ0M7lcRbIYHQudKsSy2RtSQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { "pump": "^3.0.0" @@ -542,14 +570,14 @@ } }, "@lerna/create": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.3.1.tgz", - "integrity": "sha512-4VASkTLvN66euTcWMPN2vIzEoP07hgutx7V70CXSOc+DiWV8S22z0PjXATi2yli83TC/Qj4gHYtU2futQrdY1A==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.4.1.tgz", + "integrity": "sha512-l+4t2SRO5nvW0MNYY+EWxbaMHsAN8bkWH3nyt7EzhBjs4+TlRAJRIEqd8o9NWznheE3pzwczFz1Qfl3BWbyM5A==", "dev": true, "requires": { "@lerna/child-process": "^3.3.0", "@lerna/command": "^3.3.0", - "@lerna/npm-conf": "^3.0.0", + "@lerna/npm-conf": "^3.4.1", "@lerna/validation-error": "^3.0.0", "camelcase": "^4.1.0", "dedent": "^0.7.0", @@ -825,9 +853,9 @@ } }, "@lerna/npm-conf": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-3.0.0.tgz", - "integrity": "sha512-xXG7qt349t+xzaHTQELmIDjbq8Q49HOMR8Nx/gTDBkMl02Fno91LXFnA4A7ErPiyUSGqNSfLw+zgij0hgpeN7w==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-3.4.1.tgz", + "integrity": "sha512-i9G6DnbCqiAqxKx2rSXej/n14qxlV/XOebL6QZonxJKzNTB+Q2wglnhTXmfZXTPJfoqimLaY4NfAEtbOXRWOXQ==", "dev": true, "requires": { "config-chain": "^1.1.11", @@ -970,33 +998,6 @@ "write-json-file": "^2.3.0" }, "dependencies": { - "cosmiconfig": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.6.tgz", - "integrity": "sha512-6DWfizHriCrFWURP1/qyhsiFvYdlJzbCzmtFWh744+KyWsJo5+kPzUZZaMRSSItoYc0pxFX7gEO7ZC1/gN/7AQ==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, "globby": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz", @@ -1018,25 +1019,6 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -1049,16 +1031,6 @@ "strip-bom": "^3.0.0" } }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -1135,13 +1107,22 @@ "strip-ansi": "^4.0.0", "through": "^2.3.6" } + }, + "rxjs": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } } } }, "@lerna/publish": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.4.0.tgz", - "integrity": "sha512-wcqWDKbkDjyj6F9Mw4/LL2CtpCN61RazNKxYm+fyJ20P2zfcAwLEwxttA6ZWIO8xUiLXkCTFIhwOulHyAPAq3w==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.4.1.tgz", + "integrity": "sha512-Nd5PT2Ksngo1GHqT6ccmGVfMvoA9MplBvqyzPFIjFIJOgODrJ9IGJAMobxgBQ6xXXShflQ/1dHWk8faTOYKLoQ==", "dev": true, "requires": { "@lerna/batch-packages": "^3.1.2", @@ -1151,15 +1132,15 @@ "@lerna/command": "^3.3.0", "@lerna/describe-ref": "^3.3.0", "@lerna/get-npm-exec-opts": "^3.0.0", - "@lerna/npm-conf": "^3.0.0", + "@lerna/npm-conf": "^3.4.1", "@lerna/npm-dist-tag": "^3.3.0", "@lerna/npm-publish": "^3.3.1", "@lerna/output": "^3.0.0", "@lerna/prompt": "^3.3.1", - "@lerna/run-lifecycle": "^3.3.1", + "@lerna/run-lifecycle": "^3.4.1", "@lerna/run-parallel-batches": "^3.0.0", "@lerna/validation-error": "^3.0.0", - "@lerna/version": "^3.3.2", + "@lerna/version": "^3.4.1", "fs-extra": "^7.0.0", "libnpmaccess": "^3.0.0", "npm-package-arg": "^6.0.0", @@ -1246,12 +1227,12 @@ } }, "@lerna/run-lifecycle": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-3.3.1.tgz", - "integrity": "sha512-xy4K3amlXk0LjSa5d3VqmrW9SsxMyvI7lw2dHDMb5PqjjcjMQgb6+nFbycwyJMhCP8u7MwQIZ4hFYF9XYbWSzQ==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-3.4.1.tgz", + "integrity": "sha512-N/hi2srM9A4BWEkXccP7vCEbf4MmIuALF00DTBMvc0A/ccItwUpl3XNuM7+ADDRK0mkwE3hDw89lJ3A7f8oUQw==", "dev": true, "requires": { - "@lerna/npm-conf": "^3.0.0", + "@lerna/npm-conf": "^3.4.1", "npm-lifecycle": "^2.0.0", "npmlog": "^4.1.2" } @@ -1302,16 +1283,6 @@ "strip-bom": "^3.0.0" } }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -1384,9 +1355,9 @@ } }, "@lerna/version": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.3.2.tgz", - "integrity": "sha512-2MHP6mA1f0t3UdzqPpfgAhsT1L64HOedlJxrQUoHrkou/G25Od4wjmKr9OZ0Oc4CLDbXD/sYEmE/9fZi1GGgKg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.4.1.tgz", + "integrity": "sha512-oefNaQLBJSI2WLZXw5XxDXk4NyF5/ct0V9ys/J308NpgZthPgwRPjk9ZR0o1IOxW1ABi6z3E317W/dxHDjvAkg==", "dev": true, "requires": { "@lerna/batch-packages": "^3.1.2", @@ -1394,10 +1365,10 @@ "@lerna/child-process": "^3.3.0", "@lerna/collect-updates": "^3.3.2", "@lerna/command": "^3.3.0", - "@lerna/conventional-commits": "^3.3.0", + "@lerna/conventional-commits": "^3.4.1", "@lerna/output": "^3.0.0", "@lerna/prompt": "^3.3.1", - "@lerna/run-lifecycle": "^3.3.1", + "@lerna/run-lifecycle": "^3.4.1", "@lerna/validation-error": "^3.0.0", "chalk": "^2.3.1", "dedent": "^0.7.0", @@ -1457,24 +1428,102 @@ "@magento/devcert": "^0.7.0", "@magento/directive-parser": "^0.1.1", "ajv": "^6.1.1", + "apollo-boost": "^0.1.15", "babel-helper-module-imports": "^7.0.0-beta.3", "babel-plugin-syntax-dynamic-import": "^6.18.0", "boxen": "^1.3.0", "chalk": "^2.4.1", + "cssnano": "^4.1.0", "debug": "^3.1.0", + "debug-error-middleware": "^1.3.0", "express": "^4.16.2", "flat-file-db": "^1.0.0", + "graphql": "^14.0.2", + "graphql-tag": "^2.9.2", "harmon": "^1.3.2", "hostile": "^1.3.1", "http-proxy-middleware": "^0.18.0", "lodash.get": "^4.4.2", + "node-fetch": "^2.2.0", "one-time": "^0.0.4", "openport": "0.0.5", "post-compile-webpack-plugin": "^0.1.2", + "react-dom": "^16.5.0", "through": "^2.3.8", "webpack-sources": "^1.1.0", "workbox-webpack-plugin": "^3.0.0-beta.1", "write-file-webpack-plugin": "^4.2.0" + }, + "dependencies": { + "@magento/upward-js": { + "version": "2.0.0-rc2.0.5", + "bundled": true, + "dependencies": { + "@magento/upward-spec": { + "version": "2.0.0-rc2.0.5", + "bundled": true + } + } + }, + "graphql": { + "version": "14.0.2", + "bundled": true, + "requires": { + "iterall": "^1.2.2" + } + } + } + }, + "@magento/upward-js": { + "version": "file:packages/upward-js", + "requires": { + "apollo-boost": "^0.1.13", + "apollo-link": "^1.2.2", + "apollo-link-http": "^1.5.4", + "contains-path": "^1.0.0", + "debug": "^3.1.0", + "debug-error-middleware": "^1.3.0", + "dotenv": "^6.0.0", + "graphql": "^0.13.2", + "graphql-tag": "^2.9.2", + "hogan.js": "^3.0.2", + "http-proxy-middleware": "^0.19.0", + "js-yaml": "^3.12.0", + "lodash": "^4.17.10", + "morgan": "^1.9.0", + "mustache": "^2.3.2", + "traverse": "^0.6.6" + }, + "dependencies": { + "@magento/upward-spec": { + "version": "2.0.0-rc2.0.5", + "bundled": true + }, + "http-proxy-middleware": { + "version": "0.19.0", + "bundled": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.10", + "micromatch": "^3.1.10" + } + } + } + }, + "@magento/upward-spec": { + "version": "file:packages/upward-spec", + "requires": { + "apollo-server": "^2.0.5", + "chalk": "^2.4.1", + "csv-parse": "^3.0.0", + "death": "^1.1.0", + "graphql": "^0.13.2", + "graphql-tag": "^2.9.2", + "js-yaml": "^3.12.0", + "tap-diff": "^0.1.1", + "tap-xunit": "^2.3.0", + "tape": "^4.9.1" } }, "@mrmlnc/readdir-enhanced": { @@ -1494,9 +1543,9 @@ "dev": true }, "@octokit/rest": { - "version": "15.12.1", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.12.1.tgz", - "integrity": "sha512-Nn3o2iVHHWNbw8MSOWTa67/6N1e7muaDWVMWr3FXUDSlMzB3lXlr6EdO1hwfUzpmZAkNc1rFN4y0tuy38gtd+A==", + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.13.0.tgz", + "integrity": "sha512-zgsrqMCLcv4XqpT0QGUykHTvKo33aCVzXP86Bq6HmeKuwY6hEWJ+AVCeL/m3bXk1JBpLyBgzjJDfWEfZcqsR6g==", "dev": true, "requires": { "before-after-hook": "^1.1.0", @@ -1510,6 +1559,60 @@ "url-template": "^2.0.8" } }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + }, "@storybook/addon-actions": { "version": "3.4.11", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-3.4.11.tgz", @@ -1526,14 +1629,6 @@ "prop-types": "^15.6.1", "react-inspector": "^2.2.2", "uuid": "^3.2.1" - }, - "dependencies": { - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - } } }, "@storybook/addon-links": { @@ -1670,15 +1765,9 @@ "supports-color": "^5.4.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "style-loader": { "version": "0.20.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", + "resolved": "http://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", "integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==", "dev": true, "requires": { @@ -1850,23 +1939,112 @@ } } }, + "@types/accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "requires": { + "@types/node": "*" + } + }, "@types/async": { "version": "2.0.49", "resolved": "https://registry.npmjs.org/@types/async/-/async-2.0.49.tgz", "integrity": "sha512-Benr3i5odUkvpFkOpzGqrltGdbSs+EVCkEBGXbuR7uT0VzhXKIkhem6PDzHdx5EonA+rfbB3QvP6aDOw5+zp5Q==", - "dev": true, "optional": true }, + "@types/body-parser": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "requires": { + "@types/node": "*" + } + }, + "@types/cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.4.tgz", + "integrity": "sha512-ipZjBVsm2tF/n8qFGOuGBkUij9X9ZswVi9G3bx/6dz7POpVa6gVHcj1wsX/LVEn9MMF41fxK/PnZPPoTD1UFPw==", + "requires": { + "@types/express": "*" + } + }, + "@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" + }, + "@types/express": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.0.tgz", + "integrity": "sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz", + "integrity": "sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w==", + "requires": { + "@types/events": "*", + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "@types/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", + "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==" + }, + "@types/mime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", + "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" + }, "@types/node": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.11.2.tgz", - "integrity": "sha512-XubfQDIg88PGJ7netQPf3QOKHF7Xht4WXGtg5W7cGBeQs9ETbYKwfchR9o+tRRA9iLTQ7nAre85M205JbYsjJA==" + "version": "10.11.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.11.4.tgz", + "integrity": "sha512-ojnbBiKkZFYRfQpmtnnWTMw+rzGp/JiystjluW9jgN3VzRwilXddJ6aGQ9V/7iuDG06SBgn7ozW9k3zcAnYjYQ==" + }, + "@types/range-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.2.tgz", + "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" + }, + "@types/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "@types/ws": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-5.1.2.tgz", + "integrity": "sha512-NkTXUKTYdXdnPE2aUUbGOXE1XfMK527SCvU/9bj86kyFF6kZ9ZnOQ3mK5jADn98Y2vEUD/7wKDgZa7Qst2wYOg==", + "requires": { + "@types/events": "*", + "@types/node": "*" + } }, "@types/zen-observable": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz", - "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==", - "dev": true + "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" }, "JSONStream": { "version": "1.3.4", @@ -1946,9 +2124,9 @@ } }, "acorn-walk": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.0.1.tgz", - "integrity": "sha512-PqVQ8c6a3kyqdsUZlC7nljp3FFuxipBRHKu+7C1h8QygBFlzTaDX5HD383jej3Peed+1aDG8HwkfB1Z1HMNPkw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.0.tgz", + "integrity": "sha512-ugTb7Lq7u4GfWSqqpwE0bGyoBZNMTok/zDBXxfEG0QM50jNlGhIWjRC1pPN7bvV1anhF+bs+/gNcRw+o55Evbg==", "dev": true }, "address": { @@ -2014,6 +2192,12 @@ "uri-js": "^4.2.2" } }, + "ajv-errors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz", + "integrity": "sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=", + "dev": true + }, "ajv-keywords": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", @@ -2099,7 +2283,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" @@ -2109,7 +2292,6 @@ "version": "0.1.16", "resolved": "https://registry.npmjs.org/apollo-boost/-/apollo-boost-0.1.16.tgz", "integrity": "sha512-ppnQr90n2cn0ZMveEbqxW6NxmskIpThiLziLlRpRLy4keoAICg1NZecxJQmeaBLMtyhf5UholAjpS2SLmEJeHg==", - "dev": true, "requires": { "apollo-cache": "^1.1.17", "apollo-cache-inmemory": "^1.2.10", @@ -2125,27 +2307,33 @@ "version": "1.1.17", "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.1.17.tgz", "integrity": "sha512-7sp24n2HZO4vXgTaKNomLyIfGxG4gDdDkBB0jkRzRi7HhnKmfwhiF/RCiKNbgLdrPX151INdls0KwIVliD0dHQ==", - "dev": true, "requires": { "apollo-utilities": "^1.0.21" } }, + "apollo-cache-control": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.2.5.tgz", + "integrity": "sha512-xEDrUvo3U2mQKSzA8NzQmgeqK4ytwFnTGl2YKGKPfG0+r8fZdswKp6CDBue29KLO8KkSuqW/hntveWrAdK51FQ==", + "requires": { + "apollo-server-env": "^2.0.3", + "graphql-extensions": "^0.2.1" + } + }, "apollo-cache-inmemory": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.2.10.tgz", - "integrity": "sha512-eBusPFVtYIuo+PIfJdAwUCQ4cs7AJ4mB7sTdXxNQCXToYw8mzE6EfHnV37kdVfBXSaa82BzE2rb/YUq/duuamw==", - "dev": true, + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.3.5.tgz", + "integrity": "sha512-CD4Dl9vcCp7N05KUqR3rNDj2WJ1DQNNfeyBUIo5T6XTiUhfBQp5x+CL7S+ezy5mPp+xo4TnwFzLFh/vy2LdDog==", "requires": { "apollo-cache": "^1.1.17", "apollo-utilities": "^1.0.21", - "graphql-anywhere": "^4.1.19" + "optimism": "^0.6.6" } }, "apollo-client": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.4.2.tgz", "integrity": "sha512-g1z23umaVSoKLj9xNl0aAnk2KBF4JeBi7MeKFc9CGTixH7TkqeQUQtxcjrC7j2h4KmDbuhOAHOFUGf8YshN+ag==", - "dev": true, "requires": { "@types/async": "2.0.49", "@types/zen-observable": "^0.8.0", @@ -2157,11 +2345,39 @@ "zen-observable": "^0.8.0" } }, + "apollo-datasource": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-0.1.3.tgz", + "integrity": "sha512-yEGEe5Cjzqqu5ml1VV3O8+C+thzdknZri9Ny0P3daTGNO+45J3vBOMcmaANeeI2+OOeWxdqUNa5aPOx/35kniw==", + "requires": { + "apollo-server-caching": "0.1.2", + "apollo-server-env": "2.0.3" + } + }, + "apollo-engine-reporting": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/apollo-engine-reporting/-/apollo-engine-reporting-0.0.6.tgz", + "integrity": "sha512-JmfNJ9v3QEJQ8ZhLfCKEDiww53n5kj5HarP85p8LreoYNojbvcWN8Qm6RgvSG5N/ZJrAYHeTRQbSxm1vWwGubw==", + "requires": { + "apollo-engine-reporting-protobuf": "^0.0.1", + "apollo-server-env": "^2.0.3", + "async-retry": "^1.2.1", + "graphql-extensions": "^0.2.1", + "lodash": "^4.17.10" + } + }, + "apollo-engine-reporting-protobuf": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.0.1.tgz", + "integrity": "sha512-AySoDgog2p1Nph44FyyqaU4AfRZOXx8XZxRsVHvYY4dHlrMmDDhhjfF3Jswa7Wr8X/ivvx3xA0jimRn6rsG8Ew==", + "requires": { + "protobufjs": "^6.8.6" + } + }, "apollo-link": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.3.tgz", "integrity": "sha512-iL9yS2OfxYhigme5bpTbmRyC+Htt6tyo2fRMHT3K1XRL/C5IQDDz37OjpPy4ndx7WInSvfSZaaOTKFja9VWqSw==", - "dev": true, "requires": { "apollo-utilities": "^1.0.0", "zen-observable-ts": "^0.8.10" @@ -2180,7 +2396,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/apollo-link-dedup/-/apollo-link-dedup-1.0.10.tgz", "integrity": "sha512-tpUI9lMZsidxdNygSY1FxflXEkUZnvKRkMUsXXuQUNoSLeNtEvUX7QtKRAl4k9ubLl8JKKc9X3L3onAFeGTK8w==", - "dev": true, "requires": { "apollo-link": "^1.2.3" } @@ -2189,7 +2404,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.1.1.tgz", "integrity": "sha512-/yPcaQWcBdB94vpJ4FsiCJt1dAGGRm+6Tsj3wKwP+72taBH+UsGRQQZk7U/1cpZwl1yqhHZn+ZNhVOebpPcIlA==", - "dev": true, "requires": { "apollo-link": "^1.2.3" } @@ -2198,7 +2412,6 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.5.tgz", "integrity": "sha512-C5N6N/mRwmepvtzO27dgMEU3MMtRKSqcljBkYNZmWwH11BxkUQ5imBLPM3V4QJXNE7NFuAQAB5PeUd4ligivTQ==", - "dev": true, "requires": { "apollo-link": "^1.2.3", "apollo-link-http-common": "^0.2.5" @@ -2208,7 +2421,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.5.tgz", "integrity": "sha512-6FV1wr5AqAyJ64Em1dq5hhGgiyxZE383VJQmhIoDVc3MyNcFL92TkhxREOs4rnH2a9X2iJMko7nodHSGLC6d8w==", - "dev": true, "requires": { "apollo-link": "^1.2.3" } @@ -2217,50 +2429,191 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/apollo-link-state/-/apollo-link-state-0.4.2.tgz", "integrity": "sha512-xMPcAfuiPVYXaLwC6oJFIZrKgV3GmdO31Ag2eufRoXpvT0AfJZjdaPB4450Nu9TslHRePN9A3quxNueILlQxlw==", - "dev": true, "requires": { "apollo-utilities": "^1.0.8", "graphql-anywhere": "^4.1.0-alpha.0" } }, - "apollo-utilities": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.21.tgz", - "integrity": "sha512-ZcxELlEl+sDCYBgEMdNXJAsZtRVm8wk4HIA58bMsqYfd1DSAJQEtZ93F0GZgYNAGy3QyaoBeZtbb0/01++G8JQ==", - "dev": true, + "apollo-server": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/apollo-server/-/apollo-server-2.1.0.tgz", + "integrity": "sha512-Uo5RFHGtUPq3OvycLXCll5QgXf2wNVBFYUhapByADBP4E1KRgbyl9Fbf82OgcbbLYwEZTlQMbyBpd6hX8XJKAw==", "requires": { - "fast-json-stable-stringify": "^2.0.0", - "fclone": "^1.0.11" + "apollo-server-core": "^2.1.0", + "apollo-server-express": "^2.1.0", + "express": "^4.0.0", + "graphql-subscriptions": "^0.5.8", + "graphql-tools": "^3.0.4" } }, - "append-transform": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", - "dev": true, + "apollo-server-caching": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.1.2.tgz", + "integrity": "sha512-jBRnsTgXN0m8yVpumoelaUq9mXR7YpJ3EE+y/alI7zgXY+0qFDqksRApU8dEfg3q6qUnO7rFxRhdG5eyc0+1ig==", "requires": { - "default-require-extensions": "^1.0.0" + "lru-cache": "^4.1.3" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + "apollo-server-core": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.1.0.tgz", + "integrity": "sha512-D1Tw0o3NzCQ2KGM8EWh9AHELHmn/SE361dtlqJxkbelxXqAkCIGIFywF30h+0ezhMbgbO7eqBBJfvRilF/oJHA==", + "requires": { + "@apollographql/apollo-upload-server": "^5.0.3", + "@types/ws": "^5.1.2", + "apollo-cache-control": "^0.2.5", + "apollo-datasource": "^0.1.3", + "apollo-engine-reporting": "^0.0.6", + "apollo-server-caching": "^0.1.2", + "apollo-server-env": "^2.0.3", + "apollo-server-errors": "^2.0.2", + "apollo-tracing": "^0.2.5", + "graphql-extensions": "^0.2.1", + "graphql-subscriptions": "^0.5.8", + "graphql-tag": "^2.9.2", + "graphql-tools": "^3.0.4", + "hash.js": "^1.1.3", + "lodash": "^4.17.10", + "subscriptions-transport-ws": "^0.9.11", + "ws": "^5.2.0" + } }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "apollo-server-env": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-2.0.3.tgz", + "integrity": "sha512-uIfKFH8n8xKO0eLb9Fa79+s2DdMuVethgznvW6SrOYq5VzgkIIobqKEuZPKa5wObw9CkCyju/+Sr7b7WWMFxUQ==", "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { + "node-fetch": "^2.1.2", + "util.promisify": "^1.0.0" + } + }, + "apollo-server-errors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-2.0.2.tgz", + "integrity": "sha512-zyWDqAVDCkj9espVsoUpZr9PwDznM8UW6fBfhV+i1br//s2AQb07N6ektZ9pRIEvkhykDZW+8tQbDwAO0vUROg==" + }, + "apollo-server-express": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.1.0.tgz", + "integrity": "sha512-jLFIz1VLduMA/rme4OAy3IPeoaMEZOPoQXpio8AhfjIqCijRPPfoWJ2QMqz56C/g3vas7rZtgcVOrHpjBKudjw==", + "requires": { + "@apollographql/apollo-upload-server": "^5.0.3", + "@apollographql/graphql-playground-html": "^1.6.0", + "@types/accepts": "^1.3.5", + "@types/body-parser": "1.17.0", + "@types/cors": "^2.8.4", + "@types/express": "4.16.0", + "accepts": "^1.3.5", + "apollo-server-core": "^2.1.0", + "body-parser": "^1.18.3", + "cors": "^2.8.4", + "graphql-subscriptions": "^0.5.8", + "graphql-tools": "^3.0.4", + "type-is": "^1.6.16" + }, + "dependencies": { + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "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" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + } + } + }, + "apollo-tracing": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.2.5.tgz", + "integrity": "sha512-DZO7pfL5LATHeJdVFoTZ/N3HwA+IMf1YnIt5K+uMQW+/MrRgYOtTszUv5tYX2cUIqHYHcbdDaBQUuIXwSpaV2Q==", + "requires": { + "apollo-server-env": "^2.0.3", + "graphql-extensions": "^0.2.1" + } + }, + "apollo-utilities": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.21.tgz", + "integrity": "sha512-ZcxELlEl+sDCYBgEMdNXJAsZtRVm8wk4HIA58bMsqYfd1DSAJQEtZ93F0GZgYNAGy3QyaoBeZtbb0/01++G8JQ==", + "requires": { + "fast-json-stable-stringify": "^2.0.0", + "fclone": "^1.0.11" + } + }, + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "dev": true, + "requires": { + "default-require-extensions": "^1.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -2528,9 +2881,12 @@ "dev": true }, "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "requires": { + "lodash": "^4.17.10" + } }, "async-each": { "version": "1.0.1", @@ -2552,6 +2908,14 @@ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, + "async-retry": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz", + "integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==", + "requires": { + "retry": "0.12.0" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2573,6 +2937,83 @@ "num2fraction": "^1.2.2", "postcss": "^5.2.16", "postcss-value-parser": "^3.2.3" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } } }, "aws-sign2": { @@ -2704,6 +3145,16 @@ "is-extglob": "^1.0.0" } }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^2.0.0" + } + }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", @@ -2750,6 +3201,12 @@ "parse-glob": "^3.0.4", "regex-cache": "^0.4.2" } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -2785,9 +3242,14 @@ "supports-color": "^2.0.0" } }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -2850,6 +3312,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -2913,6 +3380,11 @@ "to-fast-properties": "^1.0.3" } }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -3511,6 +3983,15 @@ } } }, + "babel-plugin-import-graphql": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-import-graphql/-/babel-plugin-import-graphql-2.6.2.tgz", + "integrity": "sha1-D4ijsyEzdlhUJTuLp/k4M2fNNg4=", + "dev": true, + "requires": { + "graphql-tag": "^2.9.2" + } + }, "babel-plugin-istanbul": { "version": "4.1.6", "resolved": "http://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", @@ -3550,41 +4031,13 @@ "resolve": "^1.8.1" }, "dependencies": { - "cosmiconfig": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.6.tgz", - "integrity": "sha512-6DWfizHriCrFWURP1/qyhsiFvYdlJzbCzmtFWh744+KyWsJo5+kPzUZZaMRSSItoYc0pxFX7gEO7ZC1/gN/7AQ==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", "dev": true, "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "path-parse": "^1.0.5" } } } @@ -4852,6 +5305,21 @@ "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -5021,8 +5489,7 @@ "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, "bowser": { "version": "1.9.4", @@ -5139,7 +5606,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -5196,7 +5663,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -5238,24 +5705,26 @@ } }, "browser-sync": { - "version": "2.24.7", - "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.24.7.tgz", - "integrity": "sha512-NqXek0cPNEayQm77VGnD+qrwcVBTKMIQ9bdP6IWDRUTU1Bk7tZeq5QR3OG5Rr36Rao1t+Vx1QnfolHvvr5qsTA==", + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.26.0.tgz", + "integrity": "sha512-/2f2/jPmFEdPw7wcARid/oGO237RMfZ8SyAYVtF4Zq5R/E+78zx/rH6aFc/UFY+VDHcsCqmDsfIEi/q1fA3l4Q==", "requires": { - "browser-sync-ui": "v1.0.1", + "browser-sync-client": "^2.26.0", + "browser-sync-ui": "^2.26.0", "bs-recipes": "1.3.4", - "chokidar": "1.7.0", + "bs-snippet-injector": "^2.0.1", + "chokidar": "^2.0.4", "connect": "3.6.6", - "connect-history-api-fallback": "^1.5.0", + "connect-history-api-fallback": "^1", "dev-ip": "^1.0.1", "easy-extender": "^2.3.4", - "eazy-logger": "3.0.2", + "eazy-logger": "^3", "etag": "^1.8.1", "fresh": "^0.5.2", "fs-extra": "3.0.1", "http-proxy": "1.15.2", - "immutable": "3.8.2", - "localtunnel": "1.9.0", + "immutable": "^3", + "localtunnel": "1.9.1", "micromatch": "2.3.11", "opn": "5.3.0", "portscanner": "2.1.1", @@ -5276,15 +5745,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", @@ -5313,22 +5773,6 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - } - }, "eventemitter3": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", @@ -5443,7 +5887,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -5472,16 +5916,25 @@ } } }, + "browser-sync-client": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.26.0.tgz", + "integrity": "sha512-XRVN6xNFCQYb5mjrDoVzdV2rBK6PMLtTeYkKcs5UPp+/cuviB8z8odaHx0Oe/cAs3Vl45csRdpa7T+q1Zf+6qQ==", + "requires": { + "mitt": "^1.1.3", + "rxjs": "^5.5.6" + } + }, "browser-sync-ui": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-1.0.1.tgz", - "integrity": "sha512-RIxmwVVcUFhRd1zxp7m2FfLnXHf59x4Gtj8HFwTA//3VgYI3AKkaQAuDL8KDJnE59XqCshxZa13JYuIWtZlKQg==", + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.26.0.tgz", + "integrity": "sha512-7bXPmkQ9GuSPUgji3Nb4y0IL8wS2LfdrKSG28bQwvys5bs4kWyXDec2RkYBiupTTModM5lbwXgtmoh7GWQuLGg==", "requires": { "async-each-series": "0.1.1", - "connect-history-api-fallback": "^1.1.0", - "immutable": "^3.7.6", + "connect-history-api-fallback": "^1", + "immutable": "^3", "server-destroy": "1.0.1", - "socket.io-client": "2.0.4", + "socket.io-client": "^2.0.4", "stream-throttle": "^0.1.3" } }, @@ -5558,12 +6011,13 @@ } }, "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.2.0.tgz", + "integrity": "sha512-Berls1CHL7qfQz8Lct6QxYA5d2Tvt4doDWHcjvAISybpd+EKZVppNtXgXhaN6SdrPKo7YLTSZuYBs5cYrSWN8w==", "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" + "caniuse-lite": "^1.0.30000889", + "electron-to-chromium": "^1.3.73", + "node-releases": "^1.0.0-alpha.12" } }, "bs-recipes": { @@ -5571,6 +6025,11 @@ "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", "integrity": "sha1-DS1NSKcYyMBEdp/cT4lZLci2lYU=" }, + "bs-snippet-injector": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bs-snippet-injector/-/bs-snippet-injector-2.0.1.tgz", + "integrity": "sha1-YbU5PxH1JVntEgaTEANDtu2wTdU=" + }, "bser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", @@ -5641,6 +6100,15 @@ "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", "dev": true }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + } + }, "byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", @@ -5770,26 +6238,25 @@ } }, "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "requires": { - "browserslist": "^1.3.6", - "caniuse-db": "^1.0.30000529", + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", "lodash.memoize": "^4.1.2", "lodash.uniq": "^4.5.0" } }, "caniuse-db": { - "version": "1.0.30000887", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000887.tgz", - "integrity": "sha512-yOScC1WJ6ihxxPNeWSqYc2nKHqeHzXMY382yvC0mZdi+kWBrlEdCFeR/T1s5Abe5n51HoD6IA/Gho2T8vnRT2g==" + "version": "1.0.30000890", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000890.tgz", + "integrity": "sha512-aO5uw1Taw8GkNMMXIWOz/WJz3y6tR1ETUAdH/pvO5EoJ3I1Po9vNJd9aMjY1GKucS/OXWMiQbXRbk3O1sgCbRA==" }, "caniuse-lite": { - "version": "1.0.30000887", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000887.tgz", - "integrity": "sha512-AHpONWuGFWO8yY9igdXH94tikM6ERS84286r0cAMAXYFtJBk76lhiMhtCxBJNBZsD6hzlvpWZ2AtbVFEkf4JQA==", - "dev": true + "version": "1.0.30000890", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000890.tgz", + "integrity": "sha512-4NI3s4Y6ROm+SgZN5sLUG4k7nVWQnedis3c/RWkynV5G6cHSY7+a8fwFyn2yoBDE3E6VswhTNNwR3PvzGqlTkg==" }, "capture-exit": { "version": "1.2.0", @@ -5881,6 +6348,18 @@ "parse5": "^3.0.1" }, "dependencies": { + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, "domhandler": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", @@ -5890,9 +6369,19 @@ "domelementtype": "1" } }, - "htmlparser2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "htmlparser2": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", "dev": true, "requires": { @@ -5940,7 +6429,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", - "dev": true, "requires": { "anymatch": "^2.0.0", "async-each": "^1.0.0", @@ -5955,29 +6443,6 @@ "path-is-absolute": "^1.0.0", "readdirp": "^2.0.0", "upath": "^1.0.5" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - } } }, "chownr": { @@ -6039,7 +6504,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -6086,14 +6551,6 @@ "dev": true, "requires": { "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "cli-boxes": { @@ -6116,6 +6573,13 @@ "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", "requires": { "colors": "1.0.3" + }, + "dependencies": { + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + } } }, "cli-width": { @@ -6171,7 +6635,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -6205,9 +6669,9 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.1.tgz", + "integrity": "sha512-5wfTTO8E2/ja4jFSxePXlG5nRu5bBtL/r1HCIpJW/lzT6yDtKl0u0Z4o/Vpz32IpKmBn7HerheEZQgA9N2DarQ==", "requires": { "q": "^1.1.2" } @@ -6227,13 +6691,12 @@ } }, "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" + "color-convert": "^1.9.1", + "color-string": "^1.5.2" } }, "color-convert": { @@ -6250,11 +6713,12 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", "requires": { - "color-name": "^1.0.0" + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, "color-support": { @@ -6270,12 +6734,32 @@ "color": "^0.11.0", "css-color-names": "0.0.4", "has": "^1.0.1" + }, + "dependencies": { + "color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "requires": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "requires": { + "color-name": "^1.0.0" + } + } } }, "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" }, "columnify": { "version": "1.5.4", @@ -6295,7 +6779,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -6343,9 +6827,9 @@ "integrity": "sha512-doWDvhXCcW5LK0cIUWrOQ8oMFXJv3lEQCkJpGVjM8v9SV0uhqYXB943538tEA2CiaWqSyuYUGAm5ezDwEx9xlw==" }, "commander": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", - "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==" + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" }, "common-tags": { "version": "1.8.0", @@ -6582,6 +7066,15 @@ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" }, + "contains-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-1.0.0.tgz", + "integrity": "sha1-NFizMhhWA+ju0Y9RjUoQiIo6vJE=", + "requires": { + "normalize-path": "^2.1.1", + "path-starts-with": "^1.0.0" + } + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -6593,9 +7086,9 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "conventional-changelog-angular": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-1.6.6.tgz", - "integrity": "sha512-suQnFSqCxRwyBxY68pYTsFkG0taIdinHLNEAX5ivtw8bCRnIgnpvcHmlR/yjUyZIrNPYAoXlY1WiEKWgSE4BNg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.1.tgz", + "integrity": "sha512-q4ylJ68fWZDdrFC9z4zKcf97HW6hp7Mo2YlqD4owfXhecFKy/PJCU/1oVFF4TqochchChqmZ0Vb0e0g8/MKNlA==", "dev": true, "requires": { "compare-func": "^1.3.1", @@ -6603,18 +7096,18 @@ } }, "conventional-changelog-core": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-2.0.11.tgz", - "integrity": "sha512-HvTE6RlqeEZ/NFPtQeFLsIDOLrGP3bXYr7lFLMhCVsbduF1MXIe8OODkwMFyo1i9ku9NWBwVnVn0jDmIFXjDRg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.1.0.tgz", + "integrity": "sha512-bcZkcFXkqVgG2W8m/1wjlp2wn/BKDcrPgw3/mvSEQtzs8Pax8JbAPFpEQReHY92+EKNNXC67wLA8y2xcNx0rDA==", "dev": true, "requires": { - "conventional-changelog-writer": "^3.0.9", - "conventional-commits-parser": "^2.1.7", + "conventional-changelog-writer": "^4.0.0", + "conventional-commits-parser": "^3.0.0", "dateformat": "^3.0.0", "get-pkg-repo": "^1.0.0", - "git-raw-commits": "^1.3.6", + "git-raw-commits": "^2.0.0", "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^1.3.6", + "git-semver-tags": "^2.0.0", "lodash": "^4.2.1", "normalize-package-data": "^2.3.5", "q": "^1.5.1", @@ -6672,19 +7165,19 @@ } }, "conventional-changelog-preset-loader": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-1.1.8.tgz", - "integrity": "sha512-MkksM4G4YdrMlT2MbTsV2F6LXu/hZR0Tc/yenRrDIKRwBl/SP7ER4ZDlglqJsCzLJi4UonBc52Bkm5hzrOVCcw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.0.1.tgz", + "integrity": "sha512-HiSfhXNzAzG9klIqJaA97MMiNBR4js+53g4Px0k7tgKeCNVXmrDrm+CY+nIqcmG5NVngEPf8rAr7iji1TWW7zg==", "dev": true }, "conventional-changelog-writer": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-3.0.9.tgz", - "integrity": "sha512-n9KbsxlJxRQsUnK6wIBRnARacvNnN4C/nxnxCkH+B/R1JS2Fa+DiP1dU4I59mEDEjgnFaN2+9wr1P1s7GYB5/Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.0.tgz", + "integrity": "sha512-hMZPe0AQ6Bi05epeK/7hz80xxk59nPA5z/b63TOHq2wigM0/akreOc8N4Jam5b9nFgKWX1e9PdPv2ewgW6bcfg==", "dev": true, "requires": { "compare-func": "^1.3.1", - "conventional-commits-filter": "^1.1.6", + "conventional-commits-filter": "^2.0.0", "dateformat": "^3.0.0", "handlebars": "^4.0.2", "json-stringify-safe": "^5.0.1", @@ -6774,16 +7267,6 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -6894,9 +7377,9 @@ } }, "conventional-commits-filter": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-1.1.6.tgz", - "integrity": "sha512-KcDgtCRKJCQhyk6VLT7zR+ZOyCnerfemE/CsR3iQpzRRFbLEs0Y6rwk3mpDvtOh04X223z+1xyJ582Stfct/0Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.0.tgz", + "integrity": "sha512-Cfl0j1/NquB/TMVx7Wrmyq7uRM+/rPQbtVVGwzfkhZ6/yH6fcMmP0Q/9044TBZPTNdGzm46vXFXL14wbET0/Mg==", "dev": true, "requires": { "is-subset": "^0.1.1", @@ -6904,9 +7387,9 @@ } }, "conventional-commits-parser": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-2.1.7.tgz", - "integrity": "sha512-BoMaddIEJ6B4QVMSDu9IkVImlGOSGA1I2BQyOZHeLQ6qVOJLcLKn97+fL6dGbzWEiqDzfH4OkcveULmeq2MHFQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.0.tgz", + "integrity": "sha512-GWh71U26BLWgMykCp+VghZ4s64wVbtseECcKQ/PvcPZR2cUnz+FUc2J9KjxNl7/ZbCxST8R03c9fc+Vi0umS9Q==", "dev": true, "requires": { "JSONStream": "^1.0.4", @@ -6991,16 +7474,6 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -7102,17 +7575,17 @@ } }, "conventional-recommended-bump": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-2.0.9.tgz", - "integrity": "sha512-YE6/o+648qkX3fTNvfBsvPW3tSnbZ6ec3gF0aBahCPgyoVHU2Mw0nUAZ1h1UN65GazpORngrgRC8QCltNYHPpQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-4.0.1.tgz", + "integrity": "sha512-9waJvW01TUs4HQJ3khwGSSlTlKsY+5u7OrxHL+oWEoGNvaNO/0qL6qqnhS3J0Fq9fNKA9bmlf5cOXjCQoW+I4Q==", "dev": true, "requires": { "concat-stream": "^1.6.0", - "conventional-changelog-preset-loader": "^1.1.8", - "conventional-commits-filter": "^1.1.6", - "conventional-commits-parser": "^2.1.7", - "git-raw-commits": "^1.3.6", - "git-semver-tags": "^1.3.6", + "conventional-changelog-preset-loader": "^2.0.1", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.0", + "git-raw-commits": "^2.0.0", + "git-semver-tags": "^2.0.0", "meow": "^4.0.0", "q": "^1.5.1" }, @@ -7184,16 +7657,6 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -7313,30 +7776,23 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", + "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.6.tgz", + "integrity": "sha512-6DWfizHriCrFWURP1/qyhsiFvYdlJzbCzmtFWh744+KyWsJo5+kPzUZZaMRSSItoYc0pxFX7gEO7ZC1/gN/7AQ==", "requires": { "is-directory": "^0.3.1", - "js-yaml": "^3.4.3", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "os-homedir": "^1.0.1", - "parse-json": "^2.2.0", - "require-from-string": "^1.1.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - } + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0" } }, "coveralls": { @@ -7353,22 +7809,6 @@ "request": "^2.85.0" }, "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, "minimist": { "version": "1.2.0", "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -7429,12 +7869,28 @@ "fbjs": "^0.8.9", "loose-envify": "^1.3.1", "object-assign": "^4.1.1" + } + }, + "cross-fetch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.2.tgz", + "integrity": "sha1-pH/09/xxLauo9qaVoRyUhEDUVyM=", + "dev": true, + "requires": { + "node-fetch": "2.1.2", + "whatwg-fetch": "2.0.4" }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "node-fetch": { + "version": "2.1.2", + "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", + "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=", + "dev": true + }, + "whatwg-fetch": { + "version": "2.0.4", + "resolved": "http://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==", "dev": true } } @@ -7478,9 +7934,18 @@ "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, "css-in-js-utils": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "resolved": "http://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", "dev": true, "requires": { @@ -7490,7 +7955,7 @@ }, "css-loader": { "version": "0.28.11", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", + "resolved": "http://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", "dev": true, "requires": { @@ -7510,123 +7975,620 @@ "source-list-map": "^2.0.0" }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true - } - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" - }, - "dependencies": { - "regexpu-core": { + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "dev": true, + "requires": { + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "dev": true, + "requires": { + "q": "^1.1.2" + } + }, + "cssnano": { + "version": "3.10.0", + "resolved": "http://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "dev": true, + "requires": { + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "dev": true, + "requires": { + "clap": "^1.0.9", + "source-map": "^0.5.3" + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "has-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", "dev": true, "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "html-comment-regex": "^1.1.0" } - } - } - }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", - "dev": true - }, - "cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "requires": { - "through": "X.X.X" - } - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", - "requires": { - "autoprefixer": "^6.3.1", - "decamelize": "^1.1.2", - "defined": "^1.0.0", - "has": "^1.0.1", - "object-assign": "^4.0.1", - "postcss": "^5.0.14", - "postcss-calc": "^5.2.0", - "postcss-colormin": "^2.1.8", - "postcss-convert-values": "^2.3.4", - "postcss-discard-comments": "^2.0.4", - "postcss-discard-duplicates": "^2.0.1", - "postcss-discard-empty": "^2.0.1", - "postcss-discard-overridden": "^0.1.1", - "postcss-discard-unused": "^2.2.1", - "postcss-filter-plugins": "^2.0.0", - "postcss-merge-idents": "^2.1.5", - "postcss-merge-longhand": "^2.0.1", - "postcss-merge-rules": "^2.0.3", - "postcss-minify-font-values": "^1.0.2", - "postcss-minify-gradients": "^1.0.1", - "postcss-minify-params": "^1.0.4", - "postcss-minify-selectors": "^2.0.4", - "postcss-normalize-charset": "^1.1.0", - "postcss-normalize-url": "^3.0.7", - "postcss-ordered-values": "^2.1.0", - "postcss-reduce-idents": "^2.2.2", - "postcss-reduce-initial": "^1.0.0", - "postcss-reduce-transforms": "^1.0.3", - "postcss-svgo": "^2.1.1", - "postcss-unique-selectors": "^2.0.2", - "postcss-value-parser": "^3.2.3", - "postcss-zindex": "^2.0.1" - }, + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + } + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "http://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "dev": true, + "requires": { + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "dev": true, + "requires": { + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "dev": true, + "requires": { + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "http://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "http://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "dev": true, + "requires": { + "postcss": "^5.0.16" + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "dev": true, + "requires": { + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" + } + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "http://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "http://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "dev": true, + "requires": { + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "http://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "http://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "requires": { + "postcss": "^5.0.5" + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "http://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "http://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "http://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "dev": true, + "requires": { + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" + } + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "http://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "http://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dev": true, + "requires": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "dev": true, + "requires": { + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" + } + } + } + }, + "css-select": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.0.tgz", + "integrity": "sha512-MGhoq1S9EyPgZIGnts8Yz5WwUOyHmPMdlqeifsYs/xFX7AAm3hY0RJe1dqVlXtYPI66Nsk39R/sa5/ree6L2qg==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.1" + } + }, + "css-select-base-adapter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz", + "integrity": "sha1-AQKz0UYw34bD65+p9UVicBBs+ZA=" + }, + "css-selector-tokenizer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } } } }, - "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "css-tree": { + "version": "1.0.0-alpha.28", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz", + "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==", "requires": { - "clap": "^1.0.9", + "mdn-data": "~1.1.0", "source-map": "^0.5.3" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "css-unit-converter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz", + "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=" + }, + "css-url-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz", + "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=" + }, + "css-what": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", + "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=" + }, + "cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "requires": { + "through": "X.X.X" + } + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "cssnano": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.4.tgz", + "integrity": "sha512-wP0wbOM9oqsek14CiNRYrK9N3w3jgadtGZKHXysgC/OMVpy0KZgWVPdNqODSZbz7txO9Gekr9taOfcCgL0pOOw==", + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.2", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "cssnano-preset-default": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.2.tgz", + "integrity": "sha512-zO9PeP84l1E4kbrdyF7NSLtA/JrJY1paX5FHy5+w/ziIXO2kDqDMfJ/mosXkaHHSa3RPiIY3eB6aEgwx3IiGqA==", + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^6.0.2", + "postcss-colormin": "^4.0.2", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.1", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.6", + "postcss-merge-rules": "^4.0.2", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.1", + "postcss-minify-params": "^4.0.1", + "postcss-minify-selectors": "^4.0.1", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.1", + "postcss-normalize-positions": "^4.0.1", + "postcss-normalize-repeat-style": "^4.0.1", + "postcss-normalize-string": "^4.0.1", + "postcss-normalize-timing-functions": "^4.0.1", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.1", + "postcss-ordered-values": "^4.1.1", + "postcss-reduce-initial": "^4.0.2", + "postcss-reduce-transforms": "^4.0.1", + "postcss-svgo": "^4.0.1", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=" + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=" + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "requires": { + "postcss": "^7.0.0" + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==" + }, + "csso": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", + "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", + "requires": { + "css-tree": "1.0.0-alpha.29" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.29", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", + "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", + "requires": { + "mdn-data": "~1.1.0", + "source-map": "^0.5.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } } }, "cssom": { @@ -7650,6 +8612,11 @@ "integrity": "sha512-Nt5VDyOTIIV4/nRFswoCKps1R5CD1hkiyjBE9/thNaNZILLEviVw9yWQw15+O+CpNjQKB/uvdcxFFOrSflY3Yw==", "dev": true }, + "csv-parse": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-3.1.3.tgz", + "integrity": "sha512-fs+nzn0lU+e7uZHkY/SvEYiRT3PCvb746csfl238iUXh1D+gzzN4crzgZ7gkVDutoPWYwfrFSQaSAulSeynTlg==" + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -7725,7 +8692,7 @@ }, "json5": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { @@ -7803,6 +8770,11 @@ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=" }, + "death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha1-AaqcQB7dknUFFEcLgmY5DGbGcxg=" + }, "debug": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", @@ -7811,6 +8783,22 @@ "ms": "^2.1.1" } }, + "debug-error-middleware": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/debug-error-middleware/-/debug-error-middleware-1.3.0.tgz", + "integrity": "sha1-w/1bhO+2kUqm2VixUX+FOIg1nRY=", + "requires": { + "handlebars": "^4.0.10", + "source-map": "^0.5.7" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, "debuglog": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", @@ -7855,8 +8843,7 @@ "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" }, "deep-extend": { "version": "0.6.0", @@ -7899,17 +8886,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" - }, - "dependencies": { - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", - "dev": true - } } }, "define-property": { @@ -7967,14 +8945,6 @@ "pify": "^2.0.0", "pinkie-promise": "^2.0.0", "rimraf": "^2.2.8" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "delayed-stream": { @@ -8004,6 +8974,11 @@ "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=" }, + "deprecated-decorator": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz", + "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=" + }, "des.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", @@ -8090,11 +9065,19 @@ "wrappy": "1" } }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + } + }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", + "integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=" }, "diffie-hellman": { "version": "5.0.3", @@ -8174,20 +9157,12 @@ } }, "dom-converter": { - "version": "0.1.4", - "resolved": "http://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", - "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", "dev": true, "requires": { - "utila": "~0.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } + "utila": "~0.4" } }, "dom-helpers": { @@ -8200,7 +9175,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, "requires": { "domelementtype": "~1.1.1", "entities": "~1.1.1" @@ -8209,8 +9183,7 @@ "domelementtype": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" } } }, @@ -8228,8 +9201,7 @@ "domelementtype": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" }, "domexception": { "version": "1.0.1", @@ -8250,10 +9222,9 @@ } }, "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "requires": { "dom-serializer": "0", "domelementtype": "1" @@ -8263,7 +9234,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, "requires": { "is-obj": "^1.0.0" } @@ -8271,8 +9241,7 @@ "dotenv": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.0.0.tgz", - "integrity": "sha512-FlWbnhgjtwD+uNLUGHbMykMOYQaTivdHEmYwAKFjn6GKe/CqY0fNae93ZHTd20snh9ZLr8mTzIL9m0APQ1pjQg==", - "dev": true + "integrity": "sha512-FlWbnhgjtwD+uNLUGHbMykMOYQaTivdHEmYwAKFjn6GKe/CqY0fNae93ZHTd20snh9ZLr8mTzIL9m0APQ1pjQg==" }, "dotenv-webpack": { "version": "1.5.7", @@ -8404,9 +9373,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.71", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.71.tgz", - "integrity": "sha512-VjZ6mQbbgF3GZ3eeQOMMgkdP8pWAHoW9UA+CNAVB4qSaOES4usB9RVIW764mYffdT2GOWF10Udt82RIZnTCTMg==" + "version": "1.3.75", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.75.tgz", + "integrity": "sha512-nLo03Qpw++8R6BxDZL/B1c8SQvUe/htdgc5LWYHe5YotV2jVvRUMP5AlOmxOsyeOzgMiXrNln2mC05Ixz6vuUQ==" }, "elliptic": { "version": "6.4.1", @@ -8490,13 +9459,23 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } } } }, "engine.io-client": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.6.tgz", - "integrity": "sha512-hnuHsFluXnsKOndS4Hv6SvUrgdYx1pk2NqfaDMW+GWdgfU3+/V25Cj7I8a0x92idSpa5PIhJRKxPvp9mnoLsfg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", @@ -8523,6 +9502,16 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } } } }, @@ -8547,25 +9536,17 @@ "memory-fs": "^0.4.0", "object-assign": "^4.0.1", "tapable": "^0.2.7" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - } } }, "entities": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", - "dev": true + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=" }, "enzyme": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.6.0.tgz", - "integrity": "sha512-onsINzVLGqKIapTVfWkkw6bYvm1o4CyJ9s8POExtQhAkVa4qFDW6DGCQGRy/5bfZYk+gmUbMNyayXiWDzTkHFQ==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.7.0.tgz", + "integrity": "sha512-QLWx+krGK6iDNyR1KlH5YPZqxZCQaVF6ike1eDJAOg0HvSkSCVImPsdWaNw6v+VrnK92Kg8jIOYhuOSS9sBpyg==", "dev": true, "requires": { "array.prototype.flat": "^1.2.1", @@ -8598,9 +9579,9 @@ } }, "enzyme-adapter-react-16": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.5.0.tgz", - "integrity": "sha512-R2LcVvMB2UwPH763d5jDtVedAIcEj+uZjOnq0nd1sOUs6z8TDbyHDvt8VwfrS4wMt7CawoyPmH0XzC8MtEqqDw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.6.0.tgz", + "integrity": "sha512-ay9eGFpChyUDnjTFMMJHzrb681LF3hPWJLEA7RoLFG9jSWAdAm2V50pGmFV9dYGJgh5HfdiqM+MNvle41Yf/PA==", "dev": true, "requires": { "enzyme-adapter-utils": "^1.8.0", @@ -8608,14 +9589,14 @@ "object.assign": "^4.1.0", "object.values": "^1.0.4", "prop-types": "^15.6.2", - "react-is": "^16.4.2", + "react-is": "^16.5.2", "react-test-renderer": "^16.0.0-0" } }, "enzyme-adapter-utils": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.8.0.tgz", - "integrity": "sha512-K9U2RGr1pvWPGEAIRQRVH4UdlqzpfLsKonuHyAK6lxu46yfGsMDVlO3+YvQwQpVjVw8eviEVIOmlFAnMbIhv/w==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.8.1.tgz", + "integrity": "sha512-s3QB3xQAowaDS2sHhmEqrT13GJC4+n5bG015ZkLv60n9k5vhxxHTQRIneZmQ4hmdCZEBrvUJ89PG6fRI5OEeuQ==", "dev": true, "requires": { "function.prototype.name": "^1.1.0", @@ -8659,7 +9640,6 @@ "version": "1.12.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", - "dev": true, "requires": { "es-to-primitive": "^1.1.1", "function-bind": "^1.1.1", @@ -8677,7 +9657,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -8803,6 +9782,12 @@ "private": "~0.1.5", "source-map": "~0.5.0" } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -8846,13 +9831,6 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true } } }, @@ -8869,16 +9847,16 @@ } }, "eslint": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.6.0.tgz", - "integrity": "sha512-/eVYs9VVVboX286mBK7bbKnO1yamUy2UCRjiY6MryhQL2PaaXCExsCQ2aO83OeYRhU2eCU/FMFP+tVMoOrzNrA==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.6.1.tgz", + "integrity": "sha512-hgrDtGWz368b7Wqf+v1Z69O3ZebNR0+GA7PtDdbmuz4rInFVUV9uw7whjZEiWyLzCjVb5Rs5WRN1TAS6eo7AYA==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "ajv": "^6.5.3", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", - "debug": "^3.1.0", + "debug": "^4.0.1", "doctrine": "^2.1.0", "eslint-scope": "^4.0.0", "eslint-utils": "^1.3.1", @@ -8953,6 +9931,15 @@ "which": "^1.2.9" } }, + "debug": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.0.1.tgz", + "integrity": "sha512-K23FHJ/Mt404FSlp6gSZCevIbTMLX0j3fmHhUEhQ3Wq0FMODW3+cUSoLdy1Gx4polAf4t/lphhmHH35BB8cLYw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, "eslint-scope": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", @@ -8963,12 +9950,6 @@ "estraverse": "^4.1.1" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, "external-editor": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", @@ -8981,9 +9962,9 @@ } }, "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", "dev": true }, "iconv-lite": { @@ -9016,20 +9997,13 @@ "through": "^2.3.6" } }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "rxjs": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "tslib": "^1.9.0" } } } @@ -9053,10 +10027,20 @@ "regexpp": "^2.0.0" } }, + "eslint-plugin-graphql": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-graphql/-/eslint-plugin-graphql-2.1.1.tgz", + "integrity": "sha512-JT2paUyu3e9ZDnroSshwUMc6pKcnkfXTsZInX1+/rPotvqOLVLtdrx/cmfb7PTJwjiEAshwcpm3/XPdTpsKJPw==", + "dev": true, + "requires": { + "graphql-config": "^2.0.1", + "lodash": "^4.11.1" + } + }, "eslint-plugin-jsx-a11y": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.1.1.tgz", - "integrity": "sha512-JsxNKqa3TwmPypeXNnI75FntkUktGzI1wSa1LgNZdSOMI+B4sxnr1lSF8m8lPiz4mKiC+14ysZQM4scewUrP7A==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.1.2.tgz", + "integrity": "sha512-7gSSmwb3A+fQwtw0arguwMdOdzmKUgnUcbSNlo+GjKLAQFuC2EZxWqG9XHRI8VscBJD5a8raz3RuxQNFW+XJbw==", "dev": true, "requires": { "aria-query": "^3.0.0", @@ -9081,6 +10065,17 @@ "minimatch": "^3.0.4", "resolve": "^1.8.1", "semver": "^5.5.0" + }, + "dependencies": { + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + } } }, "eslint-plugin-react": { @@ -9135,9 +10130,9 @@ } }, "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.0.1", @@ -9217,9 +10212,14 @@ }, "events": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, + "events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=" + }, "eventsource": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", @@ -9285,9 +10285,9 @@ } }, "get-stream": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.0.0.tgz", - "integrity": "sha512-FneLKMENeOR7wOK0/ZXCh+lwqtnPwkeunJjRN28LPqzGvNAhYvrTAhXv6xDm4vsJ0M7lcRbIYHQudKsSy2RtSQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { "pump": "^3.0.0" @@ -9608,9 +10608,9 @@ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.2.tgz", - "integrity": "sha512-TR6zxCKftDQnUAPvkrCWdBgDq/gbqx8A3ApnBrR5rMvpp6+KMJI0Igw7fkWPgeVK0uhRXTXdvO3O+YP0CaUX2g==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.3.tgz", + "integrity": "sha512-NiX+JXjnx43RzvVFwRWfPKo4U+1BrK5pJPsHQdKMlLoFHrrGktXglQhHliSihWAq+m1z6fHk3uwGHrtRbS9vLA==", "dev": true, "requires": { "@mrmlnc/readdir-enhanced": "^2.2.1", @@ -9619,29 +10619,6 @@ "is-glob": "^4.0.0", "merge2": "^1.2.1", "micromatch": "^3.1.10" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - } } }, "fast-json-stable-stringify": { @@ -9706,12 +10683,6 @@ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", "dev": true }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "ua-parser-js": { "version": "0.7.18", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz", @@ -9729,8 +10700,7 @@ "fclone": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", - "integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA=", - "dev": true + "integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA=" }, "feather-icons": { "version": "4.7.3", @@ -9765,19 +10735,11 @@ "requires": { "flat-cache": "^1.2.1", "object-assign": "^4.0.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "file-loader": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "dev": true, "requires": { @@ -9828,7 +10790,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "requires": { "debug": "2.6.9", @@ -9950,9 +10912,9 @@ } }, "flatmap-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.0.tgz", - "integrity": "sha512-Nlic4ZRYxikqnK5rj3YoxDVKGGtUjcNDUtvQ7XsdGLZmMwdUYnXf10o1zcXtzEZTBgc6GxeRpQxV/Wu3WPIIHA==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.1.tgz", + "integrity": "sha512-lAq4tLbm3sidmdCN8G3ExaxH7cUCtP5mgDvrYowsx84dcYkJJ4I28N7gkxA6+YlSXzaGLJYIDEi9WGfXzMiXdw==", "dev": true }, "flatten": { @@ -10025,6 +10987,14 @@ } } }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -10093,6 +11063,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "uuid": { + "version": "2.0.3", + "resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" } } }, @@ -10233,25 +11208,21 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "bundled": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "bundled": true }, "aproba": { "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "bundled": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "resolved": false, - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "bundled": true, "optional": true, "requires": { "delegates": "^1.0.0", @@ -10260,13 +11231,11 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "bundled": true }, "brace-expansion": { "version": "1.1.11", - "resolved": false, - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "bundled": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10274,35 +11243,29 @@ }, "chownr": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "bundled": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "bundled": true }, "concat-map": { "version": "0.0.1", - "resolved": false, - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "bundled": true }, "core-util-is": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "bundled": true, "optional": true }, "debug": { "version": "2.6.9", - "resolved": false, - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "bundled": true, "optional": true, "requires": { "ms": "2.0.0" @@ -10310,26 +11273,22 @@ }, "deep-extend": { "version": "0.5.1", - "resolved": false, - "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", + "bundled": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "bundled": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": false, - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "bundled": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": false, - "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "bundled": true, "optional": true, "requires": { "minipass": "^2.2.1" @@ -10337,14 +11296,12 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "bundled": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": false, - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "bundled": true, "optional": true, "requires": { "aproba": "^1.0.3", @@ -10359,8 +11316,7 @@ }, "glob": { "version": "7.1.2", - "resolved": false, - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "bundled": true, "optional": true, "requires": { "fs.realpath": "^1.0.0", @@ -10373,14 +11329,12 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": false, - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "bundled": true, "optional": true }, "iconv-lite": { "version": "0.4.21", - "resolved": false, - "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", + "bundled": true, "optional": true, "requires": { "safer-buffer": "^2.1.0" @@ -10388,8 +11342,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": false, - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "bundled": true, "optional": true, "requires": { "minimatch": "^3.0.4" @@ -10397,8 +11350,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": false, - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "bundled": true, "optional": true, "requires": { "once": "^1.3.0", @@ -10407,46 +11359,39 @@ }, "inherits": { "version": "2.0.3", - "resolved": false, - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "bundled": true }, "ini": { "version": "1.3.5", - "resolved": false, - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "bundled": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "bundled": true, "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "bundled": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": false, - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "bundled": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "resolved": false, - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "bundled": true }, "minipass": { "version": "2.2.4", - "resolved": false, - "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", + "bundled": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -10454,8 +11399,7 @@ }, "minizlib": { "version": "1.1.0", - "resolved": false, - "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", + "bundled": true, "optional": true, "requires": { "minipass": "^2.2.1" @@ -10463,22 +11407,19 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": false, - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "bundled": true, "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "bundled": true, "optional": true }, "needle": { "version": "2.2.0", - "resolved": false, - "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", + "bundled": true, "optional": true, "requires": { "debug": "^2.1.2", @@ -10488,8 +11429,7 @@ }, "node-pre-gyp": { "version": "0.10.0", - "resolved": false, - "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", + "bundled": true, "optional": true, "requires": { "detect-libc": "^1.0.2", @@ -10506,8 +11446,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": false, - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "bundled": true, "optional": true, "requires": { "abbrev": "1", @@ -10516,14 +11455,12 @@ }, "npm-bundled": { "version": "1.0.3", - "resolved": false, - "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", + "bundled": true, "optional": true }, "npm-packlist": { "version": "1.1.10", - "resolved": false, - "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", + "bundled": true, "optional": true, "requires": { "ignore-walk": "^3.0.1", @@ -10532,8 +11469,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": false, - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "bundled": true, "optional": true, "requires": { "are-we-there-yet": "~1.1.2", @@ -10544,39 +11480,33 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "bundled": true }, "object-assign": { "version": "4.1.1", - "resolved": false, - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "bundled": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": false, - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "bundled": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "bundled": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "bundled": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": false, - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "bundled": true, "optional": true, "requires": { "os-homedir": "^1.0.0", @@ -10585,20 +11515,17 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "bundled": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": false, - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "bundled": true, "optional": true }, "rc": { "version": "1.2.7", - "resolved": false, - "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", + "bundled": true, "optional": true, "requires": { "deep-extend": "^0.5.1", @@ -10609,16 +11536,14 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": false, - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "bundled": true, "optional": true } } }, "readable-stream": { "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "bundled": true, "optional": true, "requires": { "core-util-is": "~1.0.0", @@ -10632,8 +11557,7 @@ }, "rimraf": { "version": "2.6.2", - "resolved": false, - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "bundled": true, "optional": true, "requires": { "glob": "^7.0.5" @@ -10641,43 +11565,36 @@ }, "safe-buffer": { "version": "5.1.1", - "resolved": false, - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + "bundled": true }, "safer-buffer": { "version": "2.1.2", - "resolved": false, - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "bundled": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": false, - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "bundled": true, "optional": true }, "semver": { "version": "5.5.0", - "resolved": false, - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "bundled": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": false, - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "bundled": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": false, - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "bundled": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "bundled": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -10686,8 +11603,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "bundled": true, "optional": true, "requires": { "safe-buffer": "~5.1.0" @@ -10695,22 +11611,19 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": false, - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "bundled": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "resolved": false, - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "bundled": true, "optional": true }, "tar": { "version": "4.4.1", - "resolved": false, - "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", + "bundled": true, "optional": true, "requires": { "chownr": "^1.0.1", @@ -10724,14 +11637,12 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "bundled": true, "optional": true }, "wide-align": { "version": "1.1.2", - "resolved": false, - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "bundled": true, "optional": true, "requires": { "string-width": "^1.0.2" @@ -10739,13 +11650,11 @@ }, "wrappy": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "bundled": true }, "yallist": { "version": "3.0.2", - "resolved": false, - "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + "bundled": true } } }, @@ -10816,11 +11725,6 @@ "number-is-nan": "^1.0.0" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -10833,7 +11737,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -10938,7 +11842,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "get-value": { @@ -10977,9 +11881,9 @@ } }, "git-raw-commits": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-1.3.6.tgz", - "integrity": "sha512-svsK26tQ8vEKnMshTDatSIQSMDdz8CxIIqKsvPqbtV23Etmw6VNaFAitu8zwZ0VrOne7FztwPyRLxK7/DIUTQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", + "integrity": "sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==", "dev": true, "requires": { "dargs": "^4.0.1", @@ -11062,16 +11966,6 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -11183,9 +12077,9 @@ } }, "git-semver-tags": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-1.3.6.tgz", - "integrity": "sha512-2jHlJnln4D/ECk9FxGEBh3k44wgYdWjWDtMmJPaecjoRmxKo3Y1Lh8GMYuOPu04CHw86NTAODchYjC5pnpMQig==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.0.tgz", + "integrity": "sha512-lSgFc3zQTul31nFje2Q8XdNcTOI6B4I3mJRPCgFzHQQLfxfqdWTYzdtCaynkK5Xmb2wQlSJoKolhXJ1VhKROnQ==", "dev": true, "requires": { "meow": "^4.0.0", @@ -11259,16 +12153,6 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -11355,14 +12239,6 @@ "object-assign": "^4.1.1", "prop-types": "^15.5.10", "through": "^2.3.8" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "glamorous": { @@ -11403,6 +12279,14 @@ "is-glob": "^2.0.0" }, "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "requires": { + "is-glob": "^2.0.0" + } + }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", @@ -11419,24 +12303,20 @@ } }, "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "requires": { - "is-glob": "^2.0.0" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" }, "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "^2.1.0" } } } @@ -11583,14 +12463,6 @@ "object-assign": "^4.0.1", "pify": "^2.0.0", "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "globule": { @@ -11690,7 +12562,6 @@ "version": "0.13.2", "resolved": "http://registry.npmjs.org/graphql/-/graphql-0.13.2.tgz", "integrity": "sha512-QZ5BL8ZO/B20VA8APauGBg3GyEgZ19eduvpLWoq5x7gMmWnHoy8rlQWPLmWgFvo1yNgjSEFMesmS4R6pPr7xog==", - "dev": true, "requires": { "iterall": "^1.2.1" } @@ -11699,16 +12570,82 @@ "version": "4.1.19", "resolved": "https://registry.npmjs.org/graphql-anywhere/-/graphql-anywhere-4.1.19.tgz", "integrity": "sha512-mQlvbECzYPBcgBC9JsdM4v+DSvNmcIP+8Vwr+ij3gktbaLSE0Iza30mztuz40Jlf7ooMs+0emBZucNjLzqz7tA==", - "dev": true, "requires": { "apollo-utilities": "^1.0.21" } }, + "graphql-config": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-2.2.1.tgz", + "integrity": "sha512-U8+1IAhw9m6WkZRRcyj8ZarK96R6lQBQ0an4lp76Ps9FyhOXENC5YQOxOFGm5CxPrX2rD0g3Je4zG5xdNJjwzQ==", + "dev": true, + "requires": { + "graphql-import": "^0.7.1", + "graphql-request": "^1.5.0", + "js-yaml": "^3.10.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.4" + } + }, + "graphql-extensions": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.2.1.tgz", + "integrity": "sha512-/1FTPSWSffDjlRyMAV2UwQhojLmca9aQD0ieo1IYiqT5SE+uOWi4r83QF1CoER0sREIsH3s+nTmdH3cvQVG3MA==", + "requires": { + "apollo-server-env": "^2.0.3" + } + }, + "graphql-import": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/graphql-import/-/graphql-import-0.7.1.tgz", + "integrity": "sha512-YpwpaPjRUVlw2SN3OPljpWbVRWAhMAyfSba5U47qGMOSsPLi2gYeJtngGpymjm9nk57RFWEpjqwh4+dpYuFAPw==", + "dev": true, + "requires": { + "lodash": "^4.17.4", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "graphql-request": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-1.8.2.tgz", + "integrity": "sha512-dDX2M+VMsxXFCmUX0Vo0TopIZIX4ggzOtiCsThgtrKR4niiaagsGTDIHj3fsOMFETpa064vzovI+4YV4QnMbcg==", + "dev": true, + "requires": { + "cross-fetch": "2.2.2" + } + }, + "graphql-subscriptions": { + "version": "0.5.8", + "resolved": "http://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-0.5.8.tgz", + "integrity": "sha512-0CaZnXKBw2pwnIbvmVckby5Ge5e2ecmjofhYCdyeACbCly2j3WXDP/pl+s+Dqd2GQFC7y99NB+53jrt55CKxYQ==", + "requires": { + "iterall": "^1.2.1" + } + }, "graphql-tag": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.9.2.tgz", - "integrity": "sha512-qnNmof9pAqj/LUzs3lStP0Gw1qhdVCUS7Ab7+SUB6KD5aX1uqxWQRwMnOGTkhKuLvLNIs1TvNz+iS9kUGl1MhA==", - "dev": true + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.0.tgz", + "integrity": "sha512-9FD6cw976TLLf9WYIUPCaaTpniawIjHWZSwIRZSjrfufJamcXbVVYfN2TWvJYbw0Xf2JjYbl1/f2+wDnBVw3/w==" + }, + "graphql-tools": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-3.1.1.tgz", + "integrity": "sha512-yHvPkweUB0+Q/GWH5wIG60bpt8CTwBklCSzQdEHmRUgAdEQKxw+9B7zB3dG7wB3Ym7M7lfrS4Ej+jtDZfA2UXg==", + "requires": { + "apollo-link": "^1.2.2", + "apollo-utilities": "^1.0.1", + "deprecated-decorator": "^0.1.6", + "iterall": "^1.1.3", + "uuid": "^3.1.0" + } }, "growl": { "version": "1.10.5", @@ -11724,7 +12661,7 @@ }, "gulp": { "version": "3.9.1", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", + "resolved": "http://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", "requires": { "archy": "^1.0.0", @@ -11776,7 +12713,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -11798,32 +12735,100 @@ "postcss": "^5.2.12", "postcss-load-config": "^1.2.0", "vinyl-sourcemaps-apply": "^0.2.1" - } - }, - "gulp-sass": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-3.2.1.tgz", - "integrity": "sha512-UATbRpSDsyXCnpYSPBUEvdvtSEzksJs7/oQ0CujIpzKqKrO6vlnYwhX2UTsGrf4rNLwqlSSaM271It0uHYvJ3Q==", - "requires": { - "gulp-util": "^3.0", - "lodash.clonedeep": "^4.3.2", - "node-sass": "^4.8.3", - "through2": "^2.0.0", - "vinyl-sourcemaps-apply": "^0.2.0" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, - "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "gulp-sass": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-3.2.1.tgz", + "integrity": "sha512-UATbRpSDsyXCnpYSPBUEvdvtSEzksJs7/oQ0CujIpzKqKrO6vlnYwhX2UTsGrf4rNLwqlSSaM271It0uHYvJ3Q==", + "requires": { + "gulp-util": "^3.0", + "lodash.clonedeep": "^4.3.2", + "node-sass": "^4.8.3", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", @@ -11961,7 +12966,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -12010,47 +13015,11 @@ "version": "4.0.12", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz", "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==", - "dev": true, "requires": { "async": "^2.5.0", "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" - }, - "dependencies": { - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "dev": true, - "requires": { - "lodash": "^4.17.10" - } - }, - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true, - "optional": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "uglify-js": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", - "dev": true, - "optional": true, - "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1" - } - } } }, "har-schema": { @@ -12163,8 +13132,7 @@ "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" }, "has-unicode": { "version": "2.0.1", @@ -12224,6 +13192,11 @@ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" + }, "history": { "version": "4.7.2", "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", @@ -12252,6 +13225,22 @@ "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" }, + "hogan.js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", + "integrity": "sha1-TNnhq9QpQUbnZ55B14mHMrAse/0=", + "requires": { + "mkdirp": "0.3.0", + "nopt": "1.0.10" + }, + "dependencies": { + "mkdirp": { + "version": "0.3.0", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" + } + } + }, "hoist-non-react-statics": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", @@ -12351,6 +13340,16 @@ } } }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" + }, "html-comment-regex": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", @@ -12388,14 +13387,6 @@ "html-minifier": "^3.5.8", "loader-utils": "^1.1.0", "object-assign": "^4.1.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "html-minifier": { @@ -12411,30 +13402,6 @@ "param-case": "2.1.x", "relateurl": "0.2.x", "uglify-js": "3.4.x" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "uglify-js": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", - "dev": true, - "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1" - } - } } }, "html-select": { @@ -12469,6 +13436,11 @@ "through2": "~0.4.1" }, "dependencies": { + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, "readable-stream": { "version": "1.0.34", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -12530,12 +13502,6 @@ "json5": "^0.5.0", "object-assign": "^4.0.1" } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true } } }, @@ -12737,12 +13703,6 @@ "source-map": "^0.6.1", "supports-color": "^5.4.0" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -12792,6 +13752,11 @@ "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=" }, + "immutable-tuple": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/immutable-tuple/-/immutable-tuple-0.4.8.tgz", + "integrity": "sha512-1m29EVSrF+LJJAyVo1v12NsIalVKjyi4HNQVQDBx+LNCIuRXnfeMCHuLao5CyN1m3Sn0T63U5JEkmPArPCipQA==" + }, "import-cwd": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", @@ -12864,9 +13829,10 @@ } }, "informed": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/informed/-/informed-1.10.5.tgz", - "integrity": "sha512-3/lF958pr6JFc8GyAw4W+HWDYLLnF8DAjSAiPIyj6QZ4ayP/VYfaEYdM6wJwa5B8IbsQCv3eQCxaN5SeaXvbIw==", + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/informed/-/informed-1.10.6.tgz", + "integrity": "sha512-aJX9w+oIDrSDCvDYPwieshTJe6X1SFi/dpJCp5gbBHYN9u1lAdYU/Fw2dH4lVnTV4vZyUr2Ag4nIKWiN3lW21w==", + "dev": true, "requires": { "@babel/runtime-corejs2": "^7.0.0-rc.1" } @@ -13045,8 +14011,7 @@ "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" }, "is-ci": { "version": "1.2.1", @@ -13057,6 +14022,19 @@ "ci-info": "^1.5.0" } }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -13078,8 +14056,7 @@ "is-date-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" }, "is-descriptor": { "version": "0.1.6", @@ -13286,7 +14263,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, "requires": { "has": "^1.0.1" } @@ -13307,8 +14283,7 @@ "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" }, "is-retry-allowed": { "version": "1.1.0", @@ -13339,9 +14314,9 @@ "dev": true }, "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", "requires": { "html-comment-regex": "^1.1.0" } @@ -13350,7 +14325,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, "requires": { "has-symbols": "^1.0.0" } @@ -13464,17 +14438,6 @@ "js-yaml": "^3.7.0", "mkdirp": "^0.5.1", "once": "^1.4.0" - }, - "dependencies": { - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "dev": true, - "requires": { - "lodash": "^4.17.10" - } - } } }, "istanbul-lib-coverage": { @@ -13567,6 +14530,14 @@ "mkdirp": "^0.5.1", "rimraf": "^2.6.1", "source-map": "^0.5.3" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, "istanbul-reports": { @@ -13581,8 +14552,7 @@ "iterall": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", - "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==", - "dev": true + "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" }, "jekyll-tasks": { "version": "1.2.1", @@ -13617,14 +14587,136 @@ "json-stable-stringify": "^1.0.1" } }, - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "requires": { + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "requires": { + "q": "^1.1.2" + } + }, + "cssnano": { + "version": "3.10.0", + "resolved": "http://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", "requires": { - "lodash": "^4.17.10" + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "requires": { + "clap": "^1.0.9", + "source-map": "^0.5.3" } }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" + }, "fs-extra": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", @@ -13640,6 +14732,23 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "requires": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + } + }, "jsonfile": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", @@ -13659,12 +14768,241 @@ "object-assign": "^4.0.1" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } }, - "supports-color": { + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "http://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "requires": { + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "requires": { + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "requires": { + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "http://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "http://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "requires": { + "postcss": "^5.0.16" + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "requires": { + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" + } + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "http://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "requires": { + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "http://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "requires": { + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "http://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "http://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "requires": { + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "requires": { + "postcss": "^5.0.5" + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "http://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "http://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "http://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "requires": { + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" + } + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "http://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "http://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "requires": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", @@ -13672,6 +15010,43 @@ "has-flag": "^1.0.0" } }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "requires": { + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, "webpack": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-2.7.0.tgz", @@ -13699,6 +15074,16 @@ "webpack-sources": "^1.0.1", "yargs": "^6.0.0" } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" } } }, @@ -14060,6 +15445,14 @@ "diff": "^3.2.0", "jest-get-type": "^22.1.0", "pretty-format": "^23.6.0" + }, + "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + } } }, "jest-docblock": { @@ -14134,214 +15527,6 @@ "sane": "^2.0.0" }, "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } - }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", @@ -14368,15 +15553,6 @@ "repeat-element": "^1.1.2" } }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, "expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", @@ -14395,59 +15571,6 @@ "is-extglob": "^1.0.0" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, "is-extglob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", @@ -14492,233 +15615,6 @@ "parse-glob": "^3.0.4", "regex-cache": "^0.4.2" } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "sane": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz", - "integrity": "sha1-tNwYYcIbQn6SlQej51HiosuKs/o=", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "capture-exit": "^1.2.0", - "exec-sh": "^0.2.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.3", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5", - "watch": "~0.18.0" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } } } }, @@ -14743,11 +15639,12 @@ } }, "jest-junit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-5.1.0.tgz", - "integrity": "sha512-3EVf1puv2ox5wybQDfLX3AEn3IKOgDV4E76y4pO2hBu46DEtAFZZAm//X1pzPQpqKji0zqgMIzqzF/K+uGAX9A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-5.2.0.tgz", + "integrity": "sha512-Mdg0Qpdh1Xm/FA1B/mcLlmEmlr3XzH5pZg7MvcAwZhjHijPRd1z/UwYwkwNHmCV7o4ZOWCf77nLu7ZkhHHrtJg==", "dev": true, "requires": { + "jest-config": "^23.6.0", "jest-validate": "^23.0.1", "mkdirp": "^0.5.1", "strip-ansi": "^4.0.0", @@ -14941,12 +15838,6 @@ "throat": "^4.0.0" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "source-map-support": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", @@ -15231,12 +16122,6 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -15293,17 +16178,17 @@ "integrity": "sha512-xcinL3AuDJk7VSzsHgb9DvvIXayBbadtMZ4HFPx8rUszbW1MuNMlwYVC4zzCZ6e1sqZpnNS5ZFYOhXqA39T7LQ==" }, "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "requires": { "argparse": "^1.0.7", - "esprima": "^2.6.0" + "esprima": "^4.0.0" } }, "jsbn": { @@ -15351,15 +16236,6 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", "dev": true - }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } } } }, @@ -15470,8 +16346,7 @@ "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "json-schema": { "version": "0.2.3", @@ -15510,7 +16385,7 @@ }, "json5": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, "jsonfile": { @@ -15660,26 +16535,26 @@ "dev": true }, "lerna": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.4.0.tgz", - "integrity": "sha512-RCLm0gMi8PESEF8PzMxo35foA2NGGC/NKnKiUmJyRrhLybOIUfVPdPStSAWCjW1c+DYCgLZCbxu57/KWt4ZWZA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.4.1.tgz", + "integrity": "sha512-00X2mYuwJk/bvxdjJceUxTjUgUg7MIMWllo2zGfDVGPijLadrg8QCtJASZqVE7HDQbBLDxLGPjswk29HF5JS2Q==", "dev": true, "requires": { - "@lerna/add": "^3.3.2", - "@lerna/bootstrap": "^3.3.2", - "@lerna/changed": "^3.3.2", + "@lerna/add": "^3.4.1", + "@lerna/bootstrap": "^3.4.1", + "@lerna/changed": "^3.4.1", "@lerna/clean": "^3.3.2", "@lerna/cli": "^3.2.0", - "@lerna/create": "^3.3.1", + "@lerna/create": "^3.4.1", "@lerna/diff": "^3.3.0", "@lerna/exec": "^3.3.2", "@lerna/import": "^3.3.1", "@lerna/init": "^3.3.0", "@lerna/link": "^3.3.0", "@lerna/list": "^3.3.2", - "@lerna/publish": "^3.4.0", + "@lerna/publish": "^3.4.1", "@lerna/run": "^3.3.2", - "@lerna/version": "^3.3.2", + "@lerna/version": "^3.4.1", "import-local": "^1.0.0", "npmlog": "^4.1.2" } @@ -15728,9 +16603,9 @@ } }, "get-stream": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.0.0.tgz", - "integrity": "sha512-FneLKMENeOR7wOK0/ZXCh+lwqtnPwkeunJjRN28LPqzGvNAhYvrTAhXv6xDm4vsJ0M7lcRbIYHQudKsSy2RtSQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { "pump": "^3.0.0" @@ -15792,6 +16667,16 @@ "pify": "^2.0.0", "pinkie-promise": "^2.0.0", "strip-bom": "^2.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + } } }, "loader-runner": { @@ -15810,20 +16695,20 @@ } }, "localtunnel": { - "version": "1.9.0", - "resolved": "http://registry.npmjs.org/localtunnel/-/localtunnel-1.9.0.tgz", - "integrity": "sha512-wCIiIHJ8kKIcWkTQE3m1VRABvsH2ZuOkiOpZUofUCf6Q42v3VIZ+Q0YfX1Z4sYDRj0muiKL1bLvz1FeoxsPO0w==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-1.9.1.tgz", + "integrity": "sha512-HWrhOslklDvxgOGFLxi6fQVnvpl6XdX4sPscfqMZkzi3gtt9V7LKBWYvNUcpHSVvjwCQ6xzXacVvICNbNcyPnQ==", "requires": { "axios": "0.17.1", - "debug": "2.6.8", + "debug": "2.6.9", "openurl": "1.1.1", "yargs": "6.6.0" }, "dependencies": { "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } @@ -16013,6 +16898,12 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, + "lodash.flowright": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flowright/-/lodash.flowright-3.5.0.tgz", + "integrity": "sha1-K1//OZcW1+fcVyT+k0n2cGUYTWc=", + "dev": true + }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -16254,7 +17145,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -16316,6 +17207,11 @@ "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", "dev": true }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -16589,7 +17485,7 @@ }, "marked": { "version": "0.3.19", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", + "resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz", "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", "dev": true }, @@ -16609,14 +17505,27 @@ "integrity": "sha1-ivoLp7aRp6sSPn8S9l4yu10fgkM=" }, "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "requires": { "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } } }, + "mdn-data": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", + "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -16702,11 +17611,6 @@ "version": "1.2.0", "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" } } }, @@ -16970,6 +17874,11 @@ } } }, + "mitt": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.1.3.tgz", + "integrity": "sha512-mUDCnVNsAi+eD6qA0HkRkwYczbLHJ49z17BGe2PYRhZL4wpZUFZGJHU7/5tmvohoma+Hdn0Vh/oJTiPEmgSruA==" + }, "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", @@ -17014,6 +17923,33 @@ "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==", "dev": true }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -17137,12 +18073,6 @@ "lodash._bindcallback": "^3.0.0" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "strip-ansi": { "version": "3.0.1", "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -17160,6 +18090,11 @@ } } }, + "mustache": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz", + "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==" + }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", @@ -17167,9 +18102,9 @@ "dev": true }, "nan": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", - "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==" + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", + "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==" }, "nanomatch": { "version": "1.2.13", @@ -17279,8 +18214,7 @@ "node-fetch": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz", - "integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==", - "dev": true + "integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==" }, "node-fetch-npm": { "version": "2.0.2", @@ -17318,6 +18252,14 @@ "which": "1" }, "dependencies": { + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", @@ -17407,6 +18349,14 @@ "which": "^1.3.0" } }, + "node-releases": { + "version": "1.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.0.0-alpha.12.tgz", + "integrity": "sha512-VPB4rTPqpVyWKBHbSa4YPFme3+8WHsOSpvbp0Mfj0bWsC8TEjt4HQrLl1hsBDELlp1nB4lflSgSuGTYiuyaP7Q==", + "requires": { + "semver": "^5.3.0" + } + }, "node-sass": { "version": "4.9.3", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.3.tgz", @@ -17484,7 +18434,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -17513,60 +18463,6 @@ "touch": "^3.1.0", "undefsafe": "^2.0.2", "update-notifier": "^2.3.0" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - } } }, "nomnom": { @@ -17588,9 +18484,9 @@ } }, "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "requires": { "abbrev": "1" } @@ -17645,9 +18541,22 @@ "ansi-regex": "^0.2.0" } }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "object-assign": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", + "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=" + }, "strip-ansi": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", "requires": { "ansi-regex": "^0.2.1" @@ -17685,22 +18594,9 @@ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" }, "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - } - } + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" }, "npm-bundled": { "version": "1.0.5", @@ -17732,436 +18628,10 @@ } } }, - "npm-merge-driver": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/npm-merge-driver/-/npm-merge-driver-2.3.5.tgz", - "integrity": "sha512-MUxE26ZdDWAc+wlqwyOEIhRH1EdaIXCWSZbqAQ76dUkz3uSrxrLhfgQ3nb3oZbqC5e4NyLcCdzTSDVwkisoJpg==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1", - "yargs": "^10.0.3" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": false, - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "execa": { - "version": "0.7.0", - "resolved": false, - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "invert-kv": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "^1.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lru-cache": { - "version": "4.1.1", - "resolved": false, - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "mem": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "mimic-fn": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", - "dev": true - }, - "minimist": { - "version": "0.0.8", - "resolved": false, - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": false, - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": false, - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "os-locale": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", - "dev": true - }, - "p-locate": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "which": { - "version": "1.3.0", - "resolved": false, - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - }, - "y18n": { - "version": "3.2.1", - "resolved": false, - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": false, - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "10.0.3", - "resolved": false, - "integrity": "sha512-DqBpQ8NAUX4GyPP/ijDGHsJya4tYqLQrjPr95HNsr1YwL3+daCfvBwg7+gIC6IdJhR2kATh3hb61vjzMWEtjdw==", - "dev": true, - "requires": { - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^8.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": false, - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "string-width": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - } - } - }, - "yargs-parser": { - "version": "8.0.0", - "resolved": false, - "integrity": "sha1-IdR2Mw5agieaS4gTRb8GYQLiGcY=", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } - } - } - }, - "npm-package-arg": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", - "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", + "npm-package-arg": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", + "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", "dev": true, "requires": { "hosted-git-info": "^2.6.0", @@ -18254,16 +18724,6 @@ "strip-bom": "^3.0.0" } }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -18321,7 +18781,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", - "dev": true, "requires": { "boolbase": "~1.0.0" } @@ -18348,9 +18807,9 @@ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" }, "object-assign": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", - "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-component": { "version": "0.0.3", @@ -18388,8 +18847,7 @@ "object-inspect": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", - "dev": true + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" }, "object-is": { "version": "1.0.1", @@ -18398,14 +18856,14 @@ "dev": true }, "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" }, "object-path": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz", - "integrity": "sha1-D9mnT8X60a45aLWGvaXGMr1sBaU=" + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", + "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=" }, "object-visit": { "version": "1.0.1", @@ -18425,14 +18883,6 @@ "function-bind": "^1.1.1", "has-symbols": "^1.0.0", "object-keys": "^1.0.11" - }, - "dependencies": { - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", - "dev": true - } } }, "object.defaults": { @@ -18474,7 +18924,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, "requires": { "define-properties": "^1.1.2", "es-abstract": "^1.5.1" @@ -18520,7 +18969,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz", "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", - "dev": true, "requires": { "define-properties": "^1.1.2", "es-abstract": "^1.6.1", @@ -18545,8 +18993,7 @@ "on-headers": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", - "dev": true + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" }, "once": { "version": "1.4.0", @@ -18588,11 +19035,18 @@ "is-wsl": "^1.1.0" } }, + "optimism": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.6.6.tgz", + "integrity": "sha512-1Y6LY7pYbXP5y6yeXYfXhxJi9hsxYAZWpt7bBp4seAwfcYtaN7tq9wot/pdrhyI809/K4gDm3BcZcEkmwGevjg==", + "requires": { + "immutable-tuple": "^0.4.4" + } + }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" @@ -18709,14 +19163,6 @@ "graceful-fs": "^4.1.4", "mkdirp": "^0.5.1", "object-assign": "^4.1.0" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "p-defer": { @@ -18732,7 +19178,7 @@ }, "p-is-promise": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", "dev": true }, @@ -19060,7 +19506,7 @@ }, "parse-diff": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/parse-diff/-/parse-diff-0.4.2.tgz", + "resolved": "http://registry.npmjs.org/parse-diff/-/parse-diff-0.4.2.tgz", "integrity": "sha512-YYQzII66NqysdPgDVxzbdwNXMv5Ww562JSZSXZ4RIPoolzD7yqA4crgD8swrs+JNcvjoZMKMiT4kGcLYvf6IoA==", "dev": true }, @@ -19129,11 +19575,12 @@ } }, "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "requires": { - "error-ex": "^1.2.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } }, "parse-link-header": { @@ -19145,6 +19592,11 @@ "xtend": "~4.0.1" } }, + "parse-ms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", + "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=" + }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -19236,6 +19688,14 @@ "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" }, + "path-starts-with": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-starts-with/-/path-starts-with-1.0.0.tgz", + "integrity": "sha1-soJDAV6LE43lcmgqxS2kLmRq2E4=", + "requires": { + "normalize-path": "^2.1.1" + } + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -19325,6 +19785,11 @@ } } }, + "plur": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz", + "integrity": "sha1-24XGgU9eXlo7Se/CjWBP7GKXUVY=" + }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", @@ -19348,6 +19813,12 @@ "mkdirp": "0.5.x" }, "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -19372,6 +19843,13 @@ "requires": { "async": "1.5.2", "is-number-like": "^1.0.3" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } } }, "posix-character-classes": { @@ -19385,14 +19863,86 @@ "integrity": "sha1-D254Ck3/Ll9Z8gf5A6jV8EdshC4=" }, "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.5.tgz", + "integrity": "sha512-HBNpviAUFCKvEh7NZhw1e8MBPivRszIiUnhrJ+sBFVSYSqubrzwX3KG51mYgcRHX8j/cAgZJedONZcm5jTBdgQ==", "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.5.0" + } + }, + "postcss-calc": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-6.0.2.tgz", + "integrity": "sha512-fiznXjEN5T42Qm7qqMCVJXS3roaj9r4xsSi+meaBVe7CJBl8t/QLOXu02Z2E6oWAMWIvCuF6JrvzFekmVEbOKA==", + "requires": { + "css-unit-converter": "^1.1.1", + "postcss": "^7.0.2", + "postcss-selector-parser": "^2.2.2", + "reduce-css-calc": "^2.0.0" + } + }, + "postcss-colormin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.2.tgz", + "integrity": "sha512-1QJc2coIehnVFsz0otges8kQLsryi4lo19WD+U5xCWvXd0uw/Z+KKYnbiNDCnO9GP+PvErPHCG0jNvWTngk9Rw==", + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-discard-comments": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.1.tgz", + "integrity": "sha512-Ay+rZu1Sz6g8IdzRjUgG2NafSNpp2MSMOQUb+9kkzzzP+kh07fP0yNbhtFejURnyVXSX3FYy2nVNW1QTnNjgBQ==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "http://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "requires": { + "postcss": "^5.0.14", + "uniqs": "^2.0.0" }, "dependencies": { "ansi-regex": { @@ -19429,9 +19979,25 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -19447,82 +20013,80 @@ } } }, - "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", - "requires": { - "postcss": "^5.0.2", - "postcss-message-helpers": "^2.0.0", - "reduce-css-calc": "^1.2.6" - } - }, - "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", - "requires": { - "colormin": "^1.0.5", - "postcss": "^5.0.13", - "postcss-value-parser": "^3.2.3" - } - }, - "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", - "requires": { - "postcss": "^5.0.11", - "postcss-value-parser": "^3.1.2" - } - }, - "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", - "requires": { - "postcss": "^5.0.14" - } - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", - "requires": { - "postcss": "^5.0.14" - } - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", - "requires": { - "postcss": "^5.0.16" - } - }, - "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", - "requires": { - "postcss": "^5.0.14", - "uniqs": "^2.0.0" - } - }, "postcss-filter-plugins": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", "requires": { "postcss": "^5.0.4" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } } }, "postcss-flexbugs-fixes": { @@ -19544,12 +20108,6 @@ "source-map": "^0.6.1", "supports-color": "^5.4.0" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -19564,10 +20122,32 @@ "postcss-load-plugins": "^2.3.0" }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } } } }, @@ -19580,10 +20160,32 @@ "object-assign": "^4.1.0" }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } } } }, @@ -19596,10 +20198,32 @@ "object-assign": "^4.1.0" }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } } } }, @@ -19627,32 +20251,6 @@ "require-from-string": "^2.0.1" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "postcss": { "version": "6.0.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", @@ -19679,43 +20277,121 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, "postcss-merge-idents": { "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "resolved": "http://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", "requires": { "has": "^1.0.1", "postcss": "^5.0.10", "postcss-value-parser": "^3.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } } }, "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.6.tgz", + "integrity": "sha512-JavnI+V4IHWsaUAfOoKeMEiJQGXTraEy1nHM0ILlE6NIQPEZrJDAnPh3lNGZ5HAk2mSSrwp66JoGhvjp6SqShA==", "requires": { - "postcss": "^5.0.4" + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" } }, "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", - "requires": { - "browserslist": "^1.5.2", - "caniuse-api": "^1.5.2", - "postcss": "^5.0.4", - "postcss-selector-parser": "^2.2.2", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.2.tgz", + "integrity": "sha512-UiuXwCCJtQy9tAIxsnurfF0mrNHKc4NnNx6NxqmzNNjXpQwLSukUxELHTRF0Rg1pAmcoKLih8PwvZbiordchag==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } } }, "postcss-message-helpers": { @@ -19724,51 +20400,59 @@ "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=" }, "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", "requires": { - "object-assign": "^4.0.1", - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.2" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - } + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" } }, "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.1.tgz", + "integrity": "sha512-pySEW3E6Ly5mHm18rekbWiAjVi/Wj8KKt2vwSfVFAWdW6wOIekgqxKxLU7vJfb107o3FDNPkaYFCxGAJBFyogA==", "requires": { - "postcss": "^5.0.12", - "postcss-value-parser": "^3.3.0" + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" } }, "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", - "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.2", - "postcss-value-parser": "^3.0.2", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.1.tgz", + "integrity": "sha512-h4W0FEMEzBLxpxIVelRtMheskOKKp52ND6rJv+nBS33G1twu2tCyurYj/YtgU76+UDCvWeNs0hs8HFAWE2OUFg==", + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", "uniqs": "^2.0.0" } }, "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.1.tgz", + "integrity": "sha512-8+plQkomve3G+CodLCgbhAKrb5lekAnLYuL1d7Nz+/7RANpBEVdgBkPNwljfSKvZ9xkkZTZITd04KP+zeJTJqg==", "requires": { - "alphanum-sort": "^1.0.2", - "has": "^1.0.1", - "postcss": "^5.0.14", - "postcss-selector-parser": "^2.0.0" + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } } }, "postcss-modules-extract-imports": { @@ -19790,12 +20474,6 @@ "source-map": "^0.6.1", "supports-color": "^5.4.0" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -19819,12 +20497,6 @@ "source-map": "^0.6.1", "supports-color": "^5.4.0" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -19848,12 +20520,6 @@ "source-map": "^0.6.1", "supports-color": "^5.4.0" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -19877,68 +20543,206 @@ "source-map": "^0.6.1", "supports-color": "^5.4.0" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-normalize-display-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.1.tgz", + "integrity": "sha512-R5mC4vaDdvsrku96yXP7zak+O3Mm9Y8IslUobk7IMP+u/g+lXvcN4jngmHY5zeJnrQvE13dfAg5ViU05ZFDwdg==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-positions": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.1.tgz", + "integrity": "sha512-GNoOaLRBM0gvH+ZRb2vKCIujzz4aclli64MBwDuYGU2EY53LwiP7MxOZGE46UGtotrSnmarPPZ69l2S/uxdaWA==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.1.tgz", + "integrity": "sha512-fFHPGIjBUyUiswY2rd9rsFcC0t3oRta4wxE1h3lpwfQZwFeFjXFSiDtdJ7APCmHQOnUZnqYBADNRPKPwFAONgA==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-string": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.1.tgz", + "integrity": "sha512-IJoexFTkAvAq5UZVxWXAGE0yLoNN/012v7TQh5nDo6imZJl2Fwgbhy3J2qnIoaDBrtUP0H7JrXlX1jjn2YcvCQ==", + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.1.tgz", + "integrity": "sha512-1nOtk7ze36+63ONWD8RCaRDYsnzorrj+Q6fxkQV+mlY5+471Qx9kspqv0O/qQNMeApg8KNrRf496zHwJ3tBZ7w==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", "requires": { - "postcss": "^5.0.5" + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" } }, "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", "requires": { "is-absolute-url": "^2.0.0", - "normalize-url": "^1.4.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3" + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.1.tgz", + "integrity": "sha512-U8MBODMB2L+nStzOk6VvWWjZgi5kQNShCyjRhMT3s+W9Jw93yIjOnrEkKYD3Ul7ChWbEcjDWmXq0qOL9MIAnAw==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" } }, "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.1.tgz", + "integrity": "sha512-PeJiLgJWPzkVF8JuKSBcylaU+hDJ/TX3zqAMIjlghgn1JBi6QwQaDZoDIlqWRcCAI8SxKrt3FCPSRmOgKRB97Q==", "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.1" + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" } }, "postcss-reduce-idents": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "resolved": "http://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", "requires": { "postcss": "^5.0.4", "postcss-value-parser": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } } }, "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.2.tgz", + "integrity": "sha512-epUiC39NonKUKG+P3eAOKKZtm5OtAtQJL7Ye0CBN1f+UQTHzqotudp+hki7zxXm7tT0ZAKDMBj1uihpPjP25ug==", "requires": { - "postcss": "^5.0.4" + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" } }, "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.1.tgz", + "integrity": "sha512-sZVr3QlGs0pjh6JAIe6DzWvBaqYw05V1t3d9Tp+VnFRT5j+rsqoWsysh/iSD7YNsULjq9IAylCznIwVd5oU/zA==", "requires": { - "has": "^1.0.1", - "postcss": "^5.0.8", - "postcss-value-parser": "^3.0.1" + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" } }, "postcss-reporter": { @@ -19974,9 +20778,40 @@ "supports-color": "^2.0.0" } }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -20000,23 +20835,23 @@ } }, "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.1.tgz", + "integrity": "sha512-YD5uIk5NDRySy0hcI+ZJHwqemv2WiqqzDgtvgMzO8EGSkK5aONyX8HMVFRFJSdO8wUWTuisUFn/d7yRRbBr5Qw==", "requires": { - "is-svg": "^2.0.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3", - "svgo": "^0.7.0" + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" } }, "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.4", + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", "uniqs": "^2.0.0" } }, @@ -20027,12 +20862,80 @@ }, "postcss-zindex": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", "requires": { "has": "^1.0.1", "postcss": "^5.0.4", "uniqs": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } } }, "prelude-ls": { @@ -20113,6 +21016,16 @@ "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" }, + "pretty-ms": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz", + "integrity": "sha1-QlfCVt8/sLRR1q/6qwIYhBJpgdw=", + "requires": { + "is-finite": "^1.0.1", + "parse-ms": "^1.0.0", + "plur": "^1.0.0" + } + }, "prismjs": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.15.0.tgz", @@ -20172,6 +21085,14 @@ "requires": { "err-code": "^1.0.0", "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "dev": true + } } }, "promise.prototype.finally": { @@ -20208,18 +21129,9 @@ "version": "15.6.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", - "dev": true, "requires": { "loose-envify": "^1.3.1", "object-assign": "^4.1.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "propagate": { @@ -20234,6 +21146,26 @@ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, + "protobufjs": { + "version": "6.8.8", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", + "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "@types/node": "^10.1.0", + "long": "^4.0.0" + } + }, "protoduck": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.0.tgz", @@ -20286,15 +21218,23 @@ } }, "public-encrypt": { - "version": "4.0.2", - "resolved": "http://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", - "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } } }, "pump": { @@ -20343,9 +21283,6 @@ "jquery": "^3.3.1" } }, - "pwa-module": { - "version": "file:packages/pwa-module" - }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -20363,13 +21300,6 @@ "requires": { "object-assign": "^4.1.0", "strict-uri-encode": "^1.0.0" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - } } }, "querystring": { @@ -20546,26 +21476,19 @@ "object-assign": "^4.1.1", "prop-types": "^15.6.2", "schedule": "^0.5.0" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "react-apollo": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-apollo/-/react-apollo-2.2.1.tgz", - "integrity": "sha512-M1UY0o66e2rc4xhAtLKXWRx183yHW/ei1XAno8vWyIUQaEAg3rP5Sgw9nzl3wvpvU1lFjlsY50sbh2Ia1NB5+w==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/react-apollo/-/react-apollo-2.2.4.tgz", + "integrity": "sha512-haS5R30Qvteb65ZLfWomUZQh47VU4ld4Kof3zlqdbLOrYPt3/DdVZC8ZFPZSxd5zPeIJtZqpUfAxD1WHVoMPIA==", "dev": true, "requires": { "fbjs": "^1.0.0", "hoist-non-react-statics": "^3.0.0", "invariant": "^2.2.2", - "lodash": "^4.17.10", + "lodash.flowright": "^3.5.0", + "lodash.isequal": "^4.5.0", "prop-types": "^15.6.0" }, "dependencies": { @@ -20594,12 +21517,6 @@ "react-is": "^16.3.2" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "ua-parser-js": { "version": "0.7.18", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz", @@ -20676,7 +21593,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -20704,37 +21621,17 @@ "doctrine": "^2.0.0", "node-dir": "^0.1.10", "recast": "^0.15.0" - }, - "dependencies": { - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "dev": true, - "requires": { - "lodash": "^4.17.10" - } - } } }, "react-dom": { "version": "16.5.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.5.2.tgz", "integrity": "sha512-RC8LDw8feuZOHVgzEf7f+cxBr/DnKdqp56VU0lAs1f4UfKc4cU8wU4fTq/mgnvynLQo8OtlPC19NUFh/zjZPuA==", - "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", "schedule": "^0.5.0" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "react-error-overlay": { @@ -20932,14 +21829,6 @@ "prop-types": "^15.6.2", "react-is": "^16.5.2", "schedule": "^0.5.0" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "react-transition-group": { @@ -21126,20 +22015,6 @@ "esprima": "~4.0.0", "private": "~0.1.5", "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "rechoir": { @@ -21180,20 +22055,12 @@ } }, "reduce-css-calc": { - "version": "1.3.0", - "resolved": "http://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.5.tgz", + "integrity": "sha512-AybiBU03FKbjYzyvJvwkJZY6NLN+80Ufc2EqEs+41yQH+8wqBEslD6eGiS0oIeq5TNLA5PrhBeYHXWdn8gtW7A==", "requires": { - "balanced-match": "^0.4.2", - "math-expression-evaluator": "^1.2.14", - "reduce-function-call": "^1.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" - } + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" } }, "reduce-function-call": { @@ -21310,9 +22177,9 @@ } }, "regexpp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", - "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, "regexpu-core": { @@ -21376,16 +22243,16 @@ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" }, "renderkid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", - "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.2.tgz", + "integrity": "sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg==", "dev": true, "requires": { "css-select": "^1.1.0", - "dom-converter": "~0.1", + "dom-converter": "~0.2", "htmlparser2": "~3.3.0", "strip-ansi": "^3.0.0", - "utila": "~0.3" + "utila": "^0.4.0" }, "dependencies": { "ansi-regex": { @@ -21394,20 +22261,36 @@ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "^2.0.0" } - }, - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true } } }, @@ -21473,11 +22356,6 @@ "requires": { "punycode": "^1.4.1" } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" } } }, @@ -21540,9 +22418,9 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", "requires": { "path-parse": "^1.0.5" } @@ -21616,16 +22494,23 @@ "signal-exit": "^3.0.2" } }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "requires": { + "through": "~2.3.4" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", - "dev": true + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" }, "rfc6902": { "version": "2.4.0", @@ -21633,6 +22518,16 @@ "integrity": "sha512-Oof0+ZGIey7+U2kIU51Ao2YUjgkik6iFwyKNIRzNnl9DD/WnaxQnp21iUwBlkbqrRkxuE/DGPRroLzYjj/ngMA==", "dev": true }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -21747,12 +22642,18 @@ } }, "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dev": true, + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", "requires": { - "tslib": "^1.9.0" + "symbol-observable": "1.0.1" + }, + "dependencies": { + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" + } } }, "safe-buffer": { @@ -21773,6 +22674,31 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sane": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz", + "integrity": "sha1-tNwYYcIbQn6SlQej51HiosuKs/o=", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "capture-exit": "^1.2.0", + "exec-sh": "^0.2.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.3", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5", + "watch": "~0.18.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "sass-graph": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", @@ -21814,7 +22740,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -21859,17 +22785,8 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/schedule/-/schedule-0.5.0.tgz", "integrity": "sha512-HUcJicG5Ou8xfR//c2rPT0lPIRR09vVvN81T9fqfVgBmhERUbDEQoYKjpBxbueJnCPpSu2ujXzOnRQt6x9o/jw==", - "dev": true, "requires": { "object-assign": "^4.1.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "schema-utils": { @@ -22155,6 +23072,21 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, "sisteransi": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-0.1.1.tgz", @@ -22230,6 +23162,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -22318,64 +23255,10 @@ "ms": "2.0.0" } }, - "engine.io-client": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", - "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "socket.io-client": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", - "to-array": "0.1.4" - } - }, - "socket.io-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", - "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", - "requires": { - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "isarray": "2.0.1" - } } } }, @@ -22385,29 +23268,30 @@ "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" }, "socket.io-client": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", - "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", "requires": { "backo2": "1.0.2", "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "~2.6.4", - "engine.io-client": "~3.1.0", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", "has-cors": "1.1.0", "indexof": "0.0.1", "object-component": "0.0.3", "parseqs": "0.0.5", "parseuri": "0.0.5", - "socket.io-parser": "~3.1.1", + "socket.io-parser": "~3.2.0", "to-array": "0.1.4" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { "ms": "2.0.0" } @@ -22420,13 +23304,12 @@ } }, "socket.io-parser": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.3.tgz", - "integrity": "sha512-g0a2HPqLguqAczs3dMECuA1RgoGFPyvDqcbaDEdCWY9g59kdUAz3YRmaJBNKXflrHNwB7Q12Gkf/0CZXfdHR7g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "requires": { "component-emitter": "1.2.1", "debug": "~3.1.0", - "has-binary2": "~1.0.2", "isarray": "2.0.1" }, "dependencies": { @@ -22466,14 +23349,8 @@ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true + "websocket-driver": ">=0.5.1" + } } } }, @@ -22542,9 +23419,9 @@ "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==" }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { "version": "0.5.2", @@ -22564,6 +23441,13 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "requires": { "source-map": "^0.5.6" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } } }, "source-map-url": { @@ -22577,18 +23461,18 @@ "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==" }, "spdx-correct": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.1.tgz", - "integrity": "sha512-hxSPZbRZvSDuOvADntOElzJpenIR7wXJkuoUcUtS0erbgt2fgeaoPIYretfKpslMhfFDY4k0MZ2F5CUzhBsSvQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", + "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" }, "spdx-expression-parse": { "version": "3.0.0", @@ -22799,6 +23683,11 @@ "safe-buffer": "^5.1.1" } }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, "stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.1.tgz", @@ -23040,6 +23929,11 @@ "limiter": "^1.0.5" } }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -23114,7 +24008,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", - "dev": true, "requires": { "define-properties": "^1.1.2", "es-abstract": "^1.5.0", @@ -23201,13 +24094,66 @@ } }, "style-loader": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.0.tgz", - "integrity": "sha512-uCcN7XWHkqwGVt7skpInW6IGO1tG6ReyFQ1Cseh0VcN6VdcFQi62aG/2F3Y9ueA8x4IVlfaSUxpmQXQD9QrEuQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", "dev": true, "requires": { "loader-utils": "^1.1.0", - "schema-utils": "^0.4.5" + "schema-utils": "^1.0.0" + }, + "dependencies": { + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "stylehacks": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.1.tgz", + "integrity": "sha512-TK5zEPeD9NyC1uPIdjikzsgWxdQQN/ry1X3d1iOz1UkYDCmcr928gWD1KHgyC27F50UnE0xCTrBOO1l6KR8M4w==", + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "subscriptions-transport-ws": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.15.tgz", + "integrity": "sha512-f9eBfWdHsePQV67QIX+VRhf++dn1adyC/PZHP6XI5AfKnZ4n0FW+v5omxwdHVpd4xq2ZijaHEcmlQrhBY79ZWQ==", + "requires": { + "backo2": "^1.0.2", + "eventemitter3": "^3.1.0", + "iterall": "^1.2.1", + "symbol-observable": "^1.0.4", + "ws": "^5.2.0" } }, "superagent": { @@ -23232,13 +24178,257 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "supertest": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.3.0.tgz", + "integrity": "sha512-dMQSzYdaZRSANH5LL8kX3UpgK9G1LRh/jnggs/TI0W2Sz7rkMx9Y48uia3K9NgcaWEV28tYkBnXE4tiFC77ygQ==", + "dev": true, + "requires": { + "methods": "^1.1.2", + "superagent": "^3.8.3" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-hyperlinks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", + "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", + "dev": true, + "requires": { + "has-flag": "^2.0.0", + "supports-color": "^5.0.0" + }, + "dependencies": { + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + } + } + }, + "svg-tag-names": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/svg-tag-names/-/svg-tag-names-1.1.1.tgz", + "integrity": "sha1-lkGynvcQJe4JTHBD983efZn71Qo=", + "dev": true + }, + "svgo": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.1.1.tgz", + "integrity": "sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g==", + "requires": { + "coa": "~2.0.1", + "colors": "~1.1.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "~0.1.0", + "css-tree": "1.0.0-alpha.28", + "css-url-regex": "^1.1.0", + "csso": "^3.5.0", + "js-yaml": "^3.12.0", + "mkdirp": "~0.5.1", + "object.values": "^1.0.4", + "sax": "~1.2.4", + "stable": "~0.1.6", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "symbol-tree": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "dev": true + }, + "symbol.prototype.description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/symbol.prototype.description/-/symbol.prototype.description-1.0.0.tgz", + "integrity": "sha512-I9mrbZ5M96s7QeJDv95toF1svkUjeBybe8ydhY7foPaBmr0SPJMFupArmMkDrOKTTj0sJVr+nvQNxWLziQ7nDQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "table": { + "version": "4.0.3", + "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "dev": true, + "requires": { + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + }, + "dependencies": { + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + } + } + }, + "tap-diff": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tap-diff/-/tap-diff-0.1.1.tgz", + "integrity": "sha1-j78zM9hWQ/7qG/F1m5CCCwSjfd8=", + "requires": { + "chalk": "^1.1.1", + "diff": "^2.2.1", + "duplexer": "^0.1.1", + "figures": "^1.4.0", + "pretty-ms": "^2.1.0", + "tap-parser": "^1.2.2", + "through2": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + } + } + }, + "tap-parser": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-1.3.2.tgz", + "integrity": "sha1-EgxQiciMPIp5PvKIhn3jIeGPjCI=", + "requires": { + "events-to-array": "^1.0.1", + "inherits": "~2.0.1", + "js-yaml": "^3.2.7", + "readable-stream": "^2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -23253,124 +24443,112 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } } } }, - "supertest": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.3.0.tgz", - "integrity": "sha512-dMQSzYdaZRSANH5LL8kX3UpgK9G1LRh/jnggs/TI0W2Sz7rkMx9Y48uia3K9NgcaWEV28tYkBnXE4tiFC77ygQ==", - "dev": true, - "requires": { - "methods": "^1.1.2", - "superagent": "^3.8.3" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-hyperlinks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", - "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", - "dev": true, - "requires": { - "has-flag": "^2.0.0", - "supports-color": "^5.0.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - } - } - }, - "svg-tag-names": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/svg-tag-names/-/svg-tag-names-1.1.1.tgz", - "integrity": "sha1-lkGynvcQJe4JTHBD983efZn71Qo=", - "dev": true - }, - "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "tap-xunit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tap-xunit/-/tap-xunit-2.3.0.tgz", + "integrity": "sha512-YVsURNvn1wfVUWb5wjansxhfbfeo2hOBTUbVgZoaMO8lyZzpiSi9IiZTZ7JG56m6A49LeWjfJIx/SnAre41V/A==", "requires": { - "coa": "~1.0.1", - "colors": "~1.1.2", - "csso": "~2.3.1", - "js-yaml": "~3.7.0", - "mkdirp": "~0.5.1", - "sax": "~1.2.1", - "whet.extend": "~0.9.9" + "duplexer": "~0.1.1", + "minimist": "~1.2.0", + "tap-parser": "~1.2.2", + "through2": "~2.0.0", + "xmlbuilder": "~4.2.0", + "xtend": "~4.0.0" }, "dependencies": { - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "tap-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-1.2.2.tgz", + "integrity": "sha1-Xi9pcGEfB5x8+FfeHceqG0gN56U=", + "requires": { + "events-to-array": "^1.0.1", + "inherits": "~2.0.1", + "js-yaml": "^3.2.7", + "readable-stream": "^2" + } + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } } } }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, - "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", - "dev": true - }, - "symbol.prototype.description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/symbol.prototype.description/-/symbol.prototype.description-1.0.0.tgz", - "integrity": "sha512-I9mrbZ5M96s7QeJDv95toF1svkUjeBybe8ydhY7foPaBmr0SPJMFupArmMkDrOKTTj0sJVr+nvQNxWLziQ7nDQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } + "tapable": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", + "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" }, - "table": { - "version": "4.0.3", - "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", - "dev": true, - "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "tape": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-4.9.1.tgz", + "integrity": "sha512-6fKIXknLpoe/Jp4rzHKFPpJUHDHDqn8jus99IfPnHIjyz78HYlefTGD3b5EkbQzuLfaEvmfPK3IolLgq2xT3kw==", + "requires": { + "deep-equal": "~1.0.1", + "defined": "~1.0.0", + "for-each": "~0.3.3", + "function-bind": "~1.1.1", + "glob": "~7.1.2", + "has": "~1.0.3", + "inherits": "~2.0.3", + "minimist": "~1.2.0", + "object-inspect": "~1.6.0", + "resolve": "~1.7.1", + "resumer": "~0.0.0", + "string.prototype.trim": "~1.1.2", + "through": "~2.3.8" }, "dependencies": { - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } }, - "tapable": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", - "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" - }, "tar": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", @@ -23406,12 +24584,6 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true } } }, @@ -23540,12 +24712,6 @@ "parse-glob": "^3.0.4", "regex-cache": "^0.4.2" } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true } } }, @@ -23592,9 +24758,14 @@ "supports-color": "^2.0.0" } }, + "object-path": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz", + "integrity": "sha1-D9mnT8X60a45aLWGvaXGMr1sBaU=" + }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -23607,9 +24778,6 @@ } } }, - "theme-frontend-venia": { - "version": "file:packages/venia-concept" - }, "throat": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", @@ -23735,6 +24903,11 @@ "setimmediate": "^1.0.4" } }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + }, "tiny-emitter": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz", @@ -23830,17 +25003,6 @@ "dev": true, "requires": { "nopt": "~1.0.10" - }, - "dependencies": { - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "requires": { - "abbrev": "1" - } - } } }, "tough-cookie": { @@ -23868,6 +25030,11 @@ "punycode": "^2.1.0" } }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", @@ -23965,46 +25132,12 @@ "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==" }, "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" - }, - "yargs": { - "version": "3.10.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - } + "commander": "~2.17.1", + "source-map": "~0.6.1" } }, "uglify-to-browserify": { @@ -24035,12 +25168,6 @@ "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", "dev": true }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, "uglify-es": { "version": "3.3.9", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", @@ -24212,6 +25339,11 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -24404,13 +25536,6 @@ "is-object": "^1.0.1", "is-string": "^1.0.4", "object-assign": "^4.1.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - } } }, "use": { @@ -24449,7 +25574,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, "requires": { "define-properties": "^1.1.2", "object.getownpropertydescriptors": "^2.0.3" @@ -24467,9 +25591,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "v8flags": { "version": "2.1.1", @@ -24625,6 +25749,13 @@ "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", "requires": { "source-map": "^0.5.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } } }, "vm-browserify": { @@ -24700,56 +25831,6 @@ "chokidar": "^2.0.2", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "^2.1.0" - } - } - } - } } }, "wbuf": { @@ -24812,13 +25893,27 @@ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", "dev": true }, - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "requires": { - "lodash": "^4.17.10" + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" } }, "execa": { @@ -24851,6 +25946,15 @@ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -24874,6 +25978,15 @@ "mem": "^1.1.0" } }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, "path-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", @@ -24904,6 +26017,21 @@ "read-pkg": "^2.0.0" } }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -24919,6 +26047,31 @@ "has-flag": "^2.0.0" } }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, "uglifyjs-webpack-plugin": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", @@ -24936,6 +26089,18 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, "yargs": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", @@ -24955,6 +26120,38 @@ "which-module": "^2.0.0", "y18n": "^3.2.1", "yargs-parser": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + } } }, "yargs-parser": { @@ -24964,6 +26161,14 @@ "dev": true, "requires": { "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } } } } @@ -25030,16 +26235,6 @@ "yargs": "6.6.0" }, "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", @@ -25055,25 +26250,15 @@ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, - "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" } }, "del": { @@ -25116,27 +26301,6 @@ } } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", @@ -25168,66 +26332,15 @@ "is-glob": "^3.1.0", "lodash": "^4.17.2", "micromatch": "^2.3.11" - }, - "dependencies": { - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - } - } - } + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" } }, "kind-of": { @@ -25239,18 +26352,50 @@ "is-buffer": "^1.1.5" } }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -25285,9 +26430,9 @@ } }, "webpack-hot-middleware": { - "version": "2.24.2", - "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.24.2.tgz", - "integrity": "sha512-VsBGNMC5JDnab5hbReMjmIYtnvDMT+odLSP49EbLZHwuAoJJDNOi0YLhTe40vvP7u7Be+Ww1mYHjwwelqdnaKA==", + "version": "2.24.3", + "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.24.3.tgz", + "integrity": "sha512-pPlmcdoR2Fn6UhYjAhp1g/IJy1Yc9hD+T6O9mjRcWV2pFbBjIFoJXhP0CoD0xPOhWJuWXuZXGBga9ybbOdzXpg==", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -25304,7 +26449,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -25320,13 +26465,6 @@ "requires": { "source-list-map": "^2.0.0", "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "websocket-driver": { @@ -25346,18 +26484,18 @@ "dev": true }, "whatwg-encoding": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.4.tgz", - "integrity": "sha512-vM9KWN6MP2mIHZ86ytcyIv7e8Cj3KTfO2nd2c8PFDqcI4bxFmQp83ibq4wadq7rL9l9sZV6o9B0LTt8ygGAAXg==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", "dev": true, "requires": { - "iconv-lite": "0.4.23" + "iconv-lite": "0.4.24" }, "dependencies": { "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -25436,30 +26574,30 @@ "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" }, "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" }, "workbox-background-sync": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-3.6.1.tgz", - "integrity": "sha512-lMXwUbZ/FQBWf/jkFwz0ARfMLeIk9q73+fsqGwwUwUcr8NC6GDZlgtH9sPndxU1VbM+bieRM9R9kVStE4s2uVQ==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-3.6.2.tgz", + "integrity": "sha512-K34wiTM50gSpzJUuRmGRqbd91IpJj0vwMBSHCpixw/jiTg10uytSfnixMNGzeTK0i7LTd/bkA8ptx4HXP+MliA==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-broadcast-cache-update": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-broadcast-cache-update/-/workbox-broadcast-cache-update-3.6.1.tgz", - "integrity": "sha512-wC6LQNNPGLfZ1W+kSA2sP5YrbUl5axYgD0iNU37l4ABaCmjpu2XiFwPH+v4NOj/YWJt+z2563YB4SFM+/rC2mg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-broadcast-cache-update/-/workbox-broadcast-cache-update-3.6.2.tgz", + "integrity": "sha512-wmN3k94Kv3/lYOqRy08ymp8RyTPCpgLI9UW/BrQ1XuZHJyFejWnBoy/pCKk9mRZYZX7EyvnzA4O1PLILgLC43g==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-build": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-3.6.1.tgz", - "integrity": "sha512-1xEaFzVWzcIMwNHIW8WesGL+7MOx7XeHU1et1VWGDbxHV0i2HL8koMxq3g2WpKJy+yEh0hRG+4KuTGMmeuQcMA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-3.6.2.tgz", + "integrity": "sha512-PYw4SRbfbUE/+DDhb89zbspDLBi86hpra+l6SsX7yBqCthw4sHyH8IIQw5MMHI04HPV5ZDYru8A5SNLXVDGMcg==", "requires": { "babel-runtime": "^6.26.0", "common-tags": "^1.4.0", @@ -25470,113 +26608,114 @@ "pretty-bytes": "^4.0.2", "stringify-object": "^3.2.2", "strip-comments": "^1.0.2", - "workbox-background-sync": "^3.6.1", - "workbox-broadcast-cache-update": "^3.6.1", - "workbox-cache-expiration": "^3.6.1", - "workbox-cacheable-response": "^3.6.1", - "workbox-core": "^3.6.1", - "workbox-google-analytics": "^3.6.1", - "workbox-navigation-preload": "^3.6.1", - "workbox-precaching": "^3.6.1", - "workbox-range-requests": "^3.6.1", - "workbox-routing": "^3.6.1", - "workbox-strategies": "^3.6.1", - "workbox-streams": "^3.6.1", - "workbox-sw": "^3.6.1" + "workbox-background-sync": "^3.6.2", + "workbox-broadcast-cache-update": "^3.6.2", + "workbox-cache-expiration": "^3.6.2", + "workbox-cacheable-response": "^3.6.2", + "workbox-core": "^3.6.2", + "workbox-google-analytics": "^3.6.2", + "workbox-navigation-preload": "^3.6.2", + "workbox-precaching": "^3.6.2", + "workbox-range-requests": "^3.6.2", + "workbox-routing": "^3.6.2", + "workbox-strategies": "^3.6.2", + "workbox-streams": "^3.6.2", + "workbox-sw": "^3.6.2" } }, "workbox-cache-expiration": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-cache-expiration/-/workbox-cache-expiration-3.6.1.tgz", - "integrity": "sha512-mOV90D8cJ6S2s1GUGbfTvQ+WWKEMAvbHkH+vjIFdl+hXcToC7toctBI3UzI7vmmFv3TRQcP3TAxBmJgEFjgJew==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-cache-expiration/-/workbox-cache-expiration-3.6.2.tgz", + "integrity": "sha512-LJLYfqG7ItYucppun5I92fcN21kDZFEVqZ8uAOz5t8piOsHh1ThAiiLv/4ubG/d7CUgqW/1bmcX6DM4xqackzg==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-cacheable-response": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-3.6.1.tgz", - "integrity": "sha512-67tsFrpgX5LI3Nu82eVXlqfdq/PegF+agjcrLRDfSlD6cySCZCb0cyFHwLhegw5A6BayRHzYcBM5/imGeaMwjw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-3.6.2.tgz", + "integrity": "sha512-WvICMN3SfEi48C96KEfkLDIqnU0rkQeajdLjYXuzbUID3EX31gzUVlIbqQGrc+9xtIlvxs2+ZoaTR3Rjdtbh/Q==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-core": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-3.6.1.tgz", - "integrity": "sha512-xrrPQ335Cdekihe1jhscXYm4mmhh7ZXqSlZqJhyVmp9lNp5xzz1t0R22AfoaVNsg0zdu6Ouqnn0RsQ53QkZ8tw==" + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-3.6.2.tgz", + "integrity": "sha512-5T5WBFy5nMm7zx+P2RwdzEVu5CK++bqwiEsGF+INwUxsOKpH9oXUlUdJE/KfUaMsKcZtHXEb74mMB6vvE88a/w==" }, "workbox-google-analytics": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-3.6.1.tgz", - "integrity": "sha512-rnAT2RseiQO8DWjjkQkojVHX/6MK/L88+UFKr92xG1fKGD2asOk97AG6i0fAAti6f4KMg2s5UWZthiFR9QH4fQ==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-3.6.2.tgz", + "integrity": "sha512-NXBbo9xyHQvkHcvYoZkNJw7DB53dJUnmusKdSPg138A6HGt2ilycwTUuXNDWpkXXp3YHxcslrBMdptolwbzidg==", "requires": { - "workbox-background-sync": "^3.6.1", - "workbox-core": "^3.6.1", - "workbox-routing": "^3.6.1", - "workbox-strategies": "^3.6.1" + "workbox-background-sync": "^3.6.2", + "workbox-core": "^3.6.2", + "workbox-routing": "^3.6.2", + "workbox-strategies": "^3.6.2" } }, "workbox-navigation-preload": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-3.6.1.tgz", - "integrity": "sha512-Dzzp0JasGZWHSbZdx+UY0ZFaJu6P1LGIlFw3A+oTHbop9rZY219BNREcpzHT107SP7VFBkz/KIGF7s05FD6tpQ==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-3.6.2.tgz", + "integrity": "sha512-fN/CWSFZiySQH/OEJQsIizAM4ob6IgZVDfWvA58jAwiyI5QziqfFtL/EiHHNvmIa5jTdcoXfuNNv1WUdpRV18A==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-precaching": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-3.6.1.tgz", - "integrity": "sha512-qKNelNcGGSnKZveZRO+4sFKxr33iNMcP9Pbm8OkdEdAAZem0Ia4hIL87mHvwCvRnuj/vaV2BwRghZnzmLsK8Ng==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-3.6.2.tgz", + "integrity": "sha512-oQmBfvCzCUfLcwTokfbVhIIcyNS9aF692EhdqAz/SB2e40ehUgcctAUhQOezsedZFqBBnwphJQUhs+hD3mu72A==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-range-requests": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-3.6.1.tgz", - "integrity": "sha512-/ZPvMvKUzWBVmmN+fCPslF8RXin0C5WAla9EE0taiAMWdrVfPGghERtZLHumk/yM/M3k+OqWzPxCV0Wg/mjbqg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-3.6.2.tgz", + "integrity": "sha512-y1MFB97ydbT8PxBiihndLzG66sNIRzL0lkyoeaWPGfaPGWTP8ghMe4SkGqqdiY+E54rhd7lTdb7RZdv3Av1lTg==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-routing": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-3.6.1.tgz", - "integrity": "sha512-9LoBs5ck9Rjutno4yfBzqFyiUqj2UvILgYHGFkjHLp7lA4KK9VIuBJjGCLNaWr2r0pe6DVM5tM2OvlAOa2JO3w==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-3.6.2.tgz", + "integrity": "sha512-rhoH1AlETUfffJXJSlc0/T5rBB6vatxpD/8IZgxgHByBnYokV+/HxO7It6wBbxIzdO31UrWVroYm0iVa5sO7Jw==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-strategies": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-3.6.1.tgz", - "integrity": "sha512-OEw1psICNGpL1bqFZGyjl9N9hnmnRvsOA2EWAXxKVlwqmVvGzSeb8S46DPnH2JQgy77DRfNzeHVdfN6XirqdJw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-3.6.2.tgz", + "integrity": "sha512-4jAyL3n0Fl1BLB3QDUoUoBTzBsE8FwH0K7He1JvLzFiDtYp1ewcKjDecYCNZyTsFVgaLL7WClEQCOKSBquBfOg==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-streams": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-3.6.1.tgz", - "integrity": "sha512-SjO8wyUJ72+EXFVn9i9140hxbiN7d56k8gDHTxVoQT7OrCVYWkGFR0GzfnoRONaaHvO+/CGOyS37/Y+IXr7Vdw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-3.6.2.tgz", + "integrity": "sha512-lKTh5fOAf+Qae7GHYXZve40ZXULCf9kxlkrWjTXqGcTh6cxeibuWl6Mnt4aroChNB8jOEbHfGOy0iaG0R159ew==", "requires": { - "workbox-core": "^3.6.1" + "workbox-core": "^3.6.2" } }, "workbox-sw": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-3.6.1.tgz", - "integrity": "sha512-bXKvvjt5Oet87cpuSuVpiFYBohCv9xLmniZwMZPAFQVEtpPCyWv5X+UVZ+BpHVDBcRMYz7lXNIRWa9J54zR2vA==" + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-3.6.2.tgz", + "integrity": "sha512-EwQZaeGB+tEogABMj9FaEDuszaSBQgjAUEqTFiizZWSU8owZrt0BFfi69TMAhILOfWLFh3aASMzQnPMDY7id4w==" }, "workbox-webpack-plugin": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-3.6.1.tgz", - "integrity": "sha512-+rEBX+JxdNIoEtQLifn7+0YO6lhEWW7PGx9tuCesxcMACpbSJ/ihfKhmqK7BfvnvPv9OhhIyuPDLs3IROrPk0A==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-3.6.2.tgz", + "integrity": "sha512-FGSkcaiMDM41uTGkYf7O6hf2W7UvkNc+iUIltfGiRp+qeQfXKOOh5fJCz+a6AFkeuGELSSYROsQRuOqX8LytcQ==", "requires": { + "babel-runtime": "^6.26.0", "json-stable-stringify": "^1.0.1", - "workbox-build": "^3.6.1" + "workbox-build": "^3.6.2" } }, "worker-farm": { @@ -25622,7 +26761,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -25655,9 +26794,9 @@ } }, "write-file-webpack-plugin": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/write-file-webpack-plugin/-/write-file-webpack-plugin-4.4.0.tgz", - "integrity": "sha512-NcfhBlPbG8yb/3eUkYGuAyNj8P7W227HL9cXcGp0jUp+JnL3CgPlPtY30c0yl3CIbJEW7OfBU90bxHO2v9fQoA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/write-file-webpack-plugin/-/write-file-webpack-plugin-4.4.1.tgz", + "integrity": "sha512-PuYlu2TZqF/Rkdk3Ri6hiofz+6HQAVkH55VpJ75axQXU2m6cS5jvbgwcJe+vwbgJTLhiX0O7GAPMbgO5DMZEiA==", "requires": { "chalk": "^2.4.0", "debug": "^3.1.0", @@ -25727,13 +26866,11 @@ } }, "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" + "async-limiter": "~1.0.0" } }, "xdg-basedir": { @@ -25754,6 +26891,14 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xmlbuilder": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", + "requires": { + "lodash": "^4.0.0" + } + }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", @@ -25830,7 +26975,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -25840,7 +26985,7 @@ }, "yargs-parser": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "resolved": "http://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "requires": { "camelcase": "^3.0.0" @@ -25861,14 +27006,12 @@ "zen-observable": { "version": "0.8.9", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.9.tgz", - "integrity": "sha512-Y9kPzjGvIZ5jchSlqlCpBW3I82zBBL4z+ulXDRVA1NwsKzjt5kwAi+gOYIy0htNkfuehGZZtP5mRXHRV6TjDWw==", - "dev": true + "integrity": "sha512-Y9kPzjGvIZ5jchSlqlCpBW3I82zBBL4z+ulXDRVA1NwsKzjt5kwAi+gOYIy0htNkfuehGZZtP5mRXHRV6TjDWw==" }, "zen-observable-ts": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.10.tgz", "integrity": "sha512-5vqMtRggU/2GhePC9OU4sYEWOdvmayp2k3gjPf4F0mXwB3CSbbNznfDUvDJx9O2ZTa1EIXdJhPchQveFKwNXPQ==", - "dev": true, "requires": { "zen-observable": "^0.8.0" } diff --git a/package.json b/package.json index a217220d145..c80639d94e9 100644 --- a/package.json +++ b/package.json @@ -12,18 +12,21 @@ "node": ">=8.0.0" }, "scripts": { - "build": "lerna --stream --scope '{@magento/pwa-buildpack,@magento/peregrine,theme-frontend-venia}' exec -- npm run build", + "build": "lerna --stream --scope '{@magento/pwa-buildpack,@magento/peregrine,@magento/venia-concept}' exec -- npm run build", "clean:all": "lerna run clean && lerna clean --yes && rimraf ./node_modules", "clean:dist": "lerna run clean", "coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", "danger": "danger-ci", "lint": "eslint '@(packages|scripts)/**/*.js' --ignore-pattern node_modules --ignore-pattern storybook-dist", + "prepare": "node ./scripts/update_repo_environment.js", "prettier": "prettier --write '@(packages|scripts)/**/*.@(js|css)' '*.js'", "prettier:check": "prettier --list-different '@(packages|scripts)/**/*.@(js|css)' '*.js'", + "stage:venia": "cd packages/venia-concept && npm start; cd - >/dev/null", "test": "jest", "test:ci": "npm run -s test -- -i --json --outputFile=test-results.json", - "test:debug": "node --inspect-brk node_modules/.bin/jest -i --watch", + "test:debug": "node --inspect-brk node_modules/.bin/jest -i", "test:dev": "jest --watch", + "validate:venia:gql": "cd packages/venia-concept && npm run -s validate-queries; cd - >/dev/null", "watch:all": "node scripts/watch-all.js", "watch:buildpack": "cd packages/pwa-buildpack && npm run -s watch; cd - >/dev/null", "watch:peregrine": "cd packages/peregrine && npm run -s watch; cd - >/dev/null", @@ -42,6 +45,7 @@ "babel-eslint": "^8.2.3", "babel-loader": "^7.1.2", "babel-plugin-graphql-tag": "^1.6.0", + "babel-plugin-import-graphql": "^2.6.2", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-syntax-jsx": "^6.18.0", "babel-plugin-transform-class-properties": "^6.24.1", @@ -63,6 +67,7 @@ "enzyme-adapter-react-16": "^1.1.1", "eslint": "^5.2.0", "eslint-plugin-babel": "^5.1.0", + "eslint-plugin-graphql": "^2.1.1", "eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-node": "^7.0.1", "eslint-plugin-react": "^7.9.1", @@ -89,7 +94,6 @@ "nock": "^10.0.0", "node-fetch": "^2.2.0", "nodemon": "^1.18.4", - "npm-merge-driver": "^2.3.5", "npm-run-all": "^4.1.2", "prettier": "^1.13.5", "prettier-check": "^2.0.0", @@ -116,9 +120,8 @@ "dependencies": { "@magento/peregrine": "file:packages/peregrine", "@magento/pwa-buildpack": "file:packages/pwa-buildpack", - "informed": "^1.10.5", "pwa-devdocs": "file:packages/pwa-devdocs", - "pwa-module": "file:packages/pwa-module", - "theme-frontend-venia": "file:packages/venia-concept" + "@magento/upward-js": "file:packages/upward-js", + "@magento/upward-spec": "file:packages/upward-spec" } } diff --git a/packages/peregrine/CHANGELOG.md b/packages/peregrine/CHANGELOG.md new file mode 100644 index 00000000000..9eec12082cd --- /dev/null +++ b/packages/peregrine/CHANGELOG.md @@ -0,0 +1,13 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +# 0.5.0 (2018-08-13) + + +### Features + +* multicasting REST API client ([#164](https://github.com/magento-research/pwa-studio/issues/164)) ([2852e14](https://github.com/magento-research/pwa-studio/commit/2852e14)), closes [#140](https://github.com/magento-research/pwa-studio/issues/140) +* Simulators for async testing ([#24](https://github.com/magento-research/pwa-studio/issues/24)) ([a380da9](https://github.com/magento-research/pwa-studio/commit/a380da9)) diff --git a/packages/peregrine/README.md b/packages/peregrine/README.md index a9d1eb4d6a9..6e8d1935d3d 100644 --- a/packages/peregrine/README.md +++ b/packages/peregrine/README.md @@ -1,7 +1,7 @@ # Peregrine The Peregrine project is a collection of UI components for Magento PWA projects. -Use, extend, or remix these components to create a unique Magento PWA theme. +Use, extend, or remix these components to create a unique Magento PWA storefront. See [Peregrine documentation] diff --git a/packages/peregrine/package.json b/packages/peregrine/package.json index 44c0b9eab1e..7805589fbae 100644 --- a/packages/peregrine/package.json +++ b/packages/peregrine/package.json @@ -1,6 +1,6 @@ { "name": "@magento/peregrine", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc2.0.5", "publishConfig": { "access": "public" }, @@ -20,7 +20,6 @@ "build": "babel src --out-dir dist --ignore '__tests__/,__mocks__/,__fixtures__/' --source-maps --copy-files", "clean": "rimraf dist", "danger": "danger-ci", - "prepare": "npm-merge-driver install", "storybook": "start-storybook -p 9001 -c .storybook", "storybook:build": "build-storybook -c .storybook -o storybook-dist", "watch": "npm run -s build -- --watch" @@ -38,7 +37,6 @@ "babel-cli": "^6.26.0", "babel-runtime": "^6.26.0", "danger": "^3.9.0", - "npm-merge-driver": "^2.3.5", "react": "^16.4.2", "react-dom": "^16.4.2", "react-redux": "^5.0.7", diff --git a/packages/peregrine/src/List/__tests__/items.spec.js b/packages/peregrine/src/List/__tests__/items.spec.js index 785623ea82b..c63fb5cd638 100644 --- a/packages/peregrine/src/List/__tests__/items.spec.js +++ b/packages/peregrine/src/List/__tests__/items.spec.js @@ -10,7 +10,9 @@ const items = [ { id: '001', name: 'Test Product 1', - small_image: '/test/product/1.png', + small_image: { + path: '/test/product/1.png' + }, price: { regularPrice: { amount: { @@ -22,7 +24,9 @@ const items = [ { id: '002', name: 'Test Product 2', - small_image: '/test/product/2.png', + small_image: { + path: '/test/product/2.png' + }, price: { regularPrice: { amount: { diff --git a/packages/peregrine/src/List/__tests__/list.spec.js b/packages/peregrine/src/List/__tests__/list.spec.js index aadfc68e4e2..cb1833ca3d9 100644 --- a/packages/peregrine/src/List/__tests__/list.spec.js +++ b/packages/peregrine/src/List/__tests__/list.spec.js @@ -14,7 +14,9 @@ const items = [ { id: '001', name: 'Test Product 1', - small_image: '/test/product/1.png', + small_image: { + path: '/test/product/1.png' + }, price: { regularPrice: { amount: { @@ -26,7 +28,9 @@ const items = [ { id: '002', name: 'Test Product 2', - small_image: '/test/product/2.png', + small_image: { + path: '/test/product/2.png' + }, price: { regularPrice: { amount: { diff --git a/packages/peregrine/src/Router/resolveUnknownRoute.js b/packages/peregrine/src/Router/resolveUnknownRoute.js index 7957f81f7c4..65c0230d302 100644 --- a/packages/peregrine/src/Router/resolveUnknownRoute.js +++ b/packages/peregrine/src/Router/resolveUnknownRoute.js @@ -4,13 +4,11 @@ * @param {{ route: string, apiBase: string, __tmp_webpack_public_path__: string}} opts * @returns {Promise<{matched: boolean, rootChunkID: number | undefined, rootModuleID: number | undefined, id: number }>} */ +let preloadDone = false; export default function resolveUnknownRoute(opts) { const { route, apiBase, __tmp_webpack_public_path__ } = opts; - return remotelyResolveRoute({ - route, - apiBase - }).then(res => { + function handleResolverResponse(res) { if (!(res && res.type)) { return { matched: false }; } @@ -23,7 +21,34 @@ export default function resolveUnknownRoute(opts) { id: res.id, matched: true })); - }); + } + + if (!preloadDone) { + const preloaded = document.getElementById('url-resolver'); + if (preloaded) { + const rejectPreload = e => + console.error( + 'Unable to read preload!', + preloaded.textContent, + e + ); + try { + return handleResolverResponse( + JSON.parse(preloaded.textContent) + ).then(x => { + preloadDone = true; + return x; + }, rejectPreload); + } catch (e) { + rejectPreload(e); + } + } + } + + return remotelyResolveRoute({ + route, + apiBase + }).then(handleResolverResponse); } /** diff --git a/packages/pwa-buildpack/CHANGELOG.md b/packages/pwa-buildpack/CHANGELOG.md index 0515344a795..c3409869a60 100644 --- a/packages/pwa-buildpack/CHANGELOG.md +++ b/packages/pwa-buildpack/CHANGELOG.md @@ -1,6 +1,33 @@ # Change Log -All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +# 0.9.0 (2018-08-13) + + +### Bug Fixes + +* disable buggy origin sub by default ([#23](https://github.com/magento-research/pwa-studio/issues/23)) ([fde4036](https://github.com/magento-research/pwa-studio/commit/fde4036)) +* remove buggy sudo-prompt until fallback works ([#39](https://github.com/magento-research/pwa-studio/issues/39)) ([1a1da3e](https://github.com/magento-research/pwa-studio/commit/1a1da3e)), closes [#35](https://github.com/magento-research/pwa-studio/issues/35) +* update devcert dependency ([68c7cca](https://github.com/magento-research/pwa-studio/commit/68c7cca)) +* **dev:** MagentoRootComponentsPlugin dedupe fix ([bb8b152](https://github.com/magento-research/pwa-studio/commit/bb8b152)) +* **dev:** merge 'assets' and 'output' paths ([36d8157](https://github.com/magento-research/pwa-studio/commit/36d8157)) +* **DevProxy:** put proxy after other middlewares ([#33](https://github.com/magento-research/pwa-studio/issues/33)) ([7fa577c](https://github.com/magento-research/pwa-studio/commit/7fa577c)), closes [#32](https://github.com/magento-research/pwa-studio/issues/32) +* **util:** fix run-as-root await rm bug ([#28](https://github.com/magento-research/pwa-studio/issues/28)) ([e6f0754](https://github.com/magento-research/pwa-studio/commit/e6f0754)), closes [magento-research/venia-pwa-concept#51](https://github.com/magento-research/venia-pwa-concept/issues/51) + + +### Features + +* add protocol error detection to devproxy ([#22](https://github.com/magento-research/pwa-studio/issues/22)) ([7ff4aaa](https://github.com/magento-research/pwa-studio/commit/7ff4aaa)) +* **data:** Add live GraphQL data to product detail page ([#90](https://github.com/magento-research/pwa-studio/issues/90)) ([77b6bd6](https://github.com/magento-research/pwa-studio/commit/77b6bd6)), closes [#52](https://github.com/magento-research/pwa-studio/issues/52) [#87](https://github.com/magento-research/pwa-studio/issues/87) +* **dev:** app shell detects env from proxy header ([ac5b0b0](https://github.com/magento-research/pwa-studio/commit/ac5b0b0)) +* **dev:** log dev URL prominently after build ([#114](https://github.com/magento-research/pwa-studio/issues/114)) ([84fadde](https://github.com/magento-research/pwa-studio/commit/84fadde)), closes [#4](https://github.com/magento-research/pwa-studio/issues/4) + + + + ## [0.8.2](https://github.com/magento-research/pwa-buildpack/compare/v0.8.1...v0.8.2) (2018-05-26) diff --git a/packages/pwa-buildpack/README.md b/packages/pwa-buildpack/README.md index ef41a706a60..4cc32216ee7 100644 --- a/packages/pwa-buildpack/README.md +++ b/packages/pwa-buildpack/README.md @@ -422,18 +422,13 @@ or energy setting up their own services layer. ### Elements -- [`magento-layout-loader`](docs/magento-layout-loader.md) -- Gives Magento - modules/extensions the ability to inject or remove content blocks in a layout - without modifying theme source files -- [`MagentoRootComponentsPlugin`](docs/MagentoRootComponentsPlugin.md) -- - Divides static assets into bundled "chunks" based on components registered - with the Magento PWA `RootComponent` interface -- [`PWADevServer`](docs/PWADevServer.md) -- Autoconfigures local system and - theme configuration for local PWA-optimized theme development -- [`ServiceWorkerPlugin`](docs/ServiceWorkerPlugin.md) -- Creates - a ServiceWorker with different settings based on dev scenarios -- [`MagentoResolver`](docs/MagentoResolver.md) -- Configures Webpack to resolve - modules and assets in Magento PWA themes. +- [`PWADevServer`](docs/PWADevServer.md) -- Autoconfigures local system and theme configuration for local PWA-optimized theme development +- [`MagentoResolver`](docs/MagentoResolver.md) -- Configures Webpack to resolve modules and assets in PWA Studio projects. +- [`UpwardPlugin`](docs/UpwardPlugin.md) -- Attaches a hot reloading UPWARD server, powered by [upward-js](../upward-js), to the Webpack dev server +- [`ServiceWorkerPlugin`](docs/ServiceWorkerPlugin.md) -- Creates a ServiceWorker with different settings based on dev scenarios +- [`DevServerReadyNotifierPlugin`](docs/DevServerReadyNotifierPlugin.md) -- Displays a prominent link in the console to a running dev environment once it is launched +- [`MagentoRootComponentsPlugin`](docs/MagentoRootComponentsPlugin.md) -- Divides static assets into bundled "chunks" based on components registered with the Magento PWA `RootComponent` interface +- [`magento-layout-loader`](docs/magento-layout-loader.md) -- Gives Magento modules/extensions the ability to inject or remove content blocks in a layout without modifying theme source files ## Afterword diff --git a/packages/pwa-buildpack/docs/MagentoResolver.md b/packages/pwa-buildpack/docs/MagentoResolver.md index f74a2a16c2b..f1944e9c700 100644 --- a/packages/pwa-buildpack/docs/MagentoResolver.md +++ b/packages/pwa-buildpack/docs/MagentoResolver.md @@ -54,5 +54,5 @@ module.exports = async env => { #### `options` -- `paths: object`: **Required.** Local absolute paths to theme folders. -- `root`: Absolute path to the root directory of the theme. +- `paths: object`: **Required.** Local absolute paths to project folders. +- `root`: Absolute path to the root directory of the project. diff --git a/packages/pwa-buildpack/docs/MagentoRootComponentsPlugin.md b/packages/pwa-buildpack/docs/MagentoRootComponentsPlugin.md index 7fb363807a1..725c4e7be53 100644 --- a/packages/pwa-buildpack/docs/MagentoRootComponentsPlugin.md +++ b/packages/pwa-buildpack/docs/MagentoRootComponentsPlugin.md @@ -2,9 +2,9 @@ Automagically creates [unique chunks](https://webpack.js.org/guides/code-splitting/) for each Root Component -in a Magento PWA theme and extensions. +in a Magento PWA project and extensions. -Given a `RootComponents` directory in a theme with the following structure: +Given a `RootComponents` directory in a project with the following structure: ```sh ├── Page1 diff --git a/packages/pwa-buildpack/docs/PWADevServer.md b/packages/pwa-buildpack/docs/PWADevServer.md index cc34bf1561c..b726fa74bab 100644 --- a/packages/pwa-buildpack/docs/PWADevServer.md +++ b/packages/pwa-buildpack/docs/PWADevServer.md @@ -17,11 +17,11 @@ module.exports = async env => { /* webpack entry, output, rules, etc */ devServer: await PWADevServer.configure({ - publicPath: '/pub/static/frontend/Vendor/theme/en_US/', + publicPath: '/pub/static/frontend/Vendor/project/en_US/', backendDomain: 'https://magento2.localdomain', serviceWorkerFileName: 'sw.js', paths: { - output: path.resolve(__dirname, 'web/'), + output: path.resolve(__dirname, 'dist/'), }, id: 'magento-venia' }) @@ -56,7 +56,7 @@ they need to proxy backend requests to the backing store in a customized way. PWADevServer` handles all these needs: -- Creates and caches a custom local hostname for the current theme +- Creates and caches a custom local hostname for the current project - Adds the custom local hostname to `/etc/hosts` 🔐 - Creates and caches an SSL certificate for the custom local hostname - Adds the certificate to the OS-level keychain so browsers trust it 🔐 @@ -79,19 +79,17 @@ configuration. #### `options` -- `id: string`: **Required.** A unique ID for this project. Theme name is +- `id: string`: **Required.** A unique ID for this project. Project name is recommended, but you can use any domain-name-safe string. If you're - developing several copies of a theme simultaneously, you can use this ID to + developing several copies of a project simultaneously, you can use this ID to distinguish them in the internal tooling; for example, this id will be used to create your dev domain name. -- `publicPath: string`: **Required.** The public path of theme assets in the - backend server, e.g. `'/pub/static/frontend/Vendor/themename/en_US'`. -- `backendDomain: string`: **Required.** The URL of the backing store. -- `paths: object`: **Required.** Local absolute paths to theme folders. +- `publicPath: string`: **Required.** The public path of project assets in the + backend server, e.g. `'/'`: **Required.** The URL of the backing store. +- `paths: object`: **Required.** Local absolute paths to project folders. - `output`: Directory for built JavaScript files. - - `assets`: Directory for other public static assets. - `serviceWorkerFileName: string`: **Required.** The name of the ServiceWorker - file this theme creates, e.g. `'sw.js'`. + file this project generates, e.g. `'sw.js'`. - `changeOrigin: boolean`: ⚠️ **(experimental)** Try to parse any HTML responses from the proxied Magento backend, and replace its domain name with the dev server domain name. Default `false`. diff --git a/packages/pwa-buildpack/docs/ServiceWorkerPlugin.md b/packages/pwa-buildpack/docs/ServiceWorkerPlugin.md index 30b34b61e0a..dc0037cba0f 100644 --- a/packages/pwa-buildpack/docs/ServiceWorkerPlugin.md +++ b/packages/pwa-buildpack/docs/ServiceWorkerPlugin.md @@ -60,7 +60,7 @@ Plugin constructor for the `ServiceWorkerPlugin` class. Must be either `'development'` or `'production'`. `paths: Object` **(Required)** -The local absolute paths to theme folders. +The local absolute paths to project folders. - `paths.output: String` @@ -71,7 +71,7 @@ When `true`, hot reloading is enabled and the ServiceWorker is active in the doc When `false`, the ServiceWorker is disabled to prevent cache interruptions when hot reloading assets. `serviceWorkerFileName: String` **(Required)** -The name of the ServiceWorker file this theme creates. +The name of the ServiceWorker file this project creates. Example: `'sw.js'` `runtimeCacheAssetPath: String` diff --git a/packages/pwa-buildpack/package.json b/packages/pwa-buildpack/package.json index 1974c11ebe2..c7bd6d12087 100644 --- a/packages/pwa-buildpack/package.json +++ b/packages/pwa-buildpack/package.json @@ -1,15 +1,15 @@ { "name": "@magento/pwa-buildpack", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc2.0.5", "publishConfig": { "access": "public" }, "description": "Build/Layout optimization tooling and Peregrine framework adapters for the Magento PWA", "main": "dist/index.js", "scripts": { - "build": "babel src --out-dir dist --ignore '__tests__/,__mocks__/,__fixtures__/' --source-maps --copy-files", + "build": "npx babel src --out-dir dist --ignore '__tests__/,__mocks__/,__fixtures__/' --source-maps --copy-files", "clean": "rimraf dist", - "prepublishOnly": "rimraf dist && npm run build", + "prepublishOnly": "npx rimraf dist && npm run build", "watch": "npm run -s build -- --watch" }, "files": [ @@ -36,20 +36,27 @@ "@magento/devcert": "^0.7.0", "@magento/directive-parser": "^0.1.1", "ajv": "^6.1.1", + "apollo-boost": "^0.1.15", "babel-helper-module-imports": "^7.0.0-beta.3", "babel-plugin-syntax-dynamic-import": "^6.18.0", "boxen": "^1.3.0", "chalk": "^2.4.1", + "cssnano": "^4.1.0", "debug": "^3.1.0", + "debug-error-middleware": "^1.3.0", "express": "^4.16.2", "flat-file-db": "^1.0.0", + "graphql": "^14.0.2", + "graphql-tag": "^2.9.2", "harmon": "^1.3.2", "hostile": "^1.3.1", "http-proxy-middleware": "^0.18.0", "lodash.get": "^4.4.2", + "node-fetch": "^2.2.0", "one-time": "^0.0.4", "openport": "0.0.5", "post-compile-webpack-plugin": "^0.1.2", + "react-dom": "^16.5.0", "through": "^2.3.8", "webpack-sources": "^1.1.0", "workbox-webpack-plugin": "^3.0.0-beta.1", diff --git a/packages/pwa-buildpack/src/Utilities/__tests__/setupDomain.spec.js b/packages/pwa-buildpack/src/Utilities/__tests__/setupDomain.spec.js new file mode 100644 index 00000000000..7af0e10e73a --- /dev/null +++ b/packages/pwa-buildpack/src/Utilities/__tests__/setupDomain.spec.js @@ -0,0 +1,225 @@ +jest.mock('../../util/promisified/child_process'); +jest.mock('../../util/run-as-root'); +jest.mock('../../util/global-config'); +jest.mock('../../util/check-loopback'); + +const { exec } = require('../../util/promisified/child_process'); +const runAsRoot = require('../../util/run-as-root'); +const GlobalConfig = require('../../util/global-config'); +const checkLoopback = require('../../util/check-loopback'); + +const pkgLocTest = process.cwd() + '/package.json'; +const pkg = jest.fn(); +jest.doMock(pkgLocTest, pkg, { virtual: true }); +let setupDomain; + +const simulate = { + loopsBack() { + checkLoopback.mockImplementationOnce(domains => new Set(domains)); + return simulate; + }, + certExists(pair = { key: 'fake', cert: 'fake' }) { + setupDomain.userCerts.get.mockResolvedValueOnce(pair); + return simulate; + }, + certCreated(pair = { key: 'fake', cert: 'fake' }) { + runAsRoot.mockResolvedValueOnce(JSON.stringify([pair])); + return simulate; + }, + noPackageFound() { + jest.resetModuleRegistry(); + pkg.mockImplementationOnce(() => { + const error = new Error(process.cwd() + '/package.json not found'); + error.code = error.errno = 'ENOTFOUND'; + throw error; + }); + return simulate; + }, + packageNameIs(name) { + jest.resetModuleRegistry(); + pkg.mockImplementationOnce(() => ({ name })); + return simulate; + } +}; + +beforeAll(() => { + GlobalConfig.mockImplementation(({ key }) => ({ + set: jest.fn((...args) => { + const keyParts = args.slice(0, -1); + expect(typeof key(...keyParts)).toBe('string'); + }), + get: jest.fn(), + values: jest.fn(), + del: jest.fn() + })); + exec.mockResolvedValue(''); + checkLoopback.mockResolvedValue(new Set()); + ({ setupDomain } = require('../')); +}); +afterAll(() => GlobalConfig.mockRestore()); + +test('produces a secure domain and ssl cert from default name', async () => { + jest.spyOn(console, 'warn').mockImplementation(); + simulate.noPackageFound().certCreated({ + cert: 'fakeCert', + key: 'fakeKey' + }); + const { hostname, certPair } = await setupDomain(); + expect(hostname).toMatch( + new RegExp( + `${setupDomain.DEFAULT_NAME}\\-\\w{4,5}\\.${ + setupDomain.DEV_DOMAIN + }$` + ) + ); + expect(certPair).toMatchObject({ + cert: 'fakeCert', + key: 'fakeKey' + }); + expect(console.warn.mock.calls[0]).toMatchObject([ + expect.stringContaining('Could not autodetect'), + expect.any(Error) + ]); + expect(runAsRoot).toHaveBeenCalledTimes(1); + expect(runAsRoot).toHaveBeenCalledWith( + expect.stringMatching(/./), + expect.any(Function), + expect.arrayContaining([hostname]), + expect.arrayContaining([hostname]) + ); + console.warn.mockRestore(); +}); + +test('produces a secure domain with default name if package name is unusable', async () => { + jest.spyOn(console, 'warn').mockImplementation(); + simulate + .packageNameIs(undefined) + .loopsBack() + .certCreated(); + const { hostname } = await setupDomain(); + expect(hostname).toMatch( + new RegExp( + `${setupDomain.DEFAULT_NAME}\\-\\w{4,5}\\.${ + setupDomain.DEV_DOMAIN + }$` + ) + ); + + expect(console.warn.mock.calls[0]).toMatchObject([ + expect.stringContaining('Could not autodetect'), + expect.any(Error) + ]); + + console.warn.mockRestore(); +}); + +test('produces a secure domain from package name', async () => { + jest.spyOn(console, 'warn').mockImplementation(); + simulate + .packageNameIs('package-name-yay') + .loopsBack() + .certCreated(); + const { hostname } = await setupDomain(); + expect(hostname).toMatch( + new RegExp(`package-name-yay-\\w{4,5}\\.${setupDomain.DEV_DOMAIN}$`) + ); +}); + +test('produces a secure domain from custom name', async () => { + simulate + .packageNameIs('package-name-yay') + .loopsBack() + .certCreated(); + const { hostname } = await setupDomain('custom-912'); + expect(hostname).toMatch( + new RegExp(`custom-912-\\w{4,5}\\.${setupDomain.DEV_DOMAIN}$`) + ); +}); + +test('produces a secure domain without unique autogen', async () => { + simulate.loopsBack().certCreated(); + const { hostname } = await setupDomain('custom-823', { unique: false }); + expect(hostname).toMatch( + new RegExp(`custom-823\\.${setupDomain.DEV_DOMAIN}$`) + ); +}); + +test('produces an unsecure domain', async () => { + simulate.loopsBack(); + const { certPair } = await setupDomain('custom-823', { secure: false }); + expect(certPair).toBeFalsy(); + expect(setupDomain.userCerts.get).not.toHaveBeenCalled(); +}); + +test('returns an already-set-up secure domain', async () => { + simulate.loopsBack().certExists({ + key: 'no', + cert: 'diggity' + }); + const { certPair } = await setupDomain('custom-823', { unique: false }); + expect(certPair).toMatchObject({ + key: 'no', + cert: 'diggity' + }); + expect(runAsRoot).not.toHaveBeenCalled(); +}); + +test('refreshes an expired cert', async () => { + exec.mockRejectedValueOnce({ stdout: 'Certificate will expire' }); + simulate + .loopsBack() + .certExists({ + key: 'no', + cert: 'diggity' + }) + .certCreated({ + key: 'yes', + cert: 'diggity' + }); + const { certPair } = await setupDomain('custom-823', { unique: false }); + expect(certPair).toMatchObject({ + key: 'yes', + cert: 'diggity' + }); + expect(setupDomain.userCerts.set).toHaveBeenCalledWith( + `custom-823.${setupDomain.DEV_DOMAIN}`, + expect.objectContaining({ key: 'yes', cert: 'diggity' }) + ); +}); + +test('passes errors from denied root or bad parse', async () => { + runAsRoot.mockRejectedValueOnce(new Error('Denied!')); + await expect(setupDomain('blurg')).rejects.toThrowError('Denied!'); +}); + +test('does a dry run to indicate readiness', async () => { + simulate.loopsBack().certExists(); + expect(await setupDomain('woo', { dryRun: true })).toMatchObject({ + makeCerts: [], + setLoopbacks: [], + ready: true + }); + + simulate.loopsBack(); + expect(await setupDomain('woo', { dryRun: true })).toMatchObject({ + makeCerts: [expect.stringContaining('woo')], + setLoopbacks: [], + ready: false + }); + + simulate.certExists(); + expect(await setupDomain('woo', { dryRun: true })).toMatchObject({ + makeCerts: [], + setLoopbacks: [expect.stringContaining('woo')], + ready: false + }); + + simulate.loopsBack(); + expect( + await setupDomain('woo', { dryRun: true, secure: false }) + ).toMatchObject({ + makeCerts: [], + setLoopbacks: [], + ready: true + }); +}); diff --git a/packages/pwa-buildpack/src/Utilities/index.js b/packages/pwa-buildpack/src/Utilities/index.js new file mode 100644 index 00000000000..a4a018fb7d4 --- /dev/null +++ b/packages/pwa-buildpack/src/Utilities/index.js @@ -0,0 +1,3 @@ +module.exports = { + setupDomain: require('./setupDomain') +}; diff --git a/packages/pwa-buildpack/src/Utilities/setupDomain.js b/packages/pwa-buildpack/src/Utilities/setupDomain.js new file mode 100644 index 00000000000..bbaf596c92c --- /dev/null +++ b/packages/pwa-buildpack/src/Utilities/setupDomain.js @@ -0,0 +1,149 @@ +const debug = require('../util/debug').makeFileLogger(__filename); +const GlobalConfig = require('../util/global-config'); +const { exec } = require('../util/promisified/child_process'); +const { join } = require('path'); +const { createHash } = require('crypto'); +const checkLoopback = require('../util/check-loopback'); +const runAsRoot = require('../util/run-as-root'); + +const DEFAULT_NAME = 'my-pwa'; +const DEV_DOMAIN = 'local.pwadev'; + +const userCerts = new GlobalConfig({ + prefix: 'devcert', + key: x => x +}); + +function certIsExpired(cert) { + return exec(`openssl x509 -checkend 0 <<< "${cert}"`) + .then(() => false) + .catch(({ stdout }) => stdout.trim() === 'Certificate will expire'); +} + +async function getCert(commonName) { + let certPair = await userCerts.get(commonName); + if (certPair && (await certIsExpired(certPair.cert))) { + certPair = null; + await userCerts.del(commonName); + } + return certPair || null; +} + +function getUniqueSubdomain(customName) { + let name = DEFAULT_NAME; + if (typeof customName === 'string') { + name = customName; + } else { + const pkgLoc = join(process.cwd(), 'package.json'); + try { + // eslint-disable-next-line node/no-missing-require + const pkg = require(pkgLoc); + if (!pkg.name || typeof pkg.name !== 'string') { + throw new Error( + `package.json does not have a usable "name" field!` + ); + } + name = pkg.name; + } catch (e) { + console.warn( + debug.errorMsg( + `getUniqueSubdomain(): Using default "${name}" prefix. Could not autodetect project name from package.json: ` + ), + e + ); + } + } + const dirHash = createHash('md4'); + // Using a hash of the current directory is a natural way of preserving + // the same "unique" ID for each project, and changing it only when its + // location on disk has changed. + dirHash.update(process.cwd()); + const digest = dirHash.digest('base64'); + // Base64 truncated to 5 characters, stripped of special characters, + // and lowercased to be a valid domain, is about 36^5 unique values. + // There is therefore a chance of a duplicate ID and host collision, + // specifically a 1 in 60466176 chance. + return `${name}-${digest.slice(0, 5)}` + .toLowerCase() + .replace(/[^a-zA-Z0-9]/g, '-') + .replace(/^-+/, ''); +} + +/* istanbul ignore next: runs in sub-process */ +const sudoTask = async (setLoopbacks, makeCerts) => { + if (setLoopbacks.length > 0) { + var hostile = require('hostile'); + setLoopbacks.forEach(name => hostile.set('127.0.0.1', name)); + } + if (makeCerts.length > 0) { + const devcert = require('@magento/devcert'); + const certs = Promise.all(makeCerts.map(name => devcert(name))); + process.stdout.write(JSON.stringify(certs)); + } +}; + +const defaultOpts = { + secure: true, + unique: true, + dryRun: false +}; +async function setupDomain(customName, opts) { + const { secure, unique, dryRun } = Object.assign({}, defaultOpts, opts); + const setLoopbacks = []; + const makeCerts = []; + const subdomain = unique ? getUniqueSubdomain(customName) : customName; + const hostname = subdomain + '.' + DEV_DOMAIN; + const loopbacks = await checkLoopback([hostname]); + if (!loopbacks.has(hostname)) { + setLoopbacks.push(hostname); + } + let certPair; + if (secure) { + certPair = await getCert(hostname); + if (!certPair) { + makeCerts.push(hostname); + } + } + + const ready = setLoopbacks.length === 0 && makeCerts.length === 0; + if (dryRun) { + return { + setLoopbacks, + makeCerts, + ready + }; + } + if (!ready) { + try { + const output = await runAsRoot( + 'Creating a local development domain requires temporary administrative privileges.\n Enter password for %u on %H: ', + sudoTask, + setLoopbacks, + makeCerts + ); + if (secure && !certPair) { + certPair = JSON.parse(output)[0]; + await userCerts.set(hostname, certPair); + } + } catch (e) { + throw Error( + debug.errorMsg( + `Error setting up development domain: ${e.message} ${ + e.stack + }` + ) + ); + } + } + return { + hostname, + certPair + }; +} + +setupDomain.userCerts = userCerts; +setupDomain.getUniqueSubdomain = getUniqueSubdomain; +setupDomain.DEFAULT_NAME = DEFAULT_NAME; +setupDomain.DEV_DOMAIN = DEV_DOMAIN; + +module.exports = setupDomain; diff --git a/packages/pwa-buildpack/src/WebpackTools/PWADevServer.js b/packages/pwa-buildpack/src/WebpackTools/PWADevServer.js index e2f252d3f13..3487fca18da 100644 --- a/packages/pwa-buildpack/src/WebpackTools/PWADevServer.js +++ b/packages/pwa-buildpack/src/WebpackTools/PWADevServer.js @@ -1,68 +1,26 @@ const debug = require('../util/debug').makeFileLogger(__filename); -const { join } = require('path'); -const { createHash } = require('crypto'); +const debugErrorMiddleware = require('debug-error-middleware').express; const url = require('url'); -const express = require('express'); const GlobalConfig = require('../util/global-config'); -const SSLCertStore = require('../util/ssl-cert-store'); const optionsValidator = require('../util/options-validator'); -const middlewares = { - originSubstitution: require('./middlewares/OriginSubstitution'), - devProxy: require('./middlewares/DevProxy'), - staticRootRoute: require('./middlewares/StaticRootRoute') -}; -const { lookup } = require('../util/promisified/dns'); +const setupDomain = require('../Utilities/setupDomain'); const { find: findPort } = require('../util/promisified/openport'); -const runAsRoot = require('../util/run-as-root'); const PWADevServer = { - DEFAULT_NAME: 'my-pwa', - DEV_DOMAIN: 'local.pwadev', validateConfig: optionsValidator('PWADevServer', { publicPath: 'string', backendDomain: 'string', - 'paths.output': 'string', - serviceWorkerFileName: 'string' + 'paths.output': 'string' }), portsByHostname: new GlobalConfig({ prefix: 'devport-byhostname', key: x => x }), - async setLoopback(hostname) { - debug(`checking if ${hostname} is loopback`); - let ip; - try { - ip = await lookup(hostname); - } catch (e) { - if (e.code !== 'ENOTFOUND') { - throw Error( - debug.errorMsg( - `Error trying to check that ${hostname} is loopback: ${ - e.message - }` - ) - ); - } - } - if (ip && (ip.address === '127.0.0.1' || ip.address === '::1')) { - debug(`${hostname} already resolves to ${ip.address}!`); - } else { - debug( - `setting ${hostname} loopback in /etc/hosts, may require password...` - ); - return runAsRoot( - `Resolving ${hostname} to localhost and editing the hostfile requires temporary administrative privileges.\n Enter password for %u on %H: `, - /* istanbul ignore next: never runs in process */ - d => require('hostile').set('127.0.0.1', d), - hostname - ); - } - }, async findFreePort() { const reserved = await PWADevServer.portsByHostname.values(Number); debug(`findFreePort(): these ports already reserved`, reserved); return findPort({ startingPort: 8000, - endingPort: 9999, + endingPort: 8999, avoid: reserved }).catch(e => { throw Error( @@ -72,55 +30,7 @@ const PWADevServer = { ); }); }, - getUniqueSubdomain(customName) { - let name = PWADevServer.DEFAULT_NAME; - if (typeof customName === 'string') { - name = customName; - } else { - const pkgLoc = join(process.cwd(), 'package.json'); - try { - // eslint-disable-next-line node/no-missing-require - const pkg = require(pkgLoc); - if (!pkg.name || typeof pkg.name !== 'string') { - throw new Error( - `package.json does not have a usable "name" field!` - ); - } - name = pkg.name; - } catch (e) { - console.warn( - debug.errorMsg( - `getUniqueSubdomain(): Using default "${name}" prefix. Could not autodetect theme name from package.json: ` - ), - e - ); - } - } - const dirHash = createHash('md4'); - // Using a hash of the current directory is a natural way of preserving - // the same "unique" ID for each project, and changing it only when its - // location on disk has changed. - dirHash.update(process.cwd()); - const digest = dirHash.digest('base64'); - // Base64 truncated to 5 characters, stripped of special characters, - // and lowercased to be a valid domain, is about 36^5 unique values. - // There is therefore a chance of a duplicate ID and host collision, - // specifically a 1 in 60466176 chance. - return `${name}-${digest.slice(0, 5)}` - .toLowerCase() - .replace(/[^a-zA-Z0-9]/g, '-') - .replace(/^-+/, ''); - }, - async provideUniqueHost(prefix) { - debug(`provideUniqueHost ${prefix}`); - return PWADevServer.provideCustomHost( - PWADevServer.getUniqueSubdomain(prefix) - ); - }, - async provideCustomHost(subdomain) { - debug(`provideUniqueHost ${subdomain}`); - const hostname = subdomain + '.' + PWADevServer.DEV_DOMAIN; - + async getPersistentDevPort(hostname) { const [usualPort, freePort] = await Promise.all([ PWADevServer.portsByHostname.get(hostname), PWADevServer.findFreePort() @@ -137,13 +47,7 @@ const PWADevServer = { ); } - PWADevServer.setLoopback(hostname); - - return { - protocol: 'https:', - hostname, - port - }; + return port; }, async configure(config) { debug('configure() invoked', config); @@ -152,7 +56,7 @@ const PWADevServer = { contentBase: false, compress: true, hot: true, - host: 'localhost', + host: '0.0.0.0', stats: { all: false, builtAt: true, @@ -164,59 +68,44 @@ const PWADevServer = { version: true, warnings: true }, - before(app) { - if (config.changeOrigin) { - // replace origins in links in returned html - app.use( - middlewares.originSubstitution( - new url.URL(config.backendDomain), - { - hostname: devServerConfig.host, - port: devServerConfig.port - } - ) - ); - } - // serviceworker root route - app.use( - middlewares.staticRootRoute( - join(config.paths.output, config.serviceWorkerFileName) - ) - ); - }, after(app) { - // set static server to load and serve from different paths - app.use(config.publicPath, express.static(config.paths.output)); - - // proxy to backend - app.use( - middlewares.devProxy({ - target: config.backendDomain - }) - ); + app.use(debugErrorMiddleware()); } }; - let devHost; - if (config.id) { - devHost = await PWADevServer.provideCustomHost(config.id); - } else if (config.provideUniqueHost) { - devHost = await PWADevServer.provideUniqueHost( - config.provideUniqueHost + const { id, provideUniqueHost, provideSSLCert } = config; + let uniqueName; + if (id || provideUniqueHost) { + const domainCustomName = + id || + (typeof provideUniqueHost === 'string' + ? provideUniqueHost + : null); + const domainSetupConfig = { + secure: provideSSLCert, + unique: !id + }; + const { hostname, certPair } = await setupDomain( + domainCustomName, + domainSetupConfig ); - } - if (devHost) { - devServerConfig.host = devHost.hostname; - devServerConfig.port = devHost.port; + uniqueName = devServerConfig.host = hostname; + devServerConfig.https = certPair; + // workaround for https://github.com/webpack/webpack-dev-server/issues/1491 + if (devServerConfig.https) { + devServerConfig.https.spdy = { + protocols: ['http/1.1'] + }; + } } else { - devServerConfig.port = await PWADevServer.findFreePort(); - } - if (config.provideSSLCert) { - devServerConfig.https = await SSLCertStore.provide( - devServerConfig.host - ); + uniqueName = setupDomain.getUniqueSubdomain(); } + + devServerConfig.port = await PWADevServer.getPersistentDevPort( + uniqueName + ); + devServerConfig.publicPath = url.format({ - protocol: config.provideSSLCert ? 'https:' : 'http:', + protocol: provideSSLCert ? 'https:' : 'http:', hostname: devServerConfig.host, port: devServerConfig.port, pathname: config.publicPath diff --git a/packages/pwa-buildpack/src/WebpackTools/__tests__/PWADevServer.spec.js b/packages/pwa-buildpack/src/WebpackTools/__tests__/PWADevServer.spec.js index e9356cd5d44..f4f1c56f31f 100644 --- a/packages/pwa-buildpack/src/WebpackTools/__tests__/PWADevServer.spec.js +++ b/packages/pwa-buildpack/src/WebpackTools/__tests__/PWADevServer.spec.js @@ -1,34 +1,21 @@ jest.mock('../../util/promisified/dns'); jest.mock('../../util/promisified/openport'); jest.mock('../../util/global-config'); -jest.mock('../../util/ssl-cert-store'); jest.mock('../../util/run-as-root'); -jest.mock('../middlewares/DevProxy'); -jest.mock('../middlewares/OriginSubstitution'); -jest.mock('../middlewares/StaticRootRoute'); const { lookup } = require('../../util/promisified/dns'); const openport = require('../../util/promisified/openport'); -const runAsRoot = require('../../util/run-as-root'); const GlobalConfig = require('../../util/global-config'); -const SSLCertStore = require('../../util/ssl-cert-store'); -const middlewares = { - DevProxy: require('../middlewares/DevProxy'), - OriginSubstitution: require('../middlewares/OriginSubstitution'), - StaticRootRoute: require('../middlewares/StaticRootRoute') -}; -// Mocking a variable path requires the `.doMock` -const pkgLocTest = process.cwd() + '/package.json'; -const pkg = jest.fn(); -jest.doMock(pkgLocTest, pkg, { virtual: true }); let PWADevServer; +let setupDomain; beforeAll(() => { GlobalConfig.mockImplementation(({ key }) => ({ set: jest.fn(key), get: jest.fn(), values: jest.fn() })); + setupDomain = require('../../Utilities/setupDomain'); PWADevServer = require('../').PWADevServer; }); @@ -61,52 +48,10 @@ const simulate = { return simulate; }, certExistsForNextHostname(pair) { - SSLCertStore.provide.mockResolvedValueOnce(pair); - }, - noPackageFound() { - jest.resetModuleRegistry(); - pkg.mockImplementationOnce(() => { - const error = new Error(process.cwd() + '/package.json not found'); - error.code = error.errno = 'ENOTFOUND'; - throw error; - }); - return simulate; - }, - packageNameIs(name) { - jest.resetModuleRegistry(); - pkg.mockImplementationOnce(() => ({ name })); - return simulate; + setupDomain.userCerts.get.mockResolvedValueOnce(pair); } }; -test('.setLoopback() checks if hostname resolves local, ipv4 or 6', async () => { - simulate.hostResolvesLoopback(); - await PWADevServer.setLoopback('excelsior.com'); - expect(lookup).toHaveBeenCalledWith('excelsior.com'); - expect(runAsRoot).not.toHaveBeenCalled(); - - simulate.hostResolvesLoopback({ family: 6 }); - await PWADevServer.setLoopback('excelsior.com'); - expect(runAsRoot).not.toHaveBeenCalled(); -}); - -test('.setLoopback() updates /etc/hosts to make hostname local', async () => { - lookup.mockRejectedValueOnce({ code: 'ENOTFOUND' }); - await PWADevServer.setLoopback('excelsior.com'); - expect(runAsRoot).toHaveBeenCalledWith( - expect.any(String), - expect.any(Function), - 'excelsior.com' - ); -}); - -test('.setLoopback() dies under mysterious circumstances', async () => { - lookup.mockRejectedValueOnce({ code: 'UNKNOWN' }); - await expect(PWADevServer.setLoopback('excelsior.com')).rejects.toThrow( - 'Error trying to check' - ); -}); - test('.findFreePort() uses openPort to get a free port', async () => { simulate.savedPortsAre(8543, 9002, 8765).aFreePortWasFound(); @@ -126,94 +71,6 @@ test('.findFreePort() passes formatted errors from port lookup', async () => { ); }); -test('.getUniqueSubdomain() makes a new hostname for an identifier', async () => { - const hostname = await PWADevServer.getUniqueSubdomain('bar'); - expect(hostname).toMatch(/bar\-(\w){4,5}/); -}); - -test('.getUniqueSubdomain() makes a new hostname from the local package name', async () => { - simulate.packageNameIs('lorax'); - - const hostname = await PWADevServer.getUniqueSubdomain(); - expect(hostname).toMatch(/lorax\-(\w){4,5}/); -}); - -test('.getUniqueSubdomain() logs a warning if it cannot determine a name', async () => { - jest.spyOn(console, 'warn').mockImplementation(); - simulate.packageNameIs(undefined); - - const hostname = await PWADevServer.getUniqueSubdomain(); - expect(hostname).toMatch(/my\-pwa\-(\w){4,5}/); - expect(console.warn).toHaveBeenCalledWith( - expect.stringMatching('Could not autodetect'), - expect.any(Error) - ); - expect(console.warn.mock.calls[0][1].message).toMatchSnapshot(); - - // and even if package cannot be found: - simulate.noPackageFound(); - await PWADevServer.getUniqueSubdomain(); - expect(console.warn).toHaveBeenLastCalledWith( - expect.stringMatching('Could not autodetect'), - expect.any(Error) - ); - expect(console.warn.mock.calls[1][1].code).toBe('ENOTFOUND'); - console.warn.mockRestore(); -}); - -test('.provideUniqueHost() returns a URL object with a free dev host origin and stores a port', async () => { - simulate - .noPortSavedForNextHostname() - .aFreePortWasFound(8765) - .hostDoesNotResolve(); - - const { protocol, hostname, port } = await PWADevServer.provideUniqueHost( - 'woah' - ); - - expect(protocol).toBe('https:'); - expect(hostname).toMatch(/woah\-(\w){4,5}\.local\.pwadev/); - expect(port).toBe(8765); - - expect(PWADevServer.portsByHostname.get).toHaveBeenCalledWith(hostname); - expect(PWADevServer.portsByHostname.set).toHaveBeenCalledWith( - hostname, - port - ); -}); - -test('.provideUniqueHost() returns a cached port for the hostname', async () => { - const warn = jest.spyOn(console, 'warn').mockImplementation(); - simulate - .portSavedForNextHostname(8000) - .aFreePortWasFound(8776) - .hostResolvesLoopback(); - - const { port } = await PWADevServer.provideUniqueHost('woah'); - - expect(port).toBe(8776); - expect(console.warn).toHaveBeenCalledWith( - expect.stringMatching( - 'port 8000 is in use. The dev server will instead run' - ) - ); - warn.mockRestore(); -}); - -test('.provideUniqueHost() warns about reserved port conflict', async () => { - const warn = jest.spyOn(console, 'warn').mockImplementation(); - simulate - .portSavedForNextHostname(8888) - .aFreePortWasFound(8889) - .hostResolvesLoopback(); - - const { port } = await PWADevServer.provideUniqueHost('woah'); - - expect(port).toBe(8889); - - warn.mockRestore(); -}); - test('.configure() throws errors on missing config', async () => { await expect(PWADevServer.configure({ id: 'foo' })).rejects.toThrow( 'publicPath must be of type string' @@ -237,14 +94,6 @@ test('.configure() throws errors on missing config', async () => { paths: { output: 1234 } }) ).rejects.toThrow('paths.output must be of type string'); - await expect( - PWADevServer.configure({ - id: 'foo', - publicPath: 'bar', - backendDomain: 'https://dumb.domain', - paths: { output: 'foo' } - }) - ).rejects.toThrow('serviceWorkerFileName must be of type string'); }); test('.configure() gets or creates an SSL cert if `provideSSLCert: true`', async () => { @@ -263,9 +112,9 @@ test('.configure() gets or creates an SSL cert if `provideSSLCert: true`', async publicPath: 'bork', serviceWorkerFileName: 'doin', backendDomain: 'growe', + id: 'flowk', provideSSLCert: true }); - expect(SSLCertStore.provide).toHaveBeenCalled(); expect(server.https).toHaveProperty('cert', 'fakeCert'); }); @@ -312,7 +161,11 @@ test('.configure() is backwards compatible with `id` param', async () => { simulate .portSavedForNextHostname(8765) .aFreePortWasFound(8765) - .hostResolvesLoopback(); + .hostResolvesLoopback() + .certExistsForNextHostname({ + key: 'fakeKey2', + cert: 'fakeCert2' + }); const config = { id: 'samiam', @@ -385,12 +238,18 @@ test('.configure() `id` param overrides `provideUniqueHost` param', async () => }); }); -test('.configure() returns a configuration object with before() and after() handlers that add middlewares in order', async () => { - simulate.aFreePortWasFound(); +test('debugErrorMiddleware attached', async () => { + simulate + .portSavedForNextHostname(8765) + .aFreePortWasFound(8765) + .hostResolvesLoopback(); const config = { + id: 'samiam', + provideUniqueHost: 'samiam', paths: { - output: 'path/to/static' + output: 'path/to/static', + assets: 'path/to/assets' }, publicPath: 'full/path/to/publicPath', serviceWorkerFileName: 'swname.js', @@ -399,77 +258,10 @@ test('.configure() returns a configuration object with before() and after() hand const devServer = await PWADevServer.configure(config); + expect(devServer.after).toBeInstanceOf(Function); const app = { use: jest.fn() }; - - middlewares.StaticRootRoute.mockReturnValueOnce('fakeStaticRootRoute'); - - devServer.before(app); - - middlewares.DevProxy.mockReturnValueOnce('fakeDevProxy'); - devServer.after(app); - - expect(middlewares.DevProxy).toHaveBeenCalledWith( - expect.objectContaining({ - target: 'https://magento.backend.domain' - }) - ); - - expect(middlewares.OriginSubstitution).not.toHaveBeenCalled(); - - expect(app.use).toHaveBeenCalledWith('fakeDevProxy'); - - expect(middlewares.StaticRootRoute).toHaveBeenCalledWith( - 'path/to/static/swname.js' - ); - - expect(app.use).toHaveBeenCalledWith('fakeStaticRootRoute'); - - expect(app.use).toHaveBeenCalledWith( - 'full/path/to/publicPath', - expect.any(Function) - ); -}); - -test('.configure() optionally adds OriginSubstitution middleware', async () => { - simulate.aFreePortWasFound(8002); - - const config = { - paths: { - output: 'path/to/static' - }, - publicPath: 'full/path/to/publicPath', - serviceWorkerFileName: 'swname.js', - backendDomain: 'https://magento.backend.domain', - changeOrigin: true - }; - - const devServer = await PWADevServer.configure(config); - - const app = { - use: jest.fn() - }; - - middlewares.OriginSubstitution.mockReturnValueOnce( - 'fakeOriginSubstitution' - ); - middlewares.DevProxy.mockReturnValueOnce('fakeDevProxy'); - middlewares.StaticRootRoute.mockReturnValueOnce('fakeStaticRootRoute'); - - devServer.before(app); - - expect(middlewares.OriginSubstitution).toHaveBeenCalledWith( - expect.objectContaining({ - protocol: 'https:', - hostname: 'magento.backend.domain' - }), - expect.objectContaining({ - hostname: 'localhost', - port: 8002 - }) - ); - - expect(app.use).toHaveBeenCalledWith('fakeOriginSubstitution'); + expect(app.use).toHaveBeenCalledWith(expect.any(Function)); }); diff --git a/packages/pwa-buildpack/src/WebpackTools/__tests__/__snapshots__/PWADevServer.spec.js.snap b/packages/pwa-buildpack/src/WebpackTools/__tests__/__snapshots__/PWADevServer.spec.js.snap deleted file mode 100644 index b7bcf31a478..00000000000 --- a/packages/pwa-buildpack/src/WebpackTools/__tests__/__snapshots__/PWADevServer.spec.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`.getUniqueSubdomain() logs a warning if it cannot determine a name 1`] = `"package.json does not have a usable \\"name\\" field!"`; diff --git a/packages/pwa-buildpack/src/WebpackTools/index.js b/packages/pwa-buildpack/src/WebpackTools/index.js index 227b3853961..bfb867498c8 100644 --- a/packages/pwa-buildpack/src/WebpackTools/index.js +++ b/packages/pwa-buildpack/src/WebpackTools/index.js @@ -3,5 +3,6 @@ module.exports = { ServiceWorkerPlugin: require('./plugins/ServiceWorkerPlugin'), DevServerReadyNotifierPlugin: require('./plugins/DevServerReadyNotifierPlugin'), MagentoResolver: require('./MagentoResolver'), - PWADevServer: require('./PWADevServer') + PWADevServer: require('./PWADevServer'), + UpwardPlugin: require('./plugins/UpwardPlugin') }; diff --git a/packages/pwa-buildpack/src/WebpackTools/middlewares/DevProxy.js b/packages/pwa-buildpack/src/WebpackTools/middlewares/DevProxy.js deleted file mode 100644 index 0700c5e87a9..00000000000 --- a/packages/pwa-buildpack/src/WebpackTools/middlewares/DevProxy.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Proxies all requests not served by Webpack in-memory bundling back - * to the underlying store. - * - * Tries to detect a case where the target URL is misconfigured and the - * store tries to redirect to http or https. - */ -const proxyMiddleware = require('http-proxy-middleware'); -const optionsValidator = require('../../util/options-validator'); -const { format, URL } = require('url'); - -const RedirectCodes = [201, 301, 302, 307, 308]; -const findRedirect = message => - RedirectCodes.includes(message.statusCode) && message.headers.location; - -const emitErrorOnProtocolChange = (emit, target, redirected) => { - const backend = new URL(target); - const { host, protocol } = new URL(redirected); - if (backend.host === host && backend.protocol === protocol) { - return; - } - - if (backend.protocol === 'https:' && protocol === 'http:') { - return emit( - new Error( - `pwa-buildpack: Backend domain is configured to ${target}, but redirected to unsecure HTTP. Please configure backend server to use SSL.` - ) - ); - } - - if (backend.protocol === 'http:' && protocol === 'https:') { - return emit( - new Error( - `pwa-buildpack: Backend domain is configured to ${target}, but redirected to secure HTTPS. Please change configuration to point to secure backend domain: ${format( - Object.assign(backend, { protocol: 'https:' }) - )}.` - ) - ); - } - - emit( - new Error( - `pwa-buildpack: Backend domain redirected to unknown protocol: ${redirected}` - ) - ); -}; - -const validateConfig = optionsValidator('DevProxyMiddleware', { - target: 'string' -}); - -module.exports = function createDevProxy(config) { - validateConfig('createDevProxy', config); - const proxyConf = Object.assign( - { - logLevel: 'debug', - logProvider: defaultProvider => config.logger || defaultProvider, - onProxyRes(proxyRes) { - const redirected = findRedirect(proxyRes); - if (redirected) { - emitErrorOnProtocolChange( - nextCallback, - config.target, - redirected - ); - } - }, - secure: false, - changeOrigin: true, - autoRewrite: true, - cookieDomainRewrite: '' // remove any absolute domain on cookies - }, - config - ); - let nextCallback; - const proxy = proxyMiddleware('**', proxyConf); - // Return an outer middleware so we can access the `next` function to - // properly pass errors along. - return (req, res, next) => { - nextCallback = err => { - proxyConf.logProvider(console).error(err); - return next(err); - }; - return proxy(req, res, next); - }; -}; diff --git a/packages/pwa-buildpack/src/WebpackTools/middlewares/OriginSubstitution.js b/packages/pwa-buildpack/src/WebpackTools/middlewares/OriginSubstitution.js deleted file mode 100644 index 5d0377114b5..00000000000 --- a/packages/pwa-buildpack/src/WebpackTools/middlewares/OriginSubstitution.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Replace all instances of one URL base in an HTML response, with another. - * Useful for proxying to systems like Magento, which often generate absolute - * URLs in their render output. - * - * Rather than configure Magento to use the your temporary dev server URL as - * its configured base domain, this middleware allows the dev server to text - * replace any links, resources, or reference URLs on the fly. - * - * For Magento 2 specifically, This is a stopgap until we can hack Framework to - * have branch logic in asset URL resolvers. - * - * EXPERIMENTAL -- not ready for prime time - */ -const debug = require('../../util/debug').makeFileLogger(__filename); -const url = require('url'); -const harmon = require('harmon'); -const through = require('through'); -const removeTrailingSlash = x => x.replace(/\/$/, ''); -module.exports = function createOriginSubstitutionMiddleware( - oldDomain, - newDomain -) { - const oldOrigin = removeTrailingSlash(url.format(oldDomain)); - const newOrigin = removeTrailingSlash(url.format(newDomain)); - const attributesToReplaceOrigin = ['href', 'src', 'style'].map(attr => ({ - query: `[${attr}*="${oldOrigin}"]`, - func(node) { - node.setAttribute( - attr, - node - .getAttribute(attr) - .split(oldOrigin) - .join(newOrigin) - ); - } - })); - const tagsToReplaceOrigin = ['style'].map(attr => ({ - query: attr, - func(node) { - debug('tag', attr, node); - const stream = node.createStream(); - stream - .pipe( - through(function(buf) { - this.queue( - buf - .toString() - .split(oldOrigin) - .join(newOrigin) - ); - }) - ) - .pipe(stream); - } - })); - debug( - `replace ${oldOrigin} with ${newOrigin} in html`, - attributesToReplaceOrigin - ); - const allTransforms = [ - ...tagsToReplaceOrigin, - ...attributesToReplaceOrigin - ]; - return harmon([], allTransforms, true); -}; diff --git a/packages/pwa-buildpack/src/WebpackTools/middlewares/StaticRootRoute.js b/packages/pwa-buildpack/src/WebpackTools/middlewares/StaticRootRoute.js deleted file mode 100644 index 441ed006e88..00000000000 --- a/packages/pwa-buildpack/src/WebpackTools/middlewares/StaticRootRoute.js +++ /dev/null @@ -1,11 +0,0 @@ -const { basename } = require('path'); -module.exports = function createStaticRootRoute(filepath) { - const publicPath = '/' + basename(filepath); - return (req, res, next) => { - if (req.method === 'GET' && req.path === publicPath) { - res.sendFile(filepath); - } else { - next(); - } - }; -}; diff --git a/packages/pwa-buildpack/src/WebpackTools/middlewares/__fixtures__/root-available-file.json b/packages/pwa-buildpack/src/WebpackTools/middlewares/__fixtures__/root-available-file.json deleted file mode 100644 index 2f8d0ebb860..00000000000 --- a/packages/pwa-buildpack/src/WebpackTools/middlewares/__fixtures__/root-available-file.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "goodJsonResponse": true -} diff --git a/packages/pwa-buildpack/src/WebpackTools/middlewares/__tests__/DevProxy.spec.js b/packages/pwa-buildpack/src/WebpackTools/middlewares/__tests__/DevProxy.spec.js deleted file mode 100644 index 2c1b9c7f567..00000000000 --- a/packages/pwa-buildpack/src/WebpackTools/middlewares/__tests__/DevProxy.spec.js +++ /dev/null @@ -1,149 +0,0 @@ -let devProxy; -const request = require('supertest'); -const nock = require('nock'); -const express = require('express'); - -const TARGET = 'https://proxytarget.test'; -const TARGET_UNSECURE = 'http://proxytarget.test'; -const logger = {}; -const logMethods = ['log', 'debug', 'info', 'warn', 'error']; - -beforeAll(() => { - logMethods.forEach(method => { - logger[method] = jest.fn(); - jest.spyOn(console, method).mockImplementation(); - }); - // We wait to require DevProxy here because DevProxy imports `http-proxy-middleware`, and - // `http-proxy-middleware` creates a "default logger" at module definition time--that is, when - // we require() it. The default logger destructures the console object, so it no longer holds - // a reference to `console` itself. This makes us unable to mock `console` for the - // `http-proxy-middleware` library if it loads before the mock does. - devProxy = require('../DevProxy'); -}); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -afterAll(() => { - logMethods.forEach(method => { - console[method].mockRestore(); - }); - nock.restore(); -}); - -test('logs to custom logger', async () => { - nock(TARGET) - .get('/will-log') - .reply(200, 'Hello world!'); - - const appWithCustomLogger = express(); - appWithCustomLogger.use( - devProxy({ - logger, - target: TARGET - }) - ); - - await request(appWithCustomLogger).get('/will-log'); - - expect(logger.debug).toHaveBeenCalledWith( - expect.stringContaining('will-log') - ); -}); - -test('logs to console by default', async () => { - nock(TARGET) - .get('/will-log') - .reply(200, 'Hello world!'); - const app = express(); - app.use( - devProxy({ - target: TARGET - }) - ); - - await request(app).get('/will-log'); - - expect(console.log).toHaveBeenCalledWith( - expect.stringContaining('will-log') - ); -}); - -test('handles redirects silently when origin is same', async () => { - nock(TARGET) - .get('/will-proxy-to-self') - .reply(301, '', { - Location: TARGET + '/redirected-to' - }); - - const app = express(); - app.use( - devProxy({ - logger, - target: TARGET - }) - ); - - await expect( - request(app) - .get('/will-proxy-to-self') - .expect(301) - .expect('location', /redirected\-to/) - ).resolves.toBeTruthy(); -}); - -test('errors informatively on redirect with protocol change', async () => { - nock(TARGET_UNSECURE) - .get('/will-proxy-to-secure') - .reply(301, '', { - Location: TARGET + '/will-proxy-to-secure' - }); - - const app = express(); - app.use( - devProxy({ - logger, - target: TARGET_UNSECURE - }) - ); - - await expect( - request(app).get('/will-proxy-to-secure') - ).resolves.toMatchObject({ - status: 500, - text: expect.stringContaining('redirected to secure HTTPS') - }); - - nock(TARGET) - .get('/will-proxy-to-unsecure') - .reply(301, '', { - Location: TARGET_UNSECURE + '/will-proxy-to-unsecure' - }) - .get('/will-proxy-to-nowhere') - .reply(302, '', { - Location: 'badprotocol' + TARGET + '/will-proxy-to-nowhere' - }); - - const secureApp = express(); - secureApp.use( - devProxy({ - logger, - target: TARGET - }) - ); - - await expect( - request(secureApp).get('/will-proxy-to-unsecure') - ).resolves.toMatchObject({ - status: 500, - text: expect.stringContaining('redirected to unsecure HTTP') - }); - - await expect( - request(secureApp).get('/will-proxy-to-nowhere') - ).resolves.toMatchObject({ - status: 500, - text: expect.stringContaining('redirected to unknown protocol') - }); -}); diff --git a/packages/pwa-buildpack/src/WebpackTools/middlewares/__tests__/OriginSubstitution.spec.js b/packages/pwa-buildpack/src/WebpackTools/middlewares/__tests__/OriginSubstitution.spec.js deleted file mode 100644 index fbd903831cf..00000000000 --- a/packages/pwa-buildpack/src/WebpackTools/middlewares/__tests__/OriginSubstitution.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -const { URL } = require('url'); -const request = require('supertest'); -const express = require('express'); - -const originSubstitution = require('../OriginSubstitution'); - -test('swaps origins in html', async () => { - const app = express(); - const backendUrl = new URL('https://old.backend:8080'); - const frontendUrl = new URL('https://cool.frontend:8081'); - app.use(originSubstitution(backendUrl, frontendUrl)); - const htmlWithBaseDomain = base => - ` - - - - - - - - - Home - - - `.trim(); - app.get('/', (req, res) => res.send(htmlWithBaseDomain(backendUrl))); - await expect(request(app).get('/')).resolves.toMatchObject({ - text: htmlWithBaseDomain(frontendUrl) - }); -}); diff --git a/packages/pwa-buildpack/src/WebpackTools/middlewares/__tests__/StaticRootRoute.spec.js b/packages/pwa-buildpack/src/WebpackTools/middlewares/__tests__/StaticRootRoute.spec.js deleted file mode 100644 index bef49f2cd90..00000000000 --- a/packages/pwa-buildpack/src/WebpackTools/middlewares/__tests__/StaticRootRoute.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -const { resolve } = require('path'); -const request = require('supertest'); -const express = require('express'); - -const staticRootRoute = require('../StaticRootRoute'); - -test('serves the provided file at root', async () => { - const app = express(); - app.use( - staticRootRoute( - resolve(__dirname, '..', '__fixtures__', 'root-available-file.json') - ) - ); - await expect( - request(app) - .get('/root-available-file.json') - .expect(200) - ).resolves.toMatchObject({ - body: { - goodJsonResponse: true - } - }); - await expect( - request(app) - .get('/some-other-file') - .expect(404) - ).resolves.toMatchObject({ - status: 404 - }); -}); diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/MagentoRootComponentsPlugin/index.js b/packages/pwa-buildpack/src/WebpackTools/plugins/MagentoRootComponentsPlugin/index.js index c6b049f3eaa..c4379ed2025 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/MagentoRootComponentsPlugin/index.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/MagentoRootComponentsPlugin/index.js @@ -1,4 +1,4 @@ -const { isAbsolute, join } = require('path'); +const { isAbsolute, join, extname } = require('path'); const { RawSource } = require('webpack-sources'); const { rootComponentMap, @@ -7,6 +7,8 @@ const { const loaderPath = join(__dirname, 'roots-chunk-loader.js'); +const isJSFile = filename => /^\.jsx?$/.test(extname(filename)); + /** * @description webpack plugin that creates chunks for each * individual RootComponent in a store, and generates a manifest @@ -48,28 +50,31 @@ class MagentoRootComponentsPlugin { moduleByPath.set(mod.resource, mod); } + if (!isJSFile(mod.resource)) { + return; + } // To create a unique chunk for each RootComponent, we want to inject // a dynamic import() for each RootComponent, within each entry point. - // But identifying entry points is hard! - // Top-level modules injected by a downstream "issuer" are not - // entry points. const isEntrySimpleTest = this.phase === 'development' - ? mod => mod.resource.includes('/node_modules/') - : mod => !mod.issuer; + ? // Dependencies are not entry points in development. + mod => mod.resource.includes('/node_modules/') + : // Top-level modules injected by a downstream "issuer" are not + // entry points in production. + mod => !mod.issuer; const isAnEntry = isEntrySimpleTest(mod) && // Otherwise, check if the module being constructed matches a defined entry point compilation.entries.some(entryMod => { + // Check if the module being constructed matches a defined entry point if (mod === entryMod) { return true; } - if (!entryMod.identifier().startsWith('multi')) { - return false; - } + + // If a multi-module entry is used (webpack-dev-server creates one), we // need to try and match against each dependency in the multi module return entryMod.dependencies.some( singleDep => singleDep.module === mod diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/UpwardPlugin.js b/packages/pwa-buildpack/src/WebpackTools/plugins/UpwardPlugin.js new file mode 100644 index 00000000000..a692a08b99b --- /dev/null +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/UpwardPlugin.js @@ -0,0 +1,113 @@ +const debug = require('../../util/debug').makeFileLogger(__filename); +const fetch = require('node-fetch'); +const path = require('path'); +const https = require('https'); +const url = require('url'); +const upward = require('@magento/upward-js'); + +// To be used with `node-fetch` in order to allow self-signed certificates. +const httpsAgent = new https.Agent({ rejectUnauthorized: false }); + +class UpwardPlugin { + constructor(devServer, upwardPath) { + this.upwardPath = upwardPath; + // Compose `after` function if something else has defined it. + const oldAfter = devServer.after; + devServer.after = app => { + app.use((req, res, next) => this.handleRequest(req, res, next)); + if (oldAfter) oldAfter(app); + }; + } + apply(compiler) { + this.compiler = compiler; + // If a request has run to the devServer before this method has run, + // then there is already a Promise pending for the compiler, and this is + // its resolver. + if (this.resolveCompiler) { + this.resolveCompiler(compiler); + } + } + // Hold the first request (and subsequent requests) until the middleware is + // created, then swap out `handleRequest` for the simplest stack trace. + async handleRequest(req, res, next) { + // Several requests may come in. Only create the middleware once. + if (!this.middlewarePromise) { + this.middlewarePromise = this.createMiddleware(); + } + await this.middlewarePromise; + // When the promise is resolved, `this.middleware` will exist. + // Replace this function itself. + this.handleRequest = this.middleware; + // And then call it to finish the response. + this.middleware(req, res, next); + // Further requests will go straight to the middleware. + } + async createMiddleware() { + // The compiler is necessary to build the fallback filesystem + // so UPWARd can use Webpack-generated assets in dev mode. + const compiler = await this.getCompiler(); + + // Standard filesystem-and-fetch IO. + const defaultIO = upward.IOAdapter.default(this.upwardPath); + + // Use Webpack's in-memory file system for UPWARD file retrieval during + // development. Allows for hot reloading of server-side configuration. + + const io = { + async readFile(filepath, enc) { + const absolutePath = path.resolve(filepath); + + // Most likely scenario: UPWARD needs an output asset. + try { + return compiler.outputFileSystem.readFileSync( + absolutePath, + enc + ); + } catch (e) {} + + // Next most likely scenario: UPWARD needs a file on disk. + try { + const fromDefault = await defaultIO.readFile( + absolutePath, + enc + ); + return fromDefault; + } catch (e) {} + + // Fallback: Use Webpack's resolution rules. + return compiler.inputFileSystem.readFileSync(absolutePath, enc); + }, + + async networkFetch(path, options) { + debug('networkFetch %s, %o', path, options); + const { protocol } = url.parse(path); + if (protocol === 'https:') { + return fetch( + path, + Object.assign({ agent: httpsAgent }, options) + ); + } + return fetch(path, options); + // Use the https.Agent to allow self-signed certificates. + } + }; + + this.middleware = await upward.middleware(this.upwardPath, io); + } + async getCompiler() { + if (this.compiler) { + return this.compiler; + } + if (!this.compilerPromise) { + // Create a promise for the compiler and expose its resolver so it + // can be resolved when the `apply` method runs. + this.compilerPromise = new Promise(resolve => { + this.resolveCompiler = resolve; + }); + } + // Share the compiler promise. + return this.compilerPromise; + } +} + +module.exports = UpwardPlugin; diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/UpwardPlugin.spec.js b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/UpwardPlugin.spec.js new file mode 100644 index 00000000000..179c0be5adb --- /dev/null +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/UpwardPlugin.spec.js @@ -0,0 +1,246 @@ +jest.mock('node-fetch'); +jest.mock('@magento/upward-js'); +const upward = require('@magento/upward-js'); +const fetch = require('node-fetch'); +const UpwardPlugin = require('../UpwardPlugin'); + +test('creates a devServer.after function if it does not exist', () => { + const devServer = {}; + const app = { + use: jest.fn() + }; + new UpwardPlugin(devServer); + expect(devServer.after).toBeInstanceOf(Function); + devServer.after(app); + expect(app.use).toHaveBeenCalledWith(expect.any(Function)); +}); + +test('composes with an existing devServer.after function', () => { + const after = jest.fn(); + const devServer = { after }; + const app = { + use: jest.fn() + }; + new UpwardPlugin(devServer); + expect(devServer.after).not.toBe(after); + devServer.after(app); + expect(app.use).toHaveBeenCalledWith(expect.any(Function)); + expect(after).toHaveBeenCalledWith(app); +}); + +test('applies to a Webpack compiler and resolves any existing devServer requests', async () => { + const devServer = {}; + const compiler = {}; + const req = {}, + res = {}, + next = {}; + const app = { + use: jest.fn() + }; + const upwardHandler = jest.fn(); + + upward.middleware.mockResolvedValueOnce(upwardHandler); + + const noRequestsWaiting = new UpwardPlugin({}); + noRequestsWaiting.apply(compiler); + expect(noRequestsWaiting.compiler).toBe(compiler); + + const hasRequestsWaiting = new UpwardPlugin(devServer, 'path/to/upward'); + devServer.after(app); + const handler = app.use.mock.calls[0][0]; + + handler(req, res, next); + expect(upward.IOAdapter.default).not.toHaveBeenCalled(); + expect(upward.middleware).not.toHaveBeenCalled(); + expect(hasRequestsWaiting.middlewarePromise).toBeInstanceOf(Promise); + + hasRequestsWaiting.apply(compiler); + await hasRequestsWaiting.middlewarePromise; + + expect(upward.IOAdapter.default).toHaveBeenCalledWith('path/to/upward'); + expect(upward.middleware).toHaveBeenCalledWith( + 'path/to/upward', + expect.objectContaining({ + readFile: expect.any(Function), + networkFetch: expect.any(Function) + }) + ); + expect(upwardHandler).toHaveBeenCalledWith(req, res, next); +}); + +test('shares compiler promise', async () => { + const devServer = {}; + const compiler = {}; + const upwardHandler = jest.fn(); + + upward.middleware.mockResolvedValueOnce(upwardHandler); + const plugin = new UpwardPlugin(devServer, 'path/to/upward'); + + const promises = [plugin.getCompiler(), plugin.getCompiler()]; + + plugin.apply(compiler); + const [c1, c2] = await Promise.all(promises); + + expect(c1).toBe(c2); + expect(c2).toBe(compiler); +}); + +test('shares middleware promise so as not to create multiple middlewares', async () => { + const devServer = {}; + const compiler = {}; + const req = {}, + res = {}, + next = {}; + const app = { + use: jest.fn() + }; + const upwardHandler = jest.fn(); + + upward.middleware.mockResolvedValueOnce(upwardHandler); + const plugin = new UpwardPlugin(devServer, 'path/to/upward'); + devServer.after(app); + const handler = app.use.mock.calls[0][0]; + + handler(req, res, next); + handler('some', 'other', 'stuff'); + plugin.apply(compiler); + + await plugin.middlewarePromise; + + expect(upward.middleware).toHaveBeenCalledTimes(1); +}); + +test('supplies a dev-mode IOAdapter with webpack fs integration', async () => { + const devServer = {}; + const compiler = { + outputFileSystem: { + readFileSync: jest.fn() + }, + inputFileSystem: { + readFileSync: jest.fn() + } + }; + const defaultIO = { + readFile: jest.fn() + }; + const app = { + use: jest.fn() + }; + + upward.middleware.mockResolvedValueOnce(() => {}); + upward.IOAdapter.default.mockReturnValueOnce(defaultIO); + + const plugin = new UpwardPlugin(devServer); + plugin.apply(compiler); + devServer.after(app); + const handler = app.use.mock.calls[0][0]; + handler(); + await plugin.middlewarePromise; + + const io = upward.middleware.mock.calls[0][1]; + + compiler.outputFileSystem.readFileSync.mockImplementationOnce(() => { + return 'from output file system'; + }); + const fromOutputFileSystem = await io.readFile('aFile', 'binary'); + expect(fromOutputFileSystem).toBe('from output file system'); + expect(compiler.outputFileSystem.readFileSync).toHaveBeenCalledWith( + expect.stringMatching(/aFile$/), + 'binary' + ); + + compiler.outputFileSystem.readFileSync.mockImplementationOnce(() => { + throw new Error('ENOENT'); + }); + defaultIO.readFile.mockResolvedValueOnce('from default filesystem'); + const fromDefaultFileSystem = await io.readFile('bFile'); + expect(fromDefaultFileSystem).toBe('from default filesystem'); + expect(compiler.outputFileSystem.readFileSync).toHaveBeenCalledWith( + expect.stringMatching(/bFile$/), + undefined + ); + expect(defaultIO.readFile).toHaveBeenCalledWith( + expect.stringMatching(/bFile$/), + undefined + ); + + compiler.outputFileSystem.readFileSync.mockImplementationOnce(() => { + throw new Error('ENOENT'); + }); + defaultIO.readFile.mockImplementationOnce(() => + Promise.reject(new Error('ENOENT')) + ); + compiler.inputFileSystem.readFileSync.mockImplementationOnce( + () => 'from input file system' + ); + const fromInputFileSystem = await io.readFile('cFile'); + expect(fromInputFileSystem).toBe('from input file system'); + expect(compiler.outputFileSystem.readFileSync).toHaveBeenCalledWith( + expect.stringMatching(/cFile$/), + undefined + ); + expect(defaultIO.readFile).toHaveBeenCalledWith( + expect.stringMatching(/cFile$/), + undefined + ); + expect(compiler.inputFileSystem.readFileSync).toHaveBeenCalledWith( + expect.stringMatching(/cFile$/), + undefined + ); +}); + +test('dev-mode IOAdapter uses fetch', async () => { + const devServer = {}; + const app = { + use: jest.fn() + }; + + upward.middleware.mockResolvedValueOnce(() => {}); + + const plugin = new UpwardPlugin(devServer); + plugin.apply({}); + devServer.after(app); + const handler = app.use.mock.calls[0][0]; + handler(); + await plugin.middlewarePromise; + + const io = upward.middleware.mock.calls[0][1]; + + io.networkFetch('https://example.com', { method: 'POST' }); + + expect(fetch).toHaveBeenCalledWith( + 'https://example.com', + expect.objectContaining({ + method: 'POST', + agent: expect.anything() + }) + ); +}); + +test('dev-mode IOAdapter can fetch unsecure URLs', async () => { + const devServer = {}; + const app = { + use: jest.fn() + }; + + upward.middleware.mockResolvedValueOnce(() => {}); + + const plugin = new UpwardPlugin(devServer); + plugin.apply({}); + devServer.after(app); + const handler = app.use.mock.calls[0][0]; + handler(); + await plugin.middlewarePromise; + + const io = upward.middleware.mock.calls[0][1]; + + io.networkFetch('http://example.com', { method: 'POST' }); + + expect(fetch).toHaveBeenCalledWith( + 'http://example.com', + expect.objectContaining({ + method: 'POST' + }) + ); + expect(fetch.mock.calls[0][1]).not.toHaveProperty('agent'); +}); diff --git a/packages/pwa-buildpack/src/index.js b/packages/pwa-buildpack/src/index.js index 38eb4a3fac0..1a41ac869da 100644 --- a/packages/pwa-buildpack/src/index.js +++ b/packages/pwa-buildpack/src/index.js @@ -1,5 +1,6 @@ const magentoLayoutLoader = require('./magento-layout-loader'); module.exports = { magentoLayoutLoader, - WebpackTools: require('./WebpackTools') + WebpackTools: require('./WebpackTools'), + Utilities: require('./Utilities') }; diff --git a/packages/pwa-buildpack/src/util/__tests__/check-loopback.spec.js b/packages/pwa-buildpack/src/util/__tests__/check-loopback.spec.js new file mode 100644 index 00000000000..6a59599ade7 --- /dev/null +++ b/packages/pwa-buildpack/src/util/__tests__/check-loopback.spec.js @@ -0,0 +1,71 @@ +jest.mock('../promisified/dns'); +jest.mock('../run-as-root'); +jest.mock('hostile'); + +const { lookup } = require('../promisified/dns'); +const runAsRoot = require('../run-as-root'); +const hostile = require('hostile'); + +const checkLoopback = require('../check-loopback'); + +const simulate = { + hostResolvesLoopback({ family = 4 } = {}) { + lookup.mockReturnValueOnce({ + address: family === 6 ? '::1' : '127.0.0.1', + family + }); + return simulate; + }, + hostDoesNotResolve() { + lookup.mockRejectedValueOnce({ code: 'ENOTFOUND' }); + return simulate; + } +}; + +test('checks if hostnames resolve local, ipv4 or 6', async () => { + simulate.hostResolvesLoopback(); + const loopbacks = await checkLoopback(['excelsior.com']); + expect(loopbacks.has('excelsior.com')).toBe(true); + expect(lookup).toHaveBeenCalledWith('excelsior.com'); + + simulate.hostResolvesLoopback({ family: 6 }); + const v6loopbacks = await checkLoopback(['excelsior.com']); + expect(v6loopbacks.has('excelsior.com')).toBe(true); +}); + +test('works on multiple hostnames', async () => { + simulate.hostResolvesLoopback().hostResolvesLoopback(); + const loopbacks = await checkLoopback(['excelsior.com', 'reliant.com']); + expect(loopbacks.has('excelsior.com')).toBe(true); +}); + +test.skip('updates /etc/hosts to make hostname local', async () => { + lookup.mockRejectedValueOnce({ code: 'ENOTFOUND' }); + await checkLoopback(['excelsior.com']); + expect(runAsRoot).toHaveBeenCalledTimes(1); + const updater = runAsRoot.mock.calls[0][1]; + updater(['defiant.com', 'yamato.com']); + expect(hostile.set).toHaveBeenCalledTimes(2); + expect(hostile.set).toHaveBeenNthCalledWith(1, '127.0.0.1', 'defiant.com'); + expect(hostile.set).toHaveBeenNthCalledWith(2, '127.0.0.1', 'yamato.com'); +}); + +test('dies if unexpected lookup error', async () => { + lookup.mockRejectedValueOnce(new Error('unknown')); + await expect(checkLoopback(['excelsior.com'])).rejects.toThrow( + 'Error trying to check' + ); +}); + +test('throws informative error if invalid argument', async () => { + await expect( + checkLoopback('wat') + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"[pwa-buildpack:util:check-loopback.js] hostnames must be an array of strings"` + ); + await expect( + checkLoopback(['wat', {}]) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"[pwa-buildpack:util:check-loopback.js] hostnames must be an array of strings"` + ); +}); diff --git a/packages/pwa-buildpack/src/util/__tests__/ssl-cert-store.spec.js b/packages/pwa-buildpack/src/util/__tests__/ssl-cert-store.spec.js deleted file mode 100644 index ef7bbd57800..00000000000 --- a/packages/pwa-buildpack/src/util/__tests__/ssl-cert-store.spec.js +++ /dev/null @@ -1,95 +0,0 @@ -jest.mock('../promisified/child_process'); -jest.mock('../run-as-root'); -jest.mock('../global-config'); - -const GlobalConfig = require('../global-config'); -const { exec } = require('../promisified/child_process'); -const runAsRoot = require('../run-as-root'); - -let SSLCertStore; -beforeAll(() => { - GlobalConfig.mockImplementation(({ key }) => ({ - set: jest.fn((...args) => { - const keyParts = args.slice(0, -1); - expect(typeof key(...keyParts)).toBe('string'); - }), - get: jest.fn(), - values: jest.fn(), - del: jest.fn() - })); - SSLCertStore = require('../ssl-cert-store'); -}); -afterAll(() => GlobalConfig.mockRestore()); - -test('static async expired(cert) uses openssl to test whether a cert has expired or is expiring', async () => { - exec.mockRejectedValueOnce({ - stdout: '\nCertificate will expire\n' - }); - const shouldBeTrue = await SSLCertStore.expired('fakeCert'); - expect(exec).toHaveBeenCalledWith( - 'openssl x509 -checkend 0 <<< "fakeCert"' - ); - expect(shouldBeTrue).toBe(true); - - exec.mockResolvedValueOnce(null); - const shouldBeFalse = await SSLCertStore.expired('fakeCert'); - expect(shouldBeFalse).toBe(false); - - exec.mockRejectedValueOnce({ - stdout: 'Some other value' - }); - const shouldStillBeFalse = await SSLCertStore.expired('fakeCert'); - expect(shouldStillBeFalse).toBe(false); -}); - -test('static async provide() throws on a non-string', async () => { - await expect(SSLCertStore.provide(null)).rejects.toThrowError( - 'Must provide a commonName' - ); -}); - -test('static async provide() gets a valid cached cert', async () => { - exec.mockResolvedValueOnce(null); - SSLCertStore.userCerts.get.mockResolvedValueOnce({ - cert: 'cachedCert', - key: 'cachedKey' - }); - await expect(SSLCertStore.provide('example.com')).resolves.toMatchObject({ - cert: 'cachedCert', - key: 'cachedKey' - }); -}); - -test('static async provide() deletes and recreates an expired cert', async () => { - SSLCertStore.userCerts.get.mockResolvedValueOnce({ - cert: 'expiredCert', - key: 'expiredKey' - }); - exec.mockRejectedValueOnce({ stdout: 'Certificate will expire' }); - runAsRoot.mockResolvedValueOnce( - '{ "key": "refreshedKey", "cert": "refreshedCert" }' - ); - await expect(SSLCertStore.provide('example.com')).resolves.toMatchObject({ - cert: 'refreshedCert', - key: 'refreshedKey' - }); -}); - -test('static async provide() creates a cert for a fresh domain', async () => { - SSLCertStore.userCerts.get.mockRestore(); - runAsRoot.mockImplementationOnce(() => { - return Promise.resolve('{ "key": "newKey", "cert": "newCert" }'); - }); - // and again with no cert, not even an expired one - await expect(SSLCertStore.provide('existing.com')).resolves.toMatchObject({ - cert: 'newCert', - key: 'newKey' - }); -}); - -test('static async create() throws a formatted error if the root call did not work', async () => { - runAsRoot.mockRejectedValueOnce(''); - await expect(SSLCertStore.provide('example.com')).rejects.toThrowError( - /generating dev cert/ - ); -}); diff --git a/packages/pwa-buildpack/src/util/check-loopback.js b/packages/pwa-buildpack/src/util/check-loopback.js new file mode 100644 index 00000000000..bd498f2ef56 --- /dev/null +++ b/packages/pwa-buildpack/src/util/check-loopback.js @@ -0,0 +1,48 @@ +const debug = require('./debug').makeFileLogger(__filename); +const { lookup } = require('./promisified/dns'); + +async function resolveIp(hostname) { + debug(`checking if ${hostname} is loopback`); + try { + const lookedUp = await lookup(hostname); + return lookedUp; + } catch (e) { + if (e.code !== 'ENOTFOUND') { + throw Error( + debug.errorMsg( + `Error trying to check that ${hostname} is loopback: ${ + e.message + }` + ) + ); + } + } +} + +async function checkLoopback(hostnames) { + if ( + !Array.isArray(hostnames) || + hostnames.some(name => typeof name !== 'string') + ) { + throw new Error( + debug.errorMsg(`hostnames must be an array of strings`) + ); + } + const ips = await Promise.all(hostnames.map(resolveIp)); + + return new Set( + hostnames.filter((hostname, i) => { + const ip = ips[i]; + const loopsBack = + ip && (ip.address === '127.0.0.1' || ip.address === '::1'); + + if (loopsBack) { + debug(`${hostname} already resolves to ${ip.address}!`); + } + + return loopsBack; + }) + ); +} + +module.exports = checkLoopback; diff --git a/packages/pwa-buildpack/src/util/ssl-cert-store.js b/packages/pwa-buildpack/src/util/ssl-cert-store.js deleted file mode 100644 index 52e5c0e36af..00000000000 --- a/packages/pwa-buildpack/src/util/ssl-cert-store.js +++ /dev/null @@ -1,59 +0,0 @@ -const GlobalConfig = require('./global-config'); -const debug = require('./debug').makeFileLogger(__filename); -const { exec } = require('./promisified/child_process'); -const runAsRoot = require('./run-as-root'); - -const userCerts = new GlobalConfig({ - prefix: 'devcert', - key: x => x -}); - -module.exports = { - userCerts, - // treat a certificate as basically expired if it'll expire in 1 day (86400s) - async expired(cert) { - return exec(`openssl x509 -checkend 0 <<< "${cert}"`) - .then(() => false) - .catch(({ stdout }) => stdout.trim() === 'Certificate will expire'); - }, - async provide(commonName) { - if (typeof commonName !== 'string') { - throw Error( - debug.errorMsg( - `Must provide a commonName to SSLCertStore.provide(). Instead, argument was ${commonName}` - ) - ); - } - let certPair = await userCerts.get(commonName); - if (certPair && (await this.expired(certPair.cert))) { - certPair = null; - await userCerts.del(commonName); - } - if (!certPair) { - certPair = await this.create(commonName); - await userCerts.set(commonName, certPair); - } - return certPair; - }, - async create(commonName) { - try { - const cert = await runAsRoot( - 'Creating and trusting an SSL certificate for local dev requires temporary administrative privileges.\n Enter password for %u on %H: ', - /* istanbul ignore next: this runs out of band in another process, hard to test */ - async name => { - const devcert = require('@magento/devcert'); - const certs = await devcert(name); - process.stdout.write(JSON.stringify(certs)); - }, - commonName - ); - return JSON.parse(cert); - } catch (e) { - throw Error( - debug.errorMsg( - `Error generating dev certificate: ${e.message} ${e.stack}` - ) - ); - } - } -}; diff --git a/packages/pwa-devdocs/CHANGELOG.md b/packages/pwa-devdocs/CHANGELOG.md new file mode 100644 index 00000000000..12d1dbf1eff --- /dev/null +++ b/packages/pwa-devdocs/CHANGELOG.md @@ -0,0 +1,12 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +## 1.0.1 (2018-08-13) + + +### Bug Fixes + +* **dev:** merge 'assets' and 'output' paths ([36d8157](https://github.com/magento-research/pwa-studio/commit/36d8157)) diff --git a/packages/pwa-devdocs/package.json b/packages/pwa-devdocs/package.json index 2b0df5b27e6..9c5b7df1ea9 100644 --- a/packages/pwa-devdocs/package.json +++ b/packages/pwa-devdocs/package.json @@ -1,7 +1,7 @@ { "name": "pwa-devdocs", "private": true, - "version": "2.0.0-rc.1", + "version": "2.0.0-rc2.0.5", "description": "A documentation site for Magento PWA", "main": "gulpfile.js", "dependencies": { diff --git a/packages/pwa-devdocs/src/_data/top-nav.yml b/packages/pwa-devdocs/src/_data/top-nav.yml index 139f3e3ec68..9b46eeab9ff 100644 --- a/packages/pwa-devdocs/src/_data/top-nav.yml +++ b/packages/pwa-devdocs/src/_data/top-nav.yml @@ -7,5 +7,5 @@ - label: UI Components url: /peregrine/ -- label: Venia Theme +- label: Venia Storefront url: /venia-pwa-concept/ diff --git a/packages/pwa-devdocs/src/_data/venia-pwa-concept.yml b/packages/pwa-devdocs/src/_data/venia-pwa-concept.yml index 9e79ccb49f5..6b081c472bd 100644 --- a/packages/pwa-devdocs/src/_data/venia-pwa-concept.yml +++ b/packages/pwa-devdocs/src/_data/venia-pwa-concept.yml @@ -1,9 +1,9 @@ -title: Venia Theme (Concept) +title: Venia Storefront entries: - label: Overview url: /venia-pwa-concept/ - - label: Venia theme setup + - label: Venia storefront setup url: /venia-pwa-concept/setup/ - label: Simple REST checkout flow @@ -97,4 +97,4 @@ entries: url: /venia-pwa-concept/component/sort/ - label: Title bar - url: /venia-pwa-concept/component/title-bar/ \ No newline at end of file + url: /venia-pwa-concept/component/title-bar/ diff --git a/packages/pwa-devdocs/src/index.html b/packages/pwa-devdocs/src/index.html index 172cc27fa2f..87908cdf2b7 100644 --- a/packages/pwa-devdocs/src/index.html +++ b/packages/pwa-devdocs/src/index.html @@ -18,9 +18,9 @@

Overview

-

Setup Venia Theme

+

Setup Venia Storefront

- Setup a development environment for the proof-of-concept Magento theme built using PWA Buildpack tools and Peregrine components. + Setup a development environment for the Venia storefront reference theme built using PWA Buildpack tools and Peregrine components.

@@ -56,7 +56,7 @@

Tools and Libraries

Learn UI Components

- Use, extend, or remix Peregrine UI components to create a unique Magento PWA theme + Use, extend, or remix Peregrine UI components to create a unique Magento PWA storefront

@@ -119,7 +119,7 @@

Venia Theme source

- Project source code for a conceptual and customizable PWA theme for Magento 2. + Project source code for a conceptual and customizable PWA storefront for Magento 2. It is built using the Peregrine component library and Buildpack tools.

diff --git a/packages/pwa-devdocs/src/pwa-buildpack/troubleshooting/index.md b/packages/pwa-devdocs/src/pwa-buildpack/troubleshooting/index.md index ab71f705eef..8c062dd2d6b 100644 --- a/packages/pwa-devdocs/src/pwa-buildpack/troubleshooting/index.md +++ b/packages/pwa-devdocs/src/pwa-buildpack/troubleshooting/index.md @@ -16,6 +16,8 @@ Paste the result console output into the issue. Thank you! ## Common issues +* [Validation errors when running developer mode](#validation-errors) +* [Venia queries to GraphQL produce validation errors](#graphql-validation-errors) * [Browser displays "Cannot proxy to " error and the console displays `ENOTFOUND`](#cannot-proxy) * [Webpack hangs for a long time before beginning compilation](#webpack-hangs) * [Browser cannot resolve the `.local.pwadev` site](#cannot-resolve-site) @@ -23,6 +25,22 @@ Paste the result console output into the issue. Thank you! ## Resolutions +**Validation errors when running developer mode**{:#validation-errors} + +Make sure you copied over the `example.env` file into a new `.env` file in the `packages/venia-concept` directory. +This file should specify variables for your local development environment. + +**Venia queries to GraphQL produce validation errors**{:#graphql-validation-errors} + +Venia and its GraphQL queries are out of sync with the schema of the connected Magentoi instance. +Make sure your Magento instance is up to date with the latest from Magento 2.3 development branch. + +To test whether your queries are up to date, run the following command in the project root: + +``` sh +npm run validate:venia:gql +``` + **Browser displays "Cannot proxy to " error and the console displays `ENOTFOUND`**{:#cannot-proxy} Make sure your Magento store loads in more than one browser. @@ -71,4 +89,4 @@ You can install higher versions of OpenSSL with [Homebrew] on OSX, [Chocolatey] [create an issue]: https://github.com/magento-research/pwa-buildpack/issues [Slack channel]: https://magentocommeng.slack.com/messages/C71HNKYS2/team/UAFV915FB/ [Homebrew]: https://brew.sh/ -[Chocolatey]: https://chocolatey.org/ \ No newline at end of file +[Chocolatey]: https://chocolatey.org/ diff --git a/packages/pwa-devdocs/src/venia-pwa-concept/setup/index.md b/packages/pwa-devdocs/src/venia-pwa-concept/setup/index.md index 579852046d6..ede4f01291b 100644 --- a/packages/pwa-devdocs/src/venia-pwa-concept/setup/index.md +++ b/packages/pwa-devdocs/src/venia-pwa-concept/setup/index.md @@ -1,11 +1,11 @@ --- -title: Venia theme setup +title: Venia storefront setup --- -Venia is a PWA theme that runs on top of an existing Magento 2 backend. -Follow the instructions on this page to setup and install the [Venia PWA concept theme][] in your Magento 2 instance. +Venia is a PWA storefront that runs on top of an existing Magento 2 backend. +Follow the instructions on this page to setup and install the [Venia PWA concept storefront][] in your Magento 2 instance. -At the end of this tutorial, you will have a working copy of the Venia theme installed and running on top of Magento. +At the end of this tutorial, you will have a working copy of the Venia storefront installed and running on top of Magento. Use this setup to explore or develop Magento PWA components and themes. If you experience problems with the project setup, see [Troubleshooting][] in the PWA Buildpack section. @@ -21,9 +21,64 @@ If you experience problems with the project setup, see [Troubleshooting][] in th * Magento 2 installed using [valet-plus][] * [Vagrant Box for Magento 2 developers][] +## Step 1. Install Venia sample data +The Venia storefront works best with the new Venia sample data modules installed. -## Step 1. Clone repository +{: .bs-callout .bs-callout-warning} +If you have the previous `magento2-sample-data` module installed, you need to [remove the sample data modules][] and re-install Magento with a clean database. + +In your Magento installation root directory, create a file called `deployVeniaSampleData.sh` with the following content: + +``` sh +#!/usr/bin/env bash +composer='/usr/bin/env composer' +composerParams='--no-interaction --ansi' +moduleVendor='magento' +moduleList=( + module-catalog-sample-data-venia + module-configurable-sample-data-venia + module-customer-sample-data-venia + module-sales-sample-data-venia + module-tax-sample-data-venia + sample-data-media-venia +) +githubBasUrl='git@github.com:PMET-public' + +add_composer_repository () { + name=$1 + type=$2 + url=$3 + echo "adding composer repository ${url}" + ${composer} config ${composerParams} repositories.${name} ${type} ${url} +} + +add_venia_sample_data_repository () { + name=$1 + add_composer_repository ${name} github "${githubBasUrl}/${name}.git" +} + +for moduleName in "${moduleList[@]}" +do + add_venia_sample_data_repository ${moduleName} +done + +${composer} require ${composerParams} $(printf "${moduleVendor}/%s:dev-master@dev " "${moduleList[@]}") +``` + +Execute this script to add the Venia sample data packages to the project: + +``` sh +bash deployVeniaSampleData.sh +``` + +Run the following command to install the Venia data from the modules: + +``` +bin/magento setup:upgrade +``` + +## Step 2. Clone the PWA Studio repository Clone the [PWA Studio] repository into your development environment. @@ -64,14 +119,6 @@ If you clone the PWA Studio project repo into the `magento2ce` directory of the ``` yml ce: "https://github.com/magento/magento2.git::2.3-develop" ``` - And if you want to pull the sample data (optional), update: - ``` yml - ce_sample_data: "git@github.com:magento/magento2-sample-data.git" - ``` - to - ``` yml - ce_sample_data: "https://github.com/magento/magento2-sample-data.git::2.3-develop" - ``` 4. In that same file, update the PHP version to 7.1 by updating the following line: ``` yml php_version: "7.0" @@ -90,7 +137,10 @@ If you clone the PWA Studio project repo into the `magento2ce` directory of the ``` -## Step 2. Install PWA Studio dependencies +## Step 3. Install PWA Studio dependencies + +{: .bs-callout .bs-callout-warning} +If you have an existing `node_modules` directory from a previous PWA Studio version installation, remove it to prevent installation errors. In the PWA Studio project's root directory, run the following command to install the project dependencies: @@ -98,87 +148,71 @@ In the PWA Studio project's root directory, run the following command to install npm install ``` -## Step 3. Link and install module +## Step 4. Set environment variables -Navigate to your Magento installation's `app/code/Magento` directory and create a `Pwa` symlink folder linking to the project's `module` directory. +Under the `packages/venia-concept` directory, copy `.env.dist` into a new `.env` file: -**Example command:** +**Example commands:** ``` sh -ln -s /Users/magedev/pwa-studio/packages/pwa-module Pwa +cd /Users/magedev/pwa-studio/packages/venia-concept ``` -Or from your Magento 2 root ``` sh -ln -s pwa-studio/packages/pwa-module app/code/Magento/Pwa +cp .env.dist .env ``` -### Enable and install - -Navigate to your Magento installation's root director and run the following command to enable the module: +In the `.env` file set the value of `MAGENTO_BACKEND_DOMAIN` to the URL of your Magento development store. -``` sh -bin/magento module:enable Magento_Pwa +**Example:** +``` text +MAGENTO_BACKEND_DOMAIN="https://magento.test/" ``` -Install the module using the following command: -``` sh -bin/magento setup:upgrade -``` +## Step 5. Start the server -## Step 4. Link theme directory +Use any of the following commands from the **project root directory** to start the server: -Navigate to your Magento installation's `app/design/frontend/Magento` directory and create a `venia` symlink folder linking to the project's `theme-frontend-venia` directory. +`npm run watch:venia` -**Example command:** -``` sh -ln -s /Users/magedev/pwa-studio/packages/venia-concept venia -``` +: Starts the Venia storefront development environment. -## Step 5. Activate the Venia theme +`npm run watch:all` -Browse to the Admin section of your Magento store and configure it to use the **Magento Venia** theme. -You can find this configuration using the **Configuration** link in the **Content** tab. +: Runs the full PWA Studio developer experience, which include Venia hot-reloading and concurrent Buildpack/Peregrine rebuilds. -## Step 6. Set environment variables +`npm run build && npm run stage:venia` -Under the Venia project's `theme-frontend-venia` directory, copy `.env.dist` into a new `.env` file and update the variables with the URL to your Magento development store. +: Generates build artifacts and runs the staging environment, which uses more compressed assets and more closely reflects production. -**Example commands:** -``` sh -cd /Users/magedev/pwa-studio/packages/venia-concept -``` -``` sh -cp .env.dist .env -``` - -## Step 7. Start the development server +### Browsing to the application -Use the following command to start the development server: +After the development server is up and running, look for a similar line in the terminal output (the port may differ for your instance): ``` sh -npm start +PWADevServer ready at https://magento-venia.local.pwadev:8001 ``` -{: .bs-callout .bs-callout-info} -**Note:** -Some users have reported using `sudo npm start` to get around permissions. - -After the development server is up and running, look for a similar line in the terminal output (the port will differ for your instance): +OR ``` sh -Project is running at https://magento-venia.local.pwadev:8000/ +Launching staging server... + +https://magento-venia.local.pwadev:51828/ + +Staging server running at the address above. ``` -This is the new address for your PWA frontend. +This is the address for your PWA frontend. You can still use the old address to access the Admin section of Magento, but for PWA development on the frontend, use this new address. -Congratulations! You have set up your development environment for the Venia theme project. +Congratulations! You have set up your development environment for the Venia storefront project. -[Venia PWA concept theme]: https://github.com/magento-research/pwa-studio/tree/master/packages/venia-concept +[Venia PWA concept storefront]: https://github.com/magento-research/pwa-studio/tree/master/packages/venia-concept [Node Package Manager]: https://www.npmjs.com/ [NodeJS 8.x LTS]: https://nodejs.org/en/ [Vagrant Box for Magento 2 developers]: https://github.com/paliarush/magento2-vagrant-for-developers [Troubleshooting]: {{ site.baseurl }}{% link pwa-buildpack/troubleshooting/index.md %} [PWA Studio]: https://github.com/magento-research/pwa-studio [local development instance]: https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html -[valet-plus]: https://github.com/weprovide/valet-plus \ No newline at end of file +[valet-plus]: https://github.com/weprovide/valet-plus +[remove the sample data modules]: https://devdocs.magento.com/guides/v2.3/install-gde/install/cli/install-cli-sample-data-other.html#inst-sample-remove diff --git a/packages/pwa-module/Block/Bundle.php b/packages/pwa-module/Block/Bundle.php deleted file mode 100644 index 9fc55e0613f..00000000000 --- a/packages/pwa-module/Block/Bundle.php +++ /dev/null @@ -1,81 +0,0 @@ -webpackConfig = $webpackConfig; - $this->state = $state; - -// $om = \Magento\Framework\App\ObjectManager::getInstance(); -// $mode = \Magento\Framework\App\ObjectManager::getInstance()->create(\Magento\Framework\App\State::class)->getMode(); -// $page = $om->get('Magento\Framework\View\Page\Config'); -// -// if ($mode == 'production') { -// $page->addPageAsset('bundles/vendor.js'); -// $page->addPageAsset('bundles/client.js'); -// } - - } - - /** - * @return string - */ - public function getMode(): string - { - return $this->state->getMode(); - } - - /** - * @return string - */ - public function getBundleUrl(): string - { - $mode = $this->getMode(); - if ($mode === 'development') { - return $this->webpackConfig->getDevServerHost() . $this->webpackConfig->getPublicAssetPath() . 'client.js'; - } - - return ''; - } -} diff --git a/packages/pwa-module/Controller/Index/Js.php b/packages/pwa-module/Controller/Index/Js.php deleted file mode 100644 index ecb2a640096..00000000000 --- a/packages/pwa-module/Controller/Index/Js.php +++ /dev/null @@ -1,81 +0,0 @@ -webpackConfig = $webpackConfig; - $this->jsFileResultFactory = $jsFileResultFactory; - } - - /** - * @inheritdoc - */ - public function execute() - { - $filePath = implode(DIRECTORY_SEPARATOR, [ - $this->webpackConfig->getThemePath(), - 'web', - 'js', - $this->webpackConfig->getServiceWorkerFileName() - ]); - - if (file_exists($filePath)) { - $result = $this->jsFileResultFactory->create(); - $result->setHttpResponseCode(200); - $result->sendJSFile($filePath); - return $result; - } - - /** @var Raw $result */ - $result = $this->resultFactory->create(ResultFactory::TYPE_RAW); - $result->setHttpResponseCode(404); - $result->setContents('404: Could not find ' . $this->webpackConfig->getServiceWorkerFileName()); - - return $result; - } -} diff --git a/packages/pwa-module/Controller/Index/WebpackConfigEndpoint.php b/packages/pwa-module/Controller/Index/WebpackConfigEndpoint.php deleted file mode 100644 index 27f97fd1e49..00000000000 --- a/packages/pwa-module/Controller/Index/WebpackConfigEndpoint.php +++ /dev/null @@ -1,52 +0,0 @@ -webpackConfig = $webpackConfig; - } - - /** - * @inheritdoc - */ - public function execute() - { - /** @var Json $result */ - $result = $this->resultFactory->create(ResultFactory::TYPE_JSON); - $result->setHttpResponseCode(200); - $result->setData($this->webpackConfig); - return $result; - } -} diff --git a/packages/pwa-module/Controller/Router.php b/packages/pwa-module/Controller/Router.php deleted file mode 100644 index 6e140a44307..00000000000 --- a/packages/pwa-module/Controller/Router.php +++ /dev/null @@ -1,65 +0,0 @@ -actionFactory = $actionFactory; - $this->webpackConfig = $webpackConfig; - } - - /** - * @inheritdoc - */ - public function match(RequestInterface $request) - { - $rootPath = trim($request->getPathInfo(), '/'); - if ($rootPath == $this->webpackConfig->getServiceWorkerFileName()) { - $request->setModuleName('pwa') - ->setControllerName('index') - ->setActionName('js'); - return $this->actionFactory->create( - Forward::class - ); - } - if ($rootPath == "webpack-config.json") { - $request->setModuleName('pwa') - ->setControllerName('index') - ->setActionName('webpackconfigendpoint'); - return $this->actionFactory->create( - Forward::class - ); - } - return null; - } -} diff --git a/packages/pwa-module/Helper/WebpackConfig.php b/packages/pwa-module/Helper/WebpackConfig.php deleted file mode 100644 index 1d24acc238b..00000000000 --- a/packages/pwa-module/Helper/WebpackConfig.php +++ /dev/null @@ -1,331 +0,0 @@ -viewConfig = $viewConfig; - $this->directoryList = $directoryList; - $this->themeProvider = $themeProvider; - $this->scopeConfig = $scopeConfig; - $this->storeManager = $storeManager; - $this->assetRepo = $assetRepo; - $this->baseUrl = $baseUrl; - } - - /** - * Get the base origin of the store. - * - * @return string - */ - public function getStoreOrigin(): string - { - if (empty($this->storeOrigin)) { - $this->storeOrigin = $this->baseUrl->getBaseUrl(['_secure' => true]); - } - - return $this->storeOrigin; - } - - /** - * Get the public URL path of the service worker - * - * @return string - */ - public function getPublicAssetPath(): string - { - if (empty($this->publicAssetPath)) { - $this->publicAssetPath = "/" . trim(str_replace( - $this->getStoreOrigin(), - "", - $this->baseUrl->getBaseUrl(['_type' => UrlInterface::URL_TYPE_STATIC, '_secure' => true]) . - $this->assetRepo->createAsset("/")->getPath() - ), "/") . "/"; - } - - return $this->publicAssetPath; - } - - /** - * Get the name of the service worker file - * @return string - */ - public function getServiceWorkerFileName(): string - { - if (empty($this->serviceWorkerFileName)) { - $this->serviceWorkerFileName = (string) $this->_getVarOrFallback( - self::SERVICE_WORKER_NAME_VAR, - self::DEFAULT_SERVICE_WORKER_NAME - ); - } - return $this->serviceWorkerFileName; - } - - /** - * Get the absolute filesystem path of the theme - * - * @return string - * @throws FileSystemException - */ - public function getThemePath(): string - { - if (empty($this->themePath)) { - $this->themePath = implode(DIRECTORY_SEPARATOR, [ - $this->directoryList->getPath('app'), - "design", - $this->_getTheme()->getFullPath() - ]); - } - - return $this->themePath; - } - - /** - * @return string - */ - public function getDevServerHostname(): string - { - if (empty($this->devServerHostname)) { - $this->devServerHostname = (string) $this->_getVarOrFallback( - self::DEVSERVER_HOSTNAME_VAR, - self::DEFAULT_DEVSERVER_HOSTNAME - ); - } - - return $this->devServerHostname; - } - - /** - * @return string - */ - public function getDevServerPort(): string - { - if (empty($this->devServerPort)) { - $this->devServerPort = (string) $this->_getVarOrFallback( - self::DEVSERVER_PORT_VAR, - self::DEFAULT_DEVSERVER_PORT - ); - } - - return $this->devServerPort; - } - - /** - * Get the configured local webpack-dev-server hostname - * @return string - */ - public function getDevServerHost(): string - { - // TODO: proper URL builder - return self::DEVSERVER_PROTOCOL . "://" . $this->getDevServerHostname() . ":" . $this->getDevServerPort(); - } - - /** - * @return array - * @throws ReflectionException - */ - public function jsonSerialize(): array - { - $class = new ReflectionClass(self::class); - $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC); - $properties = []; - foreach ($methods as $method) { - $name = $method->getName(); - if (preg_match('/^get[A-Z0-9]/', $name)) { - $propName = lcfirst(substr($name, 3)); - $properties[$propName] = $method->invoke($this); - } - } - - return $properties; - } - - /** - * Get the currently active theme instance - * - * @return ThemeInterface - * @throws NoSuchEntityException - */ - private function _getTheme(): ThemeInterface - { - - $themeId = $this->scopeConfig->getValue( - DesignInterface::XML_PATH_THEME_ID, - ScopeInterface::SCOPE_STORE, - $this->storeManager->getStore()->getId() - ); - - return $this->themeProvider->getThemeById($themeId); - } - - /** - * Get a variable from view.xml or fall back to a default value - * - * @param string $name variable name - * @param string $fallback value if var is null - * @return string|false - */ - private function _getVarOrFallback($name, $fallback) - { - $varValue = $this->viewConfig->getViewConfig()->getVarValue(self::PWA_MODULE_NAME, $name); - return empty($varValue) ? $fallback : $varValue; - } -} diff --git a/packages/pwa-module/Model/Result/JsFileResult.php b/packages/pwa-module/Model/Result/JsFileResult.php deleted file mode 100644 index 09cddc89875..00000000000 --- a/packages/pwa-module/Model/Result/JsFileResult.php +++ /dev/null @@ -1,75 +0,0 @@ -file = $file; - } - - /** - * Serve this file from disk. - * - * @param string $path - */ - public function sendJSFile($path) - { - $this->contents = $this->file->read($path); - $this->contentLength = filesize($path); - } - - /** - * {@inheritdoc} - */ - protected function render(HttpResponseInterface $response) - { - $response->setHeader('Content-Type', 'application/json', true) - ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true) - ->setHeader('Pragma', 'public', true) - ->setHeader('Content-Type', 'application/javascript', true) - ->setHeader('Content-Length', $this->contentLength) - ->setBody($this->contents); - return $this; - } -} diff --git a/packages/pwa-module/etc/di.xml b/packages/pwa-module/etc/di.xml deleted file mode 100644 index 82e7ce1b193..00000000000 --- a/packages/pwa-module/etc/di.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - Magento\Pwa\Controller\Router - false - 100 - - - - - diff --git a/packages/pwa-module/etc/frontend/routes.xml b/packages/pwa-module/etc/frontend/routes.xml deleted file mode 100644 index 13e0af481bc..00000000000 --- a/packages/pwa-module/etc/frontend/routes.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/packages/pwa-module/etc/module.xml b/packages/pwa-module/etc/module.xml deleted file mode 100644 index a97547dea69..00000000000 --- a/packages/pwa-module/etc/module.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/packages/pwa-module/package.json b/packages/pwa-module/package.json deleted file mode 100644 index d0359b478c5..00000000000 --- a/packages/pwa-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "pwa-module", - "private": true -} diff --git a/packages/pwa-module/registration.php b/packages/pwa-module/registration.php deleted file mode 100644 index 89e24330e54..00000000000 --- a/packages/pwa-module/registration.php +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/packages/upward-js/README.md b/packages/upward-js/README.md new file mode 100644 index 00000000000..16df0ffd4ec --- /dev/null +++ b/packages/upward-js/README.md @@ -0,0 +1,60 @@ +# upward-js + +Reference implementation of the [UPWARD](../upward-spec) protocol in JavaScript. Runs as a standalone server or as an Express/Connect middleware. + +## Installation + +`npm install @magento/upward-js` + +### Usage + +Your UPWARD definition file drives the behavior of upward-js. Create that definition as a YAML file and save it in your project. + +You can use `upward-js` from the command line, through the server API, or through the middleware API. + +#### Command Line + +The `upward-js-server` command will become globally available if you install globally: `npm install -g @magento/upward-js`. + +The server takes no arguments; instead it is configured by environment variables. At minimum, the environment variable `UPWARD_JS_UPWARD_PATH` must be set to the path of your definition file, and `UPWARD_JS_BIND_LOCAL` must be set to 1. + +The server launches in the foreground and if `UPWARD_JS_LOG_URL` is set to 1, it prints its bound URL to stdout. + +```sh +$ UPWARD_JS_BIND_LOCAL=1 UPWARD_JS_UPWARD_PATH=./my-upward-server.yml upward-js-server + https://0.0.0.0:23651 +``` + +#### Server API + +Import the server into your Node script. + +```js + +const { server } = require('@magento/upward-js'); + +const { app } = upward({ + upwardPath: './my-upward-server.yml' +}) + +app.listen(8000); +``` + +#### Middleware API + +The middleware is compatible with Express 4, Connect, and other frameworks compatible with this common pattern. It returns a Promise for a function which handles request/response pairs. + +```js + +const express = require('express'); +const { middleware } = require('@magento/upward-js'); + +const app = express(); + +app.use(otherMiddlewaresMaybe); + +app.use(middleware('./my-upward-server.yml')); + +``` + +Optionally, you may pass an `IOAdapter` as a second argument to the middleware. diff --git a/packages/upward-js/bin/server b/packages/upward-js/bin/server new file mode 100755 index 00000000000..74348e2e845 --- /dev/null +++ b/packages/upward-js/bin/server @@ -0,0 +1,9 @@ +#!/usr/bin/env node +require('dotenv').config(); + +const config = require('../lib/envToConfig')(); + +require('../lib/createUpwardServer.js')(config).catch(e => { + console.error(e.stack); + process.exit(e.errno || 1); +}); diff --git a/packages/upward-js/jest.config.js b/packages/upward-js/jest.config.js new file mode 100644 index 00000000000..d79f925647a --- /dev/null +++ b/packages/upward-js/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + displayName: 'Upward JS', + clearMocks: true, + testEnvironment: 'node' +}; diff --git a/packages/upward-js/lib/Context.js b/packages/upward-js/lib/Context.js new file mode 100644 index 00000000000..3ceae4faa95 --- /dev/null +++ b/packages/upward-js/lib/Context.js @@ -0,0 +1,115 @@ +const debug = require('debug')('upward-js:Context'); +const { pick } = require('lodash'); +const { URL } = require('url'); + +const ContextPath = require('./ContextPath'); + +const statusCodes = Array.from({ length: 600 }, (_, i) => i + 100); +const constants = new Set([ + true, + false, + 'GET', + 'POST', + 'mustache', + 'text/html', + 'text/plain', + 'application/json', + 'utf-8', + 'utf8', + 'latin-1', + 'base64', + 'binary', + 'hex', + ...statusCodes, + ...statusCodes.map(code => code.toString()) +]); + +class Context { + static fromRequest(env, request) { + debug('generating from request: %s', request.url); + const hostedUrl = new URL(request.url, `http://${request.get('host')}`); + debug('url derived from host is %O', hostedUrl); + const url = pick(hostedUrl, [ + 'host', + 'hostname', + 'port', + 'pathname', + 'path', + 'search', + 'searchParams' + ]); + url.query = request.query; + return new Context({ + env, + request: { + url, + headers: request.headers, + headerEntries: Object.entries(request.headers).map( + ([name, value]) => ({ name, value }) + ), + queryEntries: Object.entries(url.query).map( + ([name, value]) => ({ name, value }) + ) + } + }); + } + + constructor(data) { + this._data = data; + this._promises = new Map(); + } + + setVisitor(visitor) { + this.visitor = visitor; + } + + async get(lookup) { + const path = ContextPath.from(lookup); + debug('lookup %s at path %s', lookup, path); + if (constants.has(path.toString())) { + debug('%s is a constant', lookup); + return lookup; + } + const base = path.base(); + debug('%s is from context base %s', lookup, base); + if (!this._data.hasOwnProperty(base)) { + debug('%s not yet assigned, acquiring promise handle', base); + let promise = this._promises.get(base); + if (!promise) { + debug('%s has never been requested, visiting from root', base); + promise = this.visitor.downward([base]).then(value => { + if (typeof value === 'function') { + debug( + '%s assigned to context as function: %o', + base, + value + ); + this.set(base, value); + } else { + debug('%s assigned: %o', base, value[base]); + this.set(base, value[base]); + } + }); + this._promises.set(base, promise); + } + await promise; + } + return path.getFrom(this._data); + } + + set(base, value, override) { + const isSet = constants.has(base) || this._data.hasOwnProperty(base); + if (isSet && !override) { + throw new Error( + `Attempted to reassign context property '${base}' to '${value}'. Context properties cannot be reassigned.` + ); + } + this._data[base] = value; + } + + forget(base) { + delete this._data[base]; + } +} + +module.exports = Context; diff --git a/packages/upward-js/lib/ContextPath.js b/packages/upward-js/lib/ContextPath.js new file mode 100644 index 00000000000..546bbe85f3c --- /dev/null +++ b/packages/upward-js/lib/ContextPath.js @@ -0,0 +1,104 @@ +const debug = require('debug')('upward-js:ContextPath'); +// Tests for dot-separated strings of "word characters" or slashes. +// Slashes so that MIME type constants, like "text/html", can be legal values. +// "Word characters" are [a-zA-Z0-9_]. +const illegalPathChars = /(^[\.\[\]])|[^\.\w\$\/]/; +const contextPathCache = new Map(); +class ContextPath { + static from(lookup) { + if (lookup instanceof ContextPath) { + return lookup; + } + if (typeof lookup !== 'string') { + throw new Error( + `Internal error: Cannot build ContextPath from non-string ${lookup}` + ); + } + if (illegalPathChars.test(lookup)) { + throw new Error( + `Illegal context property name found: ${lookup}\nContext properties must be dot-separated strings and contain only letters, numbers, and underscores, and cannot begin with a dot.` + ); + } + if (contextPathCache.has(lookup)) { + return contextPathCache.get(lookup); + } + const segments = lookup.split('.'); + const path = segments.reduce( + (parent, newSegment) => parent.extend(newSegment), + ContextPath.root + ); + contextPathCache.set(lookup, path); + return path; + } + extend(newSegment) { + const fullPath = this._segments.concat(newSegment); + const fullPathString = fullPath.join('.'); + let path = contextPathCache.get(fullPathString); + if (!path) { + path = new ContextPath(fullPath); + contextPathCache.set(fullPathString, path); + } + return path; + } + constructor(segments) { + this._segments = segments; + } + base() { + return this._segments[0]; + } + getFrom(obj) { + let current = obj; + for (const segment of this._segments) { + if (Array.isArray(current)) { + const index = Number(segment); + if (!isNaN(index)) { + debug('array index %d yields %o', segment, current[index]); + if (current.length < index - 1) { + return ''; + } + current = current[index]; + } else { + throw new Error( + `Attempted non-integer lookup on a list: ${current} [${segment}]` + ); + } + } else if (current === undefined || current === null) { + current = ''; + break; + } else if (typeof current === 'object') { + current = current[segment]; + } else { + break; + } + } + debug( + 'traverse %j yielded %j from %j', + this._segments, + current, + obj[this.base()] + ); + return current; + } + // contains(otherPath) { + // return this._segments.every((segment, i) => otherPath.keyAt(i) === i); + // } + // containsSegment(segment) { + // return this._segments.some(mySegment => mySegment === segment); + // } + // depth() { + // return this._segments.length; + // } + // keyAt(index) { + // return this._segments[index]; + // } + // relative(ancestor) { + // return this._segments.slice(ancestor.depth()); + // } + toString() { + return this._segments.join('.'); + } +} + +ContextPath.root = new ContextPath([]); + +module.exports = ContextPath; diff --git a/packages/upward-js/lib/IOAdapter.js b/packages/upward-js/lib/IOAdapter.js new file mode 100644 index 00000000000..2edc4c0892f --- /dev/null +++ b/packages/upward-js/lib/IOAdapter.js @@ -0,0 +1,59 @@ +const debug = require('debug')('upward-js:IOAdapter'); +const containsPath = require('contains-path'); +const { resolve, dirname } = require('path'); +const { readFile: fsReadFile } = require('fs'); +const { promisify } = require('util'); + +const readFile = promisify(fsReadFile); +class IOAdapter { + static default(upwardPath) { + debug(`creating default IO from ${upwardPath}`); + const baseDir = dirname(upwardPath); + debug(`baseDir ${baseDir}`); + return new IOAdapter({ + networkFetch: require('node-fetch'), + readFile: (filePath, enc) => { + // prevent path traversal above baseDir + const resolvedPath = resolve(baseDir, filePath); + if (!containsPath(resolvedPath, baseDir)) { + throw new Error( + `Cannot read ${resolvedPath} because it is outside ${baseDir}` + ); + } + return readFile(resolvedPath, enc); + } + }); + } + constructor(implementations) { + const missingImpls = ['readFile', 'networkFetch'].reduce( + (missing, method) => + method in implementations + ? missing + : missing + + `Must provide an implementation of '${method}\n`, + '' + ); + if (missingImpls) { + throw new Error(`Error creating IOAdapter:\n${missingImpls}`); + } + Object.assign(this, implementations); + } + /** + * Works like promisified Node `fs.readFile`. (Injected for testability.) + * Cannot traverse below working directory. + * @param {string} path Path of file to read. + * @param {string} [encoding] Character set, e.g. 'utf-8'. + * @return {Promise} Promise for file contents. + */ + async readFile(filePath, encoding) {} //eslint-disable-line + + /** + * Works like `node-fetch`. (Injected for testability.) + * @param {string|URL} URL URL to fetch. + * @param {object} options Fetch options, see node-fetch docs. + * @return {Promise} + */ + async networkFetch(url, options) {} //eslint-disable-line +} + +module.exports = IOAdapter; diff --git a/packages/upward-js/lib/ResolverVisitor.js b/packages/upward-js/lib/ResolverVisitor.js new file mode 100644 index 00000000000..536cb53d69a --- /dev/null +++ b/packages/upward-js/lib/ResolverVisitor.js @@ -0,0 +1,107 @@ +const debug = require('debug')('upward-js:ResolverVisitor'); +const { inspect } = require('util'); +const { ResolverList, ResolversByType } = require('./resolvers'); +const { zipObject } = require('lodash'); +const isPrimitive = require('./isPrimitive'); + +class ResolverVisitor { + constructor(io, rootDefinition, context) { + this.io = io; + this.rootDefinition = rootDefinition; + this.context = context; + this.context.setVisitor(this); + } + async downward(contextNames) { + debug('resolving downward: %o', contextNames); + let passedMiddleware = false; + const valuePromises = contextNames.map(async name => { + const value = await this.upward(this.rootDefinition, name); + if (typeof value === 'function') { + debug( + '%s request returned a function, we are assuming it is a middleware' + ); + passedMiddleware = value; + throw new Error('PASSED_MIDDLEWARE'); + } + return value; + }); + try { + const values = await Promise.all(valuePromises); + return zipObject(contextNames, values); + } catch (e) { + if (e.message === 'PASSED_MIDDLEWARE') { + debug( + `returning middleware from visitor.downward() instead of object` + ); + return passedMiddleware; + } else { + throw e; + } + } + } + async upward(definition, propertyName) { + debug('resolving upward: %s from %o', propertyName, definition); + if (!definition.hasOwnProperty(propertyName)) { + throw new Error( + `Context value '${propertyName}' not defined in ${inspect( + definition + )}.` + ); + } + const defined = definition[propertyName]; + + const resolver = this.getResolverFor(defined, propertyName); + + if (resolver) { + return resolver.resolve(defined); + } + + if (isPrimitive(defined)) { + const definedString = defined.toString(); + debug( + 'defined: %s is primitive, yielding to context.get("%s")', + definedString, + definedString + ); + return this.context.get(definedString); + } + + if (typeof defined !== 'object' || !this.getResolverFailure) { + throw new Error(`Unexpected value in config: ${defined}`); + } else { + throw new Error(this.getResolverFailure); + } + } + getResolverFor(defined, propertyName) { + let Resolver; + for (Resolver of ResolverList) { + const recognized = + Resolver.recognize && Resolver.recognize(defined); + if (recognized) { + return { + resolve: () => new Resolver(this).resolve(recognized) + }; + } + } + if (defined.resolver) { + Resolver = ResolversByType[defined.resolver]; + if (!Resolver) { + this.getResolverFailure = `Unrecognized resolver type: ${ + defined.resolver + }`; + } + } else { + Resolver = ResolverList.find(({ telltale }) => + defined.hasOwnProperty(telltale) + ); + if (!Resolver) { + this.getResolverFailure = `Unrecognized configuration. Could not match a resolver to ${propertyName}: ${inspect( + defined + )}`; + } + } + if (Resolver) return new Resolver(this); + } +} + +module.exports = ResolverVisitor; diff --git a/packages/upward-js/lib/UpwardServerError.js b/packages/upward-js/lib/UpwardServerError.js new file mode 100644 index 00000000000..a7a4ffeac9b --- /dev/null +++ b/packages/upward-js/lib/UpwardServerError.js @@ -0,0 +1,11 @@ +class UpwardServerError extends Error { + get name() { + return 'UpwardServerError'; + } + constructor(originalError, message) { + super(originalError); + this.message += ' -- ' + message; + } +} + +module.exports = UpwardServerError; diff --git a/packages/upward-js/lib/__tests__/Context.test.js b/packages/upward-js/lib/__tests__/Context.test.js new file mode 100644 index 00000000000..2b60dced002 --- /dev/null +++ b/packages/upward-js/lib/__tests__/Context.test.js @@ -0,0 +1,40 @@ +const Context = require('../Context'); + +test('promises are cached', async () => { + const context = new Context({}); + const downward = jest.fn(async () => ({ identity: 'same' })); + context.setVisitor({ downward }); + + await Promise.all([ + expect(context.get('identity')).resolves.toBe('same'), + expect(context.get('identity')).resolves.toBe('same') + ]); + expect(context._promises.size).toBe(1); +}); + +test('cannot set context property twice', () => { + const context = new Context({ immu: 'table' }); + expect(() => + context.set('immu', 'nized') + ).toThrowErrorMatchingInlineSnapshot( + `"Attempted to reassign context property 'immu' to 'nized'. Context properties cannot be reassigned."` + ); +}); + +test('forget() deletes ephemeral data', async () => { + const context = new Context({ mu: 'table' }); + const downward = jest.fn(async () => ({ mu: 'nificent' })); + context.setVisitor({ downward }); + + await expect(context.get('mu')).resolves.toBe('table'); + expect(downward).not.toHaveBeenCalled(); + context.forget('mu'); + await expect(context.get('mu')).resolves.toBe('nificent'); + expect(downward).toHaveBeenCalledWith(['mu']); +}); + +test('constants are always present', async () => { + const context = new Context({}); + await expect(context.get('text/plain')).resolves.toBe('text/plain'); + await expect(context.get('208')).resolves.toBe('208'); +}); diff --git a/packages/upward-js/lib/__tests__/ResolverVisitor.test.js b/packages/upward-js/lib/__tests__/ResolverVisitor.test.js new file mode 100644 index 00000000000..de9ee318e1b --- /dev/null +++ b/packages/upward-js/lib/__tests__/ResolverVisitor.test.js @@ -0,0 +1,93 @@ +const ResolverVisitor = require('../ResolverVisitor'); + +const mockIO = () => ({ + readFile: jest.fn(), + networkFetch: jest.fn() +}); + +const mockContext = () => ({ + setVisitor: jest.fn(), + get: jest.fn() +}); + +test('binds itself to supplied context', async () => { + const context = mockContext(); + const visitor = new ResolverVisitor(null, null, context); + expect(context.setVisitor).toHaveBeenCalledWith(visitor); +}); + +test('.upward() errors on a value not found in definition', async () => { + const visitor = new ResolverVisitor(null, null, mockContext()); + await expect(visitor.upward({}, 'foo')).rejects.toThrow( + "Context value 'foo' not defined" + ); +}); + +test('.upward() derives resolvers from shortcut strings', async () => { + const io = mockIO(); + const context = mockContext(); + context.get.mockRejectedValue('Should have resolved FileResolver shortcut'); + io.readFile.mockReturnValueOnce('sepia'); + const visitor = new ResolverVisitor(io, null, context); + await expect( + visitor.upward({ cuttlefish: './ink' }, 'cuttlefish') + ).resolves.toEqual('sepia'); + expect(io.readFile).toHaveBeenCalledWith('./ink', 'utf8'); + expect(context.get).not.toHaveBeenCalled(); +}); + +test('.upward() gets primitive from context', async () => { + const context = mockContext(); + context.get.mockResolvedValueOnce('green'); + const visitor = new ResolverVisitor(null, null, context); + await expect(visitor.upward({ foo: 'bar' }, 'foo')).resolves.toBe('green'); + expect(context.get).toHaveBeenCalledWith('bar'); +}); + +test('.upward() errors on a non-primitive, non-object value', async () => { + const visitor = new ResolverVisitor(null, null, mockContext()); + await expect(visitor.upward({ foo: () => {} }, 'foo')).rejects.toThrow( + 'Unexpected value' + ); +}); + +test('.upward() finds resolvers using `resolver` property', async () => { + const visitor = new ResolverVisitor(mockIO(), null, mockContext()); + await expect( + visitor.upward( + { foo: { resolver: 'inline', inline: 'fighters' } }, + 'foo' + ) + ).resolves.toEqual('fighters'); +}); + +test('.upward() throws on an unrecognized `resolver` property', async () => { + const visitor = new ResolverVisitor(mockIO(), null, mockContext()); + await expect( + visitor.upward({ foo: { resolver: 'wat', no: 'really?' } }, 'foo') + ).rejects.toThrow('Unrecognized resolver type'); +}); + +test('.upward() derives resolver from telltale property', async () => { + const visitor = new ResolverVisitor(mockIO(), null, mockContext()); + await expect( + visitor.upward({ foo: { inline: 'fighters' } }, 'foo') + ).resolves.toEqual('fighters'); +}); + +test('.upward() throws if it cannot derive a resolver strategy', async () => { + const visitor = new ResolverVisitor(mockIO(), null, mockContext()); + await expect( + visitor.upward({ foo: { hopeless: 'case' } }, 'foo') + ).rejects.toThrow('Unrecognized configuration. Could not match a resolver'); +}); + +test('.downward() calls visitor.upward() with root definition', async () => { + const context = mockContext(); + context.get.mockResolvedValueOnce('green'); + const visitor = new ResolverVisitor(null, { frog: 'kermit' }, context); + await expect(visitor.downward(['frog'])).resolves.toEqual({ + frog: 'green' + }); + expect(context.get).toHaveBeenCalledWith('kermit'); +}); diff --git a/packages/upward-js/lib/__tests__/buildResponse.test.js b/packages/upward-js/lib/__tests__/buildResponse.test.js new file mode 100644 index 00000000000..d1384228fc2 --- /dev/null +++ b/packages/upward-js/lib/__tests__/buildResponse.test.js @@ -0,0 +1,137 @@ +const buildResponse = require('../buildResponse'); +const { getScenarios } = require('@magento/upward-spec'); + +let scenarios, mockIO; +beforeAll(async () => { + scenarios = await getScenarios(/static\-servers/); + mockIO = { + readFile: jest.fn(scenarios.getResource) + }; +}); + +const stubRequest = () => ({ + url: 'http://example.com/nowhere?special', + query: { + special: undefined + }, + headers: { + host: 'example.com', + 'content-type': 'text/plain' + }, + get(header) { + return this.headers[header]; + } +}); + +test('builds a response from a static definition', async () => { + const definition = await scenarios.getDefinition('hello-inline-only'); + return expect( + buildResponse(mockIO, process.env, definition, stubRequest()) + ).resolves.toMatchObject({ + status: 200, + headers: { + 'content-type': 'text/plain' + }, + body: 'Hello World!!' + }); +}); + +test('handles implicit resolvers', async () => { + const definition = await scenarios.getDefinition( + 'hello-inline-implicit-resolvers' + ); + return expect( + buildResponse(mockIO, process.env, definition, stubRequest()) + ).resolves.toMatchObject({ + status: 200, + headers: { + 'content-type': 'text/plain' + }, + body: 'Hello World, concisely!!' + }); +}); + +test('handles env interpolation', async () => { + const definition = await scenarios.getDefinition('hello-env-interpolation'); + return expect( + buildResponse( + mockIO, + { UPWARD_TEST_RESPONSE_BODY: 'Hello, environment!' }, + definition, + stubRequest() + ) + ).resolves.toMatchObject({ + status: 200, + headers: { + 'content-type': 'text/plain' + }, + body: 'Hello, environment!' + }); +}); + +test('handles inline templates', async () => { + const definition = await scenarios.getDefinition( + 'hello-env-inline-template' + ); + return expect( + buildResponse(mockIO, { ADDRESSEE: 'Earth' }, definition, stubRequest()) + ).resolves.toMatchObject({ + status: 200, + headers: { + 'content-type': 'text/plain' + }, + body: 'Hello, environment of Earth!!' + }); +}); + +test('handles file resolvers and context interpolation', async () => { + const definition = await scenarios.getDefinition( + 'hello-env-context-file-template' + ); + return expect( + buildResponse(mockIO, { sender: 'world' }, definition, stubRequest()) + ).resolves.toMatchObject({ + status: 200, + headers: { + 'content-type': 'text/plain' + }, + body: 'Hello from a world of external templates!!' + }); +}); + +test('handles deep template resolvers, can return json', async () => { + const definition = await scenarios.getDefinition( + 'hello-context-inline-template-json' + ); + const response = await buildResponse( + mockIO, + { + ADDRESSEE: 'deep space' + }, + definition, + stubRequest() + ); + expect(response).toMatchObject({ + status: 200, + headers: { + 'content-type': 'application/json' + } + }); + expect(response.body).toEqual( + JSON.stringify({ + greeting: 'Hello', + subject: 'the depths of deep space...', + shouldYouReallyHandwriteJSON: 'no' + }) + ); +}); + +// test.skip('makes GQL queries'); + +// test.skip('conditionally resolves'); + +// test.skip('conditional resolution falls through in order'); + +// test.skip('conditional resolution yields to default'); + +// test.skip('responds to request data'); diff --git a/packages/upward-js/lib/__tests__/envToConfig.test.js b/packages/upward-js/lib/__tests__/envToConfig.test.js new file mode 100644 index 00000000000..ab6013f5960 --- /dev/null +++ b/packages/upward-js/lib/__tests__/envToConfig.test.js @@ -0,0 +1,16 @@ +const envToConfig = require('../envToConfig'); + +test('converts namespaced env vars to config object and casts values', () => { + Object.assign(process.env, { + UPWARD_JS_ONE_TWO: 'false', + UPWARD_JS_THREE_FOUR: 'trUE', + UPWARD_JS_FIVE_SIX_SEVEN: '9.4', + UPWARD_JS_EIGHTNINE: 'eight and nine' + }); + expect(envToConfig()).toEqual({ + oneTwo: false, + threeFour: true, + fiveSixSeven: 9.4, + eightnine: 'eight and nine' + }); +}); diff --git a/packages/upward-js/lib/__tests__/server.test.js b/packages/upward-js/lib/__tests__/server.test.js new file mode 100644 index 00000000000..b178769a1e5 --- /dev/null +++ b/packages/upward-js/lib/__tests__/server.test.js @@ -0,0 +1,35 @@ +jest.mock('morgan', () => () => (_, __, next) => next()); +const { Server } = require('http'); +const supertest = require('supertest'); +const { getScenarios } = require('@magento/upward-spec'); +const createUpwardServer = require('../createUpwardServer'); + +let upwardPath; +beforeAll(async () => { + upwardPath = (await getScenarios(/static\-servers/)).getResourcePath( + 'hello-inline-only.yml' + ); +}); + +test('returns app alone if bindLocal is false', async () => { + const { app, server } = await createUpwardServer({ upwardPath }); + expect(app).toBeTruthy(); + expect(server).not.toBeDefined(); +}); + +test('returns app and server if bindLocal is true', async () => { + const { app, server, close } = await createUpwardServer({ + upwardPath, + bindLocal: true + }); + expect(app).toBeTruthy(); + expect(server).toBeInstanceOf(Server); + await close(); +}); + +test('responds to requests based on UPWARD config', async () => { + const { app } = await createUpwardServer({ upwardPath }); + const response = await supertest(app).get('/article?articleId=1'); + expect(response.statusCode).toBe(200); + expect(response.text).toEqual('Hello World!!'); +}); diff --git a/packages/upward-js/lib/buildResponse.js b/packages/upward-js/lib/buildResponse.js new file mode 100644 index 00000000000..b785e581454 --- /dev/null +++ b/packages/upward-js/lib/buildResponse.js @@ -0,0 +1,34 @@ +const debug = require('debug')('upward-js:buildResponse'); +const { isPlainObject } = require('lodash'); +const ResolverVisitor = require('./ResolverVisitor'); +const Context = require('./Context'); + +async function buildResponse(io, env, rootDefinition, request) { + debug('creating Context'); + const requestContext = Context.fromRequest(env, request); + debug('creating ResolverVisitor'); + const visitor = new ResolverVisitor(io, rootDefinition, requestContext); + debug('visiting for status, headers, and body'); + try { + const responseData = await visitor.downward([ + 'status', + 'headers', + 'body' + ]); + if (isPlainObject(responseData)) { + debug('successfully built response, %O', responseData); + return { + status: Number(responseData.status), + headers: responseData.headers, + body: responseData.body + }; + } else { + debug('visitor returned request-handling middleware'); + return responseData; + } + } catch (e) { + throw new Error(e.stack); + } +} + +module.exports = buildResponse; diff --git a/packages/upward-js/lib/compiledResources/AbstractCompiledResource.js b/packages/upward-js/lib/compiledResources/AbstractCompiledResource.js new file mode 100644 index 00000000000..6f625134044 --- /dev/null +++ b/packages/upward-js/lib/compiledResources/AbstractCompiledResource.js @@ -0,0 +1,33 @@ +class AbstractCompiledResource { + static get supportedExtensions() { + throw new Error( + 'Internal error: CompiledResources must define static supported file extensions.' + ); + } + constructor(source, io) { + if (this.constructor === AbstractCompiledResource) { + throw new Error( + 'Internal error: Cannot instantiate AbstractCompiledResource directly' + ); + } + if (typeof source !== 'string') { + throw new Error( + `Must construct a CompiledResource with string source. Was supplied a ${typeof source}: ${source}` + ); + } + this.source = source; + this.io = io; + } + async compile() { + throw new Error( + 'Internal error: CompiledResources must define a compile method.' + ); + } + async render() { + throw new Error( + 'Internal error: CompiledResources must define a render method.' + ); + } +} + +module.exports = AbstractCompiledResource; diff --git a/packages/upward-js/lib/compiledResources/GraphQLDocument.js b/packages/upward-js/lib/compiledResources/GraphQLDocument.js new file mode 100644 index 00000000000..ebe78608b4c --- /dev/null +++ b/packages/upward-js/lib/compiledResources/GraphQLDocument.js @@ -0,0 +1,16 @@ +const AbstractCompiledResource = require('./AbstractCompiledResource'); +const gql = require('graphql-tag'); + +class GraphQLDocument extends AbstractCompiledResource { + static get supportedExtensions() { + return ['.graphql', '.gql']; + } + async compile() { + this._contents = gql(this.source); + } + async render() { + return this._contents; + } +} + +module.exports = GraphQLDocument; diff --git a/packages/upward-js/lib/compiledResources/MustacheTemplate.js b/packages/upward-js/lib/compiledResources/MustacheTemplate.js new file mode 100644 index 00000000000..f0bc0eedda4 --- /dev/null +++ b/packages/upward-js/lib/compiledResources/MustacheTemplate.js @@ -0,0 +1,100 @@ +const { inspect } = require('util'); +const AbstractCompiledResource = require('./AbstractCompiledResource'); +const Hogan = require('hogan.js'); + +class MustacheTemplate extends AbstractCompiledResource { + static get supportedExtensions() { + return ['.mst', '.mustache', '.tpt']; + } + constructor(...args) { + super(...args); + if (!this.io) { + throw new Error('IOInterface as second argument'); + } + if (typeof this.io.readFile !== 'function') { + throw new Error( + `IOInterface missing readFile method: ${inspect(this.io)}` + ); + } + this._loadedPartials = new Map(); + } + _tryLoadAllExtensions( + name, + extensions = MustacheTemplate.supportedExtensions + ) { + return this.io.readFile(name + extensions[0], 'utf8').catch(e => { + if (e.code !== 'ENOENT' || extensions.length === 1) { + throw e; + } + return this._tryLoadAllExtensions(name, extensions.slice(1)); + }); + } + _findUnloadedPartialNames(template) { + const partialNames = Object.values(template.partials).map( + ({ name }) => name + ); + const uniquePartialNames = [...new Set(partialNames)]; + return uniquePartialNames.filter( + name => !this._loadedPartials.has(name) + ); + } + async _loadPartial(name) { + let partial = this._loadedPartials.get(name); + if (!partial) { + try { + partial = Hogan.compile( + (await this._tryLoadAllExtensions(name)).trim() + ); + this._loadedPartials.set(name, partial); + } catch (error) { + return { badPartial: { name, error } }; + } + } + return partial; + } + async _loadPartials(partialNames) { + const loadedPartials = await Promise.all( + partialNames.map(name => this._loadPartial(name)) + ); + + const badPartials = loadedPartials.filter( + ({ badPartial }) => badPartial + ); + if (badPartials.length > 0) { + const partialErrors = badPartials.map( + ({ badPartial: { name, error } }) => + `'${name}: ${error.stack}\n` + ); + throw new Error(`Error in template partials: ${partialErrors}`); + } + + const foundDescendentPartials = loadedPartials.reduce( + (found, tpt) => found.concat(this._findUnloadedPartialNames(tpt)), + [] + ); + + if (foundDescendentPartials.length > 0) { + return await this._loadPartials([ + ...new Set(foundDescendentPartials) + ]); + } + } + async compile() { + this._template = Hogan.compile(this.source); + + // recursively load all descendent partials ahead of time + await this._loadPartials( + this._findUnloadedPartialNames(this._template) + ); + + this._partials = {}; + for (const [name, tpt] of this._loadedPartials.entries()) { + this._partials[name] = tpt; + } + } + async render(context) { + return this._template.render(context, this._partials, '').trim(); + } +} + +module.exports = MustacheTemplate; diff --git a/packages/upward-js/lib/compiledResources/__tests__/AbstractCompiledResource.test.js b/packages/upward-js/lib/compiledResources/__tests__/AbstractCompiledResource.test.js new file mode 100644 index 00000000000..5369aecf9df --- /dev/null +++ b/packages/upward-js/lib/compiledResources/__tests__/AbstractCompiledResource.test.js @@ -0,0 +1,35 @@ +const AbstractCompiledResource = require('../AbstractCompiledResource'); + +test('requires override to static supportedExtensions getter', () => { + expect(() => AbstractCompiledResource.supportedExtensions).toThrow( + 'must define static supported' + ); +}); + +test('cannot be directly instantiated', () => { + expect(() => new AbstractCompiledResource()).toThrow('Cannot instantiate'); +}); + +test('sets source and io properties from constructor args', () => { + const source = ''; + const io = {}; + const compiler = new class extends AbstractCompiledResource {}(source, io); + expect(compiler.source).toBe(source); + expect(compiler.io).toBe(io); +}); + +test('throws if it receives no source argument', () => { + expect(() => new class extends AbstractCompiledResource {}()).toThrow( + 'string source' + ); +}); + +test('requires override to compile method', () => { + const rsrc = new class extends AbstractCompiledResource {}(''); + expect(rsrc.compile()).rejects.toThrow('must define a compile'); +}); + +test('requires override to render method', () => { + const rsrc = new class extends AbstractCompiledResource {}(''); + expect(rsrc.render()).rejects.toThrow('must define a render'); +}); diff --git a/packages/upward-js/lib/compiledResources/__tests__/GraphQLDocument.test.js b/packages/upward-js/lib/compiledResources/__tests__/GraphQLDocument.test.js new file mode 100644 index 00000000000..bef6e2d2350 --- /dev/null +++ b/packages/upward-js/lib/compiledResources/__tests__/GraphQLDocument.test.js @@ -0,0 +1,97 @@ +const GraphQLDocument = require('../GraphQLDocument'); +const AbstractCompiledResource = require('../AbstractCompiledResource'); + +test('extends AbstractCompiledResource concretely', () => { + const instantiate = () => new GraphQLDocument('', {}); + expect(instantiate).not.toThrow(); + expect(instantiate()).toBeInstanceOf(AbstractCompiledResource); +}); + +test('supported extensions include standard .graphql', () => { + expect(GraphQLDocument.supportedExtensions).toContain('.graphql'); +}); + +test('compiles GraphQL documents', async () => { + const doc = new GraphQLDocument(` + { + foos(bar: 1) { + mahna + } + } + `); + await expect(doc.compile()).resolves.not.toThrow(); + await expect(doc.render()).resolves.toMatchInlineSnapshot(` +Object { + "definitions": Array [ + Object { + "directives": Array [], + "kind": "OperationDefinition", + "name": undefined, + "operation": "query", + "selectionSet": Object { + "kind": "SelectionSet", + "selections": Array [ + Object { + "alias": undefined, + "arguments": Array [ + Object { + "kind": "Argument", + "name": Object { + "kind": "Name", + "value": "bar", + }, + "value": Object { + "kind": "IntValue", + "value": "1", + }, + }, + ], + "directives": Array [], + "kind": "Field", + "name": Object { + "kind": "Name", + "value": "foos", + }, + "selectionSet": Object { + "kind": "SelectionSet", + "selections": Array [ + Object { + "alias": undefined, + "arguments": Array [], + "directives": Array [], + "kind": "Field", + "name": Object { + "kind": "Name", + "value": "mahna", + }, + "selectionSet": undefined, + }, + ], + }, + }, + ], + }, + "variableDefinitions": Array [], + }, + ], + "kind": "Document", + "loc": Object { + "end": 112, + "start": 0, + }, +} +`); +}); + +test('fails on illegal graphql documents', async () => { + const compiler = new GraphQLDocument(` + # missing open curly + foos(bar: 1) { + mahna + } + } + `); + await expect(compiler.compile()).rejects.toThrowErrorMatchingInlineSnapshot( + `"Syntax Error: Unexpected Name \\"foos\\""` + ); +}); diff --git a/packages/upward-js/lib/compiledResources/__tests__/MustacheTemplate.test.js b/packages/upward-js/lib/compiledResources/__tests__/MustacheTemplate.test.js new file mode 100644 index 00000000000..73b987596f4 --- /dev/null +++ b/packages/upward-js/lib/compiledResources/__tests__/MustacheTemplate.test.js @@ -0,0 +1,145 @@ +const MustacheTemplate = require('../MustacheTemplate'); +const AbstractCompiledResource = require('../AbstractCompiledResource'); + +test('supported extensions include standard .mst and .mustache', () => { + expect(MustacheTemplate.supportedExtensions).toEqual( + expect.arrayContaining(['.mst', '.mustache']) + ); +}); + +test('extends AbstractCompiledResource concretely', () => { + const io = { + readFile: () => {} + }; + const instantiate = () => new MustacheTemplate('', io); + expect(instantiate).not.toThrow(); + expect(instantiate()).toBeInstanceOf(AbstractCompiledResource); +}); + +test('throws if IOInterface is not present or lacks methods at constructor time', () => { + expect(() => new MustacheTemplate('')).toThrow( + 'IOInterface as second argument' + ); + expect(() => new MustacheTemplate('', { networkFetch() {} })).toThrow( + 'missing readFile' + ); +}); + +test('compiles Mustache ', async () => { + const template = new MustacheTemplate( + ` + {{#existenz}} + Existenz is {{status}}! + {{/existenz}} + {{^existenz}} + What, you hate Cronenberg? + {{/existenz}} + `, + { readFile: () => {} } + ); + await expect(template.compile()).resolves.not.toThrow(); + await expect( + template.render({ existenz: { status: 'paused' } }) + ).resolves.toMatchInlineSnapshot(`"Existenz is paused!"`); + await expect( + template.render({ + existenz: [ + { status: 'a movie with a weird goop gun in it' }, + { status: 'overshadowed by The Matrix' } + ] + }) + ).resolves.toMatchInlineSnapshot(` +"Existenz is a movie with a weird goop gun in it! + Existenz is overshadowed by The Matrix!" +`); + await expect(template.render({})).resolves.toMatchInlineSnapshot( + `"What, you hate Cronenberg?"` + ); + await expect(template.render()).resolves.toMatchInlineSnapshot( + `"What, you hate Cronenberg?"` + ); +}); + +test('loads Mustache partials using io', async () => { + const io = { + readFile: jest.fn( + async name => + `

Hello {{addressee}}, I am the template called ${name}!

` + ) + }; + const template = new MustacheTemplate( + ` +

Important announcements!

+ {{> firstPartial}} + {{> secondPartial}} + `, + io + ); + await expect(template.compile()).resolves.not.toThrow(); + await expect(template.render({ addressee: 'unit test' })).resolves + .toMatchInlineSnapshot(` +"

Important announcements!

+

Hello unit test, I am the template called firstPartial.mst!

Hello unit test, I am the template called secondPartial.mst!

" +`); + expect(io.readFile).toHaveBeenCalledTimes(2); + expect(io.readFile.mock.calls).toMatchObject([ + ['firstPartial.mst', 'utf8'], + ['secondPartial.mst', 'utf8'] + ]); +}); + +test('loads descendent partials using io', async () => { + const io = { + readFile: jest.fn(async name => { + if (name.indexOf('subPartial') !== -1) { + return `I'm a subpartial, {{addressee}}!!`; + } + return `

Hello {{addressee}}, I am the template called ${name}, and I have sub-partials!

{{> subPartial}}`; + }) + }; + const template = new MustacheTemplate( + ` +

Important announcements!

+ {{> firstPartial}} + {{> secondPartial}} +`, + io + ); + await expect(template.compile()).resolves.not.toThrow(); + await expect(template.render({ addressee: 'unit test' })).resolves + .toMatchInlineSnapshot(` +"

Important announcements!

+

Hello unit test, I am the template called firstPartial.mst, and I have sub-partials!

I'm a subpartial, unit test!!

Hello unit test, I am the template called secondPartial.mst, and I have sub-partials!

I'm a subpartial, unit test!!" +`); + expect(io.readFile).toHaveBeenCalledTimes(3); + expect(io.readFile.mock.calls).toMatchObject([ + ['firstPartial.mst', 'utf8'], + ['secondPartial.mst', 'utf8'], + ['subPartial.mst', 'utf8'] + ]); +}); + +test('handles missing partials', async () => { + const io = { + readFile: jest.fn(async () => + Promise.reject(new Error('Everything is very bad')) + ) + }; + const template = new MustacheTemplate( + `{{> aPartial}} will never exist`, + io + ); + await expect(template.compile()).rejects.toThrowError( + 'Error in template partials' + ); +}); + +test('handles partial errors', async () => { + const io = { + readFile: jest.fn(async () => '{{/katzs}} never closes!i809ula/sn') + }; + const template = new MustacheTemplate(`{{> aPartial }} ought to close`, io); + await expect(template.compile()).rejects.toThrowError( + 'Error in template partials' + ); +}); diff --git a/packages/upward-js/lib/compiledResources/__tests__/forFileOfType.test.js b/packages/upward-js/lib/compiledResources/__tests__/forFileOfType.test.js new file mode 100644 index 00000000000..aad0bc52174 --- /dev/null +++ b/packages/upward-js/lib/compiledResources/__tests__/forFileOfType.test.js @@ -0,0 +1,17 @@ +const { forFileOfType, GraphQLDocument, MustacheTemplate } = require('../'); + +test('returns constructor for a given file extension', () => { + expect(forFileOfType('.graphql')).toBe(GraphQLDocument); + expect(forFileOfType('.gql')).toBe(GraphQLDocument); + expect(forFileOfType('.mst')).toBe(MustacheTemplate); +}); + +test('returns constructor for a filename', () => { + expect(forFileOfType('/Somewhere/somequery.graphql')).toBe(GraphQLDocument); + expect(forFileOfType('/SomewhereElse/document.gql')).toBe(GraphQLDocument); + expect(forFileOfType('someTpt.mst')).toBe(MustacheTemplate); +}); + +test('returns undefined for unsupported file type', () => { + expect(forFileOfType('.pif')).toBeUndefined(); +}); diff --git a/packages/upward-js/lib/compiledResources/index.js b/packages/upward-js/lib/compiledResources/index.js new file mode 100644 index 00000000000..c1f042a3796 --- /dev/null +++ b/packages/upward-js/lib/compiledResources/index.js @@ -0,0 +1,23 @@ +const { extname, normalize } = require('path'); +const CompiledResources = { + GraphQLDocument: require('./GraphQLDocument'), + MustacheTemplate: require('./MustacheTemplate') +}; + +const byExtension = new Map(); + +for (const Resource of Object.values(CompiledResources)) { + for (const extension of Resource.supportedExtensions) { + byExtension.set(extension, Resource); + } +} + +module.exports = Object.assign(CompiledResources, { + forFileOfType(filenameOrExtension) { + const normalized = normalize(filenameOrExtension); + const extension = normalized.startsWith('.') + ? normalized + : extname(normalized); + return byExtension.get(extension); + } +}); diff --git a/packages/upward-js/lib/createUpwardServer.js b/packages/upward-js/lib/createUpwardServer.js new file mode 100644 index 00000000000..3c61921b9dc --- /dev/null +++ b/packages/upward-js/lib/createUpwardServer.js @@ -0,0 +1,91 @@ +/** + * Standalone server script which creates an Express server, configures and + * binds it according to configuration, and applies the upward-js middleware. + */ + +const { resolve } = require('path'); +const express = require('express'); +const middleware = require('./middleware'); +const debugErrorMiddleware = require('debug-error-middleware').express; +const morgan = require('morgan'); + +/** + * Create an upward-js standalone server and optionally bind it to a local + * address. Configure logging and debugging middleware depending on standard + * detection of environment: `process.env.NODE_ENV` === 'production'. + * + * ### `createUpwardServer` Configuration Options + * + * | Property | Type | Default | Description + * | -------- | ---- | ------- | ----------- + * |`upwardPath` | `string` | | Path, relative to the current directory, of a YML file with an UPWARD configuration. **Required**. + * |`bindLocal` | `boolean` | `false` | Create and bind an HTTP server before returning. + * |`port` | `number` | `0` | Specify the port to be bound. `0` means the first open port. + * |`host` | `string` | `0.0.0.0` | Specify the host on which to listen. `0.0.0.0` means all local IPv4 requests. + * |`https` | `object` | | To bind an HTTPS server instead of HTTP, pass a valid `{ key, cert }` object here. + * |`logUrl` | `boolean` | `false` | Log the bound URL to stdout (mostly used for testing against upward-spec) + * + * Returns a plain object with the following properties: + * an `app` property. This is an Express + * + * + * @param {object} config Configuration object. + * @return Promise `{ app, server?, close? }` + */ +async function createUpwardServer({ + bindLocal = false, + port = 0, + host = '0.0.0.0', + https, + logUrl = false, + upwardPath +}) { + if (!upwardPath) { + throw new Error(`upwardPath is required`); + } + const app = express(); + const upward = await middleware(resolve(upwardPath)); + if (process.env.NODE_ENV === 'production') { + app.use(morgan('combined')); + app.use(upward); + } else { + app.use(morgan('dev')); + app.use(upward); + app.use(debugErrorMiddleware()); + } + if (bindLocal) { + return new Promise((resolve, reject) => { + try { + const protocol = https ? 'https' : 'http'; + const server = https + ? require('https').createServer(https, app) + : require('http').createServer(app); + + server.listen(port, host); + + server.on('listening', () => { + if (logUrl) { + console.log( + `${protocol}://${host}:${server.address().port}/` + ); + } + resolve({ + app, + server, + close() { + return new Promise(resolve => { + server.on('close', resolve); + server.close(); + }); + } + }); + }); + } catch (e) { + reject(e); + } + }); + } + return { app }; +} + +module.exports = createUpwardServer; diff --git a/packages/upward-js/lib/envToConfig.js b/packages/upward-js/lib/envToConfig.js new file mode 100644 index 00000000000..2a8b4a0948b --- /dev/null +++ b/packages/upward-js/lib/envToConfig.js @@ -0,0 +1,50 @@ +const decimalRE = /^\d*\.?\d+$/; +function castValue(value) { + const boolStrings = { + false: false, + true: true + }; + const lowered = value.toString().toLowerCase(); + if (boolStrings.hasOwnProperty(lowered)) { + return boolStrings[lowered]; + } + if (decimalRE.test(value)) { + return Number(value); + } + return value; +} + +const envPrefix = 'UPWARD_JS_'; +// Parse and camelcast all environment variables beginning with a prefix. +function envToConfig() { + return Object.entries(process.env).reduce((cfg, [key, value]) => { + if (key.startsWith(envPrefix)) { + const camelCased = key + .slice(envPrefix.length) + .toLowerCase() + .replace(/_[a-z]/g, match => match.charAt(1).toUpperCase()); + cfg[camelCased] = castValue(value); + } + return cfg; + }, {}); +} + +module.exports = envToConfig; + +/** + * const config = envToConfig('UPWARD_JS_'); + * + * UPWARD_JS_BIND_LOCAL=true \ + * UPWARD_JS_FOO=FALSE \ + * UPWARD_JS_FOO_BAR=9.5 \ + * UPWARD_JS_UPWARD_PATH='./path' + * + * becomes + * + * { + * bindLocal: true, + * foo: false, + * fooBar: 9.5, + * upwardPath: './path' + * } + */ diff --git a/packages/upward-js/lib/index.js b/packages/upward-js/lib/index.js new file mode 100644 index 00000000000..8baecfca997 --- /dev/null +++ b/packages/upward-js/lib/index.js @@ -0,0 +1,6 @@ +module.exports = { + IOAdapter: require('./IOAdapter'), + middleware: require('./middleware'), + createUpwardServer: require('./createUpwardServer'), + envToConfig: require('./envToConfig') +}; diff --git a/packages/upward-js/lib/isPrimitive.js b/packages/upward-js/lib/isPrimitive.js new file mode 100644 index 00000000000..9fb1c8dffd6 --- /dev/null +++ b/packages/upward-js/lib/isPrimitive.js @@ -0,0 +1,4 @@ +const { isBoolean, isFinite, isString } = require('lodash'); + +const isPrimitive = v => isBoolean(v) || isString(v) || isFinite(v); +module.exports = isPrimitive; diff --git a/packages/upward-js/lib/middleware.js b/packages/upward-js/lib/middleware.js new file mode 100644 index 00000000000..9593c665346 --- /dev/null +++ b/packages/upward-js/lib/middleware.js @@ -0,0 +1,98 @@ +const debug = require('debug')('upward-js:middleware'); +const jsYaml = require('js-yaml'); +const UpwardServerError = require('./UpwardServerError'); +const IOAdapter = require('./IOAdapter'); +const buildResponse = require('./buildResponse'); + +class UpwardMiddleware { + constructor(upwardPath, io) { + this.upwardPath = upwardPath; + debug(`created for path ${upwardPath}`); + this.io = io; + } + async load() { + const { upwardPath } = this; + try { + this.yamlTxt = await this.io.readFile(upwardPath); + } catch (e) { + throw new UpwardServerError(e, `unable to read file ${upwardPath}`); + } + debug(`read upward.yml file successfully`); + try { + this.definition = await jsYaml.safeLoad(this.yamlTxt); + } catch (e) { + throw new UpwardServerError( + e, + `error parsing ${upwardPath} contents: \n\n${this.yamlTxt}` + ); + } + debug(`parsed upward.yml file successfully: %o`, this.definition); + } + async getHandler() { + return async (req, res, next) => { + const errors = []; + let response; + try { + response = await buildResponse( + this.io, + process.env, + this.definition, + req + ); + if (typeof response === 'function') { + debug('buildResponse returned function'); + response(req, res, next); + return; + } + if (isNaN(response.status)) { + errors.push( + `Non-numeric status! Status was '${response.status}'` + ); + } + if (typeof response.headers !== 'object') { + errors.push( + `Resolved with a non-compliant headers object! Headers are: ${ + response.headers + }` + ); + } + if (typeof response.body !== 'string') { + errors.push( + `Resolved with a non-string body! Body was '${ + response.body + }'` + ); + } + } catch (e) { + errors.push(e.stack); + } + if (errors.length > 0) { + next( + new UpwardServerError( + `Request did not evaluate to a valid response, because: \n${errors.join( + '\n' + )}` + ) + ); + } else { + debug('status, headers, and body valid. responding'); + res.status(response.status) + .set(response.headers) + .send(response.body); + } + }; + } +} + +async function upwardJSMiddlewareFactory( + upwardPath, + io = IOAdapter.default(upwardPath) +) { + const middleware = new UpwardMiddleware(upwardPath, io); + await middleware.load(); + return middleware.getHandler(); +} + +upwardJSMiddlewareFactory.UpwardMiddleware = UpwardMiddleware; + +module.exports = upwardJSMiddlewareFactory; diff --git a/packages/upward-js/lib/resolvers/AbstractResolver.js b/packages/upward-js/lib/resolvers/AbstractResolver.js new file mode 100644 index 00000000000..4f250953e7a --- /dev/null +++ b/packages/upward-js/lib/resolvers/AbstractResolver.js @@ -0,0 +1,17 @@ +class AbstractResolver { + static get resolverType() { + throw new Error( + 'Internal error: Resolvers must define a static resolverType getter' + ); + } + constructor(visitor) { + this.visitor = visitor; + } + resolve() { + throw new Error( + 'Internal error: Resolvers must define a resolve() method' + ); + } +} + +module.exports = AbstractResolver; diff --git a/packages/upward-js/lib/resolvers/ConditionalResolver.js b/packages/upward-js/lib/resolvers/ConditionalResolver.js new file mode 100644 index 00000000000..f8bb5c6f8b7 --- /dev/null +++ b/packages/upward-js/lib/resolvers/ConditionalResolver.js @@ -0,0 +1,58 @@ +const debug = require('debug')('upward-js:ConditionalResolver'); +const AbstractResolver = require('./AbstractResolver'); + +class ConditionalResolver extends AbstractResolver { + static get resolverType() { + return 'conditional'; + } + static get telltale() { + return 'when'; + } + async resolve(definition) { + if ( + !definition.when || + !Array.isArray(definition.when) || + definition.when.length === 0 + ) { + throw new Error( + `ConditionalResolver must a 'when' list, with at least one matcher.` + ); + } + if (!definition.default) { + throw new Error( + `ConditionalResolver must have a 'default' condition.` + ); + } + this.default = definition.default; + + return this.tryMatchers(definition.when); + } + async tryMatchers([top, ...rest]) { + debug('matching %o with %d left to go', top, rest.length); + const regex = new RegExp(top.pattern); + let candidate = await this.visitor.context.get(top.matches); + if (candidate === null || candidate === undefined) { + candidate = ''; + } + candidate = candidate.toString(); + debug('regex is %s, %s is %s', regex, top.matches, candidate); + const regexMatch = candidate.toString().match(regex); + + if (regexMatch) { + const match = regexMatch.reduce((contextMatch, group, index) => { + contextMatch[`$${index}`] = group || ''; + return contextMatch; + }, {}); + this.visitor.context.set('$match', match, true); + const yielded = await this.visitor.upward(top, 'use'); + this.visitor.context.forget('$match'); + return yielded; + } + if (rest.length === 0) { + return this.visitor.upward(this, 'default'); + } + return this.tryMatchers(rest); + } +} + +module.exports = ConditionalResolver; diff --git a/packages/upward-js/lib/resolvers/DirectoryResolver.js b/packages/upward-js/lib/resolvers/DirectoryResolver.js new file mode 100644 index 00000000000..65d455d43d0 --- /dev/null +++ b/packages/upward-js/lib/resolvers/DirectoryResolver.js @@ -0,0 +1,44 @@ +const debug = require('debug')('upward-js:DirectoryResolver'); +const serveStatic = require('express').static; +const AbstractResolver = require('./AbstractResolver'); + +const AllServers = new Map(); +class DirectoryResolver extends AbstractResolver { + static get resolverType() { + return 'directory'; + } + static get telltale() { + return 'directory'; + } + static get servers() { + return AllServers; + } + async resolve(definition) { + if (!definition.directory) { + throw new Error( + `Directory argument is required: ${JSON.stringify(definition)}` + ); + } + const directory = await this.visitor.upward(definition, 'directory'); + if (typeof directory !== 'string') { + throw new Error( + `'directory' argument to DirectoryResolver must be a string, but was a: ${typeof directory}` + ); + } + debug('resolved directory %s', directory); + + let server = DirectoryResolver.servers.get(directory); + if (!server) { + debug(`creating new server for ${directory}`); + server = serveStatic(directory, { + fallthrough: false, + index: false + }); + DirectoryResolver.servers.set(directory, server); + } + + return server; + } +} + +module.exports = DirectoryResolver; diff --git a/packages/upward-js/lib/resolvers/FileResolver.js b/packages/upward-js/lib/resolvers/FileResolver.js new file mode 100644 index 00000000000..49137dcbb19 --- /dev/null +++ b/packages/upward-js/lib/resolvers/FileResolver.js @@ -0,0 +1,101 @@ +const debug = require('debug')('upward-js:FileResolver'); +const AbstractResolver = require('./AbstractResolver'); +const { forFileOfType } = require('../compiledResources'); + +class FileResolver extends AbstractResolver { + static get resolverType() { + return 'file'; + } + static get telltale() { + return 'file'; + } + static get paramTypes() { + return { + file: { + type: 'string', + required: true + }, + encoding: { + type: 'oneOf', + oneOf: ['utf-8', 'utf8', 'latin1', 'binary'], + default: 'utf-8' + }, + parse: { + type: 'oneOf', + oneOf: ['auto', 'text'], + default: 'auto' + } + }; + } + static recognize(value) { + const str = value.toString(); + const derivedConfig = { file: {} }; + if (str.startsWith('file://')) { + derivedConfig.file.inline = str.slice(7); + } else if (str.startsWith('./') || str.startsWith('../')) { + derivedConfig.file.inline = str; + } + if (derivedConfig.file.inline) { + return derivedConfig; + } + } + async resolve(definition) { + if (!definition.file) { + throw new Error( + `File argument is required: ${JSON.stringify(definition)}` + ); + } + const toResolve = [ + this.visitor.upward(definition, 'file'), + definition.encoding + ? this.visitor.upward(definition, 'encoding') + : 'utf8', + definition.parse ? this.visitor.upward(definition, 'parse') : 'auto' + ]; + const [file, encoding, parse] = await Promise.all(toResolve); + debug( + 'resolved file %s, encoding %s, parse mode %s', + file, + encoding, + parse + ); + const { paramTypes } = this.constructor; + const allowedencodings = paramTypes.encoding.oneOf; + if (!allowedencodings.some(value => encoding === value)) { + throw new Error( + `Invalid 'encoding': ${encoding}. Must be one of ${allowedencodings}` + ); + } + debug('encoding %s is valid', encoding); + const fileText = await this.visitor.io.readFile(file, encoding); + if (encoding !== 'binary') debug('retrieved file text %s', fileText); + if (parse === 'text') { + debug('parse === text, returning file text directly'); + return fileText; + } + let Resource; + if (parse === 'auto') { + debug('parse === auto, detecting from filename %s\n\n\n\n', file); + Resource = forFileOfType(file); + if (!Resource) { + debug( + 'autoparse found no parser for %s, returning text instead', + file + ); + return fileText; + } + } else { + const extension = parse.startsWith('.') ? parse : '.' + parse; + Resource = forFileOfType(extension); + if (!Resource) { + throw new Error(`Unsupported parse type '${parse}'`); + } + } + debug('parse === %s, found %s to compile', parse, Resource.name); + const rsrc = new Resource(fileText, this.visitor.io); + await rsrc.compile(); + return rsrc; + } +} + +module.exports = FileResolver; diff --git a/packages/upward-js/lib/resolvers/InlineResolver.js b/packages/upward-js/lib/resolvers/InlineResolver.js new file mode 100644 index 00000000000..44ab2813864 --- /dev/null +++ b/packages/upward-js/lib/resolvers/InlineResolver.js @@ -0,0 +1,33 @@ +const debug = require('debug')('upward-js:InlineResolver'); +const { fromPairs, isPlainObject } = require('lodash'); +const AbstractResolver = require('./AbstractResolver'); +const isPrimitive = require('../isPrimitive'); + +class InlineResolver extends AbstractResolver { + static get resolverType() { + return 'inline'; + } + static get telltale() { + return 'inline'; + } + async resolve({ inline }) { + if (isPrimitive(inline)) { + debug('quick-resolving primitive %s', inline); + return inline; + } else if (isPlainObject(inline)) { + debug('resolving object %o', inline); + const resolvedEntries = await Promise.all( + Object.keys(inline).map(async key => [ + key, + await this.visitor.upward(inline, key) + ]) + ); + return fromPairs(resolvedEntries); + } + throw new Error( + `Internal error: Invalid value supplied to InlineResolver: ${inline}` + ); + } +} + +module.exports = InlineResolver; diff --git a/packages/upward-js/lib/resolvers/ProxyResolver.js b/packages/upward-js/lib/resolvers/ProxyResolver.js new file mode 100644 index 00000000000..f5e425f0034 --- /dev/null +++ b/packages/upward-js/lib/resolvers/ProxyResolver.js @@ -0,0 +1,56 @@ +const debug = require('debug')('upward-js:ProxyResolver'); +const proxyMiddleware = require('http-proxy-middleware'); +const AbstractResolver = require('./AbstractResolver'); + +const AllServers = new Map(); +class ProxyResolver extends AbstractResolver { + static get resolverType() { + return 'proxy'; + } + static get telltale() { + return 'target'; + } + static get servers() { + return AllServers; + } + async resolve(definition) { + if (!definition.target) { + throw new Error( + `'target' URL argument is required: ${JSON.stringify( + definition + )}` + ); + } + const toResolve = [ + this.visitor.upward(definition, 'target'), + definition.ignoreSSLErrors + ? this.visitor.upward(definition, 'ignoreSSLErrors') + : false + ]; + const [target, ignoreSSLErrors] = await Promise.all(toResolve); + + debug('resolved target %o', target); + if (typeof target !== 'string') { + throw new Error( + `'target' argument to ProxyResolver must be a string URL, but was a: ${typeof target}` + ); + } + + let server = ProxyResolver.servers.get(target); + if (!server) { + debug(`creating new server for ${target}`); + server = proxyMiddleware({ + target, + secure: !ignoreSSLErrors, + changeOrigin: true, + autoRewrite: true, + cookieDomainRewrite: '' + }); + ProxyResolver.servers.set(target, server); + } + + return server; + } +} + +module.exports = ProxyResolver; diff --git a/packages/upward-js/lib/resolvers/ServiceResolver.js b/packages/upward-js/lib/resolvers/ServiceResolver.js new file mode 100644 index 00000000000..08699f9cf6f --- /dev/null +++ b/packages/upward-js/lib/resolvers/ServiceResolver.js @@ -0,0 +1,99 @@ +const debug = require('debug')('upward-js:ServiceResolver'); +const { inspect } = require('util'); +const { execute, makePromise } = require('apollo-link'); +const { HttpLink } = require('apollo-link-http'); +const { isPlainObject } = require('lodash'); +const AbstractResolver = require('./AbstractResolver'); +const GraphQLDocument = require('../compiledResources/GraphQLDocument'); +class ServiceResolver extends AbstractResolver { + static get resolverType() { + return 'service'; + } + static get telltale() { + return 'url'; + } + async resolve(definition) { + const die = msg => { + throw new Error( + `Invalid arguments to ServiceResolver: ${inspect(definition, { + compact: false + })}.\n\n${msg}` + ); + }; + if (!definition.url) { + die('No URL specified.'); + } + if (!definition.query) { + die('No GraphQL query document specified.'); + } + debug('validated config %o', definition); + const toResolve = [ + this.visitor.upward(definition, 'url'), + this.visitor.upward(definition, 'query'), + definition.method + ? this.visitor.upward(definition, 'method') + : 'POST', + definition.headers + ? this.visitor.upward(definition, 'headers') + : {}, + definition.variables + ? this.visitor.upward(definition, 'variables') + : {} + ]; + + const [url, query, method, headers, variables] = await Promise.all( + toResolve + ); + + if (variables && !isPlainObject(variables)) { + die(`Variables must resolve to a plain object.`); + } + + debug('url retrieved: "%s", query resolved, creating link', url); + + const link = new HttpLink({ + uri: url, + fetch: this.visitor.io.networkFetch, + headers, + useGETForQueries: method === 'GET' + }); + + let parsedQuery; + if (typeof query === 'string') { + parsedQuery = new GraphQLDocument(query, this.visitor.io); + await parsedQuery.compile(); + } else if (query instanceof GraphQLDocument) { + parsedQuery = query; + } else { + throw new Error(`Unknown type passed to 'query'.`); + } + + debug('running query with %o', variables); + return makePromise( + execute(link, { query: await parsedQuery.render(), variables }) + ) + .then(({ data, errors }) => { + debug( + 'query %s with %o resulted in %o', + definition.query, + variables, + { data, errors } + ); + if (errors && errors.length > 0) { + throw new Error(errors[0].message); + } else { + return { data }; + } + }) + .catch(e => { + if ( + e.message.indexOf('Only absolute URLs are supported') !== -1 + ) { + throw new Error(url.toString() + 'invalid: ' + e.stack); + } + throw e; + }); + } +} + +module.exports = ServiceResolver; diff --git a/packages/upward-js/lib/resolvers/TemplateResolver.js b/packages/upward-js/lib/resolvers/TemplateResolver.js new file mode 100644 index 00000000000..363c38a7b3a --- /dev/null +++ b/packages/upward-js/lib/resolvers/TemplateResolver.js @@ -0,0 +1,108 @@ +const debug = require('debug')('upward-js:TemplateResolver'); +const { inspect } = require('util'); +const { fromPairs, isPlainObject } = require('lodash'); +const AbstractResolver = require('./AbstractResolver'); +const MustacheTemplate = require('../compiledResources/MustacheTemplate'); +const Engines = { + mustache: MustacheTemplate +}; +module.exports = class TemplateResolver extends AbstractResolver { + static get resolverType() { + return 'template'; + } + static get telltale() { + return 'engine'; + } + async resolve(definition) { + let providePromise; + const die = msg => { + throw new Error( + `Invalid arguments to TemplateResolver: ${inspect(definition, { + compact: false + })}.\n\n${msg}` + ); + }; + if (!definition.template) { + die('No template specified.'); + } + if (!definition.provide) { + die( + `'provide' property must be an array of context values or object of resolvable definitions, was ${inspect( + definition.provide + )}` + ); + } else if (Array.isArray(definition.provide)) { + if (definition.provide.some(value => typeof value != 'string')) { + die( + `'provide' property must be an array of context values or object of resolvable definitions, was ${inspect( + definition.provide + )}` + ); + } else { + providePromise = Promise.all( + definition.provide.map(async name => [ + name, + await this.visitor.context.get(name) + ]) + ); + } + } else if (isPlainObject(definition.provide)) { + providePromise = Promise.all( + Object.keys(definition.provide).map(async name => [ + name, + await this.visitor.upward(definition.provide, name) + ]) + ); + } else { + die(`Unrecognized 'provide' configuration: ${definition.provide}`); + } + debug('validated config %o', definition); + const toResolve = [ + this.visitor.upward(definition, 'engine'), + this.visitor.upward(definition, 'template'), + providePromise + ]; + + let [engine, template, rootEntries] = await Promise.all(toResolve); + debug('template retrieved, "%s"', template); + debug('rootEntries retrieved, %o', rootEntries.map(([name]) => name)); + + if (!engine) { + engine = 'mustache'; + } + + const Engine = Engines[engine]; + + if (!Engine) { + throw new Error(`Template engine '${engine}' unsupported`); + } + debug('got template engine %s', Engine.name); + + let renderer; + if (template instanceof Engine) { + debug( + 'how nice, the template is already resolved into a %s for us', + Engine.name + ); + renderer = template; + } else if (typeof template === 'string') { + debug('template is a string, creating %s', Engine.name); + renderer = new Engine(template, this.visitor.io); + } else { + throw new Error( + `Expected string or ${ + Engine.name + }-compatible template and received a foreign object ${ + template.constructor.name + }!` + ); + } + + debug('created renderer'); + + await renderer.compile(); + debug('renderer compiled'); + + return renderer.render(fromPairs(rootEntries)); + } +}; diff --git a/packages/upward-js/lib/resolvers/__tests__/AbstractResolver.test.js b/packages/upward-js/lib/resolvers/__tests__/AbstractResolver.test.js new file mode 100644 index 00000000000..f7b104e5a57 --- /dev/null +++ b/packages/upward-js/lib/resolvers/__tests__/AbstractResolver.test.js @@ -0,0 +1,9 @@ +const AbstractResolver = require('../AbstractResolver'); + +test('static resolverType must be implemented', () => { + expect(() => AbstractResolver.resolverType).toThrow('static resolverType'); +}); + +test('resolve must be implemented', () => { + expect(() => new AbstractResolver().resolve()).toThrow('resolve'); +}); diff --git a/packages/upward-js/lib/resolvers/__tests__/ConditionalResolver.test.js b/packages/upward-js/lib/resolvers/__tests__/ConditionalResolver.test.js new file mode 100644 index 00000000000..611951a0dad --- /dev/null +++ b/packages/upward-js/lib/resolvers/__tests__/ConditionalResolver.test.js @@ -0,0 +1,158 @@ +const ConditionalResolver = require('../ConditionalResolver'); + +test('resolverType is conditional', () => + expect(ConditionalResolver.resolverType).toBe('conditional')); + +test('telltale exists', () => + expect(ConditionalResolver.telltale).toBe('when')); + +test(`throws if the when list is absent or empty`, async () => + expect(new ConditionalResolver().resolve({})).rejects.toThrow( + "'when' list" + )); + +test(`throws if the default is not present`, async () => + await expect( + new ConditionalResolver().resolve({ + when: [ + { matches: 'request.url.pathname', pattern: '^/derp' }, + { use: { inline: 'lerp' } } + ] + }) + ).rejects.toThrow('default')); + +test(`resolves a matcher and yields a value`, async () => { + const visitor = { + upward: jest.fn((x, y) => [x, y]), + context: { + get: jest.fn( + name => + ({ + 'one.fish': 'blue', + 'red.fish': 'two' + }[name]) + ), + set: jest.fn(), + forget: jest.fn() + } + }; + await expect( + new ConditionalResolver(visitor).resolve({ + when: [ + { + matches: 'one.fish', + pattern: 'green', + use: { inline: '#00FF00' } + }, + { + matches: 'red.fish', + pattern: 'tw.', + use: { inline: '#FF0000' } + } + ], + default: { inline: 'chum' } + }) + ).resolves.toMatchObject([ + { + matches: 'red.fish', + pattern: 'tw.', + use: { inline: '#FF0000' } + }, + 'use' + ]); +}); + +test(`when no matchers match, yields default`, async () => { + const visitor = { + upward: jest.fn((x, y) => [x, y]), + context: { + get: jest.fn( + name => + ({ + 'one.fish': 'blue', + 'red.fish': 'two' + }[name]) + ), + set: jest.fn(), + forget: jest.fn() + } + }; + await expect( + new ConditionalResolver(visitor).resolve({ + when: [ + { + matches: 'one.fish', + pattern: 'green', + use: { inline: '#00FF00' } + }, + { + matches: 'red.fish', + pattern: 'thr', + use: { inline: '#FF0000' } + }, + { + matches: 'one.fish', + pattern: 'lemons', + use: { inline: '#00FF00' } + }, + { + matches: 'red.fish', + pattern: 'noooo', + use: { inline: '#FF0000' } + } + ], + default: { inline: 'chum' } + }) + ).resolves.toMatchObject([ + { + default: { inline: 'chum' } + }, + 'default' + ]); +}); + +test(`context values for match are temporarily present`, async () => { + const visitor = { + upward: jest.fn(() => { + expect(visitor.context.set).toHaveBeenCalledWith( + '$match', + expect.objectContaining({ + $0: 'blu', + $1: 'l', + $2: 'u' + }), + true + ); + return 'match set'; + }), + context: { + get: jest.fn( + name => + ({ + 'one.fish': 'blue', + 'red.fish': 'two' + }[name]) + ), + set: jest.fn(), + forget: jest.fn() + } + }; + await expect( + new ConditionalResolver(visitor).resolve({ + when: [ + { + matches: 'one.fish', + pattern: 'b(l)(u)', + use: { inline: '#00FF00' } + }, + { + matches: 'red.fish', + pattern: 'noooo', + use: { inline: '#FF0000' } + } + ], + default: { inline: 'chum' } + }) + ).resolves.toEqual('match set'); + expect(visitor.context.forget).toHaveBeenCalledWith('$match'); +}); diff --git a/packages/upward-js/lib/resolvers/__tests__/FileResolver.test.js b/packages/upward-js/lib/resolvers/__tests__/FileResolver.test.js new file mode 100644 index 00000000000..fc807cd86f8 --- /dev/null +++ b/packages/upward-js/lib/resolvers/__tests__/FileResolver.test.js @@ -0,0 +1,253 @@ +const FileResolver = require('../FileResolver'); + +test('resolverType is file', () => + expect(FileResolver.resolverType).toBe('file')); + +test('telltale exists', () => expect(FileResolver.telltale).toBeDefined()); + +test('resolve() throws error if no definition provided', async () => { + expect(new FileResolver().resolve({})).rejects.toThrow( + 'File argument is required' + ); +}); + +test('resolves filename and uses default encoding and parse', async () => { + const visitor = { + upward: jest.fn(() => 'in-the-way'), + io: { + readFile: jest.fn(() => 'ooh') + } + }; + await expect( + new FileResolver(visitor).resolve({ file: 'something' }) + ).resolves.toEqual('ooh'); + expect(visitor.upward).toHaveBeenCalledTimes(1); + expect(visitor.upward).toHaveBeenCalledWith( + expect.objectContaining({ + file: 'something' + }), + + 'file' + ); + expect(visitor.io.readFile).toHaveBeenCalledWith('in-the-way', 'utf8'); +}); + +test('uses custom encoding', async () => { + const visitor = { + upward: jest.fn(), + io: { + readFile: jest.fn(() => 'whats going on') + } + }; + visitor.upward + .mockResolvedValueOnce('i-said') + .mockResolvedValueOnce('latin1'); + await expect( + new FileResolver(visitor).resolve({ + file: 'something', + encoding: 'hey' + }) + ).resolves.toEqual('whats going on'); + expect(visitor.upward).toHaveBeenCalledTimes(2); + expect(visitor.upward.mock.calls).toMatchObject([ + [{ encoding: 'hey', file: 'something' }, 'file'], + [{ encoding: 'hey', file: 'something' }, 'encoding'] + ]); + expect(visitor.io.readFile).toHaveBeenCalledWith('i-said', 'latin1'); +}); + +test('throws on invalid encoding', async () => { + const visitor = { + upward: jest.fn(), + io: { + readFile: jest.fn(() => 'whats going on') + } + }; + visitor.upward + .mockResolvedValueOnce('i-said') + .mockResolvedValueOnce('bad-encoding'); + await expect( + new FileResolver(visitor).resolve({ + file: 'something', + encoding: 'hey' + }) + ).rejects.toThrow("Invalid 'encoding'"); +}); + +test('parse === "text" shortcuts parse', async () => { + const visitor = { + upward: jest.fn(), + io: { + readFile: jest.fn(() => '{{curlies}}') + } + }; + visitor.upward + .mockResolvedValueOnce('bristly.mustache') + .mockResolvedValueOnce('text'); + await expect( + new FileResolver(visitor).resolve({ + file: 'something', + parse: 'text' + }) + ).resolves.toEqual('{{curlies}}'); + expect(visitor.upward).toHaveBeenCalledWith( + expect.objectContaining({ + parse: 'text' + }), + 'parse' + ); +}); + +test('auto-parses a file from extension', async () => { + const visitor = { + upward: jest.fn(() => 'bristly.mustache'), + io: { + readFile: jest.fn(() => '{{curlies}}') + } + }; + const tpt = await new FileResolver(visitor).resolve({ + file: 'aTemplate' + }); + expect(visitor.io.readFile).toHaveBeenCalledWith( + 'bristly.mustache', + 'utf8' + ); + expect(tpt).toHaveProperty('compile', expect.any(Function)); +}); + +test('falls back silently to text for an unrecognized file type', async () => { + const visitor = { + upward: jest.fn(() => 'bristly.porcupine'), + io: { + readFile: jest.fn(() => '\\||||////') + } + }; + const txt = await new FileResolver(visitor).resolve({ + file: 'aFile' + }); + expect(visitor.io.readFile).toHaveBeenCalledWith( + 'bristly.porcupine', + 'utf8' + ); + expect(txt).toEqual('\\||||////'); +}); + +test('force parses a specific file type', async () => { + const visitor = { + upward: jest.fn(), + io: { + readFile: jest.fn(() => '{ foo { bar } }') + } + }; + visitor.upward + .mockResolvedValueOnce('graphql-disguised-by.mustache') + .mockResolvedValueOnce('graphql'); + const gqlDoc = await new FileResolver(visitor).resolve({ + file: 'anything', + parse: 'turns-into-graphql' + }); + expect(gqlDoc).toMatchObject({ + compile: expect.any(Function), + render: expect.any(Function) + }); + expect(gqlDoc.compile()).resolves.not.toThrow(); + expect(gqlDoc.render()).resolves.toMatchInlineSnapshot(` +Object { + "definitions": Array [ + Object { + "directives": Array [], + "kind": "OperationDefinition", + "name": undefined, + "operation": "query", + "selectionSet": Object { + "kind": "SelectionSet", + "selections": Array [ + Object { + "alias": undefined, + "arguments": Array [], + "directives": Array [], + "kind": "Field", + "name": Object { + "kind": "Name", + "value": "foo", + }, + "selectionSet": Object { + "kind": "SelectionSet", + "selections": Array [ + Object { + "alias": undefined, + "arguments": Array [], + "directives": Array [], + "kind": "Field", + "name": Object { + "kind": "Name", + "value": "bar", + }, + "selectionSet": undefined, + }, + ], + }, + }, + ], + }, + "variableDefinitions": Array [], + }, + ], + "kind": "Document", + "loc": Object { + "end": 15, + "start": 0, + }, +} +`); +}); + +test('force parses throws on unrecognized file type', async () => { + const visitor = { + upward: jest.fn(), + io: { + readFile: jest.fn(() => 'an executable lol') + } + }; + visitor.upward + .mockResolvedValueOnce('exe-disguised-by.mustache') + .mockResolvedValueOnce('.exe') + // should add a dot where necessary + .mockResolvedValueOnce('exe-disguised-by.mustache') + .mockResolvedValueOnce('exe'); + await expect( + new FileResolver(visitor).resolve({ + file: 'afile', + parse: 'aparser' + }) + ).rejects.toThrow('Unsupported parse type'); + await expect( + new FileResolver(visitor).resolve({ + file: 'afile', + parse: 'aparser' + }) + ).rejects.toThrow('Unsupported parse type'); +}); + +test('recognizes file paths', async () => { + expect(FileResolver.recognize('not a file')).toBeFalsy(); + expect(FileResolver.recognize('./a-file')).toBeTruthy(); + expect(FileResolver.recognize('../../..//a-file')).toBeTruthy(); + const config = FileResolver.recognize('file:///a-file'); + const visitor = { + upward: jest.fn(() => Promise.resolve('/a-file')), + io: { + readFile: () => + Promise.resolve('goodness gracious, great text of file') + } + }; + await expect(new FileResolver(visitor).resolve(config)).resolves.toEqual( + 'goodness gracious, great text of file' + ); + expect(visitor.upward).toHaveBeenCalledWith( + { + file: { inline: '/a-file' } + }, + 'file' + ); +}); diff --git a/packages/upward-js/lib/resolvers/__tests__/InlineResolver.test.js b/packages/upward-js/lib/resolvers/__tests__/InlineResolver.test.js new file mode 100644 index 00000000000..b11e77dc688 --- /dev/null +++ b/packages/upward-js/lib/resolvers/__tests__/InlineResolver.test.js @@ -0,0 +1,34 @@ +const InlineResolver = require('../InlineResolver'); + +test('resolverType is inline', () => + expect(InlineResolver.resolverType).toBe('inline')); + +test('telltale exists', () => expect(InlineResolver.telltale).toBeDefined()); + +test('primitive resolves with no visitor calls', async () => { + const visitor = { + upward: jest.fn() + }; + const resolver = new InlineResolver(visitor); + expect(resolver.resolve({ inline: 'a string' })).resolves.toBe('a string'); + expect(visitor.upward).not.toHaveBeenCalled(); +}); + +test('plain object calls visitor.upward to traverse', async () => { + const visitor = { + upward: jest.fn(() => 'a resolved string') + }; + const resolver = new InlineResolver(visitor); + expect( + resolver.resolve({ inline: { resolvedString: 'a resolved string' } }) + ).resolves.toEqual({ resolvedString: 'a resolved string' }); + expect(visitor.upward).toHaveBeenCalledWith( + expect.objectContaining({ resolvedString: 'a resolved string' }), + 'resolvedString' + ); +}); + +test('non-primitive and non-plain object inline argument throws error', async () => { + const resolver = new InlineResolver(); + expect(resolver.resolve([])).rejects.toThrow('Invalid value'); +}); diff --git a/packages/upward-js/lib/resolvers/__tests__/ServiceResolver.test.js b/packages/upward-js/lib/resolvers/__tests__/ServiceResolver.test.js new file mode 100644 index 00000000000..c45f3a04ff2 --- /dev/null +++ b/packages/upward-js/lib/resolvers/__tests__/ServiceResolver.test.js @@ -0,0 +1,260 @@ +const ServiceResolver = require('../ServiceResolver'); +const GraphQLDocument = require('../../compiledResources/GraphQLDocument'); + +test('resolverType is file', () => + expect(ServiceResolver.resolverType).toBe('service')); + +test('telltale exists', () => expect(ServiceResolver.telltale).toBeDefined()); + +test('places a server call and returns results', async () => { + const res = { data: { foo: { bar: 'exam' } } }; + const visitor = { + upward: jest.fn( + (dfn, name) => + ({ + theUrl: 'https://example.com/graphql', + theQuery: '{ foo { bar } }' + }[dfn[name]]) + ), + io: { + networkFetch: jest.fn(async () => ({ + json: async () => res, + text: async () => JSON.stringify(res) + })) + } + }; + + await expect( + new ServiceResolver(visitor).resolve({ + url: 'theUrl', + query: 'theQuery' + }) + ).resolves.toEqual(res); + + const [fetchUri, fetchOptions] = visitor.io.networkFetch.mock.calls[0]; + + expect(fetchUri).toBe('https://example.com/graphql'); + + expect(JSON.parse(fetchOptions.body)).toMatchInlineSnapshot(` +Object { + "operationName": null, + "query": "{ + foo { + bar + } +} +", + "variables": Object {}, +} +`); +}); + +test('places a server call with custom method and headers', async () => { + const res = { data: { foo: { bar: 'exam' } } }; + const visitor = { + upward: jest.fn( + (dfn, name) => + ({ + theUrl: 'https://example.com/graphql', + theHeaders: { Authorization: 'Bear OMG' }, + theMethod: 'GET', + theQuery: '{ foo { bar } }' + }[dfn[name]]) + ), + io: { + networkFetch: jest.fn(async () => ({ + json: async () => res, + text: async () => JSON.stringify(res) + })) + } + }; + + await expect( + new ServiceResolver(visitor).resolve({ + url: 'theUrl', + query: 'theQuery', + headers: 'theHeaders', + method: 'theMethod' + }) + ).resolves.toEqual(res); + + const [fetchUri, fetchOptions] = visitor.io.networkFetch.mock.calls[0]; + + expect(fetchUri).toMatch('https://example.com/graphql?query'); + + expect(fetchOptions.method).toBe('GET'); +}); + +test('places a server call with variables', async () => { + const res = { data: { foo: { bar: 'nothing' } } }; + const visitor = { + upward: jest.fn( + (dfn, name) => + ({ + theUrl: 'https://example.com/graphql', + theQuery: + 'query getFoo($id: String!) { foo(id: $id) { bar } }', + 'combination.to.my.luggage': { + id: 12345 + } + }[dfn[name]]) + ), + io: { + networkFetch: jest.fn(async () => ({ + json: async () => res, + text: async () => JSON.stringify(res) + })) + } + }; + + await expect( + new ServiceResolver(visitor).resolve({ + url: 'theUrl', + query: 'theQuery', + variables: 'combination.to.my.luggage' + }) + ).resolves.toEqual(res); + + const fetchOptions = visitor.io.networkFetch.mock.calls[0][1]; + + expect(JSON.parse(fetchOptions.body)).toMatchInlineSnapshot(` +Object { + "operationName": "getFoo", + "query": "query getFoo($id: String!) { + foo(id: $id) { + bar + } +} +", + "variables": Object { + "id": 12345, + }, +} +`); +}); + +test('throws if variables are in an unacceptable format', async () => { + const visitor = { + io: { + networkFetch: jest.fn(async () => ({ + json: async () => res, + text: async () => JSON.stringify(res) + })) + }, + upward: jest.fn(async () => 'bleh') + }; + + await expect( + new ServiceResolver(visitor).resolve({ + url: 'theUrl', + query: 'theQuery', + variables: { + inline: 'bleh' + } + }) + ).rejects.toThrow('Variables must resolve to a plain object.'); +}); + +test('throws if variables are in an unacceptable format', async () => { + const visitor = { + io: { + networkFetch: jest.fn(async () => ({ + json: async () => res, + text: async () => JSON.stringify(res) + })) + }, + upward: jest.fn(async () => 'bleh') + }; + + await expect( + new ServiceResolver(visitor).resolve({ + url: 'theUrl', + query: 'theQuery', + variables: 'combination.to.my.luggage' + }) + ).rejects.toThrow('Variables must resolve to a plain object.'); +}); + +test('throws if query is missing', async () => { + const visitor = { + io: { + networkFetch: jest.fn(async () => ({ + json: async () => res, + text: async () => JSON.stringify(res) + })) + } + }; + + await expect( + new ServiceResolver(visitor).resolve({ + url: 'theUrl' + }) + ).rejects.toThrow('No GraphQL query'); +}); + +test('throws if url is missing', async () => { + const visitor = { + io: { + networkFetch: jest.fn(async () => ({ + json: async () => res, + text: async () => JSON.stringify(res) + })) + } + }; + + await expect( + new ServiceResolver(visitor).resolve({ + query: 'theQuery' + }) + ).rejects.toThrow('No URL'); +}); + +test('accepts a precompiled GraphQLDocument', async () => { + const res = { data: { foo: { bar: 'exam' } } }; + const gqlDoc = new GraphQLDocument('{ foo { bar } }'); + await gqlDoc.compile(); + const visitor = { + upward: jest.fn( + (dfn, name) => + ({ + theUrl: 'https://example.com/graphql', + theQuery: gqlDoc + }[dfn[name]]) + ), + io: { + networkFetch: jest.fn(async () => ({ + json: async () => res, + text: async () => JSON.stringify(res) + })) + } + }; + + await expect( + new ServiceResolver(visitor).resolve({ + url: 'theUrl', + query: 'theQuery' + }) + ).resolves.toEqual(res); +}); + +test('throws if the query is neither a string nor a GraphQLDocument', async () => { + const visitor = { + upward: jest.fn( + (dfn, name) => + ({ + theUrl: 'https://example.com/graphql', + theQuery: {} + }[dfn[name]]) + ), + io: { + networkFetch: jest.fn() + } + }; + + await expect( + new ServiceResolver(visitor).resolve({ + url: 'theUrl', + query: 'theQuery' + }) + ).rejects.toThrowError('Unknown type'); +}); diff --git a/packages/upward-js/lib/resolvers/__tests__/TemplateResolver.test.js b/packages/upward-js/lib/resolvers/__tests__/TemplateResolver.test.js new file mode 100644 index 00000000000..738c09d7dff --- /dev/null +++ b/packages/upward-js/lib/resolvers/__tests__/TemplateResolver.test.js @@ -0,0 +1,196 @@ +const TemplateResolver = require('../TemplateResolver'); +const MustacheTemplate = require('../../compiledResources/MustacheTemplate'); + +test('resolverType is file', () => + expect(TemplateResolver.resolverType).toBe('template')); + +test('telltale exists', () => expect(TemplateResolver.telltale).toBeDefined()); + +test('throws if no template specified', async () => { + await expect(new TemplateResolver().resolve({ engine: 'mustache' })).rejects + .toThrowErrorMatchingInlineSnapshot(` +"Invalid arguments to TemplateResolver: { engine: 'mustache' }. + +No template specified." +`); +}); + +test('throws if no provide arg specified', async () => { + await expect( + new TemplateResolver().resolve({ + engine: 'mustache', + template: './some-template' + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(` +"Invalid arguments to TemplateResolver: { engine: 'mustache', template: './some-template' }. + +'provide' property must be an array of context values or object of resolvable definitions, was undefined" +`); +}); + +test('throws if provide arg is invalid', async () => { + await expect( + new TemplateResolver().resolve({ + engine: 'mustache', + template: './some-template', + provide: [{}] + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(` +"Invalid arguments to TemplateResolver: { engine: 'mustache', + template: './some-template', + provide: [ {} ] }. + +'provide' property must be an array of context values or object of resolvable definitions, was [ {} ]" +`); +}); + +test('throws if template engine is unsupported', async () => { + const visitor = { + upward: jest.fn( + (dfn, name) => + ({ + anEngine: 'beard', + aTemplate: 'some-template' + }[dfn[name]]) + ), + context: { + get: jest.fn(() => { + ENV_VAR: 'ENV_VALUE'; + }) + } + }; + await expect( + new TemplateResolver(visitor).resolve({ + engine: 'anEngine', + template: 'aTemplate', + provide: ['env'] + }) + ).rejects.toThrow(`Template engine 'beard' unsupported`); + expect(visitor.context.get).toHaveBeenCalledWith('env'); +}); + +test('accepts a template engine instance as the template arg', async () => { + const io = { readFile() {} }; + const visitor = { + upward: jest.fn( + (dfn, name) => + ({ + anEngine: 'mustache', + aTemplate: new MustacheTemplate('{{ env.ENV_VAR }}', io) + }[dfn[name]]) + ), + context: { + get: jest.fn(() => ({ + ENV_VAR: 'ENV_VALUE' + })) + }, + io + }; + await expect( + new TemplateResolver(visitor).resolve({ + engine: 'anEngine', + template: 'aTemplate', + provide: ['env'] + }) + ).resolves.toEqual('ENV_VALUE'); + expect(visitor.context.get).toHaveBeenCalledWith('env'); +}); + +test('accepts a template string as the template arg', async () => { + const io = { readFile() {} }; + const visitor = { + upward: jest.fn( + (dfn, name) => + ({ + anEngine: 'mustache', + aTemplate: + '{{ env.ENV_VAR }} see the {{#arbitrary}}{{animal}}{{/arbitrary}}' + }[dfn[name]]) + ), + context: { + get: jest.fn( + name => + ({ + env: { ENV_VAR: 'come and' }, + arbitrary: { + animal: 'horsie' + } + }[name]) + ) + }, + io + }; + await expect( + new TemplateResolver(visitor).resolve({ + engine: 'anEngine', + template: 'aTemplate', + provide: ['env', 'arbitrary'] + }) + ).resolves.toEqual('come and see the horsie'); + expect(visitor.context.get).toHaveBeenCalledWith('env'); +}); + +test('throws if template argument is not an enging instance or a string', async () => { + const io = { readFile() {} }; + const visitor = { + upward: jest.fn( + (dfn, name) => + ({ + anEngine: 'mustache', + aTemplate: [] + }[dfn[name]]) + ), + context: { + get: jest.fn(() => ({ + ENV_VAR: 'ANOTHER_VALUE' + })) + }, + io + }; + await expect( + new TemplateResolver(visitor).resolve({ + engine: 'anEngine', + template: 'aTemplate', + provide: ['env'] + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Expected string or MustacheTemplate-compatible template and received a foreign object Array!"` + ); +}); + +test('"provide" accepts an object mapping of string => contextValue also', async () => { + const io = { readFile() {} }; + const visitor = { + upward: jest.fn( + async (dfn, name) => + ({ + anEngine: 'mustache', + aTemplate: + 'how {{flavor}} it is to be {{quality}} like you', + 'env.ENV_VAR': 'sweet', + 'deep.structure.isNot': 'flat' + }[dfn[name]]) + ), + io + }; + await expect( + new TemplateResolver(visitor).resolve({ + engine: 'anEngine', + template: 'aTemplate', + provide: { + flavor: 'env.ENV_VAR', + quality: 'deep.structure.isNot' + } + }) + ).resolves.toEqual('how sweet it is to be flat like you'); +}); + +test('throws if "provide" is unrecognized', async () => { + await expect( + new TemplateResolver().resolve({ + engine: 'anEngine', + template: 'aTemplate', + provide: 54332 + }) + ).rejects.toThrow("Unrecognized 'provide' configuration"); +}); diff --git a/packages/upward-js/lib/resolvers/__tests__/index.test.js b/packages/upward-js/lib/resolvers/__tests__/index.test.js new file mode 100644 index 00000000000..32def91150e --- /dev/null +++ b/packages/upward-js/lib/resolvers/__tests__/index.test.js @@ -0,0 +1,22 @@ +const { ResolverList, ResolversByType } = require('../'); +const AbstractResolver = require('../AbstractResolver'); + +test('Resolvers exports', () => { + expect(ResolverList).toBeInstanceOf(Array); + expect( + ResolverList.every(resolver => + AbstractResolver.prototype.isPrototypeOf(resolver.prototype) + ) + ).toBeTruthy(); + expect(ResolversByType).toMatchInlineSnapshot(` +Object { + "conditional": [Function], + "directory": [Function], + "file": [Function], + "inline": [Function], + "proxy": [Function], + "service": [Function], + "template": [Function], +} +`); +}); diff --git a/packages/upward-js/lib/resolvers/index.js b/packages/upward-js/lib/resolvers/index.js new file mode 100644 index 00000000000..b2f0dda7489 --- /dev/null +++ b/packages/upward-js/lib/resolvers/index.js @@ -0,0 +1,17 @@ +// const debug = require('debug')('upward-js:resolvers'); +const ResolverList = [ + require('./InlineResolver'), + require('./FileResolver'), + require('./TemplateResolver'), + require('./ServiceResolver'), + require('./ProxyResolver'), + require('./DirectoryResolver'), + require('./ConditionalResolver') +]; + +const ResolversByType = ResolverList.reduce((out, Resolver) => { + out[Resolver.resolverType] = Resolver; + return out; +}, {}); + +module.exports = { ResolverList, ResolversByType }; diff --git a/packages/upward-js/package.json b/packages/upward-js/package.json new file mode 100644 index 00000000000..a657316ff55 --- /dev/null +++ b/packages/upward-js/package.json @@ -0,0 +1,60 @@ +{ + "name": "@magento/upward-js", + "version": "2.0.0-rc2.0.5", + "description": "Implementation of the UPWARD spec as a NodeJS server", + "main": "./lib/index.js", + "bin": { + "upward-js-server": "./bin/server" + }, + "directories": { + "lib": "./lib", + "bin": "./bin" + }, + "engines": { + "node": ">=8.0.0" + }, + "scripts": { + "test": "jest", + "test:spec": "upward-spec ./test_spec.sh" + }, + "repository": "github:magento-research/pwa-studio", + "keywords": [ + "magento", + "pwa", + "express", + "upward" + ], + "author": "Magento Commerce", + "license": "SEE LICENSE IN LICENSE.txt", + "bugs": { + "url": "https://github.com/magento-research/pwa-studio/issues" + }, + "homepage": "https://github.com/magento-research/pwa-studio/tree/master/packages/upward-js#readme", + "dependencies": { + "apollo-boost": "^0.1.13", + "apollo-link": "^1.2.2", + "apollo-link-http": "^1.5.4", + "contains-path": "^1.0.0", + "debug": "^3.1.0", + "debug-error-middleware": "^1.3.0", + "dotenv": "^6.0.0", + "graphql": "^0.13.2", + "graphql-tag": "^2.9.2", + "hogan.js": "^3.0.2", + "http-proxy-middleware": "^0.19.0", + "js-yaml": "^3.12.0", + "lodash": "^4.17.10", + "morgan": "^1.9.0", + "mustache": "^2.3.2", + "traverse": "^0.6.6" + }, + "devDependencies": { + "@magento/upward-spec": "^2.0.0-rc2.0.5", + "express": "^4.16.3", + "jest": "^23.5.0", + "supertest": "^3.1.0" + }, + "peerDependencies": { + "express": "^4.16.3" + } +} diff --git a/packages/upward-js/test_spec.sh b/packages/upward-js/test_spec.sh new file mode 100755 index 00000000000..d48818d2d4f --- /dev/null +++ b/packages/upward-js/test_spec.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +UPWARD_JS_BIND_LOCAL=1 \ +UPWARD_JS_LOG_URL=1 \ +UPWARD_JS_UPWARD_PATH="$UPWARD_PATH" exec ./bin/server diff --git a/packages/upward-spec/EXECUTION_SCHEDULING_STRATEGIES.md b/packages/upward-spec/EXECUTION_SCHEDULING_STRATEGIES.md new file mode 100644 index 00000000000..3164e21a6f2 --- /dev/null +++ b/packages/upward-spec/EXECUTION_SCHEDULING_STRATEGIES.md @@ -0,0 +1,256 @@ +# Execution Scheduling Strategies + +As mentioned in the ["Execution scheduling and ordering"](README.md#execution-scheduling-and-ordering) section of the specification, UPWARD servers must run their resolvers: + +- **_only_ when their results are used** +- **as _concurrently_ as possible** + +One approach to this is a state machine. A state machine can provide lazy execution by traversing a graph while keeping track of outstanding dependencies, and it can manage concurrency by spawning and keeping references to other instances of the same state machine. + +Consider the following `upward.yml` configuration: + +```yaml +status: page.status +headers: page.headers +body: page.body + +articleResult: + url: env.LIBRARY_SVC + query: './getArticle.graphql' + variables: + articleId: request.url.query.artID + +authorBioResult: + url: env.LIBRARY_SVC + query: './getAuthor.graphql' + variables: + searchTerm: request.url.query.authorID + +textHtml: + inline: + 'content-type': 'text/html' + +notFound: + inline: + status: 404 + headers: textHtml + body: + engine: mustache + template: './notFound.mst' + +page: + when: + - matches: request.url.pathname + pattern: '/article' + use: + when: + - matches: articleResult.data.article.id + pattern: '.' + use: + inline: + status: 200 + headers: textHtml + body: + engine: mustache + template: './article.mst' + default: notFound + - matches: request.url.pathname + pattern: '/author' + use: + when: + - matches: authorBioResult.data.author.id + pattern: '.' + use: + inline: + status: 200 + headers: textHtml + body: + engine: mustache + template: './authorBio.mst' + default: notFound + default: notFound +``` + +## Possible tasks + +I/O dependent tasks defined by resolvers in this configuration include: + +**A.** Reading and parsing `./getArticle.graphql` from the file system +**B.** Querying a remote service for articles +**C.** Reading and parsing `./getAuthor.graphql` from the file system +**D.** Querying a remote service for authors +**E.** Reading and parsing `./authorBio.mst` from the file system +**F.** Reading and parsing `./article.mst` from the file system +**G.** Reading and parsing `./notFound.mst` from the file system + +In the above example, a request to `/author?id=1` should never cause the `articleResult` resolver to execute its query to the remote service. If the result is not found, it should never cause the `authorBio.mst` resolver to read from the file system. Even though the file format is declarative, each request should traverse through the resolvers using a minimal path. The request `/author?id=1`, if no data exists for it in the backing resource, should only run tasks **C, D,** and **G** from the above list. + +## Example state machine resolution + +:information_source: _(The below is an example and not normative. UPWARD-compliant servers must lazily execute resolvers, and a state machine is one way to implement this. What follows is a description of one path through such a state machine, not a formal definition of a state machine.)_ + +In a state-machine-based lazy-execution implementation, the request `/author?id=1` would cause the following. + +Starting state: **Response readiness analysis** + + 1. `status`, `headers`, and `body` have not been assigned. + 1. No resolvers are currently in operation or have resolved. + 1. `status`, `headers`, and `body` resolution are all defined in configuration. + + State: **Context dependency analysis** + 1. The context names pending resolution all depend on `page`. + 1. `page` has not been assigned. + 1. `page` resolution is defined in configuration. + + State: **Context resolution** + 1. `page` depends on a ConditionalResolver. + + State: **Conditional execution** + 1. The first matcher depends on `request.url.pathname`. + 1. That value is already present in context, so the matcher can run. + + State: **Conditional matching** + 1. The first matcher tests `request.url.pathname`, which is the value `/author`, against the pattern `/article`. The test fails. + + State: **Conditional execution** + 1. The second matcher depends on `request.url.pathname`. + 1. That value is already present in context, so the matcher can run. + + State: **Conditional matching** + 1. The second matcher tests `request.url.pathname`, which is the value `/author`, against the pattern `/author`. The test passes. + 1. The conditional yields to the resolver in the `use` parameter of the second matcher. That resolver is a ConditionalResolver. + + State: **Conditional execution** + 1. The first matcher depends on `authorBioResult`. + 1. `authorBioResult` has not been assigned. + + State: **Resolver dependency detection** + 1. `authorBioResult` resolution is defined in configuration. + + State: **Context resolution** + 1. `authorBioResult` depends on a ServiceResolver. + + State: **Resolver dependency detection** + 1. The ServiceResolver depends on context values `env.LIBRARY_SVC`, `request.url.query.authorId`, and a FileResolver (shorthand form) for the `query`. + 1. Both context values are present. + 1. The shorthand FileResolver creates and resolves an implicit InlineResolver, determining that it depends on the filesystem path `./getAuthor.graphql`. + + State: **File resolution** + 1. The server reads, parses, and caches the contents of `./getAuthor.graphql`. + + State: **Service resolution** + 1. The server places an HTTP request to the GraphQL service. The GraphQL service responds with a result payload: + ```json + { + "data": { + "author:" null + } + } + ``` + + State: **Context assignment** + 1. The context value `authorBioResult` is set to the result payload. + + State: **Response readiness analysis** + 1. `status`, `headers`, and `body` have not been assigned. + 1. One or more context values have been assigned. + 1. A ConditionalResolver is still resolving. + + State: **Context dependency analysis** + 1. The context names pending resolution depend on `page` and `authorBioResult`. + 1. `page` has not been assigned. + 1. `authorBioResult` has been assigned. + + State: **Context resolution** + 1. The conditional resolver depending on `authorBioResult` resumes. + + State: **Conditional execution** + 1. The first matcher runs again and fails again. + 1. The second matcher runs again and succeeds again, yielding another ConditionalResolver. + 1. The first matcher depends on `authorBioResult`. + 1. That value is now present in context, so the matcher can run. + + State: **Conditional matching** + 1. The first matcher tests `authorBioResult.data.author.id` against the pattern `.`. Because `authorBioResult.data.author` is null, the match fails. + + State: **Conditional execution** + 1. All matchers in the `when` configuration have failed, so the conditional yields to the value of `default`. + 1. The value of `default` is the context lookup `notFound`. + 1. `notFound` has not been assigned. + + State: **Resolver dependency detection** + 1. `notFound` resolution is defined in configuration. + + State: **Context resolution** + 1. `notFound` depends on an InlineResolver. + 1. The InlineResolver specifies an object whose properties depend on context value `textHtml` for `headers` and a TemplateResolver for `body`. + 1. `textHtml` has not been assigned. + 1. `textHtml` is defined in configuration. + 1. `textHtml` depends on an InlineResolver. + 1. The InlineResolver specifies an object whose properties depend on context value `text/html`. + 1. That value is present in context (it is built in). + 1. The InlineResolver depending on `textHtml` resolves, assigning a value to the `header` property of the `notFound` resolver. + + State: **Resolver dependency detection** + 1. The TemplateResolver depends on a context value `mustache` for `engine`, and a shorthand FileResolver for `template`. + 1. The value `mustache` is present in context (it is built in). + 1. The shorthand FileResolver creates and resolves an implicit InlineResolver, determining that it depends on the filesystem path `./notFound.mst`. + + State: **File resolution** + 1. The server reads, parses, and caches the contents of `./notFound.mst`. + + State: **Template resolution** + 1. The TemplateResolver executes its template against the entire context object, creating an interpolated string: + + ```html + That doesn't look like anything to me. + ``` + + The TemplateResolver depending on `./notFound.mst` resolves, assigning the string to the `body` property of the `notFound` resolver. + + State: **Context resolution** + 1. The InlineResolver for `notFound` resolves. + 1. The conditional resolver depending on `notFound` resumes. + + State: **Conditional execution** + 1. The first matcher runs again and fails again. + 1. All matchers in the `when` configuration have failed, so the conditional yields to the value of `default`. + 1. The value of `default` is a resolved InlineResolver producing an object. + + State: **Conditional resolution** + 1. The ConditionalResolver resolves. + 1. The `use` property of the match in the parent ConditionalResolver is fully resolved. + 1. The parent ConditionalResolver resolves. + + State: **Context assignment** + 1. The context value `page` is set to the result payload. + + State: **Response readiness analysis** + 1. `status`, `headers`, and `body` have not been assigned. + 1. One or more top-level context values have resolved. + 1. No resolvers are currently resolving. + + State: **Context dependency analysis** + 1. The context names pending resolution depend on `page`. + 1. `page` has been assigned. + + State: **Context resolution** + 1. `status`, `headers`, and `body` depend on `page`. + 1. `page` exists in context and can be used. + + State: **Context assignment** + 1. The context value `status` is set to `page.status`. + 1. `headers` is set to `page.headers`. + 1. `body` is set to `page.body`. + + State: **Response readiness analysis** + 1. `status`, `headers`, and `body` have all been assigned. + + State: **Response flush** + 1. The context is cast to an HTTP response and sent to the requesting client. + +## Conclusion + +While resolving the request, the state machine did not execute all [possible tasks](#possible-tasks); it did not run tasks **A, B, E,** or **F**, even though there is no indication of task ordering or conditional execution in those task definitions themselves. + +This _topologically-ordered_, _maximally concurrent_, and _lazily evaluated_ process enables UPWARD configuration authors to describe the available tasks without writing any imperative logic. diff --git a/packages/upward-spec/RATIONALE.md b/packages/upward-spec/RATIONALE.md new file mode 100644 index 00000000000..47403423163 --- /dev/null +++ b/packages/upward-spec/RATIONALE.md @@ -0,0 +1,184 @@ +# Rationale: Why UPWARD + +- [Rationale: Why UPWARD](#rationale-why-upward) + - [The Problem With PWA Runtime Dependencies](#the-problem-with-pwa-runtime-dependencies) + - [Tiered Architectures](#tiered-architectures) + - [Edge Definition, Not Service Orchestration](#edge-definition-not-service-orchestration) + - [Common PWA Backing Service Characteristics](#common-pwa-backing-service-characteristics) + - [UPWARD with PWAs](#upward-with-pwas) + - [Enforce stateless middle tiers](#enforce-stateless-middle-tiers) + - [Example `upward.yml`](#example-upwardyml) + - [Push business logic to the edges of the system](#push-business-logic-to-the-edges-of-the-system) + - [Objections](#objections) + +Progressive Web Apps represent a new plateau of maturity for the Web as a platform. Like any other high-quality software, PWAs benefit from [Twelve-factor methodology][twelve factor], but they have historically been considered a _part_ of a full-scale deployed application, the "frontend", and not a full application to which all twelve-factor principles should apply. + +Until PWAs, "web apps" have not been apps on their own; they could not function without a specific set of backing services. Those services should be decoupled and abstracted as [resource locators][twelve factor resources], so that individual servers can be swapped in and out, but the "frontend" is still tightly coupled to a specific topology of services, culminating in end-to-end functionality that depends on all of those services being present. + +![12-factor resource diagram: 12factor.net](backing_services.png) +_Image courtesy of 12factor.net_ + +A PWA must run independently from any backing services as much as possible; therefore, it must have alternate strategies to replace the functionality of each backing service. The simplest and most efficient PWA would have as few individual backing services as possible, so a need emerges for a tool which unifies backing services into a single layer that deliver, supports, and synchronizes with a PWA. + +## The Problem With PWA Runtime Dependencies + +A PWA must have at least partial functionality offline; for example, an eCommerce store PWA should be able to navigate its catalog in several dimensions, including categories and configurable products, without placing a server call for each transition. Therefore, at least some of its business logic must be implemented in the HTML/CSS/JS layer traditionally designated as "frontend". + +However, duplication of logic is an antipattern, and the spreading of business logic across multiple layers is an antipattern that harms testability. The [ports and adapters architecture, or "hexagonal" architecture,][hexagonal architecture] describes how business logic must be confined to the center of the architecture, and expressed or interpreted through a small number of adapters, so that it can be connected with backing services and tested in multiple contexts. + +This seems incompatible with an app architecture that supports "offline mode", where workflows should be independent of the availability of services. Since business logic must be in the center and accessible through ports, it follows that a Web app should move its "center" of authoritative business logic to the frontend, or as close to the frontend as possible. + +This is a difficult migration path for many web apps, for many reasons: security concerns, scalability concerns about the JavaScript language and runtime, and the value inherent in existing codebases. Yet as more Web apps implement offline functionality, pressure increases on their business logic to duplicate and/or spread out across platforms in a dangerously coupled way. **A robust architecture must provide natural and obvious locations for different types of business logic.** Without them, an application will amass implicit, undocumented, informal business logic in one or more places. + +### Tiered Architectures + +The most frequently used solution to the problem of PWA runtime dependencies is a [multitier architecture][multitier architecture], using some combination of: + +- a "presentation tier" or "[backend-for-frontend][backend-for-frontend]" for serving the app assets and reverse proxying to services +- a "middle tier" or "[gateway][gateway aggregation]" for combining related service requests. + +In both of those cases, the intermediaries serve as a gathering point for the frontend: + +![Tiered architecture](tiered_architecture.png) + +However, these tiers are almost implemented in a **general-purpose web framework**, written in a **general-purpose programming language**. The hazard always exists for an implementer to "cheat" the architecture, and fix a bug in either the production web app, or one of the attached services, in the presentation tier! + +A declarative middle tier architecture is called for. Early efforts to standardize a format for [service discovery][uddi], as well as more recent efforts to standardize a format for [service topology][tosca], have resulted in complex and overspecified configurations most useful in closed systems. + +### Edge Definition, Not Service Orchestration + +A common solution to the problem of business logic that exists across multiple services is [service orchestration][service orchestration], but a service orchestrator is a separate program with foreknowledge of the workflows that exist, and an imperative programming paradigm that enforces the workflows. UPWARD more closely resembles [service choreography][service choreography]; an UPWARD configuration has global control over a request, rather than control from a specific node in the network. This simplification is possible because UPWARD is for a PWA [application shell][application shell], so it needs to respond to a more restricted set of conditions than some other service architectures. + +### Common PWA Backing Service Characteristics + +PWA best practices recommend a small subset of the possible things a web server can do. This set of restrictions makes it much easier to describe web server behavior with a limited set of definitions. + +A PWA should: + +- Serve over [HTTPS only][pwa https] +- Serve an [application shell][application shell] with some aspects common to all pages +- Serve static resources from [edge servers][cdn def] whenever possible +- Provide a common strategy for data exchange, such as GraphQL, to avoid excess client responsibilities +- Consume data from as few conceptual domains as possible + +A PWA should not: + +- Require many resources that cannot be [cached and reused when offline][offline mode] +- Depend on server-side state for [most interactions and workflows][high perf loading] +- Depend on a particular backing technology stack + +It follows that a server which delivers PWA resources can count on the following to be true: + +- Requests are idempotent +- Responses are small +- All services are GraphQL + +These assumptions enable a manageably small declarative specification for an app shell server. + +## UPWARD with PWAs + +The UPWARD specification enables developers and administrators to describe a middle tier and/or backend-for-frontend architecture that cannot include arbitrary business logic, since its behavior is confined to the declarative specification. + +### Enforce stateless middle tiers + +The following configuration file defines a middle tier server that renders a simple application shell, using different templates based on pattern-matching conditions. It cannot maintain any local state. It cannot, and does not need to, do any significant rule-based data transformation beyond pattern matches. + +#### Example `upward.yml` + +```yaml +status: page.status +headers: page.headers +body: page.body + +articleResult: + url: env.LIBRARY_SVC + query: './getArticle.graphql' + variables: + articleId: request.url.query.artID + +authorBioResult: + url: env.LIBRARY_SVC + query: './getAuthor.graphql' + variables: + searchTerm: request.url.query.authorID + +textHtml: + inline: + 'content-type': 'text/html' + +notFound: + inline: + status: 404 + headers: textHtml + body: + engine: mustache + template: './notFound.mst' + +page: + when: + - matches: request.url.pathname + pattern: '/article' + use: + when: + - matches: articleResult.data.article.id + pattern: '.' + use: + inline: + status: 200 + headers: textHtml + body: + engine: mustache + template: './article.mst' + default: notFound + - matches: request.url.pathname + pattern: '/author' + use: + when: + - matches: authorBioResult.data.author.id + pattern: '.' + use: + inline: + status: 200 + headers: textHtml + body: + engine: mustache + template: './authorBio.mst' + default: notFound + default: notFound +``` + +The `LIBRARY_SVC` endpoint is responsible for providing, querying, and doing existence checks on the backing data. The `.mst` template files implement only the base Mustache specification, so they are almost completely logic-free--they don't even have conditionals. A PWA expecting this server can read this document and structure the same GraphQL calls; in this way, a PWA can include an `upward.yml` to define service depdencies, or it can build against a service-provided `upward.yml` to do service discovery. + +#### Push business logic to the edges of the system + +When an architecture uses an UPWARD server as a middle tier, it can't hide any business logic there. Business logic must be extracted to reusable templates or queries, pushed forward to the frontend, or pushed backward to the backend. When logic duplication must occur, it is pushed to the edges of the system, rather than hiding in the middle of a tiered architecture. + +An UPWARD-compliant server is not responsible for caching, proxying, or static assets. Its only responsibility is to unify, in a central format, the requirements and processes that create an application shell for a Progressive Web App, so that both a backend server and a frontend client can use the same cross-platform implementations (such as templates and queries) to perform render. + +![Unified graph over backing services](unified_graph.png) + +## Objections + +Common objections to creating new specifications and standards include: + +- **Necessity**: Unclear what problem the standard solves +- **Proliferation**: Too many standards exist already +- **Restrictiveness**: The standard imposes restrictions that hamper desired functionality +- **Erosion risk**: Ensuring compliance with a standard's updates adds to the cost of software development and maintenance + +The UPWARD team shares those concerns, and applies them continuously to the standard as it develops. UPWARD exists because it answers these concerns with clear necessity, wise restrictiveness and limited scope, in this document and in its specification. + +[pwa https]: +[twelve factor]: +[twelve factor resources]: +[application shell]: +[ports and adapters architecture]: +[cdn def]: +[offline mode]: +[high perf loading]: +[service orchestration]: +[service choreography]: +[backend-for-frontend]: +[gateway aggregation]: +[uddi]: +[tosca]: diff --git a/packages/upward-spec/README.md b/packages/upward-spec/README.md new file mode 100644 index 00000000000..3d1c006498f --- /dev/null +++ b/packages/upward-spec/README.md @@ -0,0 +1,1129 @@ +# UPWARD Specification + +**U**nified **P**rogressive **W**eb **A**pp **R**esponse **D**efinitions are simple files describing how a web server delivers and supports a [Progressive Web Application][pwa def]. They denote server behavior in a platform-independent way, so that a PWA client application expecting certain behavior from HTTP endpoints can be deployed on any type of tech stack that implements the UPWARD specification. + +See [RATIONALE.md](RATIONALE.md) for a detailed explanation of the context that led to the introduction of UPWARD, and the problems it intends to solve. + +See [EXECUTION_SCHEDULING_STRATEGIES.md](EXECUTION_SCHEDULING_STRATEGIES.md) for a deep dive into how the core logic of an UPWARD-compliant server might be implemented with a state machine. + +See [UPWARD_MAGENTO.md](UPWARD_MAGENTO.md) for context on how UPWARD fills a need in Magento PWA Studio and Magento 2 frontend development. + +- [UPWARD Specification](#upward-specification) + - [Quickstart](#quickstart) + - [Summary](#summary) + - [Simple example](#simple-example) + - [Configuration](#configuration) + - [Responding to requests](#responding-to-requests) + - [Execution scheduling and ordering](#execution-scheduling-and-ordering) + - [Cyclic dependencies](#cyclic-dependencies) + - [Response flush triggering](#response-flush-triggering) + - [Context Reference](#context-reference) + - [Initial context](#initial-context) + - [Context Path Syntax](#context-path-syntax) + - [Using context](#using-context) + - [Context persistence and size](#context-persistence-and-size) + - [Resolver Reference](#resolver-reference) + - [InlineResolver](#inlineresolver) + - [InlineResolver Configuration Options](#inlineresolver-configuration-options) + - [FileResolver](#fileresolver) + - [FileResolver Configuration Options](#fileresolver-configuration-options) + - [Parsing](#parsing) + - [FileResolver Error Handling](#fileresolver-error-handling) + - [FileResolver shorthand](#fileresolver-shorthand) + - [ServiceResolver](#serviceresolver) + - [ServiceResolver Configuration Options](#serviceresolver-configuration-options) + - [Example REST service call](#example-rest-service-call) + - [Response Assignment](#response-assignment) + - [ServiceResolver Error Handling](#serviceresolver-error-handling) + - [TemplateResolver](#templateresolver) + - [TemplateResolver Configuration Options](#templateresolver-configuration-options) + - [Template Context](#template-context) + - [Template Engines](#template-engines) + - [Example React DOM Server support](#example-react-dom-server-support) + - [TemplateResolver Error Handling](#templateresolver-error-handling) + - [ConditionalResolver](#conditionalresolver) + - [ConditionalResolver Configuration Options](#conditionalresolver-configuration-options) + - [Matchers](#matchers) + - [Match context](#match-context) + - [ConditionalResolver notes](#conditionalresolver-notes) + - [ProxyResolver](#proxyresolver) + - [ProxyResolver Example](#proxyresolver-example) + - [ProxyResolver Configuration Options](#proxyresolver-configuration-options) + - [ProxyResolver notes](#proxyresolver-notes) + - [DirectoryResolver](#directoryresolver) + - [DirectoryResolver Example](#directoryresolver-example) + - [DirectoryResolver Configuration Options](#directoryresolver-configuration-options) + - [Reducing boilerplate](#reducing-boilerplate) + - [Default parameters](#default-parameters) + - [Builtin constants](#builtin-constants) + - [Resolver type inference](#resolver-type-inference) + - [YAML anchors](#yaml-anchors) + +## Quickstart + +This repository is a test suite for UPWARD compliance, testing several scenarios and features on a live web server. It requires NodeJS v8 LTS or later. To test an UPWARD server: + +1. Write or obtain a POSIX shell script which: + + - gets the path to an UPWARD definition file from the environment variable `UPWARD_PATH` + - launches and/or binds the UPWARD server under test and runs it in the foreground (not as a daemon process). + - prints the hostname and port of the now-running server instance to standard out + - responds to SIGTERM by gracefully closing the server + + [Example here.][spec-shell-script] + +2. Use `npx` to run `upward-spec` on your shell script + + ```sh + npx upward-spec ./test_upward_server.sh + ``` + +3. The shell script will run for each test suite with the environment variable `UPWARD_YAML` set to the path of a fixture YAML file for configuring a server instance. The script should launch a server (on a local port or a remote port, but resolvable to the local system) and print its host to standard out, staying in the foreground. + + The tests may run in parallel, so the server or launch script should seek open ports to bind to. When each test suite is over, the script will receive a SIGTERM or SIGKILL. + +4. By default, the test runner will print human-readable results to stdout; the argument `--xunit` will make it print XUnit-compatible (and therefore JUnit-compatible) test result XML. The argument `--tap` will make it print [Test Anything Protocol](https://testanything.org/)-compatible text. Under the hood, this uses [tape](https://github.com/substack/tape) and it can be piped to [any number of open-source TAP reporters](https://github.com/sindresorhus/awesome-tap#javascript). + +:information_source: _(The `npx` tool above is not required; it's a convenience script to avoid installing global NPM dependencies. You can also install `upward-spec` permanently using `npm install -g upward-spec`, and then simply invoke `upward-spec ./test_upward_server.sh` from that point forward.)_ + +## Summary + +UPWARD definitions are YAML files which declare the behavior of an [application shell][application shell] server. An application shell server implements a strict subset of HTTP functionality: it proxies to API backends, it serves static files, and it handles an HTTP GET request for a resource by delivering a minimal app shell: the code and data to bootstrap a Progressive Web App which displays that resource. + +An App Shell is purposefully minimal, and so is an UPWARD server. It is meant to initialize or refresh sessions, deliver small HTML documents with enough server-side rendering for initial display and SEO, and then hand off subsequent request handling to the PWA running in the client. + +The declarative format of UPWARD means that an UPWARD-compliant server may be written in any programming language and run on any tech stack; therefore, a PWA can declare the URIs and behavior of the network endpoints it depends on by including an UPWARD file. + +### Echo Example + +This example definition file echoes request data as text back to the client. + +```yaml +status: + resolver: inline + inline: 200 +headers: + resolver: inline + inline: + content-type: text/plain +body: + resolver: template + engine: mustache + provide: + - request + template: + resolver: inline + inline: | + {{#request}} + Headers: + {{#headerEntries}} + {{name}}: {{value}} + {{/headerEntries}} + URL: + {{#url}}{{#?protocol}}protocol: {{protocol}} + {{/?protocol}}{{#?host}}host: {{host}} + {{/?host}}{{#?hostname}}hostname: {{hostname}} + {{/?hostname}}{{#?port}}port: {{port}} + {{/?port}}pathname: {{pathname}} + {{/url}} + URL Query: + {{#queryEntries}} + {{name}}: {{value}} + {{/queryEntries}} + {{/request}} + +``` + +This example demonstrates the initial properties of the context object, populated by the originating HTTP request. describes a server which always returns status 200 with a single header, `content-type`, and a text body which is a plaintext summary of the GET request properties. An example request to such a server results in: + +```sh +$ curl 'http://localhost:54422/head/shoulders?and=knees&and=toes' + +Headers: + host: localhost:54422 + user-agent: curl/7.54.0 + accept: */* +URL: + host: localhost:54422 + hostname: localhost + port: 54422 + pathname: /head/shoulders +URL Query: + and: knees,toes +``` + +## Configuration + +A spec-compliant UPWARD server should be configurable with a runtime parameter: the location of the UPWARD definition YML file. The file references external resources using [Resolvers](#resolvers). + +## Responding to requests + +**An UPWARD definition file is an instruction manual for building an HTTP response.** It links values together in a global namespace hereafter called the **context**. Each request handling cycle begins with with a new context object, with the incoming `Request` assigned to the context value `request`, and current environment variables assigned to the context value `env`. Root properties of the definition file represent other named values in the context, which Resolvers populate. Resolvers can use other context properties as input, and they can also use other Resolvers directly; in this way, the definition itself can be considered an abstract decision tree, from which code could be statically generated. + +### Execution scheduling and ordering + +1. **Resolvers must execute only when needed.** If a request handling cycle moves through ConditionalResolver branches in a way that never requires a particular context value, then that context value must never be resolved during that execution cycle. + +2. **Resolvers must execute as concurrently as possible**. The maximum concurrency is left to the implementation. A compliant server detects when a Resolver uses a context value, and delays its execution until that context value becomes available, via [topological sorting][topological sorting] of resolver execution. + +See [EXECUTION_SCHEDULING_STRATEGIES.md](EXECUTION_SCHEDULING_STRATEGIES.md) for an example of how this could be implemented. + +### Cyclic dependencies + +**An UPWARD server must detect cyclic dependencies, and should detect them as soon as possible**. Only bare string literals can be used to reference context objects in a definition file, so it should be possible to detect potential cyclic dependencies upon first processing the configuration file, before any requests are made. If a cyclic dependency is detected in this manner, the server should raise an error on startup. If a cyclic dependency occurs at runtime, the server should return a 500 error. + +### Response flush triggering + +**The root context must always eventually have non-null `status`, `headers`, and `body` properties.** Once the resolution path has assigned these three values fo context, the UPWARD-compliant server should immediately use those three values to create an HTTP response and flush it to the client. No streaming or buffering interface should be provided; UPWARD servers should not deal in data large enough to require streaming. **If all resolvers finish executing and the response is lacking any of the `status`, `headers`, or `body` properties, the server should emit a 500 error.** If it is possible at startup time to trace a path through the decision tree where this occurs, the implementation may use static analysis to do so and raise an error on startup. + +:information_source: _In real-world scenarios, the generation of `status`, `headers`, and `body` will share a lot of logic. The recommended best practice is to use an InlineResolver to create a top-level object, called something like "response", with those properties, and then define the top-level `status`, `headers`, and `body` properties to refer to that object in context, e.g. `status: response.status`. + +## Context Reference + +The context is a global namespace created for each request. It is a dictionary of values, like a JSON object. A typical response cycle may append intermediate values to the context, like a query result or a template string. Those values do not emit as part of the response. Only the `status`, `headers`, and `body` properties of the context will be flushed to the client. Since the context is the global namespace and the means of sharing values between resolvers, it may become a large object at some points in the cycle, but this should not affect performance on the client. + +Context values cannot be overwritten. A resolver which attempts to overwrite an already-set context value must raise a context conflict error. UPWARD-compliant servers should be able to identify potential context conflict errors during static analysis of the definition file and raise an error on launch. UPWARD-compliant servers must be able to identify a context conflict during runtime execution and respond with a 500 error. + +### Initial context + +When an UPWARD-compliant server receives an HTTP GET request, it must populate an initial context object with the following values: + +- `request`: an object representing the incoming HTTP request. It must have these properties: + + - `headers`: An object representing HTTP headers. Header names are lower-cased, and multiple values are joined with commas. + + - `headerEntries`: An iterable array version of the `headers` object, suitable for use in a Mustache template (which cannot iterate over plain JSON objects). For this headers object: + ```json + { + "accept": "text/html", + "host": "example.com" + } + ``` + the `headerEntries` array would be: + ```json + [ + { "name": "accept", "value": "text/html" }, + { "name": "host", "value": "example.com" } + ] + ``` + - `queryEntries`: An iterable array version of the URL `query` object, suitable for Mustache like the `headerEntries` property explained above. + + - `url`: A subset of a [URL record][url spec] as specified by WHATWG. The following properties should at least be populated; the Host header can be used to infer the origin. + + | Attribute | Example + | --------- | ------- + | `host` | `example.com:8080` + | `hostname`| `example.com` + | `port` | `8080` + | `pathname`| `/deep/blue/sea` + | `search` | `?baby=beluga` + | `query` | `{ "baby": "beluga" }` + + Because HTTP servers are sometimes unable to ascertain their own domain names or origins, it is acceptable for one or more of the `href`, `origin`, `protocol`, `username`, `password`, `host`, `hostname`, and `port` properties to be undefined. However, a compliant server MUST provide `pathname`, `search`, and `query`. + + The `query` property is not part of the WHATWG specification, but it must be included in the `url` object nevertheless. Much like the `headers` and `headerEntries` properties, these objects exist for property lookup and iteration in logic-less templates, respectively. + +- `env`: an object containing the environment variables set when the server was launched. For instance, if a Dockerfile launches the server through Apache, with an environment variable MAGENTO_GRAPHQL_ENDPOINT: + + ```dockerfile + ENV MAGENTO_GRAPHQL_ENDPOINT https://m2host.com/graphql + CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"] + ``` + + then the context value `env.MAGENTO_GRAPHQL_ENDPOINT` would equal `"https://m2host.com/graphql"`. + +For convenience and concision, frequently used strings should be registered as self-referential top-level context objects. Examples include: + +- `GET`: the string `'GET'` must be included in the global context for convenience, to [reduce boilerplate](#reducing-boilerplate) + +- `POST`: the string `'POST'` must be included in the global context for convenience, to [reduce boilerplate](#reducing-boilerplate) + +- `mustache`: the string `'mustache'`, included in the global context for convenience, to [reduce boilerplate](#reducing-boilerplate) + +- Other strings that must be preset as literals in the context include: + + - `text/html` + - `text/plain` + - `application/json` + - `utf-8` + - `latin-1` + - `base64` + - `hex` + - All valid HTTP response codes, i.e. `200`, `404`, `500`, and all others. + +#### Context Path Syntax + +In UPWARD configuration, **a bare string is treated as a context lookup by default.** Some resolver configuration properties must treat bare strings as literal strings, and the default contains many constants for common strings, as mentioned earlier, so a definition file may appear to use many literal strings, but almost all string values are context lookups. + +A context lookup resembles "dot lookup" notation in JavaSript or Python, though it has simpler rules and cannot be dynamically generated. + +Rules: + +- **Valid characters**: A context lookup may contain any UTF-8 characters except control characters, whitespace, or newlines. All characters that are not lookup operators must be treated as part of a property name. _For the sake of easier manipulation in common programming languages, it is a best to use context names which are legal identifiers in those languages.)_ + - A context lookup cannot begin with a dot ., which is the lookup operator. +- **Lookup operators**: The dot . denotes a property lookup on an object. A positive integer property name in a context lookup, such as `characters.0.name`, will perform array access by index. Array indices must be positive integers; they must be specified literally, just like the rest of a context lookup string, and cannot be dynamically evaluated. +- A context lookup always starts with a **basename**. This may be any string that can be a valid YAML property name. The basename has special significance; when a top-level resolver completes resolution, it assigns a value to this name in the global context, which should trigger resolution of all paths beginning with that name. +- **Behavior for undeclared properties**: Undeclared property lookup should silently fail, resolving the empty string. If the **basename** `foo` is assigned, but it is not an object or it does not contain the property `bar`, then the context lookup `foo.bar` should wait until `foo` is assigned, and then yield the empty string. + +Parts of a context lookup: + +```txt + string property names +basename | | + | | array index | + | | | | + uxbridges.characters.0.name + | | + named property lookups +``` + +The above context lookup behaves at runtime as an instruction to wait until the `uxbridges` basename has been assigned. There must be a definition elsewhere in the definition file which assigns this property, e.g. + +```yaml +uxbridges: + resolver: service + method: POST + resolver: inline + inline: false + url: + resolver: inline + inline: http://stapi.co/api/v1/rest/character/search + headers: + resolver: inline + inline: + content-type: application/x-www-form-urlencoded + query: + resolver: inline + inline: '{ + characters @rest(type: "Character", path: ) + }' +``` + +The above definition would assign an [`HttpResponse`](#http-response) to the `uxbridges` basename once it has run. An HTTP response has no `characters` property, so the example context lookup would resolve to the empty string. However, an HTTP response does have a `body` property, so the lookup `uxbridges.body.characters.0.name` would resolve to `Kevin Uxbridge`. + +:information_source: _(Array and list handling is intentionally rudimentary in UPWARD, because of the potential for scope confusion, performance and security issues in iteration. The only recommended use case for list lookup is when a web service returns a list of items expected to have only one result, so that the result may be lifted out into a scalar value.)_ + +Since arbitrary property lookups do not through exceptions and instead return a default empty string, use [pattern matching](#conditional-resolver) to test the success or failure of requests and responses: + +```yaml +matches: uxbridges.body.characters.0..name +pattern: '.' +``` + +The regex dot character will pass if the string is non-empty, and fail if it is empty. This pattern should be common in UPWARD definitions. + +#### Using context + +Anywhere a in the definition file where a resolver is allowed, you may substitute a **context lookup** instead. A context path indicates a dependency on another root context value, which causes the current branch to wait until that value is resolved. In this example: + +```yaml +body: + resolver: conditional + when: + - matches: request.url.query.shipmentId + pattern: '\d+' + use: + resolver: template + engine: mustache + template: + resolver: file + file: + resolver: inline + inline: './renderShipment.mst' + provide: + shipment: + resolver: service + query: + resolver: file + file: + resolver: inline + inline: './getShipment.graphql' + variables: + id: request.url.query.shipmentId + - matches: request.url.query.shipmentName + pattern: '\w+' + use: + resolver: template + engine: mustache + template: + resolver: file + file: + resolver: inline + inline: './renderShipment.mst' + provide: + shipment: + resolver: service + url: env.SHIPMENTS_SVC + query: + resolver: file + file: + resolver: inline + inline: './getShipment.graphql' + variables: + name: request.url.query.shipmentName + - matches: request.url.query.shipmentTrackingNumber + pattern: '[\w\d\.]+' + use: + resolver: template + engine: mustache + template: + resolver: file + file: + resolver: inline + inline: './renderShipment.mst' + provide: + shipment: + resolver: service + url: env.SHIPMENTS_SVC + query: + resolver: file + file: + resolver: inline + inline: './getShipment.graphql' + variables: + trackingNumber: request.url.query.shipmentTrackingNumber + default: + inline: 'Please provide a query' +``` + +The matchers use the context lookups `request.url.query.shipmentId`, `request.url.query.shipmentName`, and `request.url.query.shipmentTrackingNumber` to asynchronously obtain values for comparison, as soon as they have been derived. The request object is part of the initial context, but you can also declare dependencies on other context values you have defined. The above definition could be more concisely expressed using intermediate values and more context lookups: + +```yaml + +body: + resolver: conditional + when: + - matches: gqlVariables + pattern: null + use: + inline: 'Please provide a query' + default: + resolver: template + engine: mustache + template: + resolver: file + file: + resolver: inline + inline: './renderShipment.mst' + provide: + shipment: + resolver: service + url: env.SHIPMENTS_SVC + query: + resolver: file + file: + resolver: inline + inline: './getShipment.graphql' + variables: gqlVariables +gqlVariables: + resolver: conditional + when: + - matches: request.url.query.shipmentId + pattern: '\d+' + use: + resolver: inline + inline: + id: request.url.query.shipmentId + - matches: request.url.query.shipmentName + pattern: '\w+' + use: + resolver: inline + inline: + name: request.url.query.shipmentName + - matches: request.url.query.shipmentTrackingNumber + pattern: '[\w\d\.]+' + use: + resolver: inline + inline: + trackingNumber: request.url.query.trackingNumber + default: null +``` + +The above definition produces a server with identical behavior to the previous query, in 30% fewer lines. This is not the only way to use context values to reduce the first, repetitive definition, but it demonstrates the idea. If the GraphQL service set an order of precedence for the shipment query which used the same priority logic as the ConditionalResolver, the definition could be further reduced to use the same variable set in all cases: + +```yaml +body: + resolver: conditional + when: + - matches: request.url.query.shipmentId + pattern: '\d+' + use: getShipment + - matches: request.url.query.shipmentName + pattern: '\w+' + use: getShipment + - matches: request.url.query.shipmentTrackingNumber + pattern: '[\w\d\.]+' + use: getShipment + default: + inline: 'Please provide a query' +getShipment: + resolver: template + engine: mustache + template: + resolver: file + file: + resolver: inline + inline: './renderShipment.mst' + provide: + shipment: + resolver: service + url: env.SHIPMENTS_SVC + query: + resolver: file + file: + resolver: inline + inline: './getShipment.graphql' + variables: + id: request.url.query.shipmentId + name: request.url.query.shipmentName + trackingNumber: request.url.query.trackingNumber +``` + +Setting top-level context properties for reusable values is an important way to reduce verbosity and code duplication in the definition file. See [Reducing boilerplate](#reducing-boilerplate) for more practices to reduce verbosity in a definition file. + +#### Context persistence and size + +When writing UPWARD definitions, no distinction needs to be made between "persistent" context values that should be the same for each request, and other values that may differ for each request. An UPWARD server must do topological sorting to determine the order in which to run Resolvers, so an efficient implementation can identify the parts of the root context that are not dependent on the incoming request and cache or discard them. + +## Resolver Reference + +A Resolver is an object which describes how a value is obtained. There are five kinds of Resolver: + +- `InlineResolver` adds hardcoded values +- `FileResolver` loads files from a filesystem +- `ServiceResolver` places GraphQL queries and loads the result set +- `TemplateResolver` renders a template string against the context +- `ConditionalResolver` does branch logic using pattern matching on context values +- `ProxyResolver` delegates request/response handling to a proxy +- `DirectoryResolver` delegates request/response handling to a static file directory + +Each Resolver takes different configuration parameters. Like a context lookup string, a resolver represents an operation which will execute and then deliver its results upward in the tree, until all dependencies of the top-level `status`, `headers`, and `body` definitions are resolved. + +### InlineResolver + +There is sometimes a need to put literal values in the context. However, a string property value performs a context lookup: + +```yaml +body: 'Hello world!' +``` + +The above expression uses `Hello world!` as a property name and tries to substitute the value of the context property named `Hello world!`. This is likely not the intent of the user. To set the `body` to the literal string 'Hello world!', use an `InlineResolver`: + +```yaml +body: + resolver: inline + inline: 'Hello world!' +``` + +The `inline` of an InlineResolver may be of any type; it may be a primitive value, a list, or an object of arbitrary depth. List values and Object properties may be context lookups or resolvers themselves, so when building lists or objects, make sure to use InlineResolvers again at every other level of depth. _(A consequence of this is that it is difficult and verbose to set a literal value resembling a resolver configuration, such as `{ "resolver": "inline" }`, but this seems to be a minor inconvenience.)_ + +#### InlineResolver Configuration Options + +| Property | Type | Default | Description +| -------- | ---- | ------- | --------------------------------------------- +| `inline` | `any`| | _Required._ The value to be assigned to context. + +### FileResolver + +Queries and templates are often large, and often reused by other systems. They can be inline strings, as in the [first example](#simple-example), but they should usually be separate files, referenced with a `FileResolver`. + +```yaml +query: + resolver: file + file: + resolver: inline + inline: './productDetail.graphql' + encoding: + resolver: inline + inline: 'utf-8' +``` + +The above expression loads the content of the file `./productDetail.graphql` and sets it as the property `query`. The file path is resolved relative to the location of the definition file. The `encoding` property is optional. + +#### FileResolver Configuration Options + +| Property | Type | Default | Description +| -------- | ---------- | ------- | --------------------------------------------- +| `file` | `Resolved` | | _Required_. Path to the file to be read. Resolved relative to the definition file. +| `encoding` | `Resolved` | `utf-8` | Character set to use when reading the file as text. Can be `utf-8`, `latin-1`, or `binary`. +| `parse` | `Resolved` | `auto` | Attempt to parse the file as a given file type. The default of `auto` should attempt to determine the file type from its extension. The value `text` will effectively disable parsing. + +#### Parsing + +An UPWARD server must support pre-parsing of `graphql`, `json`, and `mustache` files according to their respective specifications, but should support as many filetypes as necessary and may support custom filetypes. + +#### FileResolver Error Handling + +If the file cannot be found or there were any other failures reading the file, the resolver must resolve an object with a single `errors` property instead of a string or a parsed value. Errors must be formatted like [GraphQL errors][graphql spec errors property]. + +:information_source: _(File contents are not expected to change during server runtime, so an UPWARD-compliant server should cache file contents on startup.)_ + +#### FileResolver shorthand + +Filenames are usually not variable; they will usually be specified as literals, rather than dynamically resolved out of context. For readability and convenience, a "shorthand syntax" must be available to imply a FileResolver that loads and parses a UTF-8 encoded file from a string filesystem path. Instead of explicitly resolving a filepath as an inline string: + +```yaml +query: + resolver: file + file: + resolver: inline + inline: './path/to/file.graphql' +``` + +The shorthand syntax should allow the following to be equivalent: + +```yaml +query: './path/to/file.graphql' +``` + +An UPWARD-compliant server must treat a barestring as a literal filepath instead of a context lookup when: + +- The string is being argued to a configuration parameter that accepts a FileResolver for the implied type of the file after parsing + + AND + +- The string begins with one of the following prefixes: + - a relative path prefix, `./` and/or one or more `../` + - an absolute path prefix, `/` or `C:\` or any other Windows drive letter + - a file URI scheme, `file://` + + AND + +- Upon checking the filesystem, the string resolves to a [regular file][regular file] (not a symlink, device, or directory) + +In the latter case, if the shorthand string neither resolves to a legal file, nor exists as a resolvable context value, an UPWARD-compliant server should raise a detailed error message explaining this. + +### ServiceResolver + +An UPWARD server uses a `ServiceResolver` to obtain live data from a GraphQL backing service. URL, method, and headers can be specified manually; the query itself is constructed using `query` and `variables` parameters. + +```yaml +documentResult: + resolver: service + url: + resolver: inline + inline: 'https://example.com/graphql' + method: + resolver: inline + inline: POST + headers: + resolver: inline + inline: + 'content-type': 'application/json' + accept: 'application/json' + # A small inline template renders the full syntax of the OAuth Bearer + # token header, instead of just the plain token. + authorization: + resolver: template + engine: 'mustache' + template: + resolver: 'inline' + inline: 'Bearer {{env.BEARER_TOKEN}}' + query: + resolver: inline + inline: 'query getDocument($id: String!) { + document(id: $id) { + title + contents + } + }' + variables: + resolver: inline + inline: + # This is a barestring indicating a context lookup. It resolves to the + # `id` value in the URL query string of the request, using the builtin + # `request` context object. + id: request.url.query.id +``` + +:information_source: _(For the purposes of demonstration, the query here is resolved inline. The best practice is to store queries in files and use FileResolvers to obtain them.)_ + +#### ServiceResolver Configuration Options + +| Property | Type | Default | Description +| --------- | ------------------ | --------------------------- | --------------- +| `url` | `Resolved` | `https://localhost/graphql` | _Required_. The URL of the service endpoint to call. +| `method` | `Resolved` | `POST` | The HTTP method to use. While GraphQL queries are typically POSTS, some services expose GraphQL over GET instead. +| `headers` | `Resolved>` | | Additional HTTP headers to send with the GraphQL request. Some headers are set automatically, but the `headers` configuration can append to headers that can have multiple values. +| `query` | `Resolved` | | _Required_. The GraphQL query object. Can either be a parsed query, or a string that can be parsed as a valid query. +| `variables` | `Resolved>` | `{}` | Variables to use with the GraphQL query. Must resolve to an object with keys and values, almost always with an InlineResolver. + +**ServiceResolvers always use GraphQL.** To obtain data from a non-GraphQL service, an UPWARD server may implement client-side directives which change the behavior of a GraphQL query, such as [apollo-link-rest][apollo-link-rest], and place the directives in the query itself. This should be transparent to the UPWARD server itself, which delegates the service call to a GraphQL client. If an UPWARD server's GraphQL client has no implementation for such a directive, then it must pass the query unmodified to the backing service to handle the directive. + +#### Example REST service call + +The below behavior is not standard or normative: it is an example of how one might query a REST service using an UPWARD server whose GraphQL client implementation has a `@rest` directive like [apollo-link-rest][apollo-link-rest]. + +```yaml +documentResult: + resolver: service + url: env.REST_API_ENDPOINT + method: + resolver: inline + inline: GET + headers: + # In contrast to the template above, this assumes that en environment + # variable is already set with the "Bearer " format. + inline: + authorization: env.BEARER_TOKEN_STRING + query: 'query getDocument($id: String!) { + document(id: $id) @rest(type: "Document", path: "/documents/{args.id}") { + title + contents + } + links(documentId: $id) { + name + url + } + }' + variables: + inline: + id: request.url.query.id +``` + +#### Response Assignment + +The GraphQL specification requires that a successful response to a GraphQL query have a [root "data" property][graphql spec data property], an object whose properties correspond to the entities returned, and an that a failed response have a [root "errors" property][graphql spec errors property]. **An UPWARD server must assign the entire root object to the named context value.** The query above would, if successful, result in a context with `documentResult.data.document` and `documentResult.data.links` properties. If the query failed, the context would contain `documentResult.errors`. + +#### ServiceResolver Error Handling + +The GraphQL specification [defines error behavior][graphql spec errors] clearly, and UPWARD servers should pass the `errors` collection from a GraphQL response as described above. Other errors can occur during resolution, such as: + +- Network errors: the URL is unresolvable or unresponsive +- Parse errors: a dynamically supplied `query` could not be parsed +- Validation errors: required variables were absent or the wrong type + +An UPWARD server should format these errors the same way that GraphQL services do; it should return an object with an `errors` array containing all errors encountered. + +### TemplateResolver + +Once the UPWARD server assembles the data for a response, it must turn that data into a response body. The `TemplateResolver` renders a template into a string, executing it with the context object as its root value by default. UPWARD servers must provide a Mustache template renderer that adheres to the [Mustache specification][mustache spec]. UPWARD servers can also provide other template renderers, which must evaluate context objects into strings. + +```yaml +status: + resolver: inline + inline: 200 +headers: + resolver: inline + inline: + 'content-type': + resolver: inline + inline: 'text/html' +body: + resolver: template + engine: + resolver: inline + inline: mustache + root: documentResult.data.document + template: + resolver: inline + inline: | + {{> headtag}} + {{> header}} +
+

{{title}}

+
+ {{& contents}} +
+
+ {{> footer}} +``` + +The above configuration resolves into an HTML document displaying content from the `documentResult.data.document` context value. Its use of [Mustache partials][mustache partials] implies that additional files called `headtag.mst`, `header.mst`, and `footer.mst` exist in the directory containing the definition file. Attempting to include a missing partial should raise an error as soon as the template is resolved, ideally at server startup time. + +:information_source: _(For illustrative purposes, the above uses an InlineResolver where it would be more appropriate to use a FileResolver to obtain the template, as with the query in the ServiceResolver example.)_ + +#### TemplateResolver Configuration Options + +| Property | Type | Default | Description +| --------- | ------------------ | --------------------------- | --------------- +| `engine` | `Resolved` | | _Required_. The label of the template engine to use. +| `provide` | `Resolved>` | | _Required._ A list, or an object mapping, of values to make available in the template. Passing the entire context to a template for evaluation can cause cyclic dependencies. +| `template` | `Resolved` | | The template to render. + +#### Template Context + +The entire context cannot be available for template render; that would cause an immediate circular dependency, since the template's output is added to context! Instead, use the `provide` argument to select what values the template actually needs. They will be available, at root, inside the template. + +The `provide` argument can be a list: + +```yaml +provide: + - env + - articleResult +``` + +The resulting template eval context might look like this: + +```json +"env": { + "envVars": "here" +}, +"articleResult": { + "data": { + "article": { + articleContents: 'here' + } + } +} +``` + +*Lists may only inject "base" context properties.* The above `articleResult` could not be `articleResult.data.article` when using the list format. + +The other, more powerful option for the `provide` argument is to provide a `mapping`, as a simple object. A mapping must resolve to a plain object of string keys and context values. It might appear as: + +```yaml +provide: + inline: + article: articleResult.data.article +``` + +This would give the template a single root property "article", thus flatting out the template tree and making templates more readable. + +```json +"article": { + articleContents: 'here' +} +``` + +#### Template Engines + +The `engine` property must resolve to a string labeling a supported template engine. The only required template engine is Mustache, and its label must be `mustache`. An UPWARD server may support additional template engines. For instance, an UPWARD server may support [ReactJS server-side rendering][react dom server]. + +##### Example React DOM Server support + +```yaml +body: + resolver: template + engine: + resolver: inline + inline: react + provide: + inline: + document: documentResult.data.document + query: request.url.query + template: + resolver: file + file: + resolver: inline + inline: './build/RootComponents/Document.js' +``` + +The above configuration assumes support for a template engine labeled `react`. The underlying template engine could be a simple Node module: + +```js +const { createElement } = require('react'); +const { renderToString } = require('react-dom/server'); + +module.exports = (props, component) => + renderToString(createElement(require(component), props)); +``` + +Attaching such a template engine to a Resolver would be trivial. + +#### TemplateResolver Error Handling + +If the engine is unknown or not supported, the UPWARD server should detect this as soon as possible and send a 500 error. + +If there were any errors parsing the template, evaluating, or serializing the data, the resolver must resolve an object with an `errors` array instead of a rendered string. Errors must be formatted like [GraphQL errors][graphql spec errors property]. + +### ConditionalResolver + +A ConditionalResolver tests a context value for a particular pattern, and then yields to another Resolver depending on the match results. + +The ConditionalResolver is the only branch logic operation available in the UPWARD specification. It performs pattern matching on a context value using [Perl compatible regular expressions][pcre]. It can only test a single context value for each potential match, and it has no Boolean operators such as AND, OR, or NOT. All of these logical operations can be achieved through pattern matching, however. It takes two configuration values: `when` must be a list of `Matcher>>`, and `default` must be a resolver to use if none of the `when` conditions are true. + +```yaml +monkey: + resolver: conditional + when: + # All values are coerced to strings for regex matching. + - matches: request.url.query.grab + pattern: '^(true|1)$' + use: + resolver: inline + inline: "do anyway" + - matches: status + pattern: '^403$' + use: + resolver: inline + inline: "see" + default: + resolver: inline + inline: "do" +status: + resolver: inline + inline: 403 +body: + resolver: template + engine: + resolver: inline + inline: mustache + template: + resolver: inline + inline: "

monkey {{ monkey }}.

" +``` + +The above configuration uses a ConditionalResolver to create a context value `monkey`. The `when` list contains two matchers. They are executed in order. The first matcher tests if the request query string has a value `grab` matching the regular expression `^(true|1)$`, If the request query string contains `grab=true` or `grab=1`, this matcher succeeds and the ConditionalResolver yields to the resolver specified in the matcher's `use` property. The context now matches the object: + +```json +{ + "status": 403, + "monkey": "do anyway", + "body": "

monkey do anyway

" +} +``` + +If the request query string does not contain a qualifying `grab` property, the second matcher runs. This matcher tests if the `status` context value matches the regular expression `^403$`, effectively checking if the response status code has already been set to HTTP 403 Forbidden. The `status` resolver runs before the `monkey` resolver, though it is defined later in the document (see [Concurrency](#concurrency)), so the matcher tests against the value `403` (cast to a string) and succeeds. The ConditionalResolver uses the matcher's resolver, and the context now matches the object: + +```json +{ + "status": 403, + "monkey": "see", + "body": "

monkey see

" +} +``` + +If some other configuration provides a different `status`, such as `200`, then neither matcher in the `when` list succeeds, and the ConditionalResolver uses its `default` resolver. The context now matches the object: + +```json +{ + "status": 200, + "monkey": "do", + "body": "

monkey do

" +} +``` + +#### ConditionalResolver Configuration Options + +| Property | Type | Default | Description +| --------- | ------------------ | ----------- | --------------- +| `when` | `Matcher[]` | | | _Required_. The list of matchers to test against context. +| `default` | `Resolved` | | _Required_. The default resolver to use if no matcher succeeds. + +#### Matchers + +A `Matcher` is an object which can only be used as an item in a ConditionalResolver's `when` list. It must have the following properties: + +| Property | Type | Default | Description +| --------- | ------------------ | ------- | --------------- +| `matches` | `string` | | _Required_. The context value to match. Must be a bare string context lookup. +| `pattern` | `string` | | _Required_. [PCRE][pcre] regular expression to use to test against the value in `matches`. +| `use` | `Resolved` | | _Required_. Resolver to use if the match succeeds. + +#### Match context + +During the resolution of a matcher's `use` resolver, properties from the match object are temporarily added to the context. Using these temporary context values, resolvers can extract matching text, or capture groups, from the match itself. + +- `$match.$0` - The full text of the last matched value. +- `$match.$1` - The string captured in the first backreference the regex declared. + +The `$match` object must have additional numbered properties for each backreference. + +#### ConditionalResolver notes + +- Matchers run in sequence, top to bottom. They do not "fall through"; the first + successful matcher will exit the conditional. +- Matchers can only test against context properties; they cannot use a resolver as the `matches` value. +- Each matcher can test only one context property against one pattern. + - However, the pattern can use regex alternation to achieve OR-type logic. + - Additionally, a list of matchers need not each test the same property. +- The `default` resolver is required. +- To achieve AND-like logic, nest ConditionalResolvers to arbitrary depth. +- To achieve OR-like logic, repeat the same resolver configuration in several subsequent matchers. +- If the conditional becomes verbose, consult [Reducing Boilerplate](#reducing-boilerplate) for ways to simplify it. +- Though template engines with logical operators can be also be used to perform branch logic, this is not recommended; it can prevent static analysis of context value dependencies. + +### ProxyResolver + +The ProxyResolver acts as a "passthrough" to another service. It is guaranteed to resolve into an object with `status`, `headers`, and `body` properties from a logical point of view. In implementation, it may handle request objects more directly, for performance purposes. + +A ProxyResolver is an important part of the UPWARD philosophy: a PWA's UPWARD file must describe all of the network behavior that a PWA expects at runtime, and that includes proxies to various backing APIs. + +#### ProxyResolver Example + +```yaml +proxy: + target: env.MAGENTO_BACKEND_DOMAIN + ignoreSSLErrors: true +``` + +#### ProxyResolver Configuration Options + +| Property | Type | Default | Description +| --------- | ------------------ | ----------- | --------------- +| `target` | `Resolved` | | | _Required_. The URL that receives proxied requess. +| `ignoreSSLErrors` | `Resolved` | `false` | Ignore remnote SSL certificate errors (useful for internal communication among containers). + +#### ProxyResolver notes + +ProxyResolvers are special targets for static analysis. Using simple techniques, it should be possible for an analysis system to determine proxying rules by walking the UPWARD tree. This is effectively a _declarative proxy config_, and deployment tools can be enhanced to create proxy servers in front of UPWARD where appropriate. + +### DirectoryResolver + +Like the ProxyResolver, the DirectoryResolver delegates request and response handling to a static server. Unlike the ProxyResolver, it needs access to a local directory, which it will then sere as a public assets folder. Much like the ProxyResolver, this resolver exists to bolster the notion that an UPWARD file can describe the expected behavior of a PWA server-side site, in detail. + +#### DirectoryResolver Example + +```yaml +static: + directory: + inline: './dist' + +``` + +#### DirectoryResolver Configuration Options + +| Property | Type | Default | Description +| --------- | ------------------ | ----------- | --------------- +| `directory` | `Resolved` | | | _Required_. The local directory path to be served. + + +## Reducing boilerplate + +The above examples are fairly verbose, to make the workings of UPWARD configuration especially clear. The UPWARD specification is intentionally verbose in its canonical format, to enable maximal static analysis. However, An UPWARD-compliant server must also include features to reduce boilerplate. + +### Default parameters + +Many resolver configuration parameters have default values, so they can be omitted from configuration if the default is appropriate. + +In the first example of a [ServiceResolver](#serviceresolver) above, some config parameters are actually unnecessary. The default URL for a service call is `http://localhost/graphql`, and the default method is `POST`. MIME headers are automatically generated, so the following configuration would be equivalent: + +```yaml +documentResult: + resolver: service + headers: + resolver: inline + inline: + Authorization: env.BEARER_TOKEN + query: + resolver: inline + inline: 'query getDocument($id: String!) { + document(id: $id) { + title + contents + } + }' + variables: + id: request.url.query.id +``` + +Using the best practice of storing queries in separate files and storing reusable objects as separate context values, the configuration would likely look like this: + +```yaml +documentQuery: './queries/document.graphql' +oauthHeaders: + resolver: inline + inline: + authorization: env.BEARER_TOKEN + +documentResult: + resolver: service + headers: oauthHeaders + query: documentQuery + variables: + id: request.url.query.id +``` + +### Builtin constants + +The default context must contain some builtin constants for common strings, to avoid repetitive use of InlineResolvers. + +In the first example of a [TemplateResolver](#templateresolver) above, some inline resolvers are unnecessary. HTTP response codes, common MIME types, common template engine labels, and other useful values must be preset in the initial context, so the following, shorter configuration is equivalent to the TemplateResolver example: + +```yaml +status: 200 +headers: + resolver: inline + inline: + 'content-type': 'text/html' +body: + resolver: template + engine: 'mustache' + provide: + model: documentResult.data.document + template: + resolver: inline + inline: | + {{> headtag}} + {{> header}} +
+

{{model.title}}

+
+ {{& contents}} +
+
+ {{> footer}} +``` + +### Resolver type inference + +For any value which must be a Resolver or a context lookup, an UPWARD-compatible server should attempt to infer resolver types from a supplied resolver configuration object, rather than requiring a `resolver` name to be specified. Each resolver has required parameters that are mutually exclusive with one another, so a resolver type can be inferred from the presence of those parameters. + +| If parameter exists... | Then infer resolver type: +| ---------------------: | :-----------------------: +| `inline` | `InlineResolver` +| `file` | `FileResolver` +| `query` | `ServiceResolver` +| `engine` | `TemplateResolver` +| `when` | `ConditionalResolver` +| `target` | `ProxyResolver` +| `directory` | `DirectoryResolver` + +Resolver type inference allows configuration to omit `resolver:` parameters, which makes it possible to be far more terse. The example optimized configuration in [Builtin constants](#builtin-constants) could be further reduced: + +```yaml +status: 200 +headers: + inline: + 'content-type': 'text/html' +body: + engine: 'mustache' + root: documentResult.data.document + template: + inline: | + {{> headtag}} + {{> header}} +
+

{{title}}

+
+ {{& contents}} +
+
+ {{> footer}} +``` + +And the example optimized configuration in [Default parameters](#default-parameters) could be further reduced: + +```yaml +documentResult: + headers: + inline: + authorization: env.BEARER_TOKEN + query: './queries/document.graphql' + variables: + id: request.url.query.id +``` + +:information_source: _(Note that the `variables` object cannot be a resolver, as specified in [ServiceResolver configuration options](#serviceresolver-configuration-options) — it must be an object whose keys are expected query variable names and whose values are resolvers, so there is no ambiguity if a GraphQL query expects a variable named, for example, `file`.)_ + +### YAML anchors + +The YAML specificationsupports an [anchor and reference syntax][yaml anchors] which can also be used to shorten configuration files. While this can be used as part of legal YAML parsing, its use is discouraged by UPWARD files, since context resolution is clearer to the reader. Additionally, not all parsers support references in the same way, so the feature should be used with caution. + +[apollo-link-rest]: +[application shell]: +[pwa def]: +[js identifiers]: +[npx]: +[spec-shell-script]: <./test_upward_server.sh> +[yaml anchors]: +[pcre]: +[graphql spec data property]: +[graphql spec errors property]: +[mustache spec]: +[mustache partials]: +[react dom server]: +[topological sorting]: +[url spec]: +[regular file]: diff --git a/packages/upward-spec/UPWARD_MAGENTO.md b/packages/upward-spec/UPWARD_MAGENTO.md new file mode 100644 index 00000000000..fc018a9af4b --- /dev/null +++ b/packages/upward-spec/UPWARD_MAGENTO.md @@ -0,0 +1,80 @@ +# Why UPWARD for Magento PWA Studio + +Magento PWA Studio is meant to be cheap and easy to run; it should not be tied to a specific tech stack. A middle tier server is necessary to decouple PWA Studio from the Magento Theme resolution tier, which requires two-way binding in the filesystem. UPWARD allows us to require this middle tier without requiring a specific tech stack. + +## PWA Studio 1.0 and the Theme Architecture + +The initial architecture for PWA Studio fit the project files for the JavaScript application, both the view components and the build tooling, into the structure of a Magento Theme. This was chosen for several reasons: + +- Existing concepts easier to understand +- Might use today's extensions +- Built-in server side rendering +- Gives power to business users +- Scales up to enterprise and down to SMB + +However, big problems soon emerged with this approach. The main theme was that the coupling between PWA Studio and an instance of Magento was too tight. Problems included: + +- Difficulty connecting the instance of PWA Studio's theme with an instance of Magento in development +- Difficulty configuring Magento correctly to serve static files, and synchronizing that configuration with PWA Studio +- Discovery of how to precisely configure Magento to render the application shell +- The fundamental one-to-one binding of a Magento store and a Magento theme, preventing multicast +- Server-side rendering strategy that either led to bad SEO or required re-implementation of app shell UI + +Perhaps most significantly, there was a great disparity between the structures of the development and production environments. This violation of 12-factor principles meant that any deploy pipeline would be brittle and prone to unexpected failure. + +![Illustration of the tight coupling](tight_coupling.png) + +It became clear that a middle tier was necessary to separate Magento PWAs from Magento's existing rendering system, so that the only relationship between the two would be formal API contracts. Several options presented themselves. + +### Option 1: Use a NodeJS Server in Production + +Since the development server is a NodeJS process, we could recommend that production deployments use a NodeJS middle tier as well. + +| Advantages | Disadvantages | +| ---------- | ------------- | +| Similar to development environment | Dissimilar to other Magento production environments +| Fast, proven, and exciting to community | Different process model requires special ops knowledge +| Compatible with move towards distributed architecture | Incompatible with one of the missions of PWA Studio, to give lower-resource merchants and devs access to its features +| Server-side rendering | Isomorphic (server-and-client) React is harder to write and harder to bundle well for PWA + +The final decision came down to our reluctance to require the Magento ops and dev communities to master another advanced skill on top of all the others Magento 2 requires: the management of NodeJS process pools in production. + +### Option 2: Use a PHP Script or Magento Module in Development + +A custom PHP script, or an actual Magento module, would be easy to set up for the vast majority of today's full stack Magento 2 developers. However, it's a less traditional working style for NodeJS devs, and the tooling might be incompatible. + +| Advantages | Disadvantages | +| ---------- | ------------- | +| Familiarity in Magento community | Unfamiliar in greater frontend community | +| Compatible with Cloud | Requires extra PHP setup for many developers | +| Allows for monolithic, single-store deployments to use a PWA | Does not encourage service-oriented architecture | +| Closer to production environment | Incompatible with frontend best practices and tools | + +Ultimately, maintaining both NodeJS and PHP in the correct versions for a development environment seemed to be an obstacle to inviting new frontend developers into the Magento ecosystem. + +## PWA Studio 2.0 and the UPWARD architecture + +UPWARD aims to collect the advantages of the above approaches. It allows for different programming languages and technology stacks in development and production, but it maintains consistency by requiring that the frontend server be UPWARD-compliant and derive its behavior from a file contained in the PWA project. Development and production are more similar as a result. + +![Looser coupling and more similar envs](looser_coupling_and_similar_envs.png) + +UPWARD includes advantages from both prior options: + +- Supports NodeJS-based middle tier production deployment +- Supports all-PHP distributed architecture if necessary +- Supports monolithic Magento production deployment (pending an UPWARD implementation as an M2 module) +- Supports standard developer tooling with minimal dependencies +- Supports server-side rendering with template language extensibility + +In addition, UPWARD provides the following interesting perks: + +- In an excellent example of dependency inversion, PWA frontend projects can use an UPWARD file to declare their runtime network expectations, instead of having to develop a PWA around an existing middle tier or API layer +- The forced simplicity of the middle tier, with very little possible logical transformation, discourages implicit and undocumented hacks buried in the middle tier +- YAML files are mergeable, like XML files, so extensibility options are copious +- The new one-way connection from an UPWARD PWA to a Magento theme allows for many developers to use one Magento instance, radically simplifying the frontend dev's setup burden. + +![Many-to-one diagram](many_to_one_ci.png) + +More advantages to the UPWARD architecture will appear as it is refined and adopted, and as setbacks occur, we will refine the UPWARD specification in concert with our community. + +Progressive Web Apps are unexplored territory for many, and the best practices in the field are mutating very quickly. Like the GraphQL specification, the HTML5 specification before it, and the REST specification before any of them, this represents Magento's effort to help stabilize a technology sphere in which we are one of the first explorers. It may have been slower and more expensive to implement our PWA Studio middle tier as a specification, rather than a custom server, but we believe it will benefit us, our community, and the greater Web developer community for a long time. diff --git a/packages/upward-spec/backing_services.png b/packages/upward-spec/backing_services.png new file mode 100644 index 00000000000..4207b00f6bb Binary files /dev/null and b/packages/upward-spec/backing_services.png differ diff --git a/packages/upward-spec/bin/upward-spec b/packages/upward-spec/bin/upward-spec new file mode 100755 index 00000000000..c11f7aa88dc --- /dev/null +++ b/packages/upward-spec/bin/upward-spec @@ -0,0 +1,67 @@ +#!/usr/bin/env node +const path = require('path'); +const fs = require('fs'); + +const providedScriptPath = path.resolve(process.argv[2]); + +/** + * Unix permissions Bitmask to check if the script is + * readable and executable. + */ +const accessible = fs.constants.F_OK | fs.constants.X_OK; +try { + fs.accessSync(providedScriptPath, accessible); +} catch (e) { + throw Error( + e.message + + `\n\n\tMake sure ${providedScriptPath} exists and has executable permissions.` + ); +} + +/** + * Pass args to node-tap to expose its options. + */ +const otherArgs = process.argv.slice(3); +const hasArg = wanted => otherArgs.some(arg => arg === wanted); + +const useXunit = hasArg('--xunit'); +const useTap = hasArg('--tap'); + +if (useXunit && useTap) { + console.error( + 'Cannot use both --xunit and --tap flags. Please choose either XUnit output, TAP output, or no arguments for default human-readable output.' + ); +} + +const isPiped = !process.stdout.isTTY; + +const raw = useTap || isPiped; + +// Run tap and pass stdout. +const scenariosDir = path.resolve(__dirname, '../suite/scenarios/'); +const testProcess = require('child_process').spawn( + path.resolve(__dirname, '../node_modules/.bin/tape'), + ['*/tests.js'], + { + cwd: scenariosDir, + env: Object.assign( + { + UPWARD_SERVER_SCRIPT_PATH: providedScriptPath + }, + process.env + ) + } +); + +if (raw) { + testProcess.stdout.pipe(process.stdout); +} else if (useXunit) { + const tapXunit = require('tap-xunit'); + testProcess.stdout + .pipe(tapXunit({ package: 'UPWARD spec' })) + .pipe(process.stdout); +} else { + const tapDiff = require('tap-diff'); + testProcess.stdout.pipe(tapDiff()).pipe(process.stdout); +} +testProcess.stderr.pipe(process.stderr); diff --git a/packages/upward-spec/looser_coupling_and_similar_envs.png b/packages/upward-spec/looser_coupling_and_similar_envs.png new file mode 100644 index 00000000000..8eae122b81d Binary files /dev/null and b/packages/upward-spec/looser_coupling_and_similar_envs.png differ diff --git a/packages/upward-spec/many_to_one_ci.png b/packages/upward-spec/many_to_one_ci.png new file mode 100644 index 00000000000..a53532e8c69 Binary files /dev/null and b/packages/upward-spec/many_to_one_ci.png differ diff --git a/packages/upward-spec/package-lock.json b/packages/upward-spec/package-lock.json new file mode 100644 index 00000000000..f1c7950a7dd --- /dev/null +++ b/packages/upward-spec/package-lock.json @@ -0,0 +1,1617 @@ +{ + "name": "@magento/upward-spec", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@apollographql/apollo-upload-server": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@apollographql/apollo-upload-server/-/apollo-upload-server-5.0.3.tgz", + "integrity": "sha512-tGAp3ULNyoA8b5o9LsU2Lq6SwgVPUOKAqKywu2liEtTvrFSGPrObwanhYwArq3GPeOqp2bi+JknSJCIU3oQN1Q==", + "requires": { + "@babel/runtime-corejs2": "^7.0.0-rc.1", + "busboy": "^0.2.14", + "object-path": "^0.11.4" + } + }, + "@apollographql/graphql-playground-html": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.0.tgz", + "integrity": "sha512-QAZIFrfVRkjvMkUHIQKZXZ3La0V5t12w5PWrhihYEabHwzIZV/txQd/kSYHgYPXC4s5OURxsXZop9f0BzI2QIQ==" + }, + "@babel/runtime-corejs2": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.0.0.tgz", + "integrity": "sha512-Yww0jXgolNtkhcK+Txo5JN+DjBpNmmAtD7G99HOebhEjBzjnACG09Tip9C8lSOF6PrhA56OeJWeOZduNJaKxBA==", + "requires": { + "core-js": "^2.5.7", + "regenerator-runtime": "^0.12.0" + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + }, + "@types/accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/body-parser": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "requires": { + "@types/node": "*" + } + }, + "@types/cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.4.tgz", + "integrity": "sha512-ipZjBVsm2tF/n8qFGOuGBkUij9X9ZswVi9G3bx/6dz7POpVa6gVHcj1wsX/LVEn9MMF41fxK/PnZPPoTD1UFPw==", + "requires": { + "@types/express": "*" + } + }, + "@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" + }, + "@types/express": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.0.tgz", + "integrity": "sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz", + "integrity": "sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w==", + "requires": { + "@types/events": "*", + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "@types/graphql": { + "version": "0.12.6", + "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-0.12.6.tgz", + "integrity": "sha512-wXAVyLfkG1UMkKOdMijVWFky39+OD/41KftzqfX1Oejd0Gm6dOIKjCihSVECg6X7PHjftxXmfOKA/d1H79ZfvQ==" + }, + "@types/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", + "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==" + }, + "@types/mime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", + "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" + }, + "@types/node": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.9.4.tgz", + "integrity": "sha512-fCHV45gS+m3hH17zgkgADUSi2RR1Vht6wOZ0jyHP8rjiQra9f+mIcgwPQHllmDocYOstIEbKlxbFDYlgrTPYqw==" + }, + "@types/range-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.2.tgz", + "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" + }, + "@types/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "@types/ws": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-5.1.2.tgz", + "integrity": "sha512-NkTXUKTYdXdnPE2aUUbGOXE1XfMK527SCvU/9bj86kyFF6kZ9ZnOQ3mK5jADn98Y2vEUD/7wKDgZa7Qst2wYOg==", + "requires": { + "@types/events": "*", + "@types/node": "*" + } + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "apollo-cache-control": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.2.2.tgz", + "integrity": "sha512-N5A1hO6nHZBCR+OCV58IlE7k6hZrFJZTf/Ab2WD8wduLSa0qLLRlCp3rXvD05+jpWa6sdKw03whW2omJ+SyT+w==", + "requires": { + "apollo-server-env": "2.0.2", + "graphql-extensions": "0.1.2" + } + }, + "apollo-datasource": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-0.1.2.tgz", + "integrity": "sha512-AbUxS7Qkz9+T+g19zKRJiA+tBVGVVunzXwd4ftDSYGx1VrF5LJJO7Gc57bk719gWIZneZ02HsVCEZd6NxFF8RQ==", + "requires": { + "apollo-server-caching": "0.1.2", + "apollo-server-env": "2.0.2" + } + }, + "apollo-engine-reporting": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/apollo-engine-reporting/-/apollo-engine-reporting-0.0.2.tgz", + "integrity": "sha512-Fe/1oxC8rUXRrBTMUiqs5PSb6hnMOJHuttJMhs83u5POfplc4QrKJZtEEU4Ui8mxeJGaGNWbWf+D4q645xdQLA==", + "requires": { + "apollo-engine-reporting-protobuf": "0.0.1", + "apollo-server-env": "2.0.2", + "async-retry": "^1.2.1", + "graphql-extensions": "0.1.2", + "lodash": "^4.17.10" + } + }, + "apollo-engine-reporting-protobuf": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.0.1.tgz", + "integrity": "sha512-AySoDgog2p1Nph44FyyqaU4AfRZOXx8XZxRsVHvYY4dHlrMmDDhhjfF3Jswa7Wr8X/ivvx3xA0jimRn6rsG8Ew==", + "requires": { + "protobufjs": "^6.8.6" + } + }, + "apollo-link": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.2.tgz", + "integrity": "sha512-Uk/BC09dm61DZRDSu52nGq0nFhq7mcBPTjy5EEH1eunJndtCaNXQhQz/BjkI2NdrfGI+B+i5he6YSoRBhYizdw==", + "requires": { + "@types/graphql": "0.12.6", + "apollo-utilities": "^1.0.0", + "zen-observable-ts": "^0.8.9" + } + }, + "apollo-server": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/apollo-server/-/apollo-server-2.0.5.tgz", + "integrity": "sha512-Li840fL17GGQWnTi6HpzmzgQb3MbBtJLmKzWgbXU9FJQsp1ZbiLnOgT136DeBf7n1gbvgLuxlLGgfYrD3QAwnA==", + "requires": { + "apollo-server-core": "2.0.4", + "apollo-server-express": "2.0.4", + "express": "^4.0.0", + "graphql-subscriptions": "^0.5.8", + "graphql-tools": "^3.0.4" + } + }, + "apollo-server-caching": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.1.2.tgz", + "integrity": "sha512-jBRnsTgXN0m8yVpumoelaUq9mXR7YpJ3EE+y/alI7zgXY+0qFDqksRApU8dEfg3q6qUnO7rFxRhdG5eyc0+1ig==", + "requires": { + "lru-cache": "^4.1.3" + } + }, + "apollo-server-core": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.0.4.tgz", + "integrity": "sha512-6kNaQYZfX2GvAT1g9ih0rodfRl4hPL1jXb7b+FvQ1foFR5Yyb3oqL2DOcP65gQi/7pGhyNRUAncPU18Vo3u9rQ==", + "requires": { + "@apollographql/apollo-upload-server": "^5.0.3", + "@types/ws": "^5.1.2", + "apollo-cache-control": "0.2.2", + "apollo-datasource": "0.1.2", + "apollo-engine-reporting": "0.0.2", + "apollo-server-caching": "0.1.2", + "apollo-server-env": "2.0.2", + "apollo-server-errors": "2.0.2", + "apollo-tracing": "0.2.2", + "graphql-extensions": "0.1.2", + "graphql-subscriptions": "^0.5.8", + "graphql-tag": "^2.9.2", + "graphql-tools": "^3.0.4", + "hash.js": "^1.1.3", + "lodash": "^4.17.10", + "subscriptions-transport-ws": "^0.9.11", + "ws": "^5.2.0" + } + }, + "apollo-server-env": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-2.0.2.tgz", + "integrity": "sha512-LsSh2TSF1Sh+TnKxCv2To+UNTnoPpBGCXn6fPsmiNqVaBaSagfZEU/aaSu3ftMlmfXr4vXAfYNUDMKEi+7E6Bg==", + "requires": { + "node-fetch": "^2.1.2", + "util.promisify": "^1.0.0" + } + }, + "apollo-server-errors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-2.0.2.tgz", + "integrity": "sha512-zyWDqAVDCkj9espVsoUpZr9PwDznM8UW6fBfhV+i1br//s2AQb07N6ektZ9pRIEvkhykDZW+8tQbDwAO0vUROg==" + }, + "apollo-server-express": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.0.4.tgz", + "integrity": "sha512-9mxcFpnTgQTmrsvVRRofEY7N1bJYholjv99IfN8puu5lhNqj8ZbOPZYrw+zd+Yh4rZSonwx76ZzTRzM00Yllfw==", + "requires": { + "@apollographql/apollo-upload-server": "^5.0.3", + "@apollographql/graphql-playground-html": "^1.6.0", + "@types/accepts": "^1.3.5", + "@types/body-parser": "1.17.0", + "@types/cors": "^2.8.4", + "@types/express": "4.16.0", + "accepts": "^1.3.5", + "apollo-server-core": "2.0.4", + "body-parser": "^1.18.3", + "cors": "^2.8.4", + "graphql-subscriptions": "^0.5.8", + "graphql-tools": "^3.0.4", + "type-is": "^1.6.16" + }, + "dependencies": { + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "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" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + } + } + }, + "apollo-tracing": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.2.2.tgz", + "integrity": "sha512-zrpLRvaAqtzGufc1GfV+691xQtzq5elfBydg/7wzuaFszlMH66hkLas5Dw36drUX21CbCljOuGYvYzqSiKykuQ==", + "requires": { + "apollo-server-env": "2.0.2", + "graphql-extensions": "0.1.2" + } + }, + "apollo-utilities": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.19.tgz", + "integrity": "sha512-pyVxizjIevHFfKhtc9FLEsGHmqiK0kHx1aBdJRUXDt+X+yjoVa/fVeCEo9t0NddGximemxxrQnq6lSkbIQvDlA==", + "requires": { + "fast-json-stable-stringify": "^2.0.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "async-retry": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.1.tgz", + "integrity": "sha512-FadV8UDcyZDjzb6eV7MCJj0bfrNjwKw7/X0QHPFCbYP6T20FXgZCYXpJKlQC8RxEQP1E6Xs8pNHdh3bcrZAuAw==", + "requires": { + "retry": "0.10.1" + } + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "~1.6.15" + }, + "dependencies": { + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "requires": { + "color-name": "1.1.1" + } + }, + "color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", + "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "csv-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-3.0.0.tgz", + "integrity": "sha512-h58BzkfwYNY1gyfl6NUZe1OKxXy/Pn6ZeXlkz3fdLdg/r3Om+lgMySw9SxH7rDs8ARFFyIc6UI6fLnaeRkjNPQ==" + }, + "death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha1-AaqcQB7dknUFFEcLgmY5DGbGcxg=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "deprecated-decorator": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz", + "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "diff": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", + "integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=" + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "es-abstract": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "requires": { + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==" + }, + "events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=" + }, + "express": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "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.3", + "qs": "6.5.1", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", + "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" + }, + "dependencies": { + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "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" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graphql": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.13.2.tgz", + "integrity": "sha512-QZ5BL8ZO/B20VA8APauGBg3GyEgZ19eduvpLWoq5x7gMmWnHoy8rlQWPLmWgFvo1yNgjSEFMesmS4R6pPr7xog==", + "requires": { + "iterall": "^1.2.1" + } + }, + "graphql-extensions": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.1.2.tgz", + "integrity": "sha512-A81kfGtOKG0/1sDQGm23u60bkTuk9VDof0SrQrz7yNpPLY48JF11b8+4LNlYfEBVvceDbLAs1KRfyLQskJjJSg==", + "requires": { + "apollo-server-env": "2.0.2" + } + }, + "graphql-subscriptions": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-0.5.8.tgz", + "integrity": "sha512-0CaZnXKBw2pwnIbvmVckby5Ge5e2ecmjofhYCdyeACbCly2j3WXDP/pl+s+Dqd2GQFC7y99NB+53jrt55CKxYQ==", + "requires": { + "iterall": "^1.2.1" + } + }, + "graphql-tag": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.9.2.tgz", + "integrity": "sha512-qnNmof9pAqj/LUzs3lStP0Gw1qhdVCUS7Ab7+SUB6KD5aX1uqxWQRwMnOGTkhKuLvLNIs1TvNz+iS9kUGl1MhA==" + }, + "graphql-tools": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-3.1.1.tgz", + "integrity": "sha512-yHvPkweUB0+Q/GWH5wIG60bpt8CTwBklCSzQdEHmRUgAdEQKxw+9B7zB3dG7wB3Ym7M7lfrS4Ej+jtDZfA2UXg==", + "requires": { + "apollo-link": "^1.2.2", + "apollo-utilities": "^1.0.1", + "deprecated-decorator": "^0.1.6", + "iterall": "^1.1.3", + "uuid": "^3.1.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "hash.js": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", + "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "dependencies": { + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + } + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "iterall": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", + "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" + }, + "mime-types": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "requires": { + "mime-db": "~1.35.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "node-fetch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz", + "integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + }, + "object-keys": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" + }, + "object-path": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", + "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=" + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "parse-ms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", + "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=" + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "plur": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz", + "integrity": "sha1-24XGgU9eXlo7Se/CjWBP7GKXUVY=" + }, + "pretty-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz", + "integrity": "sha1-QlfCVt8/sLRR1q/6qwIYhBJpgdw=", + "requires": { + "is-finite": "^1.0.1", + "parse-ms": "^1.0.0", + "plur": "^1.0.0" + } + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "protobufjs": { + "version": "6.8.8", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", + "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "@types/node": "^10.1.0", + "long": "^4.0.0" + } + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" + }, + "resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "requires": { + "path-parse": "^1.0.5" + } + }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "requires": { + "through": "~2.3.4" + } + }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "string.prototype.trim": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.0", + "function-bind": "^1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "subscriptions-transport-ws": { + "version": "0.9.14", + "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.14.tgz", + "integrity": "sha512-n1+mgupVdJn1MIls1ZhSJurJjc+islp7Tv9EIaEJ3HAd9DaINneKq0KRqOYNOrvUI7orVWGomEnlIxoudjfbeA==", + "requires": { + "backo2": "^1.0.2", + "eventemitter3": "^3.1.0", + "iterall": "^1.2.1", + "symbol-observable": "^1.0.4", + "ws": "^5.2.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "tap-diff": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tap-diff/-/tap-diff-0.1.1.tgz", + "integrity": "sha1-j78zM9hWQ/7qG/F1m5CCCwSjfd8=", + "requires": { + "chalk": "^1.1.1", + "diff": "^2.2.1", + "duplexer": "^0.1.1", + "figures": "^1.4.0", + "pretty-ms": "^2.1.0", + "tap-parser": "^1.2.2", + "through2": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "tap-parser": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-1.3.2.tgz", + "integrity": "sha1-EgxQiciMPIp5PvKIhn3jIeGPjCI=", + "requires": { + "events-to-array": "^1.0.1", + "inherits": "~2.0.1", + "js-yaml": "^3.2.7", + "readable-stream": "^2" + } + }, + "tap-xunit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tap-xunit/-/tap-xunit-2.3.0.tgz", + "integrity": "sha512-YVsURNvn1wfVUWb5wjansxhfbfeo2hOBTUbVgZoaMO8lyZzpiSi9IiZTZ7JG56m6A49LeWjfJIx/SnAre41V/A==", + "requires": { + "duplexer": "~0.1.1", + "minimist": "~1.2.0", + "tap-parser": "~1.2.2", + "through2": "~2.0.0", + "xmlbuilder": "~4.2.0", + "xtend": "~4.0.0" + }, + "dependencies": { + "tap-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-1.2.2.tgz", + "integrity": "sha1-Xi9pcGEfB5x8+FfeHceqG0gN56U=", + "requires": { + "events-to-array": "^1.0.1", + "inherits": "~2.0.1", + "js-yaml": "^3.2.7", + "readable-stream": "^2" + } + } + } + }, + "tape": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-4.9.1.tgz", + "integrity": "sha512-6fKIXknLpoe/Jp4rzHKFPpJUHDHDqn8jus99IfPnHIjyz78HYlefTGD3b5EkbQzuLfaEvmfPK3IolLgq2xT3kw==", + "requires": { + "deep-equal": "~1.0.1", + "defined": "~1.0.0", + "for-each": "~0.3.3", + "function-bind": "~1.1.1", + "glob": "~7.1.2", + "has": "~1.0.3", + "inherits": "~2.0.3", + "minimist": "~1.2.0", + "object-inspect": "~1.6.0", + "resolve": "~1.7.1", + "resumer": "~0.0.0", + "string.prototype.trim": "~1.1.2", + "through": "~2.3.8" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xmlbuilder": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", + "requires": { + "lodash": "^4.0.0" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "zen-observable": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.9.tgz", + "integrity": "sha512-Y9kPzjGvIZ5jchSlqlCpBW3I82zBBL4z+ulXDRVA1NwsKzjt5kwAi+gOYIy0htNkfuehGZZtP5mRXHRV6TjDWw==" + }, + "zen-observable-ts": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.9.tgz", + "integrity": "sha512-KJz2O8FxbAdAU5CSc8qZ1K2WYEJb1HxS6XDRF+hOJ1rOYcg6eTMmS9xYHCXzqZZzKw6BbXWyF4UpwSsBQnHJeA==", + "requires": { + "zen-observable": "^0.8.0" + } + } + } +} diff --git a/packages/upward-spec/package.json b/packages/upward-spec/package.json new file mode 100644 index 00000000000..4b186e14aa9 --- /dev/null +++ b/packages/upward-spec/package.json @@ -0,0 +1,37 @@ +{ + "name": "@magento/upward-spec", + "version": "2.0.0-rc2.0.5", + "description": "UPWARD specification, guide, and test suite.", + "main": "./suite/index.js", + "bin": { + "upward-spec": "./bin/upward-spec" + }, + "engines": { + "node": ">=8.0.0" + }, + "repository": "github:magento-research/pwa-studio", + "keywords": [ + "magento", + "pwa", + "specification", + "upward" + ], + "author": "Magento Commerce", + "license": "SEE LICENSE IN LICENSE.txt", + "bugs": { + "url": "https://github.com/magento-research/pwa-studio/issues" + }, + "homepage": "https://github.com/magento-research/pwa-studio/tree/master/packages/upward-spec#readme", + "dependencies": { + "apollo-server": "^2.0.5", + "chalk": "^2.4.1", + "csv-parse": "^3.0.0", + "death": "^1.1.0", + "graphql": "^0.13.2", + "graphql-tag": "^2.9.2", + "js-yaml": "^3.12.0", + "tap-diff": "^0.1.1", + "tap-xunit": "^2.3.0", + "tape": "^4.9.1" + } +} diff --git a/packages/upward-spec/suite/MockGQLService.js b/packages/upward-spec/suite/MockGQLService.js new file mode 100644 index 00000000000..e7ff6d1a97f --- /dev/null +++ b/packages/upward-spec/suite/MockGQLService.js @@ -0,0 +1,19 @@ +const { ApolloServer, gql } = require('apollo-server'); +module.exports = async function(schema, Query) { + const typeDefs = gql(schema); + const server = new ApolloServer({ + typeDefs, + resolvers: { Query } + }); + const { url } = server.listen(); + return { + url, + server, + async close() { + return new Promise(resolve => { + server.on('close', resolve); + server.close(); + }); + } + }; +}; diff --git a/packages/upward-spec/suite/assertOnResponse.js b/packages/upward-spec/suite/assertOnResponse.js new file mode 100644 index 00000000000..3de81a36007 --- /dev/null +++ b/packages/upward-spec/suite/assertOnResponse.js @@ -0,0 +1,73 @@ +const { inspect } = require('util'); + +module.exports = async (t, response, expected) => { + // lift up as useful a stack trace as possible for the most common case, + // which is a server-side error at runtime for an unsupported scenario + if (response.status >= 400 && expected.status !== response.status) { + const errorText = await response.clone().text(); + let errors; + try { + errors = JSON.parse(errorText).errors; + } catch (e) {} + if (!errors || !Array.isArray(errors) || errors.length === 0) { + return t.fail( + `Error: Expected ${inspect( + expected + )}, server responded with a NON-COMPLIANT ERROR: (Server errors should emit GraphQL-compliant error JSON.) Received status ${ + response.status + }: ${errorText}` + ); + } + return t.fail( + 'Server reported errors: ' + + errors.map(({ message }, i) => `[Error ${i + 1}: ${message}]\t`) + ); + } + + const responseHeaders = {}; + response.headers.forEach((value, name) => { + responseHeaders[name] = value; + responseHeaders[name.toLowerCase()] = value.toString(); + }); + + t.equal(response.status, expected.status, `status code ${expected.status}`); + + Object.entries(expected.headers).forEach(([header, start]) => { + const value = responseHeaders[header.toLowerCase()]; + if (!value) { + t.fail( + `header ${header} not present in ${inspect(responseHeaders)}` + ); + } else { + t.ok(value.startsWith(start), `header ${header} is ${value}`); + } + }); + try { + const body = await response.clone().text(); + if (expected.text) { + const msg = `body should match '${expected.text}': ${body}`; + if (body.includes(expected.text)) { + t.pass(msg); + } else { + t.fail(msg); + } + } + if (expected.json) { + let parsed; + try { + parsed = JSON.parse(body); + } catch (e) { + t.fail(e.message); + } + Object.entries(expected.json).forEach(([name, value]) => + t.equal( + value, + parsed[name], + `JSON ${name} === ${JSON.stringify(parsed[name])}` + ) + ); + } + } catch (e) { + t.error(e); + } +}; diff --git a/packages/upward-spec/suite/getScenarios.js b/packages/upward-spec/suite/getScenarios.js new file mode 100644 index 00000000000..dfd73633844 --- /dev/null +++ b/packages/upward-spec/suite/getScenarios.js @@ -0,0 +1,52 @@ +const { resolve } = require('path'); +const { readdir: fsReaddir, readFile: fsReadFile } = require('fs'); +const { promisify } = require('util'); +const jsYaml = require('js-yaml'); +const readdir = promisify(fsReaddir); +const readFile = promisify(fsReadFile); + +const dirsPromise = readdir(resolve(__dirname, './scenarios')); + +function getOneMatch(candidates, pattern) { + const matching = candidates.filter(candidate => pattern.test(candidate)); + if (matching.length > 1) { + throw new Error( + `${pattern.toString()} returned multiple results: ${matching.join()}` + ); + } + if (matching.length === 0) { + throw new Error(`${pattern.toString()} returned no results`); + } + return matching[0]; +} + +async function getScenarios(pattern) { + if (!pattern || typeof pattern.test !== 'function') { + throw new Error( + `UpwardSpec.getScenarios() requires a regular expression, or an object with a 'test' method` + ); + } + const baseDir = await resolve( + __dirname, + './scenarios', + getOneMatch(await dirsPromise, pattern) + ); + + function getResourcePath(name) { + return resolve(baseDir, name); + } + + function getResource(name, enc = 'utf8') { + return readFile(getResourcePath(name), enc); + } + return { + baseDir, + getResourcePath, + getResource, + async getDefinition(name) { + return jsYaml.safeLoad(await getResource(name + '.yml')); + } + }; +} + +module.exports = getScenarios; diff --git a/packages/upward-spec/suite/index.js b/packages/upward-spec/suite/index.js new file mode 100644 index 00000000000..a530c6166f4 --- /dev/null +++ b/packages/upward-spec/suite/index.js @@ -0,0 +1,6 @@ +module.exports = { + assertOnResponse: require('./assertOnResponse'), + getScenarios: require('./getScenarios'), + MockGQLService: require('./MockGQLService'), + runServer: require('./runServer') +}; diff --git a/packages/upward-spec/suite/runServer.js b/packages/upward-spec/suite/runServer.js new file mode 100644 index 00000000000..f09eb179c3b --- /dev/null +++ b/packages/upward-spec/suite/runServer.js @@ -0,0 +1,169 @@ +const path = require('path'); +const { spawn } = require('child_process'); +const { URL } = require('url'); + +const script = process.env.UPWARD_SERVER_SCRIPT_PATH; + +module.exports = async function runServer( + test, + configFile, + env, + customTimeout +) { + let terminator; + let timeout = Number(customTimeout || process.env.TAP_TIMEOUT); + if (isNaN(timeout)) { + timeout = 5; + } else { + timeout = Math.max(timeout - 1, Math.min(timeout - 1, 2)); + } + // Create a child process and resolve the Promise if: + // - the process exits (with any code) + // - the process stays alive and echoes a URL + + // Reject only if: + // - the process throws an error and cannot start + // - the process takes too long without echoing a valid URL + let server; + try { + server = await new Promise((resolve, reject) => { + let child; + const flags = { + crashed: false, + launched: false, + running: false + }; + let stderr = ''; + let url; + + function terminateClean() { + child.removeAllListeners(); + child.on('error', () => { + child.kill('SIGKILL'); + }); + return new Promise((innerResolve, innerReject) => { + child.on('close', (_, signal) => { + flags.running = false; + return signal === 'SIGKILL' + ? innerReject(signal) + : innerResolve(signal); + }); + child.kill('SIGTERM'); + }); + } + + try { + child = spawn(path.resolve(script), { + cwd: path.dirname(script), + env: Object.assign( + {}, + process.env, + { + UPWARD_PATH: configFile + }, + env + ) + }); + } catch (e) { + reject(e); + return; + } + + child.stderr.on('data', chunk => { + stderr += chunk.toString('utf8'); + }); + + if (timeout > 0) { + terminator = setTimeout(() => { + const message = `Timed out. Spawning a server with ${script} took over ${timeout} seconds.`; + terminateClean() + .then( + signal => ` Killed with ${signal}`, + signal => + `\n\nAdditionally, the process did not respond to SIGTERM, and had to be killed with ${signal}.` + ) + .then(notice => reject(new Error(message + notice))); + }, timeout * 1000); + } + + function emitServer() { + clearTimeout(terminator); + resolve({ + stderr, + url, + hasCrashed() { + return flags.crashed; + }, + hasLaunched() { + return flags.launched; + }, + isRunning() { + return flags.running; + }, + async close() { + if (flags.running) { + return terminateClean().catch(e => test.error(e)); + } + }, + assert(flag, expected = true, msg) { + const extra = msg ? `\n\n${msg}` : ''; + let message = 'server '; + if (!flags[flag]) { + message += 'not '; + } + message += flag; + if (flag === 'launched' && url) { + message += `, listening at ${url}`; + } + if (stderr) { + message += `, emitting stderr ${stderr.slice( + 0, + 50 + )}[...]`; + } + const status = + flags[flag] === expected ? 'pass' : 'fail'; + test[status](message + extra); + return status === 'pass'; + } + }); + } + + child.on('error', reject); + child.on('exit', code => { + flags.running = false; + if (code !== 0) { + flags.crashed = true; + } + emitServer(); + }); + + let outText = ''; + child.stdout.on('data', function waitingForUrl(chunk) { + const newChunk = chunk.toString('utf8'); + outText += newChunk; + if (outText.includes('\n')) { + child.stdout.removeListener('data', waitingForUrl); + try { + url = new URL(outText.trim()); + flags.launched = true; + flags.running = true; + emitServer(); + } catch (e) { + reject( + new Error( + `Could not parse first line of server stdout as a URL: ${e} ${outText}` + ) + ); + } + } + }); + }); + } catch (e) { + clearTimeout(terminator); + test.threw(e); + throw e; + } + + return server; +}; diff --git a/packages/upward-spec/suite/scenarios/001-unknown-config/only-status.yml b/packages/upward-spec/suite/scenarios/001-unknown-config/only-status.yml new file mode 100644 index 00000000000..62e205234c3 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/001-unknown-config/only-status.yml @@ -0,0 +1 @@ +status: 200 diff --git a/packages/upward-spec/suite/scenarios/001-unknown-config/tests.js b/packages/upward-spec/suite/scenarios/001-unknown-config/tests.js new file mode 100644 index 00000000000..7cb9935bc68 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/001-unknown-config/tests.js @@ -0,0 +1,31 @@ +const tape = require('tape'); + +const { getScenarios, runServer } = require('../../'); + +const gettingScenarios = getScenarios(/unknown\-config/); + +tape.test('Crashes if config file is missing', async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath('./absolutely-no-way-this-file-exists') + ); + + server.assert('crashed'); + + await server.close(); + t.end(); +}); + +tape.test('Crashes if config file is unparseable', async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath('./unparseable.yml') + ); + + server.assert('crashed'); + + await server.close(); + t.end(); +}); diff --git a/packages/upward-spec/suite/scenarios/001-unknown-config/unparseable.yml b/packages/upward-spec/suite/scenarios/001-unknown-config/unparseable.yml new file mode 100644 index 00000000000..d172ef309fe --- /dev/null +++ b/packages/upward-spec/suite/scenarios/001-unknown-config/unparseable.yml @@ -0,0 +1 @@ +!!!!THATasda SomE BAD yAML! diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/context-is-hello-env-is-world.mst b/packages/upward-spec/suite/scenarios/002-static-servers/context-is-hello-env-is-world.mst new file mode 100644 index 00000000000..9cd622813af --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/context-is-hello-env-is-world.mst @@ -0,0 +1 @@ +{{communique}} from a {{env.sender}} of external templates!! diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/hello-context-inline-template-json.yml b/packages/upward-spec/suite/scenarios/002-static-servers/hello-context-inline-template-json.yml new file mode 100644 index 00000000000..8f2b4c30348 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/hello-context-inline-template-json.yml @@ -0,0 +1,15 @@ +status: 200 +headers: + inline: + content-type: application/json +body: + engine: mustache + provide: + - salutation + - addressee + template: + inline: '{"greeting":"{{ salutation }}","subject":{{>json-subject}},"shouldYouReallyHandwriteJSON":"no"}' + +salutation: + inline: 'Hello' +addressee: env.ADDRESSEE diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/hello-env-context-file-template.yml b/packages/upward-spec/suite/scenarios/002-static-servers/hello-env-context-file-template.yml new file mode 100644 index 00000000000..e866a071f1e --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/hello-env-context-file-template.yml @@ -0,0 +1,23 @@ +status: + resolver: inline + inline: 200 +headers: + resolver: inline + inline: + content-type: + resolver: inline + inline: text/plain +body: + resolver: template + engine: mustache + provide: + - env + - communique + template: + resolver: file + file: + resolver: inline + inline: './context-is-hello-env-is-world.mst' +communique: + resolver: inline + inline: 'Hello' diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/hello-env-inline-template.yml b/packages/upward-spec/suite/scenarios/002-static-servers/hello-env-inline-template.yml new file mode 100644 index 00000000000..2a50b4c4026 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/hello-env-inline-template.yml @@ -0,0 +1,10 @@ +status: 200 +headers: + inline: + content-type: text/plain +body: + engine: mustache + provide: + - env + template: + inline: 'Hello, environment of {{env.ADDRESSEE}}!!' diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/hello-env-interpolation.yml b/packages/upward-spec/suite/scenarios/002-static-servers/hello-env-interpolation.yml new file mode 100644 index 00000000000..859d939d030 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/hello-env-interpolation.yml @@ -0,0 +1,5 @@ +status: 200 +headers: + inline: + content-type: text/plain +body: env.UPWARD_TEST_RESPONSE_BODY diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/hello-file-shortcut.yml b/packages/upward-spec/suite/scenarios/002-static-servers/hello-file-shortcut.yml new file mode 100644 index 00000000000..7d9a71c7bc9 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/hello-file-shortcut.yml @@ -0,0 +1,8 @@ +status: 201 +headers: + inline: + content-type: + inline: 'text/csv' + x-is-cool-swords: + inline: 'yep' +body: './swords.csv' diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/hello-inline-implicit-resolvers.yml b/packages/upward-spec/suite/scenarios/002-static-servers/hello-inline-implicit-resolvers.yml new file mode 100644 index 00000000000..8a8e0acad52 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/hello-inline-implicit-resolvers.yml @@ -0,0 +1,6 @@ +status: 200 +headers: + inline: + content-type: text/plain +body: + inline: 'Hello World, concisely!!' diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/hello-inline-only.yml b/packages/upward-spec/suite/scenarios/002-static-servers/hello-inline-only.yml new file mode 100644 index 00000000000..86468525b75 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/hello-inline-only.yml @@ -0,0 +1,12 @@ +status: + resolver: inline + inline: 200 +headers: + resolver: inline + inline: + content-type: + resolver: inline + inline: text/plain +body: + resolver: inline + inline: 'Hello World!!' diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/json-subject.tpt b/packages/upward-spec/suite/scenarios/002-static-servers/json-subject.tpt new file mode 100644 index 00000000000..41ad5524494 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/json-subject.tpt @@ -0,0 +1 @@ +"{{>renders-addressee-alone}}" diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/renders-addressee-alone.mustache b/packages/upward-spec/suite/scenarios/002-static-servers/renders-addressee-alone.mustache new file mode 100644 index 00000000000..afe4522bc35 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/renders-addressee-alone.mustache @@ -0,0 +1 @@ +the depths of {{ addressee }}... diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/swords.csv b/packages/upward-spec/suite/scenarios/002-static-servers/swords.csv new file mode 100644 index 00000000000..41cbb682e43 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/swords.csv @@ -0,0 +1,4 @@ +name,origin +claymore,scotland +jian,china +shotel,egypt diff --git a/packages/upward-spec/suite/scenarios/002-static-servers/tests.js b/packages/upward-spec/suite/scenarios/002-static-servers/tests.js new file mode 100644 index 00000000000..e46726439a3 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/002-static-servers/tests.js @@ -0,0 +1,220 @@ +const fetch = require('node-fetch'); +const tape = require('tape'); +const csvParse = require('csv-parse/lib/sync'); + +const { getScenarios, runServer, assertOnResponse } = require('../../'); + +const gettingScenarios = getScenarios(/static\-servers/); + +tape.test('Static Hello World with only inline deps', async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath('./hello-inline-only.yml') + ); + + if (server.assert('launched')) { + const response = await fetch(server.url); + + await assertOnResponse(t, response, { + status: 200, + headers: { + 'content-type': 'text/plain' + }, + text: 'Hello World!!' + }); + + server.assert('crashed', false); + } + + await server.close(); + t.end(); +}); + +tape.test('Static Hello World with implicit resolvers', async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath('./hello-inline-implicit-resolvers.yml') + ); + + if (server.assert('launched')) { + const response = await fetch(server.url); + + await assertOnResponse(t, response, { + status: 200, + headers: { + 'content-type': 'text/plain' + }, + text: 'Hello World, concisely!!' + }); + + server.assert('crashed', false); + } + + await server.close(); + t.end(); +}); + +tape.test('Static Hello World with env interpolation', async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath('./hello-env-interpolation.yml'), + { + UPWARD_TEST_RESPONSE_BODY: 'Hello, environment!!' + } + ); + + if (server.assert('launched')) { + const response = await fetch(server.url); + + await assertOnResponse(t, response, { + status: 200, + headers: { + 'content-type': 'text/plain' + }, + text: 'Hello, environment!!' + }); + + server.assert('crashed', false); + } + await server.close(); + t.end(); +}); + +tape.test('Static Hello World with env dep and inline template', async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath('./hello-env-inline-template.yml'), + { + ADDRESSEE: 'Terra' + } + ); + + if (server.assert('launched')) { + const response = await fetch(server.url); + + await assertOnResponse(t, response, { + status: 200, + headers: { + 'content-type': 'text/plain' + }, + text: 'Hello, environment of Terra!!' + }); + + server.assert('crashed', false); + } + + await server.close(); + t.end(); +}); + +tape.test( + 'Static Hello World with env, context, and file template', + async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath('./hello-env-context-file-template.yml'), + { + sender: 'planet' + } + ); + + if (server.assert('launched')) { + const response = await fetch(server.url); + + await assertOnResponse(t, response, { + status: 200, + headers: { + 'content-type': 'text/plain' + }, + text: 'Hello from a planet of external templates!!' + }); + + server.assert('crashed', false); + } + + await server.close(); + t.end(); + } +); +tape.test( + 'Static JSON Hello World with template partial resolution', + async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath( + './hello-context-inline-template-json.yml' + ), + { + ADDRESSEE: 'deep space' + } + ); + if (server.assert('launched')) { + const response = await fetch(server.url); + + await assertOnResponse(t, response, { + status: 200, + headers: { + 'content-type': 'application/json' + }, + json: { + greeting: 'Hello', + subject: 'the depths of deep space...' + } + }); + + server.assert('crashed', false); + } + + await server.close(); + t.end(); + } +); +tape.test('File shortcut resolution', async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath('./hello-file-shortcut.yml') + ); + if (server.assert('launched')) { + const response = await fetch(server.url); + + await assertOnResponse(t, response, { + status: 201, + headers: { + 'content-type': 'text/csv', + 'x-is-cool-swords': 'yep' + } + }); + + const swordsCSV = await response.text(); + try { + const swords = csvParse(swordsCSV, { columns: true }).reduce( + (acc, { name, origin }) => ((acc[origin] = name), acc), + {} + ); + + t.deepEqual(swords, { + scotland: 'claymore', + china: 'jian', + egypt: 'shotel' + }); + } catch (e) { + t.fail( + `swords.csv is valid CSV, but server emitted ${swordsCSV}, which did not parse: ${ + e.message + }` + ); + } + + server.assert('crashed', false); + } + + await server.close(); + t.end(); +}); diff --git a/packages/upward-spec/suite/scenarios/003-request-handling/cyclic-dependencies.yml b/packages/upward-spec/suite/scenarios/003-request-handling/cyclic-dependencies.yml new file mode 100644 index 00000000000..86f6252f6fb --- /dev/null +++ b/packages/upward-spec/suite/scenarios/003-request-handling/cyclic-dependencies.yml @@ -0,0 +1,16 @@ +status: 200 +headers: + inline: + content-type: + when: + matches: + content-disposition: + when: + matches: headers.content-type + pattern: text/html + use: + inline: + inline + default: + inline: + attachment diff --git a/packages/upward-spec/suite/scenarios/003-request-handling/island-summary.mst b/packages/upward-spec/suite/scenarios/003-request-handling/island-summary.mst new file mode 100644 index 00000000000..e2e2d8b802e --- /dev/null +++ b/packages/upward-spec/suite/scenarios/003-request-handling/island-summary.mst @@ -0,0 +1,4 @@ +{{title}}, an island in the {{group}} +===================================== + +{{description}} diff --git a/packages/upward-spec/suite/scenarios/003-request-handling/reflect-request.yml b/packages/upward-spec/suite/scenarios/003-request-handling/reflect-request.yml new file mode 100644 index 00000000000..b4d9773e7d1 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/003-request-handling/reflect-request.yml @@ -0,0 +1,32 @@ +status: + resolver: inline + inline: 200 +headers: + resolver: inline + inline: + content-type: text/plain +body: + resolver: template + engine: mustache + provide: + - request + template: + resolver: inline + inline: | + {{#request}} + Headers: + {{#headerEntries}} + {{name}}: {{value}} + {{/headerEntries}} + URL: + {{#url}}{{#?protocol}}protocol: {{protocol}} + {{/?protocol}}{{#?host}}host: {{host}} + {{/?host}}{{#?hostname}}hostname: {{hostname}} + {{/?hostname}}{{#?port}}port: {{port}} + {{/?port}}pathname: {{pathname}} + {{/url}} + URL Query: + {{#queryEntries}} + {{name}}: {{value}} + {{/queryEntries}} + {{/request}} diff --git a/packages/upward-spec/suite/scenarios/003-request-handling/route-requests.yml b/packages/upward-spec/suite/scenarios/003-request-handling/route-requests.yml new file mode 100644 index 00000000000..5332023843d --- /dev/null +++ b/packages/upward-spec/suite/scenarios/003-request-handling/route-requests.yml @@ -0,0 +1,64 @@ +status: + when: + matches: island.name + pattern: '.' + use: 200 + default: 404 + +headers: + inline: + content-type: text/plain + +body: + when: + - matches: island.name + pattern: '.' + use: + engine: mustache + provide: + title: island.name + group: island.group + body: island.description + template: ./island-summary + default: notFound + +island: islandResult.data.islands:first + +islandResult: + when: + - matches: request.url.pathname + pattern: '^\/inmost-sea/([A-Za-z\-]+\/?$' + use: + url: env.EARTHSEA_API + query: './getIslands' + variables: + group: + inline: Inmost Sea + nameNormalized: $match.$1 + + - matches: request.url.pathname + pattern: '^\/north-reach/([A-Za-z\-]+\/?$' + use: + url: env.EARTHSEA_API + query: './getIslands' + variables: + group: + inline: North Reach + nameNormalized: $match.$1 + + - matches: request.url.pathname + pattern: '^\/kargad-lands/([A-Za-z\-]+\/?$' + use: + url: env.EARTHSEA_API + query: './getIslands' + variables: + group: + inline: Kargad Lands + nameNormalized: $match.$1 + + default: + inline: '' + +notFound: + inline: | + No such island exists here. diff --git a/packages/upward-spec/suite/scenarios/003-request-handling/tests.js b/packages/upward-spec/suite/scenarios/003-request-handling/tests.js new file mode 100644 index 00000000000..5ac57786863 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/003-request-handling/tests.js @@ -0,0 +1,34 @@ +const { URL } = require('url'); +const fetch = require('node-fetch'); +const tape = require('tape'); + +const { getScenarios, runServer, assertOnResponse } = require('../../'); + +const gettingScenarios = getScenarios(/request\-handling/); + +tape.test('Reflect request', async t => { + const scenarios = await gettingScenarios; + const server = await runServer( + t, + scenarios.getResourcePath('./reflect-request.yml') + ); + + if (server.assert('launched')) { + const response = await fetch( + new URL('/some/path?query=parameters', server.url) + ); + + await assertOnResponse(t, response, { + status: 200, + headers: { + 'content-type': 'text/plain' + }, + text: 'some/path' + }); + + server.assert('crashed', false); + } + + await server.close(); + t.end(); +}); diff --git a/packages/upward-spec/suite/scenarios/005-many-resolvers/articles-and-authors-schema.graphql b/packages/upward-spec/suite/scenarios/005-many-resolvers/articles-and-authors-schema.graphql new file mode 100644 index 00000000000..4f67b49cbd2 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/005-many-resolvers/articles-and-authors-schema.graphql @@ -0,0 +1,18 @@ +type Author { + id: Int + name: String + bio: String +} +type Article { + id: Int + title: String + text: String + author: Author +} +type Query { + author(id: Int): Author + article(id: ): Article +} +schema { + query: Query +} diff --git a/packages/upward-spec/suite/scenarios/005-many-resolvers/articles-and-authors.yml b/packages/upward-spec/suite/scenarios/005-many-resolvers/articles-and-authors.yml new file mode 100644 index 00000000000..5dd064b3029 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/005-many-resolvers/articles-and-authors.yml @@ -0,0 +1,65 @@ +status: page.status +headers: page.headers +body: page.body + +articleResult: + url: env.LIBRARY_SVC + query: './getArticle.graphql' + variables: + articleId: request.url.query.articleID + +authorBioResult: + url: env.LIBRARY_SVC + query: './getAuthor.graphql' + variables: + authorId: request.url.query.authorID + +textHtml: + inline: + 'content-type': 'text/html' + +notFound: + inline: + status: 404 + headers: textHtml + body: + engine: mustache + template: './notFound.mst' + +page: + when: + - matches: request.url.pathname + pattern: '/article' + use: + when: + - matches: articleResult.data.article.id + pattern: '.' + use: + inline: + title: + engine: mustache + template: 'Article {{authorBioResult.data.author.name}}' + status: 200 + headers: textHtml + body: + engine: mustache + template: './article.mst' + default: notFound + - matches: request.url.pathname + pattern: '/author' + use: + when: + - matches: authorBioResult.data.author.id + pattern: '.' + use: + inline: + title: + engine: mustache + template: 'Author {{authorBioResult.data.author.name}}' + status: 200 + headers: textHtml + body: + engine: mustache + template: './authorBio.mst' + default: notFound + default: notFound diff --git a/packages/upward-spec/suite/scenarios/005-many-resolvers/footer.mst b/packages/upward-spec/suite/scenarios/005-many-resolvers/footer.mst new file mode 100644 index 00000000000..99b9cdc53ac --- /dev/null +++ b/packages/upward-spec/suite/scenarios/005-many-resolvers/footer.mst @@ -0,0 +1 @@ +
Copyright © Current Year
diff --git a/packages/upward-spec/suite/scenarios/005-many-resolvers/getArticle.graphql b/packages/upward-spec/suite/scenarios/005-many-resolvers/getArticle.graphql new file mode 100644 index 00000000000..b563ec86628 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/005-many-resolvers/getArticle.graphql @@ -0,0 +1,10 @@ +{ + query getArticle($articleId: Number!) { + article(id: $articleId) { + id + title + author + body + } + } +} diff --git a/packages/upward-spec/suite/scenarios/005-many-resolvers/getAuthor.graphql b/packages/upward-spec/suite/scenarios/005-many-resolvers/getAuthor.graphql new file mode 100644 index 00000000000..05776180933 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/005-many-resolvers/getAuthor.graphql @@ -0,0 +1,9 @@ +{ + query getAuthor($authorId: Number!) { + author(id: $authorId) { + firstName + lastName + bio + } + } +} diff --git a/packages/upward-spec/suite/scenarios/005-many-resolvers/head.mst b/packages/upward-spec/suite/scenarios/005-many-resolvers/head.mst new file mode 100644 index 00000000000..bad62a74ab4 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/005-many-resolvers/head.mst @@ -0,0 +1 @@ +{{#page}}{{#title}}{{title}}{{/title}}{{^title}}404: Not Found{{/title}}{{{/page}} diff --git a/packages/upward-spec/suite/scenarios/005-many-resolvers/header.mst b/packages/upward-spec/suite/scenarios/005-many-resolvers/header.mst new file mode 100644 index 00000000000..9102bf4a433 --- /dev/null +++ b/packages/upward-spec/suite/scenarios/005-many-resolvers/header.mst @@ -0,0 +1,2 @@ +
Page Header
+{{> menu}} diff --git a/packages/upward-spec/suite/scenarios/005-many-resolvers/menu.mst b/packages/upward-spec/suite/scenarios/005-many-resolvers/menu.mst new file mode 100644 index 00000000000..446ce145f0c --- /dev/null +++ b/packages/upward-spec/suite/scenarios/005-many-resolvers/menu.mst @@ -0,0 +1 @@ +Navigation diff --git a/packages/upward-spec/suite/scenarios/005-many-resolvers/notFound.mst b/packages/upward-spec/suite/scenarios/005-many-resolvers/notFound.mst new file mode 100644 index 00000000000..fe9a0b206ab --- /dev/null +++ b/packages/upward-spec/suite/scenarios/005-many-resolvers/notFound.mst @@ -0,0 +1,9 @@ + + + {{> head}} + + {{> header}} +

404: Not Found

+ {{> footer }} + + diff --git a/packages/upward-spec/test_upward_server.sh b/packages/upward-spec/test_upward_server.sh new file mode 100644 index 00000000000..6a68e455b2a --- /dev/null +++ b/packages/upward-spec/test_upward_server.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# This simple script assumes that the server will stay in the foreground, that +# it will echo its full host (e.g. http://localhost:8919) to stdout (with a +# line feed to trigger consumption of the URL) as part of its initial logging, +# and that it will gracefully exit on receiving SIGTERM, which are good # +# habits for a web server to have anyway. + +# The `exec` ensures that the server process will receive signals sent to this +# script, such as SIGTERM, which the test harness uses to gracefully close. + +# The `grep` ensures that if more than just a URL is echoed to standard output, +# the URL be parsed out of it. + +exec ./path_to_upward_server/bin/upward --config "$UPWARD_PATH" | grep -ioE '\bhttps?://[a-z0-9][a-z0-9-\.]*\n' diff --git a/packages/upward-spec/tiered_architecture.png b/packages/upward-spec/tiered_architecture.png new file mode 100644 index 00000000000..5c11f3683a7 Binary files /dev/null and b/packages/upward-spec/tiered_architecture.png differ diff --git a/packages/upward-spec/tight_coupling.png b/packages/upward-spec/tight_coupling.png new file mode 100644 index 00000000000..934f476d7e6 Binary files /dev/null and b/packages/upward-spec/tight_coupling.png differ diff --git a/packages/upward-spec/unified_graph.png b/packages/upward-spec/unified_graph.png new file mode 100644 index 00000000000..dc4096c1aed Binary files /dev/null and b/packages/upward-spec/unified_graph.png differ diff --git a/packages/venia-concept/.babelrc b/packages/venia-concept/.babelrc index 6ca049e72f8..296006f306c 100644 --- a/packages/venia-concept/.babelrc +++ b/packages/venia-concept/.babelrc @@ -6,6 +6,7 @@ "transform-class-properties", "transform-es2015-modules-commonjs", "transform-object-rest-spread", + "import-graphql", "transform-react-jsx" ] } diff --git a/packages/venia-concept/.env.dist b/packages/venia-concept/.env.dist deleted file mode 100644 index 3bb676ad450..00000000000 --- a/packages/venia-concept/.env.dist +++ /dev/null @@ -1,14 +0,0 @@ -MAGENTO_BACKEND_DOMAIN="https://magento2.vagrant" -MAGENTO_BACKEND_PUBLIC_PATH="/pub/static/frontend/Magento/venia/en_US/" - -# Control the filename of the generated ServiceWorker. -SERVICE_WORKER_FILE_NAME="sw.js" - -# ServiceWorkers are disabled in development, to avoid stale resources. -# Set this to 1 (or any string value) to force ServiceWorkers in development. -ENABLE_SERVICE_WORKER_DEBUGGING= - -# TODO: This env var can override the hardcoded product media path, which we -# need to hardcode due to https://github.com/magento/graphql-ce/issues/88 -# By default it is /media/ -# MAGENTO_BACKEND_PRODUCT_MEDIA_PATH=/media/catalog/product/ diff --git a/packages/venia-concept/CHANGELOG.md b/packages/venia-concept/CHANGELOG.md new file mode 100644 index 00000000000..8d1d3dc5adb --- /dev/null +++ b/packages/venia-concept/CHANGELOG.md @@ -0,0 +1,26 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +# 0.2.0 (2018-08-13) + + +### Bug Fixes + +* **dev:** MagentoRootComponentsPlugin dedupe fix ([bb8b152](https://github.com/magento-research/pwa-studio/commit/bb8b152)) +* **dev:** merge 'assets' and 'output' paths ([36d8157](https://github.com/magento-research/pwa-studio/commit/36d8157)) +* **dev:** optimize uglify output ([e7dca7e](https://github.com/magento-research/pwa-studio/commit/e7dca7e)) +* **perf:** Optimize logo.svg ([0a1fb6a](https://github.com/magento-research/pwa-studio/commit/0a1fb6a)) +* **routing:** use Link component ([d4004bc](https://github.com/magento-research/pwa-studio/commit/d4004bc)) + + +### Features + +* multicasting REST API client ([#164](https://github.com/magento-research/pwa-studio/issues/164)) ([2852e14](https://github.com/magento-research/pwa-studio/commit/2852e14)), closes [#140](https://github.com/magento-research/pwa-studio/issues/140) +* **checkout:** Enable order submission ([#173](https://github.com/magento-research/pwa-studio/issues/173)) ([666327e](https://github.com/magento-research/pwa-studio/commit/666327e)) +* **checkout:** Integrate live product, cart, checkout ([#140](https://github.com/magento-research/pwa-studio/issues/140)) ([1f60e87](https://github.com/magento-research/pwa-studio/commit/1f60e87)) +* **data:** Add live GraphQL data to product detail page ([#90](https://github.com/magento-research/pwa-studio/issues/90)) ([77b6bd6](https://github.com/magento-research/pwa-studio/commit/77b6bd6)), closes [#52](https://github.com/magento-research/pwa-studio/issues/52) [#87](https://github.com/magento-research/pwa-studio/issues/87) +* **dev:** app shell detects env from proxy header ([ac5b0b0](https://github.com/magento-research/pwa-studio/commit/ac5b0b0)) +* **dev:** log dev URL prominently after build ([#114](https://github.com/magento-research/pwa-studio/issues/114)) ([84fadde](https://github.com/magento-research/pwa-studio/commit/84fadde)), closes [#4](https://github.com/magento-research/pwa-studio/issues/4) diff --git a/packages/venia-concept/Magento_Theme/templates/root.phtml b/packages/venia-concept/Magento_Theme/templates/root.phtml deleted file mode 100644 index 802897e0ab2..00000000000 --- a/packages/venia-concept/Magento_Theme/templates/root.phtml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - Venia - - - - -
- - - diff --git a/packages/venia-concept/babel.config.js b/packages/venia-concept/babel.config.js index 53883be8c3b..795b2d65050 100644 --- a/packages/venia-concept/babel.config.js +++ b/packages/venia-concept/babel.config.js @@ -8,7 +8,8 @@ const plugins = [ 'transform-class-properties', 'transform-object-rest-spread', ['transform-react-jsx'], - 'graphql-tag' + 'graphql-tag', + 'import-graphql' ]; // define default babel options diff --git a/packages/venia-concept/composer.json b/packages/venia-concept/composer.json deleted file mode 100644 index 32bbe464967..00000000000 --- a/packages/venia-concept/composer.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "magento/theme-frontend-venia", - "description": "Venia PWA Concept Theme for Magento 2", - "require": { - "php": "~7.1.3||~7.2.0", - "magento-research/theme-frontend-pwa": "*" - }, - "type": "magento2-theme", - "license": ["OSL-3.0", "AFL-3.0"], - "autoload": { - "files": ["registration.php"] - } -} diff --git a/packages/venia-concept/etc/view.xml b/packages/venia-concept/etc/view.xml deleted file mode 100644 index 8900c34abe0..00000000000 --- a/packages/venia-concept/etc/view.xml +++ /dev/null @@ -1,314 +0,0 @@ - - - - - - - 140 - 140 - - - - 152 - 188 - - - 110 - 160 - - - 240 - 300 - - - 240 - 300 - - - 100 - 100 - - - 285 - 285 - - - 113 - 113 - - - 75 - 75 - - - 100 - 100 - - - 78 - 78 - - - 240 - 300 - - - 270 - 270 - - - 78 - 78 - - - 265 - 265 - - - 140 - 140 - - - - false - - - 700 - 700 - - - 700 - 700 - false - - - 90 - 90 - - - 700 - 700 - - - 700 - 700 - - - 90 - 90 - - - 76 - 76 - - - 135 - 135 - - - 75 - 75 - - - 240 - 300 - - - 75 - 90 - - - 76 - 76 - - - 270 - 207 - - - 240 - 300 - - - 75 - 90 - - - 76 - 76 - - - 270 - 270 - - - 140 - 140 - - - 285 - 285 - - - 75 - 75 - - - 75 - 75 - - - 135 - 135 - - - 75 - 90 - - - 140 - 140 - - - 75 - 90 - - - 113 - 113 - - - 240 - 300 - - - - - - - - thumbs - true - true - true - false - true - horizontal - true - slides - - slide - 500 - - - thumbs - true - false - false - horizontal - slides - - dissolve - 500 - - - - - - 20 - - - - - hover - false - - - - - - 767px - - - - dots - - - - - - - 100 - 275 - 48 - - 166 - 370 - - 0 - - - 58 - - - 1MB - - - sw.js - - - Lib::jquery/jquery.min.js - Lib::jquery/jquery-ui-1.9.2.js - Lib::jquery/jquery.ba-hashchange.min.js - Lib::jquery/jquery.details.js - Lib::jquery/jquery.details.min.js - Lib::jquery/jquery.hoverIntent.js - Lib::jquery/colorpicker/js/colorpicker.js - Lib::requirejs/require.js - Lib::requirejs/text.js - Lib::date-format-normalizer.js - Lib::legacy-build.min.js - Lib::mage/captcha.js - Lib::mage/dropdown_old.js - Lib::mage/list.js - Lib::mage/loader_old.js - Lib::mage/webapi.js - Lib::mage/zoom.js - Lib::mage/translate-inline-vde.js - Lib::mage/requirejs/mixins.js - Lib::mage/requirejs/static.js - Magento_Customer::js/zxcvbn.js - Magento_Catalog::js/zoom.js - Magento_Ui::js/lib/step-wizard.js - Magento_Ui::js/form/element/ui-select.js - Magento_Ui::js/form/element/file-uploader.js - Magento_Ui::js/form/components/insert.js - Magento_Ui::js/form/components/insert-listing.js - Magento_Ui::js/timeline - Magento_Ui::js/grid - Magento_Ui::js/dynamic-rows - Magento_Ui::templates/timeline - Magento_Ui::templates/grid - Magento_Ui::templates/dynamic-rows - Magento_Swagger::swagger-ui - Lib::modernizr - Lib::tiny_mce - Lib::varien - Lib::jquery/editableMultiselect - Lib::jquery/jstree - Lib::jquery/fileUploader - Lib::css - Lib::lib - Lib::extjs - Lib::prototype - Lib::scriptaculous - Lib::less - Lib::mage/adminhtml - Lib::mage/backend - - diff --git a/packages/venia-concept/example.env b/packages/venia-concept/example.env new file mode 100644 index 00000000000..f1a86a796b3 --- /dev/null +++ b/packages/venia-concept/example.env @@ -0,0 +1,73 @@ +########## example.env ######################################################## +# +# This file is an example of how to configure Magento PWA Studio at +# development time and runtime. Use these environment variables for any +# settings which will change between your production, development, and +# testing environments. +# +# Copy this file to a file in the `venia-concept` directory called `.env` +# to activate your project. If a `.env` file is missing, or required +# variables are missing in your `.env` file, you should be notified. +# +############################################################################### + +########## Connecting to a Magento store ###################################### +# +# Connect to an instance of Magento 2.3 by specifying its public +# domain name. **REQUIRED.** Example: +# MAGENTO_BACKEND_DOMAIN="https://magento2.local" +# +# Use a particular website code when making API calls. Defaults to 'base', +# but for your PWA to act as a different "website" in the Magento admin, +# you can change this variable. Example: +# MAGENTO_WEBSITE_CODE="foo" +# +# TODO: This env var can override the hardcoded product media path, which we +# need to hardcode due to https://github.com/magento/graphql-ce/issues/88 +# REQUIRED, and automatically set here: +MAGENTO_BACKEND_PRODUCT_MEDIA_PATH="/media/catalog/product" +# DO NOT CHANGE THIS VALUE unless you have a custom static setup. +# +# These legacy environment variables from 1.x are no longer necessary: +# - `MAGENTO_BACKEND_PUBLIC_PATH`: Since PWA Studio no longer makes _themes_, +# it is no longer necessary to follow theme directory structure. +# - `SERVICE_WORKER_FILE_NAME`: The `sw.js` filename is suitable for almost +# all cases, and it does not vary by environment, so it should not be +# an environment variable. It has been hardcoded into package.json config. +# +############################################################################### + +########## UPWARD server production configuration ############################# +# +# The current PWA Studio production server is built with NodeJS; it is an +# implementation of the UPWARD spec, and is theoretically swappable with +# any other UPWARD-compliant server, with minimal configuration or code +# changes. Since `upward-js` is the current default implementation, the +# following environment variables configure `upward-js` specifically. +# +# Give the server the location of the UPWARD YAML configuration file which +# specifies its behavior. **REQUIRED**. For Venia, this is automatically set: +UPWARD_JS_UPWARD_PATH=venia-upward.yml +# +# Tell the upward-js server to bind to a local address and port on launch. +# Default 1 (true). **REQUIRED** unless you have a custom production setup. +UPWARD_JS_BIND_LOCAL=1 +# +# Tell the upward-js prod server to log the bound URL to stdout. Useful in +# testing and discovery scenarios, but may be disabled in production. +UPWARD_JS_LOG_URL=1 +# +# Specify a port the upward-js prod-server will bind to. If unspecified, an +# open port will be found. +# UPWARD_JS_PORT=8008 +# +############################################################################### + +########## webpack-dev-server development configuration ###################### +# +# Set variables for webpack-dev-server for editing and debugging scenarios. +# +# Set this to 1 (true) to force ServiceWorkers in development. +# ENABLE_SERVICE_WORKER_DEBUGGING=1 +# +############################################################################### diff --git a/packages/venia-concept/media/favicon.ico b/packages/venia-concept/media/favicon.ico new file mode 100644 index 00000000000..fd57e1d1a9e Binary files /dev/null and b/packages/venia-concept/media/favicon.ico differ diff --git a/packages/venia-concept/media/preview.jpg b/packages/venia-concept/media/preview.jpg deleted file mode 100644 index ab50e6281e7..00000000000 Binary files a/packages/venia-concept/media/preview.jpg and /dev/null differ diff --git a/packages/venia-concept/package.json b/packages/venia-concept/package.json index c23868a0136..2f922b6cad5 100644 --- a/packages/venia-concept/package.json +++ b/packages/venia-concept/package.json @@ -1,7 +1,10 @@ { - "name": "theme-frontend-venia", - "version": "2.0.0-rc.1", - "description": "Venia PWA Concept Theme for Magento 2", + "name": "@magento/venia-concept", + "publishConfig": { + "access": "public" + }, + "version": "2.0.0-rc2.0.5", + "description": "Venia PWA Concept Storefront for Magento 2", "license": "(OSL-3.0 OR AFL-3.0)", "author": "Magento Commerce", "main": "src/index.js", @@ -9,19 +12,22 @@ "bugs": { "url": "https://github.com/magento-research/pwa-studio/issues" }, + "config": { + "serviceWorkerFileName": "sw.js" + }, "homepage": "https://github.com/magento-research/pwa-studio/tree/master/packages/venia-concept#readme", "scripts": { "build": "webpack --color --env.phase production", "clean": "rimraf web/js", - "prepare": "npm-merge-driver install", - "start": "webpack-dev-server --progress --color --env.phase development", + "start": "node server.js", "start:debug": "node --inspect-brk ./node_modules/.bin/webpack-dev-server --progress --color --env.phase development", - "watch": "npm run -s start" + "validate-queries": "node ./validate-queries.js", + "watch": "webpack-dev-server --progress --color --env.phase development" }, "devDependencies": { - "@magento/peregrine": "^2.0.0-rc.1", - "@magento/pwa-buildpack": "^2.0.0-rc.1", - "npm-merge-driver": "^2.3.5", + "@magento/peregrine": "^2.0.0-rc2.0.5", + "@magento/pwa-buildpack": "^2.0.0-rc2.0.5", + "@magento/upward-js": "^2.0.0-rc2.0.5", "rimraf": "^2.6.2", "webpack": "3.11.0", "webpack-dev-server": "2.11.0" diff --git a/packages/venia-concept/registration.php b/packages/venia-concept/registration.php deleted file mode 100644 index 98fefc762f9..00000000000 --- a/packages/venia-concept/registration.php +++ /dev/null @@ -1,13 +0,0 @@ - 0) { + console.warn('No custom host provided.'); + } + if (makeCerts.length > 0) { + console.warn('No SSL certificate provided.'); + } + } else { + const { hostname, certPair } = await setupDomain('magento-venia', { + unique: false + }); + config.host = hostname; + config.https = certPair; + } + } + + await createUpwardServer(config); + console.log('\nStaging server running at the address above.\n'); +} + +console.log('Launching staging server...\n'); +serve(); diff --git a/packages/venia-concept/src/RootComponents/Category/category.js b/packages/venia-concept/src/RootComponents/Category/category.js index 0c8426bdd5b..8aff0206b2e 100644 --- a/packages/venia-concept/src/RootComponents/Category/category.js +++ b/packages/venia-concept/src/RootComponents/Category/category.js @@ -17,7 +17,9 @@ const categoryQuery = gql` items { id name - small_image + small_image { + path + } url_key price { regularPrice { @@ -27,6 +29,7 @@ const categoryQuery = gql` } } } + url_key } } } @@ -54,7 +57,7 @@ class Category extends Component { return ( - + {({ loading, error, data }) => { if (error) return
Data Fetch Error
; if (loading) return
Fetching Data
; diff --git a/packages/venia-concept/src/RootComponents/Product/Product.js b/packages/venia-concept/src/RootComponents/Product/Product.js index ac4a08054a0..ce55db5fbc7 100644 --- a/packages/venia-concept/src/RootComponents/Product/Product.js +++ b/packages/venia-concept/src/RootComponents/Product/Product.js @@ -1,13 +1,13 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { Query } from 'react-apollo'; -import gql from 'graphql-tag'; import { bool, shape, number, arrayOf, string } from 'prop-types'; import { addItemToCart } from 'src/actions/cart'; import Page from 'src/components/Page'; import ProductFullDetail from 'src/components/ProductFullDetail'; import getUrlKey from 'src/util/getUrlKey'; +import getProductDetail from '../../queries/getProductDetail.graphql'; /** * As of this writing, there is no single Product query type in the M2.3 schema. @@ -16,32 +16,6 @@ import getUrlKey from 'src/util/getUrlKey'; * https://github.com/magento/graphql-ce/issues/86 * TODO: Replace with a single product query when possible. */ -const productDetailQuery = gql` - query productDetail($urlKey: String) { - productDetail: products(filter: { url_key: { eq: $urlKey } }) { - items { - sku - name - price { - regularPrice { - amount { - currency - value - } - } - } - description - media_gallery_entries { - label - position - disabled - file - } - } - } - } -`; - class Product extends Component { static propTypes = { data: shape({ @@ -87,8 +61,8 @@ class Product extends Component { return ( {({ loading, error, data }) => { if (error) return
Data Fetch Error
; diff --git a/packages/venia-concept/src/components/CreateAccount/__tests__/createAccount.spec.js b/packages/venia-concept/src/components/CreateAccount/__tests__/createAccount.spec.js index 006b317e571..06dfc297dd5 100644 --- a/packages/venia-concept/src/components/CreateAccount/__tests__/createAccount.spec.js +++ b/packages/venia-concept/src/components/CreateAccount/__tests__/createAccount.spec.js @@ -96,10 +96,7 @@ test('calls `onCreateAccount` when create account button is pressed', () => { const createAccountSpy = jest.fn(); const wrapper = mount( shallow( - + ).get(0) ); wrapper.setState(state); diff --git a/packages/venia-concept/src/components/Gallery/__tests__/gallery.spec.js b/packages/venia-concept/src/components/Gallery/__tests__/gallery.spec.js index e6c7100d7d0..d9b9fdb5454 100644 --- a/packages/venia-concept/src/components/Gallery/__tests__/gallery.spec.js +++ b/packages/venia-concept/src/components/Gallery/__tests__/gallery.spec.js @@ -11,7 +11,9 @@ const items = [ { id: 1, name: 'Test Product 1', - small_image: '/test/product/1.png', + small_image: { + path: '/test/product/1.png' + }, price: { regularPrice: { amount: { @@ -24,7 +26,9 @@ const items = [ { id: 2, name: 'Test Product 2', - small_image: '/test/product/2.png', + small_image: { + path: '/test/product/2.png' + }, price: { regularPrice: { amount: { diff --git a/packages/venia-concept/src/components/Gallery/__tests__/item.spec.js b/packages/venia-concept/src/components/Gallery/__tests__/item.spec.js index 12c9bd48969..e92dd0bb383 100644 --- a/packages/venia-concept/src/components/Gallery/__tests__/item.spec.js +++ b/packages/venia-concept/src/components/Gallery/__tests__/item.spec.js @@ -25,7 +25,9 @@ const classes = { const validItem = { id: 1, name: 'Test Product', - small_image: '/foo/bar/pic.png', + small_image: { + path: '/foo/bar/pic.png' + }, url_key: 'strive-shoulder-pack', price: { regularPrice: { diff --git a/packages/venia-concept/src/components/Gallery/__tests__/items.spec.js b/packages/venia-concept/src/components/Gallery/__tests__/items.spec.js index 16d26746162..ccf9da687dc 100644 --- a/packages/venia-concept/src/components/Gallery/__tests__/items.spec.js +++ b/packages/venia-concept/src/components/Gallery/__tests__/items.spec.js @@ -10,7 +10,9 @@ const items = [ { id: 1, name: 'Test Product 1', - small_image: '/test/product/1.png', + small_image: { + path: '/test/product/1.png' + }, price: { regularPrice: { amount: { @@ -22,7 +24,9 @@ const items = [ { id: 2, name: 'Test Product 2', - small_image: '/test/product/2.png', + small_image: { + path: '/test/product/2.png' + }, price: { regularPrice: { amount: { diff --git a/packages/venia-concept/src/components/Gallery/gallery.js b/packages/venia-concept/src/components/Gallery/gallery.js index d8900cccd93..4a0486764e7 100644 --- a/packages/venia-concept/src/components/Gallery/gallery.js +++ b/packages/venia-concept/src/components/Gallery/gallery.js @@ -19,7 +19,9 @@ class Gallery extends Component { shape({ id: number.isRequired, name: string.isRequired, - small_image: string.isRequired, + small_image: shape({ + path: string.isRequired + }).isRequired, price: shape({ regularPrice: shape({ amount: shape({ diff --git a/packages/venia-concept/src/components/Gallery/item.js b/packages/venia-concept/src/components/Gallery/item.js index bd88acd589c..38f53b0b4c6 100644 --- a/packages/venia-concept/src/components/Gallery/item.js +++ b/packages/venia-concept/src/components/Gallery/item.js @@ -40,7 +40,9 @@ class GalleryItem extends Component { item: shape({ id: number.isRequired, name: string.isRequired, - small_image: string.isRequired, + small_image: shape({ + path: string.isRequired + }).isRequired, url_key: string.isRequired, price: shape({ regularPrice: shape({ @@ -138,7 +140,7 @@ class GalleryItem extends Component { return ( {name} + + + + + diff --git a/packages/venia-concept/templates/generic-shell.mst b/packages/venia-concept/templates/generic-shell.mst new file mode 100644 index 00000000000..c08e1436384 --- /dev/null +++ b/packages/venia-concept/templates/generic-shell.mst @@ -0,0 +1,12 @@ +{{> templates/doctype-and-head-start}} + Venia + + +
{{#urlResolver}} + {{#type}}Loading {{type}}{{/type}} + {{^type}}Not found!{{/type}} + {{/urlResolver}}
+{{> templates/preloads}} +{{> templates/scripts}} + + diff --git a/packages/venia-concept/templates/notfound-shell.mst b/packages/venia-concept/templates/notfound-shell.mst new file mode 100644 index 00000000000..a960933e380 --- /dev/null +++ b/packages/venia-concept/templates/notfound-shell.mst @@ -0,0 +1,11 @@ +{{> templates/doctype-and-head-start}} + Venia - Not Found + + +
+

Not found!

+
+{{> templates/preloads}} +{{> templates/scripts}} + + diff --git a/packages/venia-concept/templates/preloads.mst b/packages/venia-concept/templates/preloads.mst new file mode 100644 index 00000000000..83de0d1adf1 --- /dev/null +++ b/packages/venia-concept/templates/preloads.mst @@ -0,0 +1,3 @@ +{{#urlResolver}} + +{{/urlResolver}} diff --git a/packages/venia-concept/templates/product-shell.mst b/packages/venia-concept/templates/product-shell.mst new file mode 100644 index 00000000000..c467bffd955 --- /dev/null +++ b/packages/venia-concept/templates/product-shell.mst @@ -0,0 +1,17 @@ +{{> templates/doctype-and-head-start}} + {{model.name}} - {{site.name}} + {{#model}} + + + + + +
+ Loading {{name}}... +
+ {{/model}} +{{> templates/preloads}} +{{> templates/scripts}} + + + diff --git a/packages/venia-concept/templates/scripts.mst b/packages/venia-concept/templates/scripts.mst new file mode 100644 index 00000000000..ba17ecf7ee1 --- /dev/null +++ b/packages/venia-concept/templates/scripts.mst @@ -0,0 +1,2 @@ +{{^isDevelopment}}{{/isDevelopment}} + diff --git a/packages/venia-concept/theme.xml b/packages/venia-concept/theme.xml deleted file mode 100644 index 54ce08ba1e4..00000000000 --- a/packages/venia-concept/theme.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - Magento Venia - Magento/pwa - - media/preview.jpg - - diff --git a/packages/venia-concept/validate-queries.js b/packages/venia-concept/validate-queries.js new file mode 100644 index 00000000000..0836640e47c --- /dev/null +++ b/packages/venia-concept/validate-queries.js @@ -0,0 +1,96 @@ +require('dotenv').config(); +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; +const magentoDomainVarName = 'MAGENTO_BACKEND_DOMAIN'; +const magentoDomain = process.env[magentoDomainVarName]; +if (!magentoDomain) { + console.error( + `No ${magentoDomainVarName} environment variable specified. Have you created a .env file?` + ); + process.exit(1); +} + +const { URL } = require('url'); +let uri; +try { + uri = new URL('/graphql', magentoDomain); +} catch (e) { + console.error( + `Could not build a GraphQL endpoint URL from env var ${magentoDomainVarName}: '${magentoDomain}'.` + ); + process.exit(1); +} + +const { gql, HttpLink, makePromise, execute } = require('apollo-boost'); +const { getIntrospectionQuery, introspectionQuery } = require('graphql'); + +const query = gql( + getIntrospectionQuery ? getIntrospectionQuery() : introspectionQuery +); + +const link = new HttpLink({ uri, fetch: require('node-fetch') }); + +async function getSchema() { + console.log(`Validating queries based on schema at ${uri.href}...`); + return makePromise(execute(link, { query })); +} + +async function getRuleConfig() { + const schemaJson = await getSchema(); + console.log(`Retrieved introspection query. Configuring validator...`); + return [ + 'error', + { + env: 'apollo', + projectName: 'magento', + schemaJson + }, + { + env: 'literal', + projectName: 'magento', + schemaJson + } + ]; +} + +async function getLinterConfig() { + const ruleConfig = await getRuleConfig(); + console.log(`Validator configured. Running validator...`); + return { + parser: 'babel-eslint', + rules: { + 'graphql/template-strings': ruleConfig + }, + plugins: ['graphql'], + useEslintrc: false + }; +} + +async function validateQueries() { + const linterConfig = await getLinterConfig(); + const CLIEngine = require('eslint').CLIEngine; + const cli = new CLIEngine(linterConfig); + const files = cli.resolveFileGlobPatterns(['src/**/*.{js,graphql,gql}']); + const report = cli.executeOnFiles(files); + if (report.errorCount > 0) { + console.error(`Errors found!`); + const formatter = cli.getFormatter(); + process.stderr.write(formatter(report.results)); + console.error( + ` +These errors may indicate: + - an out-of-date Magento 2.3 codebase running at "${magentoDomain}" + - an out-of-date project codebase whose queries need updating + +Use GraphiQL or another schema exploration tool on the Magento store to learn more. + ` + ); + process.exit(1); + } else { + console.log( + `Validation passed! All queries found are valid for schema.` + ); + process.exit(0); + } +} + +validateQueries(); diff --git a/packages/venia-concept/venia-upward.yml b/packages/venia-concept/venia-upward.yml new file mode 100644 index 00000000000..ae6ef1abcc5 --- /dev/null +++ b/packages/venia-concept/venia-upward.yml @@ -0,0 +1,124 @@ +status: response.status +headers: response.headers +body: response.body + +response: + when: + - matches: request.url.pathname + pattern: '^/(graphql|rest|media/)' + use: proxy + - matches: request.url.pathname + pattern: '^/favicon.ico' + use: favicon + - matches: urlKey + pattern: '.' + use: appShell + - matches: request.url.pathname + pattern: '^/$' + use: appShell + default: static + +proxy: + target: env.MAGENTO_BACKEND_DOMAIN + ignoreSSLErrors: + inline: true + +appShell: + inline: + status: + when: + - matches: resource.model + pattern: '.' + use: 200 + default: 404 + headers: + inline: + content-type: 'text/html' + body: + engine: mustache + template: resource.template + provide: + model: resource.model + isDevelopment: + when: + - matches: env.NODE_ENV + pattern: 'development' + use: + inline: true + default: + inline: false + urlResolver: urlResolver + +resource: + when: + - matches: urlResolver.type + pattern: 'PRODUCT' + use: + inline: + model: product + template: './templates/product-shell.mst' + - matches: urlResolver.type + pattern: '.' + use: + inline: + model: genericObject + template: './templates/generic-shell.mst' + default: + inline: + template: './templates/notfound-shell.mst' + +urlResolver: urlResolverResult.data.urlResolver + +urlResolverResult: + url: magentoGQL + query: './src/queries/urlResolver.graphql' + variables: + inline: + urlKey: request.url.pathname + +magentoGQL: + engine: mustache + template: + inline: '{{env.MAGENTO_BACKEND_DOMAIN}}graphql' + provide: + - env + +product: productResult.data.productDetail.items.0 +productResult: + url: magentoGQL + query: './src/queries/getProductDetail.graphql' + variables: + inline: + onServer: + inline: true + urlKey: urlKey + +urlKey: + when: + - matches: request.url.pathname + pattern: '^/(?:(.*)\.html)?' + use: $match.$1 + default: + inline: '' + +genericObject: + inline: + name: + inline: + "Hello world!" + +favicon: + inline: + status: 200 + headers: + inline: + content-type: + inline: image/vnd.microsoft.icon + body: + file: + inline: ./media/favicon.ico + encoding: binary + +static: + directory: + inline: './dist' diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index 5abae1ef013..e15b0d9477a 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -7,17 +7,19 @@ const { ServiceWorkerPlugin, DevServerReadyNotifierPlugin, MagentoResolver, + UpwardPlugin, PWADevServer } } = require('@magento/pwa-buildpack'); const path = require('path'); +const pkg = require(path.resolve(__dirname, 'package.json')); const UglifyPlugin = require('uglifyjs-webpack-plugin'); const configureBabel = require('./babel.config.js'); const themePaths = { src: path.resolve(__dirname, 'src'), - output: path.resolve(__dirname, 'web') + output: path.resolve(__dirname, 'dist') }; // mark dependencies for vendor bundle @@ -35,10 +37,12 @@ module.exports = async function(env) { const babelOptions = configureBabel(phase); - const enableServiceWorkerDebugging = Boolean( - process.env.ENABLE_SERVICE_WORKER_DEBUGGING - ); - const serviceWorkerFileName = process.env.SERVICE_WORKER_FILE_NAME; + const enableServiceWorkerDebugging = + Number(process.env.ENABLE_SERVICE_WORKER_DEBUGGING) === 1; + + const serviceWorkerFileName = + process.env.SERVICE_WORKER_FILE_NAME || + pkg.config.serviceWorkerFileName; const config = { context: __dirname, // Node global for the running script's directory @@ -47,7 +51,7 @@ module.exports = async function(env) { }, output: { path: themePaths.output, - publicPath: process.env.MAGENTO_BACKEND_PUBLIC_PATH, + publicPath: '/', filename: 'js/[name].js', chunkFilename: 'js/[name]-[chunkhash].js', pathinfo: true @@ -130,12 +134,12 @@ module.exports = async function(env) { config.devtool = 'eval-source-map'; config.devServer = await PWADevServer.configure({ + publicPath: config.output.publicPath, serviceWorkerFileName, - publicPath: process.env.MAGENTO_BACKEND_PUBLIC_PATH, backendDomain: process.env.MAGENTO_BACKEND_DOMAIN, paths: themePaths, - provideSSLCert: true, - id: 'magento-venia' + id: 'magento-venia', + provideSSLCert: true }); // A DevServer generates its own unique output path at startup. It needs @@ -147,7 +151,11 @@ module.exports = async function(env) { new webpack.NamedChunksPlugin(), new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin(), - new DevServerReadyNotifierPlugin(config.devServer) + new DevServerReadyNotifierPlugin(config.devServer), + new UpwardPlugin( + config.devServer, + path.resolve(__dirname, 'venia-upward.yml') + ) ); } else if (phase === 'production') { config.performance = { @@ -162,12 +170,24 @@ module.exports = async function(env) { parallel: true, uglifyOptions: { ecma: 8, + parse: { + ecma: 8 + }, + compress: { + ecma: 6 + }, + output: { + ecma: 7, + semicolons: false + }, keep_fnames: true } }) ); } else { - throw Error(`Unsupported environment phase in webpack config: `); + throw Error( + `Unsupported environment phase in webpack config: ${phase}` + ); } return config; }; diff --git a/scripts/update_repo_environment.js b/scripts/update_repo_environment.js new file mode 100644 index 00000000000..8a99b614e4c --- /dev/null +++ b/scripts/update_repo_environment.js @@ -0,0 +1,36 @@ +const fs = require('fs'); +const execa = require('execa'); + +let config; +try { + config = execa.sync('git', ['config', '--list', '--local']).stdout; +} catch (e) { + // assume we're not in a git repo and there is nothing to be done + console.error(e); +} +const driverLine = config.match( + /^merge\.(.+)\.driver\s*=\s*(npx )?npm-merge-driver/m +); + +if (driverLine) { + const npmMergeDriver = driverLine[1]; + console.warn( + 'The current Git repository is still configured to merge package-lock.json with npm-merge-driver. npm-merge-driver has been removed from PWA Studio. Removing from local Git config...' + ); + execa.sync('git', [ + 'config', + '--local', + '--remove-section', + `merge.${npmMergeDriver}` + ]); + console.log('Removed from .git/config'); + const attributes = fs + .readFileSync('.git/info/attributes', 'utf8') + .split('\n'); + const lineDriverRE = new RegExp(`^.+merge=${npmMergeDriver}\s*$`); + const toKeep = attributes.filter(a => !lineDriverRE.test(a)); + if (toKeep.length < attributes.length) { + fs.writeFileSync('.git/info/attributes', toKeep.join('\n'), 'utf8'); + } + console.log('Removed from .git/info/attributes'); +}