diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000000..e2496b4cd4d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @martijnwalraven @abernix diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 4e866aef873..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,30 +0,0 @@ - - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 93bf7cf02c8..4189c574ac7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -12,13 +12,3 @@ TODO: * [ ] Rebase your changes on master so that they can be merged easily * [ ] Make sure all tests and linter rules pass - \ No newline at end of file diff --git a/.gitignore b/.gitignore index 842a0b9f634..e1ccdbfbe6c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ junit.xml # Node modules node_modules/ + +# Mac OS +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b4eac9efc6..fa2172b3e34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,83 @@ # Changelog ### vNEXT - - Fix [#1419](https://github.com/apollographql/apollo-server/issues/1419), [#1703](https://github.com/apollographql/apollo-server/issues/1703) file upload using lambda. [PR #1739](https://github.com/apollographql/apollo-server/pull/1739) + +### v2.3.3 + +- `apollo-server` (only): Stop double-invocation of `serverWillStart` life-cycle event. (More specific integrations - e.g. Express, Koa, Hapi, etc. - were unaffected.) [PR #2239](https://github.com/apollographql/apollo-server/pull/2239) +- Avoid traversing `graphql-upload` module tree in run-time environments which aren't Node.js. [PR #2235](https://github.com/apollographql/apollo-server/pull/2235) + +### v2.3.2 + +- Switch from `json-stable-stringify` to `fast-json-stable-stringify`. [PR #2065](https://github.com/apollographql/apollo-server/pull/2065) +- Fix cache hints of `maxAge: 0` to mean "uncachable". [#2197](https://github.com/apollographql/apollo-server/pull/2197) +- Apply `defaultMaxAge` to scalar fields on the root object. [#2210](https://github.com/apollographql/apollo-server/pull/2210) +- Don't write to the persisted query cache until execution will begin. [PR #2227](https://github.com/apollographql/apollo-server/pull/2227) + +### v2.3.1 + +- Provide types for `graphql-upload` in a location where they can be accessed by TypeScript consumers of `apollo-server` packages. [ccf935f9](https://github.com/apollographql/apollo-server/commit/ccf935f9) [Issue #2092](https://github.com/apollographql/apollo-server/issues/2092) + +### v2.3.0 + +- **BREAKING FOR NODE.JS <= 8.5.0 ONLY**: To continue using Apollo Server 2.x in versions of Node.js prior to v8.5.0, file uploads must be disabled by setting `uploads: false` on the `ApolloServer` constructor options. Without explicitly disabling file-uploads, the server will `throw` at launch (with instructions and a link to our documentation). + + This early deprecation is due to changes in the third-party `graphql-upload` package which Apollo Server utilizes to implement out-of-the-box file upload functionality. While, in general, Apollo Server 2.x aims to support all Node.js versions which were under an LTS policy at the time of its release, we felt this required an exception. By `throw`-ing when `uploads` is not explicitly set to `false`, we aim to make it clear immediately (rather than surprisingly) that this deprecation has taken effect. + + While Node.js 6.x is covered by a [Long Term Support agreement by the Node.js Foundation](https://github.com/nodejs/Release#release-schedule) until April 2019, there are substantial performance (e.g. [V8](https://v8.dev/) improvements) and language changes (e.g. "modern" ECMAScript support) offered by newer Node.js engines (e.g. 8.x, 10.x). We encourage _all users_ of Apollo Server to update to newer LTS versions of Node.js prior to the "end-of-life" dates for their current server version. + + **We intend to drop support for Node.js 6.x in the next major version of Apollo Server.** + + For more information, see [PR #2054](https://github.com/apollographql/apollo-server/pull/2054) and [our documentation](https://www.apollographql.com/docs/apollo-server/v2/migration-file-uploads.html). + +### v2.2.7 + +- `apollo-engine-reporting`: When multiple instances of `apollo-engine-reporting` are loaded (an uncommon edge case), ensure that `encodedTraces` are handled only once rather than once per loaded instance. [PR #2040](https://github.com/apollographql/apollo-server/pull/2040) + +### v2.2.6 + +- `apollo-server-micro`: Set the `Content-type` to `text/html` for GraphQL Playground. [PR #2026](https://github.com/apollographql/apollo-server/pull/2026) + +### v2.2.5 + +- Follow-up on the update to `graphql-playground-html` in previous release by also bumping the minor version of the `graphql-playground-react` dependency to `1.7.10` — which is the version requested from the from the CDN bundle by `graphql-playground-html`. [PR #2037](https://github.com/apollographql/apollo-server/pull/2037) + +### v2.2.4 + +- Fix GraphQL Playground documentation scrolling bug in Safari by updating to latest (rebased) fork of `graphql-playground-html`. [PR #2037](https://github.com/apollographql/apollo-server/pull/2037) + +### v2.2.3 + +- When `generateClientInfo` is not used to define the client name, client version and +client reference ID, Apollo Server will now default to the values present in the HTTP headers +of the request (`apollographql-client-name`, `apollographql-client-reference-id` and +`apollographql-client-version` respectively). As a last resort, when those headers are not set, +the query extensions' `clientInfo` values will be used. [PR #1960](https://github.com/apollographql/apollo-server/pull/1960) + +### v2.2.2 + +- Fixed TypeScript 2.2 compatibility via updated `apollo-tooling` dependency. [Issue #1951](https://github.com/apollographql/apollo-server/issues/1951) [`26d6c739`](https://github.com/apollographql/apollo-server/commit/26d6c739505b3112694e641c272c748ce38ba86b) +- Throw a more specific error when asynchronous introspection query behavior is detected. [PR #1955](https://github.com/apollographql/apollo-server/pull/1955) + +### v2.2.1 + +- Added support for an array of `modules` on the `ApolloServer` constructor options. Each element of the `modules` can point to a module which exports `typeDefs` and `resolvers`. These modules can be used in lieu of, or in combination with, directly specifying `schema` or `typeDefs`/`resolvers` on the constructor options. This provides greater modularity and improved organization for logic which might be limited to a specific service. [`8f6481e6`](https://github.com/apollographql/apollo-server/commit/8f6481e60f8418738f9ebbe9d5ab5e7e2ce4d319). +- Added `resolveObject` support to query execution. [`bb67584`](https://github.com/apollographql/apollo-server/commit/bb67584a224843a5b2509c2ebdd94e616fe6227c). +- Fix broken `apollo-server-cloud-functions` in 2.2.0 caused by missing TypeScript project references which resulted in the package not being published to npm in compiled form. [PR #1948](https://github.com/apollographql/apollo-server/pull/1948) + +### v2.2.0 + +- New request pipeline, including support for plugins which can implement lifecycle hooks at various stages of a request. [PR #1795](https://github.com/apollographql/apollo-server/pull/1795). +- Introduce new `apollo-server-testing` utilities. [PR #1909](https://github.com/apollographql/apollo-server/pull/1909) +- Fix mocks configuration to allow disabling of mocks by using `mocks: false`, even if `mockEntireSchema` is `true`. [PR #1835](https://github.com/apollographql/apollo-server/pull/1835) +- Update `graphql-playground-html` to 1.7.8. [PR #1855](https://github.com/apollographql/apollo-server/pull/1855) +- Bring back Azure functions support [Issue #1752](https://github.com/apollographql/apollo-server/issue/1752) [PR #1753](https://github.com/apollographql/apollo-server/pull/1753) - Allow an optional function to resolve the `rootValue`, passing the `DocumentNode` AST to determine the value. [PR #1555](https://github.com/apollographql/apollo-server/pull/1555) - Follow-up on the work in [PR #1516](https://github.com/apollographql/apollo-server/pull/1516) to also fix missing insertion cursor/caret when a custom GraphQL configuration is specified which doesn't specify its own `cursorShape` property. [PR #1607](https://github.com/apollographql/apollo-server/pull/1607) +- Azure functions support [Issue #1752](https://github.com/apollographql/apollo-server/issue/1752) [PR #1753](https://github.com/apollographql/apollo-server/pull/1753) [PR #1948](https://github.com/apollographql/apollo-server/pull/1948) +- Allow JSON parsing in `RESTDataSource` of Content Type `application/hal+json`. [PR #185](https://github.com/apollographql/apollo-server/pull/1853) +- Add support for a `requestAgent` configuration parameter within the `engine` configuration. This can be utilized when a proxy is necessary to transmit tracing and metrics data to Apollo Engine. It accepts either an [`http.Agent`](https://nodejs.org/docs/latest-v8.x/api/http.html#http_class_http_agent) or [`https.Agent`](https://nodejs.org/docs/latest-v8.x/api/https.html#https_class_https_agent) and behaves the same as the `agent` parameter to Node.js' [`http.request`](https://nodejs.org/docs/latest-v8.x/api/http.html#http_http_request_options_callback). [PR #1879](https://github.com/apollographql/apollo-server/pull/1879) ### v2.1.0 diff --git a/README.md b/README.md index 8a1f3e14bb8..a5c413a7b95 100644 --- a/README.md +++ b/README.md @@ -81,15 +81,15 @@ server.listen().then(({ url }) => { ## Integrations -Often times, Apollo Server needs to be run with a particular integration. To start, run `npm install --save apollo-server-` where `` is one of the following: +Often times, Apollo Server needs to be run with a particular integration. To start, run `npm install --save apollo-server-` where `apollo-server-` is one of the following: -- `express` -- `koa` -- `hapi` -- `lambda` -- `cloudflare` - -If a framework is not on this list and it should be supported, please open a PR. +- `apollo-server-express` +- `apollo-server-koa` +- `apollo-server-hapi` +- `apollo-server-lambda` +- `apollo-server-azure-functions` +- `apollo-server-cloud-functions` +- `apollo-server-cloudflare` ### Express @@ -149,7 +149,7 @@ const server = new ApolloServer({ typeDefs, resolvers }); const app = connect(); const path = '/graphql'; -server.use(query()); +app.use(query()); server.applyMiddleware({ app, path }); const port = 4000; @@ -164,7 +164,7 @@ app.listen({ port }, () => ### Koa ```js -const koa = require('koa'); +const Koa = require('koa'); const { ApolloServer, gql } = require('apollo-server-koa'); // Construct a schema, using GraphQL schema language @@ -283,3 +283,8 @@ npm link cd ~/myApp npm link apollo-server- ``` + +## Maintainers + +- [@martijnwalraven](https://github.com/martijnwalraven) (Apollo) +- [@abernix](https://github.com/abernix) (Apollo) diff --git a/codecov.yml b/codecov.yml index d4919272c08..ad84f39f3bf 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,5 +1,8 @@ coverage: status: + project: + default: + threshold: 1% patch: default: # We should raise this, but we want to evaluate how diff --git a/docs/_config.yml b/docs/_config.yml index 06fd6af1015..0df030fa1b9 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -25,6 +25,8 @@ sidebar_categories: - features/unions-interfaces - features/directives - features/creating-directives + - features/authentication + - features/testing # Schema stitching: # - features/schema-stitching # - features/remote-schemas @@ -35,21 +37,14 @@ sidebar_categories: - deployment/heroku - deployment/lambda - deployment/now - Related Guides: - - title: Monitoring - href: https://www.apollographql.com/docs/guides/monitoring.html - - title: Versioning - href: https://www.apollographql.com/docs/guides/versioning.html - - title: Access Control - href: https://www.apollographql.com/docs/guides/access-control.html - - title: Security - href: https://www.apollographql.com/docs/guides/security.html + API Reference: - api/apollo-server - api/graphql-tools Migration: - migration-two-dot - migration-engine + - migration-file-uploads github_repo: apollographql/apollo-server content_root: docs/source @@ -62,13 +57,5 @@ root: /docs/apollo-server/ public_dir: public/docs/apollo-server -redirects: - /docs/apollo-server/v2/features/cdn.html: - docs/guides/performance - /docs/apollo-server/v2/features/apq.html: - docs/guides/performance - /docs/apollo-server/v2/features/file-uploads.html: - docs/guides/file-uploads - versioned-netlify-redirects: netlify_site_id: apollo-server-docs diff --git a/docs/package-lock.json b/docs/package-lock.json index 97d8271d24e..9a8e1e3368f 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -252,7 +252,7 @@ }, "babel-plugin-syntax-decorators": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", "dev": true }, @@ -757,34 +757,13 @@ } }, "chexo": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/chexo/-/chexo-1.0.5.tgz", - "integrity": "sha512-bfJ367D4+o3MSP8MXaPp6vhhYqltHnsPoo1ULd9UFXszuD1ebB8hDP9kxSQ/ZV4Xf0BdnbcN3uHTr2S3wEiLsg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/chexo/-/chexo-1.0.7.tgz", + "integrity": "sha512-7ScGKzHsZDLpCZhzIypwbK+hkx1fkWZjeLemEkmnreThGLNWhLRDC4gJ/Wu04jAbH0zYR/ev0QPAACEnaYXrcA==", "dev": true, "requires": { - "hexo-cli": "^1.0.4", + "hexo-cli": "^1.1.0", "minimist": "^1.2.0" - }, - "dependencies": { - "hexo-cli": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hexo-cli/-/hexo-cli-1.1.0.tgz", - "integrity": "sha512-IWQPppwgmj1iBUcP5mpcMg3Tre6a8Qlr8ejXw6naZiJNSepSgh4mS3KiNPKDa2qQIgPDqJYJzNVFLw+RLA9CkA==", - "dev": true, - "requires": { - "abbrev": "^1.0.7", - "bluebird": "^3.4.0", - "chalk": "^1.1.3", - "command-exists": "^1.2.0", - "hexo-fs": "^0.2.0", - "hexo-log": "^0.2.0", - "hexo-util": "^0.6.0", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "resolve": "^1.5.0", - "tildify": "^1.2.0" - } - } } }, "chokidar": { @@ -833,18 +812,6 @@ } } }, - "clipboard": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz", - "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", - "dev": true, - "optional": true, - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -1173,13 +1140,6 @@ "dev": true, "optional": true }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", - "dev": true, - "optional": true - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1623,8 +1583,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -2039,8 +1998,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -2096,7 +2054,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2140,14 +2097,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -2221,16 +2176,6 @@ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "dev": true, - "optional": true, - "requires": { - "delegate": "^3.1.2" - } - }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -2525,6 +2470,25 @@ "safe-json-stringify": "~1" } }, + "hexo-cli": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/hexo-cli/-/hexo-cli-1.1.0.tgz", + "integrity": "sha512-IWQPppwgmj1iBUcP5mpcMg3Tre6a8Qlr8ejXw6naZiJNSepSgh4mS3KiNPKDa2qQIgPDqJYJzNVFLw+RLA9CkA==", + "dev": true, + "requires": { + "abbrev": "^1.0.7", + "bluebird": "^3.4.0", + "chalk": "^1.1.3", + "command-exists": "^1.2.0", + "hexo-fs": "^0.2.0", + "hexo-log": "^0.2.0", + "hexo-util": "^0.6.0", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "resolve": "^1.5.0", + "tildify": "^1.2.0" + } + }, "hexo-front-matter": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/hexo-front-matter/-/hexo-front-matter-0.2.3.tgz", @@ -2579,16 +2543,376 @@ } }, "hexo-prism-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexo-prism-plus/-/hexo-prism-plus-1.0.0.tgz", - "integrity": "sha512-OqJp3tp41VZAtpt0L7SqFk4GDMEIkSBQr8i45XURNLTIxEMvaXNzFgtjkB+5+wnFvZuJIpORzd8kdA2KiyHLVg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hexo-prism-plus/-/hexo-prism-plus-1.1.0.tgz", + "integrity": "sha512-lkDJRZDVN3iNpYtuwrI+JFLj3rSa4PeMwz1OlVfUOkY6CyjD/D3dGtW7cTpcxYejr3/JWjusIqWVnUTLpKr4Yw==", "dev": true, "requires": { - "hexo-fs": "^0.2.2", + "hexo-fs": "^1.0.0", "hexo-inject": "^1.0.0", - "hexo-util": "^0.6.2", - "lodash": "^4.17.4", - "node-prismjs": "^0.1.1" + "hexo-util": "^0.6.3", + "lodash": "^4.17.11" + }, + "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": "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" + } + } + } + }, + "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" + } + }, + "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-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" + } + } + } + }, + "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" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "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" + } + } + } + }, + "hexo-fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexo-fs/-/hexo-fs-1.0.0.tgz", + "integrity": "sha512-eFfF2Yb2SRW2quTywtjV49KsAOinVDYkW5ZZ9bmD3ycSHmH2M2NmjGaE8XoNI/4pYGMmrLWxsOMWnZBsBz1lZg==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chokidar": "^2.0.4", + "escape-string-regexp": "^1.0.5", + "graceful-fs": "^4.1.11" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "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 + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "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" + } + } } }, "hexo-renderer-ejs": { @@ -2662,9 +2986,9 @@ } }, "hexo-versioned-netlify-redirects": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/hexo-versioned-netlify-redirects/-/hexo-versioned-netlify-redirects-1.0.7.tgz", - "integrity": "sha512-9N0FCU7O5ZpmYx5MC0DJ5VM7HPucL0kIzBtLTk7SfH7MkFmUEbxI8yZc22g3WEev9vmUOPaTDF3XXOXO/OKw9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hexo-versioned-netlify-redirects/-/hexo-versioned-netlify-redirects-1.1.0.tgz", + "integrity": "sha512-kfY19ZZDwBnRaZFA6KQgt5DhVBpTQG1yBoq01zaACFHIFuSd9OjhiHCkJ3it+TMvGl+JGTtgKhIwZoQUJHyv/g==", "requires": { "url-join": "^4.0.0" } @@ -2679,8 +3003,7 @@ "version": "2.16.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true, - "optional": true + "dev": true }, "hosted-git-info": { "version": "2.7.1", @@ -2986,8 +3309,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "optional": true + "dev": true }, "is-wsl": { "version": "1.1.0", @@ -3245,8 +3567,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true, - "optional": true + "dev": true }, "lodash.defaults": { "version": "4.2.0", @@ -3382,9 +3703,9 @@ "dev": true }, "meteor-theme-hexo": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/meteor-theme-hexo/-/meteor-theme-hexo-1.0.16.tgz", - "integrity": "sha512-VKPrmSf/2t+kkb7eWe8g0TApJFcAOBFSmYomaV2yEqiiw9sxNSId0nI/Z92/4w0SU2eov5AlFtyiJRShQtGHGQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/meteor-theme-hexo/-/meteor-theme-hexo-2.0.1.tgz", + "integrity": "sha512-VOmIeIvWbeyjtOwNNI2qnujltrddFic49fvoJrOWPR09cDz2aMO3phAxXF7h7arlxo3YMkHisQxr2jnoM+wkGQ==", "dev": true, "dependencies": { "argparse": { @@ -3832,7 +4153,6 @@ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, - "optional": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -3851,22 +4171,19 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true + "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, - "optional": true + "dev": true }, "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, - "optional": true + "dev": true } } }, @@ -3898,15 +4215,6 @@ "integrity": "sha1-Mcur63GmeufdWn3AQuUcPHWGhQE=", "dev": true }, - "node-prismjs": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/node-prismjs/-/node-prismjs-0.1.2.tgz", - "integrity": "sha512-WKb6ZbUlPWarzS8jR2UdIbV4lYpt6sOTkIx3u5Ldz55K1Zzs982KyF6aj1zjZbrrx/UGZSZ1e0j28lIzcm3ceg==", - "dev": true, - "requires": { - "prismjs": "~1.6.0" - } - }, "nopt": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz", @@ -3994,6 +4302,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, + "optional": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -4012,6 +4321,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -4185,6 +4495,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, + "optional": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -4197,6 +4508,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -4262,7 +4574,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "dev": true, + "optional": true }, "is-glob": { "version": "4.0.0", @@ -4279,6 +4592,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -4288,6 +4602,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -4298,13 +4613,15 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "dev": true, + "optional": true }, "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 + "dev": true, + "optional": true }, "micromatch": { "version": "3.1.10", @@ -4549,8 +4866,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true, - "optional": true + "dev": true }, "path-exists": { "version": "2.1.0", @@ -4634,8 +4950,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "optional": true + "dev": true }, "postinstall-build": { "version": "5.0.3", @@ -4655,15 +4970,6 @@ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, - "prismjs": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.6.0.tgz", - "integrity": "sha1-EY2V+3pm26InLjQ7NF9SNmWds2U=", - "dev": true, - "requires": { - "clipboard": "^1.5.5" - } - }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -4988,13 +5294,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", - "dev": true, - "optional": true - }, "semver": { "version": "5.5.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", @@ -5616,13 +5915,6 @@ "os-homedir": "^1.0.0" } }, - "tiny-emitter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz", - "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==", - "dev": true, - "optional": true - }, "titlecase": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/titlecase/-/titlecase-1.1.2.tgz", @@ -5892,8 +6184,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", - "dev": true, - "optional": true + "dev": true }, "upper-case": { "version": "1.1.3", @@ -6029,8 +6320,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "ws": { "version": "3.3.3", diff --git a/docs/package.json b/docs/package.json index 95b685868cb..6f5228ebedd 100644 --- a/docs/package.json +++ b/docs/package.json @@ -3,19 +3,19 @@ "version": "0.0.0", "private": true, "hexo": { - "version": "3.7.1" + "version": "3.8.0" }, "devDependencies": { "apollo-hexo-config": "1.0.8", - "chexo": "1.0.5", + "chexo": "1.0.7", "hexo": "3.8.0", "hexo-browsersync": "0.3.0", - "hexo-prism-plus": "1.0.0", + "hexo-prism-plus": "1.1.0", "hexo-renderer-ejs": "0.3.1", "hexo-renderer-less": "0.2.0", "hexo-renderer-marked": "0.3.2", "hexo-server": "0.3.3", - "meteor-theme-hexo": "1.0.16" + "meteor-theme-hexo": "2.0.1" }, "scripts": { "start": "npm run build && chexo apollo-hexo-config -- server", @@ -24,6 +24,6 @@ "test": "npm run clean; npm run build" }, "dependencies": { - "hexo-versioned-netlify-redirects": "1.0.7" + "hexo-versioned-netlify-redirects": "1.1.0" } } diff --git a/docs/source/_redirects b/docs/source/_redirects new file mode 100644 index 00000000000..f81648aa568 --- /dev/null +++ b/docs/source/_redirects @@ -0,0 +1,3 @@ +# The test-utils lived at its current home for about 5 days. +# It at least exists on some slides and Tweets. +/docs/apollo-server/features/test-utils.html /docs/apollo-server/features/testing.html diff --git a/docs/source/api/apollo-server.md b/docs/source/api/apollo-server.md index 3b39fc456f6..54cc0372094 100644 --- a/docs/source/api/apollo-server.md +++ b/docs/source/api/apollo-server.md @@ -171,7 +171,7 @@ The `applyMiddleware` method is provided by the `apollo-server-{integration}` pa Pass the integration-specific cors options. False removes the cors middleware and true uses the defaults. - * `bodyParser`: <`Object` | `boolean`> ([express](https://github.com/expressjs/body-parser#body-parser)) + * `bodyParserConfig`: <`Object` | `boolean`> ([express](https://github.com/expressjs/body-parser#body-parser)) Pass the body-parser options. False removes the body parser middleware and true uses the defaults. @@ -303,8 +303,10 @@ addMockFunctionsToSchema({ * `calculateSignature`: (ast: DocumentNode, operationName: string) => string - Specify the function for creating a signature for a query. See signature.ts - for details. + Specify the function for creating a signature for a query. + + > See [`apollo-graphql`'s `signature.ts`](https://npm.im/apollo-graphql) + > for more information on how the default signature is generated. * `reportIntervalMs`: number @@ -322,6 +324,10 @@ addMockFunctionsToSchema({ The URL of the Engine report ingress server. +* `requestAgent`: `http.Agent | https.Agent | false` + + HTTP(s) agent to be used for Apollo Engine metrics reporting. This accepts either an [`http.Agent`](https://nodejs.org/docs/latest-v10.x/api/http.html#http_class_http_agent) or [`https.Agent`](https://nodejs.org/docs/latest-v10.x/api/https.html#https_class_https_agent) and behaves the same as the `agent` parameter to Node.js' [`http.request`](https://nodejs.org/docs/latest-v8.x/api/http.html#http_http_request_options_callback). + * `debugPrintReports`: boolean If set, prints all reports as JSON when they are sent. @@ -365,3 +371,25 @@ addMockFunctionsToSchema({ * `maskErrorDetails`: boolean Set to true to remove error details from the traces sent to Apollo's servers. Defaults to false. + +* `generateClientInfo`: (GraphQLRequestContext) => ClientInfo **AS 2.2** + + Creates a client context(ClientInfo) based on the request pipeline's + context, which contains values like the request, response, cache, and + context. This generated client information will be provided to Apollo + Engine and can be used to filter metrics. Set `clientName` to identify a + particular client. Use `clientVersion` to specify a version for a client + name. The default function will use the `clientInfo` field inside of + GraphQL Query `extensions`. + + For advanced use cases when you already have an opaque string to identify + your client (e.g. an API key, x509 certificate, or team codename), use the + `clientReferenceId` field to add a reference to its internal identity. This + client reference ID will not be displayed in the UI but will be available + for cross-correspondence, so names and reference ids should have a one to + one relationship. + + > [WARNING] If you specify a `clientReferenceId`, Engine will treat the + > `clientName` as a secondary lookup, so changing a `clientName` may result + > in an unwanted experience. + diff --git a/docs/source/api/graphql-tools.md b/docs/source/api/graphql-tools.md index ae8db196ee5..abbd9bfa308 100644 --- a/docs/source/api/graphql-tools.md +++ b/docs/source/api/graphql-tools.md @@ -25,7 +25,7 @@ const schema = makeExecutableSchema({ resolvers, }); -const rootResolveFunction = (root, args, context, info) => { +const rootResolveFunction = (parent, args, context, info) => { //perform action before any other resolvers }; diff --git a/docs/source/best-practices/authentication.md b/docs/source/best-practices/authentication.md deleted file mode 100644 index 2d986a78c97..00000000000 --- a/docs/source/best-practices/authentication.md +++ /dev/null @@ -1,248 +0,0 @@ ---- -title: Auth -description: Securing our app and serving our users ---- - -

Background: Authentication vs. Authorization

- -**Authentication** describes a process where an application proves the identity of a user, meaning someone claiming to be a certain user through the client is the actual user that has permission to make a request to the server. In most systems, a user and server share a handshake and token that uniquely pairs them together, ensuring both sides know they are communicating with their intended target. - -**Authorization** defines what a user, such as admin or user, is allowed to do. Generally a server will authenticate users and provide them an authorization role that permits the user to perform a subset of all possible operations, such as read and not write. - -

Auth in GraphQL

- -GraphQL offers similar authentication and authorization mechanics as REST and other data fetching solutions with the possibility to control more fine grain access within a single request. There are two common approaches: schema authorization and operation authorization. - -**Schema authorization** follows a similar guidance to REST, where the entire request and response is checked for an authenticated user and authorized to access the servers data. - -**Operation authorization** takes advantage of the flexibility of GraphQL to provide public portions of the schema that don't require any authorization and private portions that require authentication and authorization. - -> Authorization within our GraphQL resolvers is a great first line of defense for securing our application. We recommended having similar authorization patterns within our data fetching models to ensure a user is authorized at every level of data fetching and updating. - -

Authenticating users

- -All of the approaches require that users be authenticated with the server. If our system already has login method setup to authenticate users and provide credentials that can be used in subsequent requests, we can use this same system to authenticate GraphQL requests. With that said, if we are creating a new infrastructure for user authentication, we can follow the existing best practice to authenticate users. For a full example of authentication, follow [this example](#auth-example), which uses [passport.js](http://www.passportjs.org/). - -

Schema Authorization

- -Schema authorization is useful for GraphQL endpoints that require known users and allow access to all fields inside of a GraphQL endpoint. This approach is useful for internal applications, which are used by a group that is known and generally trusted. Additionally it's common to have separate GraphQL services for different features or products that are entirely available to users, meaning if a user is authenticated, they are authorized to access all the data. Since schema authorization does not need to be aware of the GraphQL layer, our server can add a middleware in front of the GraphQL layer to ensure authorization. - -```js -// authenticate for schema usage -const context = ({ req }) => { - const user = myAuthenticationLookupCode(req); - if (!user) { - throw new Error("You need to be authenticated to access this schema!"); - } - - return { user } -}; - -const server = new ApolloServer({ typeDefs, resolvers, context }); - -server.listen().then(({ url }) => { - console.log(`🚀 Server ready at ${url}`) -}); -``` - -Currently this server will allow any authenticated user to request all fields in the schema, which means that authorization is all or nothing. While some applications provide a shared view of the data to all users, many use cases require scoping authorizations and limiting what some users can see. The authorization scope is shared across all resolvers, so this code adds the user id and scope to the context. - -```js -const { ForbiddenError } = require("apollo-server"); - -const context = ({ req }) => { - const user = myAuthenticationLookupCode(req); - if (!user) { - throw new ForbiddenError( - "You need to be authenticated to access this schema!" - ); - } - - const scope = lookupScopeForUser(user); - - return { user, scope }; -}; - -const server = new ApolloServer({ - typeDefs, - resolvers, - context -}); - -server.listen().then(({ url }) => { - console.log(`🚀 Server ready at ${url}`); -}); -``` - -Now within a resolver, we are able to check the user's scope. If the user is not an administrator and `allTodos` are requested, a GraphQL specific forbidden error is thrown. Apollo Server will handle associate the error with the particular path and return it along with any other data successfully requested, such as `myTodos`, to the client. - -```js -const { ForbiddenError } = require("apollo-server"); - -const resolvers = { - Query: { - allTodos: (source, args, context) => { - if (context.scope !== "ADMIN") { - throw ForbiddenError("Need Administrator Privileges"); - } - return context.Todos.getAll(); - }, - myTodos: (source, args, context) => { - return context.Todos.getById(context.user_id); - } - } -}; -``` - -The major downside to schema authorization is that all requests must be authenticated, which prevents unauthenticated requests to access information that should be publicly accessible, such as a home page. The next approach, partial query authorization, enables a portion of the schema to be public and authorize portions of the schema to authenticated users. - -## Operation Authorization - -Operation authorization removes the catch all portion of our context function that throws an unauthenticated error, moving the authorization check within resolvers. The instantiation of the server becomes: - -```js -const context = ({ req }) => { - const user = myAuthenticationLookupCode(req); - if (!user) { - return { user: null, scope: null } - } - - const scope = lookupScopeForUser(user); - return { user, scope } -}; - -const server = new ApolloServer({ - typeDefs, - resolvers, - context -}); - -server.listen().then(({ url }) => { - console.log(`🚀 Serverready at ${url}`) -}); -``` - -The benefit of doing operation authorization is that private and public data is more easily managed an enforced. Take for example a schema that allows finding `allTodos` in the app (an administrative action), seeing any `publicTodos` which requires no authorization, and returning just a single users todos via `myTodos`. Using Apollo Server, we can easily build complex authorization models like so: - -```js -const { ForbiddenError, AuthenticationError } = require("apollo-server"); - -const resolvers = { - Query: { - allTodos: (source, args, context) => { - if (!context.scope) { - throw AuthenticationError("You must be logged in to see all todos"); - } - - if (context.scope !== "ADMIN") { - throw ForbiddenError("You must be an administrator to see all todos"); - } - - return context.Todos.getAllTodos(); - }, - publicTodos: (source, args, context) => { - return context.Todos.getPublicTodos(); - }, - myTodos: (source, args, context) => { - if (!context.scope) { - throw AuthenticationError("You must be logged in to see all todos"); - } - - return context.Todos.getByUserId(context.user.id); - } - } -}; -``` - -## Should I send a password in a mutation? - -Since GraphQL queries are sent to a server in the same manner as REST requests, the same policies apply to sending sensitive data over the wire. The current best practice is to provide an encrypted connection over https or wss if we are using websockets. Provided we setup this layer, passwords and other sensitive information should be secure. - -## Auth Example - -If you are new setting up new infrastructure or would like to understand an example of how to adapt your existing login system, you can follow this example using passport.js. We will use this example of authentication in the subsequent sections. To skip this section, jump down to the - -```shell -npm install --save express passport body-parser express-session node-uuid passport-local apollo-server graphql -``` - -```js -const bodyParser = require('body-parser'); -const express = require('express'); -const passport = require('passport'); -const session = require('express-session'); -const uuid = require('node-uuid'); -``` - -After installing and importing the necessary packages, this code checks the user's password and attaches their id to the request. - -```js -let LocalStrategy = require('passport-local').Strategy; -const { DB } = require('./schema/db.js'); - -passport.use( - 'local', - new LocalStrategy(function(username, password, done) { - let checkPassword = DB.Users.checkPassword(username, password); - let getUser = checkPassword - .then(is_login_valid => { - if (is_login_valid) return DB.Users.getUserByUsername(username); - else throw new Error('invalid username or password'); - }) - .then(user => done(null, user)) - .catch(err => done(err)); - }), -); - -passport.serializeUser((user, done) => done(null, user.id)); - -passport.deserializeUser((id, done) => - DB.Users.get(id).then((user, err) => done(err, user)) -); -``` - -Now that passport has been setup, we initialize the server application to use the passport middleware, attaching the user id to the request. - -```js -const app = express(); - -//passport's session piggy-backs on express-session -app.use( - session({ - genid: function(req) { - return uuid.v4(); - }, - secret: 'Z3]GJW!?9uP"/Kpe', - }) -); - -//Provide authentication and user information to all routes -app.use(passport.initialize()); -app.use(passport.session()); -``` - -Finally we provide the login route and start Apollo Server. - -```js -const { typeDefs, resolvers } = require('./schema'); - -//login route for passport -app.use('/login', bodyParser.urlencoded({ extended: true })); -app.post( - '/login', - passport.authenticate('local', { - successRedirect: '/', - failureRedirect: '/login', - failureFlash: true, - }), -); - -//Depending on the authorization model chosen, you may include some extra middleware here before you instantiate the server - -//Create and start your apollo server -const server = new ApolloServer({ typeDefs, resolvers, app }); - -server.listen().then(({ url }) => { - console.log(`🚀 Server ready at ${url}`) -}); -``` diff --git a/docs/source/best-practices/caching.md b/docs/source/best-practices/caching.md deleted file mode 100644 index 70a62770ef9..00000000000 --- a/docs/source/best-practices/caching.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Caching -description: Caching operations ---- - -One of the best ways we can speed up our application is to implement caching into it. Apollo Client has a intelligent cache which greatly lowers the work the client needs to do to fetch and manage data, but what about our server? Caching in Apollo Server can be done in a number of ways, but we recommend three in particular that have a good balance between complexity to manage and benefit of use. - -

Whole query caching

- -GraphQL operations on a client are best when they are statically defined and used in an application. When this is the case, often times there will be operations that could easily be cached as a full result of the the request. We call this *whole query caching* and it is incredibly easy to implement with Apollo Server. Unlike custom REST endpoints, using Apollo Server allows us to define the cacheability of our resources and dynamically calculate the best possible cache timing for any given operation. - -- For more information about setting up Apollo Engine with Apollo Server, [read this guide]() -- For more information about setting up whole query caching with Apollo Engine, [read this guide](https://www.apollographql.com/docs/engine/caching.html) - -

CDN integration

- -If our application has a lot of public data that doesn’t change very frequently, and it’s important for it to load quickly, we will probably benefit from using a CDN to cache our API results. This can be particularly important for media or content companies like news sites and blogs. - -A CDN will store our API result close to the “edge” of the network — that is, close to the region the user is in — and deliver a cached result much faster than it would have required to do a full round-trip to our actual server. As an added benefit, we get to save on server load since that query doesn’t actually hit our API. - -- Setting up CDN caching with Apollo Server is incredibly easy, simply setup Apollo Engine then follow this [guide](https://www.apollographql.com/docs/engine/cdn.html) -- For more information about using a CDN with Apollo Engine, check out this [article](https://blog.apollographql.com/caching-graphql-results-in-your-cdn-54299832b8e2) - diff --git a/docs/source/best-practices/monitoring.md b/docs/source/best-practices/monitoring.md deleted file mode 100644 index 9882362c1b0..00000000000 --- a/docs/source/best-practices/monitoring.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Monitoring ---- - -Intro about what to watch for? - -## ENGINE - -## formatError diff --git a/docs/source/best-practices/organization.md b/docs/source/best-practices/organization.md deleted file mode 100644 index c4282a745dc..00000000000 --- a/docs/source/best-practices/organization.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -title: Organizing your code -description: Scaling your Apollo Server from a single file to your entire team ---- - -The GraphQL schema defines the api for Apollo Server, providing the single source of truth between client and server. A complete schema contains type definitions and resolvers. Type definitions are written and documented in the [Schema Definition Language(SDL)]() to define the valid server entry points. Corresponding to one to one with type definition fields, resolvers are functions that retrieve the data described by the type definitions. - -To accommodate this tight coupling, type definitions and resolvers should be kept together in the same file. This collocation allows developers to modify fields and resolvers with atomic schema changes without unexpected consequences. At the end to build a complete schema, the type definitions are combined in an array and resolvers are merged together. Throughout all the examples, the resolvers delegate to a data model, as explained in [this section](). - -> Note: This schema separation should be done by product or real-world domain, which create natural boundaries that are easier to reason about. - -## Prerequisites - -* essentials/schema for connection between: - * GraphQL Types - * Resolvers - -

Organizing schema types

- -With large schemas, defining types in different files and merging them to create the complete schema may become necessary. We accomplish this by importing and exporting schema strings, combining them into arrays as necessary. The following example demonstrates separating the type definitions of [this schema](#first-example-schema) found at the end of the page. - -```js -// comment.js -const typeDefs = gql` - type Comment { - id: ID! - message: String - author: String - } -`; - -export typeDefs; -``` - -The `Post` includes a reference to `Comment`, which is added to the array of type definitions and exported: - -```js -// post.js -const typeDefs = gql` - type Post { - id: ID! - title: String - content: String - author: String - comments: [Comment] - } -`; - -// Export Post and all dependent types -export typeDefs; -``` - -Finally the root Query type, which uses Post, is created and passed to the server instantiation: - -```js -// schema.js -const Comment = require('./comment'); -const Post = require('./post'); - -const RootQuery = gql` - type Query { - post(id: ID!): Post - } -`; - -const server = new ApolloServer({ - typeDefs: [RootQuery, Post.typeDefs, Comment.typeDefs], - resolvers, //defined in next section -}); - -server.listen().then(({ url }) => { - console.log(`🚀 Server ready at ${url}`) -}); -``` - -

Organizing resolvers

- -For the type definitions above, we can accomplish the same modularity with resolvers by combining each type's resolvers together with Lodash's `merge` or another equivalent. The [end of this page](#first-example-resolvers) contains a complete view of the resolver map. - -```js -// comment.js -const CommentModel = require('./models/comment'); - -const resolvers = { - Comment: { - votes: (parent) => CommentModel.getVotesById(parent.id) - } -}; - -export resolvers; -``` - -The `Post` type: - -```js -// post.js -const PostModel = require('./models/post'); - -const resolvers = { - Post: { - comments: (parent) => PostModel.getCommentsById(parent.id) - } -}; - -export resolvers; -``` - -Finally, the Query type's resolvers are merged and the result is passed to the server instantiation: - -```js -// schema.js -const { merge } = require('lodash'); -const Post = require('./post'); -const Comment = require('./comment'); - -const PostModel = require('./models/post'); - -// Merge all of the resolver objects together -const resolvers = merge({ - Query: { - post: (_, args) => PostModel.getPostById(args.id) - } -}, Post.resolvers, Comment.resolvers); - -const server = new ApolloServer({ - typeDefs, //defined in previous section - resolvers, -}); - -server.listen().then(({ url }) => { - console.log(`🚀 Server ready at ${url}`) -}); -``` - -

Extending types

- -The `extend` keyword provides the ability to add fields to existing types. Using `extend` is particularly useful in avoiding a large list of fields on root Queries and Mutations. - -```js -//schema.js -const bookTypeDefs = gql` -extend type Query { - books: [Book] -} - -type Book { - id: ID! -} -`; - -// These type definitions are often in a separate file -const authorTypeDefs = gql` -extend type Query { - authors: [Author] -} - -type Author { - id: ID! -} -`; -export const typeDefs = [bookTypeDefs, authorTypeDefs] -``` - -```js -const {typeDefs, resolvers} = require('./schema'); - -const rootQuery = gql` -"Query can and must be defined once per schema to be extended" -type Query { - _empty: String -}`; - -const server = new ApolloServer({ - typeDefs: [RootQuery].concat(typeDefs), - resolvers, -}); - -server.listen().then(({ url }) => { - console.log(`🚀 Server ready at ${url}`) -}); -``` - -> Note: In the current version of GraphQL, you can’t have an empty type even if you intend to extend it later. So we need to make sure the Query type has at least one field — in this case we can add a fake `_empty` field. Hopefully in future versions it will be possible to have an empty type to be extended later. - -

Documenting a Schema

- -In addition to modularization, documentation within the SDL enables the schema to be effective as the single source of truth between client and server. GraphQL GUIs have built-in support for displaying docstrings with markdown syntax, such as those found in the following schema. - -```graphql -""" -Description for the type -""" -type MyObjectType { - """ - Description for field - Supports multi-line description - """ - myField: String! - - otherField( - """ - Description for argument - """ - arg: Int - ) -} -``` - -

API

- -Apollo Server pass `typeDefs` and `resolvers` to the `graphql-tools`'s `makeExecutableSchema`. - -TODO point at graphql-tools `makeExecutableSchema` api - -

Example Application Details

- -

Schema

- -The full type definitions for the first example: - -```graphql -type Comment { - id: ID! - message: String - author: String - votes: Int -} - -type Post { - id: ID! - title: String - content: String - author: String - comments: [Comment] -} - -type Query { - post(id: ID!): Post -} -``` - -

Resolvers

- -The full resolver map for the first example: - -```js -const CommentModel = require('./models/comment'); -const PostModel = require('./models/post'); - -const resolvers = { - Comment: { - votes: (parent) => CommentModel.getVotesById(parent.id) - } - Post: { - comments: (parent) => PostModel.getCommentsById(parent.id) - } - Query: { - post: (_, args) => PostModel.getPostById(args.id) - } -} -``` diff --git a/docs/source/best-practices/performance.md b/docs/source/best-practices/performance.md deleted file mode 100644 index 46d09ca6cd9..00000000000 --- a/docs/source/best-practices/performance.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -title: Performance -description: Reduce requests and speeding up applications ---- - -GraphQL offers performance benefits for most applications. By reducing round-trips when fetching data, lower the amount of data we are sending back, and make it easier to batch data lookups. Since GraphQL is often built as a stateless request-response pattern, scaling our app horizontally becomes much easier. In this section, we will dive into some benefits that Apollo Server brings to our app, and some patterns for speeding up our service. - -## Prevent over-fetching - -Rest endpoints often return all of the fields for whatever data they are returning. As applications grow, their data needs grow as well, which leads to a lot of unnecessary data being downloaded by our client applications. With GraphQL this isn't a problem because Apollo Server will only return the data that we ask for when making a request! Take for example a screen which shows an avatar of the currently logged in user. In a rest app we may make a request to `/api/v1/currentUser` which would return a response like this: - -```json -{ - "id": 1, - "firstName": "James", - "lastName": "Baxley", - "suffix": "III", - "avatar": "/photos/profile.jpg", - "friendIds": [2, 3, 4, 5, 6, 7], - "homeId": 1, - "occupation": "farmer", - // and so on for every field on this model that our client **could** use -} -``` - -Contrast that to the request a client would send to Apollo Server and the response they would receive: - -```graphql -query GetAvatar { - currentUser { - avatar - } -} -``` - -```json -{ - "data": { - "currentUser": { - "avatar": "/photos/profile.jpg" - } - } -} -``` - -No matter how much our data grows, this query will always only return the smallest bit of data that the client application actually needs! This will make our app faster and our end users data plan much happier! - -## Reducing round-trips - -Applications typically need to fetch multiple resources to load any given screen for a user. When building an app on top of a REST API, screens need to fetch the first round of data, then using that information, make another request to load related information. A common example of this would be to load a user, then load their friends: - -```js -const userAndFriends = fetch("/api/v1/user/currentUser").then(user => { - const friendRequest = Promise.all( - user.friendIds.map(id => fetch(`/api/vi/user/${id}`)) - ); - - return friendRequest.then(friends => { - user.friends = friends; - return user; - }); -}); - -``` - -The above code would make at minimum two requests, one for the logged in user and one for a single friend. With more friends, the number of requests jumps up quite a lot! To get around this, custom endpoints are added into a RESTful API. In this example, a `/api/v1/friends/:userId` may be added to make fetching friends a single request per user instead of one per friend. - -With GraphQL this is easily done in a single request! Given a schema like this: - -```graphql -type User { - id: ID! - name: String! - friends: [User] -} - -type Query { - currentUser: User -} -``` - -We can easily fetch the current user and all of their friends in a single request! - -```graphql -query LoadUserAndFriends { - currentUser { - id - name - friends { - id - name - } - } -} -``` - -## Batching data lookups - -If we take the above query we may think GraphQL simply moves the waterfall of requests from the client to the server. Even if this was true, application speeds would still be improved. However, Apollo Server makes it possible to make applications even faster by batching data requests. - -The most common way to batch requests is by using Facebook's [`dataloader`](https://github.com/facebook/dataloader) library. Let's explore a few options for request batching the previous operation: - -

Custom resolvers for batching

- -The simplest (and often easiest) way to speed up a GraphQL service is to create resolvers that optimistically fetch the needed data. Often times the best thing to do is to write the simplest resolver possible to look up data, profile it with a tool like Apollo Engine, then improve slow resolvers with logic tuned for the way our schema is used. Take the above query, for example: - -```js -const User = { - friends: (user, args, context) => { - // A simple approach to find each friend. - return user.friendIds.map(id => context.UserModel.findById(id)); - } -} - -``` - -The above resolver will make a database lookup for the initial user and then one lookup for every friend that our user has. This would quickly turn into an expensive resolver to call so lets look at how we could speed it up! First, lets take a simple, but proven technique: - -```js -const User = { - friends: (user, args, context) => { - // a custom model method for looking up multiple users - return context.UserModel.findByIds(user.friendIds); - } -} -``` - -Instead of fetching each user independently, we could fetch all users at once in a single lookup. This would be analogous to `SELECT * FROM users WHERE id IN (1,2,3,4)` vs the previous query would have been multiple versions of `SELECT * FROM users WHERE id = 1`. - -Often times, custom resolvers are enough to speed up our server to the levels we want. However, there may be times where we want to be even more efficient when batching data. Lets say we expanded our operation to include more information: - -```graphql -query LoadUserAndFriends { - currentUser { - id - name - friends { - id - name - } - family { - id - name - } - } -} -``` - -Assuming that `family` returns more `User` types, we now are making at minimum three database calls: 1) the user, 2) the batch of friends, and 3) the batch of family members. If we expand the query deeper: - -``` -query LoadUserAndFriends { - currentUser { - id - name - friends { - id - name - ...peopleTheyCareAbout - } - family { - id - name - ...peopleTheyCareAbout - } - } -} - -fragment peopleTheyCareAbout on User { - family { - id - name - } - friends { - id - name - } -} -``` - -We are now looking at any number of database calls! The more friends and families that are connected in our app, the more expensive this query gets. Using a library like `dataloader`, we can reduce this operation to a maximum of three database lookups. Let's take a look at how to implement it to understand what is happening: - -```js -const DataLoader = require('dataloader'); - -// give this to ApolloServer's context -const UserModelLoader = new DataLoader(UserModel.findByIds); - -// in the User resolvers -const User = { - friends: (user, args, context) => { - return context.UserModelLoader.loadMany(user.friendIds); - }, - family: (user, args, context) => { - return context.UserModelLoader.loadMany(user.familyIds); - } -} -``` - -After the first data request returns with our current user's information, we execute the resolvers for `friends` and `family` within the same "tick" of the event loop, which is technical talk for "pretty much at the same time". DataLoader will delay making a data request (in this case the `UserModel.findByIds` call) long enough for it to capture the request to look up both friends and families at once! It will combine the two arrays of ids into one so our `SELECT * FROM users WHERE id IN ...` request will contain the ids of both friends **and** families! - -The friends and families request will return at the same time so when we select friends and families for all of previously returned users, the same batching can occur across all of the new users requests! So instead of potentially hundreds of data lookups, we can only perform 3 for a query like this! - - -## Scaling our app - -Horizontal scaling is a fantastic way to increase the amount of load that our servers can handle without having to purchase more expensive computing resources to handling it. Apollo Server can scale extremely well like this as long as a couple of concerns are handled: - -- Every request should ensure it has access to the required data source. If we are building on top of a HTTP endpoint this isn't a problem, but when using a database it is a good practice to verify our connection on each request. This helps to make our app more fault tolerant and easily scale up a new service which will connect as soon as requests start! -- Any state should be saved into a shared stateful datastore like redis. By sharing state, we can easily add more and more servers into our infrastructure without fear of losing any kind of state between scale up and scale down. diff --git a/docs/source/best-practices/schema-design.md b/docs/source/best-practices/schema-design.md deleted file mode 100644 index d586eaaa3fe..00000000000 --- a/docs/source/best-practices/schema-design.md +++ /dev/null @@ -1,371 +0,0 @@ ---- -title: Schema Design -description: The best way to fetch data, update it, and keep things running for a long time ---- - -GraphQL schemas are at their best when they are designed around the need of client applications, instead of the shape of how the data is stored. Often times teams will create schemas that are literal mappings on top of their collections or tables with CRUD like root fields. While this may be a fast way to get up and running, a strong long term GraphQL schema is built around the products usage. - -## Style conventions - -The GraphQL specification is flexible in the style that it dictates and doesn't impose specific naming guidelines. In order to facilitate development and continuity across GraphQL deployments, we suggest the following style conventions : - -- **Fields**: are recommended to be written in `camelCase`, since the majority of consumers will be client applications written in JavaScript. -- **Types**: should be `PascalCase`. -- **Enums**: should have their name in `PascalCase` and their values in `ALL_CAPS` to denote their special meaning. - -## Using interfaces - -Interfaces are a powerful way to build and use GraphQL schemas through the use of _abstract types_. Abstract types can't be used directly in schema, but can be used as building blocks for creating explicit types. - -Consider an example where different types of books share a common set of attributes, such as _text books_ and _coloring books_. A simple foundation for these books might be represented as the following `interface`: - -```graphql -interface Book { - title: String - author: Author -} -``` - -We won't be able to directly use this interface to query for a book, but we can use it to implement concrete types. Imagine a screen within an application which needs to display a feed of all books, without regard to their (more specific) type. To create such functionality, we could define the following: - -```graphql -type TextBook implements Book { - title: String - author: Author - classes: [Class] -} - -type ColoringBook implements Book { - title: String - author: Author - colors: [Color] -} - -type Query { - schoolBooks: [Book] -} -``` - -In this example, we've used the `Book` interface as the foundation for the `TextBook` and `ColoringBook` types. Then, a `schoolBooks` field simply expresses that it returns a list of books (i.e. `[Book]`). - -Implementing the book feed example is now simplified since we've removed the need to worry about what kind of `Book`s will be returned. A query against this schema, which could return _text books_ and _coloring_ books, might look like: - -```graphql -query GetBooks { - schoolBooks { - title - author - } -} -``` - -This is really helpful for feeds of common content, user role systems, and more! - -Furthermore, if we need to return fields which are only provided by either `TextBook`s or `ColoringBook`s (not both) we can request fragments from the abstract types in the query. Those fragments will be filled in only as appropriate; in the case of the example, only coloring books will be returned with `colors`, and only text books will have `classes`: - -```graphql -query GetBooks { - schoolBooks { - title - ... on TextBook { - classes { - name - } - } - ... on ColoringBook { - colors { - name - } - } - } -} -``` - -To see an interface in practice, check out this [example]() - -## A `Node` interface - -A so-called "`Node` interface" is an implementation of a generic interface, on which other types can be built on, which enables the ability to fetch other _types_ in a schema by only providing an `id`. This interface isn't provided automatically by GraphQL (not does it _have_ to be called `Node`), but we highly recommend schemas consider implementing one. - -To understand its value, we'll present an example with two collections: _authors_ and _posts_, though the usefulness of such an interface grows as more collections are introduced. As is common with most database collections, each of these collections have unique `id` columns which uniquely represent the individual documents within the collection. - -To implement a so-called "`Node` interface", we'll add a `Node` interface to the schema, as follows: - -```graphql -interface Node { - id: ID! -} -``` - -This `interface` declaration has the only field it will ever need: an `ID!` field, which is required to be non-null in all operations (as indicated by the `!`). - -To take advantage of this new interface, we can use as the underlying implementation for the other types that our schema will define. For our example, this means we'll use it to build `Post` and `Author` object types: - -```graphql -type Post implements Node { - id: ID! - title: String! - author: Author! -} - -type Author implements Node { - id: ID! - name: String! - posts: [Post] -} -``` - -By implementing the `Node` interface as the foundation for `Post` and `Author`, we know that anytime a client has obtained an `id` (from either type), we can send it back to the server and retrieve that exact piece of data back! - -

Global Ids

- -When using the `Node` interface, we will want to create schema unique `id` fields. The most common way to do this is to take the `id` from the datasource and join it with the type name where it is being exposed (i.e `Post:1`, `Author:1`). In doing so, even though the database `id` is the same for the first Post and first Author, the client can refetch each successfully! - -Global Ids are often encoded into a base64 string after joined together. This is for consistency but also to denote that the client shouldn't try to parse and use the information as the shape of the `id` may change over time with schema revisions, but the uniqueness of it will not. - -

Using the node interface

- -Now that we have the `Node` interface, we need a way to globally refetch any id that the client can send. To do this, we add a field called `node` to our `Query` which returns a `Node` abstract type: - -```graphql -type Query { - node(id: ID!): Node -} -``` - -Now our client can refetch any type they want to as long as they have an `id` value for it: - - -```graphql -query GetAuthor($authorId: ID!) { - node(id: $authorId) { - id - ... on Author { - name - posts { - id - title - } - } - } -} -``` - -Using the `Node` interface can remove a ton of unnecessary fields on the `Query` type, as well as solve common patterns like data fetching for routing. Say we had a route showing content our user has liked: `/favorites` and then we wanted to drill down into those likes: `/favorites/:id` to show more information. Instead of creating a route for each kind of liked content (i.e `/favories/authors/:id`, `/favorites/posts/:id`), we can use the `node` field to request any type of liked content: - -```graphql -query GetLikedContent($id: ID!){ - favorite: node(id: $id){ - id - ... on Author { - pageTitle: name - } - ... on Post { - pageTitle: title - } - } -} -``` - -Thanks to the `Node` interface and field aliasing, my response data is easily used by my UI no matter what my likes are: - -```json -[ - { id: "Author:1", pageTitle: "Sashko" }, - { id: "Post:1", pageTitle: "GraphQL is great!" } -] -``` - -To see this in practice, check out the following [example]() - -## Mutation responses - -Mutations are an incredibly powerful part of GraphQL as they can easily return both information about the data updating transaction, as well as the actual data that has changed very easily. One pattern that we recommend to make this consistent is to have a `MutationResponse` interface that can be easily implemented for any `Mutation` fields. The `MutationResponse` is designed to allow transactional information alongside returning valuable data to make client side updates automatic! The interface looks like this: - -```graphql -interface MutationResponse { - code: String! - success: Boolean! - message: String! -} -``` - -An implementing type would look like this: - -```graphql -type AddPostMutationResponse { - code: String! - success: Boolean! - message: String! - post: Post -} -``` - -Lets break this down by field: - -- **code** is a string representing a transactional value explaning details about the status of the data change. Think of this like HTTP status codes. -- **success** is a boolean telling the client if the update was successful. It is a coarse check that makes it easy for the client application to respond to failures -- **message** is a string that is meant to be a human readable description of the status of the transaction. It is intended to be used in the UI of the product -- **post** is added by the implementing type `AddPostMutationResponse` to return back the newly created post for the client to use! - -Following this pattern for mutations provides detailed information about the data that has changed and how the operation to change it went! Client developers can easily react to failures and fetch the information they need to update their local cache. - -

Organizing your schema

- -When schemas get large, we can start to define types in different files and import them to create the complete schema. We accomplish this by importing and exporting schema strings, combining them into arrays as necessary. - -```js -// comment.js -const typeDefs = gql` - type Comment { - id: Int! - message: String - author: String - } -`; - -export typeDefs; -``` - -```js -// post.js -const Comment = require('./comment'); - -const typeDefs = [` - type Post { - id: Int! - title: String - content: String - author: String - comments: [Comment] - } -`].concat(Comment.typeDefs); - -// we export Post and all types it depends on -// in order to make sure we don't forget to include -// a dependency -export typeDefs; -``` - -```js -// schema.js -const Post = require('./post.js'); - -const RootQuery = ` - type RootQuery { - post(id: Int!): Post - } -`; - -const SchemaDefinition = ` - schema { - query: RootQuery - } -`; - -const server = new ApolloServer({ - //we may destructure Post if supported by our Node version - typeDefs: [SchemaDefinition, RootQuery].concat(Post.typeDefs), - resolvers, -}); - -server.listen().then(({ url }) => { - console.log(`🚀 Server ready at ${url}`) -}); -``` - -

Extending types

- -The `extend` keyword provides the ability to add fields to existing types. Using `extend` is particularly useful in avoiding a large list of fields on root Queries and Mutations. - -```js -const barTypeDefs = ` -"Query can and must be defined once per schema to be extended" -type Query { - bars: [Bar] -} - -type Bar { - id: String -} -`; - -const fooTypeDefs = ` -type Foo { - id: String -} - -extend type Query { - foos: [Foo] -} -` - -const typeDefs = [barTypeDefs, fooTypeDefs] -``` - -

Sharing types

- -Schemas often contain circular dependencies or a shared type that has been hoisted to be referenced in separate files. When exporting array of schema strings with circular dependencies, the array can be wrapped in a function. The Apollo Server will only include each type definition once, even if it is imported multiple times by different types. Preventing deduplication of type definitions means that domains can be self contained and fully functional regardless of how they are combined. - -```js -// author.js -const Book = require('./book'); - -const Author = ` - type Author { - id: Int! - firstName: String - lastName: String - books: [Book] - } -`; - -// we export Author and all types it depends on -// in order to make sure we don't forget to include -// a dependency and we wrap it in a function -// to avoid strings deduplication -export const typeDefs = () => [Author].concat(Book.typeDefs); -``` - -```js -// book.js -const Author = require('./author'); - -const Book = ` - type Book { - title: String - author: Author - } -`; - -export const typeDefs = () => [Book].concat(Author.typeDefs); -``` - -```js -// schema.js -const Author = require('./author.js'); - -const RootQuery = ` - type RootQuery { - author(id: Int!): Author - } -`; - -const SchemaDefinition = ` - schema { - query: RootQuery - } -`; - -const server = new ApolloServer({ - //we may destructure Post if supported by our Node version - typeDefs: [SchemaDefinition, RootQuery].concat(Author.typeDefs), - resolvers, -}); - -server.listen().then(({ url }) => { - console.log(`🚀 Server ready at ${url}`) -}); -``` - - diff --git a/docs/source/best-practices/security.md b/docs/source/best-practices/security.md deleted file mode 100644 index 816fd00b772..00000000000 --- a/docs/source/best-practices/security.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: Security ---- - -Apollo Server is a safer way to build applications thanks to GraphQL's strong typing and the conversion of raw operations into a trusted syntax tree. By validating each part of an operation, GraphQL is mostly exempt from injection-attacks which are of concern in other data-driven applications. - - This guide will discuss additional security measures which further harden the excellent foundation which GraphQL is already built upon. While Apollo Server will enable some additional protections automatically, others require attention on the part of the developer. - -

Introspection in production

- -Introspection is a powerful tool to have enabled during development and allows developers to get real-time visibility of a GraphQL server's capabilities. - -In production, such insight might be less desireable unless the server is intended to be a "public" API. - -Therefore, Apollo Server introspection is automatically disabled when the `NODE_ENV` is set to `production` in order to reduce visibility into the API. - -Of course, no system should rely solely on so-called "security through obscurity" and this practice should be combined with other security techniques like open security and security by design. - -

Securing with SSL/TLS

- -You can secure all communication between the clients and your GraphQL server by using SSL/TLS. Apollo Server, with subscriptions, can be configured to use the `https` module with `apollo-server-express`. See [example server code](../essentials/server.html#ssl). - -Alternatively, you can use a reverse proxy solution like [NGINX](https://www.nginx.com/) or [Traefik](https://traefik.io/). An additional benefit of using Traefik is that you can use a free [Let's Encrypt SSL certificate](http://niels.nu/blog/2017/traefik-https-letsencrypt.html). - -

Injection prevention

- -As we build out our schema, it may be tempting to allow for shortcut arguments to creep in which have security risks. This most commonly happens on filters and on mutation inputs: - -```graphql -query OhNo { - users(filter: "id = 1;' sql injection goes here!") { - id - } -} - -mutation Dang { - updateUser(user: { firstName: "James", id: 1 }) { - success - } -} -``` - -In the first operation we are passing a filter that is a database filter directly as a string. This opens the door for SQL injection since the string is preserved from the client to the server. - -In the second operation we are passing an id value which may let an attacker update information for someone else! This often happens if generic Input Types are created for corresponding data sources: - -```graphql -# used for both creating and updating a user -input UserInput { - id: Int - firstName: String -} - -type Mutation { - createUser(user: UserInput): User - updateUser(user: UserInput): User -} -``` - -The fix for both of these attack vectors is to create more detailed arguments and let the validation step of Apollo Server filter out bad values as well as **never** pass raw values from a client into our datasource. - -

Denial-of-Service (DoS) Protection

- -Apollo Server is a Node.js application and standard precautions should be taken in order to avoid Denial-of-Service (DoS) attacks. - -Since GraphQL involves the traversal of a graph in which circular relationships of arbitrary depths might be accessible, some additional precautions can be taken to limit the risks of Complexity Denial-of-Service (CDoS) attacks, where a bad actor could craft expensive operations and lock up resources indefinitely. - -There are two common techniques to mitigate CDoS risks, and can be enabled together: - -1. **Operation white-listing** - - By hashing the potential operations a client might send (e.g. based on field names) and storing these "permitted" hashes on the server (or a shared cache), it becomes possible to check incoming operations against the permitted hashes and skip execution if the hash is not allowed. - - Since many consumers of non-public APIs have their operations statically defined within their source code, this technique is often sufficient and is best implemented as an automated deployment step. - -2. **Complexity limits** - - These can be used to limit the use of queries which, for example, request a list of books including the authors of each book, plus the books of those authors, and _their_ authors, and so on. By limiting operations to an application-defined depth of "_n_", these can be easily prevented. - - We suggest implementing complexity limits using community-provided packages like [graphql-depth-limit](https://github.com/stems/graphql-depth-limit) and [graphql-validation-complexity](https://github.com/4Catalyzer/graphql-validation-complexity). - -> For additional information on securing a GraphQL server deployment, check out [Securing your GraphQL API from malicious queries](https://blog.apollographql.com/securing-your-graphql-api-from-malicious-queries-16130a324a6b) by Spectrum co-founder, Max Stoiber. diff --git a/docs/source/best-practices/testing.md b/docs/source/best-practices/testing.md deleted file mode 100644 index 802f256695a..00000000000 --- a/docs/source/best-practices/testing.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Testing ---- - -Intro section about separation of concerns making GraphQL ideal for unit testing as well integration testing - -> (James) Add API for ApolloServer to make it easy to run integration tests against? Dependency injection anyone? - -## Unit testing resolvers - -## Integration testing operations - -## Using your schema to mock data for client testing diff --git a/docs/source/best-practices/versioning.md b/docs/source/best-practices/versioning.md deleted file mode 100644 index 7999c2d1329..00000000000 --- a/docs/source/best-practices/versioning.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Versioning -description: How to add and remove parts of your schema without breaking your clients ---- - -tl;dr don't. Use a tool like Engine (one day) to help you iterate - -## Why versioning isn't needed - -## Practical examples of field rollovers \ No newline at end of file diff --git a/docs/source/essentials/data.md b/docs/source/essentials/data.md index 56c60b2e7b3..358350891c4 100644 --- a/docs/source/essentials/data.md +++ b/docs/source/essentials/data.md @@ -16,23 +16,23 @@ const { gql } = require('apollo-server'); const { find, filter } = require('lodash'); const schema = gql` -type Book { - title: String - author: Author -} + type Book { + title: String + author: Author + } -type Author { - books: [Book] -} + type Author { + books: [Book] + } -type Query { - author: Author -} + type Query { + author: Author + } `; const resolvers = { Query: { - author(root, args, context, info) { + author(parent, args, context, info) { return find(authors, { id: args.id }); }, }, diff --git a/docs/source/essentials/schema.md b/docs/source/essentials/schema.md index f5609ff73e3..47a5c5be673 100644 --- a/docs/source/essentials/schema.md +++ b/docs/source/essentials/schema.md @@ -3,7 +3,7 @@ title: Understanding schema concepts sidebar_title: Writing a schema --- -> Estimated time: About 6 minutes. +> Estimated time: About 10 minutes. A GraphQL schema is at the center of any GraphQL server implementation and describes the functionality available to the clients which connect to it. @@ -242,6 +242,276 @@ Introspection is an **optional** feature, enabled by default during development, By allowing the consumer of the API to view the full possibilities of the API, developers can easily write new queries, or add new fields to existing ones. +One of the main aspects of GraphQL is that it allows you to describe the space of data available in your system with a strongly typed schema. While GraphQL makes it possible to evolve your API over time without breaking your clients, it's always easier if you think about some schema design decisions up front to reduce the amount of refactoring you need to do later. + +This article details some practices around schema design which will help you design a great GraphQL API to stand the test of time. + +

Style conventions

+ +The GraphQL specification is flexible and doesn't impose specific naming guidelines. However, in order to facilitate development and continuity across GraphQL deployments, it's useful to have a general set of conventions. We suggest the following: + +* **Fields** should be named in `camelCase`, since the majority of consumers will be client applications written in JavaScript, Java, Kotlin, or Swift, all of which recommend `camelCase` for variable names. +* **Types**: should be `PascalCase`, to match how classes are defined in the languages above. +* **Enums**: should have their type name in `PascalCase`, and their value names in `ALL_CAPS`, since they are similar to constants. + +If you use the conventions above, you won't need to have any extra logic in your clients to convert names to match the conventions of these languages. + +

Design for client needs

+ +GraphQL schemas are at their best when they are designed around the needs of client applications. When a team is building their first GraphQL schema, they might be tempted to create literal mappings on top of existing database collections or tables using CRUD-like root fields. While this literal database-to-schema mapping may be a fast way to get up and running, we strongly suggest avoiding it and instead building the schema based on how the GraphQL API will be used by the front-end. + +If a database has fields or relationships that the client doesn't yet need, don’t include them in the schema up front. Adding fields later is much easier than removing them, so add fields to your API as your clients need them rather than exposing all of the possible data up front. This is especially useful because GraphQL allows you to create associations between your data that don't exist in the underlying data, enabling you to move complex data manipulation logic out of your clients. + +For example, let's say you want to create a view that lists some events, their locations, and the weather at that location. In that case, you might want to do a query like this: + +```graphql +query EventList { + upcomingEvents { + name + date + location { + name + weather { + temperature + description + } + } + } +} +``` + +The desire to display this data could inform the design of a schema like the following: + +```graphql +type Query { + upcomingEvents: [Event] + # Other fields, etc +} + +type Event { + name: String + date: String + location: Location +} + +type Location { + name: String + weather: WeatherInfo +} + +type WeatherInfo { + temperature: Float + description: String +} +``` + +This doesn't necessarily need to match the data returned from a single REST endpoint or database. For example, if you have a REST endpoint exposing a list of events and their locations, but not weather information, you would just need to fetch the weather information from a second endpoint (or even a 3rd party API) in your resolvers. This way, you can design a schema that will allow your frontend to be as simple as possible, without limiting yourself to the exact shape of data that's in your underlying data sources. + +

Designing mutations

+ +The `Mutation` type is a core type in GraphQL which specializes in _modifying_ data, which contrasts the `Query` type used for _fetching_ data. + +Unlike REST, where the behavior can be more ad-hoc, the `Mutation` type is designed with the expectation that there will be a response object. This ensures that the client receives the most current data without a subsequent round-trip re-query. + +A mutation for updating the age of a `User` might look like this: + +```graphql +type Mutation { + updateUserAge(id: ID!, age: Int!): User +} + +type User { + id: ID! + name: String! + age: Int! +} +``` + +With this definition, the following mutation becomes possible: + +```graphql +mutation updateMyUser { + updateUserAge(id: 1, age: 25){ + id + age + name + } +} +``` + +Once executed by the server, the response returned to the client might be: + +```json +{ + "data": { + "updateUserAge": { + "id": "1", + "age": "25", + "name": "Jane Doe" + } + } +} +``` + +While it's not mandatory to return the object which has been updated, the inclusion of the updated information allows the client to confidently update its local state without performing additional requests. + +As with queries, it's best to design mutations with the client in mind and in response to a user's action. In simple cases, this might only result in changes to a single document, however in many cases there will be updates to multiple documents from different resources, for example, a `likePost` mutation might update the total likes for a user as well as their post. + +In order to provide a consistent shape of response data, we recommend adopting a pattern which returns a standardized response format which supports returning any number of documents from each resource which was modified. We'll outline a recommended pattern for this in the next section. + +

Responses

+ +GraphQL mutations can return any information the developer wishes, but designing mutation responses in a consistent and robust structure makes them more approachable by humans and less complicated to traverse in client code. There are two guiding principles which we have combined into our suggested mutation response structure. + +First, while mutations might only modify a single resource type, they often need to touch several at a time. It makes sense for this to happen in a single round-trip to the server and this is one of the strengths of GraphQL! When different resources are modified, the client code can benefit from having updated fields returned from each type and the response format should support that. + +Secondly, mutations have a higher chance of causing errors than queries since they are modifying data. If only a portion of a mutation update succeeds, whether that is a partial update to a single document's fields or a failed update to an entire document, it's important to convey that information to the client to avoid stale local state on the client. + +A common way to handle errors during a mutation is to simply `throw` an error. While that's fine, throwing an error in a resolver will return an error for the entire operation to the caller and prevent a more meaningful response. Consider the following mutation example, which tries to update a user's `name` and `age`: + +```graphql +mutation updateUser { + updateUser(id: 1, user: { age: -1, name: "Foo Bar" }){ + name + age + } +} +``` + +With validation in place, this mutation might cause an error since the `age` is a negative value. While it’s possible that the entire operation should be stopped, there’s an opportunity to partially update the user’s record with the new `name` and return the updated record with the `age` left untouched. + +Fortunately, the powerful structure of GraphQL mutations accommodates this use case and can return transactional information about the update alongside the records which have been changed which enables client-side updates to occur automatically. + +In order to provide consistency across a schema, we suggest introducing a `MutationResponse` interface which can be implemented on every mutation response in a schema and enables transactional information to be returned in addition to the normal mutation response object. + +A `MutationResponse` interface would look like this: + +```graphql +interface MutationResponse { + code: String! + success: Boolean! + message: String! +} +``` + +An implementing type would look like this: + +```graphql +type UpdateUserMutationResponse implements MutationResponse { + code: String! + success: Boolean! + message: String! + user: User +} +``` + +Calling a mutation that returns that `UpdateUserMutationResponse` type would result in a response that looks something like this: + +```json +{ + "data": { + "updateUser": { + "code": "200", + "success": true, + "message": "User was successfully updated", + "user": { + "id": "1", + "name": "Jane Doe", + "age": 35 + } + } + } +} +``` + +Let’s break this down, field by field: + +* `code` is a string representing a transactional value explaining details about the status of the data change. Think of this like an HTTP status code. +* `success` is a boolean indicating whether the update was successful or not. This allows a coarse check by the client to know if there were failures. +* `message` is a string that is meant to be a human-readable description of the status of the transaction. It is intended to be used in the UI of the product. +* `user` is added by the implementing type `UpdateUserMutationResponse` to return back the newly created user for the client to use! + +For mutations which have touched multiple types, this same structure can be used to return updated objects from each one. For example, a `likePost` type, which could affect a user's "reputation" and also update the post itself, might implement `MutationResponse` in the following manner: + +```graphql +type LikePostMutationResponse implements MutationResponse { + code: String! + success: Boolean! + message: String! + post: Post + user: User +} +``` + +In this response type, we've provided the expectation that both the `user` and the `post` would be returned and an actual response to a `likePost` mutation could be: + +```json +{ + "data": { + "likePost": { + "code": "200", + "success": true, + "message": "Thanks!", + "post": { + "likes": 5040 + }, + "user": { + "reputation": 11 + } + } + } +} +``` + +Following this pattern for mutations provides detailed information about the data that has changed and feedback on whether the operation was successful or not. Armed with this information, developers can easily react to failures within the client + +

Input types

+ +Input types are a special type in GraphQL which allows an object to be passed as an argument to both queries and mutations and is helpful when simple scalar types aren't sufficient. + +This allows arguments to be structured in an more manageable way, similar to how switching to an `options` argument might be appreciated when `function` arguments become too iterative. + +For example, consider this mutation which creates a post along with its accompanying media URLs (e.g. images): + +```graphql +type Mutation { + createPost(title: String, body: String, mediaUrls: [String]): Post +} +``` + +This could be easier to digest, and the arguments would be easier to re-use within the mutation, by using an `input` type with the relevant fields. + +An input type is defined like a normal object type but using the `input` keyword. To introduce an `input` type for this example, we'd do: + +```graphql +type Mutation { + createPost(post: PostAndMediaInput): Post +} + +input PostAndMediaInput { + title: String + body: String + mediaUrls: [String] +} +``` + +Not only does this facilitate passing the `PostAndMediaInput` around within the schema, it also provides a basis for annotating fields with descriptions which are automatically exposed by GraphQL-enabled tools: + +```graphql +input PostAndMediaInput { + "A main title for the post" + title: String + "The textual body of the post." + body: String + "A list of URLs to render in the post." + mediaUrls: [String] +} +``` + +Input types can also be used when different operations require the exact same information, though we urge caution on over-using this technique since changes to `input` types are breaking changes for all operations which utilize them. + +Additionally, while it is possible to reuse an `input` type between a query and mutation which target the same resource, it's often best to avoid this since in many cases certain null fields might be tolerated for one but not the other. +

Next steps

At this point, we hope to have explained the basic information necessary to understand a GraphQL schema. diff --git a/docs/source/features/authentication.md b/docs/source/features/authentication.md new file mode 100644 index 00000000000..68b484c42fa --- /dev/null +++ b/docs/source/features/authentication.md @@ -0,0 +1,257 @@ +--- +title: Authentication +description: How to authorize users and control permissions in your GraphQL API +--- + +At some point (probably pretty early on) when building a GraphQL endpoint, you’ll probably have to face the question of how to control who can see and interact with the data in your API. + +**Authentication** is determining whether a user is logged in or not, and subsequently figuring out _which_ user someone is. **Authorization** is then deciding what the user has permission to do or see. + +This article will primarily be focusing on how to set up authorization for your schema once you know about the user trying to make the request, but we’ll go through one example of authentication just to get some _context_ for what we’re doing. + +

Putting user info on the context

+ +Before we get into figuring out user permissions, we have to figure out how to recognize a user first. From HTTP headers, to JSON web tokens, there are a number of ways to handle authentication of users, but once you have your user, controlling access looks pretty similar. + +We’ll be using a login token in an HTTP authorization header as an example. + +```js +// using apollo-server 2.x +const { ApolloServer } = require('apollo-server'); + +const server = new ApolloServer({ + typeDefs, + resolvers, + context: ({ req }) => { + // get the user token from the headers + const token = req.headers.authorization || ''; + + // try to retrieve a user with the token + const user = getUser(token); + + // add the user to the context + return { user }; + }, +}); + +server.listen().then(({ url }) => { + console.log(`🚀 Server ready at ${url}`) +}); +``` + +So what’s happening here, exactly? This block of code is setting up a new GraphQL server, using Apollo Server 2.0. This new version of Apollo Server simplifies the API for creating new servers, and has some more intelligent defaults. You can read more about it [here](https://blog.apollographql.com/apollo-server-2-0-30c9bbb4ab5e)! + +In this constructor, we pass type definitions and resolvers to the constructor as well as a function to build our `context` object. The `context` object is one that gets passed to every single resolver at every level, so we can access it anywhere in our schema code. It’s where we can store things like data fetchers, database connections, and (conveniently) information about the user making the request. + +Since the context is generated again with every new request, we don’t have to worry about cleaning up user data at the end of execution. + +The context function here looks at the request headers, pulls off the header named `authorization`, and stores it to a variable. It then calls a `getUser` function with that token, and expects a user to be returned if the token is valid. After that, it returns a context object containing the (potential) user, for all of our resolvers to use. + +The specifics of retrieving a user will look different for each method of authentication, but the final part will look about the same every time. The authorization needs for your schema may require you to put nothing more than `{ loggedIn: true }` into context, but also may require an id or roles, like `{ user: { id: 12345, roles: ['user', 'admin'] } }`. + +In the next section, we’ll look at ways to use the user information we now have to secure your schema. + +

Schema authorization

+ +Once we have information about the user making a request, the most basic thing we can do is deny them the ability to run a query at all based on their roles. This is an all-or-nothing approach to authorization that we’ll start with because it’s the simplest. If you choose to block users like this, no fields will be publicly queryable. + +We would want to do this only on very restrictive environments where there is no public access to the schema or any fields, like an internal tool or maybe an independent micro service that we don’t want exposed to the public. + +To do this kind of authorization, we can just modify the context function. + +```js +context: ({ req }) => { + // get the user token from the headers + const token = req.headers.authorization || ''; + + // try to retrieve a user with the token + const user = getUser(token); + + // optionally block the user + // we could also check user roles/permissions here + if (!user) throw new AuthorizationError('you must be logged in'); + + // add the user to the context + return { user }; +}, +``` + +The only difference from the basic context function is the check for the user. If no user exists or if lookup fails, the function throws an error, and none of the query gets executed. + +

Authorization in resolvers

+ +Schema authorization may be useful in specific instances, but more commonly, GraphQL schemas will have some fields that need to be public. An example of this would be a news site that wants to show article previews to anyone, but restrict the full body of articles to paying customers only. + +Luckily, GraphQL offers very granular control over data. In GraphQL servers, individual field resolvers have the ability to check user roles and make decisions as to what to return for each user. In the previous sections, we saw how to attach user information to the context object. In the rest of the article, we’ll discuss how to use that context object. + +For our first example, let’s look at a resolver that’s only accessible with a valid user: + +```js +users: (parent, args, context) => { + // In this case, we'll pretend there is no data when + // we're not logged in. Another option would be to + // throw an error. + if (!context.user) return []; + + return ['bob', 'jake']; +} +``` + +This example is a field in our schema named `users` that returns a list of users’ names. The `if` check on the first line of the function looks at the `context` generated from our request, checks for a `user` object, and if one doesn’t exist, returns `null` for the whole field. + +One choice to make when building out our resolvers is what an unauthorized field should return. In some use cases, returning `null` here is perfectly valid. Alternatives to this would be to return an empty array, `[]` or to throw an error, telling the client that they’re not allowed to access that field. For the sake of simplicity, we just returned `[]` in this example. + +Now let’s expand that example a little further, and only allow users with an `admin` role to look at our user list. After all, we probably don’t want just anyone to have access to all our users. + +```js +users: (parent, args, context) => { + if (!context.user || !context.user.roles.includes('admin')) return null; + return context.models.User.getAll(); +} +``` + +This example looks almost the same as the previous one, with one addition: it expects the `roles` array on a user to include an `admin` role. Otherwise, it returns `null`. The benefit of doing authorization like this is that we can short-circuit our resolvers and not even call lookup functions when we don’t have permission to use them, limiting the possible errors that could expose sensitive data. + +Because our resolvers have access to everything in the context, an important question we need to ask is how much information we want in the context. For example, we don’t need the user’s id, name, or age (at least not yet). It’s best to keep things out of the context until they’re needed, since they’re easy to add back in later. + +

Authorization in data models

+ +As our server gets more complex, there will probably be multiple places in the schema that need to fetch the same kind of data. In our last example, you may have noticed the return array was replaced with a call to `context.models.User.getAll()`. + +Since the very beginning, [we’ve recommended](https://www.apollographql.com/docs/graphql-tools/connectors.html) moving the actual data fetching and transformation logic from resolvers to centralized Model objects that each represent a concept from your application: User, Post, etc. This allows you to make your resolvers a thin routing layer, and put all of your business logic in one place. + +For example, a model file for `User` would include all the logic for operating on users, and may look something like… + +```js +export const User = { + getAll: () => { /* fetching/transform logic for all users */ }, + getById: (id) => { /* fetching/transform logic for a single user */ }, + getByGroupId: (id) => { /* fetching/transform logic for a group of users */ }, +}; +``` + +In the following example, our schema has multiple ways to request a single user… + +```js +type Query { + user (id: ID!): User + article (id: ID!): Article +} + +type Article { + author: User +} + +type User { + id: ID! + name: String! +} +``` + +Rather than having the same fetching logic for a single user in two separate places, it usually makes sense to move that logic to the model file. You may have guessed, with all this talk of model files in an authorization article, that authorization is another great thing to delegate to the model, just like data fetching. You would be right. + +**Delegating authorization to models** + +You may have noticed that our models also exist on the context, alongside the user object we added earlier. We can add the models to the context in exactly the same way as we did the user. + +```js +context: ({ req }) => { + // get the user token from the headers + const token = req.headers.authentication || ''; + + // try to retrieve a user with the token + const user = getUser(token); + + // optionally block the user + // we could also check user roles/permissions here + if (!user) throw new AuthorizationError('you must be logged in to query this schema'); + + // add the user to the context + return { + user, + models: { + User: generateUserModel({ user }), + ... + } + }; +}, +``` + +Starting to generate our models with a function requires a small refactor, that would leave our User model looking something like this: + +```js +export const generateUserModel = ({ user }) => ({ + getAll: () => { /* fetching/transform logic for all users */ }, + getById: (id) => { /* fetching/transform logic for a single user */ }, + getByGroupId: (id) => { /* fetching/transform logic for a group of users */ }, +}); +``` + +Now any model method in `User` has access to the same `user` information that resolvers already had, allowing us to refactor the `getAll` function to do the permissions check directly rather than having to put it in the resolver: + +```js +getAll: () => { + if(!user || !user.roles.includes('admin') return null; + return fetch('http://myurl.com/users'); +} +``` + +

Authorization via Custom Directives

+ +Another way to go about authorization is via GraphQL Schema Directives. A directive is an identifier preceded by a `@` character, optionally followed by a list of named arguments, which can appear after almost any form of syntax in the GraphQL query or schema languages. + +Check out this example of an authorization directive: + +```js +const typeDefs = ` + directive @auth(requires: Role = ADMIN) on OBJECT | FIELD_DEFINITION + + enum Role { + ADMIN + REVIEWER + USER + } + + type User @auth(requires: USER) { + name: String + banned: Boolean @auth(requires: ADMIN) + canPost: Boolean @auth(requires: REVIEWER) + } +` +``` + +The `@auth` directive can be called directly on the type, or on the fields if you want to limit access to specific fields as shown in the example above. The logic behind authorization is hidden away in the directive implementation. + +One way of implementing the `@auth` directive is via the [SchemaDirectiveVisitor](https://www.apollographql.com/docs/graphql-tools/schema-directives.html) class from [graphql-tools](https://github.com/apollographql/graphql-tools). Ben Newman covered creating a sample `@deprecated` and `@rest` directive in this [excellent article](https://blog.apollographql.com/reusable-graphql-schema-directives-131fb3a177d1). You can draw inspiration from these examples. + + +

Authorization outside of GraphQL

+ +If you’re using a REST API that has built-in authorization, like with an HTTP header, you have one more option. Rather than doing any authentication or authorization work in the GraphQL layer (in resolvers/models), it’s possible to simply pass through the headers or cookies to your REST endpoint and let it do the work. + +Here’s an example: + +```js +// src/server.js +context: ({ req }) => { + // pass the request information through to the model + return { + user, + models: { + User: generateUserModel({ req }), + ... + } + }; +}, +``` + +```js +// src/models/user.js +export const generateUserModel = ({ req }) => ({ + getAll: () => { + return fetch('http://myurl.com/users', { headers: req.headers }); + }, +}); +``` + +If your REST endpoint is already backed by some form of authorization, this cuts down a lot of the logic that needs to get built in the GraphQL layer. This can be a great option when building a GraphQL API over an existing REST API that has everything you need already built in. diff --git a/docs/source/features/creating-directives.md b/docs/source/features/creating-directives.md index 2979fdf577f..6e390f74e2f 100644 --- a/docs/source/features/creating-directives.md +++ b/docs/source/features/creating-directives.md @@ -53,10 +53,11 @@ In order to apply this implementation to a schema that contains `@deprecated` di const { ApolloServer, gql } = require("apollo-server"); const typeDefs = gql` -type ExampleType { - newField: String - oldField: String @deprecated(reason: "Use \`newField\`.") -}`; + type ExampleType { + newField: String + oldField: String @deprecated(reason: "Use \`newField\`.") + } +`; const server = new ApolloServer({ typeDefs, @@ -96,11 +97,12 @@ const { ApolloServer, gql, SchemaDirectiveVisitor } = require("apollo-server"); const { defaultFieldResolver } = require("graphql"); const typeDefs = gql` -directive @upper on FIELD_DEFINITION + directive @upper on FIELD_DEFINITION -type Query { - hello: String @upper -}`; + type Query { + hello: String @upper + } +`; class UpperCaseDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { @@ -138,11 +140,12 @@ Suppose you've defined an object type that corresponds to a [REST](https://en.wi const { ApolloServer, gql, SchemaDirectiveVisitor } = require("apollo-server"); const typeDefs = gql` -directive @rest(url: String) on FIELD_DEFINITION + directive @rest(url: String) on FIELD_DEFINITION -type Query { - people: [Person] @rest(url: "/api/v1/people") -}`; + type Query { + people: [Person] @rest(url: "/api/v1/people") + } +`; class RestDirective extends SchemaDirectiveVisitor { public visitFieldDefinition(field) { @@ -173,13 +176,14 @@ Suppose your resolver returns a `Date` object but you want to return a formatted const { ApolloServer, gql, SchemaDirectiveVisitor } = require("apollo-server"); const typeDefs = gql` -directive @date(format: String) on FIELD_DEFINITION + directive @date(format: String) on FIELD_DEFINITION -scalar Date + scalar Date -type Post { - published: Date @date(format: "mmmm d, yyyy") -}`; + type Post { + published: Date @date(format: "mmmm d, yyyy") + } +`; class DateFormatDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { @@ -214,15 +218,16 @@ const formatDate = require("dateformat"); const { defaultFieldResolver, GraphQLString } = require("graphql"); const typeDefs = gql` -directive @date( - defaultFormat: String = "mmmm d, yyyy" -) on FIELD_DEFINITION + directive @date( + defaultFormat: String = "mmmm d, yyyy" + ) on FIELD_DEFINITION -scalar Date + scalar Date -type Query { - today: Date @date -}`; + type Query { + today: Date @date + } +`; class FormattableDateDirective extends SchemaDirectiveVisitor { public visitFieldDefinition(field) { @@ -292,11 +297,12 @@ Here's how you might make sure `translate` is used to localize the `greeting` fi const { ApolloServer, gql, SchemaDirectiveVisitor } = require("apollo-server"); const typeDefs = gql` -directive @intl on FIELD_DEFINITION + directive @intl on FIELD_DEFINITION -type Query { - greeting: String @intl -}`; + type Query { + greeting: String @intl + } +`; class IntlDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field, details) { @@ -519,27 +525,28 @@ const { GraphQLID } = require("graphql"); const { createHash } = require("crypto"); const typeDefs = gql` -directive @uniqueID( - # The name of the new ID field, "uid" by default: - name: String = "uid" - - # Which fields to include in the new ID: - from: [String] = ["id"] -) on OBJECT - -# Since this type just uses the default values of name and from, -# we don't have to pass any arguments to the directive: -type Location @uniqueID { - id: Int - address: String -} + directive @uniqueID( + # The name of the new ID field, "uid" by default: + name: String = "uid" + + # Which fields to include in the new ID: + from: [String] = ["id"] + ) on OBJECT + + # Since this type just uses the default values of name and from, + # we don't have to pass any arguments to the directive: + type Location @uniqueID { + id: Int + address: String + } -# This type uses both the person's name and the personID field, -# in addition to the "Person" type name, to construct the ID: -type Person @uniqueID(from: ["name", "personID"]) { - personID: Int - name: String -}`; + # This type uses both the person's name and the personID field, + # in addition to the "Person" type name, to construct the ID: + type Person @uniqueID(from: ["name", "personID"]) { + personID: Int + name: String + } +`; class UniqueIdDirective extends SchemaDirectiveVisitor { visitObject(type) { diff --git a/docs/source/features/directives.md b/docs/source/features/directives.md index 53b90b9d77c..a040d29e9be 100644 --- a/docs/source/features/directives.md +++ b/docs/source/features/directives.md @@ -60,7 +60,7 @@ const typeDefs = gql` // Provide resolver functions for your schema fields const resolvers = { Query: { - hello: (root, args, context) => { + hello: (parent, args, context) => { return 'Hello world!'; }, }, diff --git a/docs/source/features/errors.md b/docs/source/features/errors.md index 4b25c14c273..74178759c73 100644 --- a/docs/source/features/errors.md +++ b/docs/source/features/errors.md @@ -38,7 +38,7 @@ The response will return: ![Screenshot demonstrating an error stacktrace and additional](../images/features/error-stacktrace.png) -> To disable stacktraces for production, pass `debug: false` to the Apollo server constructor or set the `NODE_ENV` environment variable to 'production' or 'test'. Note that this will make the stacktrace unavailable to your application. If you want to log the stacktrace, but not send it in the response to the client, see [Masking and logging errors](#masking-and-logging-errors) below. +> To disable stacktraces for production, pass `debug: false` to the Apollo server constructor or set the `NODE_ENV` environment variable to 'production' or 'test'. Note that this will make the stacktrace unavailable to your application. If you want to log the stacktrace, but not send it in the response to the client, see [Masking and logging errors](#Masking-and-logging-errors) below. ## Codes diff --git a/docs/source/features/metrics.md b/docs/source/features/metrics.md index b4e8ae161dd..666c4be47e4 100644 --- a/docs/source/features/metrics.md +++ b/docs/source/features/metrics.md @@ -14,7 +14,7 @@ To set up Apollo Server with Engine, [click here](https://engine.apollographql.c ```js line=6-8 const { ApolloServer } = require("apollo-server"); -const server = new ApolloSever({ +const server = new ApolloServer({ typeDefs, resolvers, engine: { @@ -30,17 +30,59 @@ server.listen().then(({ url }) => { The API key can also be set with the `ENGINE_API_KEY` environment variable. Setting an environment variable can be done in commandline as seen below or with the [dotenv npm package](https://www.npmjs.com/package/dotenv). ```bash -#Replace YOUR_API_KEY with the api key for you service in the Engine UI +# Replace YOUR_API_KEY with the API key provided within Apollo Engine. ENGINE_API_KEY=YOUR_API_KEY node start-server.js ``` +### Client awareness + +Apollo Engine accepts metrics annotated with client information. The Engine UI +is then able to filter metrics and usage patterns by these names and versions. To provide metrics to the Engine, pass a `generateClientInfo` function into the `ApolloServer` constructor, like so: + +```js line=8-23 +const { ApolloServer } = require("apollo-server"); + +const server = new ApolloServer({ + typeDefs, + resolvers, + engine: { + apiKey: "YOUR API KEY HERE", + generateClientInfo: ({ + request + }) => { + const headers = request.http & request.http.headers; + if(headers) { + return { + clientName: headers['apollo-client-name'], + clientVersion: headers['apollo-client-version'], + }; + } else { + return { + clientName: "Unknown Client", + clientVersion: "Unversioned", + }; + } + }, + } +}); + +server.listen().then(({ url }) => { + console.log(`🚀 Server ready at ${url}`); +}); +``` + +> Note: the default implementation looks at `clientInfo` field in the +> `extensions` of the GraphQL request + ## Logging Apollo Server provides two ways to log a server: per input, response, and errors or periodically throughout a request's lifecycle. Treating the GraphQL execution as a black box by logging the inputs and outputs of the system allows developers to diagnose issues quickly without being mired by lower level logs. Once a problem has been found at a high level, the lower level logs enable accurate tracing of how a request was handled. -### High Level Logging +### High-level logging + +Apollo Server allows `formatError` and `formatResponse` configuration options which can be defined as callback-functions which receive `error` or `response` arguments respectively. -To log, Apollo Server provides: `formatError` and `formatResponse`. This example uses `console.log` to record the information, servers can use other more sophisticated tools. +For the sake of simplicity, these examples use `console.log` to output error and debugging information though a more complete example might utilize existing logging or error-reporting facilities. ```js const server = new ApolloServer({ @@ -61,7 +103,7 @@ server.listen().then(({ url }) => { }); ``` -### Granular Logs +### Granular logs For more advanced cases, Apollo Server provides an experimental api that accepts an array of `graphql-extensions` to the `extensions` field. These extensions receive a variety of lifecycle calls for each phase of a GraphQL request and can keep state, such as the request headers. diff --git a/docs/source/features/mocking.md b/docs/source/features/mocking.md index 18a28451784..bcc0b7a781e 100644 --- a/docs/source/features/mocking.md +++ b/docs/source/features/mocking.md @@ -15,9 +15,9 @@ This example demonstrates mocking a GraphQL schema with just one line of code, u const { ApolloServer, gql } = require('apollo-server'); const typeDefs = gql` -type Query { - hello: String -} + type Query { + hello: String + } `; const server = new ApolloServer({ @@ -32,7 +32,9 @@ server.listen().then(({ url }) => { > Note: If `typeDefs` has custom scalar types, `resolvers` must still contain the `serialize`, `parseValue`, and `parseLiteral` functions -Mocking logic simply looks at the type definitions and returns a string where a string is expected, a number for a number, etc. This provides the right shape of result. For more sophisticated testing, mocks can be customized to a particular data model. +Mocking logic simply looks at the type definitions and returns a string where a string is expected, a number for a number, etc. This provides the right shape of result. By default, when using mocks, any existing resolvers are ignored. See the ["Using existing resolvers with mocks"](#existing-resolvers) section below for more info on how to change this behavior. + +For more sophisticated testing, mocks can be customized to a particular data model. ## Customizing mocks @@ -42,10 +44,10 @@ In addition to a boolean, `mocks` can be an object that describes custom mocking const { ApolloServer, gql } = require('apollo-server'); const typeDefs = gql` -type Query { - hello: String - resolved: String -} + type Query { + hello: String + resolved: String + } `; const resolvers = { @@ -136,25 +138,27 @@ The mock functions on fields are actually just GraphQL resolvers, which can use const mocks = { Person: () => ({ // the number of friends in the list now depends on numPages - paginatedFriends: (root, args, context, info) => new MockList(args.numPages * PAGE_SIZE), + paginatedFriends: (parent, args, context, info) => new MockList(args.numPages * PAGE_SIZE), }), }; ``` For some more background and flavor on this approach, read the ["Mocking your server with one line of code"](https://medium.com/apollo-stack/mocking-your-server-with-just-one-line-of-code-692feda6e9cd) article on the Apollo blog. -### Using existing resolvers with mocks +

Using existing resolvers with mocks

+ +The default behavior for mocks is to overwrite the resolvers already present in the schema. To keep the existing resolvers, set the `mockEntireSchema` option to false. -The default behavior for mocks is to overwrite the resolvers already present in the schema. To keep the existing resolvers, set the `mockEntireSchema` field to false. +> Note: mocking resolvers will not work if the `mocks` option is `false`, even if `mockEntireSchema` is true. ```js line=26 const { ApolloServer, gql } = require('apollo-server'); const typeDefs = gql` -type Query { - hello: String - resolved: String -} + type Query { + hello: String + resolved: String + } `; const resolvers = { @@ -208,4 +212,4 @@ server.listen().then(({ url }) => { ## API -Under the hood, Apollo Server uses a library for building GraphQL servers, called `graphql-tools`. The mocking functionality is provided by the function [`addMockFunctionsToSchema`](../api/graphql-tools.html#addMockFunctionsToSchema). The `mocks` object is passed directly to the function and `preserveResolvers` is always true. [`MockList`](../api/graphql-tools.html#MockList) is exported directly from the `graphql-tools` library. +Under the hood, Apollo Server uses a library for building GraphQL servers, called `graphql-tools`. The mocking functionality is provided by the function [`addMockFunctionsToSchema`](../api/graphql-tools.html#addMockFunctionsToSchema). The `mocks` object is passed directly to the function, and `preserveResolvers` is the inverse of `mockEntireSchema`. [`MockList`](../api/graphql-tools.html#MockList) is exported directly from the `graphql-tools` library. diff --git a/docs/source/features/scalars-enums.md b/docs/source/features/scalars-enums.md index a2dcc2553f0..35701a45733 100644 --- a/docs/source/features/scalars-enums.md +++ b/docs/source/features/scalars-enums.md @@ -36,17 +36,15 @@ const { ApolloServer, gql } = require('apollo-server'); const GraphQLJSON = require('graphql-type-json'); const schemaString = gql` + scalar JSON -scalar JSON - -type Foo { - aField: JSON -} - -type Query { - foo: Foo -} + type Foo { + aField: JSON + } + type Query { + foo: Foo + } `; const resolveFunctions = { @@ -92,24 +90,22 @@ const myCustomScalarType = new GraphQLScalarType({ }); const schemaString = gql` + scalar MyCustomScalar -scalar MyCustomScalar - -type Foo { - aField: MyCustomScalar -} - -type Query { - foo: Foo -} + type Foo { + aField: MyCustomScalar + } + type Query { + foo: Foo + } `; const resolverFunctions = { MyCustomScalar: myCustomScalarType }; -const server = new ApolloServer({ typeDefs: schemaString, resolvers: resolveFunctions }); +const server = new ApolloServer({ typeDefs: schemaString, resolvers: resolverFunctions }); server.listen().then(({ url }) => { console.log(`🚀 Server ready at ${url}`) @@ -127,11 +123,12 @@ The goal is to define a `Date` data type for returning `Date` values from the da The following is the implementation of the `Date` data type. First, the schema: ```js -const typeDefs = gql`scalar Date +const typeDefs = gql` + scalar Date -type MyType { - created: Date -} + type MyType { + created: Date + } ` ``` @@ -172,11 +169,12 @@ server.listen().then(({ url }) => { In this example, we follow the [official GraphQL documentation](http://graphql.org/docs/api-reference-type-system/) for the scalar datatype, which demonstrates how to validate a database field that should only contain odd numbers in GraphQL. First, the schema: ```js -const typeDefs = gql`scalar Odd +const typeDefs = gql` + scalar Odd -type MyType { + type MyType { oddValue: Odd -} + } ` ``` @@ -279,7 +277,7 @@ const typeDefs = gql` const resolvers = { Query: { favoriteColor: () => 'RED', - avatar: (root, args) => { + avatar: (parent, args) => { // args.borderColor is 'RED', 'GREEN', or 'BLUE' }, } @@ -294,7 +292,7 @@ server.listen().then(({ url }) => {

Internal values

-Sometimes a backend forces a different value for an enum internally than in the public API. In this exmple the API contains `RED`, however in resolvers we use `#f00` instead. The `resolvers` argument to `ApolloServer` allows the addition custom values to enums that only exist internally: +Sometimes a backend forces a different value for an enum internally than in the public API. In this example the API contains `RED`, however in resolvers we use `#f00` instead. The `resolvers` argument to `ApolloServer` allows the addition of custom values to enums that only exist internally: ```js const resolvers = { @@ -317,7 +315,7 @@ const resolvers = { }, Query: { favoriteColor: () => '#f00', - avatar: (root, args) => { + avatar: (parent, args) => { // args.borderColor is '#f00', '#0f0', or '#00f' }, } diff --git a/docs/source/features/subscriptions.md b/docs/source/features/subscriptions.md index 1bf9600b041..aeefac785b6 100644 --- a/docs/source/features/subscriptions.md +++ b/docs/source/features/subscriptions.md @@ -21,19 +21,22 @@ Subscriptions are another root level type, similar to Query and Mutation. To sta ```js line=2-4 const typeDefs = gql` -type Subscription { - postAdded: Post -} -type Query { - posts: [Post] -} -type Mutation { - addPost(author: String, comment: String): Post -} -type Post { - author: String - comment: String -} + type Subscription { + postAdded: Post + } + + type Query { + posts: [Post] + } + + type Mutation { + addPost(author: String, comment: String): Post + } + + type Post { + author: String + comment: String + } ` ``` @@ -73,7 +76,7 @@ const server = new ApolloServer({ context: async ({ req, connection }) => { if (connection) { // check connection for metadata - return {}; + return connection.context; } else { // check from req const token = req.headers.authorization || ""; @@ -84,6 +87,8 @@ const server = new ApolloServer({ }); ``` +> `connection` contains various metadata, found [here](https://github.com/apollographql/subscriptions-transport-ws/blob/88970eaf6d2e3f68f98696de00631acf4062c088/src/server.ts#L312-L321). + As you can see Apollo Server 2.0 allows realtime data without invasive changes to existing code. For a full working example please have a look to [this repo](https://github.com/daniele-zurico/apollo2-subscriptions-how-to) provided by [Daniele Zurico](https://github.com/daniele-zurico/apollo2-subscriptions-how-to) diff --git a/docs/source/features/testing.md b/docs/source/features/testing.md new file mode 100644 index 00000000000..e5ea248ca65 --- /dev/null +++ b/docs/source/features/testing.md @@ -0,0 +1,64 @@ +--- +title: Integration testing +description: Utilities for testing Apollo Server +--- + +Testing `apollo-server` can be done in many ways. The `apollo-server-testing` package provides tooling to make testing easier and accessible to users of all of the `apollo-server` integrations. + +## `createTestClient` + +Integration testing a GraphQL server means testing many things. `apollo-server` has a request pipeline that can support many plugins that can affect the way an operation is executed. `createTestClient` provides a single hook to run operations through the request pipeline, enabling the most thorough tests possible without starting up an HTTP server. + +```javascript +const { createTestClient } = require('apollo-server-testing'); + +const { query, mutate } = createTestClient(server); + +query({ + query: GET_USER, + variables: { id: 1 } +}); + +mutate({ + mutation: UPDATE_USER, + variables: { id: 1, email: 'nancy@foo.co' } +}); +``` + +When passed an instance of the `ApolloServer` class, `createTestClient` returns a `query` and `mutate` function that can be used to run operations against the server instance. Currently, queries and mutations are the only operation types supported by `createTestClient`. + +```javascript +const { createTestClient } = require('apollo-server-testing'); + +it('fetches single launch', async () => { + const userAPI = new UserAPI({ store }); + const launchAPI = new LaunchAPI(); + + // create a test server to test against, using our production typeDefs, + // resolvers, and dataSources. + const server = new ApolloServer({ + typeDefs, + resolvers, + dataSources: () => ({ userAPI, launchAPI }), + context: () => ({ user: { id: 1, email: 'a@a.a' } }), + }); + + // mock the dataSource's underlying fetch methods + launchAPI.get = jest.fn(() => [mockLaunchResponse]); + userAPI.store = mockStore; + userAPI.store.trips.findAll.mockReturnValueOnce([ + { dataValues: { launchId: 1 } }, + ]); + + // use the test server to create a query function + const { query } = createTestClient(server); + + // run query against the server and snapshot the output + const res = await query({ query: GET_LAUNCH, variables: { id: 1 } }); + expect(res).toMatchSnapshot(); +}); +``` + +This is an example of a full integration test being run against a test instance of `apollo-server`. This test imports the important pieces to test (`typeDefs`, `resolvers`, `dataSources`) and creates a new instance of `apollo-server`. Once an instance is created, it's passed to `createTestClient` which returns `{ query, mutate }`. These methods can then be used to execute operations against the server. + +For more examples of this tool in action, check out the [integration tests](https://github.com/apollographql/fullstack-tutorial/blob/master/final/server/src/__tests__/integration.js) in the [Fullstack Tutorial](https://www.apollographql.com/docs/tutorial/introduction.html). diff --git a/docs/source/features/unions-interfaces.md b/docs/source/features/unions-interfaces.md index eca30d2f772..6235259b5de 100644 --- a/docs/source/features/unions-interfaces.md +++ b/docs/source/features/unions-interfaces.md @@ -13,19 +13,20 @@ The `Union` type indicates that a field can return more than one object type, bu const { gql } = require('apollo-server'); const typeDefs = gql` -union Result = Book | Author + union Result = Book | Author -type Book { - title: String -} + type Book { + title: String + } -type Author { - name: String -} + type Author { + name: String + } -type Query { - search: [Result] -}`; + type Query { + search: [Result] + } +`; ``` Since a query requesting a union field, a query being made on a field which is union-typed must specify the object types containing the fields it wants. This ambiguity is solved by an extra `__resolveType` field in the resolver map. `__resolveType` defines the type of the result is out of the available options to GraphQL execution environment. @@ -77,72 +78,90 @@ A possible query for these result could appear as follows. This query demonstrat ## Interface type -An `Interface` type provides the ability to describe fields that are shared across different types. It is best used to show that all types implementing an interface always contain the interface's fields. In other words, it is the semantic opposite of a union. For example, in this example `Vehicle` interface type is used by members `Airplane` and `Car`: +Interfaces are a powerful way to build and use GraphQL schemas through the use of _abstract types_. Abstract types can't be used directly in schema, but can be used as building blocks for creating explicit types. -``` -interface Vehicle { - maxSpeed: Int +Consider an example where different types of books share a common set of attributes, such as _text books_ and _coloring books_. A simple foundation for these books might be represented as the following `interface`: + +```graphql +interface Book { + title: String + author: Author } +``` + +We won't be able to directly use this interface to query for a book, but we can use it to implement concrete types. Imagine a screen within an application which needs to display a feed of all books, without regard to their (more specific) type. To create such functionality, we could define the following: -type Airplane implements Vehicle { - maxSpeed: Int - wingspan: Int +```graphql +type TextBook implements Book { + title: String + author: Author + classes: [Class] } -type Car implements Vehicle { - maxSpeed: Int - licensePlate: String +type ColoringBook implements Book { + title: String + author: Author + colors: [Color] } type Query { - vehicle: Vehicle + schoolBooks: [Book] } ``` -Similarly to the `Union`, `Interface` requires an extra `__resolveType` field in the resolver map. +In this example, we've used the `Book` interface as the foundation for the `TextBook` and `ColoringBook` types. Then, a `schoolBooks` field simply expresses that it returns a list of books (i.e. `[Book]`). + +Similarly to the `Union`, `Interface` requires an extra `__resolveType` field in the resolver map to determine which type the interface should resolve to. ```js const resolvers = { - Vehicle: { - __resolveType(obj, context, info){ - if(obj.wingspan){ - return 'Airplane'; + Book: { + __resolveType(book, context, info){ + if(book.classes){ + return 'TextBook'; } - if(obj.licensePlate){ - return 'Car'; + if(book.colors){ + return 'ColoringBook'; } return null; }, }, Query: { - vehicle: () => { ... } + schoolBooks: () => { ... } }, }; +``` -const server = new ApolloServer({ - typeDefs, - resolvers, -}); +Implementing the book feed example is now simplified since we've removed the need to worry about what kind of `Book`s will be returned. A query against this schema, which could return _text books_ and _coloring_ books, might look like: -server.listen().then(({ url }) => { - console.log(`🚀 Server ready at ${url}`) -}); +```graphql +query GetBooks { + schoolBooks { + title + author + } +} ``` -A possible query could appear as follows. Notice that `maxSpeed` is shared, so it can be included directly: +This is really helpful for feeds of common content, user role systems, and more! -```graphql -{ - vehicle { - maxSpeed +Furthermore, if we need to return fields which are only provided by either `TextBook`s or `ColoringBook`s (not both) we can request fragments from the abstract types in the query. Those fragments will be filled in only as appropriate; in the case of the example, only coloring books will be returned with `colors`, and only textbooks will have `classes`: - ... on Car { - licensePlate +```graphql +query GetBooks { + schoolBooks { + title + ... on TextBook { + classes { + name + } } - ... on Airplane { - wingspan + ... on ColoringBook { + colors { + name + } } } } diff --git a/docs/source/images/index-get-started.svg b/docs/source/images/index-get-started.svg index 486156de3cb..5d8d5f2274e 100644 --- a/docs/source/images/index-get-started.svg +++ b/docs/source/images/index-get-started.svg @@ -8,8 +8,8 @@ - GET STARTED! + GET STARTED! - \ No newline at end of file + diff --git a/docs/source/migration-file-uploads.md b/docs/source/migration-file-uploads.md new file mode 100644 index 00000000000..592aedb7c03 --- /dev/null +++ b/docs/source/migration-file-uploads.md @@ -0,0 +1,29 @@ +--- +title: File uploads in Node.js < v8.5.0 +--- + +File uploads are supported in Apollo Server 2.x through the third-party [`graphql-upload`](https://npm.im/graphql-upload/) package. While Apollo Server 2.x aims to support Node.js LTS versions prior to v8.5.0, the `graphql-upload` project no longer supports file uploads on versions of Node.js prior to v8.5.0 due to changes in the underlying architecture. + +While Node.js versions prior to v8.5.0 are still under [_Long Term Support_ (LTS) agreements](https://github.com/nodejs/Release#release-schedule) from the Node.js Foundation, **we encourage _all users_ of Apollo Server to update to newer LTS versions of Node.js** prior to the "end-of-life" dates for their current server version. + +For example, while Node.js 6.x is covered by Long Term Support until April 2019, there are substantial performance (e.g. [V8](https://v8.dev/) improvements) and language changes (e.g. "modern" ECMAScript support) offered by newer Node.js engines (e.g. 8.x, 10.x). Switching to newer Long Term Support versions of Node.js comes with long-term benefits; Node.js 10.x LTS releases are covered by the Node.js Foundation through April 2021. + +Since file upload support for Node.js versions prior to v8.5.0 is no longer offered by `graphql-upload`, users of those versions must disable file uploads to continue using newer Apollo Server 2.x versions. + +**To disable file uploads and continue using Apollo Server 2.x on Node.js 6.x**, add the `uploads: false` setting to the options when constructing the server. For example: + +```js +const { typeDefs, resolvers } = require('./anOutsideDependency'); +const server = new ApolloServer({ + /* Existing Apollo Server settings — e.g. type definitions */ + typeDefs, + resolvers, + + /* Add this line to disable upload support! */ + uploads: false, + + /* ... other Apollo Server settings ... */ +}) +``` + +For additional assistance, please [search for existing issues](https://github.com/apollographql/apollo-server/issues?q=uploads) or file a [new issue](https://github.com/apollographql/apollo-server/issues/new) on the Apollo Server GitHub repository. diff --git a/docs/source/migration-two-dot.md b/docs/source/migration-two-dot.md index a23499061f2..d731941be20 100644 --- a/docs/source/migration-two-dot.md +++ b/docs/source/migration-two-dot.md @@ -288,7 +288,7 @@ Apollo Server 2 removes the `logFunction` to reduce the exposure of internal imp

Replacing GraphiQL

-Apollo Server 2 ships with GraphQL Playground instead of GraphiQL and collocates the gui with the endpoint. GraphQL playground can be customized in the following manner. +Apollo Server 2 ships with GraphQL Playground instead of GraphiQL and collocates the GUI with the endpoint. GraphQL playground can be customized in the following manner. ```js const { ApolloServer, gql } = require('apollo-server-express'); @@ -297,11 +297,7 @@ const server = new ApolloServer({ // These will be defined for both new or existing servers typeDefs, resolvers, -}); - -server.applyMiddleware({ - app, // app is from an existing express app - gui: { + playground: { endpoint?: string subscriptionEndpoint?: string tabs: [ @@ -313,7 +309,11 @@ server.applyMiddleware({ headers?: { [key: string]: string } }, ], - } + }, +}); + +server.applyMiddleware({ + app, // app is from an existing express app }); app.listen({ port: 4000 }, () => diff --git a/docs/source/schemas/types.md b/docs/source/schemas/types.md index 36c55fff2ff..62cc979e88e 100644 --- a/docs/source/schemas/types.md +++ b/docs/source/schemas/types.md @@ -92,9 +92,9 @@ In Apollo server, a resolver that returns an enum can use the direct string repr ```js const schema = gql` -type Query { - genre: Genre -} + type Query { + genre: Genre + } `; const resolvers = { @@ -110,9 +110,9 @@ Lists are defined with as type modifier that wraps object types, scalars, and en ```js const schema = gql` -type Query { - todos: [String] -} + type Query { + todos: [String] + } `; const resolvers = { @@ -132,9 +132,9 @@ Using the exclamation mark to declare a field as non-nullable simplifies the con ```js const schema = gql` -type Query { - genre: String! -} + type Query { + genre: String! + } `; const resolvers = { diff --git a/docs/source/whats-new.md b/docs/source/whats-new.md index bfabe6a69d4..f60e1dc3cad 100644 --- a/docs/source/whats-new.md +++ b/docs/source/whats-new.md @@ -246,7 +246,7 @@ const resolvers = { Mutation: { singleUpload: (parent, args) => { return args.file.then(file => { - //Contents of Upload scalar: https://github.com/jaydenseric/apollo-upload-server#upload-scalar + //Contents of Upload scalar: https://github.com/jaydenseric/graphql-upload#class-graphqlupload //file.stream is a node stream that contains the contents of the uploaded file //node stream api: https://nodejs.org/api/stream.html return file; diff --git a/docs/source/why-apollo-server.md b/docs/source/why-apollo-server.md index 83a0e910313..b50caaa3df7 100644 --- a/docs/source/why-apollo-server.md +++ b/docs/source/why-apollo-server.md @@ -29,7 +29,7 @@ const typeDefs = gql` const resolvers = { Query: { - posts: (root, { authorId }, { Post }) => Post.findByAuthorId(authorId), + posts: (parent, { authorId }, { Post }) => Post.findByAuthorId(authorId), }, }; diff --git a/jest.config.base.js b/jest.config.base.js index 328505f1da7..45592377f7e 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -21,7 +21,7 @@ module.exports = { // We don't want to match `apollo-server-env` and // `apollo-engine-reporting-protobuf`, because these don't depend on // compilation but need to be initialized from as parto of `prepare`. - '^(?!apollo-server-env|apollo-engine-reporting-protobuf)(apollo-(?:server|datasource|cache-control|tracing|engine)[^/]*|graphql-extensions)(?:/dist)?((?:/.*)|$)': '/../../packages/$1/src$2' + '^(?!apollo-server-env|apollo-engine-reporting-protobuf)(apollo-(?:server|graphql|datasource|cache-control|tracing|engine)[^/]*|graphql-extensions)(?:/dist)?((?:/.*)|$)': '/../../packages/$1/src$2' }, clearMocks: true, globals: { diff --git a/package-lock.json b/package-lock.json index a0e16992206..1800bdd6ba8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3,20 +3,18 @@ "requires": true, "lockfileVersion": 1, "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==", + "@apollographql/apollo-tools": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.3.0.tgz", + "integrity": "sha512-Tg0NVtCFHMQkcSX/dqT0m+BNnK9/lbjo4YFNX9W5g3EwczlC0edrleUM/dC4wXw71DwGrGwFiZxWLxqY1ocU5A==", "requires": { - "@babel/runtime-corejs2": "^7.0.0-rc.1", - "busboy": "^0.2.14", - "object-path": "^0.11.4" + "apollo-env": "0.3.0" } }, "@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==" + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.6.tgz", + "integrity": "sha512-lqK94b+caNtmKFs5oUVXlSpN3sm5IXZ+KfhMxOtr0LR2SqErzkoJilitjDvJ1WbjHlxLI7WtCjRmOLdOGJqtMQ==" }, "@babel/code-frame": { "version": "7.0.0", @@ -55,13 +53,75 @@ "regenerator-runtime": "^0.12.0" } }, - "@babel/runtime-corejs2": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.1.2.tgz", - "integrity": "sha512-drxaPByExlcRDKW4ZLubUO4ZkI8/8ax9k9wve1aEthdLKFzjB7XRkOQ0xoTIWGxqdDnWDElkjYq77bt7yrcYJQ==", + "@iamstarkov/listr-update-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz", + "integrity": "sha512-IJyxQWsYDEkf8C8QthBn5N8tIUR9V9je6j3sMIpAkonaadjbvxmRC6RAhpa3RKxndhNnU2M6iNbtJwd7usQYIA==", + "dev": true, "requires": { - "core-js": "^2.5.7", - "regenerator-runtime": "^0.12.0" + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^2.3.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://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" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://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": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, "@koa/cors": { @@ -70,57 +130,56 @@ "integrity": "sha512-Ollvsy3wB8+7R9w6hPVzlj3wekF6nK+IHpHj7faSPVXCkahqCwNEPp9+0C4b51RDkdpHjevLEGLOKuVjqtXgSQ==" }, "@lerna/add": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.4.1.tgz", - "integrity": "sha512-Vf54B42jlD6G52qnv/cAGH70cVQIa+LX//lfsbkxHvzkhIqBl5J4KsnTOPkA9uq3R+zP58ayicCHB9ReiEWGJg==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.10.6.tgz", + "integrity": "sha512-FxQ5Bmyb5fF+3BQiNffM6cTeGCrl4uaAuGvxFIWF6Pgz6U14tUc1e16xgKDvVb1CurzJgIV5sLOT5xmCOqv1kA==", "dev": true, "requires": { - "@lerna/bootstrap": "^3.4.1", - "@lerna/command": "^3.3.0", - "@lerna/filter-options": "^3.3.2", - "@lerna/npm-conf": "^3.4.1", - "@lerna/validation-error": "^3.0.0", + "@lerna/bootstrap": "3.10.6", + "@lerna/command": "3.10.6", + "@lerna/filter-options": "3.10.6", + "@lerna/npm-conf": "3.7.0", + "@lerna/validation-error": "3.6.0", "dedent": "^0.7.0", - "npm-package-arg": "^6.0.0", + "libnpm": "^2.0.1", "p-map": "^1.2.0", - "pacote": "^9.1.0", "semver": "^5.5.0" } }, "@lerna/batch-packages": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@lerna/batch-packages/-/batch-packages-3.1.2.tgz", - "integrity": "sha512-HAkpptrYeUVlBYbLScXgeCgk6BsNVXxDd53HVWgzzTWpXV4MHpbpeKrByyt7viXlNhW0w73jJbipb/QlFsHIhQ==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/batch-packages/-/batch-packages-3.10.6.tgz", + "integrity": "sha512-sInr3ZQJFMh9Zq+ZUoVjX8R67j9ViRkVy0uEMsOfG+jZlXj1lRPRMPRiRgU0jXSYEwCdwuAB5pTd9tTx0VCJUw==", "dev": true, "requires": { - "@lerna/package-graph": "^3.1.2", - "@lerna/validation-error": "^3.0.0", - "npmlog": "^4.1.2" + "@lerna/package-graph": "3.10.6", + "@lerna/validation-error": "3.6.0", + "libnpm": "^2.0.1" } }, "@lerna/bootstrap": { - "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.4.1", - "@lerna/npm-install": "^3.3.0", - "@lerna/rimraf-dir": "^3.3.0", - "@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", - "@lerna/validation-error": "^3.0.0", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.10.6.tgz", + "integrity": "sha512-qbGjAxRpV/eiI9CboUIpsPPGpSogs8mN2/iDaAUBTaWVFVz/YyU64nui84Gll0kbdaHOyPput+kk2S8NCSCCdg==", + "dev": true, + "requires": { + "@lerna/batch-packages": "3.10.6", + "@lerna/command": "3.10.6", + "@lerna/filter-options": "3.10.6", + "@lerna/has-npm-version": "3.10.0", + "@lerna/npm-install": "3.10.0", + "@lerna/package-graph": "3.10.6", + "@lerna/pulse-till-done": "3.7.1", + "@lerna/rimraf-dir": "3.10.0", + "@lerna/run-lifecycle": "3.10.5", + "@lerna/run-parallel-batches": "3.0.0", + "@lerna/symlink-binary": "3.10.0", + "@lerna/symlink-dependencies": "3.10.0", + "@lerna/validation-error": "3.6.0", "dedent": "^0.7.0", "get-port": "^3.2.0", + "libnpm": "^2.0.1", "multimatch": "^2.1.0", - "npm-package-arg": "^6.0.0", - "npmlog": "^4.1.2", "p-finally": "^1.0.0", "p-map": "^1.2.0", "p-map-series": "^1.0.0", @@ -130,26 +189,26 @@ } }, "@lerna/changed": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.4.1.tgz", - "integrity": "sha512-gT7fhl4zQWyGETDO4Yy5wsFnqNlBSsezncS1nkMW1uO6jwnolwYqcr1KbrMR8HdmsZBn/00Y0mRnbtbpPPey8w==", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.10.8.tgz", + "integrity": "sha512-K2BQPpSS93uNJqi8A5mwrFR9I6Pa/a0jgR/26jun0Wa39DTOjf5WP7EDvXQ8Pftx5kMdHb5hQDwvMCcBJw25mA==", "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.4.1" + "@lerna/collect-updates": "3.10.1", + "@lerna/command": "3.10.6", + "@lerna/listable": "3.10.6", + "@lerna/output": "3.6.0", + "@lerna/version": "3.10.8" } }, "@lerna/check-working-tree": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-3.3.0.tgz", - "integrity": "sha512-oeEP1dNhiiKUaO0pmcIi73YXJpaD0n5JczNctvVNZ8fGZmrALZtEnmC28o6Z7JgQaqq5nd2kO7xbnjoitrC51g==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-3.10.0.tgz", + "integrity": "sha512-NdIPhDgEtGHfeGjB9F0oAoPLywgMpjnJhLLwTNQkelDHo2xNAVpG8kV+A2UJ+cU5UXCZA4RZFxKNmw86rO+Drw==", "dev": true, "requires": { - "@lerna/describe-ref": "^3.3.0", - "@lerna/validation-error": "^3.0.0" + "@lerna/describe-ref": "3.10.0", + "@lerna/validation-error": "3.6.0" } }, "@lerna/child-process": { @@ -161,110 +220,41 @@ "chalk": "^2.3.1", "execa": "^1.0.0", "strong-log-transformer": "^2.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.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" - } - }, - "get-stream": { - "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" - } - } } }, "@lerna/clean": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/clean/-/clean-3.3.2.tgz", - "integrity": "sha512-mvqusgSp2ou5SGqQgTEoTvGJpGfH4+L6XSeN+Ims+eNFGXuMazmKCf+rz2PZBMFufaHJ/Os+JF0vPCcWI1Fzqg==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/clean/-/clean-3.10.6.tgz", + "integrity": "sha512-MuL8HOwnyvVtr6GOiAN/Ofjbx+BJdCrtjrM1Uuh8FFnbnZTPVf+0MPxL2jVzPMo0PmoIrX3fvlwvzKNk/lH0Ug==", "dev": true, "requires": { - "@lerna/command": "^3.3.0", - "@lerna/filter-options": "^3.3.2", - "@lerna/prompt": "^3.3.1", - "@lerna/rimraf-dir": "^3.3.0", + "@lerna/command": "3.10.6", + "@lerna/filter-options": "3.10.6", + "@lerna/prompt": "3.6.0", + "@lerna/pulse-till-done": "3.7.1", + "@lerna/rimraf-dir": "3.10.0", "p-map": "^1.2.0", "p-map-series": "^1.0.0", "p-waterfall": "^1.0.0" } }, "@lerna/cli": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@lerna/cli/-/cli-3.2.0.tgz", - "integrity": "sha512-JdbLyTxHqxUlrkI+Ke+ltXbtyA+MPu9zR6kg/n8Fl6uaez/2fZWtReXzYi8MgLxfUFa7+1OHWJv4eAMZlByJ+Q==", + "version": "3.10.7", + "resolved": "https://registry.npmjs.org/@lerna/cli/-/cli-3.10.7.tgz", + "integrity": "sha512-yuoz/24mIfYit3neKqoE5NVs42Rj9A6A6SlkNPDfsy3v/Vh7SgYkU3cwiGyvwBGzIdhqL4/SWYo8H7YJLs0C+g==", "dev": true, "requires": { - "@lerna/global-options": "^3.1.3", + "@lerna/global-options": "3.10.6", "dedent": "^0.7.0", - "npmlog": "^4.1.2", + "libnpm": "^2.0.1", "yargs": "^12.0.1" }, "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "decamelize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", - "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", - "dev": true, - "requires": { - "xregexp": "4.0.0" - } - }, - "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true }, "invert-kv": { "version": "2.0.0", @@ -282,35 +272,35 @@ } }, "mem": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", - "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", "dev": true, "requires": { "map-age-cleaner": "^0.1.1", "mimic-fn": "^1.0.0", - "p-is-promise": "^1.1.0" + "p-is-promise": "^2.0.0" } }, "os-locale": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", - "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "execa": "^0.10.0", + "execa": "^1.0.0", "lcid": "^2.0.0", "mem": "^4.0.0" } }, "yargs": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", - "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", "dev": true, "requires": { "cliui": "^4.0.0", - "decamelize": "^2.0.0", + "decamelize": "^1.2.0", "find-up": "^3.0.0", "get-caller-file": "^1.0.1", "os-locale": "^3.0.0", @@ -320,30 +310,31 @@ "string-width": "^2.0.0", "which-module": "^2.0.0", "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" + "yargs-parser": "^11.1.1" } }, "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } }, "@lerna/collect-updates": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-3.3.2.tgz", - "integrity": "sha512-9WyBJI2S5sYgEZEScu525Lbi6nknNrdBKop35sCDIC9y6AIGvH6Dr5tkTd+Kg3n1dE+kHwW/xjERkx3+h7th3w==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-3.10.1.tgz", + "integrity": "sha512-vb0wEJ8k63G+2CR/ud1WeVHNJ21Fs6Ew6lbdGZXnF4ZvaFWxWJZpoHeWwzjhMdJ75QdTzUaIhTG1hnH9faQNMw==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/describe-ref": "^3.3.0", + "@lerna/child-process": "3.3.0", + "@lerna/describe-ref": "3.10.0", + "libnpm": "^2.0.1", "minimatch": "^3.0.4", - "npmlog": "^4.1.2", "slash": "^1.0.0" }, "dependencies": { @@ -356,76 +347,37 @@ } }, "@lerna/command": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/command/-/command-3.3.0.tgz", - "integrity": "sha512-NTOkLEKlWcBLHSvUr9tzVpV7RJ4GROLeOuZ6RfztGOW/31JPSwVVBD2kPifEXNZunldOx5GVWukR+7+NpAWhsg==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/command/-/command-3.10.6.tgz", + "integrity": "sha512-jPZswMZXOpAaIuSF5hrz+eaWQzbDrvwbrkCoRJKfiAHx7URAkE6MQe9DeAnqrTKMqwfg0RciSrZLc8kWYfrzCQ==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/package-graph": "^3.1.2", - "@lerna/project": "^3.0.0", - "@lerna/validation-error": "^3.0.0", - "@lerna/write-log-file": "^3.0.0", + "@lerna/child-process": "3.3.0", + "@lerna/package-graph": "3.10.6", + "@lerna/project": "3.10.0", + "@lerna/validation-error": "3.6.0", + "@lerna/write-log-file": "3.6.0", "dedent": "^0.7.0", "execa": "^1.0.0", "is-ci": "^1.0.10", - "lodash": "^4.17.5", - "npmlog": "^4.1.2" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.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" - } - }, - "get-stream": { - "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" - } - } + "libnpm": "^2.0.1", + "lodash": "^4.17.5" } }, "@lerna/conventional-commits": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.4.1.tgz", - "integrity": "sha512-3NETrA58aUkaEW3RdwdJ766Bg9NVpLzb26mtdlsJQcvB5sQBWH5dJSHIVQH1QsGloBeH2pE/mDUEVY8ZJXuR4w==", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.10.8.tgz", + "integrity": "sha512-kjODN5f++nsvNT6w9zPuzN+tfNlq7QaKzy6KOMUb+AvGfI4+AKw8z9Uhr8AGvyuFgyNVI69/vdFaXrWC4iTKtQ==", "dev": true, "requires": { - "@lerna/validation-error": "^3.0.0", - "conventional-changelog-angular": "^5.0.1", - "conventional-changelog-core": "^3.1.0", - "conventional-recommended-bump": "^4.0.1", + "@lerna/validation-error": "3.6.0", + "conventional-changelog-angular": "^5.0.2", + "conventional-changelog-core": "^3.1.5", + "conventional-recommended-bump": "^4.0.4", "fs-extra": "^7.0.0", "get-stream": "^4.0.0", - "npm-package-arg": "^6.0.0", - "npmlog": "^4.1.2", + "libnpm": "^2.0.1", + "pify": "^3.0.0", "semver": "^5.5.0" }, "dependencies": { @@ -441,21 +393,22 @@ } }, "@lerna/create": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.4.1.tgz", - "integrity": "sha512-l+4t2SRO5nvW0MNYY+EWxbaMHsAN8bkWH3nyt7EzhBjs4+TlRAJRIEqd8o9NWznheE3pzwczFz1Qfl3BWbyM5A==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.10.6.tgz", + "integrity": "sha512-OddQtGBHM2/eJONggLWoTE6275XGbnJ6dIVF+fLsKS93o4GC6g+qcc6Y7lUWHm5bfpeOwNOVKwj0tvqBZ6MgoA==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/command": "^3.3.0", - "@lerna/npm-conf": "^3.4.1", - "@lerna/validation-error": "^3.0.0", + "@lerna/child-process": "3.3.0", + "@lerna/command": "3.10.6", + "@lerna/npm-conf": "3.7.0", + "@lerna/validation-error": "3.6.0", "camelcase": "^4.1.0", "dedent": "^0.7.0", "fs-extra": "^7.0.0", "globby": "^8.0.1", "init-package-json": "^1.10.3", - "npm-package-arg": "^6.0.0", + "libnpm": "^2.0.1", + "p-reduce": "^1.0.0", "pify": "^3.0.0", "semver": "^5.5.0", "slash": "^1.0.0", @@ -484,136 +437,171 @@ } }, "@lerna/create-symlink": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-3.3.0.tgz", - "integrity": "sha512-0lb88Nnq1c/GG+fwybuReOnw3+ah4dB81PuWwWwuqUNPE0n50qUf/M/7FfSb5JEh/93fcdbZI0La8t3iysNW1w==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-3.6.0.tgz", + "integrity": "sha512-YG3lTb6zylvmGqKU+QYA3ylSnoLn+FyLH5XZmUsD0i85R884+EyJJeHx/zUk+yrL2ZwHS4RBUgJfC24fqzgPoA==", "dev": true, "requires": { "cmd-shim": "^2.0.2", "fs-extra": "^7.0.0", - "npmlog": "^4.1.2" + "libnpm": "^2.0.1" } }, "@lerna/describe-ref": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-3.3.0.tgz", - "integrity": "sha512-4t7M4OupnYMSPNLrLUau8qkS+dgLEi4w+DkRkV0+A+KNYga1W0jVgNLPIIsxta7OHfodPkCNAqZCzNCw/dmAwA==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-3.10.0.tgz", + "integrity": "sha512-fouh3FQS07QxJJp/mW8LkGnH0xMRAzpBlejtZaiRwfDkW2kd6EuHaj8I/2/p21Wsprcvuu4dqmyia2YS1xFb/w==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "npmlog": "^4.1.2" + "@lerna/child-process": "3.3.0", + "libnpm": "^2.0.1" } }, "@lerna/diff": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/diff/-/diff-3.3.0.tgz", - "integrity": "sha512-sIoMjsm3NVxvmt6ofx8Uu/2fxgldQqLl0zmC9X1xW00j831o5hBffx1EoKj9CnmaEvoSP6j/KFjxy2RWjebCIg==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/diff/-/diff-3.10.6.tgz", + "integrity": "sha512-0MqFhosjrqsIdXiKIu7t3CiJELqiU9mkjFBhYPB7JruAzpPwjMXJnC6/Ur5/7LXJYYVpqGQwZI9ZaZlOYJhhrw==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/command": "^3.3.0", - "@lerna/validation-error": "^3.0.0", - "npmlog": "^4.1.2" + "@lerna/child-process": "3.3.0", + "@lerna/command": "3.10.6", + "@lerna/validation-error": "3.6.0", + "libnpm": "^2.0.1" } }, "@lerna/exec": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/exec/-/exec-3.3.2.tgz", - "integrity": "sha512-mN6vGxNir7JOGvWLwKr3DW3LNy1ecCo2ziZj5rO9Mw5Rew3carUu1XLmhF/4judtsvXViUY+rvGIcqHe0vvb+w==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/exec/-/exec-3.10.6.tgz", + "integrity": "sha512-cdHqaRBMYceJu8rZLO8b4ZeR27O+xKPHgzi13OOOfBJQjrTuacjMWyHgmpy8jWc/0f7QnTl4VsHks7VJ3UK+vw==", "dev": true, "requires": { - "@lerna/batch-packages": "^3.1.2", - "@lerna/child-process": "^3.3.0", - "@lerna/command": "^3.3.0", - "@lerna/filter-options": "^3.3.2", - "@lerna/run-parallel-batches": "^3.0.0", - "@lerna/validation-error": "^3.0.0" + "@lerna/batch-packages": "3.10.6", + "@lerna/child-process": "3.3.0", + "@lerna/command": "3.10.6", + "@lerna/filter-options": "3.10.6", + "@lerna/run-parallel-batches": "3.0.0", + "@lerna/validation-error": "3.6.0" } }, "@lerna/filter-options": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-3.3.2.tgz", - "integrity": "sha512-0WHqdDgAnt5WKoByi1q+lFw8HWt5tEKP2DnLlGqWv3YFwVF5DsPRlO7xbzjY9sJgvyJtZcnkMtccdBPFhGGyIQ==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-3.10.6.tgz", + "integrity": "sha512-r/dQbqN+RGFKZNn+DyWehswFmAkny/fkdMB2sRM2YVe7zRTtSl95YxD9DtdYnpJTG/jbOVICS/L5QJakrI6SSw==", "dev": true, "requires": { - "@lerna/collect-updates": "^3.3.2", - "@lerna/filter-packages": "^3.0.0", + "@lerna/collect-updates": "3.10.1", + "@lerna/filter-packages": "3.10.0", "dedent": "^0.7.0" } }, "@lerna/filter-packages": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-3.0.0.tgz", - "integrity": "sha512-zwbY1J4uRjWRZ/FgYbtVkq7I3Nduwsg2V2HwLKSzwV2vPglfGqgovYOVkND6/xqe2BHwDX4IyA2+e7OJmLaLSA==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-3.10.0.tgz", + "integrity": "sha512-3Acdj+jbany6LnQSuImU4ttcK5ULHSVug8Gh/EvwTewKCDpHAuoI3eyuzZOnSBdMvDOjE03uIESQK0dNNsn6Ow==", "dev": true, "requires": { - "@lerna/validation-error": "^3.0.0", - "multimatch": "^2.1.0", - "npmlog": "^4.1.2" + "@lerna/validation-error": "3.6.0", + "libnpm": "^2.0.1", + "multimatch": "^2.1.0" } }, "@lerna/get-npm-exec-opts": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.0.0.tgz", - "integrity": "sha512-arcYUm+4xS8J3Palhl+5rRJXnZnFHsLFKHBxznkPIxjwGQeAEw7df38uHdVjEQ+HNeFmHnBgSqfbxl1VIw5DHg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.6.0.tgz", + "integrity": "sha512-ruH6KuLlt75aCObXfUIdVJqmfVq7sgWGq5mXa05vc1MEqxTIiU23YiJdWzofQOOUOACaZkzZ4K4Nu7wXEg4Xgg==", "dev": true, "requires": { - "npmlog": "^4.1.2" + "libnpm": "^2.0.1" + } + }, + "@lerna/get-packed": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-3.7.0.tgz", + "integrity": "sha512-yuFtjsUZIHjeIvIYQ/QuytC+FQcHwo3peB+yGBST2uWCLUCR5rx6knoQcPzbxdFDCuUb5IFccFGd3B1fHFg3RQ==", + "dev": true, + "requires": { + "fs-extra": "^7.0.0", + "ssri": "^6.0.1", + "tar": "^4.4.8" + }, + "dependencies": { + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } } }, "@lerna/global-options": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@lerna/global-options/-/global-options-3.1.3.tgz", - "integrity": "sha512-LVeZU/Zgc0XkHdGMRYn+EmHfDmmYNwYRv3ta59iCVFXLVp7FRFWF7oB1ss/WRa9x/pYU0o6L8as/5DomLUGASA==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/global-options/-/global-options-3.10.6.tgz", + "integrity": "sha512-k5Xkq1M/uREFC2R9uwN5gcvIgjj4iOXo0YyeEXCMWBiW3j2GL9xN4d1MmAIcrYlAzVYh6kLlWaFWl/rNIneHIw==", "dev": true }, "@lerna/has-npm-version": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-3.3.0.tgz", - "integrity": "sha512-GX7omRep1eBRZHgjZLRw3MpBJSdA5gPZFz95P7rxhpvsiG384Tdrr/cKFMhm0A09yq27Tk/nuYTaZIj7HsVE6g==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-3.10.0.tgz", + "integrity": "sha512-N4RRYxGeivuaKgPDzrhkQOQs1Sg4tOnxnEe3akfqu1wDA4Ng5V6Y2uW3DbkAjFL3aNJhWF5Vbf7sBsGtfgDQ8w==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", + "@lerna/child-process": "3.3.0", "semver": "^5.5.0" } }, "@lerna/import": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@lerna/import/-/import-3.3.1.tgz", - "integrity": "sha512-2OzTQDkYKbBPpyP2iOI1sWfcvMjNLjjHjmREq/uOWJaSIk5J3Ukt71OPpcOHh4V2CBOlXidCcO+Hyb4FVIy8fw==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/import/-/import-3.10.6.tgz", + "integrity": "sha512-LlGxhfDhovoNoBJLF3PYd3j/G2GFTnfLh0V38+hBQ6lomMNJbjkACfiLVomQxPWWpYLk0GTlpWYR8YGv6L7Ifw==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/command": "^3.3.0", - "@lerna/prompt": "^3.3.1", - "@lerna/validation-error": "^3.0.0", + "@lerna/child-process": "3.3.0", + "@lerna/command": "3.10.6", + "@lerna/prompt": "3.6.0", + "@lerna/pulse-till-done": "3.7.1", + "@lerna/validation-error": "3.6.0", "dedent": "^0.7.0", "fs-extra": "^7.0.0", "p-map-series": "^1.0.0" } }, "@lerna/init": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/init/-/init-3.3.0.tgz", - "integrity": "sha512-HvgRLkIG6nDIeAO6ix5sUVIVV+W9UMk2rSSmFT66CDOefRi7S028amiyYnFUK1QkIAaUbVUyOnYaErtbJwICuw==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/init/-/init-3.10.6.tgz", + "integrity": "sha512-RIlEx+ofWLYRNjxCkkV3G0XQPM+/KA5RXRDb5wKQLYO1f+tZAaHoUh8fHDIvxGf/ohY/OIjYYGSsU+ysimfwiQ==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/command": "^3.3.0", + "@lerna/child-process": "3.3.0", + "@lerna/command": "3.10.6", "fs-extra": "^7.0.0", "p-map": "^1.2.0", "write-json-file": "^2.3.0" } }, "@lerna/link": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/link/-/link-3.3.0.tgz", - "integrity": "sha512-8CeXzGL7okrsVXsy2sHXI2KuBaczw3cblAnA2+FJPUqSKMPNbUTRzeU3bOlCjYtK0LbxC4ngENJTL3jJ8RaYQQ==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/link/-/link-3.10.6.tgz", + "integrity": "sha512-dwD6qftRWitgLDYbqtDrgO7c8uF5C0fHVew5M6gU5m9tBJidqd7cDwHv/bXboLEI63U7tt5y6LY+wEpYUFsBRw==", "dev": true, "requires": { - "@lerna/command": "^3.3.0", - "@lerna/package-graph": "^3.1.2", - "@lerna/symlink-dependencies": "^3.3.0", + "@lerna/command": "3.10.6", + "@lerna/package-graph": "3.10.6", + "@lerna/symlink-dependencies": "3.10.0", "p-map": "^1.2.0", "slash": "^1.0.0" }, @@ -627,43 +615,44 @@ } }, "@lerna/list": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/list/-/list-3.3.2.tgz", - "integrity": "sha512-XXEVy7w+i/xx8NeJmGirw4upEoEF9OfD6XPLjISNQc24VgQV+frXdVJ02QcP7Y/PkY1rdIVrOjvo3ipKVLUxaQ==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/list/-/list-3.10.6.tgz", + "integrity": "sha512-3ElQBj2dOB4uUkpsjC1bxdeZwEzRBuV1pBBs5E1LncwsZf7D9D99Z32fuZsDaCHpEMgHAD4/j8juI3/7m5dkaQ==", "dev": true, "requires": { - "@lerna/command": "^3.3.0", - "@lerna/filter-options": "^3.3.2", - "@lerna/listable": "^3.0.0", - "@lerna/output": "^3.0.0" + "@lerna/command": "3.10.6", + "@lerna/filter-options": "3.10.6", + "@lerna/listable": "3.10.6", + "@lerna/output": "3.6.0" } }, "@lerna/listable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@lerna/listable/-/listable-3.0.0.tgz", - "integrity": "sha512-HX/9hyx1HLg2kpiKXIUc1EimlkK1T58aKQ7ovO7rQdTx9ForpefoMzyLnHE1n4XrUtEszcSWJIICJ/F898M6Ag==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/listable/-/listable-3.10.6.tgz", + "integrity": "sha512-F7ZuvesSgeuMiJf99eOum5p1MQGQStykcmHH1ek+LQRMiGGF1o3PkBxPvHTZBADGOFarek8bFA5TVmRAMX7NIw==", "dev": true, "requires": { + "@lerna/batch-packages": "3.10.6", "chalk": "^2.3.1", "columnify": "^1.5.4" } }, "@lerna/log-packed": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-3.0.4.tgz", - "integrity": "sha512-vVQHgMagE2wnbxhNY9nFkdu+Cx2TsyWalkJfkxbNzmo6gOCrDsxCBDj9vTEV8Q+4aWx0C0Bsc0sB2Eb8y/+ofA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-3.6.0.tgz", + "integrity": "sha512-T/J41zMkzpWB5nbiTRS5PmYTFn74mJXe6RQA2qhkdLi0UqnTp97Pux1loz3jsJf2yJtiQUnyMM7KuKIAge0Vlw==", "dev": true, "requires": { "byte-size": "^4.0.3", "columnify": "^1.5.4", "has-unicode": "^2.0.1", - "npmlog": "^4.1.2" + "libnpm": "^2.0.1" } }, "@lerna/npm-conf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-3.4.1.tgz", - "integrity": "sha512-i9G6DnbCqiAqxKx2rSXej/n14qxlV/XOebL6QZonxJKzNTB+Q2wglnhTXmfZXTPJfoqimLaY4NfAEtbOXRWOXQ==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-3.7.0.tgz", + "integrity": "sha512-+WSMDfPKcKzMfqq283ydz9RRpOU6p9wfx0wy4hVSUY/6YUpsyuk8SShjcRtY8zTM5AOrxvFBuuV90H4YpZ5+Ng==", "dev": true, "requires": { "config-chain": "^1.1.11", @@ -671,102 +660,157 @@ } }, "@lerna/npm-dist-tag": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-3.3.0.tgz", - "integrity": "sha512-EtZJXzh3w5tqXEev+EBBPrWKWWn0WgJfxm4FihfS9VgyaAW8udIVZHGkIQ3f+tBtupcAzA9Q8cQNUkGF2efwmA==", + "version": "3.8.5", + "resolved": "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-3.8.5.tgz", + "integrity": "sha512-VO57yKTB4NC2LZuTd4w0LmlRpoFm/gejQ1gqqLGzSJuSZaBXmieElFovzl21S07cqiy7FNVdz75x7/a6WCZ6XA==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/get-npm-exec-opts": "^3.0.0", - "npmlog": "^4.1.2" + "figgy-pudding": "^3.5.1", + "libnpm": "^2.0.1" } }, "@lerna/npm-install": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-3.3.0.tgz", - "integrity": "sha512-WoVvKdS8ltROTGSNQwo6NDq0YKnjwhvTG4li1okcN/eHKOS3tL9bxbgPx7No0wOq5DKBpdeS9KhAfee6LFAZ5g==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-3.10.0.tgz", + "integrity": "sha512-/6/XyLY9/4jaMPBOVYUr4wZxQURIfwoELY0qCQ8gZ5zv4cOiFiiCUxZ0i4fxqFtD7nJ084zq1DsZW0aH0CIWYw==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/get-npm-exec-opts": "^3.0.0", + "@lerna/child-process": "3.3.0", + "@lerna/get-npm-exec-opts": "3.6.0", "fs-extra": "^7.0.0", - "npm-package-arg": "^6.0.0", - "npmlog": "^4.1.2", + "libnpm": "^2.0.1", "signal-exit": "^3.0.2", "write-pkg": "^3.1.0" } }, "@lerna/npm-publish": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-3.3.1.tgz", - "integrity": "sha512-bVTlWIcBL6Zpyzqvr9C7rxXYcoPw+l7IPz5eqQDNREj1R39Wj18OWB2KTJq8l7LIX7Wf4C2A1uT5hJaEf9BuvA==", + "version": "3.10.7", + "resolved": "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-3.10.7.tgz", + "integrity": "sha512-oU3/Q+eHC1fRjh7bk6Nn4tRD1OLR6XZVs3v+UWMWMrF4hVSV61pxcP5tpeI1n4gDQjSgh7seI4EzKVJe/WfraA==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/get-npm-exec-opts": "^3.0.0", - "@lerna/has-npm-version": "^3.3.0", - "@lerna/log-packed": "^3.0.4", + "@lerna/run-lifecycle": "3.10.5", + "figgy-pudding": "^3.5.1", "fs-extra": "^7.0.0", - "npmlog": "^4.1.2", - "p-map": "^1.2.0" + "libnpm": "^2.0.1" } }, "@lerna/npm-run-script": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-3.3.0.tgz", - "integrity": "sha512-YqDguWZzp4jIomaE4aWMUP7MIAJAFvRAf6ziQLpqwoQskfWLqK5mW0CcszT1oLjhfb3cY3MMfSTFaqwbdKmICg==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-3.10.0.tgz", + "integrity": "sha512-c21tBXLF1Wje4tx/Td9jKIMrlZo/8QQiyyadjdKpwyyo7orSMsVNXGyJwvZ4JVVDcwC3GPU6HQvkt63v7rcyaw==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "@lerna/get-npm-exec-opts": "^3.0.0", - "npmlog": "^4.1.2" + "@lerna/child-process": "3.3.0", + "@lerna/get-npm-exec-opts": "3.6.0", + "libnpm": "^2.0.1" } }, "@lerna/output": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@lerna/output/-/output-3.0.0.tgz", - "integrity": "sha512-EFxnSbO0zDEVKkTKpoCUAFcZjc3gn3DwPlyTDxbeqPU7neCfxP4rA4+0a6pcOfTlRS5kLBRMx79F2TRCaMM3DA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@lerna/output/-/output-3.6.0.tgz", + "integrity": "sha512-9sjQouf6p7VQtVCRnzoTGlZyURd48i3ha3WBHC/UBJnHZFuXMqWVPKNuvnMf2kRXDyoQD+2mNywpmEJg5jOnRg==", "dev": true, "requires": { - "npmlog": "^4.1.2" + "libnpm": "^2.0.1" + } + }, + "@lerna/pack-directory": { + "version": "3.10.5", + "resolved": "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-3.10.5.tgz", + "integrity": "sha512-Ulj24L9XdgjJIxBr6ZjRJEoBULVH3c10lqunUdW41bswXhzhirRtQIxv0+5shngNjDwgMmJfOBcuCVKPSez4tg==", + "dev": true, + "requires": { + "@lerna/get-packed": "3.7.0", + "@lerna/package": "3.7.2", + "@lerna/run-lifecycle": "3.10.5", + "figgy-pudding": "^3.5.1", + "libnpm": "^2.0.1", + "npm-packlist": "^1.1.12", + "tar": "^4.4.8", + "temp-write": "^3.4.0" + }, + "dependencies": { + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } } }, "@lerna/package": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@lerna/package/-/package-3.0.0.tgz", - "integrity": "sha512-djzEJxzn212wS8d9znBnlXkeRlPL7GqeAYBykAmsuq51YGvaQK67Umh5ejdO0uxexF/4r7yRwgrlRHpQs8Rfqg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@lerna/package/-/package-3.7.2.tgz", + "integrity": "sha512-8A5hN2CekM1a0Ix4VUO/g+REo+MsnXb8lnQ0bGjr1YGWzSL5NxYJ0Z9+0pwTfDpvRDYlFYO0rMVwBUW44b4dUw==", "dev": true, "requires": { - "npm-package-arg": "^6.0.0", + "libnpm": "^2.0.1", + "load-json-file": "^4.0.0", "write-pkg": "^3.1.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } } }, "@lerna/package-graph": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-3.1.2.tgz", - "integrity": "sha512-9wIWb49I1IJmyjPdEVZQ13IAi9biGfH/OZHOC04U2zXGA0GLiY+B3CAx6FQvqkZ8xEGfqzmXnv3LvZ0bQfc1aQ==", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-3.10.6.tgz", + "integrity": "sha512-mpIOJbhi+xLqT9BcUrLVD4We8WUdousQf/QndbEWl8DWAW1ethtRHVsCm9ufdBB3F9nj4PH/hqnDWWwqE+rS4w==", "dev": true, "requires": { - "@lerna/validation-error": "^3.0.0", - "npm-package-arg": "^6.0.0", + "@lerna/validation-error": "3.6.0", + "libnpm": "^2.0.1", "semver": "^5.5.0" } }, "@lerna/project": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@lerna/project/-/project-3.0.0.tgz", - "integrity": "sha512-XhDFVfqj79jG2Speggd15RpYaE8uiR25UKcQBDmumbmqvTS7xf2cvl2pq2UTvDafaJ0YwFF3xkxQZeZnFMwdkw==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/project/-/project-3.10.0.tgz", + "integrity": "sha512-9QRl8aGHuyU4zVEELQmNPnJTlS7XHqX7w9I9isCXdnilKc2R0MyvUs21lj6Yyt6xTuQnqD158TR9tbS4QufYQQ==", "dev": true, "requires": { - "@lerna/package": "^3.0.0", - "@lerna/validation-error": "^3.0.0", + "@lerna/package": "3.7.2", + "@lerna/validation-error": "3.6.0", "cosmiconfig": "^5.0.2", "dedent": "^0.7.0", "dot-prop": "^4.2.0", "glob-parent": "^3.1.0", "globby": "^8.0.1", + "libnpm": "^2.0.1", "load-json-file": "^4.0.0", - "npmlog": "^4.1.2", "p-map": "^1.2.0", "resolve-from": "^4.0.0", "write-json-file": "^2.3.0" @@ -824,42 +868,42 @@ } }, "@lerna/prompt": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@lerna/prompt/-/prompt-3.3.1.tgz", - "integrity": "sha512-eJhofrUCUaItMIH6et8kI7YqHfhjWqGZoTsE+40NRCfAraOMWx+pDzfRfeoAl3qeRAH2HhNj1bkYn70FbUOxuQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@lerna/prompt/-/prompt-3.6.0.tgz", + "integrity": "sha512-nyAjPMolJ/ZRAAVcXrUH89C4n1SiWvLh4xWNvWYKLcf3PI5yges35sDFP/HYrM4+cEbkNFuJCRq6CxaET4PRsg==", "dev": true, "requires": { "inquirer": "^6.2.0", - "npmlog": "^4.1.2" + "libnpm": "^2.0.1" } }, "@lerna/publish": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.4.3.tgz", - "integrity": "sha512-baeRL8xmOR25p86cAaS9mL0jdRzdv4dUo04PlK2Wes+YlL705F55cSXeC9npNie+9rGwFyLzCTQe18WdbZyLuw==", - "dev": true, - "requires": { - "@lerna/batch-packages": "^3.1.2", - "@lerna/check-working-tree": "^3.3.0", - "@lerna/child-process": "^3.3.0", - "@lerna/collect-updates": "^3.3.2", - "@lerna/command": "^3.3.0", - "@lerna/describe-ref": "^3.3.0", - "@lerna/get-npm-exec-opts": "^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.4.1", - "@lerna/run-parallel-batches": "^3.0.0", - "@lerna/validation-error": "^3.0.0", - "@lerna/version": "^3.4.1", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.10.8.tgz", + "integrity": "sha512-kS3zia6knsoN8nd+6ihuwRhicBM6HRmbDgoa4uii4+ZqLVz4dniHYfHCMcZzHYSN8Kj35MsT25Ax1iq5eCjxmQ==", + "dev": true, + "requires": { + "@lerna/batch-packages": "3.10.6", + "@lerna/check-working-tree": "3.10.0", + "@lerna/child-process": "3.3.0", + "@lerna/collect-updates": "3.10.1", + "@lerna/command": "3.10.6", + "@lerna/describe-ref": "3.10.0", + "@lerna/log-packed": "3.6.0", + "@lerna/npm-conf": "3.7.0", + "@lerna/npm-dist-tag": "3.8.5", + "@lerna/npm-publish": "3.10.7", + "@lerna/output": "3.6.0", + "@lerna/pack-directory": "3.10.5", + "@lerna/prompt": "3.6.0", + "@lerna/pulse-till-done": "3.7.1", + "@lerna/run-lifecycle": "3.10.5", + "@lerna/run-parallel-batches": "3.0.0", + "@lerna/validation-error": "3.6.0", + "@lerna/version": "3.10.8", + "figgy-pudding": "^3.5.1", "fs-extra": "^7.0.0", - "libnpmaccess": "^3.0.0", - "npm-package-arg": "^6.0.0", - "npm-registry-fetch": "^3.8.0", - "npmlog": "^4.1.2", + "libnpm": "^2.0.1", "p-finally": "^1.0.0", "p-map": "^1.2.0", "p-pipe": "^1.2.0", @@ -867,54 +911,64 @@ "semver": "^5.5.0" } }, + "@lerna/pulse-till-done": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-3.7.1.tgz", + "integrity": "sha512-MzpesZeW3Mc+CiAq4zUt9qTXI9uEBBKrubYHE36voQTSkHvu/Rox6YOvfUr+U7P6k8frFPeCgGpfMDTLhiqe6w==", + "dev": true, + "requires": { + "libnpm": "^2.0.1" + } + }, "@lerna/resolve-symlink": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-3.3.0.tgz", - "integrity": "sha512-KmoPDcFJ2aOK2inYHbrsiO9SodedUj0L1JDvDgirVNIjMUaQe2Q6Vi4Gh+VCJcyB27JtfHioV9R2NxU72Pk2hg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-3.6.0.tgz", + "integrity": "sha512-TVOAEqHJSQVhNDMFCwEUZPaOETqHDQV1TQWQfC8ZlOqyaUQ7veZUbg0yfG7RPNzlSpvF0ZaGFeR0YhYDAW03GA==", "dev": true, "requires": { "fs-extra": "^7.0.0", - "npmlog": "^4.1.2", + "libnpm": "^2.0.1", "read-cmd-shim": "^1.0.1" } }, "@lerna/rimraf-dir": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-3.3.0.tgz", - "integrity": "sha512-vSqOcZ4kZduiSprbt+y40qziyN3VKYh+ygiCdnbBbsaxpdKB6CfrSMUtrLhVFrqUfBHIZRzHIzgjTdtQex1KLw==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-3.10.0.tgz", + "integrity": "sha512-RSKSfxPURc58ERCD/PuzorR86lWEvIWNclXYGvIYM76yNGrWiDF44pGHQvB4J+Lxa5M+52ZtZC/eOC7A7YCH4g==", "dev": true, "requires": { - "@lerna/child-process": "^3.3.0", - "npmlog": "^4.1.2", + "@lerna/child-process": "3.3.0", + "libnpm": "^2.0.1", "path-exists": "^3.0.0", "rimraf": "^2.6.2" } }, "@lerna/run": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@lerna/run/-/run-3.3.2.tgz", - "integrity": "sha512-cruwRGZZWnQ5I0M+AqcoT3Xpq2wj3135iVw4n59/Op6dZu50sMFXZNLiTTTZ15k8rTKjydcccJMdPSpTHbH7/A==", - "dev": true, - "requires": { - "@lerna/batch-packages": "^3.1.2", - "@lerna/command": "^3.3.0", - "@lerna/filter-options": "^3.3.2", - "@lerna/npm-run-script": "^3.3.0", - "@lerna/output": "^3.0.0", - "@lerna/run-parallel-batches": "^3.0.0", - "@lerna/validation-error": "^3.0.0", + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@lerna/run/-/run-3.10.6.tgz", + "integrity": "sha512-KS2lWbu/8WUUscQPi9U8sPO6yYpzf/0GmODjpruR1nRi1u/tuncdjTiG+hjGAeFC1BD7YktT9Za6imIpE8RXmA==", + "dev": true, + "requires": { + "@lerna/batch-packages": "3.10.6", + "@lerna/command": "3.10.6", + "@lerna/filter-options": "3.10.6", + "@lerna/npm-run-script": "3.10.0", + "@lerna/output": "3.6.0", + "@lerna/run-parallel-batches": "3.0.0", + "@lerna/timer": "3.5.0", + "@lerna/validation-error": "3.6.0", "p-map": "^1.2.0" } }, "@lerna/run-lifecycle": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-3.4.1.tgz", - "integrity": "sha512-N/hi2srM9A4BWEkXccP7vCEbf4MmIuALF00DTBMvc0A/ccItwUpl3XNuM7+ADDRK0mkwE3hDw89lJ3A7f8oUQw==", + "version": "3.10.5", + "resolved": "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-3.10.5.tgz", + "integrity": "sha512-YPmXviaxVlhcKM6IkDTIpTq24mxOuMCilo+MTr1RLoafgB9ZTmP2AHRiFt/sy14wOsq2Zqr0wJyj8KFlDYLTkA==", "dev": true, "requires": { - "@lerna/npm-conf": "^3.4.1", - "npm-lifecycle": "^2.0.0", - "npmlog": "^4.1.2" + "@lerna/npm-conf": "3.7.0", + "figgy-pudding": "^3.5.1", + "libnpm": "^2.0.1" } }, "@lerna/run-parallel-batches": { @@ -928,102 +982,67 @@ } }, "@lerna/symlink-binary": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-3.3.0.tgz", - "integrity": "sha512-zRo6CimhvH/VJqCFl9T4IC6syjpWyQIxEfO2sBhrapEcfwjtwbhoGgKwucsvt4rIpFazCw63jQ/AXMT27KUIHg==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-3.10.0.tgz", + "integrity": "sha512-6mQsG+iVjBo8cD8s24O+YgFrwDyUGfUQbK4ryalAXFHI817Zd4xlI3tjg3W99whCt6rt6D0s1fpf8eslMN6dSw==", "dev": true, "requires": { - "@lerna/create-symlink": "^3.3.0", - "@lerna/package": "^3.0.0", + "@lerna/create-symlink": "3.6.0", + "@lerna/package": "3.7.2", "fs-extra": "^7.0.0", - "p-map": "^1.2.0", - "read-pkg": "^3.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } + "p-map": "^1.2.0" } }, "@lerna/symlink-dependencies": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-3.3.0.tgz", - "integrity": "sha512-IRngSNCmuD5uBKVv23tHMvr7Mplti0lKHilFKcvhbvhAfu6m/Vclxhkfs/uLyHzG+DeRpl/9o86SQET3h4XDhg==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-3.10.0.tgz", + "integrity": "sha512-vGpg5ydwGgQCuWNX5y7CRL38mGpuLhf1GRq9wMm7IGwnctEsdSNqvvE+LDgqtwEZASu5+vffYUkL0VlFXl8uWA==", "dev": true, "requires": { - "@lerna/create-symlink": "^3.3.0", - "@lerna/resolve-symlink": "^3.3.0", - "@lerna/symlink-binary": "^3.3.0", + "@lerna/create-symlink": "3.6.0", + "@lerna/resolve-symlink": "3.6.0", + "@lerna/symlink-binary": "3.10.0", "fs-extra": "^7.0.0", "p-finally": "^1.0.0", "p-map": "^1.2.0", "p-map-series": "^1.0.0" } }, + "@lerna/timer": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@lerna/timer/-/timer-3.5.0.tgz", + "integrity": "sha512-TAb99hqQN6E3JBGtG9iyZNPq1/DbmqgBOeNrKtdJsGvIeX/NGLgUDWMrj2h04V4O+jpBFmSf6HIld6triKmxCA==", + "dev": true + }, "@lerna/validation-error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-3.0.0.tgz", - "integrity": "sha512-5wjkd2PszV0kWvH+EOKZJWlHEqCTTKrWsvfHnHhcUaKBe/NagPZFWs+0xlsDPZ3DJt5FNfbAPAnEBQ05zLirFA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-3.6.0.tgz", + "integrity": "sha512-MWltncGO5VgMS0QedTlZCjFUMF/evRjDMMHrtVorkIB2Cp5xy0rkKa8iDBG43qpUWeG1giwi58yUlETBcWfILw==", "dev": true, "requires": { - "npmlog": "^4.1.2" + "libnpm": "^2.0.1" } }, "@lerna/version": { - "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", - "@lerna/check-working-tree": "^3.3.0", - "@lerna/child-process": "^3.3.0", - "@lerna/collect-updates": "^3.3.2", - "@lerna/command": "^3.3.0", - "@lerna/conventional-commits": "^3.4.1", - "@lerna/output": "^3.0.0", - "@lerna/prompt": "^3.3.1", - "@lerna/run-lifecycle": "^3.4.1", - "@lerna/validation-error": "^3.0.0", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.10.8.tgz", + "integrity": "sha512-Iko2OkwwkjyK+tIklnH/72M/f54muSiRJurCsC3JqdM8aZaeDXeUrHmAyl7nQLfBlSsHfHyRax/ELkREmO5Tng==", + "dev": true, + "requires": { + "@lerna/batch-packages": "3.10.6", + "@lerna/check-working-tree": "3.10.0", + "@lerna/child-process": "3.3.0", + "@lerna/collect-updates": "3.10.1", + "@lerna/command": "3.10.6", + "@lerna/conventional-commits": "3.10.8", + "@lerna/output": "3.6.0", + "@lerna/prompt": "3.6.0", + "@lerna/run-lifecycle": "3.10.5", + "@lerna/validation-error": "3.6.0", "chalk": "^2.3.1", "dedent": "^0.7.0", + "libnpm": "^2.0.1", "minimatch": "^3.0.4", - "npmlog": "^4.1.2", "p-map": "^1.2.0", "p-pipe": "^1.2.0", "p-reduce": "^1.0.0", @@ -1042,12 +1061,12 @@ } }, "@lerna/write-log-file": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-3.0.0.tgz", - "integrity": "sha512-SfbPp29lMeEVOb/M16lJwn4nnx5y+TwCdd7Uom9umd7KcZP0NOvpnX0PHehdonl7TyHZ1Xx2maklYuCLbQrd/A==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-3.6.0.tgz", + "integrity": "sha512-OkLK99V6sYXsJsYg+O9wtiFS3z6eUPaiz2e6cXJt80mfIIdI1t2dnmyua0Ib5cZWExQvx2z6Y32Wlf0MnsoNsA==", "dev": true, "requires": { - "npmlog": "^4.1.2", + "libnpm": "^2.0.1", "write-file-atomic": "^2.3.0" } }, @@ -1062,9 +1081,9 @@ } }, "@nodelib/fs.stat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz", - "integrity": "sha512-yprFYuno9FtNsSHVlSWd+nRlmGoAbqbeCwOryP6sC/zoCjhpArcRMYp19EvpSUSizJAlsXEwJv+wcWS9XaXdMw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", "dev": true }, "@protobufjs/aspromise": { @@ -1152,9 +1171,9 @@ "dev": true }, "@types/aws-lambda": { - "version": "8.10.13", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.13.tgz", - "integrity": "sha512-a1sC60Bqll4N2RYnd4+XuynrVd8LO+uZrgwCVaAER0ldMQ00LRM4iTjU2ulPoQF6P5bHZK5hL/6IF9088VJhUA==", + "version": "8.10.18", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.18.tgz", + "integrity": "sha512-ePcNYGsgBJaF00+fG92e8zDcre7K6/X7wJeEyn7ICAwez9+NS47XEYlGrA0+udxo0jSuZVC8xg//PUiGNk43pA==", "dev": true }, "@types/bluebird": { @@ -1180,9 +1199,9 @@ } }, "@types/boom": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/boom/-/boom-7.2.0.tgz", - "integrity": "sha512-HonbGsHFbskh9zRAzA6tabcw18mCOsSEOL2ibGAuVqk6e7nElcRmWO5L4UfIHpDbWBWw+eZYFdsQ1+MEGgpcVA==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@types/boom/-/boom-7.2.1.tgz", + "integrity": "sha512-kOiap+kSa4DPoookJXQGQyKy1rjZ55tgfKAh9F0m1NUdukkcwVzpSnXPMH42a5L+U++ugdQlh/xFJu/WAdr1aw==", "dev": true }, "@types/caseless": { @@ -1192,9 +1211,9 @@ "dev": true }, "@types/catbox": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/catbox/-/catbox-10.0.1.tgz", - "integrity": "sha512-ECuJ+f5gGHiLeiE4RlE/xdqv/0JVDToegPV1aTb10tQStYa0Ycq2OJfQukDv3IFaw3B+CMV46jHc5bXe6QXEQg==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@types/catbox/-/catbox-10.0.4.tgz", + "integrity": "sha512-N+a5rLOsZyLYoqtCHYfw5sIcTJgm0SofLM8GSNNOwvZdFbuefMyHScJrN4xHtmcXwkY+aGXN7M2bQWtz63gJhA==", "dev": true }, "@types/connect": { @@ -1270,6 +1289,12 @@ } } }, + "@types/fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha512-mky/O83TXmGY39P1H9YbUpjV6l6voRYlufqfFCvel8l1phuy8HRjdWc1rrPuN53ITBJlbyMSV6z3niOySO5pgQ==", + "dev": true + }, "@types/fibers": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/fibers/-/fibers-0.0.30.tgz", @@ -1286,15 +1311,15 @@ } }, "@types/graphql": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-14.0.3.tgz", - "integrity": "sha512-TcFkpEjcQK7w8OcrQcd7iIBPjU0rdyi3ldj6d0iJ4PPSzbWqPBvXj9KSwO14hTOX2dm9RoiH7VuxksJLNYdXUQ==", + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-14.0.5.tgz", + "integrity": "sha512-bwGYLE0SRy5ZraC91dqI2bxbspfm10kyJ2Yjuvk4OjdGznh7fkoWW+xXZHfFydJaqu9syZi099cpiZw3GlPDiA==", "dev": true }, "@types/hapi": { - "version": "17.6.1", - "resolved": "https://registry.npmjs.org/@types/hapi/-/hapi-17.6.1.tgz", - "integrity": "sha512-Fk8j8INn131ws5kmqaMxYSlyg38iMjsQ2TbY2XMvWhPDfErFWBqTDJcB4jP8Hv68A5PaG9M1+nNWM3mHxTFg4Q==", + "version": "17.8.5", + "resolved": "https://registry.npmjs.org/@types/hapi/-/hapi-17.8.5.tgz", + "integrity": "sha512-3p42y+xIjbyvM7kZqyqtfoqf7wLvqFs5n8uitFqXXW9aAss4ikDGEMfjmnZ+nuCq5gdB2qjM8VYx7cf1MNjAXw==", "dev": true, "requires": { "@types/boom": "*", @@ -1322,15 +1347,15 @@ } }, "@types/jest": { - "version": "23.3.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.3.5.tgz", - "integrity": "sha512-3LI+vUC3Wju28vbjIjsTKakhMB8HC4l+tMz+Z8WRzVK+kmvezE5jcOvKtBpznWSI5KDLFo+FouUhpTKoekadCA==", + "version": "23.3.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.3.14.tgz", + "integrity": "sha512-Q5hTcfdudEL2yOmluA1zaSyPbzWPmJ3XfSWeP3RyoYvS9hnje1ZyagrZOuQ6+1nQC1Gw+7gap3pLNL3xL6UBug==", "dev": true }, "@types/joi": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@types/joi/-/joi-13.6.0.tgz", - "integrity": "sha512-vgxADik+Va7AcMZ89C7MVod3DlB3SWcj4dDI+XIceObGFfscehtjA0fFzBCZLoRr7ShjLVHNu1gMk+HtrG8WeQ==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@types/joi/-/joi-14.3.0.tgz", + "integrity": "sha512-dkNE2Ibbe1eXqXAg8dmAXAjhuH/IUacNp7aG5ymdcIiktJIXZMCF+aEHvZAWjNQD+Cy9BYwkJla+19J/vmMAOg==", "dev": true }, "@types/keygrip": { @@ -1382,9 +1407,9 @@ } }, "@types/koa-router": { - "version": "7.0.32", - "resolved": "https://registry.npmjs.org/@types/koa-router/-/koa-router-7.0.32.tgz", - "integrity": "sha512-PHaGjcpPNBak5OYiq3N1SIfhZctlIOcYFmC47HRKXqdlly78iwLJlxH5Tnjp4AsJ+ukZjrtbOrKox1smTS2OCQ==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/@types/koa-router/-/koa-router-7.0.39.tgz", + "integrity": "sha512-B8b2qyl7ijLFz7ocqkWva4IJ1n4uY5dK7jtGhQ+YHIGTpzcZr4ldDSjZi/xIM+ko3xSu8oqlGbzWxeVXK3WnNg==", "dev": true, "requires": { "@types/koa": "*" @@ -1399,11 +1424,20 @@ } }, "@types/lodash": { - "version": "4.14.117", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.117.tgz", - "integrity": "sha512-xyf2m6tRbz8qQKcxYZa7PA4SllYcay+eh25DN3jmNYY6gSTL7Htc/bttVdkqj2wfJGbeWlQiX8pIyJpKU+tubw==", + "version": "4.14.120", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.120.tgz", + "integrity": "sha512-jQ21kQ120mo+IrDs1nFNVm/AsdFxIx2+vZ347DbogHJPd/JzKNMOqU6HCYin1W6v8l5R9XSO2/e9cxmn7HAnVw==", "dev": true }, + "@types/lodash.sortby": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@types/lodash.sortby/-/lodash.sortby-4.7.4.tgz", + "integrity": "sha512-Byy/JXUl7VCKOjqk2XyOEa4kRp2UBuPPkdQpIwSi+54t3KDa1vkIRU+qFEoWZMLcMUbBq8+Iy8Ybri8AqFYLTA==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, "@types/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", @@ -1464,15 +1498,14 @@ } }, "@types/node": { - "version": "10.12.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.0.tgz", - "integrity": "sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ==", - "dev": true + "version": "10.12.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.21.tgz", + "integrity": "sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==" }, "@types/node-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.1.2.tgz", - "integrity": "sha512-XroxUzLpKuL+CVkQqXlffRkEPi4Gh3Oui/mWyS7ztKiyqVxiU+h3imCW5I2NQmde5jK+3q++36/Q96cyRWsweg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.1.5.tgz", + "integrity": "sha512-GuJUcxJt5VjAmkf23v5P2eBX6bVFp3ZVRFhuZhTLxAytlc8puxGDwdZAmQaV8l7A3mpm/xv14UjhSZIWt1UQAQ==", "dev": true, "requires": { "@types/node": "*" @@ -1490,18 +1523,18 @@ "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" }, "@types/redis": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.7.tgz", - "integrity": "sha512-ZMW8M5LRxU0D4u2GhnCEqJ1/mUJKSudlCWxeP1FRxfZQqr0Pb4tonPLzDEyRpC50uvEfAP3xOLjDuUOWi0QHCQ==", + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.10.tgz", + "integrity": "sha512-X0NeV3ivoif2SPsmuPhwTkKfcr1fDJlaJIOyxZ9/TCIEbvzMzmZlstqCZ5r7AOolbOJtAfvuGArNXMexYYH3ng==", "dev": true, "requires": { "@types/node": "*" } }, "@types/request": { - "version": "2.47.1", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.1.tgz", - "integrity": "sha512-TV3XLvDjQbIeVxJ1Z3oCTDk/KuYwwcNKVwz2YaT0F5u86Prgc4syDAp6P96rkTQQ4bIdh+VswQIC9zS6NjY7/g==", + "version": "2.48.1", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz", + "integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==", "dev": true, "requires": { "@types/caseless": "*", @@ -1548,9 +1581,9 @@ } }, "@types/tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-Set5ZdrAaKI/qHdFlVMgm/GsAv/wkXhSTuZFkJ+JI7HK+wIkIlOaUXSXieIvJ0+OvGIqtREFoE+NHJtEq0gtEw==", "dev": true }, "@types/type-is": { @@ -1566,16 +1599,15 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.1.tgz", "integrity": "sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q==", - "dev": true, "requires": { "@types/events": "*", "@types/node": "*" } }, "JSONStream": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.4.tgz", - "integrity": "sha512-Y7vfi3I5oMOYIr+WxV8NZxDSwcbNgzdKYsTNInmycOq9bUYwGg9ryu57Wg5NLmCjqdFPNUmpMBo3kSJN9tCbXg==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, "requires": { "jsonparse": "^1.2.0", @@ -1652,9 +1684,9 @@ } }, "agentkeepalive": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.1.tgz", - "integrity": "sha512-Cte/sTY9/XcygXjJ0q58v//SnEQ7ViWExKyJpLJlLqomDbQyMLh6Is4KuWJ/wmxzhiwkGRple7Gqv1zf6Syz5w==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", "dev": true, "requires": { "humanize-ms": "^1.2.1" @@ -1672,15 +1704,6 @@ "json-schema-traverse": "^0.3.0" } }, - "ammo": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ammo/-/ammo-3.0.1.tgz", - "integrity": "sha512-4UqoM8xQjwkQ78oiU4NbBK0UgYqeKMAKmwE4ec7Rz3rGU8ZEBFxzgF2sUYKOAlqIXExBDYLN6y1ShF5yQ4hwLQ==", - "dev": true, - "requires": { - "hoek": "5.x.x" - } - }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -2026,10 +2049,11 @@ "version": "file:packages/apollo-engine-reporting", "requires": { "apollo-engine-reporting-protobuf": "file:packages/apollo-engine-reporting-protobuf", + "apollo-graphql": "file:packages/apollo-graphql", + "apollo-server-core": "file:packages/apollo-server-core", "apollo-server-env": "file:packages/apollo-server-env", "async-retry": "^1.2.1", - "graphql-extensions": "file:packages/graphql-extensions", - "lodash": "^4.17.10" + "graphql-extensions": "file:packages/graphql-extensions" } }, "apollo-engine-reporting-protobuf": { @@ -2038,6 +2062,22 @@ "protobufjs": "^6.8.6" } }, + "apollo-env": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.3.0.tgz", + "integrity": "sha512-L3oDC+q+fpnGaV2ZrcyClrezUbzzwnxDDoTeTaxUfahrfyyV2vyLI7yzEbi0TP5U4Jbb7uqrJKVeaMFe4vVjJA==", + "requires": { + "core-js": "^3.0.0-beta.3", + "node-fetch": "^2.2.0" + }, + "dependencies": { + "core-js": { + "version": "3.0.0-beta.8", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.0-beta.8.tgz", + "integrity": "sha512-ex9wpitprNDuK6bPRljFW0z0IBatqtmqeuZ1HpcFcSkdOQSGNu3XdZSTshEuAIeYgLarHpw55P3SQlKAnXmpuQ==" + } + } + }, "apollo-fetch": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/apollo-fetch/-/apollo-fetch-0.7.0.tgz", @@ -2047,38 +2087,43 @@ "cross-fetch": "^1.0.0" } }, + "apollo-graphql": { + "version": "file:packages/apollo-graphql", + "requires": { + "lodash.sortby": "^4.7.0" + } + }, "apollo-link": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.3.tgz", - "integrity": "sha512-iL9yS2OfxYhigme5bpTbmRyC+Htt6tyo2fRMHT3K1XRL/C5IQDDz37OjpPy4ndx7WInSvfSZaaOTKFja9VWqSw==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.8.tgz", + "integrity": "sha512-lfzGRxhK9RmiH3HPFi7TIEBhhDY9M5a2ZDnllcfy5QDk7cCQHQ1WQArcw1FK0g1B+mV4Kl72DSrlvZHZJEolrA==", "requires": { - "apollo-utilities": "^1.0.0", - "zen-observable-ts": "^0.8.10" + "zen-observable-ts": "^0.8.15" } }, "apollo-link-http": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.5.tgz", - "integrity": "sha512-C5N6N/mRwmepvtzO27dgMEU3MMtRKSqcljBkYNZmWwH11BxkUQ5imBLPM3V4QJXNE7NFuAQAB5PeUd4ligivTQ==", + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.11.tgz", + "integrity": "sha512-wDG+I9UmpfaZRPIvTYBgkvqiCgmz6yWgvuzW/S24Q4r4Xrfe6sLpg2FmarhtdP+hdN+IXTLbFNCZ+Trgfpifow==", "dev": true, "requires": { - "apollo-link": "^1.2.3", - "apollo-link-http-common": "^0.2.5" + "apollo-link": "^1.2.8", + "apollo-link-http-common": "^0.2.10" } }, "apollo-link-http-common": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.5.tgz", - "integrity": "sha512-6FV1wr5AqAyJ64Em1dq5hhGgiyxZE383VJQmhIoDVc3MyNcFL92TkhxREOs4rnH2a9X2iJMko7nodHSGLC6d8w==", + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.10.tgz", + "integrity": "sha512-KY9nhpAurw3z48OIYV0sCZFXrzWp/wjECsveK+Q9GUhhSe1kEbbUjFfmi+qigg+iELgdp5V8ioRJhinl1vPojw==", "dev": true, "requires": { - "apollo-link": "^1.2.3" + "apollo-link": "^1.2.8" } }, "apollo-link-persisted-queries": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/apollo-link-persisted-queries/-/apollo-link-persisted-queries-0.2.1.tgz", - "integrity": "sha512-OxBum5e5vn8XBBEURXpoYstwcKNtK/p3K3bAQ5yGjj7IyzpLmBcKLzfjk3wAnEyJJYbOUXIvPg7XnxQbcIlNGA==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/apollo-link-persisted-queries/-/apollo-link-persisted-queries-0.2.2.tgz", + "integrity": "sha512-YL7XBu/5QsSbbYaWUXgm87T2Hn/2AQZk5Wr8CLXGDr3Wl3E/TRhBhKgQQTly9xhaTi7jgBO+AeIyTH5wCBHA9w==", "dev": true, "requires": { "apollo-link": "^1.2.1", @@ -2093,48 +2138,15 @@ "express": "^4.0.0", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0" - }, - "dependencies": { - "express": { - "version": "4.16.4", - "bundled": true, - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "statuses": { - "version": "1.4.0", - "bundled": true - } + } + }, + "apollo-server-azure-functions": { + "version": "file:packages/apollo-server-azure-functions", + "requires": { + "@apollographql/graphql-playground-html": "^1.6.6", + "apollo-server-core": "file:packages/apollo-server-core", + "apollo-server-env": "file:packages/apollo-server-env", + "graphql-tools": "^4.0.0" } }, "apollo-server-cache-memcached": { @@ -2157,7 +2169,29 @@ "apollo-server-caching": { "version": "file:packages/apollo-server-caching", "requires": { - "lru-cache": "^4.1.3" + "lru-cache": "^5.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "bundled": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.3", + "bundled": true + } + } + }, + "apollo-server-cloud-functions": { + "version": "file:packages/apollo-server-cloud-functions", + "requires": { + "@apollographql/graphql-playground-html": "^1.6.6", + "apollo-server-core": "file:packages/apollo-server-core", + "apollo-server-env": "file:packages/apollo-server-env", + "graphql-tools": "^4.0.0" } }, "apollo-server-cloudflare": { @@ -2170,7 +2204,8 @@ "apollo-server-core": { "version": "file:packages/apollo-server-core", "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", + "@apollographql/apollo-tools": "^0.3.0", + "@apollographql/graphql-playground-html": "^1.6.6", "@types/ws": "^6.0.0", "apollo-cache-control": "file:packages/apollo-cache-control", "apollo-datasource": "file:packages/apollo-datasource", @@ -2180,32 +2215,22 @@ "apollo-server-errors": "file:packages/apollo-server-errors", "apollo-server-plugin-base": "file:packages/apollo-server-plugin-base", "apollo-tracing": "file:packages/apollo-tracing", + "fast-json-stable-stringify": "^2.0.0", "graphql-extensions": "file:packages/graphql-extensions", "graphql-subscriptions": "^1.0.0", "graphql-tag": "^2.9.2", "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.2", "lodash": "^4.17.10", "subscriptions-transport-ws": "^0.9.11", "ws": "^6.0.0" }, "dependencies": { - "@types/node": { - "version": "10.11.7", - "bundled": true - }, - "@types/ws": { - "version": "6.0.1", - "bundled": true, - "requires": { - "@types/events": "*", - "@types/node": "*" - } - }, - "ws": { - "version": "6.1.0", + "@apollographql/apollo-tools": { + "version": "0.3.0", "bundled": true, "requires": { - "async-limiter": "~1.0.0" + "apollo-env": "0.3.0" } } } @@ -2223,12 +2248,11 @@ "apollo-server-express": { "version": "file:packages/apollo-server-express", "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "@types/accepts": "^1.3.5", "@types/body-parser": "1.17.0", "@types/cors": "^2.8.4", - "@types/express": "4.16.0", + "@types/express": "4.16.1", "accepts": "^1.3.5", "apollo-server-core": "file:packages/apollo-server-core", "body-parser": "^1.18.3", @@ -2236,13 +2260,23 @@ "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", "type-is": "^1.6.16" + }, + "dependencies": { + "@types/express": { + "version": "4.16.1", + "bundled": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + } } }, "apollo-server-hapi": { "version": "file:packages/apollo-server-hapi", "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:packages/apollo-server-core", "boom": "^7.1.0", @@ -2259,8 +2293,7 @@ "apollo-server-koa": { "version": "file:packages/apollo-server-koa", "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "@koa/cors": "^2.2.1", "@types/accepts": "^1.3.5", "@types/cors": "^2.8.4", @@ -2272,7 +2305,7 @@ "apollo-server-core": "file:packages/apollo-server-core", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", - "koa": "2.5.3", + "koa": "2.7.0", "koa-bodyparser": "^3.0.0", "koa-router": "^7.4.0", "type-is": "^1.6.16" @@ -2281,7 +2314,7 @@ "apollo-server-lambda": { "version": "file:packages/apollo-server-lambda", "requires": { - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "apollo-server-core": "file:packages/apollo-server-core", "apollo-server-env": "file:packages/apollo-server-env", "graphql-tools": "^4.0.0" @@ -2290,8 +2323,7 @@ "apollo-server-micro": { "version": "file:packages/apollo-server-micro", "requires": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:packages/apollo-server-core", "micro": "^9.3.2" @@ -2300,6 +2332,12 @@ "apollo-server-plugin-base": { "version": "file:packages/apollo-server-plugin-base" }, + "apollo-server-testing": { + "version": "file:packages/apollo-server-testing", + "requires": { + "apollo-server-core": "file:packages/apollo-server-core" + } + }, "apollo-tracing": { "version": "file:packages/apollo-tracing", "requires": { @@ -2355,7 +2393,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -2555,12 +2593,6 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, - "b64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/b64/-/b64-4.0.0.tgz", - "integrity": "sha512-EhmUQodKB0sdzPPrbIWbGqA5cQeTWxYrAgNeeT1rLZWtD3tbNTnphz8J4vkXI3cPgBNlXBjzEbzDzq0Nwi4f9A==", - "dev": true - }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -2935,12 +2967,19 @@ "tweetnacl": "^0.14.3" } }, - "big-time": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/big-time/-/big-time-2.0.1.tgz", - "integrity": "sha1-aMffjcMPl+lT8lpnp2rJcTwWyd4=", - "dev": true - }, + "bin-links": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-1.1.2.tgz", + "integrity": "sha512-8eEHVgYP03nILphilltWjeIjMbKyJo3wvp9K816pHbhP301ismzw15mxAAEVQ/USUwcP++1uNrbERbp8lOA6Fg==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "cmd-shim": "^2.0.2", + "gentle-fs": "^2.0.0", + "graceful-fs": "^4.1.11", + "write-file-atomic": "^2.3.0" + } + }, "block-stream": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", @@ -2981,16 +3020,6 @@ "hoek": "5.x.x" } }, - "bounce": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bounce/-/bounce-1.2.0.tgz", - "integrity": "sha512-8syCGe8B2/WC53118/F/tFy5aW00j+eaGPXmAUP7iBhxc+EBZZxS1vKelWyBCH6IqojgS2t1gF0glH30qAJKEw==", - "dev": true, - "requires": { - "boom": "7.x.x", - "hoek": "5.x.x" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3028,12 +3057,12 @@ } }, "bs-logger": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.5.tgz", - "integrity": "sha512-uFLE0LFMxrH8Z5Hd9QgivvRbrl/NFkOTHzGhlqQxsnmx5JBLrp4bc249afLL+GccyY/8hkcGi2LpVaOzaEY0nQ==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, "requires": { - "fast-json-stable-stringify": "^2.0.0" + "fast-json-stable-stringify": "2.x" } }, "bser": { @@ -3079,9 +3108,9 @@ "dev": true }, "byte-size": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-4.0.3.tgz", - "integrity": "sha512-JGC3EV2bCzJH/ENSh3afyJrH4vwxbHTuO5ljLoI5+2iJOcEpMgP8T782jH9b5qGxf2mSUIp1lfGnfKNrRHpvVg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-4.0.4.tgz", + "integrity": "sha512-82RPeneC6nqCdSwCX2hZUz3JPOvN5at/nTEw/CMf05Smu3Hrpo9Psb7LjN+k+XndNArG1EY8L4+BM3aTM4BCvw==", "dev": true }, "bytes": { @@ -3090,32 +3119,59 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "cacache": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.2.0.tgz", - "integrity": "sha512-IFWl6lfK6wSeYCHUXh+N1lY72UDrpyrYQJNIVQf48paDuWbv5RbAtJYf/4gUQFObTCHZwdZ5sI8Iw7nqwP6nlQ==", + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", "dev": true, "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "figgy-pudding": "^3.1.0", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.3", + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "move-concurrently": "^1.0.1", "promise-inflight": "^1.0.1", "rimraf": "^2.6.2", - "ssri": "^6.0.0", - "unique-filename": "^1.1.0", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", "y18n": "^4.0.0" }, "dependencies": { + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true } } }, @@ -3153,22 +3209,30 @@ "ylru": "^1.2.0" } }, - "call": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/call/-/call-5.0.1.tgz", - "integrity": "sha512-ollfFPSshiuYLp7AsrmpkQJ/PxCi6AzV81rCjBwWhyF2QGyUY/vPDMzoh4aUcWyucheRglG2LaS5qkIEfLRh6A==", - "dev": true, - "requires": { - "boom": "7.x.x", - "hoek": "5.x.x" - } - }, "call-me-maybe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", "dev": true }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", @@ -3207,28 +3271,6 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "catbox": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/catbox/-/catbox-10.0.3.tgz", - "integrity": "sha512-qwus6RnVctHXYwfxvvDwvlMWHwCjQdIpQQbtyHnRF0JpwmxbQJ/UIZi9y8O6DpphKCdfO9gpxgb2ne9ZDx39BQ==", - "dev": true, - "requires": { - "boom": "7.x.x", - "hoek": "5.x.x", - "joi": "13.x.x" - } - }, - "catbox-memory": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/catbox-memory/-/catbox-memory-3.1.2.tgz", - "integrity": "sha512-lhWtutLVhsq3Mucxk2McxBPPibJ34WcHuWFz3xqub9u9Ve/IQYpZv3ijLhQXfQped9DXozURiaq9O3aZpP91eg==", - "dev": true, - "requires": { - "big-time": "2.x.x", - "boom": "7.x.x", - "hoek": "5.x.x" - } - }, "chalk": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.0.tgz", @@ -3466,7 +3508,7 @@ "dependencies": { "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -3623,15 +3665,6 @@ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, - "content": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/content/-/content-4.0.5.tgz", - "integrity": "sha512-wDP6CTWDpwCf791fNxlCCkZGRkrNzSEU/8ju9Hnr3Uc5mF/gFR5W+fcoGm6zUSlVPdSXYn5pCbySADKj7YM4Cg==", - "dev": true, - "requires": { - "boom": "7.x.x" - } - }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -3643,9 +3676,9 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "conventional-changelog-angular": { - "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==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.2.tgz", + "integrity": "sha512-yx7m7lVrXmt4nKWQgWZqxSALEiAKZhOAcbxdUaU9575mB0CzXVbgrgpfSnSP7OqWDUTYGD0YVJ0MSRdyOPgAwA==", "dev": true, "requires": { "compare-func": "^1.3.1", @@ -3653,53 +3686,133 @@ } }, "conventional-changelog-core": { - "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==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.1.5.tgz", + "integrity": "sha512-iwqAotS4zk0wA4S84YY1JCUG7X3LxaRjJxuUo6GI4dZuIy243j5nOg/Ora35ExT4DOiw5dQbMMQvw2SUjh6moQ==", "dev": true, "requires": { - "conventional-changelog-writer": "^4.0.0", - "conventional-commits-parser": "^3.0.0", + "conventional-changelog-writer": "^4.0.2", + "conventional-commits-parser": "^3.0.1", "dateformat": "^3.0.0", "get-pkg-repo": "^1.0.0", - "git-raw-commits": "^2.0.0", + "git-raw-commits": "2.0.0", "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^2.0.0", + "git-semver-tags": "^2.0.2", "lodash": "^4.2.1", "normalize-package-data": "^2.3.5", "q": "^1.5.1", - "read-pkg": "^1.1.0", - "read-pkg-up": "^1.0.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", "through2": "^2.0.0" }, "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "^1.0.0", + "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true } } }, "conventional-changelog-preset-loader": { - "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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.0.2.tgz", + "integrity": "sha512-pBY+qnUoJPXAXXqVGwQaVmcye05xi6z231QM98wHWamGAmu/ghkBprQAwmF5bdmyobdVxiLhPY3PrCfSeUNzRQ==", "dev": true }, "conventional-changelog-writer": { - "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==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.2.tgz", + "integrity": "sha512-d8/FQY/fix2xXEBUhOo8u3DCbyEw3UOQgYHxLsPDw+wHUDma/GQGAGsGtoH876WyNs32fViHmTOUrgRKVLvBug==", "dev": true, "requires": { "compare-func": "^1.3.1", - "conventional-commits-filter": "^2.0.0", + "conventional-commits-filter": "^2.0.1", "dateformat": "^3.0.0", "handlebars": "^4.0.2", "json-stringify-safe": "^5.0.1", @@ -3711,9 +3824,9 @@ } }, "conventional-commits-filter": { - "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==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.1.tgz", + "integrity": "sha512-92OU8pz/977udhBjgPEbg3sbYzIxMDFTlQT97w7KdhR9igNqdJvy8smmedAAgn4tPiqseFloKkrVfbXCVd+E7A==", "dev": true, "requires": { "is-subset": "^0.1.1", @@ -3721,9 +3834,9 @@ } }, "conventional-commits-parser": { - "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==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.1.tgz", + "integrity": "sha512-P6U5UOvDeidUJ8ebHVDIoXzI7gMlQ1OF/id6oUvp8cnZvOXMt1n8nYl74Ey9YMn0uVQtxmCtjPQawpsssBWtGg==", "dev": true, "requires": { "JSONStream": "^1.0.4", @@ -3736,17 +3849,17 @@ } }, "conventional-recommended-bump": { - "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==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-4.0.4.tgz", + "integrity": "sha512-9mY5Yoblq+ZMqJpBzgS+RpSq+SUfP2miOR3H/NR9drGf08WCrY9B6HAGJZEm6+ThsVP917VHAahSOjM6k1vhPg==", "dev": true, "requires": { "concat-stream": "^1.6.0", - "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", + "conventional-changelog-preset-loader": "^2.0.2", + "conventional-commits-filter": "^2.0.1", + "conventional-commits-parser": "^3.0.1", + "git-raw-commits": "2.0.0", + "git-semver-tags": "^2.0.2", "meow": "^4.0.0", "q": "^1.5.1" } @@ -3777,12 +3890,12 @@ "dev": true }, "cookies": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.2.tgz", - "integrity": "sha512-J2JjH9T3PUNKPHknprxgCrCaZshIfxW2j49gq1E1CP5Micj1LppWAR2y9EHSQAzEiX84zOsScWNwUZ0b/ChlMw==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.3.tgz", + "integrity": "sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A==", "requires": { "depd": "~1.1.2", - "keygrip": "~1.0.2" + "keygrip": "~1.0.3" } }, "copy-concurrently": { @@ -3808,7 +3921,8 @@ "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==" + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3868,15 +3982,6 @@ "which": "^1.2.9" } }, - "cryptiles": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-4.1.2.tgz", - "integrity": "sha512-U2ALcoAHvA1oO2xOreyHvtkQ+IELqDG2WVWRI1GH/XEmmfGIOalnM5MU5Dd2ITyWfr3m6kNqXiy8XuYyd4wKJw==", - "dev": true, - "requires": { - "boom": "7.x.x" - } - }, "cssom": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", @@ -3955,9 +4060,9 @@ "integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==" }, "date-fns": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", - "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", "dev": true }, "dateformat": { @@ -4106,6 +4211,43 @@ } } }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + } + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4141,6 +4283,12 @@ "repeating": "^2.0.0" } }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -4218,14 +4366,14 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, "duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", + "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -4242,7 +4390,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -4443,18 +4591,42 @@ } }, "execa": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.9.0.tgz", - "integrity": "sha512-BbUMBiX4hqiHZUA5+JujIjNb6TyAlp2D5KLheMjMluwOuzcnylDL4AxZYLLn1n2AGB49eSWwyKvvEQoRpnAtmA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", + "cross-spawn": "^6.0.0", + "get-stream": "^4.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" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "get-stream": { + "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" + } + } } }, "exit": { @@ -4463,12 +4635,6 @@ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, "expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", @@ -4505,7 +4671,6 @@ "version": "4.16.4", "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", - "dev": true, "requires": { "accepts": "~1.3.5", "array-flatten": "1.1.1", @@ -4542,8 +4707,7 @@ "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" } } }, @@ -4618,16 +4782,16 @@ "dev": true }, "fast-glob": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.3.tgz", - "integrity": "sha512-NiX+JXjnx43RzvVFwRWfPKo4U+1BrK5pJPsHQdKMlLoFHrrGktXglQhHliSihWAq+m1z6fHk3uwGHrtRbS9vLA==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.6.tgz", + "integrity": "sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w==", "dev": true, "requires": { "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.0.1", + "@nodelib/fs.stat": "^1.1.2", "glob-parent": "^3.1.0", "is-glob": "^4.0.0", - "merge2": "^1.2.1", + "merge2": "^1.2.3", "micromatch": "^3.1.10" }, "dependencies": { @@ -4969,10 +5133,13 @@ "integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA=" }, "fibers": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fibers/-/fibers-3.0.0.tgz", - "integrity": "sha512-cAcOHOTbTMlcpNZvr94BNFsnBDBiEu9JP5MYcRLyl12HF/X0z3KvZyNzU9+BtI8lOIaV84PlDQJOKK3f5llJug==", - "dev": true + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fibers/-/fibers-3.1.1.tgz", + "integrity": "sha512-dl3Ukt08rHVQfY8xGD0ODwyjwrRALtaghuqGH2jByYX1wpY+nAnRQjJ6Dbqq0DnVgNVQ9yibObzbF4IlPyiwPw==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3" + } }, "figgy-pudding": { "version": "3.5.1", @@ -5039,6 +5206,12 @@ } } }, + "find-npm-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz", + "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==", + "dev": true + }, "find-parent-dir": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", @@ -5072,7 +5245,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -5177,7 +5350,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -5201,10 +5374,15 @@ } } }, + "fs-capacitor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-1.0.1.tgz", + "integrity": "sha512-XdZK0Q78WP29Vm3FGgJRhRhrBm51PagovzWtW2kJ3Q6cYJbGtZqWSGTSPwvtEkyjIirFd7b8Yes/dpOYjt4RRQ==" + }, "fs-extra": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", - "integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -5221,6 +5399,17 @@ "minipass": "^2.2.1" } }, + "fs-vacuum": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/fs-vacuum/-/fs-vacuum-1.2.10.tgz", + "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "path-is-inside": "^1.0.1", + "rimraf": "^2.5.2" + } + }, "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", @@ -5252,24 +5441,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "optional": true, "requires": { @@ -5279,12 +5472,14 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -5293,34 +5488,40 @@ }, "chownr": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, "requires": { @@ -5329,25 +5530,29 @@ }, "deep-extend": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, "requires": { @@ -5356,13 +5561,15 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, "requires": { @@ -5378,7 +5585,8 @@ }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "optional": true, "requires": { @@ -5392,13 +5600,15 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.21", - "bundled": true, + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "dev": true, "optional": true, "requires": { @@ -5407,7 +5617,8 @@ }, "ignore-walk": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, "requires": { @@ -5416,7 +5627,8 @@ }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, "requires": { @@ -5426,18 +5638,21 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "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" @@ -5445,13 +5660,15 @@ }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -5459,12 +5676,14 @@ }, "minimist": { "version": "0.0.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "minipass": { "version": "2.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", + "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, "requires": { "safe-buffer": "^5.1.1", @@ -5473,7 +5692,8 @@ }, "minizlib": { "version": "1.1.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "dev": true, "optional": true, "requires": { @@ -5482,7 +5702,8 @@ }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" @@ -5490,13 +5711,15 @@ }, "ms": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz", + "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "dev": true, "optional": true, "requires": { @@ -5507,7 +5730,8 @@ }, "node-pre-gyp": { "version": "0.10.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz", + "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "dev": true, "optional": true, "requires": { @@ -5525,7 +5749,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, "requires": { @@ -5535,13 +5760,15 @@ }, "npm-bundled": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", + "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.1.10", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", + "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "dev": true, "optional": true, "requires": { @@ -5551,7 +5778,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, "requires": { @@ -5563,18 +5791,21 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" @@ -5582,19 +5813,22 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, "requires": { @@ -5604,19 +5838,22 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.7", - "bundled": true, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "optional": true, "requires": { @@ -5628,7 +5865,8 @@ "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true } @@ -5636,7 +5874,8 @@ }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, "requires": { @@ -5651,7 +5890,8 @@ }, "rimraf": { "version": "2.6.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "optional": true, "requires": { @@ -5660,42 +5900,49 @@ }, "safe-buffer": { "version": "5.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.5.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "bundled": true, + "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", @@ -5705,7 +5952,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "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": { @@ -5714,7 +5962,8 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -5722,13 +5971,15 @@ }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz", + "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "dev": true, "optional": true, "requires": { @@ -5743,13 +5994,15 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "optional": true, "requires": { @@ -5758,12 +6011,14 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "yallist": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", "dev": true } } @@ -5785,6 +6040,17 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "g-status": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/g-status/-/g-status-2.0.2.tgz", + "integrity": "sha512-kQoE9qH+T1AHKgSSD0Hkv98bobE90ILQcXAF4wvGgsr7uFqNvwmh8j+Lq3l0RVt3E3HjSbv2B9biEGcEtpHLCA==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "matcher": "^1.0.0", + "simple-git": "^1.85.0" + } + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -5823,7 +6089,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -5833,11 +6099,27 @@ } }, "genfun": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/genfun/-/genfun-4.0.1.tgz", - "integrity": "sha1-7RAEHy5KfxsKOEZtF6XD4n3x38E=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", "dev": true }, + "gentle-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gentle-fs/-/gentle-fs-2.0.1.tgz", + "integrity": "sha512-cEng5+3fuARewXktTEGbwsktcldA+YsnUEaXZwcK/3pjSE1X9ObnTs+/8rYf8s+RnIcQm2D5x3rwpN7Zom8Bew==", + "dev": true, + "requires": { + "aproba": "^1.1.2", + "fs-vacuum": "^1.2.10", + "graceful-fs": "^4.1.11", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "path-is-inside": "^1.0.2", + "read-cmd-shim": "^1.0.1", + "slide": "^1.1.6" + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -5845,9 +6127,9 @@ "dev": true }, "get-own-enumerable-property-symbols": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-2.0.1.tgz", - "integrity": "sha512-TtY/sbOemiMKPRUDDanGCSgBYe7Mf0vbRsWnBZ+9yghpZ1MvcpSpuZFjHdEeY/LZjZy0vdLjS77L6HosisFiug==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", + "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==", "dev": true }, "get-pkg-repo": { @@ -5920,7 +6202,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -6016,9 +6298,9 @@ } }, "git-semver-tags": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.0.tgz", - "integrity": "sha512-lSgFc3zQTul31nFje2Q8XdNcTOI6B4I3mJRPCgFzHQQLfxfqdWTYzdtCaynkK5Xmb2wQlSJoKolhXJ1VhKROnQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.2.tgz", + "integrity": "sha512-34lMF7Yo1xEmsK2EkbArdoU79umpvm0MfzaDkSNYSJqtM5QLAVTPWgpiXSVI5o/O9EvZPSrP4Zvnec/CqhSd5w==", "dev": true, "requires": { "meow": "^4.0.0", @@ -6080,13 +6362,13 @@ "dev": true }, "globby": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz", - "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", + "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", "dev": true, "requires": { "array-union": "^1.0.1", - "dir-glob": "^2.0.0", + "dir-glob": "2.0.0", "fast-glob": "^2.0.2", "glob": "^7.1.2", "ignore": "^3.3.5", @@ -6109,16 +6391,28 @@ "dev": true }, "graphql": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.0.2.tgz", - "integrity": "sha512-gUC4YYsaiSJT1h40krG3J+USGlwhzNTXSb4IOZljn9ag5Tj+RkoXrWp+Kh7WyE3t1NCfab5kzCuxBIvOMERMXw==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.1.1.tgz", + "integrity": "sha512-C5zDzLqvfPAgTtP8AUPIt9keDabrdRAqSWjj2OPRKrKxI9Fb65I36s1uCs1UUBFnSWTdO7hyHi7z1ZbwKMKF6Q==", "dev": true, "requires": { "iterall": "^1.2.2" } }, "graphql-extensions": { - "version": "file:packages/graphql-extensions" + "version": "file:packages/graphql-extensions", + "requires": { + "@apollographql/apollo-tools": "^0.3.0" + }, + "dependencies": { + "@apollographql/apollo-tools": { + "version": "0.3.0", + "bundled": true, + "requires": { + "apollo-env": "0.3.0" + } + } + } }, "graphql-subscriptions": { "version": "1.0.0", @@ -6129,14 +6423,14 @@ } }, "graphql-tag": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.0.tgz", - "integrity": "sha512-9FD6cw976TLLf9WYIUPCaaTpniawIjHWZSwIRZSjrfufJamcXbVVYfN2TWvJYbw0Xf2JjYbl1/f2+wDnBVw3/w==" + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.1.tgz", + "integrity": "sha512-jApXqWBzNXQ8jYa/HLkZJaVw9jgwNqZkywa2zfFn16Iv1Zb7ELNHkJaXHR7Quvd5SIGsy6Ny7SUKATgnu05uEg==" }, "graphql-tools": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-4.0.2.tgz", - "integrity": "sha512-GijRFaHmSbyVphtTb23wd6wxXNkct9usiXHl2v4cOFNdUWe3Qz7VqoNyOwINlff2nf01xO+lCkhVlay0svJqfQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-4.0.4.tgz", + "integrity": "sha512-chF12etTIGVVGy3fCTJ1ivJX2KB7OSG4c6UOJQuqOHCmBQwTyNgCDuejZKvpYxNZiEx7bwIjrodDgDe9RIkjlw==", "requires": { "apollo-link": "^1.2.3", "apollo-utilities": "^1.0.1", @@ -6145,6 +6439,31 @@ "uuid": "^3.1.0" } }, + "graphql-upload": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-8.0.2.tgz", + "integrity": "sha512-u8a5tKPfJ0rU4MY+B3skabL8pEjMkm3tUzq25KBx6nT0yEWmqUO7Z5tdwvwYLFpkLwew94Gue0ARbZtar3gLTw==", + "requires": { + "busboy": "^0.2.14", + "fs-capacitor": "^1.0.0", + "http-errors": "^1.7.1", + "object-path": "^0.11.4" + }, + "dependencies": { + "http-errors": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.1.tgz", + "integrity": "sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + } + } + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -6171,31 +6490,315 @@ } } }, - "hapi": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/hapi/-/hapi-17.6.0.tgz", - "integrity": "sha512-GSHjE1hJExluAukrT/QuYSk96irmbYBDd3wOgywiHsPoR2QeKgDnIttD+dB6NbADEmSdb9MS5gTUIVq0uHTdkA==", - "dev": true, - "requires": { - "accept": "3.x.x", - "ammo": "3.x.x", - "boom": "7.x.x", - "bounce": "1.x.x", - "call": "5.x.x", - "catbox": "10.x.x", - "catbox-memory": "3.x.x", - "heavy": "6.x.x", - "hoek": "5.x.x", - "joi": "13.x.x", - "mimos": "4.x.x", - "podium": "3.x.x", - "shot": "4.x.x", - "statehood": "6.x.x", - "subtext": "6.x.x", - "teamwork": "3.x.x", - "topo": "3.x.x" - } - }, + "hapi": { + "version": "17.8.4", + "resolved": "https://registry.npmjs.org/hapi/-/hapi-17.8.4.tgz", + "integrity": "sha512-KmW0a36iXesMKKGNI4J/Ht37f2HQP6W6iKce8U5lxf+GtSH70zSh7q3D56Ro/a4Q5U5M60gSSU4esMVFxttANQ==", + "dev": true, + "requires": { + "accept": "3.x.x", + "ammo": "3.x.x", + "boom": "7.x.x", + "bounce": "1.x.x", + "call": "5.x.x", + "catbox": "10.x.x", + "catbox-memory": "3.x.x", + "heavy": "6.x.x", + "hoek": "6.x.x", + "joi": "14.x.x", + "mimos": "4.x.x", + "podium": "3.x.x", + "shot": "4.x.x", + "somever": "2.x.x", + "statehood": "6.x.x", + "subtext": "6.x.x", + "teamwork": "3.x.x", + "topo": "3.x.x" + }, + "dependencies": { + "accept": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/accept/-/accept-3.1.3.tgz", + "integrity": "sha512-OgOEAidVEOKPup+Gv2+2wdH2AgVKI9LxsJ4hicdJ6cY0faUuZdZoi56kkXWlHp9qicN1nWQLmW5ZRGk+SBS5xg==", + "dev": true, + "requires": { + "boom": "7.x.x", + "hoek": "6.x.x" + } + }, + "ammo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ammo/-/ammo-3.0.3.tgz", + "integrity": "sha512-vo76VJ44MkUBZL/BzpGXaKzMfroF4ZR6+haRuw9p+eSWfoNaH2AxVc8xmiEPC08jhzJSeM6w7/iMUGet8b4oBQ==", + "dev": true, + "requires": { + "hoek": "6.x.x" + } + }, + "b64": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/b64/-/b64-4.1.2.tgz", + "integrity": "sha512-+GUspBxlH3CJaxMUGUE1EBoWM6RKgWiYwUDal0qdf8m3ArnXNN1KzKVo5HOnE/FSq4HHyWf3TlHLsZI8PKQgrQ==", + "dev": true, + "requires": { + "hoek": "6.x.x" + } + }, + "big-time": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/big-time/-/big-time-2.0.1.tgz", + "integrity": "sha512-qtwYYoocwpiAxTXC5sIpB6nH5j6ckt+n/jhD7J5OEiFHnUZEFn0Xk8STUaE5s10LdazN/87bTDMe+fSihaW7Kg==", + "dev": true + }, + "boom": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/boom/-/boom-7.2.2.tgz", + "integrity": "sha512-IFUbOa8PS7xqmhIjpeStwT3d09hGkNYQ6aj2iELSTxcVs2u0aKn1NzhkdUQSzsRg1FVkj3uit3I6mXQCBixw+A==", + "dev": true, + "requires": { + "hoek": "6.x.x" + } + }, + "bounce": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bounce/-/bounce-1.2.2.tgz", + "integrity": "sha512-1LPcXg3fkGVhjdA/P3DcR5cDktKEYtDpruJv9Nhmy36RoYaoxZfC82Zr2JmS3vysDJKqMtP0qJw3/P6iisTASg==", + "dev": true, + "requires": { + "boom": "7.x.x", + "hoek": "6.x.x" + } + }, + "bourne": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bourne/-/bourne-1.1.1.tgz", + "integrity": "sha512-Ou0l3W8+n1FuTOoIfIrCk9oF9WVWc+9fKoAl67XQr9Ws0z7LgILRZ7qtc9xdT4BveSKtnYXfKPgn8pFAqeQRew==", + "dev": true + }, + "call": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/call/-/call-5.0.3.tgz", + "integrity": "sha512-eX16KHiAYXugbFu6VifstSdwH6aMuWWb4s0qvpq1nR1b+Sf+u68jjttg8ixDBEldPqBi30bDU35OJQWKeTLKxg==", + "dev": true, + "requires": { + "boom": "7.x.x", + "hoek": "6.x.x" + } + }, + "catbox": { + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/catbox/-/catbox-10.0.5.tgz", + "integrity": "sha512-5SpI/tEP3SiLE1qkkV+/hdVW48sHVBEbzPX4jBiwl6hsZh/gkl4bqfGLkvh7mjpMK5evJ0Rm/6NRlhF/Jsy9ow==", + "dev": true, + "requires": { + "boom": "7.x.x", + "hoek": "6.x.x", + "joi": "14.x.x" + } + }, + "catbox-memory": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/catbox-memory/-/catbox-memory-3.1.4.tgz", + "integrity": "sha512-1tDnll066au0HXBSDHS/YQ34MQ2omBsmnA9g/jseyq/M3m7UPrajVtPDZK/rXgikSC1dfjo9Pa+kQ1qcyG2d3g==", + "dev": true, + "requires": { + "big-time": "2.x.x", + "boom": "7.x.x", + "hoek": "6.x.x" + } + }, + "content": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/content/-/content-4.0.6.tgz", + "integrity": "sha512-lR9ND3dXiMdmsE84K6l02rMdgiBVmtYWu1Vr/gfSGHcIcznBj2QxmSdUgDuNFOA+G9yrb1IIWkZ7aKtB6hDGyA==", + "dev": true, + "requires": { + "boom": "7.x.x" + } + }, + "cryptiles": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-4.1.3.tgz", + "integrity": "sha512-gT9nyTMSUC1JnziQpPbxKGBbUg8VL7Zn2NB4E1cJYvuXdElHrwxrV9bmltZGDzet45zSDGyYceueke1TjynGzw==", + "dev": true, + "requires": { + "boom": "7.x.x" + } + }, + "heavy": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/heavy/-/heavy-6.1.2.tgz", + "integrity": "sha512-cJp884bqhiebNcEHydW0g6V1MUGYOXRPw9c7MFiHQnuGxtbWuSZpsbojwb2kxb3AA1/Rfs8CNiV9MMOF8pFRDg==", + "dev": true, + "requires": { + "boom": "7.x.x", + "hoek": "6.x.x", + "joi": "14.x.x" + } + }, + "hoek": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.0.1.tgz", + "integrity": "sha512-3PvUwBerLNVJiIVQdpkWF9F/M0ekgb2NPJWOhsE28RXSQPsY42YSnaJ8d1kZjcAz58TZ/Fk9Tw64xJsENFlJNw==", + "dev": true + }, + "iron": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/iron/-/iron-5.0.6.tgz", + "integrity": "sha512-zYUMOSkEXGBdwlV/AXF9zJC0aLuTJUKHkGeYS5I2g225M5i6SrxQyGJGhPgOR8BK1omL6N5i6TcwfsXbP8/Exw==", + "dev": true, + "requires": { + "b64": "4.x.x", + "boom": "7.x.x", + "cryptiles": "4.x.x", + "hoek": "6.x.x" + } + }, + "joi": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/joi/-/joi-14.0.4.tgz", + "integrity": "sha512-KUXRcinDUMMbtlOk7YLGHQvG73dLyf8bmgE+6sBTkdJbZpeGVGAlPXEHLiQBV7KinD/VLD5OA0EUgoTTfbRAJQ==", + "dev": true, + "requires": { + "hoek": "6.x.x", + "isemail": "3.x.x", + "topo": "3.x.x" + } + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mimos": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/mimos/-/mimos-4.0.2.tgz", + "integrity": "sha512-5XBsDqBqzSN88XPPH/TFpOalWOjHJM5Z2d3AMx/30iq+qXvYKd/8MPhqBwZDOLtoaIWInR3nLzMQcxfGK9djXA==", + "dev": true, + "requires": { + "hoek": "6.x.x", + "mime-db": "1.x.x" + } + }, + "nigel": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/nigel/-/nigel-3.0.4.tgz", + "integrity": "sha512-3SZCCS/duVDGxFpTROHEieC+itDo4UqL9JNUyQJv3rljudQbK6aqus5B4470OxhESPJLN93Qqxg16rH7DUjbfQ==", + "dev": true, + "requires": { + "hoek": "6.x.x", + "vise": "3.x.x" + } + }, + "pez": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pez/-/pez-4.0.5.tgz", + "integrity": "sha512-HvL8uiFIlkXbx/qw4B8jKDCWzo7Pnnd65Uvanf9OOCtb20MRcb9gtTVBf9NCnhETif1/nzbDHIjAWC/sUp7LIQ==", + "dev": true, + "requires": { + "b64": "4.x.x", + "boom": "7.x.x", + "content": "4.x.x", + "hoek": "6.x.x", + "nigel": "3.x.x" + } + }, + "podium": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/podium/-/podium-3.1.5.tgz", + "integrity": "sha512-+fAPmAj3d5fWKx5oSjQKeBIcl46/qZnGLhzyi/dJ/HzNiOpuxyX/Y4091LiVxZQ4ALdf/LCS7siV6ai5nNLlOg==", + "dev": true, + "requires": { + "hoek": "6.x.x", + "joi": "14.x.x" + } + }, + "shot": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/shot/-/shot-4.0.7.tgz", + "integrity": "sha512-RKaKAGKxJ11EjJl0cf2fYVSsd4KB5Cncb9J0v7w+0iIaXpxNqFWTYNDNhBX7f0XSyDrjOH9a4OWZ9Gp/ZML+ew==", + "dev": true, + "requires": { + "hoek": "6.x.x", + "joi": "14.x.x" + } + }, + "somever": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/somever/-/somever-2.0.0.tgz", + "integrity": "sha512-9JaIPP+HxwYGqCDqqK3tRaTqdtQHoK6Qy3IrXhIt2q5x8fs8RcfU7BMWlFTCOgFazK8p88zIv1tHQXvAwtXMyw==", + "dev": true, + "requires": { + "bounce": "1.x.x", + "hoek": "6.x.x" + } + }, + "statehood": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/statehood/-/statehood-6.0.9.tgz", + "integrity": "sha512-jbFg1+MYEqfC7ABAoWZoeF4cQUtp3LUvMDUGExL76cMmleBHG7I6xlZFsE8hRi7nEySIvutHmVlLmBe9+2R5LQ==", + "dev": true, + "requires": { + "boom": "7.x.x", + "bounce": "1.x.x", + "bourne": "1.x.x", + "cryptiles": "4.x.x", + "hoek": "6.x.x", + "iron": "5.x.x", + "joi": "14.x.x" + } + }, + "subtext": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/subtext/-/subtext-6.0.12.tgz", + "integrity": "sha512-yT1wCDWVgqvL9BIkWzWqgj5spUSYo/Enu09iUV8t2ZvHcr2tKGTGg2kc9tUpVEsdhp1ihsZeTAiDqh0TQciTPQ==", + "dev": true, + "requires": { + "boom": "7.x.x", + "bourne": "1.x.x", + "content": "4.x.x", + "hoek": "6.x.x", + "pez": "4.x.x", + "wreck": "14.x.x" + } + }, + "teamwork": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/teamwork/-/teamwork-3.0.2.tgz", + "integrity": "sha512-tpG01+9Qws/oGhMBiZN3BnB32gn5QeKY84AmLxxaCJw4mNeRzhEZ6jEj/vBhKerHD7Hgq9/vOZ58pyryYSn9gA==", + "dev": true + }, + "topo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", + "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", + "dev": true, + "requires": { + "hoek": "6.x.x" + } + }, + "vise": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vise/-/vise-3.0.1.tgz", + "integrity": "sha512-7BJNjsv2o83+E6AHAFSnjQF324UTgypsR/Sw/iFmLvr7RgJrEXF1xNBvb5LJfi+1FvWQXjJK4X41WMuHMeunPQ==", + "dev": true, + "requires": { + "hoek": "6.x.x" + } + }, + "wreck": { + "version": "14.1.3", + "resolved": "https://registry.npmjs.org/wreck/-/wreck-14.1.3.tgz", + "integrity": "sha512-hb/BUtjX3ObbwO3slCOLCenQ4EP8e+n8j6FmTne3VhEFp5XV1faSJojiyxVSvw34vgdeTG5baLTl4NmjwokLlw==", + "dev": true, + "requires": { + "boom": "7.x.x", + "hoek": "6.x.x" + } + } + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -6324,17 +6927,6 @@ "simple-lru-cache": "0.0.x" } }, - "heavy": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/heavy/-/heavy-6.1.0.tgz", - "integrity": "sha512-TKS9DC9NOTGulHQI31Lx+bmeWmNOstbJbGMiN3pX6bF+Zc2GKSpbbym4oasNnB6yPGkqJ9TQXXYDGohqNSJRxA==", - "dev": true, - "requires": { - "boom": "7.x.x", - "hoek": "5.x.x", - "joi": "13.x.x" - } - }, "hoek": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", @@ -6473,21 +7065,50 @@ } }, "husky": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/husky/-/husky-1.1.2.tgz", - "integrity": "sha512-9TdkUpBeEOjz0AnFdUN4i3w8kEbOsVs9/WSeJqWLq2OO6bcKQhVW64Zi+pVd/AMRLpN3QTINb6ZXiELczvdmqQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/husky/-/husky-1.3.1.tgz", + "integrity": "sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==", "dev": true, "requires": { - "cosmiconfig": "^5.0.6", - "execa": "^0.9.0", + "cosmiconfig": "^5.0.7", + "execa": "^1.0.0", "find-up": "^3.0.0", "get-stdin": "^6.0.0", - "is-ci": "^1.2.1", + "is-ci": "^2.0.0", "pkg-dir": "^3.0.0", "please-upgrade-node": "^3.1.1", "read-pkg": "^4.0.1", "run-node": "^1.0.0", "slash": "^2.0.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cosmiconfig": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.7.tgz", + "integrity": "sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0" + } + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + } } }, "iconv-lite": { @@ -6519,6 +7140,16 @@ "minimatch": "^3.0.4" } }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, "import-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", @@ -6638,24 +7269,67 @@ } }, "inquirer": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", - "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", + "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^3.0.0", + "external-editor": "^3.0.3", "figures": "^2.0.0", - "lodash": "^4.17.10", + "lodash": "^4.17.11", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rxjs": "^6.1.0", + "rxjs": "^6.4.0", "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", + "strip-ansi": "^5.0.0", "through": "^2.3.6" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", + "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "rxjs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", + "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "strip-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", + "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", + "dev": true, + "requires": { + "ansi-regex": "^4.0.0" + } + } } }, "invariant": { @@ -6684,17 +7358,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" }, - "iron": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/iron/-/iron-5.0.4.tgz", - "integrity": "sha512-7iQ5/xFMIYaNt9g2oiNiWdhrOTdRUMFaWENUd0KghxwPUhrIH8DUY8FEyLNTTzf75jaII+jMexLdY/2HfV61RQ==", - "dev": true, - "requires": { - "boom": "7.x.x", - "cryptiles": "4.x.x", - "hoek": "5.x.x" - } - }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -6864,6 +7527,30 @@ "symbol-observable": "^1.1.0" } }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -6971,9 +7658,9 @@ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "isemail": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.3.tgz", - "integrity": "sha512-5xbsG5wYADIcB+mfLsd+nst1V/D+I7EU7LEZPo2GOIMu4JzfcRs5yQoypP4avA7QtUqgxYLKBYNv4IdzBmbhdw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", + "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", "dev": true, "requires": { "punycode": "2.x.x" @@ -7595,17 +8282,6 @@ "merge-stream": "^1.0.1" } }, - "joi": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz", - "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==", - "dev": true, - "requires": { - "hoek": "5.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" - } - }, "js-sha256": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", @@ -7763,9 +8439,9 @@ "dev": true }, "koa": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.5.3.tgz", - "integrity": "sha512-U6rgy2kwlfO+3P1phAidDrRZpGfwcpHCxl33wFe+fHXalpzEshHGnMaSU7I/ZeDFpGRQkbQOYsXkXfUjn+AtdQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.7.0.tgz", + "integrity": "sha512-7ojD05s2Q+hFudF8tDLZ1CpCdVZw8JQELWSkcfG9bdtoTDzMmkRF6BQBU7JzIzCCOY3xd3tftiy/loHBUYaY2Q==", "requires": { "accepts": "^1.3.5", "cache-content-type": "^1.0.0", @@ -7933,28 +8609,28 @@ "dev": true }, "lerna": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.4.3.tgz", - "integrity": "sha512-tWq1LvpHqkyB+FaJCmkEweivr88yShDMmauofPVdh0M5gU1cVucszYnIgWafulKYu2LMQ3IfUMUU5Pp3+MvADQ==", - "dev": true, - "requires": { - "@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.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.3", - "@lerna/run": "^3.3.2", - "@lerna/version": "^3.4.1", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.10.8.tgz", + "integrity": "sha512-Ua5SkZnVk+gtplaw/IiXOckk9TEvNwNyTXJke5gkf0vxku809iRmI7RlI0mKFUjeweBs7AJDgBoD/A+vHst/UQ==", + "dev": true, + "requires": { + "@lerna/add": "3.10.6", + "@lerna/bootstrap": "3.10.6", + "@lerna/changed": "3.10.8", + "@lerna/clean": "3.10.6", + "@lerna/cli": "3.10.7", + "@lerna/create": "3.10.6", + "@lerna/diff": "3.10.6", + "@lerna/exec": "3.10.6", + "@lerna/import": "3.10.6", + "@lerna/init": "3.10.6", + "@lerna/link": "3.10.6", + "@lerna/list": "3.10.6", + "@lerna/publish": "3.10.8", + "@lerna/run": "3.10.6", + "@lerna/version": "3.10.8", "import-local": "^1.0.0", - "npmlog": "^4.1.2" + "libnpm": "^2.0.1" } }, "leven": { @@ -7973,15 +8649,205 @@ "type-check": "~0.3.2" } }, + "libnpm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/libnpm/-/libnpm-2.0.1.tgz", + "integrity": "sha512-qTKoxyJvpBxHZQB6k0AhSLajyXq9ZE/lUsZzuHAplr2Bpv9G+k4YuYlExYdUCeVRRGqcJt8hvkPh4tBwKoV98w==", + "dev": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.3", + "find-npm-prefix": "^1.0.2", + "libnpmaccess": "^3.0.1", + "libnpmconfig": "^1.2.1", + "libnpmhook": "^5.0.2", + "libnpmorg": "^1.0.0", + "libnpmpublish": "^1.1.0", + "libnpmsearch": "^2.0.0", + "libnpmteam": "^1.0.1", + "lock-verify": "^2.0.2", + "npm-lifecycle": "^2.1.0", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "npm-profile": "^4.0.1", + "npm-registry-fetch": "^3.8.0", + "npmlog": "^4.1.2", + "pacote": "^9.2.3", + "read-package-json": "^2.0.13", + "stringify-package": "^1.0.0" + }, + "dependencies": { + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + } + } + }, "libnpmaccess": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-3.0.0.tgz", - "integrity": "sha512-SiE4AZAzMpD7pmmXHfgD7rof8QIQGoKaeyAS8exgx2CKA6tzRTbRljq1xM4Tgj8/tIg+KBJPJWkR0ifqKT3irQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-3.0.1.tgz", + "integrity": "sha512-RlZ7PNarCBt+XbnP7R6PoVgOq9t+kou5rvhaInoNibhPO7eMlRfS0B8yjatgn2yaHIwWNyoJDolC/6Lc5L/IQA==", + "dev": true, + "requires": { + "aproba": "^2.0.0", + "get-stream": "^4.0.0", + "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^3.8.0" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "get-stream": { + "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" + } + } + } + }, + "libnpmconfig": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz", + "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "find-up": "^3.0.0", + "ini": "^1.3.5" + } + }, + "libnpmhook": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/libnpmhook/-/libnpmhook-5.0.2.tgz", + "integrity": "sha512-vLenmdFWhRfnnZiNFPNMog6CK7Ujofy2TWiM2CrpZUjBRIhHkJeDaAbJdYCT6W4lcHtyrJR8yXW8KFyq6UAp1g==", + "dev": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^3.8.0" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "get-stream": { + "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" + } + } + } + }, + "libnpmorg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/libnpmorg/-/libnpmorg-1.0.0.tgz", + "integrity": "sha512-o+4eVJBoDGMgRwh2lJY0a8pRV2c/tQM/SxlqXezjcAg26Qe9jigYVs+Xk0vvlYDWCDhP0g74J8UwWeAgsB7gGw==", + "dev": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^3.8.0" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "get-stream": { + "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" + } + } + } + }, + "libnpmpublish": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-1.1.1.tgz", + "integrity": "sha512-nefbvJd/wY38zdt+b9SHL6171vqBrMtZ56Gsgfd0duEKb/pB8rDT4/ObUQLrHz1tOfht1flt2zM+UGaemzAG5g==", "dev": true, "requires": { "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", "get-stream": "^4.0.0", + "lodash.clonedeep": "^4.5.0", + "normalize-package-data": "^2.4.0", "npm-package-arg": "^6.1.0", + "npm-registry-fetch": "^3.8.0", + "semver": "^5.5.1", + "ssri": "^6.0.1" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "get-stream": { + "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" + } + } + } + }, + "libnpmsearch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/libnpmsearch/-/libnpmsearch-2.0.0.tgz", + "integrity": "sha512-vd+JWbTGzOSfiOc+72MU6y7WqmBXn49egCCrIXp27iE/88bX8EpG64ST1blWQI1bSMUr9l1AKPMVsqa2tS5KWA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "npm-registry-fetch": "^3.8.0" + }, + "dependencies": { + "get-stream": { + "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" + } + } + } + }, + "libnpmteam": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/libnpmteam/-/libnpmteam-1.0.1.tgz", + "integrity": "sha512-gDdrflKFCX7TNwOMX1snWojCoDE5LoRWcfOC0C/fqF7mBq8Uz9zWAX4B2RllYETNO7pBupBaSyBDkTAC15cAMg==", + "dev": true, + "requires": { + "aproba": "^2.0.0", + "figgy-pudding": "^3.4.1", + "get-stream": "^4.0.0", "npm-registry-fetch": "^3.8.0" }, "dependencies": { @@ -8003,22 +8869,24 @@ } }, "lint-staged": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-7.2.2.tgz", - "integrity": "sha512-BWT3kx242hq5oaKJ8QiazPeHwJnEXImvjmgZfjljMI5HX6RrTxI3cTJXywre6GNafMONCD/suFnEiFmC69Gscg==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.3.tgz", + "integrity": "sha512-6TGkikL1B+6mIOuSNq2TV6oP21IhPMnV8q0cf9oYZ296ArTVNcbFh1l1pfVOHHbBIYLlziWNsQ2q45/ffmJ4AA==", "dev": true, "requires": { + "@iamstarkov/listr-update-renderer": "0.4.1", "chalk": "^2.3.1", "commander": "^2.14.1", "cosmiconfig": "^5.0.2", "debug": "^3.1.0", "dedent": "^0.7.0", - "execa": "^0.9.0", + "del": "^3.0.0", + "execa": "^1.0.0", "find-parent-dir": "^0.3.0", + "g-status": "^2.0.2", "is-glob": "^4.0.0", "is-windows": "^1.0.2", - "jest-validate": "^23.5.0", - "listr": "^0.14.1", + "listr": "^0.14.2", "lodash": "^4.17.5", "log-symbols": "^2.2.0", "micromatch": "^3.1.8", @@ -8027,9 +8895,10 @@ "path-is-inside": "^1.0.2", "pify": "^3.0.0", "please-upgrade-node": "^3.0.2", - "staged-git-files": "1.1.1", + "staged-git-files": "1.1.2", "string-argv": "^0.0.2", - "stringify-object": "^3.2.2" + "stringify-object": "^3.2.2", + "yup": "^0.26.10" }, "dependencies": { "arr-diff": { @@ -8354,9 +9223,9 @@ } }, "listr": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.2.tgz", - "integrity": "sha512-vmaNJ1KlGuGWShHI35X/F8r9xxS0VTHh9GejVXwSN20fG5xpq3Jh4bJbnumoT6q5EDM/8/YP1z3YMtQbFmhuXw==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", + "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", "dev": true, "requires": { "@samverschueren/stream-to-observable": "^0.3.0", @@ -8364,10 +9233,18 @@ "is-promise": "^2.1.0", "is-stream": "^1.1.0", "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.4.0", - "listr-verbose-renderer": "^0.4.0", - "p-map": "^1.1.1", - "rxjs": "^6.1.0" + "listr-update-renderer": "^0.5.0", + "listr-verbose-renderer": "^0.5.0", + "p-map": "^2.0.0", + "rxjs": "^6.3.3" + }, + "dependencies": { + "p-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.0.0.tgz", + "integrity": "sha512-GO107XdrSUmtHxVoi60qc9tUl/KkNKm+X2CF4P9amalpGxv5YqVPJNfSb0wcA+syCopkZvYYIzW8OVTQW59x/w==", + "dev": true + } } }, "listr-silent-renderer": { @@ -8377,9 +9254,9 @@ "dev": true }, "listr-update-renderer": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz", - "integrity": "sha1-NE2YDaLKLosUW6MFkI8yrj9MyKc=", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", + "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", "dev": true, "requires": { "chalk": "^1.1.3", @@ -8388,7 +9265,7 @@ "figures": "^1.7.0", "indent-string": "^3.0.0", "log-symbols": "^1.0.2", - "log-update": "^1.0.2", + "log-update": "^2.3.0", "strip-ansi": "^3.0.1" }, "dependencies": { @@ -8448,91 +9325,33 @@ } }, "listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", + "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", "dev": true, "requires": { - "chalk": "^1.1.3", - "cli-cursor": "^1.0.2", + "chalk": "^2.4.1", + "cli-cursor": "^2.1.0", "date-fns": "^1.27.2", - "figures": "^1.7.0" + "figures": "^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=", - "dev": true - }, "chalk": { - "version": "1.1.3", - "resolved": "https://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" - } - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { + "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://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": "^5.3.0" } - }, - "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 } } }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -8570,6 +9389,16 @@ "path-exists": "^3.0.0" } }, + "lock-verify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.0.2.tgz", + "integrity": "sha512-QNVwK0EGZBS4R3YQ7F1Ox8p41Po9VGl2QG/2GsuvTbkJZYSsPeWHKMbbH6iZMCHWSMww5nrJroZYnGzI4cePuw==", + "dev": true, + "requires": { + "npm-package-arg": "^5.1.2 || 6", + "semver": "^5.4.1" + } + }, "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", @@ -8581,11 +9410,16 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" }, "lodash.template": { "version": "4.4.0", @@ -8616,44 +9450,24 @@ } }, "log-update": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", - "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", "dev": true, "requires": { - "ansi-escapes": "^1.0.0", - "cli-cursor": "^1.0.2" + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" }, "dependencies": { - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "dev": true - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", "dev": true, "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" } } } @@ -8686,6 +9500,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -8743,9 +9558,9 @@ } }, "map-age-cleaner": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz", - "integrity": "sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, "requires": { "p-defer": "^1.0.0" @@ -8772,6 +9587,15 @@ "object-visit": "^1.0.0" } }, + "matcher": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-1.1.1.tgz", + "integrity": "sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.4" + } + }, "math-random": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", @@ -8857,7 +9681,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -8924,9 +9748,9 @@ } }, "merge": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", - "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", "dev": true }, "merge-descriptors": { @@ -8976,15 +9800,15 @@ } }, "merge2": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.2.tgz", - "integrity": "sha512-bgM8twH86rWni21thii6WCMQMRMmwqqdW3sGWi9IipnVAszdLXRjwDwAnyrVXo6DuP3AjRMMttZKUB48QWIFGg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", + "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==", "dev": true }, "meteor-promise": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/meteor-promise/-/meteor-promise-0.8.6.tgz", - "integrity": "sha512-HP6tOr67z/9XU2Dr0F2SSr8WRTuE23AG9Dj578DCJPEYHs67OLKBviU8A8rwvbwMD7Lu2+Of+yAMz2Wd8r4yxg==", + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/meteor-promise/-/meteor-promise-0.8.7.tgz", + "integrity": "sha512-wAu9XI3+35H1YyhTnnk5QXCfdrho5n/0Ltj65DhsUwtFCuktV7hre92FjtJgBbs+RHj5/iZ8Zcbml40EGLcFTQ==", "dev": true }, "methods": { @@ -9088,16 +9912,6 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, - "mimos": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimos/-/mimos-4.0.0.tgz", - "integrity": "sha512-JvlvRLqGIlk+AYypWrbrDmhsM+6JVx/xBM5S3AMwTBz1trPCEoPN/swO2L4Wu653fL7oJdgk8DMQyG/Gq3JkZg==", - "dev": true, - "requires": { - "hoek": "5.x.x", - "mime-db": "1.x.x" - } - }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -9130,9 +9944,9 @@ } }, "minipass": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.4.tgz", - "integrity": "sha512-mlouk1OHlaUE8Odt1drMtG1bAJA4ZA6B/ehysgV0LUIrDHdKgo1KorZq3pK0b/7Z7LJIQ12MNM6aC+Tn6lUZ5w==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -9140,17 +9954,17 @@ }, "dependencies": { "yallist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", - "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true } } }, "minizlib": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.1.tgz", - "integrity": "sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "requires": { "minipass": "^2.2.1" @@ -9340,20 +10154,10 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "nigel": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/nigel/-/nigel-3.0.1.tgz", - "integrity": "sha512-kCVtUG9JyD//tsYrZY+/Y+2gUrANVSba8y23QkM5Znx0FOxlnl9Z4OVPBODmstKWTOvigfTO+Va1VPOu3eWSOQ==", - "dev": true, - "requires": { - "hoek": "5.x.x", - "vise": "3.x.x" - } - }, "node-fetch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz", - "integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", + "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==" }, "node-fetch-npm": { "version": "2.0.2", @@ -9472,6 +10276,12 @@ } } }, + "npm-logical-tree": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz", + "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg==", + "dev": true + }, "npm-package-arg": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", @@ -9485,9 +10295,9 @@ } }, "npm-packlist": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz", - "integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.2.0.tgz", + "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==", "dev": true, "requires": { "ignore-walk": "^3.0.1", @@ -9504,19 +10314,31 @@ } }, "npm-pick-manifest": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.1.0.tgz", - "integrity": "sha512-q9zLP8cTr8xKPmMZN3naxp1k/NxVFsjxN6uWuO1tiw9gxg7wZWQ/b5UTfzD0ANw2q1lQxdLKTeCCksq+bPSgbQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", + "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", "dev": true, "requires": { + "figgy-pudding": "^3.5.1", "npm-package-arg": "^6.0.0", "semver": "^5.4.1" } }, + "npm-profile": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.1.tgz", + "integrity": "sha512-NQ1I/1Q7YRtHZXkcuU1/IyHeLy6pd+ScKg4+DQHdfsm769TGq6HPrkbuNJVJS4zwE+0mvvmeULzQdWn2L2EsVA==", + "dev": true, + "requires": { + "aproba": "^1.1.2 || 2", + "figgy-pudding": "^3.4.1", + "npm-registry-fetch": "^3.8.0" + } + }, "npm-registry-fetch": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.8.0.tgz", - "integrity": "sha512-hrw8UMD+Nob3Kl3h8Z/YjmKamb1gf7D1ZZch2otrIXM3uFLB5vjEY6DhMlq80z/zZet6eETLbOXcuQudCB3Zpw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz", + "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==", "dev": true, "requires": { "JSONStream": "^1.3.4", @@ -9793,9 +10615,9 @@ "dev": true }, "p-is-promise": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", "dev": true }, "p-limit": { @@ -9859,59 +10681,83 @@ } }, "pacote": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.1.0.tgz", - "integrity": "sha512-AFXaSWhOtQf3jHqEvg+ZYH/dfT8TKq6TKspJ4qEFwVVuh5aGvMIk6SNF8vqfzz+cBceDIs9drOcpBbrPai7i+g==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.4.1.tgz", + "integrity": "sha512-YKSRsQqmeHxgra0KCdWA2FtVxDPUlBiCdmew+mSe44pzlx5t1ViRMWiQg18T+DREA+vSqYfKzynaToFR4hcKHw==", "dev": true, "requires": { - "bluebird": "^3.5.1", - "cacache": "^11.0.2", - "figgy-pudding": "^3.2.1", - "get-stream": "^3.0.0", - "glob": "^7.1.2", - "lru-cache": "^4.1.3", + "bluebird": "^3.5.3", + "cacache": "^11.3.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "lru-cache": "^5.1.1", "make-fetch-happen": "^4.0.1", "minimatch": "^3.0.4", - "minipass": "^2.3.3", + "minipass": "^2.3.5", "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "normalize-package-data": "^2.4.0", "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.10", - "npm-pick-manifest": "^2.1.0", - "npm-registry-fetch": "^3.0.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^2.2.3", + "npm-registry-fetch": "^3.8.0", "osenv": "^0.1.5", "promise-inflight": "^1.0.1", "promise-retry": "^1.1.1", - "protoduck": "^5.0.0", + "protoduck": "^5.0.1", "rimraf": "^2.6.2", "safe-buffer": "^5.1.2", - "semver": "^5.5.0", - "ssri": "^6.0.0", - "tar": "^4.4.3", - "unique-filename": "^1.1.0", - "which": "^1.3.0" + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.8", + "unique-filename": "^1.1.1", + "which": "^1.3.1" }, "dependencies": { + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + }, + "get-stream": { + "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" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, "tar": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz", - "integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==", + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "requires": { - "chownr": "^1.0.1", + "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.3", - "minizlib": "^1.1.0", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", "yallist": "^3.0.2" } }, "yallist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", - "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true } } @@ -9935,7 +10781,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -10070,19 +10916,6 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, - "pez": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pez/-/pez-4.0.2.tgz", - "integrity": "sha512-HuPxmGxHsEFPWhdkwBs2gIrHhFqktIxMtudISTFN95RQ85ZZAOl8Ki6u3nnN/X8OUaGlIGldk/l8p2IR4/i76w==", - "dev": true, - "requires": { - "b64": "4.x.x", - "boom": "7.x.x", - "content": "4.x.x", - "hoek": "5.x.x", - "nigel": "3.x.x" - } - }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -10128,16 +10961,6 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, - "podium": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/podium/-/podium-3.1.2.tgz", - "integrity": "sha512-18VrjJAduIdPv7d9zWsfmKxTj3cQTYC5Pv5gtKxcWujYBpGbV+mhNSPYhlHW5xeWoazYyKfB9FEsPT12r5rY1A==", - "dev": true, - "requires": { - "hoek": "5.x.x", - "joi": "13.x.x" - } - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -10157,9 +10980,9 @@ "dev": true }, "prettier": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.3.tgz", - "integrity": "sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==", + "version": "1.16.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", + "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==", "dev": true }, "prettier-check": { @@ -10301,12 +11124,12 @@ } }, "protoduck": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.0.tgz", - "integrity": "sha512-agsGWD8/RZrS4ga6v82Fxb0RHIS2RZnbsSue6A9/MBRhB/jcqOANAMNrqM9900b8duj+Gx+T/JMy5IowDoO/hQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", "dev": true, "requires": { - "genfun": "^4.0.1" + "genfun": "^5.0.0" } }, "proxy-addr": { @@ -10321,7 +11144,8 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true }, "psl": { "version": "1.1.29", @@ -10606,9 +11430,9 @@ "integrity": "sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw==" }, "redis-mock": { - "version": "0.33.0", - "resolved": "https://registry.npmjs.org/redis-mock/-/redis-mock-0.33.0.tgz", - "integrity": "sha512-Ncyz+IzlQ944Yq9COSImiXPc/UFHMHf6XXS820Mh4NDWpSVaCW3QhkqhobVDGY2+FzysimgXqsAyu9lApycHnw==", + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/redis-mock/-/redis-mock-0.43.0.tgz", + "integrity": "sha512-ayNlCxbeaVp16U5HU220C24lFk7TEzW8hL4RE5SXv/GeKq+P4ek1QqKKbu0K2QkGRdO2bvHE2ymJ/pbBLqyUww==", "dev": true }, "redis-parser": { @@ -10619,7 +11443,8 @@ "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==" + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", + "dev": true }, "regex-cache": { "version": "0.4.4", @@ -10827,9 +11652,9 @@ } }, "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==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", + "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -11264,22 +12089,38 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, - "shot": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/shot/-/shot-4.0.5.tgz", - "integrity": "sha1-x+dFXRHWD2ts08Q+FaO0McF+VWY=", - "dev": true, - "requires": { - "hoek": "5.x.x", - "joi": "13.x.x" - } - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-git": { + "version": "1.107.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.107.0.tgz", + "integrity": "sha512-t4OK1JRlp4ayKRfcW6owrWcRVLyHRUlhGd0uN6ZZTqfDq8a5XpcUdOKiGRNobHEuMtNqzp0vcJNvhYWwh5PsQA==", + "dev": true, + "requires": { + "debug": "^4.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "simple-lru-cache": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/simple-lru-cache/-/simple-lru-cache-0.0.2.tgz", @@ -11310,9 +12151,9 @@ "dev": true }, "smart-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz", - "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", + "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", "dev": true }, "snapdragon": { @@ -11424,13 +12265,13 @@ } }, "socks": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.1.tgz", - "integrity": "sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz", + "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==", "dev": true, "requires": { "ip": "^1.1.5", - "smart-buffer": "^4.0.1" + "smart-buffer": "4.0.2" } }, "socks-proxy-agent": { @@ -11584,25 +12425,11 @@ "dev": true }, "staged-git-files": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-1.1.1.tgz", - "integrity": "sha512-H89UNKr1rQJvI1c/PIR3kiAMBV23yvR7LItZiV74HWZwzt7f3YHuujJ9nJZlt58WlFox7XQsOahexwk7nTe69A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-1.1.2.tgz", + "integrity": "sha512-0Eyrk6uXW6tg9PYkhi/V/J4zHp33aNyi2hOCmhFLqLTIhbgqWn5jlSzI+IU0VqrZq6+DbHcabQl/WP6P3BG0QA==", "dev": true }, - "statehood": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/statehood/-/statehood-6.0.6.tgz", - "integrity": "sha512-jR45n5ZMAkasw0xoE9j9TuLmJv4Sa3AkXe+6yIFT6a07kXYHgSbuD2OVGECdZGFxTmvNqLwL1iRIgvq6O6rq+A==", - "dev": true, - "requires": { - "boom": "7.x.x", - "bounce": "1.x.x", - "cryptiles": "4.x.x", - "hoek": "5.x.x", - "iron": "5.x.x", - "joi": "13.x.x" - } - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -11688,16 +12515,22 @@ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, "stringify-object": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.2.2.tgz", - "integrity": "sha512-O696NF21oLiDy8PhpWu8AEqoZHw++QW6mUv0UvKZe8gWSdSvMXkiLufK7OmnP27Dro4GU5kb9U7JIO0mBuCRQg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", "dev": true, "requires": { - "get-own-enumerable-property-symbols": "^2.0.1", + "get-own-enumerable-property-symbols": "^3.0.0", "is-obj": "^1.0.1", "is-regexp": "^1.0.0" } }, + "stringify-package": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.0.tgz", + "integrity": "sha512-JIQqiWmLiEozOC0b0BtxZ/AOUtdUZHCBPgqIZ2kSJJqGwgb9neo44XdTHUC4HZSGqi03hOeB7W/E8rAlKnGe9g==", + "dev": true + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -11737,12 +12570,11 @@ "dev": true }, "strong-log-transformer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.0.0.tgz", - "integrity": "sha512-FQmNqAXJgOX8ygOcvPLlGWBNT41mvNJ9ALoYf0GTwVt9t30mGTqpmp/oJx5gLcu52DXK10kS7dVWhx8aPXDTlg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", + "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", "dev": true, "requires": { - "byline": "^5.0.0", "duplexer": "^0.1.1", "minimist": "^1.2.0", "through": "^2.3.4" @@ -11750,7 +12582,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -11778,19 +12610,6 @@ } } }, - "subtext": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/subtext/-/subtext-6.0.7.tgz", - "integrity": "sha512-IcJUvRjeR+NB437Iq+LORFNJW4L6Knqkj3oQrBrkdhIaS2VKJvx/9aYEq7vi+PEx5/OuehOL/40SkSZotLi/MA==", - "dev": true, - "requires": { - "boom": "7.x.x", - "content": "4.x.x", - "hoek": "5.x.x", - "pez": "4.x.x", - "wreck": "14.x.x" - } - }, "superagent": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", @@ -11857,9 +12676,9 @@ } }, "supertest": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.3.0.tgz", - "integrity": "sha512-dMQSzYdaZRSANH5LL8kX3UpgK9G1LRh/jnggs/TI0W2Sz7rkMx9Y48uia3K9NgcaWEV28tYkBnXE4tiFC77ygQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.4.2.tgz", + "integrity": "sha512-WZWbwceHUo2P36RoEIdXvmqfs47idNNZjCuJOqDz6rvtkk8ym56aU5oglORCpPeXGxT7l9rkJ41+O1lffQXYSA==", "dev": true, "requires": { "methods": "^1.1.2", @@ -11902,12 +12721,6 @@ "inherits": "2" } }, - "teamwork": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/teamwork/-/teamwork-3.0.1.tgz", - "integrity": "sha512-hEkJIpDOfOYe9NYaLFk00zQbzZeKNCY8T2pRH3I13Y1mJwxaSQ6NEsjY5rCp+11ezCiZpWGoGFTbOuhg4qKevQ==", - "dev": true - }, "temp-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", @@ -11948,9 +12761,9 @@ "dev": true }, "text-extensions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.8.0.tgz", - "integrity": "sha512-mVzjRxuWnDKs/qH1rbOJEVHLlSX9kty9lpi7lMvLgU9S74mQ8/Ozg9UPcKxShh0qG2NZ+NyPOPpcZU4C1Eld9A==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", "dev": true }, "throat": { @@ -11961,17 +12774,17 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "^2.1.5", + "readable-stream": "~2.3.6", "xtend": "~4.0.1" }, "dependencies": { @@ -11983,7 +12796,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -12075,15 +12888,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, - "topo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.0.tgz", - "integrity": "sha512-Tlu1fGlR90iCdIPURqPiufqAlCZYzLjHYVVbcFWDMcX7+tK8hdZWAfsMrD/pBul9jqHHwFjNdf1WaxA9vTRRhw==", - "dev": true, - "requires": { - "hoek": "5.x.x" - } - }, "toposort": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", @@ -12136,9 +12940,9 @@ "dev": true }, "ts-jest": { - "version": "23.10.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-23.10.4.tgz", - "integrity": "sha512-oV/wBwGUS7olSk/9yWMiSIJWbz5xO4zhftnY3gwv6s4SMg6WHF1m8XZNBvQOKQRiTAexZ9754Z13dxBq3Zgssw==", + "version": "23.10.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-23.10.5.tgz", + "integrity": "sha512-MRCs9qnGoyKgFc8adDEntAOP64fWK1vZKnOYU1o2HxaqjdJvGqmkLCPCnVq1/If4zkUmEjKPnCiUisTrlX2p2A==", "dev": true, "requires": { "bs-logger": "0.x", @@ -12147,6 +12951,7 @@ "json5": "2.x", "make-error": "1.x", "mkdirp": "0.x", + "resolve": "1.x", "semver": "^5.5", "yargs-parser": "10.x" }, @@ -12162,7 +12967,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -12184,9 +12989,9 @@ "dev": true }, "tslint": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", - "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz", + "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==", "dev": true, "requires": { "babel-code-frame": "^6.22.0", @@ -12204,12 +13009,12 @@ }, "dependencies": { "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", + "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-parse": "^1.0.6" } } } @@ -12263,9 +13068,9 @@ "dev": true }, "typescript": { - "version": "3.2.0-dev.20181018", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.0-dev.20181018.tgz", - "integrity": "sha512-BML75AnK2PZu54xULlF3zQaJh1flrtt66NEYc4NBHZwCh1jFbVP2E8nNpoRcZyMh4EiSCvX3r6zUpPYcY10vzQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.1.tgz", + "integrity": "sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA==", "dev": true }, "uglify-js": { @@ -12499,15 +13304,6 @@ "extsprintf": "^1.2.0" } }, - "vise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vise/-/vise-3.0.0.tgz", - "integrity": "sha512-kBFZLmiL1Vm3rHXphkhvvAcsjgeQXRrOFCbJb0I50YZZP4HGRNH+xGzK3matIMcpbsfr3I02u9odj4oCD0TWgA==", - "dev": true, - "requires": { - "hoek": "5.x.x" - } - }, "w3c-hr-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", @@ -12679,16 +13475,6 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "wreck": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/wreck/-/wreck-14.1.0.tgz", - "integrity": "sha512-y/iwFhwdGoM8Hk1t1I4LbuLhM3curVD8STd5NcFI0c/4b4cQAMLcnCRxXX9sLQAggDC8dXYSaQNsT64hga6lvA==", - "dev": true, - "requires": { - "boom": "7.x.x", - "hoek": "5.x.x" - } - }, "write-file-atomic": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", @@ -12733,10 +13519,9 @@ } }, "ws": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.0.tgz", - "integrity": "sha512-H3dGVdGvW2H8bnYpIDc3u3LH8Wue3Qh+Zto6aXXFzvESkTVT6rAfKR6tR/+coaUvxs8yHtmNV0uioBF62ZGSTg==", - "dev": true, + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.3.tgz", + "integrity": "sha512-tbSxiT+qJI223AP4iLfQbkbxkwdFcneYinM2+x46Gx2wgvbaOMO36czfdfVUBRTHvzAMRhDd98sA5d/BuWbQdg==", "requires": { "async-limiter": "~1.0.0" } @@ -12753,12 +13538,6 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, - "xregexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", - "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", - "dev": true - }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", @@ -12774,7 +13553,8 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true }, "yargs": { "version": "11.1.0", @@ -12856,9 +13636,9 @@ "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==" }, "yup": { - "version": "0.26.5", - "resolved": "https://registry.npmjs.org/yup/-/yup-0.26.5.tgz", - "integrity": "sha512-fBf7pfOCoGgk6ppcRkR5UjVxBqTHM/4d4WCrJN5fa7vFJFwM3f3tQYKY5cLSks2k8FixPW+WO7HGSPKoJCxbZw==", + "version": "0.26.10", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.26.10.tgz", + "integrity": "sha512-keuNEbNSnsOTOuGCt3UJW69jDE3O4P+UHAakO7vSeFMnjaitcmlbij/a3oNb9g1Y1KvSKH/7O1R2PQ4m4TRylw==", "dev": true, "requires": { "@babel/runtime": "7.0.0", @@ -12870,14 +13650,14 @@ } }, "zen-observable": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.9.tgz", - "integrity": "sha512-Y9kPzjGvIZ5jchSlqlCpBW3I82zBBL4z+ulXDRVA1NwsKzjt5kwAi+gOYIy0htNkfuehGZZtP5mRXHRV6TjDWw==" + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.13.tgz", + "integrity": "sha512-fa+6aDUVvavYsefZw0zaZ/v3ckEtMgCFi30sn91SEZea4y/6jQp05E3omjkX91zV6RVdn15fqnFZ6RKjRGbp2g==" }, "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==", + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.15.tgz", + "integrity": "sha512-sXKPWiw6JszNEkRv5dQ+lQCttyjHM2Iks74QU5NP8mMPS/NrzTlHDr780gf/wOBqmHkPO6NCLMlsa+fAQ8VE8w==", "requires": { "zen-observable": "^0.8.0" } diff --git a/package.json b/package.json index 2058f195297..443d9a554cd 100644 --- a/package.json +++ b/package.json @@ -34,15 +34,19 @@ "node": ">=6" }, "dependencies": { + "@apollographql/apollo-tools": "^0.3.0", "apollo-cache-control": "file:packages/apollo-cache-control", "apollo-datasource": "file:packages/apollo-datasource", "apollo-datasource-rest": "file:packages/apollo-datasource-rest", "apollo-engine-reporting": "file:packages/apollo-engine-reporting", "apollo-engine-reporting-protobuf": "file:packages/apollo-engine-reporting-protobuf", + "apollo-graphql": "file:packages/apollo-graphql", "apollo-server": "file:packages/apollo-server", + "apollo-server-azure-functions": "file:packages/apollo-server-azure-functions", "apollo-server-cache-memcached": "file:packages/apollo-server-cache-memcached", "apollo-server-cache-redis": "file:packages/apollo-server-cache-redis", "apollo-server-caching": "file:packages/apollo-server-caching", + "apollo-server-cloud-functions": "file:packages/apollo-server-cloud-functions", "apollo-server-cloudflare": "file:packages/apollo-server-cloudflare", "apollo-server-core": "file:packages/apollo-server-core", "apollo-server-env": "file:packages/apollo-server-env", @@ -54,76 +58,79 @@ "apollo-server-lambda": "file:packages/apollo-server-lambda", "apollo-server-micro": "file:packages/apollo-server-micro", "apollo-server-plugin-base": "file:packages/apollo-server-plugin-base", + "apollo-server-testing": "file:packages/apollo-server-testing", "apollo-tracing": "file:packages/apollo-tracing", "graphql-extensions": "file:packages/graphql-extensions" }, "devDependencies": { "@types/async-retry": "1.2.1", - "@types/aws-lambda": "8.10.13", + "@types/aws-lambda": "8.10.18", "@types/body-parser": "1.17.0", "@types/connect": "3.4.32", + "@types/fast-json-stable-stringify": "2.0.0", "@types/fibers": "0.0.30", - "@types/graphql": "14.0.3", - "@types/hapi": "17.6.1", - "@types/jest": "23.3.5", + "@types/graphql": "14.0.5", + "@types/hapi": "17.8.5", + "@types/jest": "23.3.14", "@types/koa-multer": "1.0.0", - "@types/koa-router": "7.0.32", - "@types/lodash": "4.14.117", + "@types/koa-router": "7.0.39", + "@types/lodash": "4.14.120", + "@types/lodash.sortby": "4.7.4", "@types/lru-cache": "4.1.1", "@types/memcached": "2.2.5", "@types/micro": "7.3.3", "@types/multer": "1.3.7", - "@types/node": "10.12.0", - "@types/node-fetch": "2.1.2", - "@types/redis": "2.8.7", - "@types/request": "2.47.1", + "@types/node": "10.12.21", + "@types/node-fetch": "2.1.5", + "@types/redis": "2.8.10", + "@types/request": "2.48.1", "@types/request-promise": "4.1.42", "@types/test-listen": "1.1.0", "@types/type-is": "1.6.2", "@types/ws": "6.0.1", "apollo-fetch": "0.7.0", - "apollo-link": "1.2.3", - "apollo-link-http": "1.5.5", - "apollo-link-persisted-queries": "0.2.1", + "apollo-link": "1.2.8", + "apollo-link-http": "1.5.11", + "apollo-link-persisted-queries": "0.2.2", "body-parser": "1.18.3", "codecov": "3.1.0", "connect": "3.6.6", "express": "4.16.4", - "fibers": "3.0.0", + "fibers": "3.1.1", "form-data": "2.3.3", - "graphql": "14.0.2", + "graphql": "14.1.1", "graphql-subscriptions": "1.0.0", - "graphql-tag": "2.10.0", - "graphql-tools": "4.0.2", - "hapi": "17.6.0", - "husky": "1.1.2", + "graphql-tag": "2.10.1", + "graphql-tools": "4.0.4", + "hapi": "17.8.4", + "husky": "1.3.1", "jest": "23.6.0", "jest-junit": "5.2.0", "jest-matcher-utils": "23.6.0", "js-sha256": "0.9.0", - "koa": "2.5.3", + "koa": "2.7.0", "koa-multer": "1.0.2", - "lerna": "3.4.3", - "lint-staged": "7.2.2", + "lerna": "3.10.8", + "lint-staged": "8.1.3", "memcached-mock": "0.1.0", - "meteor-promise": "0.8.6", + "meteor-promise": "0.8.7", "mock-req": "0.2.0", "multer": "1.4.1", - "node-fetch": "2.2.0", - "prettier": "1.14.3", + "node-fetch": "2.3.0", + "prettier": "1.16.4", "prettier-check": "2.0.0", "qs-middleware": "1.0.3", - "redis-mock": "0.33.0", + "redis-mock": "0.43.0", "request": "2.88.0", "request-promise": "4.2.2", "subscriptions-transport-ws": "0.9.15", - "supertest": "3.3.0", + "supertest": "3.4.2", "test-listen": "1.1.0", - "ts-jest": "23.10.4", - "tslint": "5.11.0", - "typescript": "next", - "ws": "6.1.0", - "yup": "0.26.5" + "ts-jest": "23.10.5", + "tslint": "5.12.1", + "typescript": "3.3.1", + "ws": "6.1.3", + "yup": "0.26.10" }, "jest": { "projects": [ diff --git a/packages/apollo-cache-control/CHANGELOG.md b/packages/apollo-cache-control/CHANGELOG.md index 26db288ce83..1e773242f21 100644 --- a/packages/apollo-cache-control/CHANGELOG.md +++ b/packages/apollo-cache-control/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +> **A note on ommitted versions**: Due to the way that the Apollo Server +> monorepo releases occur (via Lerna with _exact_ version pinning), the +> version of the `apollo-cache-control` package is sometimes bumped and +> published despite having no functional changes in its behavior. We will +> always attempt to specifically mention functional changes to the +> `apollo-cache-control` package within this particular `CHANGELOG.md`. + +### v0.4.1 + +* Fix cache hints of `maxAge: 0` to mean "uncachable". (#2197) +* Apply `defaultMaxAge` to scalar fields on the root object. (#2210) + +### v0.3.0 + +* Support calculating Cache-Control HTTP headers when used by `apollo-server@2.0.0`. + +### v0.2.0 + +Moved to the `apollo-server` git repository. No code changes. (There are a +number of other 0.2.x releases with no code changes due to how the +`apollo-server` release process works.) + ### v0.1.1 * Fix `defaultMaxAge` feature (introduced in 0.1.0) so that `maxAge: 0` overrides the default, as previously documented. diff --git a/packages/apollo-cache-control/package.json b/packages/apollo-cache-control/package.json index 6f8bf86974c..044e8fc7d02 100644 --- a/packages/apollo-cache-control/package.json +++ b/packages/apollo-cache-control/package.json @@ -1,6 +1,6 @@ { "name": "apollo-cache-control", - "version": "0.3.0-alpha.0", + "version": "0.4.1", "description": "A GraphQL extension for cache control", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/apollo-cache-control/src/__tests__/cacheControlDirective.test.ts b/packages/apollo-cache-control/src/__tests__/cacheControlDirective.test.ts index 5327c3c5e08..a10ded07543 100644 --- a/packages/apollo-cache-control/src/__tests__/cacheControlDirective.test.ts +++ b/packages/apollo-cache-control/src/__tests__/cacheControlDirective.test.ts @@ -30,6 +30,25 @@ describe('@cacheControl directives', () => { expect(hints).toContainEqual({ path: ['droid'], maxAge: 0 }); }); + it('should set maxAge: 0 and no scope for a top-level scalar field without cache hints', async () => { + const schema = buildSchemaWithCacheControlSupport(` + type Query { + name: String + } + `); + + const hints = await collectCacheControlHints( + schema, + ` + query { + name + } + `, + ); + + expect(hints).toContainEqual({ path: ['name'], maxAge: 0 }); + }); + it('should set maxAge to the default and no scope for a field without cache hints', async () => { const schema = buildSchemaWithCacheControlSupport(` type Query { diff --git a/packages/apollo-cache-control/src/__tests__/cacheControlExtension.test.ts b/packages/apollo-cache-control/src/__tests__/cacheControlExtension.test.ts new file mode 100644 index 00000000000..127071f55e9 --- /dev/null +++ b/packages/apollo-cache-control/src/__tests__/cacheControlExtension.test.ts @@ -0,0 +1,68 @@ +import { ResponsePath } from 'graphql'; +import { CacheControlExtension, CacheScope } from '../'; + +describe('CacheControlExtension', () => { + let cacheControlExtension: CacheControlExtension; + + beforeEach(() => { + cacheControlExtension = new CacheControlExtension(); + }); + + describe('computeOverallCachePolicy', () => { + const responsePath: ResponsePath = { + key: 'test', + prev: undefined, + }; + const responseSubPath: ResponsePath = { + key: 'subTest', + prev: responsePath, + }; + const responseSubSubPath: ResponsePath = { + key: 'subSubTest', + prev: responseSubPath, + }; + + it('returns undefined without cache hints', () => { + const cachePolicy = cacheControlExtension.computeOverallCachePolicy(); + expect(cachePolicy).toBeUndefined(); + }); + + it('returns lowest max age value', () => { + cacheControlExtension.addHint(responsePath, { maxAge: 10 }); + cacheControlExtension.addHint(responseSubPath, { maxAge: 20 }); + + const cachePolicy = cacheControlExtension.computeOverallCachePolicy(); + expect(cachePolicy).toHaveProperty('maxAge', 10); + }); + + it('returns undefined if any cache hint has a maxAge of 0', () => { + cacheControlExtension.addHint(responsePath, { maxAge: 120 }); + cacheControlExtension.addHint(responseSubPath, { maxAge: 0 }); + cacheControlExtension.addHint(responseSubSubPath, { maxAge: 20 }); + + const cachePolicy = cacheControlExtension.computeOverallCachePolicy(); + expect(cachePolicy).toBeUndefined(); + }); + + it('returns PUBLIC scope by default', () => { + cacheControlExtension.addHint(responsePath, { maxAge: 10 }); + + const cachePolicy = cacheControlExtension.computeOverallCachePolicy(); + expect(cachePolicy).toHaveProperty('scope', CacheScope.Public); + }); + + it('returns PRIVATE scope if any cache hint has PRIVATE scope', () => { + cacheControlExtension.addHint(responsePath, { + maxAge: 10, + scope: CacheScope.Public, + }); + cacheControlExtension.addHint(responseSubPath, { + maxAge: 10, + scope: CacheScope.Private, + }); + + const cachePolicy = cacheControlExtension.computeOverallCachePolicy(); + expect(cachePolicy).toHaveProperty('scope', CacheScope.Private); + }); + }); +}); diff --git a/packages/apollo-cache-control/src/index.ts b/packages/apollo-cache-control/src/index.ts index db145f7a15f..0165628f106 100644 --- a/packages/apollo-cache-control/src/index.ts +++ b/packages/apollo-cache-control/src/index.ts @@ -75,26 +75,27 @@ export class CacheControlExtension } } - // If this field is a field on an object, look for hints on the field - // itself, taking precedence over previously calculated hints. - const parentType = info.parentType; - if (parentType instanceof GraphQLObjectType) { - const fieldDef = parentType.getFields()[info.fieldName]; - if (fieldDef.astNode) { - hint = mergeHints( - hint, - cacheHintFromDirectives(fieldDef.astNode.directives), - ); - } + // Look for hints on the field itself (on its parent type), taking + // precedence over previously calculated hints. + const fieldDef = info.parentType.getFields()[info.fieldName]; + if (fieldDef.astNode) { + hint = mergeHints( + hint, + cacheHintFromDirectives(fieldDef.astNode.directives), + ); } - // If this resolver returns an object and we haven't seen an explicit maxAge - // hint, set the maxAge to 0 (uncached) or the default if specified in the - // constructor. (Non-object fields by default are assumed to inherit their - // cacheability from their parents.) + // If this resolver returns an object or is a root field and we haven't seen + // an explicit maxAge hint, set the maxAge to 0 (uncached) or the default if + // specified in the constructor. (Non-object fields by default are assumed + // to inherit their cacheability from their parents. But on the other hand, + // while root non-object fields can get explicit hints from their definition + // on the Query/Mutation object, if that doesn't exist then there's no + // parent field that would assign the default maxAge, so we do it here.) if ( (targetType instanceof GraphQLObjectType || - targetType instanceof GraphQLInterfaceType) && + targetType instanceof GraphQLInterfaceType || + !info.path.prev) && hint.maxAge === undefined ) { hint.maxAge = this.defaultMaxAge; @@ -156,16 +157,19 @@ export class CacheControlExtension let scope: CacheScope = CacheScope.Public; for (const hint of this.hints.values()) { - if (hint.maxAge) { - lowestMaxAge = lowestMaxAge - ? Math.min(lowestMaxAge, hint.maxAge) - : hint.maxAge; + if (hint.maxAge !== undefined) { + lowestMaxAge = + lowestMaxAge !== undefined + ? Math.min(lowestMaxAge, hint.maxAge) + : hint.maxAge; } if (hint.scope === CacheScope.Private) { scope = CacheScope.Private; } } + // If maxAge is 0, then we consider it uncacheable so it doesn't matter what + // the scope was. return lowestMaxAge ? { maxAge: lowestMaxAge, diff --git a/packages/apollo-datasource-rest/package.json b/packages/apollo-datasource-rest/package.json index 2ee3a5d50ce..d5b08204802 100644 --- a/packages/apollo-datasource-rest/package.json +++ b/packages/apollo-datasource-rest/package.json @@ -1,6 +1,6 @@ { "name": "apollo-datasource-rest", - "version": "0.2.0-alpha.0", + "version": "0.2.2", "author": "opensource@apollographql.com", "license": "MIT", "repository": { diff --git a/packages/apollo-datasource-rest/src/RESTDataSource.ts b/packages/apollo-datasource-rest/src/RESTDataSource.ts index 59b068768e6..0c8f9bf1d2c 100644 --- a/packages/apollo-datasource-rest/src/RESTDataSource.ts +++ b/packages/apollo-datasource-rest/src/RESTDataSource.ts @@ -104,7 +104,11 @@ export abstract class RESTDataSource extends DataSource { protected parseBody(response: Response): Promise { const contentType = response.headers.get('Content-Type'); - if (contentType && contentType.startsWith('application/json')) { + if ( + contentType && + (contentType.startsWith('application/json') || + contentType.startsWith('application/hal+json')) + ) { return response.json(); } else { return response.text(); diff --git a/packages/apollo-datasource-rest/src/__tests__/RESTDataSource.test.ts b/packages/apollo-datasource-rest/src/__tests__/RESTDataSource.test.ts index b2a8ef90220..48868de308f 100644 --- a/packages/apollo-datasource-rest/src/__tests__/RESTDataSource.test.ts +++ b/packages/apollo-datasource-rest/src/__tests__/RESTDataSource.test.ts @@ -392,6 +392,27 @@ describe('RESTDataSource', () => { expect(data).toEqual({ foo: 'bar' }); }); + it('returns data as parsed JSON when Content-Type is application/hal+json', async () => { + const dataSource = new class extends RESTDataSource { + baseURL = 'https://api.example.com'; + + getFoo() { + return this.get('foo'); + } + }(); + + dataSource.httpCache = httpCache; + + fetch.mockJSONResponseOnce( + { foo: 'bar' }, + { 'Content-Type': 'application/hal+json' }, + ); + + const data = await dataSource.getFoo(); + + expect(data).toEqual({ foo: 'bar' }); + }); + it('returns data as a string when Content-Type is text/plain', async () => { const dataSource = new class extends RESTDataSource { baseURL = 'https://api.example.com'; diff --git a/packages/apollo-datasource/package.json b/packages/apollo-datasource/package.json index 54551f66623..e2498acc89b 100644 --- a/packages/apollo-datasource/package.json +++ b/packages/apollo-datasource/package.json @@ -1,6 +1,6 @@ { "name": "apollo-datasource", - "version": "0.2.0-alpha.0", + "version": "0.2.2", "author": "opensource@apollographql.com", "license": "MIT", "repository": { diff --git a/packages/apollo-engine-reporting-protobuf/README.md b/packages/apollo-engine-reporting-protobuf/README.md index d420ece8695..c4038ba7c42 100644 --- a/packages/apollo-engine-reporting-protobuf/README.md +++ b/packages/apollo-engine-reporting-protobuf/README.md @@ -1,7 +1,29 @@ -# apollo-engine-reporting-protobuf +# `apollo-engine-reporting-protobuf` -This contains generated Javascript/TypeScript code for the protobuf definitions -for the Engine reporting API. +> **Note:** The Apollo Engine reporting API is subject to change. We strongly +> encourage developers to contact Apollo Engine support to discuss their use +> case prior to building their own reporting agent using this module. -The Engine reporting API is currently subject to change at any time; do not rely -on this to build your own client. +This module provides JavaScript/TypeScript +[Protocol buffer](https://developers.google.com/protocol-buffers/) definitions +for the Apollo Engine reporting API. These definitions are generated for +consumption from the `reports.proto` file which is defined internally within +Apollo. + +## Development + +> **Note:** Due to a dependency on Unix tools (e.g. `bash`, `grep`, etc.), the +> development of this module requires a Unix system. There is no reason why +> this can't be avoided, the time just hasn't been taken to make those changes. +> We'd happily accept a PR which makes the appropriate changes! + +Currently, this package generates a majority of its code with +[`protobufjs`](https://www.npmjs.com/package/protobufjs) based on the +`reports.proto` file. The output is generated with the `prepare` npm script. + +The root of the repository provides the `devDependencies` necessary to build +these definitions (e.g. `pbjs`, `pbts`, `protobuf`, etc.) and the the `prepare` +npm script is invoked programmatically via the monorepo tooling (e.g. Lerna) +thanks to _this_ module's `postinstall` script. Therefore, when making +changes to this module, `npx lerna run prepare` should be run from the **root** +of this monorepo in order to update the definitions in _this_ module. diff --git a/packages/apollo-engine-reporting-protobuf/package.json b/packages/apollo-engine-reporting-protobuf/package.json index 84438fd3123..cf5de43d20a 100644 --- a/packages/apollo-engine-reporting-protobuf/package.json +++ b/packages/apollo-engine-reporting-protobuf/package.json @@ -1,13 +1,13 @@ { "name": "apollo-engine-reporting-protobuf", - "version": "0.1.0-alpha.0", + "version": "0.2.0", "description": "Protobuf format for Apollo Engine", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { - "prepare": "npm run pbjs && npm run pbts", - "pbjs": "bash -c 'mkdir -p dist && pbjs --target static-module --out dist/index.js --wrap commonjs --force-number <(grep -v \"package mdg.engine.proto\" reports.proto)'", - "pbts": "pbts -o dist/index.d.ts dist/index.js" + "prepare": "npm run pbjs && npm run pbts && cp src/* dist", + "pbjs": "bash -c 'mkdir -p dist && pbjs --target static-module --out dist/protobuf.js --wrap commonjs --force-number <(grep -v \"package mdg.engine.proto\" src/reports.proto)'", + "pbts": "pbts -o dist/protobuf.d.ts dist/protobuf.js" }, "repository": { "type": "git", diff --git a/packages/apollo-engine-reporting-protobuf/src/index.d.ts b/packages/apollo-engine-reporting-protobuf/src/index.d.ts new file mode 100644 index 00000000000..627ce2e12e5 --- /dev/null +++ b/packages/apollo-engine-reporting-protobuf/src/index.d.ts @@ -0,0 +1,2 @@ +import * as protobuf from './protobuf'; +export = protobuf; diff --git a/packages/apollo-engine-reporting-protobuf/src/index.js b/packages/apollo-engine-reporting-protobuf/src/index.js new file mode 100644 index 00000000000..31fd01481f4 --- /dev/null +++ b/packages/apollo-engine-reporting-protobuf/src/index.js @@ -0,0 +1,24 @@ +const protobuf = require('./protobuf'); + +// Override the generated protobuf Traces.encode function so that it will look +// for Traces that are already encoded to Buffer as well as unencoded +// Traces. This amortizes the protobuf encoding time over each generated Trace +// instead of bunching it all up at once at sendReport time. In load tests, this +// change improved p99 end-to-end HTTP response times by a factor of 11 without +// a casually noticeable effect on p50 times. This also makes it easier for us +// to implement maxUncompressedReportSize as we know the encoded size of traces +// as we go. +const originalTracesEncode = protobuf.Traces.encode; +protobuf.Traces.encode = function(message, originalWriter) { + const writer = originalTracesEncode(message, originalWriter); + const encodedTraces = message.encodedTraces; + if (encodedTraces != null && encodedTraces.length) { + for (let i = 0; i < encodedTraces.length; ++i) { + writer.uint32(/* id 1, wireType 2 =*/ 10); + writer.bytes(encodedTraces[i]); + } + } + return writer; +}; + +module.exports = protobuf; diff --git a/packages/apollo-engine-reporting-protobuf/reports.proto b/packages/apollo-engine-reporting-protobuf/src/reports.proto similarity index 78% rename from packages/apollo-engine-reporting-protobuf/reports.proto rename to packages/apollo-engine-reporting-protobuf/src/reports.proto index 005597cb6ae..437394728e9 100644 --- a/packages/apollo-engine-reporting-protobuf/reports.proto +++ b/packages/apollo-engine-reporting-protobuf/src/reports.proto @@ -147,11 +147,12 @@ message Trace { Details details = 6; - // Note: engineproxy always sets these to "none". apollo-engine-reporting - // should allow for them to be set by the user. + // Note: engineproxy always sets client_name, client_version, and client_address to "none". + // apollo-engine-reporting allows for them to be set by the user. string client_name = 7; string client_version = 8; string client_address = 9; + string client_reference_id = 23; HTTP http = 10; @@ -175,6 +176,13 @@ message Trace { reserved 12, 13, 1, 2; } +// The `service` value embedded within the header key is not guaranteed to contain an actual service, +// and, in most cases, the service information is trusted to come from upstream processing. If the +// service _is_ specified in this header, then it is checked to match the context that is reporting it. +// Otherwise, the service information is deduced from the token context of the reporter and then sent +// along via other mechanisms (in Kafka, the `ReportKafkaKey). The other information (hostname, +// agent_version, etc.) is sent by the Apollo Engine Reporting agent, but we do not currently save that +// information to any of our persistent storage. message ReportHeader { string service = 3; // eg "host-01.example.com" @@ -188,6 +196,10 @@ message ReportHeader { string runtime_version = 8; // eg "Linux box 4.6.5-1-ec2 #1 SMP Mon Aug 1 02:31:38 PDT 2016 x86_64 GNU/Linux" string uname = 9; + // eg "current", "prod" + string schema_tag = 10; + // The hex representation of the sha512 of the introspection response + string schema_hash = 11; } message PathErrorStats { @@ -201,6 +213,13 @@ message ClientNameStats { // (See docs/histograms.md for the histogram format.) repeated int64 latency_count = 1; reserved 2; // removed: repeated uint64 error_count = 2; + // These per-version fields were used to understand what versions contributed to this sample + // when we were implementing the aggregation of this information ourselves using BigTable. + // However, since the per-version stats don't separate out latency, it makes more sense to + // have stats reported with contextual information so we can have the specific breakdown we're + // looking for. These fields are somewhat misleading as we never actually do any per-version + // awareness with anything reporting in the legacy "per_client_name" stats, and instead use + // "query_stats_with_context" to have more contextual information. map requests_count_per_version = 3; // required map cache_hits_per_version = 4; map persisted_query_hits_per_version = 10; @@ -214,6 +233,35 @@ message ClientNameStats { repeated int64 private_cache_ttl_count = 9; } +message QueryLatencyStats { + repeated int64 latency_count = 1; + uint64 request_count = 2; + uint64 cache_hits = 3; + uint64 persisted_query_hits = 4; + uint64 persisted_query_misses = 5; + repeated int64 cache_latency_count = 6; + PathErrorStats root_error_stats = 7; + uint64 requests_with_errors_count = 8; + repeated int64 public_cache_ttl_count = 9; + repeated int64 private_cache_ttl_count = 10; +} + +message StatsContext { + string client_reference_id = 1; + string client_name = 2; + string client_version = 3; +} + +message ContextualizedQueryLatencyStats { + QueryLatencyStats query_latency_stats = 1; + StatsContext context = 2; +} + +message ContextualizedTypeStats { + StatsContext context = 1; + map per_type_stat = 2; +} + message FieldStat { string name = 2; // deprecated; only set when stored in TypeStat.field string return_type = 3; // required; eg "String!" for User.email:String! @@ -231,10 +279,17 @@ message TypeStat { } message QueryStats { - map per_client_name = 1; // required - repeated TypeStat per_type = 2; // deprecated; use per_type_stat instead + // Either per_client_name (for back-compat) or query_stats_with_context must be specified. If both are + // specified, then query_stats_with_context will be used and per_client_name will be ignored. Although + // the fields in ClientNameStats mention things "per-version," the information in the "per-version" + // fields will only ever be over the default version, the empty String: "", if arrived at via the + // FullTracesAggregator. + map per_client_name = 1; // deprecated; use stats_with_context instead + repeated ContextualizedQueryLatencyStats query_stats_with_context = 4; + repeated TypeStat per_type = 2; // deprecated; use type_stats_with_context instead // Key is the parent type, e.g. "User" for User.email:String! - map per_type_stat = 3; + map per_type_stat = 3; // deprecated; use type_stats_with_context instead + repeated ContextualizedTypeStats type_stats_with_context = 5; } // Top-level message type for the server-side traces endpoint @@ -339,4 +394,4 @@ message FullTracesReport { // Just a sequence of traces with the same statsReportKey. message Traces { repeated Trace trace = 1; -} +} \ No newline at end of file diff --git a/packages/apollo-engine-reporting/CHANGELOG.md b/packages/apollo-engine-reporting/CHANGELOG.md index 70a5527e966..cb79cc96467 100644 --- a/packages/apollo-engine-reporting/CHANGELOG.md +++ b/packages/apollo-engine-reporting/CHANGELOG.md @@ -1,4 +1,10 @@ ### vNext -* Initial release. +# v1.0.0 + +* The signature functions which were previously exported from this package's + main module have been removed from `apollo-engine-reporting` and + moved to the `apollo-graphql` package. They should be more universally + helpful in that library, and should avoid tooling which needs to use them + from needing to bring in all of `apollo-server-core`. diff --git a/packages/apollo-engine-reporting/package.json b/packages/apollo-engine-reporting/package.json index 0e2b7c737d5..efee5abfa13 100644 --- a/packages/apollo-engine-reporting/package.json +++ b/packages/apollo-engine-reporting/package.json @@ -1,6 +1,6 @@ { "name": "apollo-engine-reporting", - "version": "0.1.0-alpha.0", + "version": "1.0.0", "description": "Send reports about your GraphQL services to Apollo Engine", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -12,9 +12,10 @@ }, "dependencies": { "apollo-engine-reporting-protobuf": "file:../apollo-engine-reporting-protobuf", + "apollo-graphql": "file:../apollo-graphql", + "apollo-server-core": "file:../apollo-server-core", "apollo-server-env": "file:../apollo-server-env", "async-retry": "^1.2.1", - "graphql-extensions": "file:../graphql-extensions", - "lodash": "^4.17.10" + "graphql-extensions": "file:../graphql-extensions" } } diff --git a/packages/apollo-engine-reporting/src/__tests__/extension.test.ts b/packages/apollo-engine-reporting/src/__tests__/extension.test.ts index a7d06f1921e..841854b856f 100644 --- a/packages/apollo-engine-reporting/src/__tests__/extension.test.ts +++ b/packages/apollo-engine-reporting/src/__tests__/extension.test.ts @@ -7,6 +7,7 @@ import { Trace } from 'apollo-engine-reporting-protobuf'; import { graphql } from 'graphql'; import { Request } from 'node-fetch'; import { EngineReportingExtension } from '../extension'; +import { InMemoryLRUCache } from 'apollo-server-caching'; test('trace construction', async () => { const typeDefs = ` @@ -52,11 +53,23 @@ test('trace construction', async () => { function addTrace(signature: string, operationName: string, trace: Trace) { traces.push({ signature, operationName, trace }); } + const reportingExtension = new EngineReportingExtension({}, addTrace); const stack = new GraphQLExtensionStack([reportingExtension]); const requestDidEnd = stack.requestDidStart({ request: new Request('http://localhost:123/foo') as any, queryString: query, + requestContext: { + request: { + query, + operationName: 'q', + extensions: { + clientName: 'testing suite', + }, + }, + context: {}, + cache: new InMemoryLRUCache(), + }, }); await graphql({ schema, diff --git a/packages/apollo-engine-reporting/src/agent.ts b/packages/apollo-engine-reporting/src/agent.ts index 8d2587fe5be..e09f5ef3c10 100644 --- a/packages/apollo-engine-reporting/src/agent.ts +++ b/packages/apollo-engine-reporting/src/agent.ts @@ -8,38 +8,26 @@ import { Trace, } from 'apollo-engine-reporting-protobuf'; -import { fetch, Response } from 'apollo-server-env'; +import { fetch, RequestAgent, Response } from 'apollo-server-env'; import retry from 'async-retry'; import { EngineReportingExtension } from './extension'; - -// Override the generated protobuf Traces.encode function so that it will look -// for Traces that are already encoded to Buffer as well as unencoded -// Traces. This amortizes the protobuf encoding time over each generated Trace -// instead of bunching it all up at once at sendReport time. In load tests, this -// change improved p99 end-to-end HTTP response times by a factor of 11 without -// a casually noticeable effect on p50 times. This also makes it easier for us -// to implement maxUncompressedReportSize as we know the encoded size of traces -// as we go. -const originalTracesEncode = Traces.encode; -Traces.encode = function(message, originalWriter) { - const writer = originalTracesEncode(message, originalWriter); - const encodedTraces = (message as any).encodedTraces; - if (encodedTraces != null && encodedTraces.length) { - for (let i = 0; i < encodedTraces.length; ++i) { - writer.uint32(/* id 1, wireType 2 =*/ 10); - writer.bytes(encodedTraces[i]); - } - } - return writer; -}; +import { + GraphQLRequestContext, + GraphQLServiceContext, +} from 'apollo-server-core/dist/requestPipelineAPI'; export interface ClientInfo { clientName?: string; clientVersion?: string; + clientReferenceId?: string; } -export interface EngineReportingOptions { +export type GenerateClientInfo = ( + requestContext: GraphQLRequestContext, +) => ClientInfo; + +export interface EngineReportingOptions { // API key for the service. Get this from // [Engine](https://engine.apollographql.com) by logging in and creating // a service. You may also specify this with the `ENGINE_API_KEY` @@ -60,6 +48,8 @@ export interface EngineReportingOptions { endpointUrl?: string; // If set, prints all reports as JSON when they are sent. debugPrintReports?: boolean; + // HTTP(s) agent to be used on the fetch call to apollo-engine metrics endpoint + requestAgent?: RequestAgent | false; // Reporting is retried with exponential backoff up to this many times // (including the original request). Defaults to 5. maxAttempts?: number; @@ -88,45 +78,38 @@ export interface EngineReportingOptions { sendReportsImmediately?: boolean; // To remove the error message from traces, set this to true. Defaults to false maskErrorDetails?: boolean; - - /** - * (Experimental) Creates the client information for operation traces. - * - * @remarks This is experimental and subject to change or removal. - * - * @private - * - */ - generateClientInfo?: ( - o: { - context: any; - extensions?: Record; - }, - ) => ClientInfo; + // A human readable name to tag this variant of a schema (i.e. staging, EU) + schemaTag?: string; + //Creates the client information for operation traces. + generateClientInfo?: GenerateClientInfo; } -const REPORT_HEADER = new ReportHeader({ +const serviceHeaderDefaults = { hostname: os.hostname(), // tslint:disable-next-line no-var-requires agentVersion: `apollo-engine-reporting@${require('../package.json').version}`, runtimeVersion: `node ${process.version}`, // XXX not actually uname, but what node has easily. uname: `${os.platform()}, ${os.type()}, ${os.release()}, ${os.arch()})`, -}); +}; // EngineReportingAgent is a persistent object which creates // EngineReportingExtensions for each request and sends batches of trace reports // to the Engine server. export class EngineReportingAgent { - private options: EngineReportingOptions; + private options: EngineReportingOptions; private apiKey: string; private report!: FullTracesReport; private reportSize!: number; private reportTimer: any; // timer typing is weird and node-specific private sendReportsImmediately?: boolean; private stopped: boolean = false; + private reportHeader: ReportHeader; - public constructor(options: EngineReportingOptions = {}) { + public constructor( + options: EngineReportingOptions = {}, + { schemaHash }: GraphQLServiceContext, + ) { this.options = options; this.apiKey = options.apiKey || process.env.ENGINE_API_KEY || ''; if (!this.apiKey) { @@ -135,6 +118,11 @@ export class EngineReportingAgent { ); } + this.reportHeader = new ReportHeader({ + ...serviceHeaderDefaults, + schemaHash, + schemaTag: options.schemaTag || process.env.ENGINE_SCHEMA_TAG || '', + }); this.resetReport(); this.sendReportsImmediately = options.sendReportsImmediately; @@ -181,8 +169,8 @@ export class EngineReportingAgent { this.report.tracesPerQuery[statsReportKey] = new Traces(); (this.report.tracesPerQuery[statsReportKey] as any).encodedTraces = []; } - // See comment on our override of Traces.encode to learn more about this - // strategy. + // See comment on our override of Traces.encode inside of + // apollo-engine-reporting-protobuf to learn more about this strategy. (this.report.tracesPerQuery[statsReportKey] as any).encodedTraces.push( encodedTrace, ); @@ -256,6 +244,7 @@ export class EngineReportingAgent { 'content-encoding': 'gzip', }, body: compressed, + agent: this.options.requestAgent, }); if (curResponse.status >= 500 && curResponse.status < 600) { @@ -314,7 +303,7 @@ export class EngineReportingAgent { } private resetReport() { - this.report = new FullTracesReport({ header: REPORT_HEADER }); + this.report = new FullTracesReport({ header: this.reportHeader }); this.reportSize = 0; } } diff --git a/packages/apollo-engine-reporting/src/extension.ts b/packages/apollo-engine-reporting/src/extension.ts index 7f870ad1a6d..86ad82476a5 100644 --- a/packages/apollo-engine-reporting/src/extension.ts +++ b/packages/apollo-engine-reporting/src/extension.ts @@ -15,8 +15,13 @@ import { } from 'graphql-extensions'; import { Trace, google } from 'apollo-engine-reporting-protobuf'; -import { EngineReportingOptions, ClientInfo } from './agent'; -import { defaultSignature } from './signature'; +import { EngineReportingOptions, GenerateClientInfo } from './agent'; +import { defaultEngineReportingSignature } from 'apollo-graphql'; +import { GraphQLRequestContext } from 'apollo-server-core/dist/requestPipelineAPI'; + +const clientNameHeaderKey = 'apollographql-client-name'; +const clientReferenceIdHeaderKey = 'apollographql-client-reference-id'; +const clientVersionHeaderKey = 'apollographql-client-version'; // EngineReportingExtension is the per-request GraphQLExtension which creates a // trace (in protobuf Trace format) for a single request. When the request is @@ -32,21 +37,16 @@ export class EngineReportingExtension private operationName?: string; private queryString?: string; private documentAST?: DocumentNode; - private options: EngineReportingOptions; + private options: EngineReportingOptions; private addTrace: ( signature: string, operationName: string, trace: Trace, ) => void; - private generateClientInfo: ( - o: { - context: any; - extensions?: Record; - }, - ) => ClientInfo; + private generateClientInfo: GenerateClientInfo; public constructor( - options: EngineReportingOptions, + options: EngineReportingOptions, addTrace: (signature: string, operationName: string, trace: Trace) => void, ) { this.options = { @@ -59,9 +59,32 @@ export class EngineReportingExtension this.nodes.set(responsePathAsString(undefined), root); this.generateClientInfo = options.generateClientInfo || - // Default to using the clientInfo field of the request's extensions, when - // the ClientInfo fields are undefined, we send the empty string - (({ extensions }) => (extensions && extensions.clientInfo) || {}); + // Default to using the `apollo-client-x` header fields if present. + // If none are present, fallback on the `clientInfo` query extension + // for backwards compatibility. + // The default value if neither header values nor query extension is + // set is the empty String for all fields (as per protobuf defaults) + (({ request }) => { + if ( + request.http && + request.http.headers && + (request.http.headers.get(clientNameHeaderKey) || + request.http.headers.get(clientVersionHeaderKey) || + request.http.headers.get(clientReferenceIdHeaderKey)) + ) { + return { + clientName: request.http.headers.get(clientNameHeaderKey), + clientVersion: request.http.headers.get(clientVersionHeaderKey), + clientReferenceId: request.http.headers.get( + clientReferenceIdHeaderKey, + ), + }; + } else if (request.extensions && request.extensions.clientInfo) { + return request.extensions.clientInfo; + } else { + return {}; + } + }); } public requestDidStart(o: { @@ -71,8 +94,9 @@ export class EngineReportingExtension variables?: Record; persistedQueryHit?: boolean; persistedQueryRegister?: boolean; - context: any; + context: TContext; extensions?: Record; + requestContext: GraphQLRequestContext; }): EndHandler { this.trace.startTime = dateToTimestamp(new Date()); this.startHrTime = process.hrtime(); @@ -167,15 +191,18 @@ export class EngineReportingExtension }); } - // While clientAddress could be a part of the protobuf, we'll ignore it for - // now, since the backend does not group by it and Engine frontend will not - // support it in the short term - const { clientName, clientVersion } = this.generateClientInfo({ - context: o.context, - extensions: o.extensions, - }); - this.trace.clientName = clientName || ''; - this.trace.clientVersion = clientVersion || ''; + const clientInfo = this.generateClientInfo(o.requestContext); + if (clientInfo) { + // While clientAddress could be a part of the protobuf, we'll ignore it for + // now, since the backend does not group by it and Engine frontend will not + // support it in the short term + const { clientName, clientVersion, clientReferenceId } = clientInfo; + // the backend makes the choice of mapping clientName => clientReferenceId if + // no custom reference id is provided + this.trace.clientVersion = clientVersion || ''; + this.trace.clientReferenceId = clientReferenceId || ''; + this.trace.clientName = clientName || ''; + } return () => { this.trace.durationNs = durationHrTimeToNanos( @@ -187,7 +214,7 @@ export class EngineReportingExtension let signature; if (this.documentAST) { const calculateSignature = - this.options.calculateSignature || defaultSignature; + this.options.calculateSignature || defaultEngineReportingSignature; signature = calculateSignature(this.documentAST, operationName); } else if (this.queryString) { // We didn't get an AST, possibly because of a parse failure. Let's just diff --git a/packages/apollo-engine-reporting/src/index.ts b/packages/apollo-engine-reporting/src/index.ts index 6fc476833b0..1f7d07fbb74 100644 --- a/packages/apollo-engine-reporting/src/index.ts +++ b/packages/apollo-engine-reporting/src/index.ts @@ -1,10 +1 @@ -export { - hideLiterals, - dropUnusedDefinitions, - sortAST, - removeAliases, - printWithReducedWhitespace, - defaultSignature, -} from './signature'; - export { EngineReportingOptions, EngineReportingAgent } from './agent'; diff --git a/packages/apollo-engine-reporting/tsconfig.json b/packages/apollo-engine-reporting/tsconfig.json index 2f8668da1ac..07e4625888e 100644 --- a/packages/apollo-engine-reporting/tsconfig.json +++ b/packages/apollo-engine-reporting/tsconfig.json @@ -7,6 +7,8 @@ "include": ["src/**/*"], "exclude": ["**/__tests__", "**/__mocks__"], "references": [ - { "path": "../graphql-extensions" } + { "path": "../graphql-extensions" }, + { "path": "../apollo-graphql" }, + { "path": "../apollo-server-core/tsconfig.requestPipelineAPI.json" } ] } diff --git a/packages/apollo-server-cloud-function/.npmignore b/packages/apollo-graphql/.npmignore similarity index 100% rename from packages/apollo-server-cloud-function/.npmignore rename to packages/apollo-graphql/.npmignore diff --git a/packages/apollo-graphql/CHANGELOG.md b/packages/apollo-graphql/CHANGELOG.md new file mode 100644 index 00000000000..ef45b84f48c --- /dev/null +++ b/packages/apollo-graphql/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +### vNEXT + diff --git a/packages/apollo-graphql/README.md b/packages/apollo-graphql/README.md new file mode 100644 index 00000000000..07909aab7ea --- /dev/null +++ b/packages/apollo-graphql/README.md @@ -0,0 +1 @@ +# `apollo-graphql` diff --git a/packages/apollo-server-cloud-function/jest.config.js b/packages/apollo-graphql/jest.config.js similarity index 100% rename from packages/apollo-server-cloud-function/jest.config.js rename to packages/apollo-graphql/jest.config.js diff --git a/packages/apollo-graphql/package.json b/packages/apollo-graphql/package.json new file mode 100644 index 00000000000..8c70e35a2ca --- /dev/null +++ b/packages/apollo-graphql/package.json @@ -0,0 +1,20 @@ +{ + "name": "apollo-graphql", + "version": "0.0.1-alpha.0", + "description": "Apollo GraphQL utility library", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "keywords": [], + "author": "Apollo ", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "dependencies": { + "lodash.sortby": "^4.7.0" + }, + "devDependencies": {}, + "peerDependencies": { + "graphql": "^14.0.0" + } +} diff --git a/packages/apollo-engine-reporting/src/__tests__/signature.test.ts b/packages/apollo-graphql/src/__tests__/signature.test.ts similarity index 65% rename from packages/apollo-engine-reporting/src/__tests__/signature.test.ts rename to packages/apollo-graphql/src/__tests__/signature.test.ts index 7e3e7314480..161e07ef30f 100644 --- a/packages/apollo-engine-reporting/src/__tests__/signature.test.ts +++ b/packages/apollo-graphql/src/__tests__/signature.test.ts @@ -7,82 +7,12 @@ import { dropUnusedDefinitions, sortAST, removeAliases, -} from '../signature'; +} from '../transforms'; // The gql duplicate fragment warning feature really is just warnings; nothing // breaks if you turn it off in tests. disableFragmentWarnings(); -describe('printWithReducedWhitespace', () => { - const cases = [ - { - name: 'lots of whitespace', - // Note: there's a tab after "tab->", which prettier wants to keep as a - // literal tab rather than \t. In the output, there should be a literal - // backslash-t. - input: gql` - query Foo($a: Int) { - user( - name: " tab-> yay" - other: """ - apple - bag - cat - """ - ) { - name - } - } - `, - output: - 'query Foo($a:Int){user(name:" tab->\\tyay",other:"apple\\n bag\\ncat"){name}}', - }, - ]; - cases.forEach(({ name, input, output }) => { - test(name, () => { - expect(printWithReducedWhitespace(input)).toEqual(output); - }); - }); -}); - -describe('hideLiterals', () => { - const cases = [ - { - name: 'full test', - input: gql` - query Foo($b: Int, $a: Boolean) { - user(name: "hello", age: 5) { - ...Bar - ... on User { - hello - bee - } - tz - aliased: name - } - } - - fragment Bar on User { - age @skip(if: $a) - ...Nested - } - - fragment Nested on User { - blah - } - `, - output: - 'query Foo($b:Int,$a:Boolean){user(name:"",age:0){...Bar...on User{hello bee}tz aliased:name}}' + - 'fragment Bar on User{age@skip(if:$a)...Nested}fragment Nested on User{blah}', - }, - ]; - cases.forEach(({ name, input, output }) => { - test(name, () => { - expect(printWithReducedWhitespace(hideLiterals(input))).toEqual(output); - }); - }); -}); - describe('aggressive signature', () => { function aggressive(ast: DocumentNode, operationName: string): string { return printWithReducedWhitespace( diff --git a/packages/apollo-graphql/src/__tests__/transforms.test.ts b/packages/apollo-graphql/src/__tests__/transforms.test.ts new file mode 100644 index 00000000000..e0ad5e471d3 --- /dev/null +++ b/packages/apollo-graphql/src/__tests__/transforms.test.ts @@ -0,0 +1,77 @@ +import { default as gql, disableFragmentWarnings } from 'graphql-tag'; + +import { printWithReducedWhitespace, hideLiterals } from '../transforms'; + +// The gql duplicate fragment warning feature really is just warnings; nothing +// breaks if you turn it off in tests. +disableFragmentWarnings(); + +describe('printWithReducedWhitespace', () => { + const cases = [ + { + name: 'lots of whitespace', + // Note: there's a tab after "tab->", which prettier wants to keep as a + // literal tab rather than \t. In the output, there should be a literal + // backslash-t. + input: gql` + query Foo($a: Int) { + user( + name: " tab-> yay" + other: """ + apple + bag + cat + """ + ) { + name + } + } + `, + output: + 'query Foo($a:Int){user(name:" tab->\\tyay",other:"apple\\n bag\\ncat"){name}}', + }, + ]; + cases.forEach(({ name, input, output }) => { + test(name, () => { + expect(printWithReducedWhitespace(input)).toEqual(output); + }); + }); +}); + +describe('hideLiterals', () => { + const cases = [ + { + name: 'full test', + input: gql` + query Foo($b: Int, $a: Boolean) { + user(name: "hello", age: 5) { + ...Bar + ... on User { + hello + bee + } + tz + aliased: name + } + } + + fragment Bar on User { + age @skip(if: $a) + ...Nested + } + + fragment Nested on User { + blah + } + `, + output: + 'query Foo($b:Int,$a:Boolean){user(name:"",age:0){...Bar...on User{hello bee}tz aliased:name}}' + + 'fragment Bar on User{age@skip(if:$a)...Nested}fragment Nested on User{blah}', + }, + ]; + cases.forEach(({ name, input, output }) => { + test(name, () => { + expect(printWithReducedWhitespace(hideLiterals(input))).toEqual(output); + }); + }); +}); diff --git a/packages/apollo-graphql/src/__tests__/tsconfig.json b/packages/apollo-graphql/src/__tests__/tsconfig.json new file mode 100644 index 00000000000..428259da813 --- /dev/null +++ b/packages/apollo-graphql/src/__tests__/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../../tsconfig.test.base", + "include": ["**/*"], + "references": [ + { "path": "../../" } + ] +} diff --git a/packages/apollo-graphql/src/index.ts b/packages/apollo-graphql/src/index.ts new file mode 100644 index 00000000000..8161d6647f0 --- /dev/null +++ b/packages/apollo-graphql/src/index.ts @@ -0,0 +1 @@ +export { defaultEngineReportingSignature } from './signature'; diff --git a/packages/apollo-graphql/src/signature.ts b/packages/apollo-graphql/src/signature.ts new file mode 100644 index 00000000000..8ddc71fe9f2 --- /dev/null +++ b/packages/apollo-graphql/src/signature.ts @@ -0,0 +1,68 @@ +// In Engine, we want to group requests making the same query together, and +// treat different queries distinctly. But what does it mean for two queries to +// be "the same"? And what if you don't want to send the full text of the query +// to Apollo Engine's servers, either because it contains sensitive data or +// because it contains extraneous operations or fragments? +// +// To solve these problems, EngineReportingAgent has the concept of +// "signatures". We don't (by default) send the full query string of queries to +// the Engine servers. Instead, each trace has its query string's "signature". +// +// You can specify any function mapping a GraphQL query AST (DocumentNode) to +// string as your signature algorithm by providing it as the 'signature' option +// to the EngineReportingAgent constructor. Ideally, your signature should be a +// valid GraphQL query, though as of now the Engine servers do not re-parse your +// signature and do not expect it to match the execution tree in the trace. +// +// This module utilizes several AST transformations from the adjacent +// 'transforms' module (which are also for writing your own signature method). + +// - dropUnusedDefinitions, which removes operations and fragments that +// aren't going to be used in execution +// - hideLiterals, which replaces all numeric and string literals as well +// as list and object input values with "empty" values +// - removeAliases, which removes field aliasing from the query +// - sortAST, which sorts the children of most multi-child nodes +// consistently +// - printWithReducedWhitespace, a variant on graphql-js's 'print' +// which gets rid of unneeded whitespace +// +// defaultSignature consists of applying all of these building blocks. +// +// Historical note: the default signature algorithm of the Go engineproxy +// performed all of the above operations, and the Engine servers then re-ran a +// mostly identical signature implementation on received traces. This was +// primarily to deal with edge cases where some users used literal interpolation +// instead of GraphQL variables, included randomized alias names, etc. In +// addition, the servers relied on the fact that dropUnusedDefinitions had been +// called in order (and that the signature could be parsed as GraphQL) to +// extract the name of the operation for display. This caused confusion, as the +// query document shown in the Engine UI wasn't the same as the one actually +// sent. apollo-engine-reporting uses a new reporting API which requires it to +// explicitly include the operation name with each signature; this means that +// the server no longer needs to parse the signature or run its own signature +// algorithm on it, and the details of the signature algorithm are now up to the +// reporting agent. + +import { DocumentNode } from 'graphql'; +import { + printWithReducedWhitespace, + dropUnusedDefinitions, + removeAliases, + sortAST, + hideLiterals, +} from './transforms'; + +// The default signature function consists of removing unused definitions +// and whitespace. +// XXX consider caching somehow +export function defaultEngineReportingSignature( + ast: DocumentNode, + operationName: string, +): string { + return printWithReducedWhitespace( + sortAST( + removeAliases(hideLiterals(dropUnusedDefinitions(ast, operationName))), + ), + ); +} diff --git a/packages/apollo-engine-reporting/src/signature.ts b/packages/apollo-graphql/src/transforms.ts similarity index 66% rename from packages/apollo-engine-reporting/src/signature.ts rename to packages/apollo-graphql/src/transforms.ts index 64ca5389d17..c8565f5c37f 100644 --- a/packages/apollo-engine-reporting/src/signature.ts +++ b/packages/apollo-graphql/src/transforms.ts @@ -1,71 +1,26 @@ -// XXX maybe this should just be its own graphql-signature package - -// In Engine, we want to group requests making the same query together, and -// treat different queries distinctly. But what does it mean for two queries to -// be "the same"? And what if you don't want to send the full text of the query -// to Apollo Engine's servers, either because it contains sensitive data or -// because it contains extraneous operations or fragments? -// -// To solve these problems, EngineReportingAgent has the concept of -// "signatures". We don't (by default) send the full query string of queries to -// the Engine servers. Instead, each trace has its query string's "signature". -// -// You can specify any function mapping a GraphQL query AST (DocumentNode) to -// string as your signature algorithm by providing it as the 'signature' option -// to the EngineReportingAgent constructor. Ideally, your signature should be a -// valid GraphQL query, though as of now the Engine servers do not re-parse your -// signature and do not expect it to match the execution tree in the trace. -// -// This file provides several useful building blocks for writing your own -// signature function. These are: -// -// - dropUnusedDefinitions, which removes operations and fragments that -// aren't going to be used in execution -// - hideLiterals, which replaces all numeric and string literals as well -// as list and object input values with "empty" values -// - removeAliases, which removes field aliasing from the query -// - sortAST, which sorts the children of most multi-child nodes -// consistently -// - printWithReducedWhitespace, a variant on graphql-js's 'print' -// which gets rid of unneeded whitespace -// -// defaultSignature consists of applying all of these building blocks. -// -// Historical note: the default signature algorithm of the Go engineproxy -// performed all of the above operations, and the Engine servers then re-ran a -// mostly identical signature implementation on received traces. This was -// primarily to deal with edge cases where some users used literal interpolation -// instead of GraphQL variables, included randomized alias names, etc. In -// addition, the servers relied on the fact that dropUnusedDefinitions had been -// called in order (and that the signature could be parsed as GraphQL) to -// extract the name of the operation for display. This caused confusion, as the -// query document shown in the Engine UI wasn't the same as the one actually -// sent. apollo-engine-reporting uses a new reporting API which requires it to -// explicitly include the operation name with each signature; this means that -// the server no longer needs to parse the signature or run its own signature -// algorithm on it, and the details of the signature algorithm are now up to the -// reporting agent. - -import { sortBy, ListIteratee } from 'lodash'; - +import { visit } from 'graphql/language/visitor'; import { - print, - visit, DocumentNode, + FloatValueNode, + IntValueNode, + StringValueNode, OperationDefinitionNode, SelectionSetNode, - FieldNode, FragmentSpreadNode, InlineFragmentNode, - FragmentDefinitionNode, DirectiveNode, - IntValueNode, - FloatValueNode, - StringValueNode, - ListValueNode, + FieldNode, + FragmentDefinitionNode, ObjectValueNode, - separateOperations, -} from 'graphql'; + ListValueNode, +} from 'graphql/language/ast'; +import { print } from 'graphql/language/printer'; +import { separateOperations } from 'graphql/utilities'; +// We'll only fetch the `ListIteratee` type from the `@types/lodash`, but get +// `sortBy` from the modularized version of the package to avoid bringing in +// all of `lodash`. +import { ListIteratee } from 'lodash'; +import sortBy from 'lodash.sortby'; // Replace numeric, string, list, and object literals with "empty" // values. Leaves enums alone (since there's no consistent "zero" enum). This @@ -93,6 +48,22 @@ export function hideLiterals(ast: DocumentNode): DocumentNode { }); } +// In the same spirit as the similarly named `hideLiterals` function, only +// hide string and numeric literals. +export function hideStringAndNumericLiterals(ast: DocumentNode): DocumentNode { + return visit(ast, { + IntValue(node: IntValueNode): IntValueNode { + return { ...node, value: '0' }; + }, + FloatValue(node: FloatValueNode): FloatValueNode { + return { ...node, value: '0' }; + }, + StringValue(node: StringValueNode): StringValueNode { + return { ...node, value: '', block: false }; + }, + }); +} + // A GraphQL query may contain multiple named operations, with the operation to // use specified separately by the client. This transformation drops unused // operations from the query, as well as any fragment definitions that are not @@ -226,17 +197,3 @@ export function printWithReducedWhitespace(ast: DocumentNode): string { JSON.stringify(Buffer.from(hex, 'hex').toString('utf8')), ); } - -// The default signature function consists of removing unused definitions -// and whitespace. -// XXX consider caching somehow -export function defaultSignature( - ast: DocumentNode, - operationName: string, -): string { - return printWithReducedWhitespace( - sortAST( - removeAliases(hideLiterals(dropUnusedDefinitions(ast, operationName))), - ), - ); -} diff --git a/packages/apollo-graphql/tsconfig.json b/packages/apollo-graphql/tsconfig.json new file mode 100644 index 00000000000..4fcb63fe14f --- /dev/null +++ b/packages/apollo-graphql/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["src/**/*"], + "exclude": ["**/__tests__", "**/__mocks__"], + "references": [] +} diff --git a/packages/apollo-server-azure-functions/.npmignore b/packages/apollo-server-azure-functions/.npmignore new file mode 100644 index 00000000000..a165046d359 --- /dev/null +++ b/packages/apollo-server-azure-functions/.npmignore @@ -0,0 +1,6 @@ +* +!src/**/* +!dist/**/* +dist/**/*.test.* +!package.json +!README.md diff --git a/packages/apollo-server-azure-functions/README.md b/packages/apollo-server-azure-functions/README.md new file mode 100644 index 00000000000..b7ed7384805 --- /dev/null +++ b/packages/apollo-server-azure-functions/README.md @@ -0,0 +1,154 @@ +--- +title: Azure Functions +description: Setting up Apollo Server with Azure Functions +--- + +This is the Azure functions integration of GraphQL Server. Apollo Server is a community-maintained open-source GraphQL server that works with many Node.js HTTP server frameworks. [Read the docs](https://www.apollographql.com/docs/apollo-server/v2). [Read the CHANGELOG](https://github.com/apollographql/apollo-server/blob/master/CHANGELOG.md). + +```sh +npm install apollo-server-azure-functions@alpha graphql +``` + +## Writing azure function + +Azure functions currently support two runtime versions. This package assumes that function is running under **runtime 2.0**. + +Azure functions typically consist of at least 2 files - index.js (function handler definition) and function.json (function settings and bindings). +For more information about azure functions development model in general, refer to [official Azure functions docs](https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node). + +Example index.js: + +```js +const { gql, ApolloServer } = require("apollo-server-azure-functions"); + +// Construct a schema, using GraphQL schema language +const typeDefs = gql` + type Query { + hello: String + } +`; + +// A map of functions which return data for the schema. +const resolvers = { + Query: { + hello: () => "world" + } +}; + +const server = new ApolloServer({ typeDefs, resolvers }); + +module.exports = server.createHandler(); +``` + +Example function.json: +```json +{ + "disabled": false, + "bindings": [ + { + "authLevel": "function", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] +} +``` + +It is important to set output binding name to '$return' for apollo-server-azure-function to work correctly. + +## Modifying the Azure Function Response (Enable CORS) + +To enable CORS the response HTTP headers need to be modified. To accomplish this use the `cors` option. + +```js +const { ApolloServer, gql } = require('apollo-server-azure-functions'); + +// Construct a schema, using GraphQL schema language +const typeDefs = gql` + type Query { + hello: String + } +`; + +// Provide resolver functions for your schema fields +const resolvers = { + Query: { + hello: () => 'Hello world!', + }, +}; + +const server = new ApolloServer({ + typeDefs, + resolvers, +}); + +module.exports = server.createHandler({ + cors: { + origin: '*', + credentials: true, + }, +}); +``` + +To enable CORS response for requests with credentials (cookies, http authentication) the allow origin header must equal the request origin and the allow credential header must be set to true. + +```js +const { ApolloServer, gql } = require('apollo-server-azure-functions'); + +// Construct a schema, using GraphQL schema language +const typeDefs = gql` + type Query { + hello: String + } +`; + +// Provide resolver functions for your schema fields +const resolvers = { + Query: { + hello: () => 'Hello world!', + }, +}; + +const server = new ApolloServer({ + typeDefs, + resolvers, +}); + +module.exports = server.createHandler({ + cors: { + origin: true, + credentials: true, + }, +}); +``` + +### Cors Options + +The options correspond to the [express cors configuration](https://github.com/expressjs/cors#configuration-options) with the following fields(all are optional): + +* `origin`: boolean | string | string[] +* `methods`: string | string[] +* `allowedHeaders`: string | string[] +* `exposedHeaders`: string | string[] +* `credentials`: boolean +* `maxAge`: number + +## Principles + +GraphQL Server is built with the following principles in mind: + +* **By the community, for the community**: GraphQL Server's development is driven by the needs of developers +* **Simplicity**: by keeping things simple, GraphQL Server is easier to use, easier to contribute to, and more secure +* **Performance**: GraphQL Server is well-tested and production-ready - no modifications needed + +Anyone is welcome to contribute to GraphQL Server, just read [CONTRIBUTING.md](https://github.com/apollographql/apollo-server/blob/master/CONTRIBUTING.md), take a look at the [roadmap](https://github.com/apollographql/apollo-server/blob/master/ROADMAP.md) and make your first PR! diff --git a/packages/apollo-server-azure-functions/package.json b/packages/apollo-server-azure-functions/package.json new file mode 100644 index 00000000000..1478ae2b661 --- /dev/null +++ b/packages/apollo-server-azure-functions/package.json @@ -0,0 +1,39 @@ +{ + "name": "apollo-server-azure-functions", + "version": "2.3.3", + "description": "Production-ready Node.js GraphQL server for Azure Functions", + "keywords": [ + "GraphQL", + "Apollo", + "Server", + "Azure", + "Javascript" + ], + "author": "opensource@apollographql.com", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-azure-functions" + }, + "homepage": "https://github.com/apollographql/apollo-server#readme", + "bugs": { + "url": "https://github.com/apollographql/apollo-server/issues" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "engines": { + "node": ">=6" + }, + "dependencies": { + "@apollographql/graphql-playground-html": "^1.6.6", + "apollo-server-core": "file:../apollo-server-core", + "apollo-server-env": "file:../apollo-server-env", + "graphql-tools": "^4.0.0" + }, + "devDependencies": { + "apollo-server-integration-testsuite": "file:../apollo-server-integration-testsuite" + }, + "peerDependencies": { + "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0" + } +} diff --git a/packages/apollo-server-azure-functions/src/ApolloServer.ts b/packages/apollo-server-azure-functions/src/ApolloServer.ts new file mode 100644 index 00000000000..0a257b68c17 --- /dev/null +++ b/packages/apollo-server-azure-functions/src/ApolloServer.ts @@ -0,0 +1,165 @@ +import { + HttpContext, + FunctionRequest, + FunctionResponse, +} from './azureFunctions'; +import { ApolloServerBase } from 'apollo-server-core'; +import { GraphQLOptions, Config } from 'apollo-server-core'; +import { + renderPlaygroundPage, + RenderPageOptions as PlaygroundRenderPageOptions, +} from '@apollographql/graphql-playground-html'; + +import { graphqlAzureFunction } from './azureFunctionApollo'; + +export interface CreateHandlerOptions { + cors?: { + origin?: boolean | string | string[]; + methods?: string | string[]; + allowedHeaders?: string | string[]; + exposedHeaders?: string | string[]; + credentials?: boolean; + maxAge?: number; + }; +} + +export class ApolloServer extends ApolloServerBase { + // If you feel tempted to add an option to this constructor. Please consider + // another place, since the documentation becomes much more complicated when + // the constructor is not longer shared between all integration + constructor(options: Config) { + if (process.env.ENGINE_API_KEY || options.engine) { + options.engine = { + sendReportsImmediately: true, + ...(typeof options.engine !== 'boolean' ? options.engine : {}), + }; + } + super(options); + } + + // This translates the arguments from the middleware into graphQL options It + // provides typings for the integration specific behavior, ideally this would + // be propagated with a generic to the super class + createGraphQLServerOptions( + request: FunctionRequest, + context: HttpContext, + ): Promise { + return super.graphQLServerOptions({ request, context }); + } + + public createHandler({ cors }: CreateHandlerOptions = { cors: undefined }) { + // We will kick off the `willStart` event once for the server, and then + // await it before processing any requests by incorporating its `await` into + // the GraphQLServerOptions function which is called before each request. + const promiseWillStart = this.willStart(); + + const corsHeaders: FunctionResponse['headers'] = {}; + + if (cors) { + if (cors.methods) { + if (typeof cors.methods === 'string') { + corsHeaders['Access-Control-Allow-Methods'] = cors.methods; + } else if (Array.isArray(cors.methods)) { + corsHeaders['Access-Control-Allow-Methods'] = cors.methods.join(','); + } + } + + if (cors.allowedHeaders) { + if (typeof cors.allowedHeaders === 'string') { + corsHeaders['Access-Control-Allow-Headers'] = cors.allowedHeaders; + } else if (Array.isArray(cors.allowedHeaders)) { + corsHeaders[ + 'Access-Control-Allow-Headers' + ] = cors.allowedHeaders.join(','); + } + } + + if (cors.exposedHeaders) { + if (typeof cors.exposedHeaders === 'string') { + corsHeaders['Access-Control-Expose-Headers'] = cors.exposedHeaders; + } else if (Array.isArray(cors.exposedHeaders)) { + corsHeaders[ + 'Access-Control-Expose-Headers' + ] = cors.exposedHeaders.join(','); + } + } + + if (cors.credentials) { + corsHeaders['Access-Control-Allow-Credentials'] = 'true'; + } + if (cors.maxAge) { + corsHeaders['Access-Control-Max-Age'] = cors.maxAge; + } + } + + return (context: HttpContext, req: FunctionRequest) => { + if (cors && cors.origin) { + if (typeof cors.origin === 'string') { + corsHeaders['Access-Control-Allow-Origin'] = cors.origin; + } else if ( + typeof cors.origin === 'boolean' || + (Array.isArray(cors.origin) && + cors.origin.includes( + req.headers['Origin'] || req.headers['origin'], + )) + ) { + corsHeaders['Access-Control-Allow-Origin'] = + req.headers['Origin'] || req.headers['origin']; + } + + if (!cors.allowedHeaders) { + corsHeaders['Access-Control-Allow-Headers'] = + req.headers['Access-Control-Request-Headers']; + } + } + + if (req.method === 'OPTIONS') { + context.done(null, { + body: '', + status: 204, + headers: corsHeaders, + }); + return; + } + + if (this.playgroundOptions && req.method === 'GET') { + const acceptHeader = req.headers['Accept'] || req.headers['accept']; + if (acceptHeader && acceptHeader.includes('text/html')) { + const path = req.originalUrl || '/'; + + const playgroundRenderPageOptions: PlaygroundRenderPageOptions = { + endpoint: path, + ...this.playgroundOptions, + }; + const body = renderPlaygroundPage(playgroundRenderPageOptions); + context.done(null, { + body: body, + status: 200, + headers: { + 'Content-Type': 'text/html', + ...corsHeaders, + }, + }); + return; + } + } + + const callbackFilter = (error?: any, output?: FunctionResponse) => { + context.done( + error, + output && { + ...output, + headers: { + ...output.headers, + ...corsHeaders, + }, + }, + ); + }; + graphqlAzureFunction(async () => { + await promiseWillStart; + return this.createGraphQLServerOptions(req, context); + })(context, req, callbackFilter); + }; + } +} diff --git a/packages/apollo-server-azure-functions/src/__tests__/azureFunctionApollo.test.ts b/packages/apollo-server-azure-functions/src/__tests__/azureFunctionApollo.test.ts new file mode 100644 index 00000000000..8a918178157 --- /dev/null +++ b/packages/apollo-server-azure-functions/src/__tests__/azureFunctionApollo.test.ts @@ -0,0 +1,155 @@ +import { ApolloServer } from '../ApolloServer'; +import testSuite, { + schema as Schema, + CreateAppOptions, +} from 'apollo-server-integration-testsuite'; +import { Config } from 'apollo-server-core'; +import url from 'url'; +import { IncomingMessage, ServerResponse } from 'http'; + +const createAzureFunction = (options: CreateAppOptions = {}) => { + const server = new ApolloServer( + (options.graphqlOptions as Config) || { schema: Schema }, + ); + + const handler = server.createHandler(); + + return (req: IncomingMessage, res: ServerResponse) => { + // return 404 if path is /bogus-route to pass the test, azure doesn't have paths + if (req.url.includes('/bogus-route')) { + res.statusCode = 404; + return res.end(); + } + + let body = ''; + req.on('data', chunk => (body += chunk)); + req.on('end', () => { + const urlObject = url.parse(req.url, true); + const request = { + method: req.method, + body: body && JSON.parse(body), + path: req.url, + query: urlObject.query, + headers: req.headers, + }; + const context = { + done(error, result) { + if (error) throw error; + res.statusCode = result.status; + for (let key in result.headers) { + if (result.headers.hasOwnProperty(key)) { + res.setHeader(key, result.headers[key]); + } + } + res.write(result.body); + res.end(); + }, + }; + handler(context as any, request as any); + }); + }; +}; + +describe('integration:AzureFunctions', () => { + testSuite(createAzureFunction); + + it('can append CORS headers to GET request', () => { + const server = new ApolloServer({ schema: Schema }); + const handler = server.createHandler({ + cors: { + origin: 'CORSOrigin', + methods: ['GET', 'POST', 'PUT'], + allowedHeaders: 'AllowedCORSHeader1,AllowedCORSHeader1', + exposedHeaders: 'ExposedCORSHeader1,ExposedCORSHeader2', + credentials: true, + maxAge: 42, + }, + }); + const expectedResult = { + testString: 'it works', + }; + const query = { + query: 'query test{ testString }', + }; + const request = { + method: 'GET', + body: null, + path: '/graphql', + query: query, + headers: {}, + }; + const context = { + done(error, result) { + if (error) throw error; + expect(result.status).toEqual(200); + expect(result.body).toEqual(expectedResult); + expect(result.headers['Access-Control-Allow-Origin']).toEqual( + 'CORSOrigin', + ); + expect(result.headers['Access-Control-Allow-Methods']).toEqual( + 'GET,POST,PUT', + ); + expect(result.headers['Access-Control-Allow-Headers']).toEqual( + 'AllowedCORSHeader1,AllowedCORSHeader1', + ); + expect(result.headers['Access-Control-Expose-Headers']).toEqual( + 'ExposedCORSHeader1,ExposedCORSHeader2', + ); + expect(result.headers['Access-Control-Allow-Credentials']).toEqual( + 'true', + ); + expect(result.headers['Access-Control-Max-Age']).toEqual(42); + }, + }; + handler(context as any, request as any); + }); + + it('can handle OPTIONS request with CORS headers', () => { + const server = new ApolloServer({ schema: Schema }); + const handler = server.createHandler({ + cors: { + allowedHeaders: 'AllowedCORSHeader1,AllowedCORSHeader1', + }, + }); + const request = { + method: 'OPTIONS', + body: null, + path: '/graphql', + query: null, + headers: {}, + }; + const context = { + done(error, result) { + if (error) throw error; + expect(result.status).toEqual(204); + expect(result.headers['Access-Control-Allow-Headers']).toEqual( + 'AllowedCORSHeader1,AllowedCORSHeader1', + ); + }, + }; + handler(context as any, request as any); + }); + + it('can return playground html', () => { + const server = new ApolloServer({ schema: Schema }); + const handler = server.createHandler({}); + const request = { + method: 'GET', + body: null, + path: '/', + query: null, + headers: { + Accept: 'text/html', + }, + }; + const context = { + done(error, result) { + if (error) throw error; + expect(result.status).toEqual(200); + expect(result.body).toMatch(/GraphQL Playground/gi); + expect(result.headers['Content-Type']).toEqual('text/html'); + }, + }; + handler(context as any, request as any); + }); +}); diff --git a/packages/apollo-server-azure-functions/src/azureFunctionApollo.ts b/packages/apollo-server-azure-functions/src/azureFunctionApollo.ts new file mode 100644 index 00000000000..fd680501701 --- /dev/null +++ b/packages/apollo-server-azure-functions/src/azureFunctionApollo.ts @@ -0,0 +1,87 @@ +import { + HttpContext, + FunctionRequest, + FunctionResponse, +} from './azureFunctions'; +import { + GraphQLOptions, + HttpQueryError, + runHttpQuery, +} from 'apollo-server-core'; +import { Headers } from 'apollo-server-env'; + +export interface AzureFunctionGraphQLOptionsFunction { + (request: FunctionRequest, context: HttpContext): + | GraphQLOptions + | Promise; +} + +export interface AzureFunctionHandler { + ( + context: HttpContext, + request: FunctionRequest, + callback: (err?: any, output?: FunctionResponse) => void, + ): void; +} + +export function graphqlAzureFunction( + options: GraphQLOptions | AzureFunctionGraphQLOptionsFunction, +): AzureFunctionHandler { + if (!options) { + throw new Error('Apollo Server requires options.'); + } + + if (arguments.length > 1) { + throw new Error( + `Apollo Server expects exactly one argument, got ${arguments.length}`, + ); + } + + const graphqlHandler: AzureFunctionHandler = ( + context, + request, + callback, + ): void => { + if (request.method === 'POST' && !request.body) { + callback(null, { + body: 'POST body missing.', + status: 500, + }); + return; + } + runHttpQuery([request, context], { + method: request.method, + options: options, + query: + request.method === 'POST' && request.body + ? request.body + : request.query, + request: { + url: request.originalUrl, + method: request.method, + headers: new Headers(request.headers), + }, + }).then( + ({ graphqlResponse, responseInit }) => { + callback(null, { + body: graphqlResponse, + status: 200, + headers: responseInit.headers, + }); + }, + (error: HttpQueryError) => { + if ('HttpQueryError' !== error.name) { + callback(error); + } else { + callback(null, { + body: error.message, + status: error.statusCode, + headers: error.headers, + }); + } + }, + ); + }; + + return graphqlHandler; +} diff --git a/packages/apollo-server-azure-functions/src/azureFunctions.d.ts b/packages/apollo-server-azure-functions/src/azureFunctions.d.ts new file mode 100644 index 00000000000..c9dbd78a553 --- /dev/null +++ b/packages/apollo-server-azure-functions/src/azureFunctions.d.ts @@ -0,0 +1,151 @@ +export type Context = { + invocationId: string; + bindingData: any; + bindings: any; + + log: (text: any) => void; + done: (err?: any, output?: object) => void; +}; + +export type HttpMethod = + | 'GET' + | 'POST' + | 'PUT' + | 'DELETE' + | 'HEAD' + | 'OPTIONS' + | 'TRACE' + | 'CONNECT' + | 'PATCH'; + +export type FunctionRequest = { + originalUrl: string; + method: HttpMethod; + query: { [key: string]: string }; + headers: { [name: string]: string }; + body: any; + rawbody: any; +}; + +export type FunctionResponse = { + body?: any; + status?: number; + headers?: { + 'content-type'?: string; + 'content-length'?: HttpStatusCodes | number; + 'content-disposition'?: string; + 'content-encoding'?: string; + 'content-language'?: string; + 'content-range'?: string; + 'content-location'?: string; + 'content-md5'?: Buffer; + expires?: Date; + 'last-modified'?: Date; + [name: string]: any; + }; +}; + +export enum HttpStatusCodes { + // 1XX Informational + Continue = 100, + SwitchingProtocols = 101, + Processing = 102, + Checkpoint = 103, + + // 2XX Success + OK = 200, + Created = 201, + Accepted = 202, + NonAuthoritativeInformation = 203, + NoContent = 204, + ResetContent = 205, + PartialContent = 206, + MultiStatus = 207, + AlreadyReported = 208, + IMUsed = 226, + + // 3XX Redirection + MultipleChoices = 300, + MovedPermanently = 301, + Found = 302, + SeeOther = 303, + NotModified = 304, + UseProxy = 305, + SwitchProxy = 306, + TemporaryRedirect = 307, + PermanentRedirect = 308, + + // 4XX Client Error + BadRequest = 400, + Unauthorized = 401, + PaymentRequired = 402, + Forbidden = 403, + NotFound = 404, + MethodNotAllowed = 405, + NotAcceptable = 406, + ProxyAuthenticationRequired = 407, + RequestTimeout = 408, + Conflict = 409, + Gone = 410, + LengthRequired = 411, + PreconditionFailed = 412, + PayloadTooLarge = 413, + URITooLong = 414, + UnsupportedMediaType = 415, + RangeNotSatisfiable = 416, + ExpectationFailed = 417, + ImATeapot = 418, + MethodFailure = 420, + EnhanceYourCalm = 420, + MisdirectedRequest = 421, + UnprocessableEntity = 422, + Locked = 423, + FailedDependency = 424, + UpgradeRequired = 426, + PreconditionRequired = 428, + TooManyRequests = 429, + RequestHeaderFieldsTooLarge = 431, + LoginTimeout = 440, + NoResponse = 444, + RetryWith = 449, + BlockedByWindowsParentalControls = 450, + UnavailableForLegalReasons = 451, + Redirect = 451, + SSLCertificateError = 495, + SSLCertificateRequired = 496, + HttpRequestSentToHttpsPort = 497, + ClientClosedRequest = 499, + InvalidToken = 498, + TokenRequired = 499, + RequestWasForbiddenByAntivirus = 499, + + // 5XX Server Error + InternalServerError = 500, + NotImplemented = 501, + BadGateway = 502, + ServiceUnavailable = 503, + GatewayTimeout = 504, + HttpVersionNotSupported = 505, + VariantAlsoNegotiates = 506, + InsufficientStorage = 507, + LoopDetected = 508, + BandwidthLimitExceeded = 509, + NotExtended = 510, + NetworkAuthenticationRequired = 511, + UnknownError = 520, + WebServerIsDown = 521, + ConnectionTimedOut = 522, + OriginIsUnreachable = 523, + ATimeoutOccurred = 524, + SSLHandshakeFailed = 525, + InvalidSSLCertificate = 526, + SiteIsFrozen = 530, +} + +export type HttpContext = Context & { + res: FunctionResponse; +}; + +export interface FunctionHandler { + (context: HttpContext, request: FunctionRequest): void | Promise; +} diff --git a/packages/apollo-server-cloud-function/src/index.ts b/packages/apollo-server-azure-functions/src/index.ts similarity index 100% rename from packages/apollo-server-cloud-function/src/index.ts rename to packages/apollo-server-azure-functions/src/index.ts diff --git a/packages/apollo-server-cloud-function/tsconfig.json b/packages/apollo-server-azure-functions/tsconfig.json similarity index 100% rename from packages/apollo-server-cloud-function/tsconfig.json rename to packages/apollo-server-azure-functions/tsconfig.json diff --git a/packages/apollo-server-cache-memcached/package.json b/packages/apollo-server-cache-memcached/package.json index c0d5f84ca85..cdd034708be 100644 --- a/packages/apollo-server-cache-memcached/package.json +++ b/packages/apollo-server-cache-memcached/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-cache-memcached", - "version": "0.2.0-alpha.0", + "version": "0.2.2", "author": "opensource@apollographql.com", "license": "MIT", "repository": { diff --git a/packages/apollo-server-cache-memcached/src/__tests__/Memcached.test.ts b/packages/apollo-server-cache-memcached/src/__tests__/Memcached.test.ts index cc769325efb..d3d7d20b47e 100644 --- a/packages/apollo-server-cache-memcached/src/__tests__/Memcached.test.ts +++ b/packages/apollo-server-cache-memcached/src/__tests__/Memcached.test.ts @@ -2,6 +2,13 @@ jest.mock('memcached', () => require('memcached-mock')); import { MemcachedCache } from '../index'; -import { testKeyValueCache } from '../../../apollo-server-caching/src/__tests__/testsuite'; +import { + testKeyValueCache_Basics, + testKeyValueCache_Expiration, +} from '../../../apollo-server-caching/src/__tests__/testsuite'; -testKeyValueCache(new MemcachedCache('localhost')); +describe('Memcached', () => { + const cache = new MemcachedCache('localhost'); + testKeyValueCache_Basics(cache); + testKeyValueCache_Expiration(cache); +}); diff --git a/packages/apollo-server-cache-memcached/src/index.ts b/packages/apollo-server-cache-memcached/src/index.ts index a4b8498367e..e369be35d63 100644 --- a/packages/apollo-server-cache-memcached/src/index.ts +++ b/packages/apollo-server-cache-memcached/src/index.ts @@ -12,6 +12,7 @@ export class MemcachedCache implements KeyValueCache { constructor(serverLocation: Memcached.Location, options?: Memcached.options) { const client = new Memcached(serverLocation, options); // promisify client calls for convenience + client.del = promisify(client.del).bind(client); client.get = promisify(client.get).bind(client); client.set = promisify(client.set).bind(client); client.flush = promisify(client.flush).bind(client); @@ -21,17 +22,21 @@ export class MemcachedCache implements KeyValueCache { async set( key: string, - data: string, + value: string, options?: { ttl?: number }, ): Promise { const { ttl } = Object.assign({}, this.defaultSetOptions, options); - await this.client.set(key, data, ttl); + await this.client.set(key, value, ttl); } async get(key: string): Promise { return await this.client.get(key); } + async delete(key: string): Promise { + return await this.client.del(key); + } + async flush(): Promise { await this.client.flush(); } diff --git a/packages/apollo-server-cache-redis/package.json b/packages/apollo-server-cache-redis/package.json index c6b41b19f3d..681a5f1d0ea 100644 --- a/packages/apollo-server-cache-redis/package.json +++ b/packages/apollo-server-cache-redis/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-cache-redis", - "version": "0.2.0-alpha.0", + "version": "0.2.2", "author": "opensource@apollographql.com", "license": "MIT", "repository": { diff --git a/packages/apollo-server-cache-redis/src/__tests__/Redis.test.ts b/packages/apollo-server-cache-redis/src/__tests__/Redis.test.ts index 4e0ea455af3..d13f0d30ccd 100644 --- a/packages/apollo-server-cache-redis/src/__tests__/Redis.test.ts +++ b/packages/apollo-server-cache-redis/src/__tests__/Redis.test.ts @@ -3,6 +3,13 @@ jest.mock('redis', () => require('redis-mock')); jest.useFakeTimers(); // mocks out setTimeout that is used in redis-mock import { RedisCache } from '../index'; -import { testKeyValueCache } from '../../../apollo-server-caching/src/__tests__/testsuite'; +import { + testKeyValueCache_Basics, + testKeyValueCache_Expiration, +} from '../../../apollo-server-caching/src/__tests__/testsuite'; -testKeyValueCache(new RedisCache({ host: 'localhost' })); +describe('Redis', () => { + const cache = new RedisCache({ host: 'localhost' }); + testKeyValueCache_Basics(cache); + testKeyValueCache_Expiration(cache); +}); diff --git a/packages/apollo-server-cache-redis/src/index.ts b/packages/apollo-server-cache-redis/src/index.ts index 7b81a7169aa..7c0b1b61330 100644 --- a/packages/apollo-server-cache-redis/src/index.ts +++ b/packages/apollo-server-cache-redis/src/index.ts @@ -3,7 +3,7 @@ import Redis from 'redis'; import { promisify } from 'util'; import DataLoader from 'dataloader'; -export class RedisCache implements KeyValueCache { +export class RedisCache implements KeyValueCache { // FIXME: Replace any with proper promisified type readonly client: any; readonly defaultSetOptions = { @@ -16,6 +16,7 @@ export class RedisCache implements KeyValueCache { const client = Redis.createClient(options) as any; // promisify client calls for convenience + client.del = promisify(client.del).bind(client); client.mget = promisify(client.mget).bind(client); client.set = promisify(client.set).bind(client); client.flushdb = promisify(client.flushdb).bind(client); @@ -30,11 +31,11 @@ export class RedisCache implements KeyValueCache { async set( key: string, - data: string, + value: string, options?: { ttl?: number }, ): Promise { const { ttl } = Object.assign({}, this.defaultSetOptions, options); - await this.client.set(key, data, 'EX', ttl); + await this.client.set(key, value, 'EX', ttl); } async get(key: string): Promise { @@ -46,6 +47,10 @@ export class RedisCache implements KeyValueCache { return; } + async delete(key: string): Promise { + return await this.client.del(key); + } + async flush(): Promise { await this.client.flushdb(); } diff --git a/packages/apollo-server-caching/README.md b/packages/apollo-server-caching/README.md index 5b9aaac8349..26980ea60ef 100644 --- a/packages/apollo-server-caching/README.md +++ b/packages/apollo-server-caching/README.md @@ -16,7 +16,9 @@ export interface KeyValueCache { } ``` -## Running test suite +## Testing cache implementations + +### Test helpers You can export and run a jest test suite from `apollo-server-caching` to test your implementation: @@ -28,4 +30,15 @@ import { testKeyValueCache } from 'apollo-server-caching'; testKeyValueCache(new MemcachedCache('localhost')); ``` +The default `testKeyValueCache` helper will run all key-value store tests on the specified store, including basic `get` and `set` functionality, along with time-based expunging rules. + +Some key-value cache implementations may not be able to support the full suite of tests (for example, some tests might not be able to expire based on time). For those cases, there are more granular implementations which can be used: + +* `testKeyValueCache_Basic` +* `testKeyValueCache_Expiration` + +For more details, consult the [source for `apollo-server-caching`](./src/__tests__/testsuite.ts). + +### Running tests + Run tests with `jest --verbose` diff --git a/packages/apollo-server-caching/package.json b/packages/apollo-server-caching/package.json index e88f5810aa9..8f9b72a663d 100644 --- a/packages/apollo-server-caching/package.json +++ b/packages/apollo-server-caching/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-caching", - "version": "0.2.0-alpha.0", + "version": "0.2.2", "author": "opensource@apollographql.com", "license": "MIT", "repository": { @@ -17,6 +17,6 @@ "node": ">=6" }, "dependencies": { - "lru-cache": "^4.1.3" + "lru-cache": "^5.0.0" } } diff --git a/packages/apollo-server-caching/src/InMemoryLRUCache.ts b/packages/apollo-server-caching/src/InMemoryLRUCache.ts index 32517a7f84d..b75dfcb37d0 100644 --- a/packages/apollo-server-caching/src/InMemoryLRUCache.ts +++ b/packages/apollo-server-caching/src/InMemoryLRUCache.ts @@ -1,21 +1,46 @@ import LRU from 'lru-cache'; import { KeyValueCache } from './KeyValueCache'; -export class InMemoryLRUCache implements KeyValueCache { - private store: LRU.Cache; +export class InMemoryLRUCache implements KeyValueCache { + private store: LRU.Cache; // FIXME: Define reasonable default max size of the cache constructor({ maxSize = Infinity }: { maxSize?: number } = {}) { - this.store = LRU({ + this.store = new LRU({ max: maxSize, - length: item => item.length, + length(item) { + if (Array.isArray(item) || typeof item === 'string') { + return item.length; + } + + // If it's an object, we'll use the length to get an approximate, + // relative size of what it would take to store it. It's certainly not + // 100% accurate, but it's a very, very fast implementation and it + // doesn't require bringing in other dependencies or logic which we need + // to maintain. In the future, we might consider something like: + // npm.im/object-sizeof, but this should be sufficient for now. + if (typeof item === 'object') { + return JSON.stringify(item).length; + } + + // Go with the lru-cache default "naive" size, in lieu anything better: + // https://github.com/isaacs/node-lru-cache/blob/a71be6cd/index.js#L17 + return 1; + }, }); } async get(key: string) { return this.store.get(key); } - async set(key: string, value: string) { - this.store.set(key, value); + async set(key: string, value: V, options?: { ttl?: number }) { + const maxAge = options && options.ttl && options.ttl * 1000; + this.store.set(key, value, maxAge); + } + async delete(key: string) { + this.store.del(key); + } + async flush(): Promise { + this.store.reset(); } } diff --git a/packages/apollo-server-caching/src/KeyValueCache.ts b/packages/apollo-server-caching/src/KeyValueCache.ts index 5896ce048f9..86c3e136273 100644 --- a/packages/apollo-server-caching/src/KeyValueCache.ts +++ b/packages/apollo-server-caching/src/KeyValueCache.ts @@ -1,4 +1,5 @@ -export interface KeyValueCache { - get(key: string): Promise; - set(key: string, value: string, options?: { ttl?: number }): Promise; +export interface KeyValueCache { + get(key: string): Promise; + set(key: string, value: V, options?: { ttl?: number }): Promise; + delete(key: string): Promise; } diff --git a/packages/apollo-server-caching/src/__tests__/InMemoryLRUCache.test.ts b/packages/apollo-server-caching/src/__tests__/InMemoryLRUCache.test.ts new file mode 100644 index 00000000000..f6dd0a1dfaf --- /dev/null +++ b/packages/apollo-server-caching/src/__tests__/InMemoryLRUCache.test.ts @@ -0,0 +1,11 @@ +import { + testKeyValueCache_Basics, + testKeyValueCache_Expiration, +} from '../../../apollo-server-caching/src/__tests__/testsuite'; +import { InMemoryLRUCache } from '../InMemoryLRUCache'; + +describe('InMemoryLRUCache', () => { + const cache = new InMemoryLRUCache(); + testKeyValueCache_Basics(cache); + testKeyValueCache_Expiration(cache); +}); diff --git a/packages/apollo-server-caching/src/__tests__/testsuite.ts b/packages/apollo-server-caching/src/__tests__/testsuite.ts index 8895d8d2ae4..3ee62aa22e0 100644 --- a/packages/apollo-server-caching/src/__tests__/testsuite.ts +++ b/packages/apollo-server-caching/src/__tests__/testsuite.ts @@ -1,7 +1,28 @@ import { advanceTimeBy, mockDate, unmockDate } from '__mocks__/date'; -export function testKeyValueCache(keyValueCache: any) { - describe('KeyValueCache Test Suite', () => { +export function testKeyValueCache_Basics(keyValueCache: any) { + describe('basic cache functionality', () => { + beforeEach(() => { + keyValueCache.flush(); + }); + + it('can do a basic get and set', async () => { + await keyValueCache.set('hello', 'world'); + expect(await keyValueCache.get('hello')).toBe('world'); + expect(await keyValueCache.get('missing')).toBeUndefined(); + }); + + it('can do a basic set and delete', async () => { + await keyValueCache.set('hello', 'world'); + expect(await keyValueCache.get('hello')).toBe('world'); + await keyValueCache.delete('hello'); + expect(await keyValueCache.get('hello')).toBeUndefined(); + }); + }); +} + +export function testKeyValueCache_Expiration(keyValueCache: any) { + describe('time-based cache expunging', () => { beforeAll(() => { mockDate(); jest.useFakeTimers(); @@ -16,12 +37,6 @@ export function testKeyValueCache(keyValueCache: any) { keyValueCache.close(); }); - it('can do a basic get and set', async () => { - await keyValueCache.set('hello', 'world'); - expect(await keyValueCache.get('hello')).toBe('world'); - expect(await keyValueCache.get('missing')).toBeUndefined(); - }); - it('is able to expire keys based on ttl', async () => { await keyValueCache.set('short', 's', { ttl: 1 }); await keyValueCache.set('long', 'l', { ttl: 5 }); @@ -38,3 +53,10 @@ export function testKeyValueCache(keyValueCache: any) { }); }); } + +export function testKeyValueCache(keyValueCache: any) { + describe('KeyValueCache Test Suite', () => { + testKeyValueCache_Basics(keyValueCache); + testKeyValueCache_Expiration(keyValueCache); + }); +} diff --git a/packages/apollo-server-cloud-functions/.npmignore b/packages/apollo-server-cloud-functions/.npmignore new file mode 100644 index 00000000000..a165046d359 --- /dev/null +++ b/packages/apollo-server-cloud-functions/.npmignore @@ -0,0 +1,6 @@ +* +!src/**/* +!dist/**/* +dist/**/*.test.* +!package.json +!README.md diff --git a/packages/apollo-server-cloud-function/README.md b/packages/apollo-server-cloud-functions/README.md similarity index 87% rename from packages/apollo-server-cloud-function/README.md rename to packages/apollo-server-cloud-functions/README.md index 07b651c34d4..e28e3f0c4e3 100644 --- a/packages/apollo-server-cloud-function/README.md +++ b/packages/apollo-server-cloud-functions/README.md @@ -3,22 +3,22 @@ title: Google Cloud Functions description: Setting up Apollo Server with Google Cloud Functions --- -[![npm version](https://badge.fury.io/js/apollo-server-cloud-function.svg)](https://badge.fury.io/js/apollo-server-cloud-function) [![Build Status](https://circleci.com/gh/apollographql/apollo-server.svg?style=svg)](https://circleci.com/gh/apollographql/apollo-server) [![Coverage Status](https://coveralls.io/repos/github/apollographql/apollo-server/badge.svg?branch=master)](https://coveralls.io/github/apollographql/apollo-server?branch=master) [![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](https://www.apollographql.com/#slack) +[![npm version](https://badge.fury.io/js/apollo-server-cloud-functions.svg)](https://badge.fury.io/js/apollo-server-cloud-functions) [![Build Status](https://circleci.com/gh/apollographql/apollo-server.svg?style=svg)](https://circleci.com/gh/apollographql/apollo-server) [![Coverage Status](https://coveralls.io/repos/github/apollographql/apollo-server/badge.svg?branch=master)](https://coveralls.io/github/apollographql/apollo-server?branch=master) [![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](https://www.apollographql.com/#slack) This is the Google Cloud Function integration of GraphQL Server. Apollo Server is a community-maintained open-source GraphQL server that works with many Node.js HTTP server frameworks. [Read the docs](https://www.apollographql.com/docs/apollo-server/v2). [Read the CHANGELOG](https://github.com/apollographql/apollo-server/blob/master/CHANGELOG.md). ```sh -npm install apollo-server-cloud-function@rc graphql +npm install apollo-server-cloud-functions graphql ``` ## Deploying with Google Cloud Function #### 1. Write the API handlers -First, create a `package.json` file and include `apollo-server-cloud-function` in your dependencies. Then in a file named `index.js`, place the following code: +First, create a `package.json` file and include `apollo-server-cloud-functions` in your dependencies. Then in a file named `index.js`, place the following code: ```js -const { ApolloServer, gql } = require('apollo-server-cloud-function'); +const { ApolloServer, gql } = require('apollo-server-cloud-functions'); // Construct a schema, using GraphQL schema language const typeDefs = gql` @@ -60,7 +60,7 @@ You can refer to the [Cloud Functions documentation](https://cloud.google.com/fu To read information about the currently executing Google Cloud Function (HTTP headers, HTTP method, body, path, ...) use the context option. This way you can pass any request specific data to your schema resolvers. ```js -const { ApolloServer, gql } = require('apollo-server-cloud-function'); +const { ApolloServer, gql } = require('apollo-server-cloud-functions'); // Construct a schema, using GraphQL schema language const typeDefs = gql` @@ -94,7 +94,7 @@ exports.handler = server.createHandler(); To enable CORS the response HTTP headers need to be modified. To accomplish this use the `cors` option. ```js -const { ApolloServer, gql } = require('apollo-server-cloud-function'); +const { ApolloServer, gql } = require('apollo-server-cloud-functions'); // Construct a schema, using GraphQL schema language const typeDefs = gql` @@ -126,7 +126,7 @@ exports.handler = server.createHandler({ To enable CORS response for requests with credentials (cookies, http authentication) the allow origin header must equal the request origin and the allow credential header must be set to true. ```js -const { ApolloServer, gql } = require('apollo-server-cloud-function'); +const { ApolloServer, gql } = require('apollo-server-cloud-functions'); // Construct a schema, using GraphQL schema language const typeDefs = gql` diff --git a/packages/apollo-server-cloud-functions/jest.config.js b/packages/apollo-server-cloud-functions/jest.config.js new file mode 100644 index 00000000000..a383fbc925f --- /dev/null +++ b/packages/apollo-server-cloud-functions/jest.config.js @@ -0,0 +1,3 @@ +const config = require('../../jest.config.base'); + +module.exports = Object.assign(Object.create(null), config); diff --git a/packages/apollo-server-cloud-function/package.json b/packages/apollo-server-cloud-functions/package.json similarity index 92% rename from packages/apollo-server-cloud-function/package.json rename to packages/apollo-server-cloud-functions/package.json index 4f9fc056cee..27c962c5b65 100644 --- a/packages/apollo-server-cloud-function/package.json +++ b/packages/apollo-server-cloud-functions/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-cloud-functions", - "version": "2.2.0-alpha.0", + "version": "2.3.3", "description": "Production-ready Node.js GraphQL server for Google Cloud Functions", "keywords": [ "GraphQL", @@ -25,7 +25,7 @@ "node": ">=6" }, "dependencies": { - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "apollo-server-core": "file:../apollo-server-core", "apollo-server-env": "file:../apollo-server-env", "graphql-tools": "^4.0.0" diff --git a/packages/apollo-server-cloud-function/src/ApolloServer.ts b/packages/apollo-server-cloud-functions/src/ApolloServer.ts similarity index 82% rename from packages/apollo-server-cloud-function/src/ApolloServer.ts rename to packages/apollo-server-cloud-functions/src/ApolloServer.ts index 94e358e3e79..ea8ad6df2e2 100644 --- a/packages/apollo-server-cloud-function/src/ApolloServer.ts +++ b/packages/apollo-server-cloud-functions/src/ApolloServer.ts @@ -43,6 +43,11 @@ export class ApolloServer extends ApolloServerBase { } public createHandler({ cors }: CreateHandlerOptions = { cors: undefined }) { + // We will kick off the `willStart` event once for the server, and then + // await it before processing any requests by incorporating its `await` into + // the GraphQLServerOptions function which is called before each request. + const promiseWillStart = this.willStart(); + const corsHeaders = {} as Record; if (cors) { @@ -132,10 +137,17 @@ export class ApolloServer extends ApolloServerBase { res.set(corsHeaders); - graphqlCloudFunction(this.createGraphQLServerOptions.bind(this))( - req, - res, - ); + graphqlCloudFunction(async () => { + // In a world where this `createHandler` was async, we might avoid this + // but since we don't want to introduce a breaking change to this API + // (by switching it to `async`), we'll leverage the + // `GraphQLServerOptions`, which are dynamically built on each request, + // to `await` the `promiseWillStart` which we kicked off at the top of + // this method to ensure that it runs to completion (which is part of + // its contract) prior to processing the request. + await promiseWillStart; + return this.createGraphQLServerOptions(req, res); + })(req, res); }; } } diff --git a/packages/apollo-server-cloud-function/src/__tests__/googleCloudApollo.test.ts b/packages/apollo-server-cloud-functions/src/__tests__/googleCloudApollo.test.ts similarity index 100% rename from packages/apollo-server-cloud-function/src/__tests__/googleCloudApollo.test.ts rename to packages/apollo-server-cloud-functions/src/__tests__/googleCloudApollo.test.ts diff --git a/packages/apollo-server-cloud-function/src/__tests__/tsconfig.json b/packages/apollo-server-cloud-functions/src/__tests__/tsconfig.json similarity index 100% rename from packages/apollo-server-cloud-function/src/__tests__/tsconfig.json rename to packages/apollo-server-cloud-functions/src/__tests__/tsconfig.json diff --git a/packages/apollo-server-cloud-function/src/googleCloudApollo.ts b/packages/apollo-server-cloud-functions/src/googleCloudApollo.ts similarity index 85% rename from packages/apollo-server-cloud-function/src/googleCloudApollo.ts rename to packages/apollo-server-cloud-functions/src/googleCloudApollo.ts index 196ee5c92c0..23daeb9184c 100644 --- a/packages/apollo-server-cloud-function/src/googleCloudApollo.ts +++ b/packages/apollo-server-cloud-functions/src/googleCloudApollo.ts @@ -6,7 +6,13 @@ import { import { Headers } from 'apollo-server-env'; import { Request, Response } from 'express'; -export function graphqlCloudFunction(options: GraphQLOptions): any { +export interface CloudFunctionGraphQLOptionsFunction { + (req?: Request, res?: Response): GraphQLOptions | Promise; +} + +export function graphqlCloudFunction( + options: GraphQLOptions | CloudFunctionGraphQLOptionsFunction, +): any { if (!options) { throw new Error('Apollo Server requires options.'); } diff --git a/packages/apollo-server-cloud-functions/src/index.ts b/packages/apollo-server-cloud-functions/src/index.ts new file mode 100644 index 00000000000..2ff184ef590 --- /dev/null +++ b/packages/apollo-server-cloud-functions/src/index.ts @@ -0,0 +1,24 @@ +export { + GraphQLUpload, + GraphQLOptions, + GraphQLExtension, + Config, + gql, + // Errors + ApolloError, + toApolloError, + SyntaxError, + ValidationError, + AuthenticationError, + ForbiddenError, + UserInputError, + // playground + defaultPlaygroundOptions, + PlaygroundConfig, + PlaygroundRenderPageOptions, +} from 'apollo-server-core'; + +export * from 'graphql-tools'; + +// ApolloServer integration. +export { ApolloServer, CreateHandlerOptions } from './ApolloServer'; diff --git a/packages/apollo-server-cloud-functions/tsconfig.json b/packages/apollo-server-cloud-functions/tsconfig.json new file mode 100644 index 00000000000..b9d700923fa --- /dev/null +++ b/packages/apollo-server-cloud-functions/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["src/**/*"], + "exclude": ["**/__tests__", "**/__mocks__"], + "references": [ + { "path": "../apollo-server-core" }, + ] +} diff --git a/packages/apollo-server-cloudflare/package.json b/packages/apollo-server-cloudflare/package.json index 59d960c7d23..a57bacef459 100644 --- a/packages/apollo-server-cloudflare/package.json +++ b/packages/apollo-server-cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-cloudflare", - "version": "2.2.0-alpha.0", + "version": "2.3.3", "description": "Production-ready Node.js GraphQL server for Cloudflare workers", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-cloudflare/src/ApolloServer.ts b/packages/apollo-server-cloudflare/src/ApolloServer.ts index bb2ce9fe49a..052b234268e 100644 --- a/packages/apollo-server-cloudflare/src/ApolloServer.ts +++ b/packages/apollo-server-cloudflare/src/ApolloServer.ts @@ -14,9 +14,13 @@ export class ApolloServer extends ApolloServerBase { } public async listen() { - const graphql = this.createGraphQLServerOptions.bind(this); + await this.willStart(); addEventListener('fetch', (event: FetchEvent) => { - event.respondWith(graphqlCloudflare(graphql)(event.request)); + event.respondWith( + graphqlCloudflare(() => { + return this.createGraphQLServerOptions(event.request); + })(event.request), + ); }); return await { url: '', port: null }; } diff --git a/packages/apollo-server-cloudflare/src/cloudflareApollo.ts b/packages/apollo-server-cloudflare/src/cloudflareApollo.ts index f4475fa3660..92e385ed29a 100644 --- a/packages/apollo-server-cloudflare/src/cloudflareApollo.ts +++ b/packages/apollo-server-cloudflare/src/cloudflareApollo.ts @@ -1,6 +1,5 @@ import { GraphQLOptions, - ServerOptionsFunction, HttpQueryError, runHttpQuery, } from 'apollo-server-core'; @@ -12,7 +11,9 @@ import { Request, Response, URL } from 'apollo-server-env'; // - simple, fast and secure // -export type CloudflareOptionsFunction = ServerOptionsFunction<[Request]>; +export interface CloudflareOptionsFunction { + (req?: Request): GraphQLOptions | Promise; +} export function graphqlCloudflare( options: GraphQLOptions | CloudflareOptionsFunction, diff --git a/packages/apollo-server-core/package.json b/packages/apollo-server-core/package.json index 33cf0e01941..a0aa70b53a7 100644 --- a/packages/apollo-server-core/package.json +++ b/packages/apollo-server-core/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-core", - "version": "2.2.0-alpha.0", + "version": "2.3.4", "description": "Core engine for Apollo GraphQL server", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -24,7 +24,8 @@ "node": ">=6" }, "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", + "@apollographql/apollo-tools": "^0.3.0", + "@apollographql/graphql-playground-html": "^1.6.6", "@types/ws": "^6.0.0", "apollo-cache-control": "file:../apollo-cache-control", "apollo-datasource": "file:../apollo-datasource", @@ -34,10 +35,12 @@ "apollo-server-errors": "file:../apollo-server-errors", "apollo-server-plugin-base": "file:../apollo-server-plugin-base", "apollo-tracing": "file:../apollo-tracing", + "fast-json-stable-stringify": "^2.0.0", "graphql-extensions": "file:../graphql-extensions", "graphql-subscriptions": "^1.0.0", "graphql-tag": "^2.9.2", "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.2", "lodash": "^4.17.10", "subscriptions-transport-ws": "^0.9.11", "ws": "^6.0.0" diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index 2a596691266..945476ea04d 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -14,6 +14,7 @@ import { GraphQLExtension } from 'graphql-extensions'; import { EngineReportingAgent } from 'apollo-engine-reporting'; import { InMemoryLRUCache } from 'apollo-server-caching'; import { ApolloServerPlugin } from 'apollo-server-plugin-base'; +import runtimeSupportsUploads from './utils/runtimeSupportsUploads'; import { SubscriptionServer, @@ -44,6 +45,16 @@ import { PlaygroundRenderPageOptions, } from './playground'; +import { generateSchemaHash } from './utils/schemaHash'; +import { + processGraphQLRequest, + GraphQLRequestContext, + GraphQLRequest, +} from './requestPipeline'; + +import { Headers } from 'apollo-server-env'; +import { buildServiceDefinition } from '@apollographql/apollo-tools'; + const NoIntrospection = (context: ValidationContext) => ({ Field(node: FieldDefinitionNode) { if (node.name.value === '__schema' || node.name.value === '__type') { @@ -78,6 +89,9 @@ function getEngineServiceId(engine: Config['engine']): string | undefined { return; } +const forbidUploadsForTesting = + process && process.env.NODE_ENV === 'test' && !runtimeSupportsUploads; + export class ApolloServerBase { public subscriptionsPath?: string; public graphqlPath: string = '/graphql'; @@ -87,6 +101,7 @@ export class ApolloServerBase { private engineReportingAgent?: EngineReportingAgent; private engineServiceId?: string; private extensions: Array<() => GraphQLExtension>; + private schemaHash: string; protected plugins: ApolloServerPlugin[] = []; protected schema: GraphQLSchema; @@ -107,6 +122,7 @@ export class ApolloServerBase { resolvers, schema, schemaDirectives, + modules, typeDefs, introspection, mocks, @@ -187,8 +203,16 @@ export class ApolloServerBase { this.requestOptions = requestOptions as GraphQLOptions; this.context = context; - if (uploads !== false) { + if (uploads !== false && !forbidUploadsForTesting) { if (this.supportsUploads()) { + if (!runtimeSupportsUploads) { + printNodeFileUploadsMessage(); + throw new Error( + '`graphql-upload` is no longer supported on Node.js < v8.5.0. ' + + 'See https://bit.ly/gql-upload-node-6.', + ); + } + if (uploads === true || typeof uploads === 'undefined') { this.uploadsConfig = {}; } else { @@ -198,17 +222,23 @@ export class ApolloServerBase { //default we enable them if supported by the integration } else if (uploads) { throw new Error( - 'This implementation of ApolloServer does not support file uploads because the environmnet cannot accept multi-part forms', + 'This implementation of ApolloServer does not support file uploads because the environment cannot accept multi-part forms', ); } } if (schema) { this.schema = schema; + } else if (modules) { + const { schema, errors } = buildServiceDefinition(modules); + if (errors && errors.length > 0) { + throw new Error(errors.map(error => error.message).join('\n\n')); + } + this.schema = schema!; } else { if (!typeDefs) { throw Error( - 'Apollo Server requires either an existing schema or typeDefs', + 'Apollo Server requires either an existing schema, modules or typeDefs', ); } @@ -231,9 +261,7 @@ export class ApolloServerBase { ); if (this.uploadsConfig) { - const { - GraphQLUpload, - } = require('@apollographql/apollo-upload-server'); + const { GraphQLUpload } = require('graphql-upload'); if (resolvers && !resolvers.Upload) { resolvers.Upload = GraphQLUpload; } @@ -254,7 +282,7 @@ export class ApolloServerBase { }); } - if (mocks || typeof mockEntireSchema !== 'undefined') { + if (mocks || (typeof mockEntireSchema !== 'undefined' && mocks !== false)) { addMockFunctionsToSchema({ schema: this.schema, mocks: @@ -266,6 +294,10 @@ export class ApolloServerBase { }); } + // The schema hash is a string representation of the shape of the schema + // it is used for reporting and can be used for a cache key if needed + this.schemaHash = generateSchemaHash(this.schema); + // Note: doRunQuery will add its own extensions if you set tracing, // or cacheControl. this.extensions = []; @@ -290,6 +322,13 @@ export class ApolloServerBase { if (this.engineServiceId) { this.engineReportingAgent = new EngineReportingAgent( typeof engine === 'object' ? engine : Object.create(null), + { + schema: this.schema, + schemaHash: this.schemaHash, + engine: { + serviceID: this.engineServiceId, + }, + }, ); // Let's keep this extension second so it wraps everything, except error formatting this.extensions.push(() => this.engineReportingAgent!.newExtension()); @@ -341,6 +380,7 @@ export class ApolloServerBase { plugin.serverWillStart && plugin.serverWillStart({ schema: this.schema, + schemaHash: this.schemaHash, engine: { serviceID: this.engineServiceId, }, @@ -438,15 +478,11 @@ export class ApolloServerBase { return; } - // FIXME: We also want to support default exports and possibly module names - // but this requires adjustments to typing (see PluginDefinition type), and - // I had to give up on that for now. this.plugins = plugins.map(plugin => { if (typeof plugin === 'function') { - return new plugin(); - } else { - return plugin as ApolloServerPlugin; + return plugin(); } + return plugin; }); } @@ -487,4 +523,61 @@ export class ApolloServerBase { ...this.requestOptions, } as GraphQLOptions; } + + public async executeOperation(request: GraphQLRequest) { + let options; + + try { + options = await this.graphQLServerOptions(); + } catch (e) { + e.message = `Invalid options provided to ApolloServer: ${e.message}`; + throw new Error(e); + } + + if (typeof options.context === 'function') { + options.context = (options.context as () => never)(); + } + + const requestCtx: GraphQLRequestContext = { + request, + context: options.context || Object.create(null), + cache: options.cache!, + response: { + http: { + headers: new Headers(), + }, + }, + }; + + return processGraphQLRequest(options, requestCtx); + } +} + +function printNodeFileUploadsMessage() { + console.error( + [ + '*****************************************************************', + '* *', + '* ERROR! Manual intervention is necessary for Node.js < v8.5.0! *', + '* *', + '*****************************************************************', + '', + 'The third-party `graphql-upload` package, which is used to implement', + 'file uploads in Apollo Server 2.x, no longer supports Node.js LTS', + 'versions prior to Node.js v8.5.0.', + '', + 'Deployments which NEED file upload capabilities should update to', + 'Node.js >= v8.5.0 to continue using uploads.', + '', + 'If this server DOES NOT NEED file uploads and wishes to continue', + 'using this version of Node.js, uploads can be disabled by adding:', + '', + ' uploads: false,', + '', + '...to the options for Apollo Server and re-deploying the server.', + '', + 'For more information, see https://bit.ly/gql-upload-node-6.', + '', + ].join('\n'), + ); } diff --git a/packages/apollo-server-core/src/__tests__/runHttpQuery.test.ts b/packages/apollo-server-core/src/__tests__/runHttpQuery.test.ts index 4ab1c2e3db0..2f0a1565d0b 100644 --- a/packages/apollo-server-core/src/__tests__/runHttpQuery.test.ts +++ b/packages/apollo-server-core/src/__tests__/runHttpQuery.test.ts @@ -38,6 +38,8 @@ describe('runHttpQuery', () => { const noQueryRequest = Object.assign({}, mockQueryRequest, { query: 'foo', }); + + expect.assertions(2); return runHttpQuery([], noQueryRequest).catch((err: HttpQueryError) => { expect(err.statusCode).toEqual(400); expect(err.message).toEqual('Must provide query string.'); diff --git a/packages/apollo-server-core/src/__tests__/runQuery.test.ts b/packages/apollo-server-core/src/__tests__/runQuery.test.ts index e790f78a046..d18f1b2517e 100644 --- a/packages/apollo-server-core/src/__tests__/runQuery.test.ts +++ b/packages/apollo-server-core/src/__tests__/runQuery.test.ts @@ -9,8 +9,6 @@ import { GraphQLNonNull, parse, DocumentNode, - ValidationContext, - GraphQLFieldResolver, } from 'graphql'; import { @@ -19,10 +17,9 @@ import { GraphQLResponse, } from 'graphql-extensions'; -import { CacheControlExtensionOptions } from 'apollo-cache-control'; - import { processGraphQLRequest, GraphQLRequest } from '../requestPipeline'; import { Request } from 'apollo-server-env'; +import { GraphQLOptions, Context as GraphQLContext } from 'apollo-server-core'; // This is a temporary kludge to ensure we preserve runQuery behavior with the // GraphQLRequestProcessor refactoring. @@ -46,25 +43,26 @@ function runQuery(options: QueryOptions): Promise { }); } -interface QueryOptions { - schema: GraphQLSchema; - +interface QueryOptions + extends Pick< + GraphQLOptions>, + | 'cacheControl' + | 'context' + | 'debug' + | 'extensions' + | 'fieldResolver' + | 'formatError' + | 'formatResponse' + | 'rootValue' + | 'schema' + | 'tracing' + | 'validationRules' + > { queryString?: string; parsedQuery?: DocumentNode; - - rootValue?: any; - context?: any; variables?: { [key: string]: any }; operationName?: string; - validationRules?: Array<(context: ValidationContext) => any>; - fieldResolver?: GraphQLFieldResolver; - formatError?: Function; - formatResponse?: Function; - debug?: boolean; - tracing?: boolean; - cacheControl?: CacheControlExtensionOptions; request: Pick; - extensions?: Array<() => GraphQLExtension>; } const queryType = new GraphQLObjectType({ @@ -99,13 +97,13 @@ const queryType = new GraphQLObjectType({ }, testContextValue: { type: GraphQLString, - resolve(_root, _args, context) { + resolve(_parent, _args, context) { return context.s + ' works'; }, }, testArgumentValue: { type: GraphQLInt, - resolve(_root, args) { + resolve(_parent, args) { return args['base'] + 5; }, args: { @@ -388,7 +386,7 @@ describe('runQuery', () => { fields: { testString: { type: GraphQLString, - resolve(_root, _args, context) { + resolve(_parent, _args, context) { expect(context._extensionStack).toBeInstanceOf( GraphQLExtensionStack, ); diff --git a/packages/apollo-server-core/src/graphqlOptions.ts b/packages/apollo-server-core/src/graphqlOptions.ts index 2d9ffe75fa4..afed78ab98a 100644 --- a/packages/apollo-server-core/src/graphqlOptions.ts +++ b/packages/apollo-server-core/src/graphqlOptions.ts @@ -55,13 +55,13 @@ export interface PersistedQueryOptions { export default GraphQLServerOptions; -export async function resolveGraphqlOptions( +export async function resolveGraphqlOptions( options: | GraphQLServerOptions | (( - ...args: HandlerArguments + ...args: Array ) => Promise | GraphQLServerOptions), - ...args: HandlerArguments + ...args: Array ): Promise { if (typeof options === 'function') { return await options(...args); diff --git a/packages/apollo-server-core/src/index.ts b/packages/apollo-server-core/src/index.ts index c000b836c45..dd6015c4213 100644 --- a/packages/apollo-server-core/src/index.ts +++ b/packages/apollo-server-core/src/index.ts @@ -41,6 +41,18 @@ export const gql: ( ...substitutions: any[] ) => DocumentNode = gqlTag; +import runtimeSupportsUploads from './utils/runtimeSupportsUploads'; import { GraphQLScalarType } from 'graphql'; -import { GraphQLUpload as UploadScalar } from '@apollographql/apollo-upload-server'; -export const GraphQLUpload = UploadScalar as GraphQLScalarType; +export { default as processFileUploads } from './processFileUploads'; + +// This is a conditional export intended to avoid traversing the +// entire module tree of `graphql-upload`. This only defined if the +// version of Node.js is >= 8.5.0 since those are the only Node.js versions +// which are supported by `graphql-upload@8`. Since the source of +// `graphql-upload` is not transpiled for older targets (in fact, it includes +// experimental ECMAScript modules), this conditional export is necessary +// to avoid modern ECMAScript from failing to parse by versions of Node.js +// which don't support it (yet — eg. Node.js 6 and async/await). +export const GraphQLUpload = runtimeSupportsUploads + ? (require('graphql-upload').GraphQLUpload as GraphQLScalarType) + : undefined; diff --git a/packages/apollo-server-core/src/playground.ts b/packages/apollo-server-core/src/playground.ts index 313ebbf309a..dcbfb7eca0f 100644 --- a/packages/apollo-server-core/src/playground.ts +++ b/packages/apollo-server-core/src/playground.ts @@ -6,16 +6,18 @@ export { RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html/dist/render-playground-page'; -// This specifies the version of GraphQL Playground that will be served -// from graphql-playground-html, and is passed to renderPlaygroundPage -// by the integration subclasses -const playgroundVersion = '1.7.4'; +// This specifies the version of `graphql-playground-react` that will be served +// from `graphql-playground-html`. It's passed to ``graphql-playground-html`'s +// renderPlaygroundPage` via the integration packages' playground configuration. +const playgroundVersion = '1.7.10'; // https://stackoverflow.com/a/51365037 type RecursivePartial = { [P in keyof T]?: T[P] extends (infer U)[] ? RecursivePartial[] - : T[P] extends object ? RecursivePartial : T[P] + : T[P] extends object + ? RecursivePartial + : T[P] }; export type PlaygroundConfig = @@ -37,7 +39,7 @@ export const defaultPlaygroundOptions = { }; export function createPlaygroundOptions( - playground: PlaygroundConfig = {}, + playground?: PlaygroundConfig, ): PlaygroundRenderPageOptions | undefined { const isDev = process.env.NODE_ENV !== 'production'; const enabled: boolean = diff --git a/packages/apollo-server-core/src/processFileUploads.ts b/packages/apollo-server-core/src/processFileUploads.ts new file mode 100644 index 00000000000..c2e0cb84808 --- /dev/null +++ b/packages/apollo-server-core/src/processFileUploads.ts @@ -0,0 +1,18 @@ +/// + +import runtimeSupportsUploads from './utils/runtimeSupportsUploads'; + +// We'll memoize this function once at module load time since it should never +// change during runtime. In the event that we're using a version of Node.js +// less than 8.5.0, we'll +const processFileUploads: + | typeof import('graphql-upload').processRequest + | undefined = (() => { + if (runtimeSupportsUploads) { + return require('graphql-upload') + .processRequest as typeof import('graphql-upload').processRequest; + } + return undefined; +})(); + +export default processFileUploads; diff --git a/packages/apollo-server-core/src/requestPipeline.ts b/packages/apollo-server-core/src/requestPipeline.ts index f085532e4a7..03ca7128715 100644 --- a/packages/apollo-server-core/src/requestPipeline.ts +++ b/packages/apollo-server-core/src/requestPipeline.ts @@ -22,6 +22,7 @@ import { } from 'apollo-cache-control'; import { TracingExtension } from 'apollo-tracing'; import { + ApolloError, fromGraphQLError, SyntaxError, ValidationError, @@ -43,6 +44,7 @@ import { } from 'apollo-server-plugin-base'; import { Dispatcher } from './utils/dispatcher'; +import { KeyValueCache } from 'apollo-server-caching'; export { GraphQLRequest, @@ -101,6 +103,7 @@ export async function processGraphQLRequest( let queryHash: string; + let persistedQueryCache: KeyValueCache | undefined; let persistedQueryHit = false; let persistedQueryRegister = false; @@ -115,10 +118,14 @@ export async function processGraphQLRequest( ); } + // We'll store a reference to the persisted query cache so we can actually + // do the write at a later point in the request pipeline processing. + persistedQueryCache = config.persistedQueries.cache; + queryHash = extensions.persistedQuery.sha256Hash; if (query === undefined) { - query = await config.persistedQueries.cache.get(`apq:${queryHash}`); + query = await persistedQueryCache.get(`apq:${queryHash}`); if (query) { persistedQueryHit = true; } else { @@ -133,17 +140,11 @@ export async function processGraphQLRequest( ); } + // We won't write to the persisted query cache until later. + // Defering the writing gives plugins the ability to "win" from use of + // the cache, but also have their say in whether or not the cache is + // written to (by interrupting the request with an error). persistedQueryRegister = true; - - // Store the query asynchronously so we don't block. - (async () => { - return ( - config.persistedQueries && - config.persistedQueries.cache.set(`apq:${queryHash}`, query) - ); - })().catch(error => { - console.warn(error); - }); } } else if (query) { // FIXME: We'll compute the APQ query hash to use as our cache key for @@ -164,6 +165,7 @@ export async function processGraphQLRequest( persistedQueryHit, persistedQueryRegister, context: requestContext.context, + requestContext, }); const parsingDidEnd = await dispatcher.invokeDidStartHook( @@ -178,13 +180,7 @@ export async function processGraphQLRequest( parsingDidEnd(); } catch (syntaxError) { parsingDidEnd(syntaxError); - return sendResponse({ - errors: [ - fromGraphQLError(syntaxError, { - errorClass: SyntaxError, - }), - ], - }); + return sendErrorResponse(syntaxError, SyntaxError); } requestContext.document = document; @@ -196,19 +192,13 @@ export async function processGraphQLRequest( const validationErrors = validate(document); - if (validationErrors.length > 0) { + if (validationErrors.length === 0) { + validationDidEnd(); + } else { validationDidEnd(validationErrors); - return sendResponse({ - errors: validationErrors.map(validationError => - fromGraphQLError(validationError, { - errorClass: ValidationError, - }), - ), - }); + return sendErrorResponse(validationErrors, ValidationError); } - validationDidEnd(); - // FIXME: If we want to guarantee an operation has been set when invoking // `willExecuteOperation` and executionDidStart`, we need to throw an // error here and not leave this to `buildExecutionContext` in @@ -228,6 +218,16 @@ export async function processGraphQLRequest( >, ); + // Now that we've gone through the pre-execution phases of the request + // pipeline, and given plugins appropriate ability to object (by throwing + // an error) and not actually write, we'll write to the cache if it was + // determined earlier in the request pipeline that we should do so. + if (persistedQueryRegister && persistedQueryCache) { + Promise.resolve(persistedQueryCache.set(`apq:${queryHash}`, query)).catch( + console.warn, + ); + } + const executionDidEnd = await dispatcher.invokeDidStartHook( 'executionDidStart', requestContext as WithRequired< @@ -247,9 +247,7 @@ export async function processGraphQLRequest( executionDidEnd(); } catch (executionError) { executionDidEnd(executionError); - return sendResponse({ - errors: [fromGraphQLError(executionError)], - }); + return sendErrorResponse(executionError); } const formattedExtensions = extensionStack.format(); @@ -345,6 +343,27 @@ export async function processGraphQLRequest( return requestContext.response!; } + function sendErrorResponse( + errorOrErrors: ReadonlyArray | GraphQLError, + errorClass?: typeof ApolloError, + ) { + // If a single error is passed, it should still be encapsulated in an array. + const errors = Array.isArray(errorOrErrors) + ? errorOrErrors + : [errorOrErrors]; + + return sendResponse({ + errors: errors.map(err => + fromGraphQLError( + err, + errorClass && { + errorClass, + }, + ), + ), + }); + } + function initializeRequestListenerDispatcher(): Dispatcher< GraphQLRequestListener > { diff --git a/packages/apollo-server-core/src/requestPipelineAPI.ts b/packages/apollo-server-core/src/requestPipelineAPI.ts index 91e0cca20da..a4d00fc8999 100644 --- a/packages/apollo-server-core/src/requestPipelineAPI.ts +++ b/packages/apollo-server-core/src/requestPipelineAPI.ts @@ -15,6 +15,7 @@ import { KeyValueCache } from 'apollo-server-caching'; export interface GraphQLServiceContext { schema: GraphQLSchema; + schemaHash: string; engine: { serviceID?: string; }; diff --git a/packages/apollo-server-core/src/runHttpQuery.ts b/packages/apollo-server-core/src/runHttpQuery.ts index a79da960882..977593bcdd3 100644 --- a/packages/apollo-server-core/src/runHttpQuery.ts +++ b/packages/apollo-server-core/src/runHttpQuery.ts @@ -18,7 +18,7 @@ import { import { CacheControlExtensionOptions } from 'apollo-cache-control'; import { ApolloServerPlugin, WithRequired } from 'apollo-server-plugin-base'; -export interface HttpQueryRequest { +export interface HttpQueryRequest { method: string; // query is either the POST body or the GET query string map. In the GET // case, all values are strings and need to be parsed as JSON; in the POST @@ -28,7 +28,7 @@ export interface HttpQueryRequest { query: Record | Array>; options: | GraphQLOptions - | ((...args: HandlerArguments) => Promise | GraphQLOptions); + | ((...args: Array) => Promise | GraphQLOptions); request: Pick; } @@ -91,9 +91,9 @@ function throwHttpGraphQLError( ); } -export async function runHttpQuery( - handlerArguments: HandlerArguments, - request: HttpQueryRequest, +export async function runHttpQuery( + handlerArguments: Array, + request: HttpQueryRequest, ): Promise { let options: GraphQLOptions; const debugDefault = @@ -178,7 +178,7 @@ export async function processHTTPRequest( options: WithRequired, 'cache' | 'plugins'> & { context: TContext; }, - httpRequest: HttpQueryRequest, + httpRequest: HttpQueryRequest, ): Promise { let requestPayload; @@ -212,7 +212,12 @@ export async function processHTTPRequest( ); } - options.plugins.push(checkOperationPlugin); + // Create a local copy of `options`, based on global options, but maintaining + // that appropriate plugins are in place. + options = { + ...options, + plugins: [checkOperationPlugin, ...options.plugins], + }; function buildRequestContext( request: GraphQLRequest, diff --git a/packages/apollo-server-core/src/types.ts b/packages/apollo-server-core/src/types.ts index d5a55fffd9a..36d050ad487 100644 --- a/packages/apollo-server-core/src/types.ts +++ b/packages/apollo-server-core/src/types.ts @@ -17,6 +17,9 @@ import { import { CacheControlExtensionOptions } from 'apollo-cache-control'; import { ApolloServerPlugin } from 'apollo-server-plugin-base'; +import { GraphQLSchemaModule } from '@apollographql/apollo-tools'; +export { GraphQLSchemaModule }; + export { KeyValueCache } from 'apollo-server-caching'; export type Context = T; @@ -24,11 +27,9 @@ export type ContextFunction = ( context: Context, ) => Promise>; -type ValueOrPromise = T | Promise; - -export type ServerOptionsFunction = ( - ...args: HandlerArguments -) => ValueOrPromise; +// A plugin can return an interface that matches `ApolloServerPlugin`, or a +// factory function that returns `ApolloServerPlugin`. +export type PluginDefinition = ApolloServerPlugin | (() => ApolloServerPlugin); export interface SubscriptionServerOptions { path: string; @@ -45,17 +46,18 @@ export interface SubscriptionServerOptions { // fields that are not specific to a single integration export interface Config extends Pick< - GraphQLOptions>, - | 'formatError' - | 'debug' - | 'rootValue' - | 'validationRules' - | 'formatResponse' - | 'fieldResolver' - | 'tracing' - | 'dataSources' - | 'cache' - > { + GraphQLOptions>, + | 'formatError' + | 'debug' + | 'rootValue' + | 'validationRules' + | 'formatResponse' + | 'fieldResolver' + | 'tracing' + | 'dataSources' + | 'cache' + > { + modules?: GraphQLSchemaModule[]; typeDefs?: DocumentNode | Array; resolvers?: IResolvers; schema?: GraphQLSchema; @@ -64,21 +66,17 @@ export interface Config introspection?: boolean; mocks?: boolean | IMocks; mockEntireSchema?: boolean; - engine?: boolean | EngineReportingOptions; + engine?: boolean | EngineReportingOptions>; extensions?: Array<() => GraphQLExtension>; cacheControl?: CacheControlExtensionOptions | boolean; plugins?: PluginDefinition[]; persistedQueries?: PersistedQueryOptions | false; subscriptions?: Partial | string | false; - //https://github.com/jaydenseric/apollo-upload-server#options + //https://github.com/jaydenseric/graphql-upload#type-uploadoptions uploads?: boolean | FileUploadOptions; playground?: PlaygroundConfig; } -export type PluginDefinition = - | ApolloServerPlugin - | (new () => ApolloServerPlugin); - export interface FileUploadOptions { //Max allowed non-file multipart form field size in bytes; enough for your queries (default: 1 MB). maxFieldSize?: number; diff --git a/packages/apollo-server-core/src/types/graphql-upload.d.ts b/packages/apollo-server-core/src/types/graphql-upload.d.ts new file mode 100644 index 00000000000..3516f84fa35 --- /dev/null +++ b/packages/apollo-server-core/src/types/graphql-upload.d.ts @@ -0,0 +1,30 @@ +declare module 'graphql-upload' { + import { GraphQLScalarType } from 'graphql'; + + export const GraphQLUpload: GraphQLScalarType; + + export interface ApolloUploadOptions { + /** + * Max allowed non-file multipart form field size in bytes; enough for your queries (default: 1 MB) + */ + maxFieldSize?: number; + /** + * Max allowed file size in bytes (default: Infinity) + */ + maxFileSize?: number; + /** + * Max allowed number of files (default: Infinity) + */ + maxFiles?: number; + } + + export type Request = any; + + export type Response = any; + + export function processRequest( + request: Request, + response: Response, + options?: ApolloUploadOptions, + ): Promise; +} diff --git a/packages/apollo-server-core/src/utils/dispatcher.ts b/packages/apollo-server-core/src/utils/dispatcher.ts index c2748a651a0..09b01f9cfb8 100644 --- a/packages/apollo-server-core/src/utils/dispatcher.ts +++ b/packages/apollo-server-core/src/utils/dispatcher.ts @@ -30,7 +30,7 @@ export class Dispatcher { public invokeDidStartHook< TMethodName extends FunctionPropertyNames< Required, - ((...args: any[]) => AnyFunction | void) + (...args: any[]) => AnyFunction | void >, TEndHookArgs extends Args>> >( diff --git a/packages/apollo-server-core/src/utils/runtimeSupportsUploads.ts b/packages/apollo-server-core/src/utils/runtimeSupportsUploads.ts new file mode 100644 index 00000000000..f1a50344a6d --- /dev/null +++ b/packages/apollo-server-core/src/utils/runtimeSupportsUploads.ts @@ -0,0 +1,24 @@ +const runtimeSupportsUploads = (() => { + if ( + process && + process.release && + process.release.name === 'node' && + process.versions && + typeof process.versions.node === 'string' + ) { + const [nodeMajor, nodeMinor] = process.versions.node + .split('.', 2) + .map(segment => parseInt(segment, 10)); + + if (nodeMajor < 8 || (nodeMajor === 8 && nodeMinor < 5)) { + return false; + } + return true; + } + + // If we haven't matched any of the above criteria, we'll remain unsupported + // for this mysterious environment until a pull-request proves us otherwise. + return false; +})(); + +export default runtimeSupportsUploads; diff --git a/packages/apollo-server-core/src/utils/schemaHash.ts b/packages/apollo-server-core/src/utils/schemaHash.ts new file mode 100644 index 00000000000..a1b01b3628f --- /dev/null +++ b/packages/apollo-server-core/src/utils/schemaHash.ts @@ -0,0 +1,44 @@ +import { parse } from 'graphql/language'; +import { execute, ExecutionResult } from 'graphql/execution'; +import { getIntrospectionQuery, IntrospectionSchema } from 'graphql/utilities'; +import stableStringify from 'fast-json-stable-stringify'; +import { GraphQLSchema } from 'graphql/type'; +import { createHash } from 'crypto'; + +export function generateSchemaHash(schema: GraphQLSchema): string { + const introspectionQuery = getIntrospectionQuery(); + const documentAST = parse(introspectionQuery); + const result = execute(schema, documentAST) as ExecutionResult; + + // If the execution of an introspection query results in a then-able, it + // indicates that one or more of its resolvers is behaving in an asynchronous + // manner. This is not the expected behavior of a introspection query + // which does not have any asynchronous resolvers. + if ( + result && + typeof (result as PromiseLike).then === 'function' + ) { + throw new Error( + [ + 'The introspection query is resolving asynchronously; execution of an introspection query is not expected to return a `Promise`.', + '', + 'Wrapped type resolvers should maintain the existing execution dynamics of the resolvers they wrap (i.e. async vs sync) or introspection types should be excluded from wrapping by checking them with `graphql/type`s, `isIntrospectionType` predicate function prior to wrapping.', + ].join('\n'), + ); + } + + if (!result || !result.data || !result.data.__schema) { + throw new Error('Unable to generate server introspection document.'); + } + + const introspectionSchema: IntrospectionSchema = result.data.__schema; + + // It's important that we perform a deterministic stringification here + // since, depending on changes in the underlying `graphql-js` execution + // layer, varying orders of the properties in the introspection + const stringifiedSchema = stableStringify(introspectionSchema); + + return createHash('sha512') + .update(stringifiedSchema) + .digest('hex'); +} diff --git a/packages/apollo-server-env/package.json b/packages/apollo-server-env/package.json index f90a73c48bf..905e4e2b869 100644 --- a/packages/apollo-server-env/package.json +++ b/packages/apollo-server-env/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-env", - "version": "2.1.0-alpha.0", + "version": "2.2.0", "author": "opensource@apollographql.com", "license": "MIT", "repository": { diff --git a/packages/apollo-server-env/src/fetch.d.ts b/packages/apollo-server-env/src/fetch.d.ts index be422134a8d..6d138daea2b 100644 --- a/packages/apollo-server-env/src/fetch.d.ts +++ b/packages/apollo-server-env/src/fetch.d.ts @@ -1,10 +1,13 @@ -import { Agent } from 'http'; +import { Agent as HttpAgent } from 'http'; +import { Agent as HttpsAgent } from 'https'; export declare function fetch( input?: RequestInfo, init?: RequestInit, ): Promise; +export type RequestAgent = HttpAgent | HttpsAgent; + export type RequestInfo = Request | string; export declare class Headers implements Iterable<[string, string]> { @@ -58,7 +61,7 @@ export interface RequestInit { timeout?: number; compress?: boolean; size?: number; - agent?: Agent; + agent?: RequestAgent | false; // Cloudflare Workers accept a `cf` property to control Cloudflare features // See https://developers.cloudflare.com/workers/reference/cloudflare-features/ diff --git a/packages/apollo-server-env/src/global.d.ts b/packages/apollo-server-env/src/global.d.ts index c38e01e7d09..82813030cb7 100644 --- a/packages/apollo-server-env/src/global.d.ts +++ b/packages/apollo-server-env/src/global.d.ts @@ -12,6 +12,7 @@ type Headers = import('./fetch').Headers; type HeadersInit = import('./fetch').HeadersInit; type Body = import('./fetch').Body; type Request = import('./fetch').Request; +type RequestAgent = import('./fetch').RequestAgent; type RequestInit = import('./fetch').RequestInit; type RequestMode = import('./fetch').RequestMode; type RequestCredentials = import('./fetch').RequestCredentials; diff --git a/packages/apollo-server-errors/package.json b/packages/apollo-server-errors/package.json index 257a21ad7df..ab4668a91a8 100644 --- a/packages/apollo-server-errors/package.json +++ b/packages/apollo-server-errors/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-errors", - "version": "2.1.0-alpha.0", + "version": "2.2.0", "author": "opensource@apollographql.com", "license": "MIT", "repository": { diff --git a/packages/apollo-server-express/README.md b/packages/apollo-server-express/README.md index b7d0ec5e672..f2788e7eab8 100644 --- a/packages/apollo-server-express/README.md +++ b/packages/apollo-server-express/README.md @@ -67,7 +67,7 @@ const server = new ApolloServer({ typeDefs, resolvers }); const app = connect(); const path = '/graphql'; -server.use(query()); +app.use(query()); server.applyMiddleware({ app, path }); app.listen({ port: 4000 }, () => diff --git a/packages/apollo-server-express/package.json b/packages/apollo-server-express/package.json index fc7e5caad91..5544680ad3e 100644 --- a/packages/apollo-server-express/package.json +++ b/packages/apollo-server-express/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-express", - "version": "2.2.0-alpha.0", + "version": "2.3.3", "description": "Production-ready Node.js GraphQL server for Express and Connect", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -26,12 +26,11 @@ "node": ">=6" }, "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "@types/accepts": "^1.3.5", "@types/body-parser": "1.17.0", "@types/cors": "^2.8.4", - "@types/express": "4.16.0", + "@types/express": "4.16.1", "accepts": "^1.3.5", "apollo-server-core": "file:../apollo-server-core", "body-parser": "^1.18.3", diff --git a/packages/apollo-server-express/src/ApolloServer.ts b/packages/apollo-server-express/src/ApolloServer.ts index 2205414cb21..dae1b59dbad 100644 --- a/packages/apollo-server-express/src/ApolloServer.ts +++ b/packages/apollo-server-express/src/ApolloServer.ts @@ -10,14 +10,13 @@ import { FileUploadOptions, ApolloServerBase, formatApolloErrors, + processFileUploads, } from 'apollo-server-core'; import accepts from 'accepts'; import typeis from 'type-is'; import { graphqlExpress } from './expressApollo'; -import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server'; - export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core'; export interface ServerRegistration { @@ -44,8 +43,11 @@ const fileUploadMiddleware = ( next: express.NextFunction, ) => { // Note: we use typeis directly instead of via req.is for connect support. - if (typeis(req, ['multipart/form-data'])) { - processFileUploads(req, uploadsConfig) + if ( + typeof processFileUploads === 'function' && + typeis(req, ['multipart/form-data']) + ) { + processFileUploads(req, res, uploadsConfig) .then(body => { req.body = body; next(); @@ -84,6 +86,9 @@ export class ApolloServer extends ApolloServerBase { return true; } + // TODO: While `express` is not Promise-aware, this should become `async` in + // a major release in order to align the API with other integrations (e.g. + // Hapi) which must be `async`. public applyMiddleware({ app, path, @@ -94,8 +99,24 @@ export class ApolloServer extends ApolloServerBase { }: ServerRegistration) { if (!path) path = '/graphql'; + // Despite the fact that this `applyMiddleware` function is `async` in + // other integrations (e.g. Hapi), currently it is not for Express (@here). + // That should change in a future version, but that would be a breaking + // change right now (see comment above this method's declaration above). + // + // That said, we do need to await the `willStart` lifecycle event which + // can perform work prior to serving a request. Since Express doesn't + // natively support Promises yet, we'll do this via a middleware that + // calls `next` when the `willStart` finishes. We'll kick off the + // `willStart` right away, so hopefully it'll finish before the first + // request comes in, but we won't call `next` on this middleware until it + // does. (And we'll take care to surface any errors via the `.catch`-able.) + const promiseWillStart = this.willStart(); + app.use(path, (_req, _res, next) => { + promiseWillStart.then(() => next()).catch(next); + }); + if (!disableHealthCheck) { - // uses same path as engine proxy, but is generally useful. app.use('/.well-known/apollo/server-health', (req, res) => { // Response follows https://tools.ietf.org/html/draft-inadarei-api-health-check-01 res.type('application/health+json'); @@ -115,7 +136,7 @@ export class ApolloServer extends ApolloServerBase { } let uploadsMiddleware; - if (this.uploadsConfig) { + if (this.uploadsConfig && typeof processFileUploads === 'function') { uploadsMiddleware = fileUploadMiddleware(this.uploadsConfig, this); } @@ -169,11 +190,9 @@ export class ApolloServer extends ApolloServerBase { return; } } - return graphqlExpress(this.createGraphQLServerOptions.bind(this))( - req, - res, - next, - ); + return graphqlExpress(() => { + return this.createGraphQLServerOptions(req, res); + })(req, res, next); }); } } diff --git a/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts index ce385c50bd4..e9db26e4461 100644 --- a/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-express/src/__tests__/ApolloServer.test.ts @@ -11,7 +11,7 @@ import { gql, AuthenticationError, Config } from 'apollo-server-core'; import { ApolloServer, ServerRegistration } from '../ApolloServer'; import { - atLeastMajorNodeVersion, + NODE_MAJOR_VERSION, testApolloServer, createServerInfo, } from 'apollo-server-integration-testsuite'; @@ -427,8 +427,9 @@ describe('apollo-server-express', () => { }); }); }); - // NODE: Intentionally skip file upload tests on Node.js 10 or higher. - (atLeastMajorNodeVersion(10) ? describe.skip : describe)( + // NODE: Skip Node.js 6, but only because `graphql-upload` + // doesn't support it. + (NODE_MAJOR_VERSION === 6 ? describe.skip : describe)( 'file uploads', () => { it('enabled uploads', async () => { diff --git a/packages/apollo-server-express/src/expressApollo.ts b/packages/apollo-server-express/src/expressApollo.ts index 0affc8170b6..607c699f70e 100644 --- a/packages/apollo-server-express/src/expressApollo.ts +++ b/packages/apollo-server-express/src/expressApollo.ts @@ -1,15 +1,16 @@ import express from 'express'; import { GraphQLOptions, - ServerOptionsFunction, HttpQueryError, runHttpQuery, convertNodeHttpToRequest, } from 'apollo-server-core'; -export type ExpressGraphQLOptionsFunction = ServerOptionsFunction< - [express.Request, express.Response] ->; +export interface ExpressGraphQLOptionsFunction { + (req?: express.Request, res?: express.Response): + | GraphQLOptions + | Promise; +} // Design principles: // - there is just one way allowed: POST request with JSON body. Nothing else. diff --git a/packages/apollo-server-hapi/package.json b/packages/apollo-server-hapi/package.json index 812e6c04f0e..5d393fb6115 100644 --- a/packages/apollo-server-hapi/package.json +++ b/packages/apollo-server-hapi/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-hapi", - "version": "2.2.0-alpha.0", + "version": "2.3.3", "description": "Production-ready Node.js GraphQL server for Hapi", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -25,8 +25,7 @@ "node": ">=8" }, "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:../apollo-server-core", "boom": "^7.1.0", diff --git a/packages/apollo-server-hapi/src/ApolloServer.ts b/packages/apollo-server-hapi/src/ApolloServer.ts index 77b60ce36c1..79c7d2dc2d8 100644 --- a/packages/apollo-server-hapi/src/ApolloServer.ts +++ b/packages/apollo-server-hapi/src/ApolloServer.ts @@ -4,7 +4,6 @@ import { renderPlaygroundPage, RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html'; -import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server'; import { graphqlHapi } from './hapiApollo'; @@ -13,13 +12,21 @@ import { ApolloServerBase, GraphQLOptions, FileUploadOptions, + processFileUploads, } from 'apollo-server-core'; function handleFileUploads(uploadsConfig: FileUploadOptions) { - return async (request: hapi.Request) => { - if (request.mime === 'multipart/form-data') { + return async (request: hapi.Request, _h?: hapi.ResponseToolkit) => { + if ( + typeof processFileUploads === 'function' && + request.mime === 'multipart/form-data' + ) { Object.defineProperty(request, 'payload', { - value: await processFileUploads(request, uploadsConfig), + value: await processFileUploads( + request, + request.response, + uploadsConfig, + ), writable: false, }); } @@ -53,6 +60,8 @@ export class ApolloServer extends ApolloServerBase { disableHealthCheck, onHealthCheck, }: ServerRegistration) { + await this.willStart(); + if (!path) path = '/graphql'; await app.ext({ @@ -62,7 +71,7 @@ export class ApolloServer extends ApolloServerBase { return h.continue; } - if (this.uploadsConfig) { + if (this.uploadsConfig && typeof processFileUploads === 'function') { await handleFileUploads(this.uploadsConfig)(request); } diff --git a/packages/apollo-server-hapi/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-hapi/src/__tests__/ApolloServer.test.ts index 60cf32e1206..43d00dd7761 100644 --- a/packages/apollo-server-hapi/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-hapi/src/__tests__/ApolloServer.test.ts @@ -1,5 +1,5 @@ import { - atLeastMajorNodeVersion, + NODE_MAJOR_VERSION, testApolloServer, createServerInfo, } from 'apollo-server-integration-testsuite'; @@ -16,7 +16,7 @@ import { ApolloServer } from '../ApolloServer'; const port = 5555; // NODE: Intentionally skip for Node.js < 8 since Hapi 17 doesn't support those. -(atLeastMajorNodeVersion(8) ? describe : describe.skip)( +(NODE_MAJOR_VERSION < 8 ? describe.skip : describe)( 'apollo-server-hapi', () => { let server: ApolloServer; diff --git a/packages/apollo-server-hapi/src/__tests__/hapiApollo.test.ts b/packages/apollo-server-hapi/src/__tests__/hapiApollo.test.ts index d76eec1f832..2145b44c751 100644 --- a/packages/apollo-server-hapi/src/__tests__/hapiApollo.test.ts +++ b/packages/apollo-server-hapi/src/__tests__/hapiApollo.test.ts @@ -4,40 +4,37 @@ import { Config } from 'apollo-server-core'; import testSuite, { schema as Schema, CreateAppOptions, - atLeastMajorNodeVersion, + NODE_MAJOR_VERSION, } from 'apollo-server-integration-testsuite'; // NODE: Intentionally skip on Node.js < 8 since Hapi 17 doesn't support less -(atLeastMajorNodeVersion(8) ? describe : describe.skip)( - 'integration:Hapi', - () => { - async function createApp(options: CreateAppOptions = {}) { - const { Server } = require('hapi'); - - const app: import('hapi').Server = new Server({ - host: 'localhost', - port: 8000, - }); - - const server = new ApolloServer( - (options.graphqlOptions as Config) || { schema: Schema }, - ); - await server.applyMiddleware({ - app, - }); - - await app.start(); - - return app.listener; +(NODE_MAJOR_VERSION < 8 ? describe.skip : describe)('integration:Hapi', () => { + async function createApp(options: CreateAppOptions = {}) { + const { Server } = require('hapi'); + + const app: import('hapi').Server = new Server({ + host: 'localhost', + port: 8000, + }); + + const server = new ApolloServer( + (options.graphqlOptions as Config) || { schema: Schema }, + ); + await server.applyMiddleware({ + app, + }); + + await app.start(); + + return app.listener; + } + + async function destroyApp(app) { + if (!app || !app.close) { + return; } + await new Promise(resolve => app.close(resolve)); + } - async function destroyApp(app) { - if (!app || !app.close) { - return; - } - await new Promise(resolve => app.close(resolve)); - } - - testSuite(createApp, destroyApp); - }, -); + testSuite(createApp, destroyApp); +}); diff --git a/packages/apollo-server-hapi/src/hapiApollo.ts b/packages/apollo-server-hapi/src/hapiApollo.ts index eac464a9e10..c85e8f15058 100644 --- a/packages/apollo-server-hapi/src/hapiApollo.ts +++ b/packages/apollo-server-hapi/src/hapiApollo.ts @@ -1,8 +1,7 @@ import Boom from 'boom'; -import { Server, Request, RouteOptions, ResponseToolkit } from 'hapi'; +import { Server, Request, RouteOptions } from 'hapi'; import { GraphQLOptions, - ServerOptionsFunction, runHttpQuery, convertNodeHttpToRequest, } from 'apollo-server-core'; @@ -17,9 +16,9 @@ export interface IPlugin { register: IRegister; } -export type HapiOptionsFunction = ServerOptionsFunction< - [Request, ResponseToolkit] ->; +export interface HapiOptionsFunction { + (request?: Request): GraphQLOptions | Promise; +} export interface HapiPluginOptions { path: string; diff --git a/packages/apollo-server-integration-testsuite/package.json b/packages/apollo-server-integration-testsuite/package.json index bbe497a9294..d29acdfee57 100644 --- a/packages/apollo-server-integration-testsuite/package.json +++ b/packages/apollo-server-integration-testsuite/package.json @@ -1,7 +1,7 @@ { "name": "apollo-server-integration-testsuite", "private": true, - "version": "2.2.0-alpha.0", + "version": "2.3.3", "description": "Apollo Server Integrations testsuite", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts index 380c130f97c..750e4a5beaf 100644 --- a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts +++ b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts @@ -567,6 +567,9 @@ export function testApolloServer( engineServerDidStart = startEngineServer({ check: (req, res) => { const report = FullTracesReport.decode(req.body); + const header = report.header; + expect(header.schemaTag).toEqual(''); + expect(header.schemaHash).toBeDefined(); const trace = Object.values(report.tracesPerQuery)[0].trace[0]; resolve(trace); res.end(); @@ -597,6 +600,11 @@ export function testApolloServer( }`, apiKey: 'service:my-app:secret', maxUncompressedReportSize: 1, + generateClientInfo: () => ({ + clientName: 'testing', + clientReferenceId: '1234', + clientVersion: 'v1.0.1', + }), }, formatError, debug: true, @@ -620,6 +628,10 @@ export function testApolloServer( const trace = await didReceiveTrace; + expect(trace.clientReferenceId).toMatch(/1234/); + expect(trace.clientName).toMatch(/testing/); + expect(trace.clientVersion).toEqual('v1.0.1'); + expect(trace.root!.child![0].error![0].message).toMatch(/nope/); expect(trace.root!.child![0].error![0].message).not.toMatch(/masked/); }); @@ -966,7 +978,7 @@ export function testApolloServer( }); }); }); - it('disables subscritpions when option set to false', done => { + it('disables subscriptions when option set to false', done => { const typeDefs = gql` type Query { "graphql-js forces there to be a query type" @@ -1013,7 +1025,7 @@ export function testApolloServer( } const client = new SubscriptionClient( - `ws://localhost:${port}${server.subscriptionsPath}`, + `ws://localhost:${port}${server.subscriptionsPath || ''}`, {}, WebSocket, ); diff --git a/packages/apollo-server-integration-testsuite/src/index.ts b/packages/apollo-server-integration-testsuite/src/index.ts index 0379a6bb4b1..0cd9fac44a1 100644 --- a/packages/apollo-server-integration-testsuite/src/index.ts +++ b/packages/apollo-server-integration-testsuite/src/index.ts @@ -23,13 +23,10 @@ import gql from 'graphql-tag'; export * from './ApolloServer'; -const NODE_MAJOR_VERSION: number = parseInt( +export const NODE_MAJOR_VERSION: number = parseInt( process.versions.node.split('.', 1)[0], 10, ); -export function atLeastMajorNodeVersion(desiredVersion: number): boolean { - return NODE_MAJOR_VERSION >= desiredVersion; -} const QueryRootType = new GraphQLObjectType({ name: 'QueryRoot', @@ -126,7 +123,7 @@ const queryType = new GraphQLObjectType({ }, testContext: { type: GraphQLString, - resolve(_root, _args, context) { + resolve(_parent, _args, context) { if (context.otherField) { return 'unexpected'; } @@ -664,33 +661,28 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => { }); }); - it( - 'can handle batch requests in parallel', - async function() { - const parallels = 100; - const delayPerReq = 40; + it('can handle batch requests in parallel', async function() { + const parallels = 100; + const delayPerReq = 40; - app = await createApp(); - const expected = Array(parallels).fill({ - data: { testStringWithDelay: 'it works' }, - }); - const req = request(app) - .post('/graphql') - .send( - Array(parallels).fill({ - query: `query test($delay: Int!) { testStringWithDelay(delay: $delay) }`, - operationName: 'test', - variables: { delay: delayPerReq }, - }), - ); - return req.then(res => { - expect(res.status).toEqual(200); - expect(res.body).toEqual(expected); - }); - }, - // this test will fail due to timeout if running serially. - 3000, - ); + app = await createApp(); + const expected = Array(parallels).fill({ + data: { testStringWithDelay: 'it works' }, + }); + const req = request(app) + .post('/graphql') + .send( + Array(parallels).fill({ + query: `query test($delay: Int!) { testStringWithDelay(delay: $delay) }`, + operationName: 'test', + variables: { delay: delayPerReq }, + }), + ); + return req.then(res => { + expect(res.status).toEqual(200); + expect(res.body).toEqual(expected); + }); + }, 3000); // this test will fail due to timeout if running serially. it('clones batch context', async () => { app = await createApp({ @@ -1072,6 +1064,80 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => { }); }); + describe('request pipeline plugins', () => { + describe('lifecycle hooks', () => { + it('calls serverWillStart before serving a request', async () => { + // We'll use this eventually-assigned function to programmatically + // resolve the `serverWillStart` event. + let resolveServerWillStart: Function; + + // We'll use this mocked function to determine the order in which + // the events we're expecting to happen actually occur and validate + // those expectations in various stages of this test. + const fn = jest.fn(); + + // We want this to create the app as fast as `createApp` will allow. + // for integrations whose `applyMiddleware` currently returns a + // Promise we want them to resolve at whatever eventual pace they + // will so we can make sure that things are happening in order. + const unawaitedApp = createApp({ + graphqlOptions: { + schema, + plugins: [ + { + serverWillStart() { + fn('zero'); + return new Promise(resolve => { + resolveServerWillStart = () => { + fn('one'); + resolve(); + }; + }); + }, + }, + ], + }, + }); + + // Make sure that things were called in the expected order. + expect(fn.mock.calls).toEqual([['zero']]); + + resolveServerWillStart(); + + // Account for the fact that `createApp` might return a Promise, + // and might not, depending on the integration's implementation of + // createApp. This is entirely to account for the fact that + // non-async implementations of `applyMiddleware` leverage a + // middleware as the technique for yielding to `startWillStart` + // hooks while their `async` counterparts simply `await` those same + // hooks. In a future where we make the behavior of `applyMiddleware` + // the same across all integrations, this should be changed to simply + // `await unawaitedApp`. + app = 'then' in unawaitedApp ? await unawaitedApp : unawaitedApp; + + // Intentionally fire off the request asynchronously, without await. + const res = request(app) + .get('/graphql') + .query({ + query: 'query test{ testString }', + }) + .then(res => { + fn('two'); + return res; + }); + + // Ensure the request has not gone through. + expect(fn.mock.calls).toEqual([['zero'], ['one']]); + + // Now, wait for the request to finish. + await res; + + // Finally, ensure that the order we expected was achieved. + expect(fn.mock.calls).toEqual([['zero'], ['one'], ['two']]); + }); + }); + }); + describe('Persisted Queries', () => { const query = '{testString}'; const query2 = '{ testString }'; @@ -1104,6 +1170,7 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => { await map.set(key, val); }, get: async key => map.get(key), + delete: async key => map.delete(key), }; app = await createApp({ graphqlOptions: { diff --git a/packages/apollo-server-koa/package.json b/packages/apollo-server-koa/package.json index 95a0512f918..872bcdb5542 100644 --- a/packages/apollo-server-koa/package.json +++ b/packages/apollo-server-koa/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-koa", - "version": "2.2.0-alpha.0", + "version": "2.3.3", "description": "Production-ready Node.js GraphQL server for Koa", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -25,8 +25,7 @@ "node": ">=6" }, "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "@koa/cors": "^2.2.1", "@types/accepts": "^1.3.5", "@types/cors": "^2.8.4", @@ -38,7 +37,7 @@ "apollo-server-core": "file:../apollo-server-core", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", - "koa": "2.5.3", + "koa": "2.7.0", "koa-bodyparser": "^3.0.0", "koa-router": "^7.4.0", "type-is": "^1.6.16" diff --git a/packages/apollo-server-koa/src/ApolloServer.ts b/packages/apollo-server-koa/src/ApolloServer.ts index 8f3c7631139..2ad247ea052 100644 --- a/packages/apollo-server-koa/src/ApolloServer.ts +++ b/packages/apollo-server-koa/src/ApolloServer.ts @@ -6,14 +6,16 @@ import { renderPlaygroundPage, RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html'; -import { ApolloServerBase, formatApolloErrors } from 'apollo-server-core'; +import { + ApolloServerBase, + formatApolloErrors, + processFileUploads, +} from 'apollo-server-core'; import accepts from 'accepts'; import typeis from 'type-is'; import { graphqlKoa } from './koaApollo'; -import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server'; - export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core'; import { GraphQLOptions, FileUploadOptions } from 'apollo-server-core'; @@ -32,7 +34,11 @@ const fileUploadMiddleware = ( ) => async (ctx: Koa.Context, next: Function) => { if (typeis(ctx.req, ['multipart/form-data'])) { try { - ctx.request.body = await processFileUploads(ctx.req, uploadsConfig); + ctx.request.body = await processFileUploads( + ctx.req, + ctx.res, + uploadsConfig, + ); return next(); } catch (error) { if (error.status && error.expose) ctx.status = error.status; @@ -74,6 +80,10 @@ export class ApolloServer extends ApolloServerBase { return true; } + // TODO: While Koa is Promise-aware, this API hasn't been historically, even + // though other integration's (e.g. Hapi) implementations of this method + // are `async`. Therefore, this should become `async` in a major release in + // order to align the API with other integrations. public applyMiddleware({ app, path, @@ -84,8 +94,27 @@ export class ApolloServer extends ApolloServerBase { }: ServerRegistration) { if (!path) path = '/graphql'; + // Despite the fact that this `applyMiddleware` function is `async` in + // other integrations (e.g. Hapi), currently it is not for Koa (@here). + // That should change in a future version, but that would be a breaking + // change right now (see comment above this method's declaration above). + // + // That said, we do need to await the `willStart` lifecycle event which + // can perform work prior to serving a request. While we could do this + // via awaiting in a Koa middleware, well kick off `willStart` right away, + // so hopefully it'll finish before the first request comes in. We won't + // call `next` until it's ready, which will effectively yield until that + // work has finished. Any errors will be surfaced to Koa through its own + // native Promise-catching facilities. + const promiseWillStart = this.willStart(); + app.use( + middlewareFromPath(path, async (_ctx: Koa.Context, next: Function) => { + await promiseWillStart; + return next(); + }), + ); + if (!disableHealthCheck) { - // uses same path as engine proxy, but is generally useful. app.use( middlewareFromPath( '/.well-known/apollo/server-health', @@ -111,7 +140,7 @@ export class ApolloServer extends ApolloServerBase { } let uploadsMiddleware; - if (this.uploadsConfig) { + if (this.uploadsConfig && typeof processFileUploads === 'function') { uploadsMiddleware = fileUploadMiddleware(this.uploadsConfig, this); } @@ -158,10 +187,9 @@ export class ApolloServer extends ApolloServerBase { return; } } - return graphqlKoa(this.createGraphQLServerOptions.bind(this))( - ctx, - next, - ); + return graphqlKoa(() => { + return this.createGraphQLServerOptions(ctx); + })(ctx, next); }), ); } diff --git a/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts index 25ed0197002..9318be58c96 100644 --- a/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts @@ -11,7 +11,7 @@ import { gql, AuthenticationError, Config } from 'apollo-server-core'; import { ApolloServer, ServerRegistration } from '../ApolloServer'; import { - atLeastMajorNodeVersion, + NODE_MAJOR_VERSION, testApolloServer, createServerInfo, } from 'apollo-server-integration-testsuite'; @@ -323,8 +323,9 @@ describe('apollo-server-koa', () => { }); }); }); - // NODE: Intentionally skip file upload tests on Node.js 10 or higher. - (atLeastMajorNodeVersion(10) ? describe.skip : describe)( + // NODE: Skip Node.js 6, but only because `graphql-upload` + // doesn't support it anymore. + (NODE_MAJOR_VERSION === 6 ? describe.skip : describe)( 'file uploads', () => { it('enabled uploads', async () => { diff --git a/packages/apollo-server-koa/src/koaApollo.ts b/packages/apollo-server-koa/src/koaApollo.ts index 2a73e574243..b25aba2d3f8 100644 --- a/packages/apollo-server-koa/src/koaApollo.ts +++ b/packages/apollo-server-koa/src/koaApollo.ts @@ -1,13 +1,14 @@ import Koa from 'koa'; import { GraphQLOptions, - ServerOptionsFunction, HttpQueryError, runHttpQuery, convertNodeHttpToRequest, } from 'apollo-server-core'; -export type KoaGraphQLOptionsFunction = ServerOptionsFunction<[Koa.Context]>; +export interface KoaGraphQLOptionsFunction { + (ctx: Koa.Context): GraphQLOptions | Promise; +} export interface KoaHandler { (ctx: Koa.Context, next): void; diff --git a/packages/apollo-server-lambda/package.json b/packages/apollo-server-lambda/package.json index b0679d4eb44..aace6d15090 100644 --- a/packages/apollo-server-lambda/package.json +++ b/packages/apollo-server-lambda/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-lambda", - "version": "2.2.0-alpha.0", + "version": "2.3.3", "description": "Production-ready Node.js GraphQL server for AWS Lambda", "keywords": [ "GraphQL", @@ -25,8 +25,7 @@ "node": ">=6" }, "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "apollo-server-core": "file:../apollo-server-core", "apollo-server-env": "file:../apollo-server-env", "graphql-tools": "^4.0.0" diff --git a/packages/apollo-server-lambda/src/ApolloServer.ts b/packages/apollo-server-lambda/src/ApolloServer.ts index 87f565c7f69..0f989d08d52 100644 --- a/packages/apollo-server-lambda/src/ApolloServer.ts +++ b/packages/apollo-server-lambda/src/ApolloServer.ts @@ -1,10 +1,11 @@ import lambda from 'aws-lambda'; -import { ApolloServerBase } from 'apollo-server-core'; -import { GraphQLOptions, Config, FileUploadOptions } from 'apollo-server-core'; +import { ApolloServerBase, formatApolloErrors } from 'apollo-server-core'; +import { GraphQLOptions, Config, FileUploadOptions, processFileUploads } from 'apollo-server-core'; import { renderPlaygroundPage, RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html'; +import stream, { Stream } from 'stream'; import { graphqlLambda } from './lambdaApollo'; @@ -44,9 +45,12 @@ export class ApolloServer extends ApolloServerBase { return super.graphQLServerOptions({ event, context }); } - public createHandler( - { cors, uploadsConfig }: CreateHandlerOptions = { cors: undefined }, - ) { + public createHandler({ cors }: CreateHandlerOptions = { cors: undefined }) { + // We will kick off the `willStart` event once for the server, and then + // await it before processing any requests by incorporating its `await` into + // the GraphQLServerOptions function which is called before each request. + const promiseWillStart = this.willStart(); + const corsHeaders: lambda.APIGatewayProxyResult['headers'] = {}; if (cors) { @@ -143,27 +147,81 @@ export class ApolloServer extends ApolloServerBase { } } - const callbackFilter: lambda.APIGatewayProxyCallback = ( - error, - result, - ) => { - callback( + const makeCallbackFilter = (doneFn: any): lambda.APIGatewayProxyCallback => { + return ( error, - result && { - ...result, - headers: { - ...result.headers, - ...corsHeaders, + result, + ) => { + doneFn() + callback( + error, + result && { + ...result, + headers: { + ...result.headers, + ...corsHeaders, + }, }, - }, - ); - }; + ); + }; + } - graphqlLambda(this.createGraphQLServerOptions.bind(this), uploadsConfig)( - event, - context, - callbackFilter, - ); + const response = new Stream.Writable(); + + fileUploadProcess(event, response, this.uploadsConfig || {}) + .then((body: any) => { + event.body = body; + graphqlLambda(async () => { + // In a world where this `createHandler` was async, we might avoid this + // but since we don't want to introduce a breaking change to this API + // (by switching it to `async`), we'll leverage the + // `GraphQLServerOptions`, which are dynamically built on each request, + // to `await` the `promiseWillStart` which we kicked off at the top of + // this method to ensure that it runs to completion (which is part of + // its contract) prior to processing the request. + await promiseWillStart; + return this.createGraphQLServerOptions(event, context); + })(event, context, makeCallbackFilter(() => { response.end() })); + }) + .catch(error => { + throw formatApolloErrors([error], { + formatter: this.requestOptions.formatError, + debug: this.requestOptions.debug, + }); + }); }; } } + + +const fileUploadProcess = ( + event: any, + response: any, + uploadsConfig: FileUploadOptions, +) => { + return new Promise((resolve, reject) => { + const contentType = + event.headers['content-type'] || event.headers['Content-Type']; + + if (typeof processFileUploads === 'function' && contentType && contentType.startsWith('multipart/form-data')) { + const request = new stream.Readable() as any; + request.push( + Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'ascii'), + ); + request.push(null); + request.headers = event.headers; + request.headers['content-type'] = contentType; + + processFileUploads(request, response, uploadsConfig) + .then(body => { + resolve(body); + }) + .catch(error => { + reject(error) + }); + } + else { + resolve(event.body) + } + }) +}; diff --git a/packages/apollo-server-lambda/src/__tests__/lambdaApollo.test.ts b/packages/apollo-server-lambda/src/__tests__/lambdaApollo.test.ts index 452bbc8a748..abf0a7cfa7e 100644 --- a/packages/apollo-server-lambda/src/__tests__/lambdaApollo.test.ts +++ b/packages/apollo-server-lambda/src/__tests__/lambdaApollo.test.ts @@ -2,6 +2,7 @@ import { ApolloServer } from '../ApolloServer'; import testSuite, { schema as Schema, CreateAppOptions, + NODE_MAJOR_VERSION } from 'apollo-server-integration-testsuite'; import { Config } from 'apollo-server-core'; import url from 'url'; @@ -79,7 +80,7 @@ const typeDefs = gql` const resolvers = { Query: { - uploads: () => {}, + uploads: () => { }, helloWorld: () => 'hi', }, Mutation: { @@ -90,47 +91,52 @@ const resolvers = { }, }; -describe('file uploads', () => { - it('enabled uploads', async () => { - const app = await createLambda({ - graphqlOptions: { - typeDefs, - resolvers, - }, - }); +// NODE: Skip Node.js 6, but only because `graphql-upload` +// doesn't support it. +(NODE_MAJOR_VERSION === 6 ? describe.skip : describe)( + 'file uploads', + () => { + it('enabled uploads', async () => { + const app = await createLambda({ + graphqlOptions: { + typeDefs, + resolvers, + }, + }); - const expected = { - filename: 'package.json', - encoding: '7bit', - mimetype: 'application/json', - }; + const expected = { + filename: 'package.json', + encoding: '7bit', + mimetype: 'application/json', + }; - const req = request(app) - .post('/graphql') - .type('form') - .field( - 'operations', - JSON.stringify({ - query: ` - mutation($file: Upload!) { - singleUpload(file: $file) { - filename - encoding - mimetype + const req = request(app) + .post('/graphql') + .type('form') + .field( + 'operations', + JSON.stringify({ + query: ` + mutation($file: Upload!) { + singleUpload(file: $file) { + filename + encoding + mimetype + } } - } - `, - variables: { - file: null, - }, - }), - ) - .field('map', JSON.stringify({ 1: ['variables.file'] })) - .attach('1', 'package.json'); + `, + variables: { + file: null, + }, + }), + ) + .field('map', JSON.stringify({ 1: ['variables.file'] })) + .attach('1', 'package.json'); - return req.then((res: any) => { - expect(res.status).toEqual(200); - expect(res.body.data.singleUpload).toEqual(expected); + return req.then((res: any) => { + expect(res.status).toEqual(200); + expect(res.body.data.singleUpload).toEqual(expected); + }); }); - }); -}); + }, +); diff --git a/packages/apollo-server-lambda/src/lambdaApollo.ts b/packages/apollo-server-lambda/src/lambdaApollo.ts index 0269fcfe279..f649f968e90 100644 --- a/packages/apollo-server-lambda/src/lambdaApollo.ts +++ b/packages/apollo-server-lambda/src/lambdaApollo.ts @@ -1,52 +1,27 @@ import lambda from 'aws-lambda'; import { GraphQLOptions, - ServerOptionsFunction, HttpQueryError, runHttpQuery, - FileUploadOptions, } from 'apollo-server-core'; import { Headers } from 'apollo-server-env'; -import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server'; -import stream from 'stream'; -export type LambdaGraphQLOptionsFunction = ServerOptionsFunction< - [lambda.APIGatewayProxyEvent, lambda.Context] ->; - -const fileUploadProcess = async ( - event: any, - uploadsConfig?: FileUploadOptions, -) => { - const contentType = - event.headers['content-type'] || event.headers['Content-Type']; - - if (contentType && contentType.startsWith('multipart/form-data')) { - const request = new stream.Readable() as any; - request.push( - Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'ascii'), - ); - request.push(null); - request.headers = event.headers; - request.headers['content-type'] = contentType; - - return await processFileUploads(request, uploadsConfig || {}); - } - - return event.body; -}; +export interface LambdaGraphQLOptionsFunction { + (event: lambda.APIGatewayProxyEvent, context: lambda.Context): + | GraphQLOptions + | Promise; +} export function graphqlLambda( options: GraphQLOptions | LambdaGraphQLOptionsFunction, - uploadsConfig?: FileUploadOptions, ): lambda.APIGatewayProxyHandler { if (!options) { throw new Error('Apollo Server requires options.'); } - if (arguments.length > 2) { + if (arguments.length > 1) { throw new Error( - `Apollo Server expects one or two argument, got ${arguments.length}`, + `Apollo Server expects exactly one argument, got ${arguments.length}`, ); } @@ -64,47 +39,41 @@ export function graphqlLambda( }); } - fileUploadProcess(event, uploadsConfig) - .then((body: any) => { - event.body = body; - - let query: any = event.queryStringParameters; - if (event.httpMethod === 'POST' && event.body) { - if (typeof event.body === 'string') { - query = JSON.parse(event.body); - } else { - query = event.body; - } - } + let query: any = event.queryStringParameters; + if (event.httpMethod === 'POST' && event.body) { + if (typeof event.body === 'string') { + query = JSON.parse(event.body); + } else { + query = event.body; + } + } - return runHttpQuery([event, context], { - method: event.httpMethod, - options: options, - query, - request: { - url: event.path, - method: event.httpMethod, - headers: new Headers(event.headers), - }, + runHttpQuery([event, context], { + method: event.httpMethod, + options: options, + query, + request: { + url: event.path, + method: event.httpMethod, + headers: new Headers(event.headers), + }, + }).then( + ({ graphqlResponse, responseInit }) => { + callback(null, { + body: graphqlResponse, + statusCode: 200, + headers: responseInit.headers, }); - }) - .then( - ({ graphqlResponse, responseInit }) => { - callback(null, { - body: graphqlResponse, - statusCode: 200, - headers: responseInit.headers, - }); - }, - (error: HttpQueryError) => { - if ('HttpQueryError' !== error.name) return callback(error); - callback(null, { - body: error.message, - statusCode: error.statusCode, - headers: error.headers, - }); - }, - ); + }, + (error: HttpQueryError) => { + if ('HttpQueryError' !== error.name) return callback(error); + callback(null, { + body: error.message, + statusCode: error.statusCode, + headers: error.headers, + }); + }, + ); }; return graphqlHandler; diff --git a/packages/apollo-server-lambda/src/types.d.ts b/packages/apollo-server-lambda/src/types.d.ts deleted file mode 100644 index 4d3fbdfa624..00000000000 --- a/packages/apollo-server-lambda/src/types.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module '@apollographql/apollo-upload-server'; diff --git a/packages/apollo-server-micro/README.md b/packages/apollo-server-micro/README.md index d0719241a8b..4b393f9d7cc 100644 --- a/packages/apollo-server-micro/README.md +++ b/packages/apollo-server-micro/README.md @@ -31,7 +31,7 @@ const typeDefs = gql` const resolvers = { Query: { - sayHello(root, args, context) { + sayHello(parent, args, context) { return 'Hello World!'; }, }, @@ -82,7 +82,7 @@ const typeDefs = gql` const resolvers = { Query: { - sayHello(root, args, context) { + sayHello(parent, args, context) { return 'Hello World!'; }, }, @@ -132,7 +132,7 @@ const typeDefs = gql` const resolvers = { Query: { - sayHello(root, args, context) { + sayHello(parent, args, context) { return 'Hello World!'; }, }, @@ -184,7 +184,7 @@ const typeDefs = gql` const resolvers = { Query: { - sayHello(root, args, context) { + sayHello(parent, args, context) { return 'Hello World!'; }, }, @@ -195,7 +195,6 @@ const graphqlPath = '/data'; const graphqlHandler = apolloServer.createHandler({ path: graphqlPath }); module.exports = router( get('/', (req, res) => 'Welcome!'), - options(graphqlPath, graphqlHandler), post(graphqlPath, graphqlHandler), get(graphqlPath, graphqlHandler), ); diff --git a/packages/apollo-server-micro/package.json b/packages/apollo-server-micro/package.json index fbdf19b0c98..acef9838f40 100644 --- a/packages/apollo-server-micro/package.json +++ b/packages/apollo-server-micro/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-micro", - "version": "2.2.0-alpha.0", + "version": "2.3.3", "description": "Production-ready Node.js GraphQL server for Micro", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -23,8 +23,7 @@ }, "homepage": "https://github.com/apollographql/apollo-server#readme", "dependencies": { - "@apollographql/apollo-upload-server": "^5.0.3", - "@apollographql/graphql-playground-html": "^1.6.0", + "@apollographql/graphql-playground-html": "^1.6.6", "accept": "^3.0.2", "apollo-server-core": "file:../apollo-server-core", "micro": "^9.3.2" diff --git a/packages/apollo-server-micro/src/ApolloServer.ts b/packages/apollo-server-micro/src/ApolloServer.ts index 1db04cf3f0f..6df712e063e 100644 --- a/packages/apollo-server-micro/src/ApolloServer.ts +++ b/packages/apollo-server-micro/src/ApolloServer.ts @@ -1,5 +1,8 @@ -import { ApolloServerBase, GraphQLOptions } from 'apollo-server-core'; -import { processRequest as processFileUploads } from '@apollographql/apollo-upload-server'; +import { + ApolloServerBase, + GraphQLOptions, + processFileUploads, +} from 'apollo-server-core'; import { ServerResponse } from 'http'; import { send } from 'micro'; import { renderPlaygroundPage } from '@apollographql/graphql-playground-html'; @@ -30,10 +33,18 @@ export class ApolloServer extends ApolloServerBase { disableHealthCheck, onHealthCheck, }: ServerRegistration = {}) { + // We'll kick off the `willStart` right away, so hopefully it'll finish + // before the first request comes in. + const promiseWillStart = this.willStart(); + return async (req, res) => { this.graphqlPath = path || '/graphql'; - await this.handleFileUploads(req); + await promiseWillStart; + + if (typeof processFileUploads === 'function') { + await this.handleFileUploads(req, res); + } (await this.handleHealthCheck({ req, @@ -124,6 +135,7 @@ export class ApolloServer extends ApolloServerBase { subscriptionEndpoint: this.subscriptionsPath, ...this.playgroundOptions, }; + res.setHeader('Content-Type', 'text/html; charset=utf-8'); send(res, 200, renderPlaygroundPage(middlewareOptions)); handled = true; } @@ -143,9 +155,9 @@ export class ApolloServer extends ApolloServerBase { let handled = false; const url = req.url.split('?')[0]; if (url === this.graphqlPath) { - const graphqlHandler = graphqlMicro( - this.createGraphQLServerOptions.bind(this), - ); + const graphqlHandler = graphqlMicro(() => { + return this.createGraphQLServerOptions(req, res); + }); const responseData = await graphqlHandler(req, res); send(res, 200, responseData); handled = true; @@ -154,15 +166,19 @@ export class ApolloServer extends ApolloServerBase { } // If file uploads are detected, prepare them for easier handling with - // the help of `apollo-upload-server`. - private async handleFileUploads(req: MicroRequest) { + // the help of `graphql-upload`. + private async handleFileUploads(req: MicroRequest, res: ServerResponse) { + if (typeof processFileUploads !== 'function') { + return; + } + const contentType = req.headers['content-type']; if ( this.uploadsConfig && contentType && contentType.startsWith('multipart/form-data') ) { - req.filePayload = await processFileUploads(req, this.uploadsConfig); + req.filePayload = await processFileUploads(req, res, this.uploadsConfig); } } } diff --git a/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts b/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts index fcbad2cb06a..89742ae4da1 100644 --- a/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts +++ b/packages/apollo-server-micro/src/__tests__/ApolloServer.test.ts @@ -1,7 +1,7 @@ import micro from 'micro'; import listen from 'test-listen'; import { createApolloFetch } from 'apollo-fetch'; -import { atLeastMajorNodeVersion } from 'apollo-server-integration-testsuite'; +import { NODE_MAJOR_VERSION } from 'apollo-server-integration-testsuite'; import { gql } from 'apollo-server-core'; import FormData from 'form-data'; import fs from 'fs'; @@ -146,7 +146,10 @@ describe('apollo-server-micro', function() { }); }); - (atLeastMajorNodeVersion(10) ? describe.skip : describe)( + // NODE: Intentionally skip file upload tests on Node.js 10. + // Also skip Node.js 6, but only because `graphql-upload` + // doesn't support it. + (NODE_MAJOR_VERSION === 6 ? describe.skip : describe)( 'file uploads', function() { it('should handle file uploads', async function() { diff --git a/packages/apollo-server-micro/src/microApollo.ts b/packages/apollo-server-micro/src/microApollo.ts index a0aac672f53..2aad76747de 100644 --- a/packages/apollo-server-micro/src/microApollo.ts +++ b/packages/apollo-server-micro/src/microApollo.ts @@ -1,20 +1,18 @@ import { GraphQLOptions, - ServerOptionsFunction, runHttpQuery, convertNodeHttpToRequest, } from 'apollo-server-core'; import { send, json, RequestHandler } from 'micro'; import url from 'url'; -import { ServerResponse } from 'http'; +import { IncomingMessage, ServerResponse } from 'http'; import { MicroRequest } from './types'; // Allowed Micro Apollo Server options. - -export type MicroGraphQLOptionsFunction = ServerOptionsFunction< - [MicroRequest, ServerResponse] ->; +export interface MicroGraphQLOptionsFunction { + (req?: IncomingMessage): GraphQLOptions | Promise; +} // Utility function used to set multiple headers on a response object. function setHeaders(res: ServerResponse, headers: Object): void { diff --git a/packages/apollo-server-plugin-base/package.json b/packages/apollo-server-plugin-base/package.json index 218c6e7157c..b352b834d01 100644 --- a/packages/apollo-server-plugin-base/package.json +++ b/packages/apollo-server-plugin-base/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server-plugin-base", - "version": "0.1.0-alpha.0", + "version": "0.2.3", "description": "Apollo Server plugin base classes", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apollo-server-plugin-base/src/index.ts b/packages/apollo-server-plugin-base/src/index.ts index 68c46536680..e09d4efc971 100644 --- a/packages/apollo-server-plugin-base/src/index.ts +++ b/packages/apollo-server-plugin-base/src/index.ts @@ -13,7 +13,7 @@ export { type ValueOrPromise = T | Promise; -export abstract class ApolloServerPlugin { +export interface ApolloServerPlugin { serverWillStart?(service: GraphQLServiceContext): ValueOrPromise; requestDidStart?( requestContext: GraphQLRequestContext, @@ -21,15 +21,14 @@ export abstract class ApolloServerPlugin { } export type WithRequired = T & Required>; -export type DidEndHook = (...args: TArgs) => void; export interface GraphQLRequestListener> { parsingDidStart?( requestContext: GraphQLRequestContext, - ): DidEndHook<[Error?]> | void; + ): (err?: Error) => void | void; validationDidStart?( requestContext: WithRequired, 'document'>, - ): DidEndHook<[ReadonlyArray?]> | void; + ): (err?: ReadonlyArray) => void | void; didResolveOperation?( requestContext: WithRequired< GraphQLRequestContext, @@ -41,7 +40,7 @@ export interface GraphQLRequestListener> { GraphQLRequestContext, 'document' | 'operationName' | 'operation' >, - ): DidEndHook<[Error?]> | void; + ): (err?: Error) => void | void; willSendResponse?( requestContext: WithRequired, 'response'>, ): ValueOrPromise; diff --git a/packages/apollo-server-testing/CHANGELOG.md b/packages/apollo-server-testing/CHANGELOG.md new file mode 100644 index 00000000000..a64c146b38f --- /dev/null +++ b/packages/apollo-server-testing/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +### vNEXT + +* `apollo-server-testing`: Added createTestClient function diff --git a/packages/apollo-server-testing/README.md b/packages/apollo-server-testing/README.md new file mode 100644 index 00000000000..a9ecec5d2b8 --- /dev/null +++ b/packages/apollo-server-testing/README.md @@ -0,0 +1,7 @@ +# apollo-server-testing + +[![npm version](https://badge.fury.io/js/apollo-server-testing.svg)](https://badge.fury.io/js/apollo-server-testing) +[![Build Status](https://circleci.com/gh/apollographql/apollo-server.svg?style=svg)](https://circleci.com/gh/apollographql/apollo-server) + +This is the testing module of the Apollo community GraphQL Server. [Read the docs.](https://www.apollographql.com/docs/apollo-server/) +[Read the CHANGELOG.](https://github.com/apollographql/apollo-server/blob/master/CHANGELOG.md) diff --git a/packages/apollo-server-testing/jest.config.js b/packages/apollo-server-testing/jest.config.js new file mode 100644 index 00000000000..a383fbc925f --- /dev/null +++ b/packages/apollo-server-testing/jest.config.js @@ -0,0 +1,3 @@ +const config = require('../../jest.config.base'); + +module.exports = Object.assign(Object.create(null), config); diff --git a/packages/apollo-server-testing/package.json b/packages/apollo-server-testing/package.json new file mode 100644 index 00000000000..c1cc86e0f02 --- /dev/null +++ b/packages/apollo-server-testing/package.json @@ -0,0 +1,32 @@ +{ + "name": "apollo-server-testing", + "version": "2.3.3", + "description": "Test utils for apollo-server", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-testing" + }, + "keywords": [ + "GraphQL", + "Apollo", + "Server", + "Javascript" + ], + "author": "Jonas Helfer ", + "license": "MIT", + "bugs": { + "url": "https://github.com/apollographql/apollo-server/issues" + }, + "homepage": "https://github.com/apollographql/apollo-server#readme", + "engines": { + "node": ">=6" + }, + "dependencies": { + "apollo-server-core": "file:../apollo-server-core" + }, + "peerDependencies": { + "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0" + } +} diff --git a/packages/apollo-server-testing/src/__tests__/createTestClient.test.ts b/packages/apollo-server-testing/src/__tests__/createTestClient.test.ts new file mode 100644 index 00000000000..e3fd1c7e06d --- /dev/null +++ b/packages/apollo-server-testing/src/__tests__/createTestClient.test.ts @@ -0,0 +1,81 @@ +import { ApolloServerBase, gql } from 'apollo-server-core'; +import createTestClient from '../createTestClient'; + +describe('createTestClient', () => { + const typeDefs = gql` + type Query { + test(echo: String): String + # this resolver uses context + hello: String + } + + type Mutation { + increment: Int! + } + `; + + const resolvers = { + Query: { + test: (_, { echo }) => echo, + hello: (_, __, { person }) => { + return `hello ${person}`; + }, + }, + Mutation: { + increment: () => 1, + }, + }; + + const myTestServer = new ApolloServerBase({ + typeDefs, + context: () => ({ person: 'tom' }), + resolvers, + }); + + it('allows queries', async () => { + const query = `{ test(echo: "foo") }`; + const client = createTestClient(myTestServer); + const res = await client.query({ query }); + expect(res.data).toEqual({ test: 'foo' }); + }); + + it('allows mutations', async () => { + const mutation = `mutation increment { increment }`; + const client = createTestClient(myTestServer); + const res = await client.mutate({ mutation }); + expect(res.data).toEqual({ increment: 1 }); + }); + + it('allows variables to be passed', async () => { + const query = `query test($echo: String){ test(echo: $echo) }`; + const client = createTestClient(myTestServer); + const res = await client.query({ query, variables: { echo: 'wow' } }); + expect(res.data).toEqual({ test: 'wow' }); + }); + + it('resolves with context', async () => { + const query = `{ hello }`; + const client = createTestClient(myTestServer); + const res = await client.query({ query }); + expect(res.data).toEqual({ hello: 'hello tom' }); + }); + + it('allows query documents as input', async () => { + const query = gql` + { + test(echo: "foo") + } + `; + const client = createTestClient(myTestServer); + const clientRes = await client.query({ query }); + expect(clientRes.data).toEqual({ test: 'foo' }); + + const mutation = gql` + mutation increment { + increment + } + `; + const mutationRes = await client.mutate({ mutation }); + expect(mutationRes.data).toEqual({ increment: 1 }); + }); +}); diff --git a/packages/apollo-server-testing/src/__tests__/tsconfig.json b/packages/apollo-server-testing/src/__tests__/tsconfig.json new file mode 100644 index 00000000000..2f3a365cbfc --- /dev/null +++ b/packages/apollo-server-testing/src/__tests__/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../../../tsconfig.test.base", + "include": ["**/*"], + "references": [] +} diff --git a/packages/apollo-server-testing/src/createTestClient.ts b/packages/apollo-server-testing/src/createTestClient.ts new file mode 100644 index 00000000000..5c71a3d6669 --- /dev/null +++ b/packages/apollo-server-testing/src/createTestClient.ts @@ -0,0 +1,30 @@ +import { ApolloServerBase } from 'apollo-server-core'; +import { print, DocumentNode } from 'graphql'; + +type StringOrAst = string | DocumentNode; + +// A query must not come with a mutation (and vice versa). +type Query = { query: StringOrAst; mutation?: undefined }; +type Mutation = { mutation: StringOrAst; query?: undefined }; + +export default (server: ApolloServerBase) => { + const executeOperation = server.executeOperation.bind(server); + const test = ({ query, mutation, ...args }: Query | Mutation) => { + const operation = query || mutation; + + if ((!query && !mutation) || (query && mutation)) { + throw new Error( + 'Either `query` or `mutation` must be passed, but not both.', + ); + } + + return executeOperation({ + // Convert ASTs, which are produced by `graphql-tag` but not currently + // used by `executeOperation`, to a String using `graphql/language/print`. + query: typeof operation === 'string' ? operation : print(operation), + ...args, + }); + }; + + return { query: test, mutate: test }; +}; diff --git a/packages/apollo-server-testing/src/index.ts b/packages/apollo-server-testing/src/index.ts new file mode 100644 index 00000000000..acd8c5f2b2b --- /dev/null +++ b/packages/apollo-server-testing/src/index.ts @@ -0,0 +1 @@ +export { default as createTestClient } from './createTestClient'; diff --git a/packages/apollo-server-testing/tsconfig.json b/packages/apollo-server-testing/tsconfig.json new file mode 100644 index 00000000000..dcf69da0397 --- /dev/null +++ b/packages/apollo-server-testing/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["src/**/*"], + "exclude": ["**/__tests__", "**/__mocks__"], + "references": [ + { "path": "../apollo-server-core" } + ] +} diff --git a/packages/apollo-server/package.json b/packages/apollo-server/package.json index e47df61c3a3..26cb450fd21 100644 --- a/packages/apollo-server/package.json +++ b/packages/apollo-server/package.json @@ -1,6 +1,6 @@ { "name": "apollo-server", - "version": "2.2.0-alpha.0", + "version": "2.3.3", "description": "Production ready GraphQL Server", "author": "opensource@apollographql.com", "main": "dist/index.js", diff --git a/packages/apollo-server/src/exports.ts b/packages/apollo-server/src/exports.ts index ded825c35c2..7a625e2cbe4 100644 --- a/packages/apollo-server/src/exports.ts +++ b/packages/apollo-server/src/exports.ts @@ -7,6 +7,7 @@ export { GraphQLOptions, GraphQLExtension, Config, + GraphQLSchemaModule, // Errors ApolloError, toApolloError, diff --git a/packages/apollo-server/src/index.ts b/packages/apollo-server/src/index.ts index 3daae6d646b..e92623fb9ee 100644 --- a/packages/apollo-server/src/index.ts +++ b/packages/apollo-server/src/index.ts @@ -78,8 +78,6 @@ export class ApolloServer extends ApolloServerBase { // Listen takes the same arguments as http.Server.listen. public async listen(...opts: Array): Promise { - await this.willStart(); - // This class is the easy mode for people who don't create their own express // object, so we have to create it. const app = express(); diff --git a/packages/apollo-tracing/package.json b/packages/apollo-tracing/package.json index c2429089aa3..5c3827d9094 100644 --- a/packages/apollo-tracing/package.json +++ b/packages/apollo-tracing/package.json @@ -1,6 +1,6 @@ { "name": "apollo-tracing", - "version": "0.3.0-alpha.0", + "version": "0.4.1", "description": "Collect and expose trace data for GraphQL requests", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -15,6 +15,6 @@ "graphql-extensions": "file:../graphql-extensions" }, "peerDependencies": { - "graphql": "0.10.x - 14.0.x" + "graphql": "0.10.x - 14.1.x" } } diff --git a/packages/graphql-extensions/package.json b/packages/graphql-extensions/package.json index 0d918c73737..4d6d7d04904 100644 --- a/packages/graphql-extensions/package.json +++ b/packages/graphql-extensions/package.json @@ -1,6 +1,6 @@ { "name": "graphql-extensions", - "version": "0.3.0-alpha.0", + "version": "0.4.3", "description": "Add extensions to GraphQL servers", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,6 +13,9 @@ "engines": { "node": ">=6.0" }, + "dependencies": { + "@apollographql/apollo-tools": "^0.3.0" + }, "devDependencies": { "apollo-server-core": "file:../apollo-server-core", "apollo-server-env": "file:../apollo-server-env" diff --git a/packages/graphql-extensions/src/index.ts b/packages/graphql-extensions/src/index.ts index 2876fc5961f..467e4017d4e 100644 --- a/packages/graphql-extensions/src/index.ts +++ b/packages/graphql-extensions/src/index.ts @@ -8,14 +8,21 @@ import { GraphQLResolveInfo, ExecutionArgs, DocumentNode, + ResponsePath, + FieldNode, } from 'graphql'; import { Request } from 'apollo-server-env'; export { Request } from 'apollo-server-env'; -import { GraphQLResponse } from 'apollo-server-core/dist/requestPipelineAPI'; +import { + GraphQLResponse, + GraphQLRequestContext, +} from 'apollo-server-core/dist/requestPipelineAPI'; export { GraphQLResponse }; +import { GraphQLObjectResolver } from '@apollographql/apollo-tools'; + export type EndHandler = (...errors: Array) => void; // A StartHandlerInvoker is a function that, given a specific GraphQLExtension, // finds a specific StartHandler on that extension and calls it with appropriate @@ -34,6 +41,7 @@ export class GraphQLExtension { persistedQueryHit?: boolean; persistedQueryRegister?: boolean; context: TContext; + requestContext: GraphQLRequestContext; }): EndHandler | void; public parsingDidStart?(o: { queryString: string }): EndHandler | void; public validationDidStart?(): EndHandler | void; @@ -75,6 +83,7 @@ export class GraphQLExtensionStack { persistedQueryRegister?: boolean; context: TContext; extensions?: Record; + requestContext: GraphQLRequestContext; }): EndHandler { return this.handleDidStart( ext => ext.requestDidStart && ext.requestDidStart(o), @@ -194,6 +203,15 @@ function wrapField(field: GraphQLField): void { const fieldResolver = field.resolve; field.resolve = (source, args, context, info) => { + // This is a bit of a hack, but since `ResponsePath` is a linked list, + // a new object gets created every time a path segment is added. + // So we can use that to share our `whenObjectResolved` promise across + // all field resolvers for the same object. + const parentPath = info.path.prev as ResponsePath & { + __fields?: Record>; + __whenObjectResolved?: Promise; + }; + const extensionStack = context && context._extensionStack; const handler = (extensionStack && @@ -202,12 +220,53 @@ function wrapField(field: GraphQLField): void { /* do nothing */ }); - // If no resolver has been defined for a field, use the default field resolver - // (which matches the behavior of graphql-js when there is no explicit resolve function defined). + const resolveObject: GraphQLObjectResolver< + any, + any + > = (info.parentType as any).resolveObject; + + let whenObjectResolved: Promise | undefined; + + if (parentPath && resolveObject) { + if (!parentPath.__fields) { + parentPath.__fields = {}; + } + + parentPath.__fields[info.fieldName] = info.fieldNodes; + + whenObjectResolved = parentPath.__whenObjectResolved; + if (!whenObjectResolved) { + // Use `Promise.resolve().then()` to delay executing + // `resolveObject()` so we can collect all the fields first. + whenObjectResolved = Promise.resolve().then(() => { + return resolveObject(source, parentPath.__fields!, context, info); + }); + parentPath.__whenObjectResolved = whenObjectResolved; + } + } + try { - const result = (fieldResolver || + // If no resolver has been defined for a field, use either the configured + // field resolver or the default field resolver + // (which matches the behavior of graphql-js when there is no explicit + // resolve function defined). + // XXX: Can't this be pulled up to the top of `wrapField` and only + // assigned once? It seems `extensionStack.fieldResolver` isn't set + // anywhere? + const actualFieldResolver = + fieldResolver || (extensionStack && extensionStack.fieldResolver) || - defaultFieldResolver)(source, args, context, info); + defaultFieldResolver; + + let result: any; + if (whenObjectResolved) { + result = whenObjectResolved.then((resolvedObject: any) => { + return actualFieldResolver(resolvedObject, args, context, info); + }); + } else { + result = actualFieldResolver(source, args, context, info); + } + // Call the stack's handlers either immediately (if result is not a // Promise) or once the Promise is done. Then return that same // maybe-Promise value. diff --git a/renovate.json b/renovate.json index 3b7c0b69440..5efbfc16089 100644 --- a/renovate.json +++ b/renovate.json @@ -1,26 +1,19 @@ { - "extends": [":pinOnlyDevDependencies"], - "semanticCommits": true, - "timezone": "America/Los_Angeles", - "schedule": ["after 6pm and before 8am on every weekday"], - "rebaseStalePrs": true, - "prCreation": "not-pending", - "automerge": "minor", - "labels": ["tooling", "dependencies"], - "pathRules": [ + "extends": [ + "apollo-open-source" + ], + "packageRules": [ { - "paths": ["docs/package.json"], - "extends": ["apollo-docs"], + "paths": [ + "docs/package.json" + ], + "extends": [ + "apollo-docs" + ], "baseBranches": [ "master", "version-2" ] } - ], - "packageRules": [ - { - "packageNames": ["lint-staged"], - "allowedVersions": "<7" - } ] } diff --git a/tsconfig.build.json b/tsconfig.build.json index 09db1dd58d5..ebb88ce4ba9 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -9,10 +9,13 @@ { "path": "./packages/apollo-datasource" }, { "path": "./packages/apollo-datasource-rest" }, { "path": "./packages/apollo-engine-reporting" }, + { "path": "./packages/apollo-graphql" }, { "path": "./packages/apollo-server" }, + { "path": "./packages/apollo-server-azure-functions" }, { "path": "./packages/apollo-server-cache-memcached" }, { "path": "./packages/apollo-server-cache-redis" }, { "path": "./packages/apollo-server-caching" }, + { "path": "./packages/apollo-server-cloud-functions" }, { "path": "./packages/apollo-server-cloudflare" }, { "path": "./packages/apollo-server-core" }, { "path": "./packages/apollo-server-errors" }, @@ -22,6 +25,7 @@ { "path": "./packages/apollo-server-lambda" }, { "path": "./packages/apollo-server-micro" }, { "path": "./packages/apollo-server-plugin-base" }, + { "path": "./packages/apollo-server-testing" }, { "path": "./packages/apollo-tracing" }, { "path": "./packages/graphql-extensions" }, ] diff --git a/tsconfig.test.json b/tsconfig.test.json index c740e3dbf68..85f4028c15d 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -8,10 +8,13 @@ { "path": "./packages/apollo-cache-control/src/__tests__/" }, { "path": "./packages/apollo-datasource-rest/src/__tests__/" }, { "path": "./packages/apollo-engine-reporting/src/__tests__/" }, + { "path": "./packages/apollo-graphql/src/__tests__/" }, { "path": "./packages/apollo-server/src/__tests__/" }, + { "path": "./packages/apollo-server-azure-functions/src/__tests__/" }, { "path": "./packages/apollo-server-cache-memcached/src/__tests__/" }, { "path": "./packages/apollo-server-cache-redis/src/__tests__/" }, { "path": "./packages/apollo-server-caching/src/__tests__/" }, + { "path": "./packages/apollo-server-cloud-functions/src/__tests__/" }, { "path": "./packages/apollo-server-core/src/__tests__/" }, { "path": "./packages/apollo-server-express/src/__tests__/" }, { "path": "./packages/apollo-server-hapi/src/__tests__/" }, diff --git a/types/@apollographql/apollo-upload-server/index.d.ts b/types/@apollographql/apollo-upload-server/index.d.ts deleted file mode 100644 index 71bd0ea4514..00000000000 --- a/types/@apollographql/apollo-upload-server/index.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { GraphQLScalarType } from 'graphql'; - -export const GraphQLUpload: GraphQLScalarType; - -export interface ApolloUploadOptions { - /** - * Max allowed non-file multipart form field size in bytes; enough for your queries (default: 1 MB) - */ - maxFieldSize?: number; - /** - * Max allowed file size in bytes (default: Infinity) - */ - maxFileSize?: number; - /** - * Max allowed number of files (default: Infinity) - */ - maxFiles?: number; -} - -export type Request = any; - -export function processRequest( - request: Request, - options?: ApolloUploadOptions, -): Promise; diff --git a/types/fast-json-stable-stringify/index.d.ts b/types/fast-json-stable-stringify/index.d.ts new file mode 100644 index 00000000000..c94014460d9 --- /dev/null +++ b/types/fast-json-stable-stringify/index.d.ts @@ -0,0 +1 @@ +export default function stringify(obj: any): string;