From 4149a97d134f96be17a34b78e9e262cf35b49700 Mon Sep 17 00:00:00 2001 From: Giancarlo Anemone Date: Sun, 30 Jun 2019 10:05:02 -0400 Subject: [PATCH] Expose composed middleware via getMiddleware(). (#2435) This adds `getMiddleware` for Express, Koa and Hapi integrations, which are the most pressing locations to reveal the previously-internal, composed middleware which is currently applied via `applyMiddleware`. --- CHANGELOG.md | 1 + docs/source/api/apollo-server.md | 10 +- package-lock.json | 452 ++++++++---------- package.json | 2 + packages/apollo-server-express/package.json | 2 + .../apollo-server-express/src/ApolloServer.ts | 169 ++++--- .../src/expressApollo.ts | 4 +- packages/apollo-server-koa/README.md | 2 + .../apollo-server-koa/src/ApolloServer.ts | 36 +- 9 files changed, 341 insertions(+), 337 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30d0b7e7ad7..fa511fb17b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The version headers in this history reflect the versions of Apollo Server itself - `apollo-engine-reporting`: **BEHAVIOR CHANGE**: If the error returned from the `engine.rewriteError` hook has an `extensions` property, that property will be used instead of the original error's extensions. Document that changes to most other `GraphQLError` fields by `engine.rewriteError` are ignored. [PR #2932](https://github.com/apollographql/apollo-server/pull/2932) - `apollo-engine-reporting`: **BEHAVIOR CHANGE**: The `engine.maskErrorDetails` option, deprecated by `engine.rewriteError` in v2.5.0, now behaves a bit more like the new option: while all error messages will be redacted, they will still show up on the appropriate nodes in a trace. [PR #2932](https://github.com/apollographql/apollo-server/pull/2932) - `apollo-engine-reporting`: **BEHAVIOR CHANGE**: By default, send no GraphQL variable values to Apollo's servers instead of sending all variable values. Adding the new EngineReportingOption `sendVariableValues` to send some or all variable values, possibly after transforming them. This replaces the `privateVariables` option, which is now deprecated. [PR #2931](https://github.com/apollographql/apollo-server/pull/2931) +- `apollo-server-express`, `apollo-server-koa`, `apollo-server-hapi`: A new `getMiddleware` method, which accepts the same parameters as `applyMiddleware` with the exception of the `app`, has been added. This allows implementors to obtain the composed middleware and "`use`" it within an existing `app`. This was previously only possible by passing an `app` to `applyMiddleware` or reaching into Apollo Server internals, but `getMiddleware` should allow a more natural method and will hopefully resolve many issues raised around the previous pattern. [PR #2435](https://github.com/apollographql/apollo-server/pull/2435) > Note: In order to keep shipping all GraphQL variable values to Apollo Engine, pass in the option: > diff --git a/docs/source/api/apollo-server.md b/docs/source/api/apollo-server.md index a831458e7b6..8c3d721e5ac 100644 --- a/docs/source/api/apollo-server.md +++ b/docs/source/api/apollo-server.md @@ -147,7 +147,7 @@ In `apollo-server`, the listen call starts the subscription server and passes th * `subscriptionsPath`: <`String`> * `server`: <[`http.Server`](https://nodejs.org/api/http.html#http_class_http_server)> -## ApolloServer.applyMiddleware +## `ApolloServer.applyMiddleware` The `applyMiddleware` method is provided by the `apollo-server-{integration}` packages that use middleware, such as hapi and express. This function connects ApolloServer to a specific framework. @@ -193,6 +193,14 @@ app.use('*', jwtCheck, requireAuth, checkScope); server.applyMiddleware({ app, path: '/specialUrl' }); // app is from an existing express app. Mount Apollo middleware here. If no path is specified, it defaults to `/graphql`. ``` +## `ApolloServer.getMiddleware` + +Similar to the `applyMiddleware` method above, though rather than applying the composition of the various Apollo Server middlewares which comprise a full-featured Apollo Server deployment (e.g. middleware for HTTP body parsing, GraphQL Playground, uploads and subscriptions) the `getMiddleware` simply returns the middleware. + +The `getMiddleware` method takes the same arguments as `applyMiddleware` **except** `app` should not be passed. Instead, the result of `getMiddleware` must be added as a middleware directly to an existing application (e.g. with `app.use(...)`). + +For example, for `apollo-server-express`, this means that rather than passing `applyMiddleware` an `app` which was already initiated from calling `express()`, and `applyMiddleware` "using" (i.e. `app.use`), the implementor will instead call `app.use(...)` on the result of `getMiddleware` with the same arguments. + ## `gql` The `gql` is a [template literal tag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates). Template literals were introduced in recent versions of ECMAScript to provide embedded expressions (i.e. `` `A string with interpolated ${variables}` ``) and template literal tags exist to provide additional functionality for what would otherwise be a normal template literal. diff --git a/package-lock.json b/package-lock.json index d02a7d5b53e..ffb8c7a9a7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,23 +9,6 @@ "apollo-env": "^0.5.1", "apollo-graphql": "^0.3.3", "apollo-server-env": "file:packages/apollo-server-env" - }, - "dependencies": { - "apollo-env": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.5.1.tgz", - "integrity": "sha512-fndST2xojgSdH02k5hxk1cbqA9Ti8RX4YzzBoAB4oIe1Puhq7+YlhXGXfXB5Y4XN0al8dLg+5nAkyjNAR2qZTw==", - "requires": { - "core-js": "^3.0.1", - "node-fetch": "^2.2.0", - "sha.js": "^2.4.11" - } - }, - "core-js": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.3.tgz", - "integrity": "sha512-PWZ+ZfuaKf178BIAg+CRsljwjIMRV8MY00CbZczkR6Zk5LfkSkjGoaab3+bqRQWVITNZxQB7TFYz+CFcyuamvA==" - } } }, "@apollo/gateway": { @@ -40,23 +23,6 @@ "loglevel": "^1.6.1", "loglevel-debug": "^0.0.1", "pretty-format": "^24.7.0" - }, - "dependencies": { - "apollo-env": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.5.1.tgz", - "integrity": "sha512-fndST2xojgSdH02k5hxk1cbqA9Ti8RX4YzzBoAB4oIe1Puhq7+YlhXGXfXB5Y4XN0al8dLg+5nAkyjNAR2qZTw==", - "requires": { - "core-js": "^3.0.1", - "node-fetch": "^2.2.0", - "sha.js": "^2.4.11" - } - }, - "core-js": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.3.tgz", - "integrity": "sha512-PWZ+ZfuaKf178BIAg+CRsljwjIMRV8MY00CbZczkR6Zk5LfkSkjGoaab3+bqRQWVITNZxQB7TFYz+CFcyuamvA==" - } } }, "@apollographql/apollo-tools": { @@ -84,6 +50,11 @@ } } }, + "@apollographql/graphql-playground-html": { + "version": "1.6.20", + "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", + "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" + }, "@azure/functions": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@azure/functions/-/functions-1.0.3.tgz", @@ -2663,6 +2634,11 @@ "@types/express": "*" } }, + "@types/debug": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-0.0.31.tgz", + "integrity": "sha512-LS1MCPaQKqspg7FvexuhmDbWUhE2yIJ+4AgVIyObfc06/UKZ8REgxGNjZc82wPLWmbeOm7S+gSsLgo75TanG4A==" + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -2972,6 +2948,15 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/parseurl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/parseurl/-/parseurl-1.3.1.tgz", + "integrity": "sha1-48sRAhYOSO+ln0l8TsIt7k87Wyc=", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/pino": { "version": "4.16.1", "resolved": "https://registry.npmjs.org/@types/pino/-/pino-4.16.1.tgz", @@ -2993,6 +2978,15 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, + "@types/redis": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.12.tgz", + "integrity": "sha512-eT5cGYr08OnF6OlAHdc2hVOBAKBpfQQNQHsWEvUwRPFiXRd+vv+hOHSSIo4xB7M5vZOZdjMT2OUlXYqo3YlIGQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/request": { "version": "2.48.1", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz", @@ -3277,23 +3271,6 @@ "apollo-server-env": "file:packages/apollo-server-env", "async-retry": "^1.2.1", "graphql-extensions": "file:packages/graphql-extensions" - }, - "dependencies": { - "apollo-env": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.5.1.tgz", - "integrity": "sha512-fndST2xojgSdH02k5hxk1cbqA9Ti8RX4YzzBoAB4oIe1Puhq7+YlhXGXfXB5Y4XN0al8dLg+5nAkyjNAR2qZTw==", - "requires": { - "core-js": "^3.0.1", - "node-fetch": "^2.2.0", - "sha.js": "^2.4.11" - } - }, - "core-js": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.3.tgz", - "integrity": "sha512-PWZ+ZfuaKf178BIAg+CRsljwjIMRV8MY00CbZczkR6Zk5LfkSkjGoaab3+bqRQWVITNZxQB7TFYz+CFcyuamvA==" - } } }, "apollo-engine-reporting-protobuf": { @@ -3411,13 +3388,6 @@ "apollo-server-core": "file:packages/apollo-server-core", "apollo-server-env": "file:packages/apollo-server-env", "graphql-tools": "^4.0.0" - }, - "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.20", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", - "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" - } } }, "apollo-server-cache-memcached": { @@ -3435,38 +3405,6 @@ "apollo-server-env": "file:packages/apollo-server-env", "dataloader": "^1.4.0", "ioredis": "^4.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ioredis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.2.0.tgz", - "integrity": "sha512-PdxZGNJBfPiR2RI6DkqmiacL1+ML3gaqEiaC5QXWQt9eSTlGj+BwDCct0s8irn1ed8GyzAHTzcjvU9fmnl6D7A==", - "requires": { - "cluster-key-slot": "^1.0.6", - "debug": "^3.1.0", - "denque": "^1.1.0", - "flexbuffer": "0.0.6", - "lodash.defaults": "^4.2.0", - "lodash.flatten": "^4.4.0", - "redis-commands": "1.4.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^1.0.0" - } - }, - "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==" - } } }, "apollo-server-caching": { @@ -3482,13 +3420,6 @@ "apollo-server-core": "file:packages/apollo-server-core", "apollo-server-env": "file:packages/apollo-server-env", "graphql-tools": "^4.0.0" - }, - "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.20", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", - "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" - } } }, "apollo-server-cloudflare": { @@ -3521,35 +3452,6 @@ "sha.js": "^2.4.11", "subscriptions-transport-ws": "^0.9.11", "ws": "^6.0.0" - }, - "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.20", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", - "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" - }, - "subscriptions-transport-ws": { - "version": "0.9.16", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.16.tgz", - "integrity": "sha512-pQdoU7nC+EpStXnCfh/+ho0zE0Z+ma+i7xvj7bkXKb1dvYHSZxgRPaU6spRP+Bjzow67c/rRDoix5RT0uU9omw==", - "requires": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0" - }, - "dependencies": { - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "requires": { - "async-limiter": "~1.0.0" - } - } - } - } } }, "apollo-server-env": { @@ -3573,17 +3475,14 @@ "accepts": "^1.3.5", "apollo-server-core": "file:packages/apollo-server-core", "body-parser": "^1.18.3", + "compose-middleware": "^5.0.1", "cors": "^2.8.4", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", + "parseurl": "^1.3.2", "type-is": "^1.6.16" }, "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.20", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", - "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" - }, "@types/express": { "version": "4.17.0", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.0.tgz", @@ -3605,13 +3504,6 @@ "fastify-cors": "^0.2.0", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0" - }, - "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.20", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", - "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" - } } }, "apollo-server-hapi": { @@ -3623,13 +3515,6 @@ "boom": "^7.1.0", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0" - }, - "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.20", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", - "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" - } } }, "apollo-server-integration-testsuite": { @@ -3657,13 +3542,6 @@ "koa-bodyparser": "^3.0.0", "koa-router": "^7.4.0", "type-is": "^1.6.16" - }, - "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.20", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", - "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" - } } }, "apollo-server-lambda": { @@ -3673,13 +3551,6 @@ "apollo-server-core": "file:packages/apollo-server-core", "apollo-server-env": "file:packages/apollo-server-env", "graphql-tools": "^4.0.0" - }, - "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.20", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", - "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" - } } }, "apollo-server-micro": { @@ -3689,13 +3560,6 @@ "accept": "^3.0.2", "apollo-server-core": "file:packages/apollo-server-core", "micro": "^9.3.2" - }, - "dependencies": { - "@apollographql/graphql-playground-html": { - "version": "1.6.20", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz", - "integrity": "sha512-3LWZa80HcP70Pl+H4KhLDJ7S0px+9/c8GTXdl6SpunRecUaB27g/oOQnAjNHLHdbWuGE0uyqcuGiTfbKB3ilaQ==" - } } }, "apollo-server-plugin-base": { @@ -4813,6 +4677,36 @@ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", "dev": true }, + "compose-middleware": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compose-middleware/-/compose-middleware-5.0.1.tgz", + "integrity": "sha512-Rcv19QgPOtYHu8wDJsu4ehSfkqSXjQLwKRXhIy9TFiIijSZz330ORyLCeirb4sPuBBbDNC5lUvQLuM72vWjKSQ==", + "requires": { + "@types/debug": "0.0.31", + "array-flatten": "^2.1.2", + "debug": "^4.1.0" + }, + "dependencies": { + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -6312,11 +6206,6 @@ "integrity": "sha512-qFlJnOBWDfIaunF54/lBqNKmXOI0HqNhu+mHkLmbaBXlS71PUd9OjFOdyevHt/aHoHB1+eW7eKHgRKOG5aHSpw==", "dev": true }, - "flexbuffer": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz", - "integrity": "sha1-A5/fI/iCPkQMOPMnfm/vEXQhWzA=" - }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -6543,25 +6432,29 @@ "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, "optional": 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.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, "requires": { @@ -6571,13 +6464,15 @@ }, "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, "optional": 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, "optional": true, "requires": { @@ -6587,37 +6482,43 @@ }, "chownr": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "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, "optional": 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, "optional": 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, "optional": 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": { @@ -6626,25 +6527,29 @@ }, "deep-extend": { "version": "0.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "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": { @@ -6653,13 +6558,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": { @@ -6675,7 +6582,8 @@ }, "glob": { "version": "7.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, "requires": { @@ -6689,13 +6597,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.24", - "bundled": true, + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, "requires": { @@ -6704,7 +6614,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": { @@ -6713,7 +6624,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": { @@ -6723,19 +6635,22 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": 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, "optional": true, "requires": { @@ -6744,13 +6659,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, "optional": true, "requires": { @@ -6759,13 +6676,15 @@ }, "minimist": { "version": "0.0.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true }, "minipass": { "version": "2.3.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "optional": true, "requires": { @@ -6775,7 +6694,8 @@ }, "minizlib": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, "requires": { @@ -6784,7 +6704,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, "optional": true, "requires": { @@ -6793,13 +6714,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.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", + "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", "dev": true, "optional": true, "requires": { @@ -6810,7 +6733,8 @@ }, "node-pre-gyp": { "version": "0.10.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", + "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", "dev": true, "optional": true, "requires": { @@ -6828,7 +6752,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": { @@ -6838,13 +6763,15 @@ }, "npm-bundled": { "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", + "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.2.0.tgz", + "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==", "dev": true, "optional": true, "requires": { @@ -6854,7 +6781,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": { @@ -6866,19 +6794,22 @@ }, "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, "optional": 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, "optional": true, "requires": { @@ -6887,19 +6818,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": { @@ -6909,19 +6843,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.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, "requires": { @@ -6933,7 +6870,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 } @@ -6941,7 +6879,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": { @@ -6956,7 +6895,8 @@ }, "rimraf": { "version": "2.6.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, "requires": { @@ -6965,43 +6905,50 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": 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.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "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, "optional": true, "requires": { @@ -7012,7 +6959,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": { @@ -7021,7 +6969,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, "optional": true, "requires": { @@ -7030,13 +6979,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.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, "requires": { @@ -7051,13 +7002,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.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, "requires": { @@ -7066,13 +7019,15 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true, "optional": true } @@ -8380,7 +8335,6 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.10.0.tgz", "integrity": "sha512-bAdt/sKdOvUyKhjLJ8HKFmO6ZQ+OHHmfFgWn9X/ecsp1lJNnOtmh/Xl2+AdKwUdSkl/Rrw1CKOkR8+Kv8tRinQ==", - "dev": true, "requires": { "cluster-key-slot": "^1.0.6", "debug": "^3.1.0", @@ -8397,7 +8351,6 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, "requires": { "ms": "^2.1.1" } @@ -8405,20 +8358,17 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "redis-commands": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", - "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==", - "dev": true + "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" }, "standard-as-callback": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz", - "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==", - "dev": true + "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==" } } }, @@ -13371,11 +13321,6 @@ "strip-indent": "^2.0.0" } }, - "redis-commands": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz", - "integrity": "sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw==" - }, "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -14164,11 +14109,6 @@ "integrity": "sha512-0Eyrk6uXW6tg9PYkhi/V/J4zHp33aNyi2hOCmhFLqLTIhbgqWn5jlSzI+IU0VqrZq6+DbHcabQl/WP6P3BG0QA==", "dev": true }, - "standard-as-callback": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-1.0.1.tgz", - "integrity": "sha512-izxEITSyc7S+5oOiF/URiYaNkemPUxIndCNv66jJ548Y1TVxhBvioNMSPrZIQdaZDlhnguOdUzHA/7hJ3xFhuQ==" - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -14344,7 +14284,6 @@ "version": "0.9.16", "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.16.tgz", "integrity": "sha512-pQdoU7nC+EpStXnCfh/+ho0zE0Z+ma+i7xvj7bkXKb1dvYHSZxgRPaU6spRP+Bjzow67c/rRDoix5RT0uU9omw==", - "dev": true, "requires": { "backo2": "^1.0.2", "eventemitter3": "^3.1.0", @@ -14357,7 +14296,6 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, "requires": { "async-limiter": "~1.0.0" } diff --git a/package.json b/package.json index 2fb9eba92f1..e3eae52f288 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,8 @@ "@types/multer": "1.3.7", "@types/node": "8.10.49", "@types/node-fetch": "2.3.2", + "@types/parseurl": "1.3.1", + "@types/redis": "2.8.12", "@types/request": "2.48.1", "@types/request-promise": "4.1.44", "@types/test-listen": "1.1.0", diff --git a/packages/apollo-server-express/package.json b/packages/apollo-server-express/package.json index aff91c741e0..325f0828e9e 100644 --- a/packages/apollo-server-express/package.json +++ b/packages/apollo-server-express/package.json @@ -34,9 +34,11 @@ "accepts": "^1.3.5", "apollo-server-core": "file:../apollo-server-core", "body-parser": "^1.18.3", + "compose-middleware": "^5.0.1", "cors": "^2.8.4", "graphql-subscriptions": "^1.0.0", "graphql-tools": "^4.0.0", + "parseurl": "^1.3.2", "type-is": "^1.6.16" }, "devDependencies": { diff --git a/packages/apollo-server-express/src/ApolloServer.ts b/packages/apollo-server-express/src/ApolloServer.ts index 19374520d61..d207d4f1eff 100644 --- a/packages/apollo-server-express/src/ApolloServer.ts +++ b/packages/apollo-server-express/src/ApolloServer.ts @@ -17,12 +17,21 @@ import { } from 'apollo-server-core'; import accepts from 'accepts'; import typeis from 'type-is'; - +import { compose } from 'compose-middleware'; +import parseurl from 'parseurl'; import { graphqlExpress } from './expressApollo'; export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core'; -export interface ServerRegistration { +export interface GetMiddlewareOptions { + path?: string; + cors?: corsMiddleware.CorsOptions | boolean; + bodyParserConfig?: OptionsJson | boolean; + onHealthCheck?: (req: express.Request) => Promise; + disableHealthCheck?: boolean; +} + +export interface ServerRegistration extends GetMiddlewareOptions { // Note: You can also pass a connect.Server here. If we changed this field to // `express.Application | connect.Server`, it would be very hard to get the // app.use calls to typecheck even though they do work properly. Our @@ -30,11 +39,6 @@ export interface ServerRegistration { // we suspect the only connect users left writing GraphQL apps are Meteor // users). app: express.Application; - path?: string; - cors?: corsMiddleware.CorsOptions | boolean; - bodyParserConfig?: OptionsJson | boolean; - onHealthCheck?: (req: express.Request) => Promise; - disableHealthCheck?: boolean; } const fileUploadMiddleware = ( @@ -70,6 +74,28 @@ const fileUploadMiddleware = ( } }; +const middlewareFromPath = ( + path: string, + middleware: express.RequestHandler, +) => ( + req: express.Request, + res: express.Response, + next: express.NextFunction, +) => { + // While Express is quite capable of providing the `path`, `connect` doesn't + // provide `req.path` in the same way, even though it's available on the `req` + // as `req._parsedUrl`. That property is a cached representation of a + // previous parse done by the `parseurl` package, and by using that (popular) + // package here, we can still reap those cache benefits without directly + // accessing _parsedUrl ourselves, which could be risky. + const parsedUrl = parseurl(req); + if (parsedUrl && parsedUrl.pathname === path) { + return middleware(req, res, next); + } else { + return next(); + } +}; + export interface ExpressContext { req: express.Request; res: express.Response; @@ -102,17 +128,20 @@ export class ApolloServer extends ApolloServerBase { return true; } + public applyMiddleware({ app, ...rest }: ServerRegistration) { + app.use(this.getMiddleware(rest)); + } + // 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, + public getMiddleware({ path, cors, bodyParserConfig, disableHealthCheck, onHealthCheck, - }: ServerRegistration) { + }: GetMiddlewareOptions = {}) { if (!path) path = '/graphql'; // Despite the fact that this `applyMiddleware` function is `async` in @@ -128,27 +157,34 @@ export class ApolloServer extends ApolloServerBase { // 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); - }); + + const middleware: express.RequestHandler[] = []; + + middleware.push( + middlewareFromPath(path, (_req, _res, next) => { + promiseWillStart.then(() => next()).catch(next); + }), + ); if (!disableHealthCheck) { - 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'); - - if (onHealthCheck) { - onHealthCheck(req) - .then(() => { - res.json({ status: 'pass' }); - }) - .catch(() => { - res.status(503).json({ status: 'fail' }); - }); - } else { - res.json({ status: 'pass' }); - } - }); + middleware.push( + middlewareFromPath('/.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'); + + if (onHealthCheck) { + onHealthCheck(req) + .then(() => { + res.json({ status: 'pass' }); + }) + .catch(() => { + res.status(503).json({ status: 'fail' }); + }); + } else { + res.json({ status: 'pass' }); + } + }), + ); } let uploadsMiddleware; @@ -162,54 +198,63 @@ export class ApolloServer extends ApolloServerBase { // Note that we don't just pass all of these handlers to a single app.use call // for 'connect' compatibility. if (cors === true) { - app.use(path, corsMiddleware()); + middleware.push(middlewareFromPath(path, corsMiddleware())); } else if (cors !== false) { - app.use(path, corsMiddleware(cors)); + middleware.push(middlewareFromPath(path, corsMiddleware(cors))); } if (bodyParserConfig === true) { - app.use(path, json()); + middleware.push(middlewareFromPath(path, json())); } else if (bodyParserConfig !== false) { - app.use(path, json(bodyParserConfig)); + middleware.push(middlewareFromPath(path, json(bodyParserConfig))); } if (uploadsMiddleware) { - app.use(path, uploadsMiddleware); + middleware.push(middlewareFromPath(path, uploadsMiddleware)); } // Note: if you enable playground in production and expect to be able to see your // schema, you'll need to manually specify `introspection: true` in the // ApolloServer constructor; by default, the introspection query is only // enabled in dev. - app.use(path, (req, res, next) => { - if (this.playgroundOptions && req.method === 'GET') { - // perform more expensive content-type check only if necessary - // XXX We could potentially move this logic into the GuiOptions lambda, - // but I don't think it needs any overriding - const accept = accepts(req); - const types = accept.types() as string[]; - const prefersHTML = - types.find( - (x: string) => x === 'text/html' || x === 'application/json', - ) === 'text/html'; - - if (prefersHTML) { - const playgroundRenderPageOptions: PlaygroundRenderPageOptions = { - endpoint: req.originalUrl, - subscriptionEndpoint: this.subscriptionsPath, - ...this.playgroundOptions, - }; - res.setHeader('Content-Type', 'text/html'); - const playground = renderPlaygroundPage(playgroundRenderPageOptions); - res.write(playground); - res.end(); - return; + middleware.push( + middlewareFromPath(path, (req, res, next) => { + if (this.playgroundOptions && req.method === 'GET') { + // perform more expensive content-type check only if necessary + // XXX We could potentially move this logic into the GuiOptions lambda, + // but I don't think it needs any overriding + const accept = accepts(req); + const types = accept.types() as string[]; + const prefersHTML = + types.find( + (x: string) => x === 'text/html' || x === 'application/json', + ) === 'text/html'; + + if (prefersHTML) { + const playgroundRenderPageOptions: PlaygroundRenderPageOptions = { + endpoint: req.originalUrl, + subscriptionEndpoint: this.subscriptionsPath, + ...this.playgroundOptions, + }; + res.setHeader('Content-Type', 'text/html'); + const playground = renderPlaygroundPage( + playgroundRenderPageOptions, + ); + res.write(playground); + res.end(); + return; + } } - } - return graphqlExpress(() => { - return this.createGraphQLServerOptions(req, res); - })(req, res, next); - }); + + return graphqlExpress(() => this.createGraphQLServerOptions(req, res))( + req, + res, + next, + ); + }), + ); + + return compose(middleware); } } diff --git a/packages/apollo-server-express/src/expressApollo.ts b/packages/apollo-server-express/src/expressApollo.ts index 4f22a8a9f21..b592175d13d 100644 --- a/packages/apollo-server-express/src/expressApollo.ts +++ b/packages/apollo-server-express/src/expressApollo.ts @@ -8,9 +8,7 @@ import { import { ValueOrPromise } from 'apollo-server-env'; export interface ExpressGraphQLOptionsFunction { - (req?: express.Request, res?: express.Response): ValueOrPromise< - GraphQLOptions - >; + (req: express.Request, res: express.Response): ValueOrPromise; } // Design principles: diff --git a/packages/apollo-server-koa/README.md b/packages/apollo-server-koa/README.md index 0aa826cdaa4..1ba937a892c 100644 --- a/packages/apollo-server-koa/README.md +++ b/packages/apollo-server-koa/README.md @@ -36,6 +36,8 @@ const server = new ApolloServer({ typeDefs, resolvers }); const app = new Koa(); server.applyMiddleware({ app }); +// alternatively you can get a composed middleware from the apollo server +// app.use(server.getMiddleware()); app.listen({ port: 4000 }, () => console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`), diff --git a/packages/apollo-server-koa/src/ApolloServer.ts b/packages/apollo-server-koa/src/ApolloServer.ts index d0d56e55f73..71f45b7e859 100644 --- a/packages/apollo-server-koa/src/ApolloServer.ts +++ b/packages/apollo-server-koa/src/ApolloServer.ts @@ -1,4 +1,4 @@ -import Koa from 'koa'; +import Koa, { Middleware } from 'koa'; import corsMiddleware from '@koa/cors'; import bodyParser from 'koa-bodyparser'; import compose from 'koa-compose'; @@ -19,8 +19,7 @@ import { graphqlKoa } from './koaApollo'; export { GraphQLOptions, GraphQLExtension } from 'apollo-server-core'; import { GraphQLOptions, FileUploadOptions } from 'apollo-server-core'; -export interface ServerRegistration { - app: Koa; +export interface GetMiddlewareOptions { path?: string; cors?: corsMiddleware.Options | boolean; bodyParserConfig?: bodyParser.Options | boolean; @@ -28,6 +27,10 @@ export interface ServerRegistration { disableHealthCheck?: boolean; } +export interface ServerRegistration extends GetMiddlewareOptions { + app: Koa; +} + const fileUploadMiddleware = ( uploadsConfig: FileUploadOptions, server: ApolloServerBase, @@ -80,18 +83,21 @@ export class ApolloServer extends ApolloServerBase { return true; } + public applyMiddleware({ app, ...rest }: ServerRegistration) { + app.use(this.getMiddleware(rest)); + } + // 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, + public getMiddleware({ path, cors, bodyParserConfig, disableHealthCheck, onHealthCheck, - }: ServerRegistration) { + }: GetMiddlewareOptions = {}): Middleware { if (!path) path = '/graphql'; // Despite the fact that this `applyMiddleware` function is `async` in @@ -107,7 +113,8 @@ export class ApolloServer extends ApolloServerBase { // work has finished. Any errors will be surfaced to Koa through its own // native Promise-catching facilities. const promiseWillStart = this.willStart(); - app.use( + const middlewares = []; + middlewares.push( middlewareFromPath(path, async (_ctx: Koa.Context, next: Function) => { await promiseWillStart; return next(); @@ -115,7 +122,7 @@ export class ApolloServer extends ApolloServerBase { ); if (!disableHealthCheck) { - app.use( + middlewares.push( middlewareFromPath( '/.well-known/apollo/server-health', (ctx: Koa.Context) => { @@ -147,22 +154,22 @@ export class ApolloServer extends ApolloServerBase { this.graphqlPath = path; if (cors === true) { - app.use(middlewareFromPath(path, corsMiddleware())); + middlewares.push(middlewareFromPath(path, corsMiddleware())); } else if (cors !== false) { - app.use(middlewareFromPath(path, corsMiddleware(cors))); + middlewares.push(middlewareFromPath(path, corsMiddleware(cors))); } if (bodyParserConfig === true) { - app.use(middlewareFromPath(path, bodyParser())); + middlewares.push(middlewareFromPath(path, bodyParser())); } else if (bodyParserConfig !== false) { - app.use(middlewareFromPath(path, bodyParser(bodyParserConfig))); + middlewares.push(middlewareFromPath(path, bodyParser(bodyParserConfig))); } if (uploadsMiddleware) { - app.use(middlewareFromPath(path, uploadsMiddleware)); + middlewares.push(middlewareFromPath(path, uploadsMiddleware)); } - app.use( + middlewares.push( middlewareFromPath(path, (ctx: Koa.Context, next: Function) => { if (ctx.request.method === 'OPTIONS') { ctx.status = 204; @@ -199,6 +206,7 @@ export class ApolloServer extends ApolloServerBase { })(ctx, next); }), ); + return compose(middlewares); } }