├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── Demo.ts ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── FastifyGraphQL.ts ├── GraphQLPlugin.ts ├── GraphiQLPlugin.ts └── Typings.d.ts ├── test ├── FastifyGraphql.test.ts └── apollo-server-integration-testsuite.ts ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | .vscode 61 | jsconfig.json 62 | 63 | out -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | src 3 | test -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "6" 5 | 6 | cache: 7 | yarn: true 8 | 9 | deploy: 10 | provider: npm 11 | email: sirsavary@outlook.com 12 | api_key: $NPM_TOKEN 13 | skip_cleanup: true 14 | on: 15 | tags: true -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | 6 | # [4.0.0](https://github.com/sirsavary/fastify-graphql/compare/v3.1.11...v4.0.0) (2018-03-05) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **travis:** don't test releases that are unsupported by fastify ([e1432c2](https://github.com/sirsavary/fastify-graphql/commit/e1432c2)) 12 | 13 | 14 | ### BREAKING CHANGES 15 | 16 | * **travis:** users running node v4 or v5 will no longer be able to use this module (or fastify) 17 | 18 | 19 | 20 | 21 | ## [3.1.11](https://github.com/sirsavary/fastify-graphql/compare/v3.1.10...v3.1.11) (2018-03-05) 22 | 23 | 24 | ### Bug Fixes 25 | 26 | * test command not running anything ([e1b680e](https://github.com/sirsavary/fastify-graphql/commit/e1b680e)) 27 | 28 | 29 | 30 | 31 | ## [3.1.10](https://github.com/sirsavary/fastify-graphql/compare/v3.1.9...v3.1.10) (2018-03-03) 32 | 33 | 34 | ### Bug Fixes 35 | 36 | * **readme:** Update links to apollo-server documentation ([7061f6d](https://github.com/sirsavary/fastify-graphql/commit/7061f6d)) 37 | 38 | 39 | 40 | 41 | ## [3.1.9](https://github.com/sirsavary/fastify-graphql/compare/v3.1.8...v3.1.9) (2018-02-16) 42 | 43 | 44 | 45 | 46 | ## [3.1.8](https://github.com/sirsavary/fastify-graphql/compare/v3.1.7...v3.1.8) (2018-02-16) 47 | 48 | 49 | 50 | 51 | ## [3.1.7](https://github.com/sirsavary/fastify-graphql/compare/v3.1.6...v3.1.7) (2018-01-31) 52 | 53 | 54 | ### Bug Fixes 55 | 56 | * fixed Travis not deploying compiled Typescript files ([3be965e](https://github.com/sirsavary/fastify-graphql/commit/3be965e)) 57 | 58 | 59 | 60 | 61 | ## [3.1.6](https://github.com/sirsavary/fastify-graphql/compare/v3.1.5...v3.1.6) (2018-01-31) 62 | 63 | 64 | 65 | 66 | ## [3.1.5](https://github.com/sirsavary/fastify-graphql/compare/v3.1.4...v3.1.5) (2018-01-31) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * Prepare runs before NPM deploy, no need for Travis to run it ([f20ec9b](https://github.com/sirsavary/fastify-graphql/commit/f20ec9b)) 72 | 73 | 74 | 75 | 76 | ## [3.1.4](https://github.com/sirsavary/fastify-graphql/compare/v3.1.3...v3.1.4) (2018-01-31) 77 | 78 | 79 | ### Bug Fixes 80 | 81 | * Travis no longer references the old build script ([1db22ec](https://github.com/sirsavary/fastify-graphql/commit/1db22ec)) 82 | 83 | 84 | 85 | 86 | ## [3.1.3](https://github.com/sirsavary/fastify-graphql/compare/v3.1.2...v3.1.3) (2018-01-31) 87 | 88 | 89 | ### Bug Fixes 90 | 91 | * prepare really is the proper name for a build script ([fefc9a9](https://github.com/sirsavary/fastify-graphql/commit/fefc9a9)) 92 | 93 | 94 | 95 | 96 | ## [3.1.2](https://github.com/sirsavary/fastify-graphql/compare/v3.1.1...v3.1.2) (2018-01-31) 97 | 98 | 99 | ### Bug Fixes 100 | 101 | * attempt to fix Travis not compiling our Typescript ([6295e68](https://github.com/sirsavary/fastify-graphql/commit/6295e68)) 102 | 103 | 104 | 105 | 106 | ## [3.1.1](https://github.com/sirsavary/fastify-graphql/compare/v3.1.0...v3.1.1) (2018-01-31) 107 | 108 | 109 | ### Bug Fixes 110 | 111 | * have Travis cache Yarn downloads ([5f2af8e](https://github.com/sirsavary/fastify-graphql/commit/5f2af8e)) 112 | 113 | 114 | 115 | 116 | # [3.1.0](https://github.com/sirsavary/fastify-graphql/compare/v0.1.1...v3.1.0) (2018-01-31) 117 | 118 | 119 | ### Features 120 | 121 | * improve CI, add automated deployments ([e751ca9](https://github.com/sirsavary/fastify-graphql/commit/e751ca9)) 122 | -------------------------------------------------------------------------------- /Demo.ts: -------------------------------------------------------------------------------- 1 | import {createFastifyApp, createGQLSchema} from "./test/TestUtils"; 2 | 3 | createFastifyApp(createGQLSchema()).listen(3000, (err) => { 4 | if (err) console.error(err); 5 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nickolena Coop 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastify-graphql 2 | [![Travis](https://img.shields.io/travis/sirsavary/fastify-graphql.svg)](https://travis-ci.org/sirsavary/fastify-graphql) 3 | [![npm](https://img.shields.io/npm/v/fastify-graphql.svg)](https://www.npmjs.com/package/fastify-graphql) 4 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) 5 | 6 | A plugin for [Fastify](https://github.com/fastify/fastify) that adds GraphQL and GraphiQL support. 7 | 8 | This project was forked from [fastify-apollo](https://github.com/coopnd/fastify-apollo) as it is no longer being maintained fast enough to keep pace with the rapid changes happening in the GraphQL ecosystem. 9 | 10 | ## Installation 11 | ```bash 12 | npm install --save fastify-graphql graphql 13 | ``` 14 | 15 | or 16 | 17 | ```bash 18 | yarn add fastify-graphql graphql 19 | ``` 20 | 21 | ## Usage 22 | ```js 23 | const Fastify = require('fastify'); 24 | const app = Fastify(); 25 | 26 | const {graphiqlFastify, graphqlFastify} = require('fastify-graphql'); 27 | app.register(graphqlFastify, { 28 | prefix: '/graphql', 29 | graphql: { 30 | schema: your_graphql_schema, 31 | }, 32 | }); 33 | app.register(graphiqlFastify, { 34 | prefix: '/graphiql', 35 | graphiql: { 36 | endpointURL: '/graphql', 37 | } 38 | }); 39 | ``` 40 | 41 | ## Configuration 42 | 43 | Both plugins need to be given a prefix, under which they will mount. 44 | 45 | GraphQL settings extends [GraphQLServerOptions](https://github.com/apollographql/apollo-server/blob/master/packages/apollo-server-core/src/graphqlOptions.ts#L9-L37) 46 | 47 | GraphiQL settings extends [GraphiQLData](https://github.com/apollographql/apollo-server/blob/master/packages/apollo-server-module-graphiql/src/renderGraphiQL.ts#L9-L33) 48 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-graphql", 3 | "version": "4.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/chai": { 8 | "version": "4.1.2", 9 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.2.tgz", 10 | "integrity": "sha512-D8uQwKYUw2KESkorZ27ykzXgvkDJYXVEihGklgfp5I4HUP8D6IxtcdLTMB1emjQiWzV7WZ5ihm1cxIzVwjoleQ==", 11 | "dev": true 12 | }, 13 | "@types/events": { 14 | "version": "1.2.0", 15 | "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", 16 | "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", 17 | "dev": true 18 | }, 19 | "@types/node": { 20 | "version": "10.3.1", 21 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.1.tgz", 22 | "integrity": "sha512-IsX9aDHDzJohkm3VCDB8tkzl5RQ34E/PFA29TQk6uDGb7Oc869ZBtmdKVDBzY3+h9GnXB8ssrRXEPVZrlIOPOw==", 23 | "dev": true 24 | }, 25 | "@types/pino": { 26 | "version": "4.16.0", 27 | "resolved": "https://registry.npmjs.org/@types/pino/-/pino-4.16.0.tgz", 28 | "integrity": "sha512-RP7YA3r9nhrLJHidgygV8IdNmkBIivZmYamKgUKjuUF8qfO+eENo8HImNbs8v/Ns4nqBcu7gGmBoIIn8Hbwilw==", 29 | "dev": true, 30 | "requires": { 31 | "@types/events": "*", 32 | "@types/node": "*" 33 | } 34 | }, 35 | "abstract-logging": { 36 | "version": "1.0.0", 37 | "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-1.0.0.tgz", 38 | "integrity": "sha1-i33q/TEFWbwo93ck3RuzAXcnjBs=", 39 | "dev": true 40 | }, 41 | "ajv": { 42 | "version": "6.5.0", 43 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", 44 | "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", 45 | "dev": true, 46 | "requires": { 47 | "fast-deep-equal": "^2.0.1", 48 | "fast-json-stable-stringify": "^2.0.0", 49 | "json-schema-traverse": "^0.3.0", 50 | "uri-js": "^4.2.1" 51 | } 52 | }, 53 | "ansi-styles": { 54 | "version": "3.2.1", 55 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 56 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 57 | "dev": true, 58 | "requires": { 59 | "color-convert": "^1.9.0" 60 | } 61 | }, 62 | "apollo-cache-control": { 63 | "version": "0.0.9", 64 | "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.0.9.tgz", 65 | "integrity": "sha512-bspKyM9gBDxv2nnKPSErzzZiSOdvRXnHwS/3gwBucZG1Dz5U4H6xyPtCx754/YfRto1yT9bUMc7vW85jJ/acOA==", 66 | "requires": { 67 | "graphql-extensions": "^0.0.x" 68 | } 69 | }, 70 | "apollo-server-core": { 71 | "version": "1.3.2", 72 | "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-1.3.2.tgz", 73 | "integrity": "sha1-82hVo+vcLXe4ucRUOAvx1wYQX/w=", 74 | "requires": { 75 | "apollo-cache-control": "^0.0.x", 76 | "apollo-tracing": "^0.1.0", 77 | "graphql-extensions": "^0.0.x" 78 | } 79 | }, 80 | "apollo-server-module-graphiql": { 81 | "version": "1.3.2", 82 | "resolved": "https://registry.npmjs.org/apollo-server-module-graphiql/-/apollo-server-module-graphiql-1.3.2.tgz", 83 | "integrity": "sha1-Cp5MSN7OOvkE/uMz+V97mBczXKc=" 84 | }, 85 | "apollo-tracing": { 86 | "version": "0.1.3", 87 | "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.1.3.tgz", 88 | "integrity": "sha512-LZhSDL4oe9iNkedzJk6tQ6zLXmw/lwCvB0HDZyJjdp8rqrW+RN0Fk6MmZtNq9Z0olwOUh8EN3vIpzLwR3CJTrw==", 89 | "requires": { 90 | "graphql-extensions": "^0.0.x" 91 | } 92 | }, 93 | "assertion-error": { 94 | "version": "1.1.0", 95 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 96 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 97 | "dev": true 98 | }, 99 | "avvio": { 100 | "version": "5.4.3", 101 | "resolved": "https://registry.npmjs.org/avvio/-/avvio-5.4.3.tgz", 102 | "integrity": "sha512-rsBXDzL8WxRysiY+V17TJGOMYuufN/xsVkbCEoITyguA/O4pGJ4t2oW9JJAJcZOir3YodyPdMcwsj36z9RpLgw==", 103 | "dev": true, 104 | "requires": { 105 | "debug": "^3.1.0", 106 | "fastq": "^1.5.0" 107 | } 108 | }, 109 | "chai": { 110 | "version": "4.1.2", 111 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", 112 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", 113 | "dev": true, 114 | "requires": { 115 | "assertion-error": "^1.0.1", 116 | "check-error": "^1.0.1", 117 | "deep-eql": "^3.0.0", 118 | "get-func-name": "^2.0.0", 119 | "pathval": "^1.0.0", 120 | "type-detect": "^4.0.0" 121 | } 122 | }, 123 | "chai-graphql": { 124 | "version": "4.0.0", 125 | "resolved": "https://registry.npmjs.org/chai-graphql/-/chai-graphql-4.0.0.tgz", 126 | "integrity": "sha512-CvgtShjnUEsW3lVNiCSwjXOycYRurvyBPqfIQL9PRmqJF3KvmCJId13RQb0h0EZKwjGCws+Pgbduqn3lT33vlg==", 127 | "dev": true, 128 | "requires": { 129 | "check-error": "^1.0.2" 130 | } 131 | }, 132 | "chalk": { 133 | "version": "2.4.1", 134 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 135 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 136 | "dev": true, 137 | "requires": { 138 | "ansi-styles": "^3.2.1", 139 | "escape-string-regexp": "^1.0.5", 140 | "supports-color": "^5.3.0" 141 | } 142 | }, 143 | "check-error": { 144 | "version": "1.0.2", 145 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 146 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 147 | "dev": true 148 | }, 149 | "color-convert": { 150 | "version": "1.9.1", 151 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", 152 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", 153 | "dev": true, 154 | "requires": { 155 | "color-name": "^1.1.1" 156 | } 157 | }, 158 | "color-name": { 159 | "version": "1.1.3", 160 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 161 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 162 | "dev": true 163 | }, 164 | "core-js": { 165 | "version": "2.5.3", 166 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", 167 | "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=" 168 | }, 169 | "core-util-is": { 170 | "version": "1.0.2", 171 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 172 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 173 | "dev": true 174 | }, 175 | "debug": { 176 | "version": "3.1.0", 177 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 178 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 179 | "dev": true, 180 | "requires": { 181 | "ms": "2.0.0" 182 | } 183 | }, 184 | "deep-eql": { 185 | "version": "3.0.1", 186 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 187 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 188 | "dev": true, 189 | "requires": { 190 | "type-detect": "^4.0.0" 191 | } 192 | }, 193 | "deepmerge": { 194 | "version": "2.1.1", 195 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.1.tgz", 196 | "integrity": "sha512-urQxA1smbLZ2cBbXbaYObM1dJ82aJ2H57A1C/Kklfh/ZN1bgH4G/n5KWhdNfOK11W98gqZfyYj7W4frJJRwA2w==", 197 | "dev": true 198 | }, 199 | "end-of-stream": { 200 | "version": "1.4.1", 201 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 202 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 203 | "dev": true, 204 | "requires": { 205 | "once": "^1.4.0" 206 | } 207 | }, 208 | "escape-string-regexp": { 209 | "version": "1.0.5", 210 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 211 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 212 | "dev": true 213 | }, 214 | "fast-decode-uri-component": { 215 | "version": "1.0.0", 216 | "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.0.tgz", 217 | "integrity": "sha512-WQSYVKn6tDW/3htASeUkrx5LcnuTENQIZQPCVlwdnvIJ7bYtSpoJYq38MgUJnx1CQIR1gjZ8HJxAEcN4gqugBg==", 218 | "dev": true 219 | }, 220 | "fast-deep-equal": { 221 | "version": "2.0.1", 222 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 223 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", 224 | "dev": true 225 | }, 226 | "fast-json-parse": { 227 | "version": "1.0.3", 228 | "resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz", 229 | "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==", 230 | "dev": true 231 | }, 232 | "fast-json-stable-stringify": { 233 | "version": "2.0.0", 234 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 235 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", 236 | "dev": true 237 | }, 238 | "fast-json-stringify": { 239 | "version": "1.5.4", 240 | "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-1.5.4.tgz", 241 | "integrity": "sha512-rasizCB0oTM1OORpgt37bEuRHI3i5UYkG9BHhhopPEb9ZJ9UsU5v8uFnA4Dpl6xXfBP+uFFyIwHbJDUfJU5eiw==", 242 | "dev": true, 243 | "requires": { 244 | "ajv": "^6.5.0", 245 | "deepmerge": "^2.1.0" 246 | } 247 | }, 248 | "fast-safe-stringify": { 249 | "version": "1.2.3", 250 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-1.2.3.tgz", 251 | "integrity": "sha512-QJYT/i0QYoiZBQ71ivxdyTqkwKkQ0oxACXHYxH2zYHJEgzi2LsbjgvtzTbLi1SZcF190Db2YP7I7eTsU2egOlw==", 252 | "dev": true 253 | }, 254 | "fastify": { 255 | "version": "1.5.0", 256 | "resolved": "https://registry.npmjs.org/fastify/-/fastify-1.5.0.tgz", 257 | "integrity": "sha512-X/foUNk5FF1dfnFxwjSzZY/ZDyaWM6QM78T+YFTErkfJ2vZ56DHmHgPiMwa6I8MNvC1/LiOHKzxX8WoIBm+AcQ==", 258 | "dev": true, 259 | "requires": { 260 | "@types/pino": "^4.7.1", 261 | "abstract-logging": "^1.0.0", 262 | "ajv": "^6.4.0", 263 | "avvio": "^5.4.3", 264 | "end-of-stream": "^1.4.1", 265 | "fast-json-stringify": "^1.5.2", 266 | "find-my-way": "^1.12.0", 267 | "flatstr": "^1.0.5", 268 | "light-my-request": "^2.0.2", 269 | "middie": "^3.1.0", 270 | "pino": "^4.16.1", 271 | "tiny-lru": "^1.5.2" 272 | } 273 | }, 274 | "fastify-plugin": { 275 | "version": "0.2.2", 276 | "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-0.2.2.tgz", 277 | "integrity": "sha512-oRJdjdudgCkQQUARNeh2rkbxFAmj2OhCJSVBNBLUbhS0orF+IMQ4u/bc661N1jh/wDI2J+YKmXmmHSVFQI4e7A==", 278 | "requires": { 279 | "semver": "^5.4.1" 280 | } 281 | }, 282 | "fastq": { 283 | "version": "1.6.0", 284 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", 285 | "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==", 286 | "dev": true, 287 | "requires": { 288 | "reusify": "^1.0.0" 289 | } 290 | }, 291 | "find-my-way": { 292 | "version": "1.13.0", 293 | "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-1.13.0.tgz", 294 | "integrity": "sha512-aIa4UTxZ3klfApaQEJJ5cQvNeqfrxVngcjMgy+G5ygkrOrDPkORhY/RMH6F8mLwBpPt3Z0F03CtzN7gs2Q5H1w==", 295 | "dev": true, 296 | "requires": { 297 | "fast-decode-uri-component": "^1.0.0", 298 | "safe-regex": "^1.1.0" 299 | } 300 | }, 301 | "flatstr": { 302 | "version": "1.0.8", 303 | "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.8.tgz", 304 | "integrity": "sha512-YXblbv/vc1zuVVUtnKl1hPqqk7TalZCppnKE7Pr8FI/Rp48vzckS/4SJ4Y9O9RNiI82Vcw/FydmtqdQOg1Dpqw==", 305 | "dev": true 306 | }, 307 | "get-func-name": { 308 | "version": "2.0.0", 309 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 310 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 311 | "dev": true 312 | }, 313 | "graphql": { 314 | "version": "0.12.3", 315 | "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.12.3.tgz", 316 | "integrity": "sha512-Hn9rdu4zacplKXNrLCvR8YFiTGnbM4Zw/UH8FDmzBDsH7ou40lSNH4tIlsxcYnz2TGNVJCpu1WxCM23yd6kzhA==", 317 | "dev": true, 318 | "requires": { 319 | "iterall": "1.1.3" 320 | } 321 | }, 322 | "graphql-extensions": { 323 | "version": "0.0.7", 324 | "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.0.7.tgz", 325 | "integrity": "sha512-OQQeBySD16f+1JWx6RJ3u7p3zQd0eg/tEvSxwiB7B+nc68SJk77Cd1GtkOusJ52MDtCmD5ybwcoLjU9mNCw6nA==", 326 | "requires": { 327 | "core-js": "^2.5.3", 328 | "source-map-support": "^0.5.1" 329 | } 330 | }, 331 | "has-flag": { 332 | "version": "3.0.0", 333 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 334 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 335 | "dev": true 336 | }, 337 | "inherits": { 338 | "version": "2.0.3", 339 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 340 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 341 | "dev": true 342 | }, 343 | "isarray": { 344 | "version": "1.0.0", 345 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 346 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 347 | "dev": true 348 | }, 349 | "iterall": { 350 | "version": "1.1.3", 351 | "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", 352 | "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", 353 | "dev": true 354 | }, 355 | "json-schema-traverse": { 356 | "version": "0.3.1", 357 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 358 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", 359 | "dev": true 360 | }, 361 | "light-my-request": { 362 | "version": "2.0.2", 363 | "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-2.0.2.tgz", 364 | "integrity": "sha512-e7863fgFyGZ2dbz0ag9AdkFjrQcnEdDFu4lwMeIbDWAW9PcL+zS75WPCkbWstM2jU6WR7/E8ZMjxUVJtCQtrLw==", 365 | "dev": true, 366 | "requires": { 367 | "ajv": "^6.0.0", 368 | "readable-stream": "^2.3.3", 369 | "safe-buffer": "^5.1.1" 370 | } 371 | }, 372 | "middie": { 373 | "version": "3.1.0", 374 | "resolved": "https://registry.npmjs.org/middie/-/middie-3.1.0.tgz", 375 | "integrity": "sha512-673tjCpr9xbI30cVqNbCvBe1hNS4pNs7Fi8Yp9wPiqX3qTpuRm87uD3aRtH9ji7Gt8SavbX7cwYMqY2muIPMJw==", 376 | "dev": true, 377 | "requires": { 378 | "path-to-regexp": "^2.0.0", 379 | "reusify": "^1.0.2" 380 | } 381 | }, 382 | "ms": { 383 | "version": "2.0.0", 384 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 385 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 386 | "dev": true 387 | }, 388 | "nyc": { 389 | "version": "11.4.1", 390 | "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.4.1.tgz", 391 | "integrity": "sha512-5eCZpvaksFVjP2rt1r60cfXmt3MUtsQDw8bAzNqNEr4WLvUMLgiVENMf/B9bE9YAX0mGVvaGA3v9IS9ekNqB1Q==", 392 | "dev": true, 393 | "requires": { 394 | "archy": "^1.0.0", 395 | "arrify": "^1.0.1", 396 | "caching-transform": "^1.0.0", 397 | "convert-source-map": "^1.3.0", 398 | "debug-log": "^1.0.1", 399 | "default-require-extensions": "^1.0.0", 400 | "find-cache-dir": "^0.1.1", 401 | "find-up": "^2.1.0", 402 | "foreground-child": "^1.5.3", 403 | "glob": "^7.0.6", 404 | "istanbul-lib-coverage": "^1.1.1", 405 | "istanbul-lib-hook": "^1.1.0", 406 | "istanbul-lib-instrument": "^1.9.1", 407 | "istanbul-lib-report": "^1.1.2", 408 | "istanbul-lib-source-maps": "^1.2.2", 409 | "istanbul-reports": "^1.1.3", 410 | "md5-hex": "^1.2.0", 411 | "merge-source-map": "^1.0.2", 412 | "micromatch": "^2.3.11", 413 | "mkdirp": "^0.5.0", 414 | "resolve-from": "^2.0.0", 415 | "rimraf": "^2.5.4", 416 | "signal-exit": "^3.0.1", 417 | "spawn-wrap": "^1.4.2", 418 | "test-exclude": "^4.1.1", 419 | "yargs": "^10.0.3", 420 | "yargs-parser": "^8.0.0" 421 | }, 422 | "dependencies": { 423 | "align-text": { 424 | "version": "0.1.4", 425 | "bundled": true, 426 | "dev": true, 427 | "requires": { 428 | "kind-of": "^3.0.2", 429 | "longest": "^1.0.1", 430 | "repeat-string": "^1.5.2" 431 | } 432 | }, 433 | "amdefine": { 434 | "version": "1.0.1", 435 | "bundled": true, 436 | "dev": true 437 | }, 438 | "ansi-regex": { 439 | "version": "2.1.1", 440 | "bundled": true, 441 | "dev": true 442 | }, 443 | "ansi-styles": { 444 | "version": "2.2.1", 445 | "bundled": true, 446 | "dev": true 447 | }, 448 | "append-transform": { 449 | "version": "0.4.0", 450 | "bundled": true, 451 | "dev": true, 452 | "requires": { 453 | "default-require-extensions": "^1.0.0" 454 | } 455 | }, 456 | "archy": { 457 | "version": "1.0.0", 458 | "bundled": true, 459 | "dev": true 460 | }, 461 | "arr-diff": { 462 | "version": "2.0.0", 463 | "bundled": true, 464 | "dev": true, 465 | "requires": { 466 | "arr-flatten": "^1.0.1" 467 | } 468 | }, 469 | "arr-flatten": { 470 | "version": "1.1.0", 471 | "bundled": true, 472 | "dev": true 473 | }, 474 | "array-unique": { 475 | "version": "0.2.1", 476 | "bundled": true, 477 | "dev": true 478 | }, 479 | "arrify": { 480 | "version": "1.0.1", 481 | "bundled": true, 482 | "dev": true 483 | }, 484 | "async": { 485 | "version": "1.5.2", 486 | "bundled": true, 487 | "dev": true 488 | }, 489 | "babel-code-frame": { 490 | "version": "6.26.0", 491 | "bundled": true, 492 | "dev": true, 493 | "requires": { 494 | "chalk": "^1.1.3", 495 | "esutils": "^2.0.2", 496 | "js-tokens": "^3.0.2" 497 | } 498 | }, 499 | "babel-generator": { 500 | "version": "6.26.0", 501 | "bundled": true, 502 | "dev": true, 503 | "requires": { 504 | "babel-messages": "^6.23.0", 505 | "babel-runtime": "^6.26.0", 506 | "babel-types": "^6.26.0", 507 | "detect-indent": "^4.0.0", 508 | "jsesc": "^1.3.0", 509 | "lodash": "^4.17.4", 510 | "source-map": "^0.5.6", 511 | "trim-right": "^1.0.1" 512 | } 513 | }, 514 | "babel-messages": { 515 | "version": "6.23.0", 516 | "bundled": true, 517 | "dev": true, 518 | "requires": { 519 | "babel-runtime": "^6.22.0" 520 | } 521 | }, 522 | "babel-runtime": { 523 | "version": "6.26.0", 524 | "bundled": true, 525 | "dev": true, 526 | "requires": { 527 | "core-js": "^2.4.0", 528 | "regenerator-runtime": "^0.11.0" 529 | } 530 | }, 531 | "babel-template": { 532 | "version": "6.26.0", 533 | "bundled": true, 534 | "dev": true, 535 | "requires": { 536 | "babel-runtime": "^6.26.0", 537 | "babel-traverse": "^6.26.0", 538 | "babel-types": "^6.26.0", 539 | "babylon": "^6.18.0", 540 | "lodash": "^4.17.4" 541 | } 542 | }, 543 | "babel-traverse": { 544 | "version": "6.26.0", 545 | "bundled": true, 546 | "dev": true, 547 | "requires": { 548 | "babel-code-frame": "^6.26.0", 549 | "babel-messages": "^6.23.0", 550 | "babel-runtime": "^6.26.0", 551 | "babel-types": "^6.26.0", 552 | "babylon": "^6.18.0", 553 | "debug": "^2.6.8", 554 | "globals": "^9.18.0", 555 | "invariant": "^2.2.2", 556 | "lodash": "^4.17.4" 557 | } 558 | }, 559 | "babel-types": { 560 | "version": "6.26.0", 561 | "bundled": true, 562 | "dev": true, 563 | "requires": { 564 | "babel-runtime": "^6.26.0", 565 | "esutils": "^2.0.2", 566 | "lodash": "^4.17.4", 567 | "to-fast-properties": "^1.0.3" 568 | } 569 | }, 570 | "babylon": { 571 | "version": "6.18.0", 572 | "bundled": true, 573 | "dev": true 574 | }, 575 | "balanced-match": { 576 | "version": "1.0.0", 577 | "bundled": true, 578 | "dev": true 579 | }, 580 | "brace-expansion": { 581 | "version": "1.1.8", 582 | "bundled": true, 583 | "dev": true, 584 | "requires": { 585 | "balanced-match": "^1.0.0", 586 | "concat-map": "0.0.1" 587 | } 588 | }, 589 | "braces": { 590 | "version": "1.8.5", 591 | "bundled": true, 592 | "dev": true, 593 | "requires": { 594 | "expand-range": "^1.8.1", 595 | "preserve": "^0.2.0", 596 | "repeat-element": "^1.1.2" 597 | } 598 | }, 599 | "builtin-modules": { 600 | "version": "1.1.1", 601 | "bundled": true, 602 | "dev": true 603 | }, 604 | "caching-transform": { 605 | "version": "1.0.1", 606 | "bundled": true, 607 | "dev": true, 608 | "requires": { 609 | "md5-hex": "^1.2.0", 610 | "mkdirp": "^0.5.1", 611 | "write-file-atomic": "^1.1.4" 612 | } 613 | }, 614 | "camelcase": { 615 | "version": "1.2.1", 616 | "bundled": true, 617 | "dev": true, 618 | "optional": true 619 | }, 620 | "center-align": { 621 | "version": "0.1.3", 622 | "bundled": true, 623 | "dev": true, 624 | "optional": true, 625 | "requires": { 626 | "align-text": "^0.1.3", 627 | "lazy-cache": "^1.0.3" 628 | } 629 | }, 630 | "chalk": { 631 | "version": "1.1.3", 632 | "bundled": true, 633 | "dev": true, 634 | "requires": { 635 | "ansi-styles": "^2.2.1", 636 | "escape-string-regexp": "^1.0.2", 637 | "has-ansi": "^2.0.0", 638 | "strip-ansi": "^3.0.0", 639 | "supports-color": "^2.0.0" 640 | } 641 | }, 642 | "cliui": { 643 | "version": "2.1.0", 644 | "bundled": true, 645 | "dev": true, 646 | "optional": true, 647 | "requires": { 648 | "center-align": "^0.1.1", 649 | "right-align": "^0.1.1", 650 | "wordwrap": "0.0.2" 651 | }, 652 | "dependencies": { 653 | "wordwrap": { 654 | "version": "0.0.2", 655 | "bundled": true, 656 | "dev": true, 657 | "optional": true 658 | } 659 | } 660 | }, 661 | "code-point-at": { 662 | "version": "1.1.0", 663 | "bundled": true, 664 | "dev": true 665 | }, 666 | "commondir": { 667 | "version": "1.0.1", 668 | "bundled": true, 669 | "dev": true 670 | }, 671 | "concat-map": { 672 | "version": "0.0.1", 673 | "bundled": true, 674 | "dev": true 675 | }, 676 | "convert-source-map": { 677 | "version": "1.5.1", 678 | "bundled": true, 679 | "dev": true 680 | }, 681 | "core-js": { 682 | "version": "2.5.3", 683 | "bundled": true, 684 | "dev": true 685 | }, 686 | "cross-spawn": { 687 | "version": "4.0.2", 688 | "bundled": true, 689 | "dev": true, 690 | "requires": { 691 | "lru-cache": "^4.0.1", 692 | "which": "^1.2.9" 693 | } 694 | }, 695 | "debug": { 696 | "version": "2.6.9", 697 | "bundled": true, 698 | "dev": true, 699 | "requires": { 700 | "ms": "2.0.0" 701 | } 702 | }, 703 | "debug-log": { 704 | "version": "1.0.1", 705 | "bundled": true, 706 | "dev": true 707 | }, 708 | "decamelize": { 709 | "version": "1.2.0", 710 | "bundled": true, 711 | "dev": true 712 | }, 713 | "default-require-extensions": { 714 | "version": "1.0.0", 715 | "bundled": true, 716 | "dev": true, 717 | "requires": { 718 | "strip-bom": "^2.0.0" 719 | } 720 | }, 721 | "detect-indent": { 722 | "version": "4.0.0", 723 | "bundled": true, 724 | "dev": true, 725 | "requires": { 726 | "repeating": "^2.0.0" 727 | } 728 | }, 729 | "error-ex": { 730 | "version": "1.3.1", 731 | "bundled": true, 732 | "dev": true, 733 | "requires": { 734 | "is-arrayish": "^0.2.1" 735 | } 736 | }, 737 | "escape-string-regexp": { 738 | "version": "1.0.5", 739 | "bundled": true, 740 | "dev": true 741 | }, 742 | "esutils": { 743 | "version": "2.0.2", 744 | "bundled": true, 745 | "dev": true 746 | }, 747 | "execa": { 748 | "version": "0.7.0", 749 | "bundled": true, 750 | "dev": true, 751 | "requires": { 752 | "cross-spawn": "^5.0.1", 753 | "get-stream": "^3.0.0", 754 | "is-stream": "^1.1.0", 755 | "npm-run-path": "^2.0.0", 756 | "p-finally": "^1.0.0", 757 | "signal-exit": "^3.0.0", 758 | "strip-eof": "^1.0.0" 759 | }, 760 | "dependencies": { 761 | "cross-spawn": { 762 | "version": "5.1.0", 763 | "bundled": true, 764 | "dev": true, 765 | "requires": { 766 | "lru-cache": "^4.0.1", 767 | "shebang-command": "^1.2.0", 768 | "which": "^1.2.9" 769 | } 770 | } 771 | } 772 | }, 773 | "expand-brackets": { 774 | "version": "0.1.5", 775 | "bundled": true, 776 | "dev": true, 777 | "requires": { 778 | "is-posix-bracket": "^0.1.0" 779 | } 780 | }, 781 | "expand-range": { 782 | "version": "1.8.2", 783 | "bundled": true, 784 | "dev": true, 785 | "requires": { 786 | "fill-range": "^2.1.0" 787 | } 788 | }, 789 | "extglob": { 790 | "version": "0.3.2", 791 | "bundled": true, 792 | "dev": true, 793 | "requires": { 794 | "is-extglob": "^1.0.0" 795 | } 796 | }, 797 | "filename-regex": { 798 | "version": "2.0.1", 799 | "bundled": true, 800 | "dev": true 801 | }, 802 | "fill-range": { 803 | "version": "2.2.3", 804 | "bundled": true, 805 | "dev": true, 806 | "requires": { 807 | "is-number": "^2.1.0", 808 | "isobject": "^2.0.0", 809 | "randomatic": "^1.1.3", 810 | "repeat-element": "^1.1.2", 811 | "repeat-string": "^1.5.2" 812 | } 813 | }, 814 | "find-cache-dir": { 815 | "version": "0.1.1", 816 | "bundled": true, 817 | "dev": true, 818 | "requires": { 819 | "commondir": "^1.0.1", 820 | "mkdirp": "^0.5.1", 821 | "pkg-dir": "^1.0.0" 822 | } 823 | }, 824 | "find-up": { 825 | "version": "2.1.0", 826 | "bundled": true, 827 | "dev": true, 828 | "requires": { 829 | "locate-path": "^2.0.0" 830 | } 831 | }, 832 | "for-in": { 833 | "version": "1.0.2", 834 | "bundled": true, 835 | "dev": true 836 | }, 837 | "for-own": { 838 | "version": "0.1.5", 839 | "bundled": true, 840 | "dev": true, 841 | "requires": { 842 | "for-in": "^1.0.1" 843 | } 844 | }, 845 | "foreground-child": { 846 | "version": "1.5.6", 847 | "bundled": true, 848 | "dev": true, 849 | "requires": { 850 | "cross-spawn": "^4", 851 | "signal-exit": "^3.0.0" 852 | } 853 | }, 854 | "fs.realpath": { 855 | "version": "1.0.0", 856 | "bundled": true, 857 | "dev": true 858 | }, 859 | "get-caller-file": { 860 | "version": "1.0.2", 861 | "bundled": true, 862 | "dev": true 863 | }, 864 | "get-stream": { 865 | "version": "3.0.0", 866 | "bundled": true, 867 | "dev": true 868 | }, 869 | "glob": { 870 | "version": "7.1.2", 871 | "bundled": true, 872 | "dev": true, 873 | "requires": { 874 | "fs.realpath": "^1.0.0", 875 | "inflight": "^1.0.4", 876 | "inherits": "2", 877 | "minimatch": "^3.0.4", 878 | "once": "^1.3.0", 879 | "path-is-absolute": "^1.0.0" 880 | } 881 | }, 882 | "glob-base": { 883 | "version": "0.3.0", 884 | "bundled": true, 885 | "dev": true, 886 | "requires": { 887 | "glob-parent": "^2.0.0", 888 | "is-glob": "^2.0.0" 889 | } 890 | }, 891 | "glob-parent": { 892 | "version": "2.0.0", 893 | "bundled": true, 894 | "dev": true, 895 | "requires": { 896 | "is-glob": "^2.0.0" 897 | } 898 | }, 899 | "globals": { 900 | "version": "9.18.0", 901 | "bundled": true, 902 | "dev": true 903 | }, 904 | "graceful-fs": { 905 | "version": "4.1.11", 906 | "bundled": true, 907 | "dev": true 908 | }, 909 | "handlebars": { 910 | "version": "4.0.11", 911 | "bundled": true, 912 | "dev": true, 913 | "requires": { 914 | "async": "^1.4.0", 915 | "optimist": "^0.6.1", 916 | "source-map": "^0.4.4", 917 | "uglify-js": "^2.6" 918 | }, 919 | "dependencies": { 920 | "source-map": { 921 | "version": "0.4.4", 922 | "bundled": true, 923 | "dev": true, 924 | "requires": { 925 | "amdefine": ">=0.0.4" 926 | } 927 | } 928 | } 929 | }, 930 | "has-ansi": { 931 | "version": "2.0.0", 932 | "bundled": true, 933 | "dev": true, 934 | "requires": { 935 | "ansi-regex": "^2.0.0" 936 | } 937 | }, 938 | "has-flag": { 939 | "version": "1.0.0", 940 | "bundled": true, 941 | "dev": true 942 | }, 943 | "hosted-git-info": { 944 | "version": "2.5.0", 945 | "bundled": true, 946 | "dev": true 947 | }, 948 | "imurmurhash": { 949 | "version": "0.1.4", 950 | "bundled": true, 951 | "dev": true 952 | }, 953 | "inflight": { 954 | "version": "1.0.6", 955 | "bundled": true, 956 | "dev": true, 957 | "requires": { 958 | "once": "^1.3.0", 959 | "wrappy": "1" 960 | } 961 | }, 962 | "inherits": { 963 | "version": "2.0.3", 964 | "bundled": true, 965 | "dev": true 966 | }, 967 | "invariant": { 968 | "version": "2.2.2", 969 | "bundled": true, 970 | "dev": true, 971 | "requires": { 972 | "loose-envify": "^1.0.0" 973 | } 974 | }, 975 | "invert-kv": { 976 | "version": "1.0.0", 977 | "bundled": true, 978 | "dev": true 979 | }, 980 | "is-arrayish": { 981 | "version": "0.2.1", 982 | "bundled": true, 983 | "dev": true 984 | }, 985 | "is-buffer": { 986 | "version": "1.1.6", 987 | "bundled": true, 988 | "dev": true 989 | }, 990 | "is-builtin-module": { 991 | "version": "1.0.0", 992 | "bundled": true, 993 | "dev": true, 994 | "requires": { 995 | "builtin-modules": "^1.0.0" 996 | } 997 | }, 998 | "is-dotfile": { 999 | "version": "1.0.3", 1000 | "bundled": true, 1001 | "dev": true 1002 | }, 1003 | "is-equal-shallow": { 1004 | "version": "0.1.3", 1005 | "bundled": true, 1006 | "dev": true, 1007 | "requires": { 1008 | "is-primitive": "^2.0.0" 1009 | } 1010 | }, 1011 | "is-extendable": { 1012 | "version": "0.1.1", 1013 | "bundled": true, 1014 | "dev": true 1015 | }, 1016 | "is-extglob": { 1017 | "version": "1.0.0", 1018 | "bundled": true, 1019 | "dev": true 1020 | }, 1021 | "is-finite": { 1022 | "version": "1.0.2", 1023 | "bundled": true, 1024 | "dev": true, 1025 | "requires": { 1026 | "number-is-nan": "^1.0.0" 1027 | } 1028 | }, 1029 | "is-fullwidth-code-point": { 1030 | "version": "1.0.0", 1031 | "bundled": true, 1032 | "dev": true, 1033 | "requires": { 1034 | "number-is-nan": "^1.0.0" 1035 | } 1036 | }, 1037 | "is-glob": { 1038 | "version": "2.0.1", 1039 | "bundled": true, 1040 | "dev": true, 1041 | "requires": { 1042 | "is-extglob": "^1.0.0" 1043 | } 1044 | }, 1045 | "is-number": { 1046 | "version": "2.1.0", 1047 | "bundled": true, 1048 | "dev": true, 1049 | "requires": { 1050 | "kind-of": "^3.0.2" 1051 | } 1052 | }, 1053 | "is-posix-bracket": { 1054 | "version": "0.1.1", 1055 | "bundled": true, 1056 | "dev": true 1057 | }, 1058 | "is-primitive": { 1059 | "version": "2.0.0", 1060 | "bundled": true, 1061 | "dev": true 1062 | }, 1063 | "is-stream": { 1064 | "version": "1.1.0", 1065 | "bundled": true, 1066 | "dev": true 1067 | }, 1068 | "is-utf8": { 1069 | "version": "0.2.1", 1070 | "bundled": true, 1071 | "dev": true 1072 | }, 1073 | "isarray": { 1074 | "version": "1.0.0", 1075 | "bundled": true, 1076 | "dev": true 1077 | }, 1078 | "isexe": { 1079 | "version": "2.0.0", 1080 | "bundled": true, 1081 | "dev": true 1082 | }, 1083 | "isobject": { 1084 | "version": "2.1.0", 1085 | "bundled": true, 1086 | "dev": true, 1087 | "requires": { 1088 | "isarray": "1.0.0" 1089 | } 1090 | }, 1091 | "istanbul-lib-coverage": { 1092 | "version": "1.1.1", 1093 | "bundled": true, 1094 | "dev": true 1095 | }, 1096 | "istanbul-lib-hook": { 1097 | "version": "1.1.0", 1098 | "bundled": true, 1099 | "dev": true, 1100 | "requires": { 1101 | "append-transform": "^0.4.0" 1102 | } 1103 | }, 1104 | "istanbul-lib-instrument": { 1105 | "version": "1.9.1", 1106 | "bundled": true, 1107 | "dev": true, 1108 | "requires": { 1109 | "babel-generator": "^6.18.0", 1110 | "babel-template": "^6.16.0", 1111 | "babel-traverse": "^6.18.0", 1112 | "babel-types": "^6.18.0", 1113 | "babylon": "^6.18.0", 1114 | "istanbul-lib-coverage": "^1.1.1", 1115 | "semver": "^5.3.0" 1116 | } 1117 | }, 1118 | "istanbul-lib-report": { 1119 | "version": "1.1.2", 1120 | "bundled": true, 1121 | "dev": true, 1122 | "requires": { 1123 | "istanbul-lib-coverage": "^1.1.1", 1124 | "mkdirp": "^0.5.1", 1125 | "path-parse": "^1.0.5", 1126 | "supports-color": "^3.1.2" 1127 | }, 1128 | "dependencies": { 1129 | "supports-color": { 1130 | "version": "3.2.3", 1131 | "bundled": true, 1132 | "dev": true, 1133 | "requires": { 1134 | "has-flag": "^1.0.0" 1135 | } 1136 | } 1137 | } 1138 | }, 1139 | "istanbul-lib-source-maps": { 1140 | "version": "1.2.2", 1141 | "bundled": true, 1142 | "dev": true, 1143 | "requires": { 1144 | "debug": "^3.1.0", 1145 | "istanbul-lib-coverage": "^1.1.1", 1146 | "mkdirp": "^0.5.1", 1147 | "rimraf": "^2.6.1", 1148 | "source-map": "^0.5.3" 1149 | }, 1150 | "dependencies": { 1151 | "debug": { 1152 | "version": "3.1.0", 1153 | "bundled": true, 1154 | "dev": true, 1155 | "requires": { 1156 | "ms": "2.0.0" 1157 | } 1158 | } 1159 | } 1160 | }, 1161 | "istanbul-reports": { 1162 | "version": "1.1.3", 1163 | "bundled": true, 1164 | "dev": true, 1165 | "requires": { 1166 | "handlebars": "^4.0.3" 1167 | } 1168 | }, 1169 | "js-tokens": { 1170 | "version": "3.0.2", 1171 | "bundled": true, 1172 | "dev": true 1173 | }, 1174 | "jsesc": { 1175 | "version": "1.3.0", 1176 | "bundled": true, 1177 | "dev": true 1178 | }, 1179 | "kind-of": { 1180 | "version": "3.2.2", 1181 | "bundled": true, 1182 | "dev": true, 1183 | "requires": { 1184 | "is-buffer": "^1.1.5" 1185 | } 1186 | }, 1187 | "lazy-cache": { 1188 | "version": "1.0.4", 1189 | "bundled": true, 1190 | "dev": true, 1191 | "optional": true 1192 | }, 1193 | "lcid": { 1194 | "version": "1.0.0", 1195 | "bundled": true, 1196 | "dev": true, 1197 | "requires": { 1198 | "invert-kv": "^1.0.0" 1199 | } 1200 | }, 1201 | "load-json-file": { 1202 | "version": "1.1.0", 1203 | "bundled": true, 1204 | "dev": true, 1205 | "requires": { 1206 | "graceful-fs": "^4.1.2", 1207 | "parse-json": "^2.2.0", 1208 | "pify": "^2.0.0", 1209 | "pinkie-promise": "^2.0.0", 1210 | "strip-bom": "^2.0.0" 1211 | } 1212 | }, 1213 | "locate-path": { 1214 | "version": "2.0.0", 1215 | "bundled": true, 1216 | "dev": true, 1217 | "requires": { 1218 | "p-locate": "^2.0.0", 1219 | "path-exists": "^3.0.0" 1220 | }, 1221 | "dependencies": { 1222 | "path-exists": { 1223 | "version": "3.0.0", 1224 | "bundled": true, 1225 | "dev": true 1226 | } 1227 | } 1228 | }, 1229 | "lodash": { 1230 | "version": "4.17.4", 1231 | "bundled": true, 1232 | "dev": true 1233 | }, 1234 | "longest": { 1235 | "version": "1.0.1", 1236 | "bundled": true, 1237 | "dev": true 1238 | }, 1239 | "loose-envify": { 1240 | "version": "1.3.1", 1241 | "bundled": true, 1242 | "dev": true, 1243 | "requires": { 1244 | "js-tokens": "^3.0.0" 1245 | } 1246 | }, 1247 | "lru-cache": { 1248 | "version": "4.1.1", 1249 | "bundled": true, 1250 | "dev": true, 1251 | "requires": { 1252 | "pseudomap": "^1.0.2", 1253 | "yallist": "^2.1.2" 1254 | } 1255 | }, 1256 | "md5-hex": { 1257 | "version": "1.3.0", 1258 | "bundled": true, 1259 | "dev": true, 1260 | "requires": { 1261 | "md5-o-matic": "^0.1.1" 1262 | } 1263 | }, 1264 | "md5-o-matic": { 1265 | "version": "0.1.1", 1266 | "bundled": true, 1267 | "dev": true 1268 | }, 1269 | "mem": { 1270 | "version": "1.1.0", 1271 | "bundled": true, 1272 | "dev": true, 1273 | "requires": { 1274 | "mimic-fn": "^1.0.0" 1275 | } 1276 | }, 1277 | "merge-source-map": { 1278 | "version": "1.0.4", 1279 | "bundled": true, 1280 | "dev": true, 1281 | "requires": { 1282 | "source-map": "^0.5.6" 1283 | } 1284 | }, 1285 | "micromatch": { 1286 | "version": "2.3.11", 1287 | "bundled": true, 1288 | "dev": true, 1289 | "requires": { 1290 | "arr-diff": "^2.0.0", 1291 | "array-unique": "^0.2.1", 1292 | "braces": "^1.8.2", 1293 | "expand-brackets": "^0.1.4", 1294 | "extglob": "^0.3.1", 1295 | "filename-regex": "^2.0.0", 1296 | "is-extglob": "^1.0.0", 1297 | "is-glob": "^2.0.1", 1298 | "kind-of": "^3.0.2", 1299 | "normalize-path": "^2.0.1", 1300 | "object.omit": "^2.0.0", 1301 | "parse-glob": "^3.0.4", 1302 | "regex-cache": "^0.4.2" 1303 | } 1304 | }, 1305 | "mimic-fn": { 1306 | "version": "1.1.0", 1307 | "bundled": true, 1308 | "dev": true 1309 | }, 1310 | "minimatch": { 1311 | "version": "3.0.4", 1312 | "bundled": true, 1313 | "dev": true, 1314 | "requires": { 1315 | "brace-expansion": "^1.1.7" 1316 | } 1317 | }, 1318 | "minimist": { 1319 | "version": "0.0.8", 1320 | "bundled": true, 1321 | "dev": true 1322 | }, 1323 | "mkdirp": { 1324 | "version": "0.5.1", 1325 | "bundled": true, 1326 | "dev": true, 1327 | "requires": { 1328 | "minimist": "0.0.8" 1329 | } 1330 | }, 1331 | "ms": { 1332 | "version": "2.0.0", 1333 | "bundled": true, 1334 | "dev": true 1335 | }, 1336 | "normalize-package-data": { 1337 | "version": "2.4.0", 1338 | "bundled": true, 1339 | "dev": true, 1340 | "requires": { 1341 | "hosted-git-info": "^2.1.4", 1342 | "is-builtin-module": "^1.0.0", 1343 | "semver": "2 || 3 || 4 || 5", 1344 | "validate-npm-package-license": "^3.0.1" 1345 | } 1346 | }, 1347 | "normalize-path": { 1348 | "version": "2.1.1", 1349 | "bundled": true, 1350 | "dev": true, 1351 | "requires": { 1352 | "remove-trailing-separator": "^1.0.1" 1353 | } 1354 | }, 1355 | "npm-run-path": { 1356 | "version": "2.0.2", 1357 | "bundled": true, 1358 | "dev": true, 1359 | "requires": { 1360 | "path-key": "^2.0.0" 1361 | } 1362 | }, 1363 | "number-is-nan": { 1364 | "version": "1.0.1", 1365 | "bundled": true, 1366 | "dev": true 1367 | }, 1368 | "object-assign": { 1369 | "version": "4.1.1", 1370 | "bundled": true, 1371 | "dev": true 1372 | }, 1373 | "object.omit": { 1374 | "version": "2.0.1", 1375 | "bundled": true, 1376 | "dev": true, 1377 | "requires": { 1378 | "for-own": "^0.1.4", 1379 | "is-extendable": "^0.1.1" 1380 | } 1381 | }, 1382 | "once": { 1383 | "version": "1.4.0", 1384 | "bundled": true, 1385 | "dev": true, 1386 | "requires": { 1387 | "wrappy": "1" 1388 | } 1389 | }, 1390 | "optimist": { 1391 | "version": "0.6.1", 1392 | "bundled": true, 1393 | "dev": true, 1394 | "requires": { 1395 | "minimist": "~0.0.1", 1396 | "wordwrap": "~0.0.2" 1397 | } 1398 | }, 1399 | "os-homedir": { 1400 | "version": "1.0.2", 1401 | "bundled": true, 1402 | "dev": true 1403 | }, 1404 | "os-locale": { 1405 | "version": "2.1.0", 1406 | "bundled": true, 1407 | "dev": true, 1408 | "requires": { 1409 | "execa": "^0.7.0", 1410 | "lcid": "^1.0.0", 1411 | "mem": "^1.1.0" 1412 | } 1413 | }, 1414 | "p-finally": { 1415 | "version": "1.0.0", 1416 | "bundled": true, 1417 | "dev": true 1418 | }, 1419 | "p-limit": { 1420 | "version": "1.1.0", 1421 | "bundled": true, 1422 | "dev": true 1423 | }, 1424 | "p-locate": { 1425 | "version": "2.0.0", 1426 | "bundled": true, 1427 | "dev": true, 1428 | "requires": { 1429 | "p-limit": "^1.1.0" 1430 | } 1431 | }, 1432 | "parse-glob": { 1433 | "version": "3.0.4", 1434 | "bundled": true, 1435 | "dev": true, 1436 | "requires": { 1437 | "glob-base": "^0.3.0", 1438 | "is-dotfile": "^1.0.0", 1439 | "is-extglob": "^1.0.0", 1440 | "is-glob": "^2.0.0" 1441 | } 1442 | }, 1443 | "parse-json": { 1444 | "version": "2.2.0", 1445 | "bundled": true, 1446 | "dev": true, 1447 | "requires": { 1448 | "error-ex": "^1.2.0" 1449 | } 1450 | }, 1451 | "path-exists": { 1452 | "version": "2.1.0", 1453 | "bundled": true, 1454 | "dev": true, 1455 | "requires": { 1456 | "pinkie-promise": "^2.0.0" 1457 | } 1458 | }, 1459 | "path-is-absolute": { 1460 | "version": "1.0.1", 1461 | "bundled": true, 1462 | "dev": true 1463 | }, 1464 | "path-key": { 1465 | "version": "2.0.1", 1466 | "bundled": true, 1467 | "dev": true 1468 | }, 1469 | "path-parse": { 1470 | "version": "1.0.5", 1471 | "bundled": true, 1472 | "dev": true 1473 | }, 1474 | "path-type": { 1475 | "version": "1.1.0", 1476 | "bundled": true, 1477 | "dev": true, 1478 | "requires": { 1479 | "graceful-fs": "^4.1.2", 1480 | "pify": "^2.0.0", 1481 | "pinkie-promise": "^2.0.0" 1482 | } 1483 | }, 1484 | "pify": { 1485 | "version": "2.3.0", 1486 | "bundled": true, 1487 | "dev": true 1488 | }, 1489 | "pinkie": { 1490 | "version": "2.0.4", 1491 | "bundled": true, 1492 | "dev": true 1493 | }, 1494 | "pinkie-promise": { 1495 | "version": "2.0.1", 1496 | "bundled": true, 1497 | "dev": true, 1498 | "requires": { 1499 | "pinkie": "^2.0.0" 1500 | } 1501 | }, 1502 | "pkg-dir": { 1503 | "version": "1.0.0", 1504 | "bundled": true, 1505 | "dev": true, 1506 | "requires": { 1507 | "find-up": "^1.0.0" 1508 | }, 1509 | "dependencies": { 1510 | "find-up": { 1511 | "version": "1.1.2", 1512 | "bundled": true, 1513 | "dev": true, 1514 | "requires": { 1515 | "path-exists": "^2.0.0", 1516 | "pinkie-promise": "^2.0.0" 1517 | } 1518 | } 1519 | } 1520 | }, 1521 | "preserve": { 1522 | "version": "0.2.0", 1523 | "bundled": true, 1524 | "dev": true 1525 | }, 1526 | "pseudomap": { 1527 | "version": "1.0.2", 1528 | "bundled": true, 1529 | "dev": true 1530 | }, 1531 | "randomatic": { 1532 | "version": "1.1.7", 1533 | "bundled": true, 1534 | "dev": true, 1535 | "requires": { 1536 | "is-number": "^3.0.0", 1537 | "kind-of": "^4.0.0" 1538 | }, 1539 | "dependencies": { 1540 | "is-number": { 1541 | "version": "3.0.0", 1542 | "bundled": true, 1543 | "dev": true, 1544 | "requires": { 1545 | "kind-of": "^3.0.2" 1546 | }, 1547 | "dependencies": { 1548 | "kind-of": { 1549 | "version": "3.2.2", 1550 | "bundled": true, 1551 | "dev": true, 1552 | "requires": { 1553 | "is-buffer": "^1.1.5" 1554 | } 1555 | } 1556 | } 1557 | }, 1558 | "kind-of": { 1559 | "version": "4.0.0", 1560 | "bundled": true, 1561 | "dev": true, 1562 | "requires": { 1563 | "is-buffer": "^1.1.5" 1564 | } 1565 | } 1566 | } 1567 | }, 1568 | "read-pkg": { 1569 | "version": "1.1.0", 1570 | "bundled": true, 1571 | "dev": true, 1572 | "requires": { 1573 | "load-json-file": "^1.0.0", 1574 | "normalize-package-data": "^2.3.2", 1575 | "path-type": "^1.0.0" 1576 | } 1577 | }, 1578 | "read-pkg-up": { 1579 | "version": "1.0.1", 1580 | "bundled": true, 1581 | "dev": true, 1582 | "requires": { 1583 | "find-up": "^1.0.0", 1584 | "read-pkg": "^1.0.0" 1585 | }, 1586 | "dependencies": { 1587 | "find-up": { 1588 | "version": "1.1.2", 1589 | "bundled": true, 1590 | "dev": true, 1591 | "requires": { 1592 | "path-exists": "^2.0.0", 1593 | "pinkie-promise": "^2.0.0" 1594 | } 1595 | } 1596 | } 1597 | }, 1598 | "regenerator-runtime": { 1599 | "version": "0.11.1", 1600 | "bundled": true, 1601 | "dev": true 1602 | }, 1603 | "regex-cache": { 1604 | "version": "0.4.4", 1605 | "bundled": true, 1606 | "dev": true, 1607 | "requires": { 1608 | "is-equal-shallow": "^0.1.3" 1609 | } 1610 | }, 1611 | "remove-trailing-separator": { 1612 | "version": "1.1.0", 1613 | "bundled": true, 1614 | "dev": true 1615 | }, 1616 | "repeat-element": { 1617 | "version": "1.1.2", 1618 | "bundled": true, 1619 | "dev": true 1620 | }, 1621 | "repeat-string": { 1622 | "version": "1.6.1", 1623 | "bundled": true, 1624 | "dev": true 1625 | }, 1626 | "repeating": { 1627 | "version": "2.0.1", 1628 | "bundled": true, 1629 | "dev": true, 1630 | "requires": { 1631 | "is-finite": "^1.0.0" 1632 | } 1633 | }, 1634 | "require-directory": { 1635 | "version": "2.1.1", 1636 | "bundled": true, 1637 | "dev": true 1638 | }, 1639 | "require-main-filename": { 1640 | "version": "1.0.1", 1641 | "bundled": true, 1642 | "dev": true 1643 | }, 1644 | "resolve-from": { 1645 | "version": "2.0.0", 1646 | "bundled": true, 1647 | "dev": true 1648 | }, 1649 | "right-align": { 1650 | "version": "0.1.3", 1651 | "bundled": true, 1652 | "dev": true, 1653 | "optional": true, 1654 | "requires": { 1655 | "align-text": "^0.1.1" 1656 | } 1657 | }, 1658 | "rimraf": { 1659 | "version": "2.6.2", 1660 | "bundled": true, 1661 | "dev": true, 1662 | "requires": { 1663 | "glob": "^7.0.5" 1664 | } 1665 | }, 1666 | "semver": { 1667 | "version": "5.4.1", 1668 | "bundled": true, 1669 | "dev": true 1670 | }, 1671 | "set-blocking": { 1672 | "version": "2.0.0", 1673 | "bundled": true, 1674 | "dev": true 1675 | }, 1676 | "shebang-command": { 1677 | "version": "1.2.0", 1678 | "bundled": true, 1679 | "dev": true, 1680 | "requires": { 1681 | "shebang-regex": "^1.0.0" 1682 | } 1683 | }, 1684 | "shebang-regex": { 1685 | "version": "1.0.0", 1686 | "bundled": true, 1687 | "dev": true 1688 | }, 1689 | "signal-exit": { 1690 | "version": "3.0.2", 1691 | "bundled": true, 1692 | "dev": true 1693 | }, 1694 | "slide": { 1695 | "version": "1.1.6", 1696 | "bundled": true, 1697 | "dev": true 1698 | }, 1699 | "source-map": { 1700 | "version": "0.5.7", 1701 | "bundled": true, 1702 | "dev": true 1703 | }, 1704 | "spawn-wrap": { 1705 | "version": "1.4.2", 1706 | "bundled": true, 1707 | "dev": true, 1708 | "requires": { 1709 | "foreground-child": "^1.5.6", 1710 | "mkdirp": "^0.5.0", 1711 | "os-homedir": "^1.0.1", 1712 | "rimraf": "^2.6.2", 1713 | "signal-exit": "^3.0.2", 1714 | "which": "^1.3.0" 1715 | } 1716 | }, 1717 | "spdx-correct": { 1718 | "version": "1.0.2", 1719 | "bundled": true, 1720 | "dev": true, 1721 | "requires": { 1722 | "spdx-license-ids": "^1.0.2" 1723 | } 1724 | }, 1725 | "spdx-expression-parse": { 1726 | "version": "1.0.4", 1727 | "bundled": true, 1728 | "dev": true 1729 | }, 1730 | "spdx-license-ids": { 1731 | "version": "1.2.2", 1732 | "bundled": true, 1733 | "dev": true 1734 | }, 1735 | "string-width": { 1736 | "version": "2.1.1", 1737 | "bundled": true, 1738 | "dev": true, 1739 | "requires": { 1740 | "is-fullwidth-code-point": "^2.0.0", 1741 | "strip-ansi": "^4.0.0" 1742 | }, 1743 | "dependencies": { 1744 | "ansi-regex": { 1745 | "version": "3.0.0", 1746 | "bundled": true, 1747 | "dev": true 1748 | }, 1749 | "is-fullwidth-code-point": { 1750 | "version": "2.0.0", 1751 | "bundled": true, 1752 | "dev": true 1753 | }, 1754 | "strip-ansi": { 1755 | "version": "4.0.0", 1756 | "bundled": true, 1757 | "dev": true, 1758 | "requires": { 1759 | "ansi-regex": "^3.0.0" 1760 | } 1761 | } 1762 | } 1763 | }, 1764 | "strip-ansi": { 1765 | "version": "3.0.1", 1766 | "bundled": true, 1767 | "dev": true, 1768 | "requires": { 1769 | "ansi-regex": "^2.0.0" 1770 | } 1771 | }, 1772 | "strip-bom": { 1773 | "version": "2.0.0", 1774 | "bundled": true, 1775 | "dev": true, 1776 | "requires": { 1777 | "is-utf8": "^0.2.0" 1778 | } 1779 | }, 1780 | "strip-eof": { 1781 | "version": "1.0.0", 1782 | "bundled": true, 1783 | "dev": true 1784 | }, 1785 | "supports-color": { 1786 | "version": "2.0.0", 1787 | "bundled": true, 1788 | "dev": true 1789 | }, 1790 | "test-exclude": { 1791 | "version": "4.1.1", 1792 | "bundled": true, 1793 | "dev": true, 1794 | "requires": { 1795 | "arrify": "^1.0.1", 1796 | "micromatch": "^2.3.11", 1797 | "object-assign": "^4.1.0", 1798 | "read-pkg-up": "^1.0.1", 1799 | "require-main-filename": "^1.0.1" 1800 | } 1801 | }, 1802 | "to-fast-properties": { 1803 | "version": "1.0.3", 1804 | "bundled": true, 1805 | "dev": true 1806 | }, 1807 | "trim-right": { 1808 | "version": "1.0.1", 1809 | "bundled": true, 1810 | "dev": true 1811 | }, 1812 | "uglify-js": { 1813 | "version": "2.8.29", 1814 | "bundled": true, 1815 | "dev": true, 1816 | "optional": true, 1817 | "requires": { 1818 | "source-map": "~0.5.1", 1819 | "uglify-to-browserify": "~1.0.0", 1820 | "yargs": "~3.10.0" 1821 | }, 1822 | "dependencies": { 1823 | "yargs": { 1824 | "version": "3.10.0", 1825 | "bundled": true, 1826 | "dev": true, 1827 | "optional": true, 1828 | "requires": { 1829 | "camelcase": "^1.0.2", 1830 | "cliui": "^2.1.0", 1831 | "decamelize": "^1.0.0", 1832 | "window-size": "0.1.0" 1833 | } 1834 | } 1835 | } 1836 | }, 1837 | "uglify-to-browserify": { 1838 | "version": "1.0.2", 1839 | "bundled": true, 1840 | "dev": true, 1841 | "optional": true 1842 | }, 1843 | "validate-npm-package-license": { 1844 | "version": "3.0.1", 1845 | "bundled": true, 1846 | "dev": true, 1847 | "requires": { 1848 | "spdx-correct": "~1.0.0", 1849 | "spdx-expression-parse": "~1.0.0" 1850 | } 1851 | }, 1852 | "which": { 1853 | "version": "1.3.0", 1854 | "bundled": true, 1855 | "dev": true, 1856 | "requires": { 1857 | "isexe": "^2.0.0" 1858 | } 1859 | }, 1860 | "which-module": { 1861 | "version": "2.0.0", 1862 | "bundled": true, 1863 | "dev": true 1864 | }, 1865 | "window-size": { 1866 | "version": "0.1.0", 1867 | "bundled": true, 1868 | "dev": true, 1869 | "optional": true 1870 | }, 1871 | "wordwrap": { 1872 | "version": "0.0.3", 1873 | "bundled": true, 1874 | "dev": true 1875 | }, 1876 | "wrap-ansi": { 1877 | "version": "2.1.0", 1878 | "bundled": true, 1879 | "dev": true, 1880 | "requires": { 1881 | "string-width": "^1.0.1", 1882 | "strip-ansi": "^3.0.1" 1883 | }, 1884 | "dependencies": { 1885 | "string-width": { 1886 | "version": "1.0.2", 1887 | "bundled": true, 1888 | "dev": true, 1889 | "requires": { 1890 | "code-point-at": "^1.0.0", 1891 | "is-fullwidth-code-point": "^1.0.0", 1892 | "strip-ansi": "^3.0.0" 1893 | } 1894 | } 1895 | } 1896 | }, 1897 | "wrappy": { 1898 | "version": "1.0.2", 1899 | "bundled": true, 1900 | "dev": true 1901 | }, 1902 | "write-file-atomic": { 1903 | "version": "1.3.4", 1904 | "bundled": true, 1905 | "dev": true, 1906 | "requires": { 1907 | "graceful-fs": "^4.1.11", 1908 | "imurmurhash": "^0.1.4", 1909 | "slide": "^1.1.5" 1910 | } 1911 | }, 1912 | "y18n": { 1913 | "version": "3.2.1", 1914 | "bundled": true, 1915 | "dev": true 1916 | }, 1917 | "yallist": { 1918 | "version": "2.1.2", 1919 | "bundled": true, 1920 | "dev": true 1921 | }, 1922 | "yargs": { 1923 | "version": "10.0.3", 1924 | "bundled": true, 1925 | "dev": true, 1926 | "requires": { 1927 | "cliui": "^3.2.0", 1928 | "decamelize": "^1.1.1", 1929 | "find-up": "^2.1.0", 1930 | "get-caller-file": "^1.0.1", 1931 | "os-locale": "^2.0.0", 1932 | "require-directory": "^2.1.1", 1933 | "require-main-filename": "^1.0.1", 1934 | "set-blocking": "^2.0.0", 1935 | "string-width": "^2.0.0", 1936 | "which-module": "^2.0.0", 1937 | "y18n": "^3.2.1", 1938 | "yargs-parser": "^8.0.0" 1939 | }, 1940 | "dependencies": { 1941 | "cliui": { 1942 | "version": "3.2.0", 1943 | "bundled": true, 1944 | "dev": true, 1945 | "requires": { 1946 | "string-width": "^1.0.1", 1947 | "strip-ansi": "^3.0.1", 1948 | "wrap-ansi": "^2.0.0" 1949 | }, 1950 | "dependencies": { 1951 | "string-width": { 1952 | "version": "1.0.2", 1953 | "bundled": true, 1954 | "dev": true, 1955 | "requires": { 1956 | "code-point-at": "^1.0.0", 1957 | "is-fullwidth-code-point": "^1.0.0", 1958 | "strip-ansi": "^3.0.0" 1959 | } 1960 | } 1961 | } 1962 | } 1963 | } 1964 | }, 1965 | "yargs-parser": { 1966 | "version": "8.0.0", 1967 | "bundled": true, 1968 | "dev": true, 1969 | "requires": { 1970 | "camelcase": "^4.1.0" 1971 | }, 1972 | "dependencies": { 1973 | "camelcase": { 1974 | "version": "4.1.0", 1975 | "bundled": true, 1976 | "dev": true 1977 | } 1978 | } 1979 | } 1980 | } 1981 | }, 1982 | "once": { 1983 | "version": "1.4.0", 1984 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1985 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1986 | "dev": true, 1987 | "requires": { 1988 | "wrappy": "1" 1989 | } 1990 | }, 1991 | "path-to-regexp": { 1992 | "version": "2.2.1", 1993 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", 1994 | "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==", 1995 | "dev": true 1996 | }, 1997 | "pathval": { 1998 | "version": "1.1.0", 1999 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 2000 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 2001 | "dev": true 2002 | }, 2003 | "pino": { 2004 | "version": "4.17.3", 2005 | "resolved": "https://registry.npmjs.org/pino/-/pino-4.17.3.tgz", 2006 | "integrity": "sha512-3CBWMG1k6QdOyz9br3WzOhUQD52QFskbmlgX51wGXLPAY0UbKPsUIayYcVOQMCqjmeqEPCAG3V/Uvry+FjwtoQ==", 2007 | "dev": true, 2008 | "requires": { 2009 | "chalk": "^2.4.1", 2010 | "fast-json-parse": "^1.0.3", 2011 | "fast-safe-stringify": "^1.2.3", 2012 | "flatstr": "^1.0.5", 2013 | "pino-std-serializers": "^2.0.0", 2014 | "pump": "^3.0.0", 2015 | "quick-format-unescaped": "^1.1.2", 2016 | "split2": "^2.2.0" 2017 | } 2018 | }, 2019 | "pino-std-serializers": { 2020 | "version": "2.1.0", 2021 | "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.1.0.tgz", 2022 | "integrity": "sha512-NqWvrQD/GpY78ybiNBzi/dg8ylERhDo6nB33j5sfCKpUmWLc3lYzeoBjyRoCMvEpDpL9lmH6ufRd0jw6rcd1pQ==", 2023 | "dev": true 2024 | }, 2025 | "process-nextick-args": { 2026 | "version": "2.0.0", 2027 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 2028 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", 2029 | "dev": true 2030 | }, 2031 | "pump": { 2032 | "version": "3.0.0", 2033 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 2034 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 2035 | "dev": true, 2036 | "requires": { 2037 | "end-of-stream": "^1.1.0", 2038 | "once": "^1.3.1" 2039 | } 2040 | }, 2041 | "punycode": { 2042 | "version": "2.1.1", 2043 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 2044 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 2045 | "dev": true 2046 | }, 2047 | "quick-format-unescaped": { 2048 | "version": "1.1.2", 2049 | "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-1.1.2.tgz", 2050 | "integrity": "sha1-DKWB3jF0vs7yWsPC6JVjQjgdtpg=", 2051 | "dev": true, 2052 | "requires": { 2053 | "fast-safe-stringify": "^1.0.8" 2054 | } 2055 | }, 2056 | "readable-stream": { 2057 | "version": "2.3.6", 2058 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 2059 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 2060 | "dev": true, 2061 | "requires": { 2062 | "core-util-is": "~1.0.0", 2063 | "inherits": "~2.0.3", 2064 | "isarray": "~1.0.0", 2065 | "process-nextick-args": "~2.0.0", 2066 | "safe-buffer": "~5.1.1", 2067 | "string_decoder": "~1.1.1", 2068 | "util-deprecate": "~1.0.1" 2069 | } 2070 | }, 2071 | "ret": { 2072 | "version": "0.1.15", 2073 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", 2074 | "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", 2075 | "dev": true 2076 | }, 2077 | "reusify": { 2078 | "version": "1.0.4", 2079 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 2080 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 2081 | "dev": true 2082 | }, 2083 | "safe-buffer": { 2084 | "version": "5.1.2", 2085 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 2086 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 2087 | "dev": true 2088 | }, 2089 | "safe-regex": { 2090 | "version": "1.1.0", 2091 | "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", 2092 | "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", 2093 | "dev": true, 2094 | "requires": { 2095 | "ret": "~0.1.10" 2096 | } 2097 | }, 2098 | "semver": { 2099 | "version": "5.5.0", 2100 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", 2101 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" 2102 | }, 2103 | "source-map": { 2104 | "version": "0.6.1", 2105 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2106 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 2107 | }, 2108 | "source-map-support": { 2109 | "version": "0.5.3", 2110 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz", 2111 | "integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", 2112 | "requires": { 2113 | "source-map": "^0.6.0" 2114 | } 2115 | }, 2116 | "split2": { 2117 | "version": "2.2.0", 2118 | "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", 2119 | "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", 2120 | "dev": true, 2121 | "requires": { 2122 | "through2": "^2.0.2" 2123 | } 2124 | }, 2125 | "string_decoder": { 2126 | "version": "1.1.1", 2127 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 2128 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 2129 | "dev": true, 2130 | "requires": { 2131 | "safe-buffer": "~5.1.0" 2132 | } 2133 | }, 2134 | "supports-color": { 2135 | "version": "5.4.0", 2136 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 2137 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 2138 | "dev": true, 2139 | "requires": { 2140 | "has-flag": "^3.0.0" 2141 | } 2142 | }, 2143 | "through2": { 2144 | "version": "2.0.3", 2145 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", 2146 | "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", 2147 | "dev": true, 2148 | "requires": { 2149 | "readable-stream": "^2.1.5", 2150 | "xtend": "~4.0.1" 2151 | } 2152 | }, 2153 | "tiny-lru": { 2154 | "version": "1.6.1", 2155 | "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-1.6.1.tgz", 2156 | "integrity": "sha512-m8oyPnHjnQlbDk8+MCw33qRMp6+BxPxoayN9C743VToeyQ5zZV6F6vkklrYVEI0z9MQ3+jmc+22tKmvPg4gmoA==", 2157 | "dev": true 2158 | }, 2159 | "type-detect": { 2160 | "version": "4.0.7", 2161 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.7.tgz", 2162 | "integrity": "sha512-4Rh17pAMVdMWzktddFhISRnUnFIStObtUMNGzDwlA6w/77bmGv3aBbRdCmQR6IjzfkTo9otnW+2K/cDRhKSxDA==", 2163 | "dev": true 2164 | }, 2165 | "uri-js": { 2166 | "version": "4.2.2", 2167 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 2168 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 2169 | "dev": true, 2170 | "requires": { 2171 | "punycode": "^2.1.0" 2172 | } 2173 | }, 2174 | "util-deprecate": { 2175 | "version": "1.0.2", 2176 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2177 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 2178 | "dev": true 2179 | }, 2180 | "wrappy": { 2181 | "version": "1.0.2", 2182 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2183 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2184 | "dev": true 2185 | }, 2186 | "xtend": { 2187 | "version": "4.0.1", 2188 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 2189 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", 2190 | "dev": true 2191 | } 2192 | } 2193 | } 2194 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-graphql", 3 | "version": "4.0.0", 4 | "description": "Apollo server plugin for Fastify", 5 | "main": "out/FastifyGraphQL.js", 6 | "typings": "out/FastifyGraphQL.d.ts", 7 | "engines": { 8 | "npm": "=< ~5.1.0" 9 | }, 10 | "scripts": { 11 | "prepare": "./node_modules/typescript/bin/tsc --extendedDiagnostics", 12 | "release": "standard-version", 13 | "test": "./node_modules/mocha/bin/mocha $NODE_DEBUG_OPTION --require ts-node/register ./test/**/*.test.ts" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/sirsavary/fastify-graphql.git" 18 | }, 19 | "keywords": [ 20 | "fastify", 21 | "apollo", 22 | "graphql" 23 | ], 24 | "author": "Jesse Savary", 25 | "contributors": [ 26 | { 27 | "name": "Nickolena Coop", 28 | "url": "https://github.com/coopnd/" 29 | } 30 | ], 31 | "license": "MIT", 32 | "homepage": "https://github.com/sirsavary/fastify-graphql#readme", 33 | "bugs": { 34 | "url": "https://github.com/sirsavary/fastify-graphql/issues" 35 | }, 36 | "dependencies": { 37 | "apollo-server-core": "^1.3.2", 38 | "apollo-server-module-graphiql": "^1.3.2", 39 | "fastify-plugin": "^0.2.2" 40 | }, 41 | "devDependencies": { 42 | "@types/chai": "^4.1.2", 43 | "@types/graphql": "^0.12.4", 44 | "@types/mocha": "^2.2.48", 45 | "@types/sinon": "^4.3.0", 46 | "@types/supertest": "^2.0.4", 47 | "apollo-server-module-operation-store": "^1.3.2", 48 | "chai": "^4.1.2", 49 | "chai-graphql": "^4.0.0", 50 | "fastify": "^1.5.0", 51 | "graphql": "^0.12.3", 52 | "graphql-tools": "^2.21.0", 53 | "mocha": "^5.0.1", 54 | "nyc": "^11.4.1", 55 | "sinon": "^4.4.2", 56 | "standard-version": "^4.3.0", 57 | "supertest": "^3.0.0", 58 | "supertest-as-promised": "^4.0.2", 59 | "ts-node": "^5.0.1", 60 | "typescript": "^2.7.2" 61 | }, 62 | "peerDependencies": { 63 | "graphql": "^0.12" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/FastifyGraphQL.ts: -------------------------------------------------------------------------------- 1 | import FastifyPlugin from 'fastify-plugin'; 2 | 3 | import GraphQLPlugin from "./GraphQLPlugin"; 4 | import GraphiQLPlugin from "./GraphiQLPlugin"; 5 | import {FastifyInstance, Middleware} from "fastify"; 6 | 7 | // prevent fastify-plugin from stripping our prefix 8 | const fp = (plugin: Middleware) => 9 | FastifyPlugin(function (fastify: FastifyInstance, opts: object, next: Function) { 10 | fastify.register(plugin, opts); 11 | next() 12 | }, { 13 | fastify: '>=0.40.0', 14 | }); 15 | 16 | const graphqlFastify = fp(GraphQLPlugin); 17 | const graphiqlFastify = fp(GraphiQLPlugin); 18 | 19 | export { graphqlFastify, graphiqlFastify }; 20 | -------------------------------------------------------------------------------- /src/GraphQLPlugin.ts: -------------------------------------------------------------------------------- 1 | import { runHttpQuery, GraphQLOptions } from 'apollo-server-core'; 2 | import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; 3 | import { IncomingMessage, OutgoingMessage, Server } from 'http'; 4 | 5 | function GraphQLPlugin(fastify: FastifyInstance, pluginOptions: { prefix: string, graphql: Function | GraphQLOptions }, next: (err?: Error) => void) { 6 | if (!pluginOptions) throw new Error('Fastify GraphQL requires options!'); 7 | else if (!pluginOptions.prefix) throw new Error('Fastify GraphQL requires `prefix` to be part of passed options!'); 8 | else if (!pluginOptions.graphql) throw new Error('Fastify GraphQL requires `graphql` to be part of passed options!'); 9 | 10 | const handler = async (request: FastifyRequest, reply: FastifyReply) => { 11 | try { 12 | let method = request.req.method; 13 | const gqlResponse = await runHttpQuery([request, reply], { 14 | method : method, 15 | options: pluginOptions.graphql, 16 | query : method === 'POST' ? request.body : request.query, 17 | }); 18 | 19 | // bypass Fastify's response layer, so we can avoid having to 20 | // parse the serialized gqlResponse due to Fastify's internal 21 | // JSON serializer seeing our Content-Type header and assuming 22 | // the response payload is unserialized 23 | reply.sent = true 24 | reply.res.setHeader('Content-Type', 'application/json'); 25 | reply.res.end(gqlResponse); 26 | } catch (error) { 27 | if ('HttpQueryError' !== error.name) { 28 | throw error; 29 | } 30 | 31 | if (error.headers) { 32 | Object.keys(error.headers).forEach(header => { 33 | reply.header(header, error.headers[header]); 34 | }); 35 | } 36 | 37 | reply.code(error.statusCode); 38 | // error.message is actually a stringified GQL response, see 39 | // comment @ line 19 for why we bypass Fastify's response layer 40 | if (error.isGraphQLError) { 41 | reply.sent = true 42 | reply.res.setHeader('Content-Type', 'application/json'); 43 | reply.res.end(error.message); 44 | } else { 45 | reply.send(error.message); 46 | } 47 | } 48 | }; 49 | 50 | fastify.get('/', handler); 51 | fastify.post('/', handler); 52 | 53 | //TODO determine if this is really the best way to have Fastify not 404 on an invalid HTTP method 54 | fastify.setNotFoundHandler((request, reply) => { 55 | if (request.req.method !== 'POST' && request.req.method !== 'POST') { 56 | reply.code(405); 57 | reply.header('allow', ['GET', 'POST']); 58 | } else { 59 | reply.code(404); 60 | } 61 | reply.send(); 62 | }); 63 | 64 | next(); 65 | } 66 | 67 | export default GraphQLPlugin; 68 | -------------------------------------------------------------------------------- /src/GraphiQLPlugin.ts: -------------------------------------------------------------------------------- 1 | import URL from 'url'; 2 | import { GraphiQLData, resolveGraphiQLString } from 'apollo-server-module-graphiql'; 3 | import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; 4 | import { IncomingMessage, OutgoingMessage, Server } from 'http'; 5 | 6 | function GraphiQLPlugin(fastify: FastifyInstance, options: { prefix: string, graphiql: GraphiQLData | Function }, next: (err?: Error) => void) { 7 | options = Object.assign({ 8 | prefix : '/graphiql', 9 | graphiql: { 10 | endpointURL: '/graphql', 11 | }, 12 | }, options); 13 | 14 | const handler = async (request: FastifyRequest, reply: FastifyReply) => { 15 | try { 16 | const query = request.req.url && URL.parse(request.req.url, true).query; 17 | const graphiqlString = await resolveGraphiQLString(query, options.graphiql, [request, reply]); 18 | reply.type('text/html').send(graphiqlString); 19 | } catch (error) { 20 | reply.code(500); 21 | reply.send(error.message); 22 | } 23 | }; 24 | 25 | fastify.get('/', handler); 26 | 27 | next(); 28 | } 29 | 30 | export default GraphiQLPlugin; -------------------------------------------------------------------------------- /src/Typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'fastify-plugin'; 2 | 3 | // chai-graphql definitions 4 | declare module 'chai-graphql'; 5 | declare namespace Chai { 6 | interface Assertion { 7 | graphQL: Equal 8 | graphQLError(): void; 9 | } 10 | } -------------------------------------------------------------------------------- /test/FastifyGraphql.test.ts: -------------------------------------------------------------------------------- 1 | import Fastify from 'fastify'; 2 | import * as http from 'http'; 3 | import { graphiqlFastify, graphqlFastify } from '../src/FastifyGraphQL'; 4 | import testSuite, { CreateAppOptions, schema, } from './apollo-server-integration-testsuite'; 5 | 6 | async function createFastifyApp(options: CreateAppOptions = {}): Promise { 7 | const app = Fastify(); 8 | 9 | options.graphqlOptions = options.graphqlOptions || { schema: schema }; 10 | 11 | app.register(graphqlFastify, { 12 | prefix : '/graphql', 13 | graphql: options.graphqlOptions, 14 | }); 15 | 16 | if (options.graphiqlOptions) { 17 | app.register(graphiqlFastify, { 18 | prefix : '/graphiql', 19 | graphiql: options.graphiqlOptions, 20 | }); 21 | } 22 | 23 | await app.listen(0); 24 | return app.server; 25 | } 26 | 27 | async function destroyFastifyApp(app: http.Server): Promise { 28 | await app.close(); 29 | } 30 | 31 | describe('integration:Fastify', () => { 32 | testSuite(createFastifyApp, destroyFastifyApp); 33 | }); -------------------------------------------------------------------------------- /test/apollo-server-integration-testsuite.ts: -------------------------------------------------------------------------------- 1 | // borrowed from 2 | // https://github.com/apollographql/apollo-server/blob/master/packages/apollo-server-integration-testsuite/src/index.ts 3 | // because apollo-server-integration-testsuite is not a published module 4 | 5 | 6 | import { expect } from 'chai'; 7 | import { stub } from 'sinon'; 8 | import 'mocha'; 9 | 10 | import { 11 | GraphQLSchema, 12 | GraphQLObjectType, 13 | GraphQLString, 14 | GraphQLInt, 15 | GraphQLError, 16 | GraphQLNonNull, 17 | introspectionQuery, 18 | BREAK, 19 | } from 'graphql'; 20 | 21 | // tslint:disable-next-line 22 | const request = require('supertest'); 23 | 24 | import { GraphQLOptions } from 'apollo-server-core'; 25 | import * as GraphiQL from 'apollo-server-module-graphiql'; 26 | import { OperationStore } from 'apollo-server-module-operation-store'; 27 | 28 | const queryType = new GraphQLObjectType({ 29 | name: 'QueryType', 30 | fields: { 31 | testString: { 32 | type: GraphQLString, 33 | resolve() { 34 | return 'it works'; 35 | }, 36 | }, 37 | testStringWithDelay: { 38 | type: GraphQLString, 39 | args: { 40 | delay: { type: new GraphQLNonNull(GraphQLInt) }, 41 | }, 42 | resolve(root, args) { 43 | return new Promise((resolve, reject) => { 44 | setTimeout(() => resolve('it works'), args['delay']); 45 | }); 46 | }, 47 | }, 48 | testContext: { 49 | type: GraphQLString, 50 | resolve(_, args, context) { 51 | if (context.otherField) { 52 | return 'unexpected'; 53 | } 54 | context.otherField = true; 55 | return context.testField; 56 | }, 57 | }, 58 | testRootValue: { 59 | type: GraphQLString, 60 | resolve(rootValue) { 61 | return rootValue; 62 | }, 63 | }, 64 | testArgument: { 65 | type: GraphQLString, 66 | args: { echo: { type: GraphQLString } }, 67 | resolve(root, { echo }) { 68 | return `hello ${echo}`; 69 | }, 70 | }, 71 | testError: { 72 | type: GraphQLString, 73 | resolve() { 74 | throw new Error('Secret error message'); 75 | }, 76 | }, 77 | }, 78 | }); 79 | 80 | const personType = new GraphQLObjectType({ 81 | name: 'PersonType', 82 | fields: { 83 | firstName: { 84 | type: GraphQLString, 85 | }, 86 | lastName: { 87 | type: GraphQLString, 88 | }, 89 | }, 90 | }); 91 | 92 | const mutationType = new GraphQLObjectType({ 93 | name: 'MutationType', 94 | fields: { 95 | testMutation: { 96 | type: GraphQLString, 97 | args: { echo: { type: GraphQLString } }, 98 | resolve(root, { echo }) { 99 | return `not really a mutation, but who cares: ${echo}`; 100 | }, 101 | }, 102 | testPerson: { 103 | type: personType, 104 | args: { 105 | firstName: { 106 | type: new GraphQLNonNull(GraphQLString), 107 | }, 108 | lastName: { 109 | type: new GraphQLNonNull(GraphQLString), 110 | }, 111 | }, 112 | resolve(root, args) { 113 | return args; 114 | }, 115 | }, 116 | }, 117 | }); 118 | 119 | export const schema = new GraphQLSchema({ 120 | query: queryType, 121 | mutation: mutationType, 122 | }); 123 | 124 | export interface CreateAppOptions { 125 | excludeParser?: boolean; 126 | graphqlOptions?: 127 | | GraphQLOptions 128 | | { (): GraphQLOptions | Promise }; 129 | graphiqlOptions?: 130 | | GraphiQL.GraphiQLData 131 | | { (): GraphiQL.GraphiQLData | Promise }; 132 | } 133 | 134 | export interface CreateAppFunc { 135 | (options?: CreateAppOptions): any | Promise; 136 | } 137 | 138 | export interface DestroyAppFunc { 139 | (app: any): void | Promise; 140 | } 141 | 142 | export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => { 143 | describe('apolloServer', () => { 144 | let app; 145 | 146 | afterEach(async () => { 147 | if (app) { 148 | if (destroyApp) { 149 | await destroyApp(app); 150 | app = null; 151 | } else { 152 | app = null; 153 | } 154 | } 155 | }); 156 | 157 | describe('graphqlHTTP', () => { 158 | it('can be called with an options function', async () => { 159 | app = await createApp({ 160 | graphqlOptions: (): GraphQLOptions => ({ schema }), 161 | }); 162 | const expected = { 163 | testString: 'it works', 164 | }; 165 | const req = request(app) 166 | .post('/graphql') 167 | .send({ 168 | query: 'query test{ testString }', 169 | }); 170 | return req.then(res => { 171 | expect(res.status).to.equal(200); 172 | return expect(res.body.data).to.deep.equal(expected); 173 | }); 174 | }); 175 | 176 | it('can be called with an options function that returns a promise', async () => { 177 | app = await createApp({ 178 | graphqlOptions: () => { 179 | return new Promise(resolve => { 180 | resolve({ schema }); 181 | }); 182 | }, 183 | }); 184 | const expected = { 185 | testString: 'it works', 186 | }; 187 | const req = request(app) 188 | .post('/graphql') 189 | .send({ 190 | query: 'query test{ testString }', 191 | }); 192 | return req.then(res => { 193 | expect(res.status).to.equal(200); 194 | return expect(res.body.data).to.deep.equal(expected); 195 | }); 196 | }); 197 | 198 | it('throws an error if options promise is rejected', async () => { 199 | app = await createApp({ 200 | graphqlOptions: () => { 201 | return (Promise.reject({}) as any) as GraphQLOptions; 202 | }, 203 | }); 204 | const expected = 'Invalid options'; 205 | const req = request(app) 206 | .post('/graphql') 207 | .send({ 208 | query: 'query test{ testString }', 209 | }); 210 | return req.then(res => { 211 | expect(res.status).to.equal(500); 212 | return expect(res.error.text).to.contain(expected); 213 | }); 214 | }); 215 | 216 | it('rejects the request if the method is not POST or GET', async () => { 217 | app = await createApp({ excludeParser: true }); 218 | const req = request(app) 219 | .head('/graphql') 220 | .send(); 221 | return req.then(res => { 222 | expect(res.status).to.equal(405); 223 | expect(res.headers['allow']).to.equal('GET, POST'); 224 | }); 225 | }); 226 | 227 | it('throws an error if POST body is missing', async () => { 228 | app = await createApp({ excludeParser: true }); 229 | const req = request(app) 230 | .post('/graphql') 231 | .send(); 232 | return req.then(res => { 233 | expect(res.status).to.equal(500); 234 | return expect(res.error.text).to.contain('POST body missing.'); 235 | }); 236 | }); 237 | 238 | it('throws an error if GET query is missing', async () => { 239 | app = await createApp(); 240 | const req = request(app).get(`/graphql`); 241 | return req.then(res => { 242 | expect(res.status).to.equal(400); 243 | return expect(res.error.text).to.contain('GET query missing.'); 244 | }); 245 | }); 246 | 247 | it('can handle a basic GET request', async () => { 248 | app = await createApp(); 249 | const expected = { 250 | testString: 'it works', 251 | }; 252 | const query = { 253 | query: 'query test{ testString }', 254 | }; 255 | const req = request(app) 256 | .get('/graphql') 257 | .query(query); 258 | return req.then(res => { 259 | expect(res.status).to.equal(200); 260 | return expect(res.body.data).to.deep.equal(expected); 261 | }); 262 | }); 263 | 264 | it('can handle a basic implicit GET request', async () => { 265 | app = await createApp(); 266 | const expected = { 267 | testString: 'it works', 268 | }; 269 | const query = { 270 | query: '{ testString }', 271 | }; 272 | const req = request(app) 273 | .get('/graphql') 274 | .query(query); 275 | return req.then(res => { 276 | expect(res.status).to.equal(200); 277 | return expect(res.body.data).to.deep.equal(expected); 278 | }); 279 | }); 280 | 281 | it('throws error if trying to use mutation using GET request', async () => { 282 | app = await createApp(); 283 | const query = { 284 | query: 'mutation test{ testMutation(echo: "ping") }', 285 | }; 286 | const req = request(app) 287 | .get('/graphql') 288 | .query(query); 289 | return req.then(res => { 290 | expect(res.status).to.equal(405); 291 | expect(res.headers['allow']).to.equal('POST'); 292 | return expect(res.error.text).to.contain( 293 | 'GET supports only query operation', 294 | ); 295 | }); 296 | }); 297 | 298 | it('throws error if trying to use mutation with fragment using GET request', async () => { 299 | app = await createApp(); 300 | const query = { 301 | query: `fragment PersonDetails on PersonType { 302 | firstName 303 | } 304 | 305 | mutation test { 306 | testPerson(firstName: "Test", lastName: "Me") { 307 | ...PersonDetails 308 | } 309 | }`, 310 | }; 311 | const req = request(app) 312 | .get('/graphql') 313 | .query(query); 314 | return req.then(res => { 315 | expect(res.status).to.equal(405); 316 | expect(res.headers['allow']).to.equal('POST'); 317 | return expect(res.error.text).to.contain( 318 | 'GET supports only query operation', 319 | ); 320 | }); 321 | }); 322 | 323 | it('can handle a GET request with variables', async () => { 324 | app = await createApp(); 325 | const query = { 326 | query: 'query test($echo: String){ testArgument(echo: $echo) }', 327 | variables: JSON.stringify({ echo: 'world' }), 328 | }; 329 | const expected = { 330 | testArgument: 'hello world', 331 | }; 332 | const req = request(app) 333 | .get('/graphql') 334 | .query(query); 335 | return req.then(res => { 336 | expect(res.status).to.equal(200); 337 | return expect(res.body.data).to.deep.equal(expected); 338 | }); 339 | }); 340 | 341 | it('can handle a basic request', async () => { 342 | app = await createApp(); 343 | const expected = { 344 | testString: 'it works', 345 | }; 346 | const req = request(app) 347 | .post('/graphql') 348 | .send({ 349 | query: 'query test{ testString }', 350 | }); 351 | return req.then(res => { 352 | expect(res.status).to.equal(200); 353 | return expect(res.body.data).to.deep.equal(expected); 354 | }); 355 | }); 356 | 357 | it('can handle a request with variables', async () => { 358 | app = await createApp(); 359 | const expected = { 360 | testArgument: 'hello world', 361 | }; 362 | const req = request(app) 363 | .post('/graphql') 364 | .send({ 365 | query: 'query test($echo: String){ testArgument(echo: $echo) }', 366 | variables: { echo: 'world' }, 367 | }); 368 | return req.then(res => { 369 | expect(res.status).to.equal(200); 370 | return expect(res.body.data).to.deep.equal(expected); 371 | }); 372 | }); 373 | 374 | it('can handle a request with variables as string', async () => { 375 | app = await createApp(); 376 | const expected = { 377 | testArgument: 'hello world', 378 | }; 379 | const req = request(app) 380 | .post('/graphql') 381 | .send({ 382 | query: 'query test($echo: String!){ testArgument(echo: $echo) }', 383 | variables: '{ "echo": "world" }', 384 | }); 385 | return req.then(res => { 386 | expect(res.status).to.equal(200); 387 | return expect(res.body.data).to.deep.equal(expected); 388 | }); 389 | }); 390 | 391 | it('can handle a request with variables as an invalid string', async () => { 392 | app = await createApp(); 393 | const req = request(app) 394 | .post('/graphql') 395 | .send({ 396 | query: 'query test($echo: String!){ testArgument(echo: $echo) }', 397 | variables: '{ echo: "world" }', 398 | }); 399 | return req.then(res => { 400 | expect(res.status).to.equal(400); 401 | return expect(res.error.text).to.contain( 402 | 'Variables are invalid JSON.', 403 | ); 404 | }); 405 | }); 406 | 407 | it('can handle a request with operationName', async () => { 408 | app = await createApp(); 409 | const expected = { 410 | testString: 'it works', 411 | }; 412 | const req = request(app) 413 | .post('/graphql') 414 | .send({ 415 | query: ` 416 | query test($echo: String){ testArgument(echo: $echo) } 417 | query test2{ testString }`, 418 | variables: { echo: 'world' }, 419 | operationName: 'test2', 420 | }); 421 | return req.then(res => { 422 | expect(res.status).to.equal(200); 423 | return expect(res.body.data).to.deep.equal(expected); 424 | }); 425 | }); 426 | 427 | it('can handle introspection request', async () => { 428 | app = await createApp(); 429 | const req = request(app) 430 | .post('/graphql') 431 | .send({ query: introspectionQuery }); 432 | return req.then(res => { 433 | expect(res.status).to.equal(200); 434 | return expect( 435 | res.body.data.__schema.types[0].fields[0].name, 436 | ).to.equal('testString'); 437 | }); 438 | }); 439 | 440 | it('can handle batch requests', async () => { 441 | app = await createApp(); 442 | const expected = [ 443 | { 444 | data: { 445 | testString: 'it works', 446 | }, 447 | }, 448 | { 449 | data: { 450 | testArgument: 'hello yellow', 451 | }, 452 | }, 453 | ]; 454 | const req = request(app) 455 | .post('/graphql') 456 | .send([ 457 | { 458 | query: ` 459 | query test($echo: String){ testArgument(echo: $echo) } 460 | query test2{ testString }`, 461 | variables: { echo: 'world' }, 462 | operationName: 'test2', 463 | }, 464 | { 465 | query: ` 466 | query testX($echo: String){ testArgument(echo: $echo) }`, 467 | variables: { echo: 'yellow' }, 468 | operationName: 'testX', 469 | }, 470 | ]); 471 | return req.then(res => { 472 | expect(res.status).to.equal(200); 473 | return expect(res.body).to.deep.equal(expected); 474 | }); 475 | }); 476 | 477 | it('can handle batch requests', async () => { 478 | app = await createApp(); 479 | const expected = [ 480 | { 481 | data: { 482 | testString: 'it works', 483 | }, 484 | }, 485 | ]; 486 | const req = request(app) 487 | .post('/graphql') 488 | .send([ 489 | { 490 | query: ` 491 | query test($echo: String){ testArgument(echo: $echo) } 492 | query test2{ testString }`, 493 | variables: { echo: 'world' }, 494 | operationName: 'test2', 495 | }, 496 | ]); 497 | return req.then(res => { 498 | expect(res.status).to.equal(200); 499 | return expect(res.body).to.deep.equal(expected); 500 | }); 501 | }); 502 | 503 | it('can handle batch requests in parallel', async function() { 504 | // this test will fail due to timeout if running serially. 505 | const parallels = 100; 506 | const delayPerReq = 40; 507 | this.timeout(3000); 508 | 509 | app = await createApp(); 510 | const expected = Array(parallels).fill({ 511 | data: { testStringWithDelay: 'it works' }, 512 | }); 513 | const req = request(app) 514 | .post('/graphql') 515 | .send( 516 | Array(parallels).fill({ 517 | query: `query test($delay: Int!) { testStringWithDelay(delay: $delay) }`, 518 | operationName: 'test', 519 | variables: { delay: delayPerReq }, 520 | }), 521 | ); 522 | return req.then(res => { 523 | expect(res.status).to.equal(200); 524 | return expect(res.body).to.deep.equal(expected); 525 | }); 526 | }); 527 | 528 | it('clones batch context', async () => { 529 | app = await createApp({ 530 | graphqlOptions: { 531 | schema, 532 | context: { testField: 'expected' }, 533 | }, 534 | }); 535 | const expected = [ 536 | { 537 | data: { 538 | testContext: 'expected', 539 | }, 540 | }, 541 | { 542 | data: { 543 | testContext: 'expected', 544 | }, 545 | }, 546 | ]; 547 | const req = request(app) 548 | .post('/graphql') 549 | .send([ 550 | { 551 | query: 'query test{ testContext }', 552 | }, 553 | { 554 | query: 'query test{ testContext }', 555 | }, 556 | ]); 557 | return req.then(res => { 558 | expect(res.status).to.equal(200); 559 | return expect(res.body).to.deep.equal(expected); 560 | }); 561 | }); 562 | 563 | it('executes batch context if it is a function', async () => { 564 | let callCount = 0; 565 | app = await createApp({ 566 | graphqlOptions: { 567 | schema, 568 | context: () => { 569 | callCount++; 570 | return { testField: 'expected' }; 571 | }, 572 | }, 573 | }); 574 | const expected = [ 575 | { 576 | data: { 577 | testContext: 'expected', 578 | }, 579 | }, 580 | { 581 | data: { 582 | testContext: 'expected', 583 | }, 584 | }, 585 | ]; 586 | const req = request(app) 587 | .post('/graphql') 588 | .send([ 589 | { 590 | query: 'query test{ testContext }', 591 | }, 592 | { 593 | query: 'query test{ testContext }', 594 | }, 595 | ]); 596 | return req.then(res => { 597 | expect(callCount).to.equal(2); 598 | expect(res.status).to.equal(200); 599 | return expect(res.body).to.deep.equal(expected); 600 | }); 601 | }); 602 | 603 | it('can handle a request with a mutation', async () => { 604 | app = await createApp(); 605 | const expected = { 606 | testMutation: 'not really a mutation, but who cares: world', 607 | }; 608 | const req = request(app) 609 | .post('/graphql') 610 | .send({ 611 | query: 'mutation test($echo: String){ testMutation(echo: $echo) }', 612 | variables: { echo: 'world' }, 613 | }); 614 | return req.then(res => { 615 | expect(res.status).to.equal(200); 616 | return expect(res.body.data).to.deep.equal(expected); 617 | }); 618 | }); 619 | 620 | it('applies the formatResponse function', async () => { 621 | app = await createApp({ 622 | graphqlOptions: { 623 | schema, 624 | formatResponse(response) { 625 | response['extensions'] = { it: 'works' }; 626 | return response; 627 | }, 628 | }, 629 | }); 630 | const expected = { it: 'works' }; 631 | const req = request(app) 632 | .post('/graphql') 633 | .send({ 634 | query: 'mutation test($echo: String){ testMutation(echo: $echo) }', 635 | variables: { echo: 'world' }, 636 | }); 637 | return req.then(res => { 638 | expect(res.status).to.equal(200); 639 | return expect(res.body.extensions).to.deep.equal(expected); 640 | }); 641 | }); 642 | 643 | it('passes the context to the resolver', async () => { 644 | const expected = 'context works'; 645 | app = await createApp({ 646 | graphqlOptions: { 647 | schema, 648 | context: { testField: expected }, 649 | }, 650 | }); 651 | const req = request(app) 652 | .post('/graphql') 653 | .send({ 654 | query: 'query test{ testContext }', 655 | }); 656 | return req.then(res => { 657 | expect(res.status).to.equal(200); 658 | return expect(res.body.data.testContext).to.equal(expected); 659 | }); 660 | }); 661 | 662 | it('passes the rootValue to the resolver', async () => { 663 | const expected = 'it passes rootValue'; 664 | app = await createApp({ 665 | graphqlOptions: { 666 | schema, 667 | rootValue: expected, 668 | }, 669 | }); 670 | const req = request(app) 671 | .post('/graphql') 672 | .send({ 673 | query: 'query test{ testRootValue }', 674 | }); 675 | return req.then(res => { 676 | expect(res.status).to.equal(200); 677 | return expect(res.body.data.testRootValue).to.equal(expected); 678 | }); 679 | }); 680 | 681 | it('returns errors', async () => { 682 | const expected = 'Secret error message'; 683 | app = await createApp({ 684 | graphqlOptions: { 685 | schema, 686 | }, 687 | }); 688 | const req = request(app) 689 | .post('/graphql') 690 | .send({ 691 | query: 'query test{ testError }', 692 | }); 693 | return req.then(res => { 694 | expect(res.status).to.equal(200); 695 | return expect(res.body.errors[0].message).to.equal(expected); 696 | }); 697 | }); 698 | 699 | it('applies formatError if provided', async () => { 700 | const expected = '--blank--'; 701 | app = await createApp({ 702 | graphqlOptions: { 703 | schema, 704 | formatError: err => ({ message: expected }), 705 | }, 706 | }); 707 | const req = request(app) 708 | .post('/graphql') 709 | .send({ 710 | query: 'query test{ testError }', 711 | }); 712 | return req.then(res => { 713 | expect(res.status).to.equal(200); 714 | return expect(res.body.errors[0].message).to.equal(expected); 715 | }); 716 | }); 717 | 718 | it('sends internal server error when formatError fails', async () => { 719 | app = await createApp({ 720 | graphqlOptions: { 721 | schema, 722 | formatError: err => { 723 | throw new Error('I should be caught'); 724 | }, 725 | }, 726 | }); 727 | const req = request(app) 728 | .post('/graphql') 729 | .send({ 730 | query: 'query test{ testError }', 731 | }); 732 | return req.then(res => { 733 | return expect(res.body.errors[0].message).to.equal( 734 | 'Internal server error', 735 | ); 736 | }); 737 | }); 738 | 739 | it('sends stack trace to error if debug mode is set', async () => { 740 | const expected = /at resolveFieldValueOrError/; 741 | const stackTrace = []; 742 | const origError = console.error; 743 | console.error = (...args) => stackTrace.push(args); 744 | app = await createApp({ 745 | graphqlOptions: { 746 | schema, 747 | debug: true, 748 | }, 749 | }); 750 | const req = request(app) 751 | .post('/graphql') 752 | .send({ 753 | query: 'query test{ testError }', 754 | }); 755 | return req.then(res => { 756 | console.error = origError; 757 | return expect(stackTrace[0][0]).to.match(expected); 758 | }); 759 | }); 760 | 761 | it('sends stack trace to error log if debug mode is set', async () => { 762 | const logStub = stub(console, 'error'); 763 | const expected = /at resolveFieldValueOrError/; 764 | app = await createApp({ 765 | graphqlOptions: { 766 | schema, 767 | debug: true, 768 | }, 769 | }); 770 | const req = request(app) 771 | .post('/graphql') 772 | .send({ 773 | query: 'query test{ testError }', 774 | }); 775 | return req.then(res => { 776 | logStub.restore(); 777 | expect(logStub.callCount).to.equal(1); 778 | return expect(logStub.getCall(0).args[0]).to.match(expected); 779 | }); 780 | }); 781 | 782 | it('applies additional validationRules', async () => { 783 | const expected = 'alwaysInvalidRule was really invalid!'; 784 | const alwaysInvalidRule = function(context) { 785 | return { 786 | enter() { 787 | context.reportError(new GraphQLError(expected)); 788 | return BREAK; 789 | }, 790 | }; 791 | }; 792 | app = await createApp({ 793 | graphqlOptions: { 794 | schema, 795 | validationRules: [alwaysInvalidRule], 796 | }, 797 | }); 798 | const req = request(app) 799 | .post('/graphql') 800 | .send({ 801 | query: 'query test{ testString }', 802 | }); 803 | return req.then(res => { 804 | expect(res.status).to.equal(400); 805 | return expect(res.body.errors[0].message).to.equal(expected); 806 | }); 807 | }); 808 | }); 809 | 810 | describe('renderGraphiQL', () => { 811 | it('presents GraphiQL when accepting HTML', async () => { 812 | app = await createApp({ 813 | graphiqlOptions: { 814 | endpointURL: '/graphql', 815 | }, 816 | }); 817 | 818 | const req = request(app) 819 | .get('/graphiql') 820 | .query('query={test}') 821 | .set('Accept', 'text/html'); 822 | return req.then(response => { 823 | expect(response.status).to.equal(200); 824 | expect(response.type).to.equal('text/html'); 825 | expect(response.text).to.include('{test}'); 826 | expect(response.text).to.include('/graphql'); 827 | expect(response.text).to.include('graphiql.min.js'); 828 | }); 829 | }); 830 | 831 | it('allows options to be a function', async () => { 832 | app = await createApp({ 833 | graphiqlOptions: () => ({ 834 | endpointURL: '/graphql', 835 | }), 836 | }); 837 | 838 | const req = request(app) 839 | .get('/graphiql') 840 | .set('Accept', 'text/html'); 841 | return req.then(response => { 842 | expect(response.status).to.equal(200); 843 | }); 844 | }); 845 | 846 | it('handles options function errors', async () => { 847 | app = await createApp({ 848 | graphiqlOptions: () => { 849 | throw new Error('I should be caught'); 850 | }, 851 | }); 852 | 853 | const req = request(app) 854 | .get('/graphiql') 855 | .set('Accept', 'text/html'); 856 | return req.then(response => { 857 | expect(response.status).to.equal(500); 858 | }); 859 | }); 860 | 861 | it('presents options variables', async () => { 862 | app = await createApp({ 863 | graphiqlOptions: { 864 | endpointURL: '/graphql', 865 | variables: { key: 'optionsValue' }, 866 | }, 867 | }); 868 | 869 | const req = request(app) 870 | .get('/graphiql') 871 | .set('Accept', 'text/html'); 872 | return req.then(response => { 873 | expect(response.status).to.equal(200); 874 | expect(response.text.replace(/\s/g, '')).to.include( 875 | 'variables:"{\\n\\"key\\":\\"optionsValue\\"\\n}"', 876 | ); 877 | }); 878 | }); 879 | 880 | it('presents query variables over options variables', async () => { 881 | app = await createApp({ 882 | graphiqlOptions: { 883 | endpointURL: '/graphql', 884 | variables: { key: 'optionsValue' }, 885 | }, 886 | }); 887 | 888 | const req = request(app) 889 | .get('/graphiql?variables={"key":"queryValue"}') 890 | .set('Accept', 'text/html'); 891 | return req.then(response => { 892 | expect(response.status).to.equal(200); 893 | expect(response.text.replace(/\s/g, '')).to.include( 894 | 'variables:"{\\n\\"key\\":\\"queryValue\\"\\n}"', 895 | ); 896 | }); 897 | }); 898 | }); 899 | 900 | describe('stored queries', () => { 901 | it('works with formatParams', async () => { 902 | const store = new OperationStore(schema); 903 | store.put('query testquery{ testString }'); 904 | app = await createApp({ 905 | graphqlOptions: { 906 | schema, 907 | formatParams(params) { 908 | params['query'] = store.get(params.operationName); 909 | return params; 910 | }, 911 | }, 912 | }); 913 | const expected = { testString: 'it works' }; 914 | const req = request(app) 915 | .post('/graphql') 916 | .send({ 917 | operationName: 'testquery', 918 | }); 919 | return req.then(res => { 920 | expect(res.status).to.equal(200); 921 | return expect(res.body.data).to.deep.equal(expected); 922 | }); 923 | }); 924 | 925 | it('can reject non-whitelisted queries', async () => { 926 | const store = new OperationStore(schema); 927 | store.put('query testquery{ testString }'); 928 | app = await createApp({ 929 | graphqlOptions: { 930 | schema, 931 | formatParams(params) { 932 | if (params.query) { 933 | throw new Error('Must not provide query, only operationName'); 934 | } 935 | params['query'] = store.get(params.operationName); 936 | return params; 937 | }, 938 | }, 939 | }); 940 | const expected = [ 941 | { 942 | data: { 943 | testString: 'it works', 944 | }, 945 | }, 946 | { 947 | errors: [ 948 | { 949 | message: 'Must not provide query, only operationName', 950 | }, 951 | ], 952 | }, 953 | ]; 954 | 955 | const req = request(app) 956 | .post('/graphql') 957 | .send([ 958 | { 959 | operationName: 'testquery', 960 | }, 961 | { 962 | query: '{ testString }', 963 | }, 964 | ]); 965 | return req.then(res => { 966 | expect(res.status).to.equal(200); 967 | return expect(res.body).to.deep.equal(expected); 968 | }); 969 | }); 970 | }); 971 | 972 | describe('server setup', () => { 973 | it('throws error on 404 routes', async () => { 974 | app = await createApp(); 975 | 976 | const query = { 977 | query: '{ testString }', 978 | }; 979 | const req = request(app) 980 | .get('/bogus-route') 981 | .query(query); 982 | return req.then(res => { 983 | expect(res.status).to.equal(404); 984 | }); 985 | }); 986 | }); 987 | }); 988 | }; 989 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "experimentalDecorators": true, 8 | "lib": ["es6", "esnext", "dom"], 9 | "declaration": true, 10 | "noImplicitAny": true, 11 | "allowSyntheticDefaultImports": true, 12 | "esModuleInterop": true, 13 | "outDir": "./out/" 14 | }, 15 | "include": [ 16 | "./src/**/*.ts" 17 | ], 18 | "exclude": [ 19 | "node_modules", 20 | "./out" 21 | ], 22 | "compileOnSave": true 23 | } --------------------------------------------------------------------------------