├── .gitignore ├── LICENSE ├── README.md ├── example.js ├── index.js ├── package-lock.json ├── package.json └── test ├── descs-to-fields.js └── integration.js /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | pids 5 | *.pid 6 | *.seed 7 | lib-cov 8 | coverage 9 | .nyc_output 10 | .grunt 11 | .lock-wscript 12 | build/Release 13 | node_modules 14 | jspm_packages 15 | .npm 16 | .node_repl_history 17 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Craig Spaeth 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 | # joiql 2 | 3 | Make [GraphQL](http://graphql.org/) schema creation and data validation easy with [Joi](https://github.com/hapijs/joi). 4 | 5 | ## Example 6 | 7 | Run this using `node example`... 8 | 9 | ````javascript 10 | const joiql = require('joiql') 11 | const { object, string, number, array, date } = require('joi') 12 | const app = require('express')() 13 | const graphqlHTTP = require('express-graphql') 14 | 15 | // Joi Schemas 16 | const Film = object({ 17 | title: string(), 18 | producers: array().items(string()), 19 | release_date: date() 20 | }) 21 | 22 | const Person = object({ 23 | name: string(), 24 | films: array().items(Film) 25 | }).meta({ 26 | args: { id: number().required() }, 27 | resolve: (root, args, req, ast) => ({ name: 'Spike Jonze' }) 28 | }) 29 | 30 | // Convert Joi schemas to GraphQL 31 | const schema = joiql({ 32 | query: { 33 | person: Person, 34 | film: Film 35 | } 36 | }) 37 | 38 | // Mount schema to express 39 | app.use('/', graphqlHTTP({ schema: schema, graphiql: true })) 40 | app.listen(3000, () => console.log('listening on 3000')) 41 | ```` 42 | 43 | ## Breaking it down 44 | 45 | First, define some schemas using [Joi](https://github.com/hapijs/joi). 46 | 47 | ````javascript 48 | const { object, string, number, array, date } = require('joi') 49 | 50 | const Film = object({ 51 | title: string(), 52 | producers: array().items(string()), 53 | releaseDate: date() 54 | }) 55 | 56 | const Person = object({ 57 | name: string(), 58 | films: array().items(Film) 59 | }) 60 | ```` 61 | 62 | JoiQL uses the [`meta` property](https://github.com/hapijs/joi/blob/v9.0.4/API.md#anymetameta) to extend GraphQL fields. Use `meta.args` to define GraphQL arguments (adding automatic 63 | input validation), and `meta.name` to declare the `GraphQLObjectType` type name (without it JoiQL will automatically 64 | add a "Anon" type name). 65 | 66 | ````javascript 67 | Person.meta({ 68 | name: 'Person', 69 | args: { id: number().required() } 70 | }) 71 | ```` 72 | 73 | Then create a JoiQL `api` object from the Joi schemas and expose a GraphQL.js schema object for mounting into a server like Express. 74 | 75 | ````javascript 76 | const { graphql } = require('graphql') 77 | const joiql = require('../') 78 | 79 | const api = joiql({ 80 | query: { 81 | person: Person, 82 | film: Film 83 | } 84 | }) 85 | 86 | graphql(api.schema, ...) 87 | ```` 88 | 89 | ## TODO 90 | 91 | * Figure out how to do circular dependencies (ideally with Joi `lazy`) 92 | 93 | ## Contributing 94 | 95 | Please fork the project and submit a pull request with tests. Install node modules `npm install` and run tests with `npm test`. 96 | 97 | ## License 98 | 99 | MIT 100 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const joiql = require('./') 2 | const { object, string, number, array, date } = require('joi') 3 | const app = require('express')() 4 | const graphqlHTTP = require('express-graphql') 5 | 6 | // Joi Schemas 7 | const Film = object({ 8 | title: string(), 9 | producers: array().items(string()).meta({ 10 | resolve: () => [''] 11 | }), 12 | release_date: date() 13 | }).meta({ 14 | resolve: () => ({ title: 'Her' }) 15 | }) 16 | 17 | const Person = object({ 18 | name: string(), 19 | films: array().items(Film) 20 | }).meta({ 21 | args: { id: number().required() }, 22 | resolve: () => ({ name: 'Spike Jonze' }) 23 | }) 24 | 25 | // Convert Joi schemas to GraphQL 26 | const schema = joiql({ 27 | query: { 28 | person: Person, 29 | film: Film 30 | } 31 | }) 32 | 33 | // Mount schema to express 34 | app.use('/', graphqlHTTP({ schema: schema, graphiql: true })) 35 | app.listen(3000, () => console.log('listening on 3000')) 36 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // 2 | // Takes an object of { key: JoiSchema } pairs to generate a GraphQL Schema. 3 | // 4 | const Joi = require('joi') 5 | const { 6 | uniqueId, 7 | map, 8 | find, 9 | capitalize, 10 | flatten, 11 | isEmpty, 12 | mapValues, 13 | omit, 14 | omitBy, 15 | isNull, 16 | fromPairs, 17 | compact, 18 | first 19 | } = require('lodash') 20 | const { 21 | GraphQLSchema, 22 | GraphQLObjectType, 23 | GraphQLString, 24 | GraphQLFloat, 25 | GraphQLInt, 26 | GraphQLInputObjectType, 27 | GraphQLList, 28 | GraphQLUnionType, 29 | GraphQLBoolean, 30 | GraphQLNonNull 31 | } = require('graphql') 32 | 33 | // Convenience helpers to determine a Joi schema's 34 | // "presence", e.g. required or forbidden 35 | const presence = (desc, name) => 36 | desc.flags && 37 | desc.flags.presence && 38 | desc.flags.presence === name 39 | 40 | // Cache converted types by their `meta({ name: '' })` property so we 41 | // don't end up with a litter of anonymously generated GraphQL types 42 | const cachedTypes = {} 43 | 44 | // Maps a Joi description to a GraphQL type. `isInput` is used to determine 45 | // when to use, say, GraphQLInputObjectType vs. GraphQLObjectType—useful in 46 | // cases such as args and mutations. 47 | const descToType = (schema, isInput) => { 48 | let desc = (schema && schema.describe ? schema.describe() : schema) 49 | let typeName = getTypeName(schema, isInput) 50 | let required = isInput && presence(desc, 'required') 51 | const type = { 52 | boolean: () => GraphQLBoolean, 53 | date: () => GraphQLString, 54 | string: () => GraphQLString, 55 | number: () => { 56 | const isInteger = !!find(desc.rules, { name: 'integer' }) 57 | return isInteger ? GraphQLInt : GraphQLFloat 58 | }, 59 | object: () => { 60 | if (cachedTypes[typeName]) return cachedTypes[typeName] 61 | let type 62 | if (isInput) { 63 | type = new GraphQLInputObjectType({ 64 | name: typeName, 65 | description: desc.description, 66 | fields: () => omitBy(mapValues(desc.children, (child, k) => { 67 | if (presence(child, 'forbidden')) return null 68 | return { type: descToType(Joi.reach(schema, k), true) } 69 | }), isNull) 70 | }) 71 | } else { 72 | type = new GraphQLObjectType({ 73 | name: typeName, 74 | description: desc.description, 75 | fields: () => descsToFields(mapValues(desc.children, (child, k) => Joi.reach(schema, k))) 76 | }) 77 | } 78 | cachedTypes[typeName] = type 79 | return type 80 | }, 81 | array: () => { 82 | let type 83 | const items = schema._inner.items.filter((item) => !presence(item.describe(), 'forbidden')) 84 | if (items.length === 1) { 85 | type = descToType(items[0], isInput) 86 | } else { 87 | type = makeArrayAlternativeType(cachedTypes, isInput, typeName, desc, items) 88 | } 89 | if (!cachedTypes[typeName]) cachedTypes[typeName] = type 90 | return new GraphQLList(type) 91 | }, 92 | alternatives: () => { 93 | let type 94 | const alternatives = map(schema._inner.matches, 'schema') 95 | .filter((a) => !presence(a.describe(), 'forbidden')) 96 | type = makeArrayAlternativeType(cachedTypes, isInput, typeName, desc, alternatives) 97 | if (!cachedTypes[typeName]) cachedTypes[typeName] = type 98 | return type 99 | }, 100 | lazy: () => descToType(schema._flags.lazy(), isInput) 101 | }[desc.type]() 102 | 103 | return required ? new GraphQLNonNull(type) : type 104 | } 105 | 106 | const makeArrayAlternativeType = (cachedTypes, isInput, typeName, desc, items) => { 107 | const types = items.map((item) => descToType(item, isInput)) 108 | if (cachedTypes[typeName]) { 109 | return cachedTypes[typeName] 110 | } else if (isInput) { 111 | const children = fromPairs(flatten(items.map((item) => map(item._inner.children, (c) => [c.key, c.schema])))) 112 | 113 | // Strip resolvers from generated types 114 | const fields = mapValues(descsToFields(children), (field) => { 115 | return omit(field, 'resolve'); 116 | }); 117 | 118 | return new GraphQLInputObjectType({ 119 | name: typeName, 120 | description: desc.description, 121 | fields: fields 122 | }) 123 | } else { 124 | return new GraphQLUnionType({ 125 | name: typeName, 126 | description: desc.description, 127 | types: types, 128 | resolveType: (val) => 129 | find(map(items, (item, i) => { 130 | try { 131 | return Joi.attempt(val, item) 132 | } catch (e) {} 133 | })) 134 | }) 135 | } 136 | } 137 | 138 | // Convert a Joi description's `meta({ args: {} })` to a GraphQL field's 139 | // arguments 140 | const descToArgs = (schema) => { 141 | const desc = schema.describe() 142 | const argsSchema = first(compact(map(desc.meta, 'args'))) 143 | return argsSchema && omitBy(mapValues(argsSchema, (schema) => { 144 | if (presence(schema.describe(), 'forbidden')) return null 145 | return { 146 | type: descToType(schema, true) 147 | } 148 | }), isNull) 149 | } 150 | 151 | // Wraps a resolve function specifid in a Joi schema to add validation. 152 | const validatedResolve = (schema) => (source, args, context, opts) => { 153 | const desc = schema.describe() 154 | const resolve = desc.meta && first(compact(map(desc.meta, 'resolve'))) 155 | if (args && !isEmpty(args)) { 156 | const argsSchema = first(compact(map(desc.meta, 'args'))) 157 | const value = Joi.attempt(args, argsSchema) 158 | return resolve(source, value, context, opts) 159 | } 160 | if (resolve) return resolve(source, args, context, opts) 161 | else return source && source[opts.fieldNodes[0].name.value] 162 | } 163 | 164 | // Convert a hash of descriptions into an object appropriate to put in a 165 | // GraphQL.js `fields` key. 166 | const descsToFields = (schemas, resolveMiddlewares = () => {}) => 167 | omitBy(mapValues(schemas, (schema) => { 168 | const desc = (schema && schema.describe ? schema.describe() : schema) 169 | const cleanSchema = clean(schema) 170 | if (presence(desc, 'forbidden')) return null 171 | return { 172 | type: descToType(cleanSchema), 173 | args: descToArgs(cleanSchema), 174 | description: desc.description || '', 175 | resolve: validatedResolve(cleanSchema) 176 | } 177 | }), isNull) 178 | 179 | // Converts the { key: JoiSchema } pairs to a GraphQL.js schema object 180 | module.exports = (jois) => { 181 | const attrs = {} 182 | if (jois.query) { 183 | attrs.query = new GraphQLObjectType({ 184 | name: 'RootQueryType', 185 | fields: descsToFields(jois.query) 186 | }) 187 | } 188 | if (jois.mutation) { 189 | attrs.mutation = new GraphQLObjectType({ 190 | name: 'RootMutationType', 191 | fields: descsToFields(jois.mutation) 192 | }) 193 | } 194 | return new GraphQLSchema(attrs) 195 | } 196 | 197 | const clean = (schema) => { 198 | const desc = schema.describe() 199 | switch (desc.type) { 200 | case 'lazy': 201 | return clean(schema._flags.lazy()) 202 | } 203 | 204 | return schema 205 | } 206 | 207 | const getTypeName = (schema, isInput) => { 208 | const desc = schema.describe() 209 | let typeName = first(compact(map(desc.meta, 'typeName'))) 210 | 211 | if (!typeName) { 212 | switch (desc.type) { 213 | case 'array': 214 | const items = schema._inner.items.filter((item) => !presence(item.describe(), 'forbidden')) 215 | if (items.length > 1) { 216 | typeName = map(items, (d) => { 217 | const name = ( 218 | (d.meta && capitalize(d.meta.name)) || 219 | capitalize(d.type) || 220 | 'Anon' + uniqueId() 221 | ) 222 | return (isInput ? 'Input' : '') + name 223 | }).join('Or') 224 | } 225 | break 226 | } 227 | 228 | if (!typeName) { 229 | typeName = ( 230 | (isInput ? 'Input' : '') + 231 | (first(compact(map(desc.meta, 'name'))) || 'Anon' + uniqueId()) 232 | ) 233 | } 234 | 235 | schema._meta.push({ typeName: typeName }) 236 | } 237 | 238 | return typeName 239 | } 240 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joiql", 3 | "version": "0.1.5", 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "accepts": { 7 | "version": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", 8 | "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", 9 | "dev": true 10 | }, 11 | "acorn": { 12 | "version": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", 13 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", 14 | "dev": true 15 | }, 16 | "acorn-jsx": { 17 | "version": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", 18 | "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", 19 | "dev": true 20 | }, 21 | "ajv": { 22 | "version": "https://registry.npmjs.org/ajv/-/ajv-4.10.3.tgz", 23 | "integrity": "sha1-Pk/qlnWxV954iLgN0O1zW4PyjhE=", 24 | "dev": true 25 | }, 26 | "ajv-keywords": { 27 | "version": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.0.tgz", 28 | "integrity": "sha1-wR5oWer/+D4Nr8QWkpRy7KlGqiw=", 29 | "dev": true 30 | }, 31 | "ansi-escapes": { 32 | "version": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", 33 | "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", 34 | "dev": true 35 | }, 36 | "ansi-regex": { 37 | "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", 38 | "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=", 39 | "dev": true 40 | }, 41 | "ansi-styles": { 42 | "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 43 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 44 | "dev": true 45 | }, 46 | "argparse": { 47 | "version": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", 48 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", 49 | "dev": true 50 | }, 51 | "array-flatten": { 52 | "version": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 53 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", 54 | "dev": true 55 | }, 56 | "array-union": { 57 | "version": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 58 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 59 | "dev": true 60 | }, 61 | "array-uniq": { 62 | "version": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 63 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", 64 | "dev": true 65 | }, 66 | "arrify": { 67 | "version": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 68 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 69 | "dev": true 70 | }, 71 | "async": { 72 | "version": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 73 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 74 | "dev": true 75 | }, 76 | "balanced-match": { 77 | "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", 78 | "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", 79 | "dev": true 80 | }, 81 | "brace-expansion": { 82 | "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", 83 | "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=", 84 | "dev": true 85 | }, 86 | "buffer-shims": { 87 | "version": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", 88 | "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", 89 | "dev": true 90 | }, 91 | "bytes": { 92 | "version": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", 93 | "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=", 94 | "dev": true 95 | }, 96 | "caller-path": { 97 | "version": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", 98 | "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", 99 | "dev": true 100 | }, 101 | "callsites": { 102 | "version": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", 103 | "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", 104 | "dev": true 105 | }, 106 | "chalk": { 107 | "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 108 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 109 | "dev": true, 110 | "dependencies": { 111 | "supports-color": { 112 | "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 113 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 114 | "dev": true 115 | } 116 | } 117 | }, 118 | "circular-json": { 119 | "version": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", 120 | "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0=", 121 | "dev": true 122 | }, 123 | "cli-cursor": { 124 | "version": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", 125 | "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", 126 | "dev": true 127 | }, 128 | "cli-width": { 129 | "version": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", 130 | "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=", 131 | "dev": true 132 | }, 133 | "clone": { 134 | "version": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", 135 | "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", 136 | "dev": true 137 | }, 138 | "co": { 139 | "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 140 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 141 | "dev": true 142 | }, 143 | "code-point-at": { 144 | "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 145 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", 146 | "dev": true 147 | }, 148 | "combined-stream": { 149 | "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", 150 | "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", 151 | "dev": true 152 | }, 153 | "commander": { 154 | "version": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", 155 | "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", 156 | "dev": true 157 | }, 158 | "component-emitter": { 159 | "version": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", 160 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", 161 | "dev": true 162 | }, 163 | "concat-map": { 164 | "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 165 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 166 | "dev": true 167 | }, 168 | "concat-stream": { 169 | "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", 170 | "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", 171 | "dev": true 172 | }, 173 | "content-disposition": { 174 | "version": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.1.tgz", 175 | "integrity": "sha1-h0dsamfI2qh+Muh2Ft+IO6f7Bxs=", 176 | "dev": true 177 | }, 178 | "content-type": { 179 | "version": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", 180 | "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=", 181 | "dev": true 182 | }, 183 | "cookie": { 184 | "version": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 185 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", 186 | "dev": true 187 | }, 188 | "cookie-signature": { 189 | "version": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 190 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", 191 | "dev": true 192 | }, 193 | "cookiejar": { 194 | "version": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.0.tgz", 195 | "integrity": "sha1-hlSWiVObbQ4mm2Y3owS+UIGU2Jg=", 196 | "dev": true 197 | }, 198 | "core-util-is": { 199 | "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 200 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 201 | "dev": true 202 | }, 203 | "d": { 204 | "version": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", 205 | "integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=", 206 | "dev": true 207 | }, 208 | "debug": { 209 | "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 210 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 211 | "dev": true 212 | }, 213 | "debug-log": { 214 | "version": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", 215 | "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", 216 | "dev": true 217 | }, 218 | "deep-is": { 219 | "version": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 220 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 221 | "dev": true 222 | }, 223 | "defaults": { 224 | "version": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", 225 | "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", 226 | "dev": true 227 | }, 228 | "deglob": { 229 | "version": "https://registry.npmjs.org/deglob/-/deglob-1.1.2.tgz", 230 | "integrity": "sha1-dtV3wl/j9zKUEqK1nq3qV6xQDj8=", 231 | "dev": true, 232 | "dependencies": { 233 | "glob": { 234 | "version": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 235 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 236 | "dev": true 237 | }, 238 | "minimatch": { 239 | "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", 240 | "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", 241 | "dev": true 242 | } 243 | } 244 | }, 245 | "del": { 246 | "version": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", 247 | "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", 248 | "dev": true 249 | }, 250 | "delayed-stream": { 251 | "version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 252 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 253 | "dev": true 254 | }, 255 | "depd": { 256 | "version": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", 257 | "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=", 258 | "dev": true 259 | }, 260 | "destroy": { 261 | "version": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 262 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 263 | "dev": true 264 | }, 265 | "diff": { 266 | "version": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", 267 | "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", 268 | "dev": true 269 | }, 270 | "doctrine": { 271 | "version": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", 272 | "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", 273 | "dev": true 274 | }, 275 | "ee-first": { 276 | "version": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 277 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 278 | "dev": true 279 | }, 280 | "encodeurl": { 281 | "version": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", 282 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", 283 | "dev": true 284 | }, 285 | "es5-ext": { 286 | "version": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz", 287 | "integrity": "sha1-qoRkHU23a2Krul5F/YBey6sUAEc=", 288 | "dev": true 289 | }, 290 | "es6-iterator": { 291 | "version": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz", 292 | "integrity": "sha1-vZaFZ9YWNeM8C4BydhPJy0sJa6w=", 293 | "dev": true 294 | }, 295 | "es6-map": { 296 | "version": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.4.tgz", 297 | "integrity": "sha1-o0sUe+IkdzpNfagHJ5TO+jYyuJc=", 298 | "dev": true 299 | }, 300 | "es6-set": { 301 | "version": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.4.tgz", 302 | "integrity": "sha1-lRa2dhwpZLkv9HlFYjOiR9xwfOg=", 303 | "dev": true 304 | }, 305 | "es6-symbol": { 306 | "version": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz", 307 | "integrity": "sha1-lEgcZV56fK2C66gy2X1UM0ltf/o=", 308 | "dev": true 309 | }, 310 | "es6-weak-map": { 311 | "version": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.1.tgz", 312 | "integrity": "sha1-DSu9iCfrX7S6j5f7/qUNQ9sh6oE=", 313 | "dev": true 314 | }, 315 | "escape-html": { 316 | "version": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 317 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 318 | "dev": true 319 | }, 320 | "escape-string-regexp": { 321 | "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", 322 | "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", 323 | "dev": true 324 | }, 325 | "escope": { 326 | "version": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", 327 | "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", 328 | "dev": true 329 | }, 330 | "eslint": { 331 | "version": "https://registry.npmjs.org/eslint/-/eslint-2.10.2.tgz", 332 | "integrity": "sha1-sjCUgv7wQ9MgM2WjIShebM4Bw9c=", 333 | "dev": true, 334 | "dependencies": { 335 | "glob": { 336 | "version": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 337 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 338 | "dev": true 339 | }, 340 | "minimatch": { 341 | "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", 342 | "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", 343 | "dev": true 344 | } 345 | } 346 | }, 347 | "eslint-config-standard": { 348 | "version": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-5.3.1.tgz", 349 | "integrity": "sha1-WRyWkVF0QTL1YdO5FagS6kE/5JA=", 350 | "dev": true 351 | }, 352 | "eslint-config-standard-jsx": { 353 | "version": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-1.2.1.tgz", 354 | "integrity": "sha1-DRmxcF8K1INj7yqLv6cd8BLZibM=", 355 | "dev": true 356 | }, 357 | "eslint-plugin-promise": { 358 | "version": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-1.3.2.tgz", 359 | "integrity": "sha1-/OMy1vX/UjIApTdwSGPsPCQiunw=", 360 | "dev": true 361 | }, 362 | "eslint-plugin-react": { 363 | "version": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-5.2.2.tgz", 364 | "integrity": "sha1-fbBo4fVIf2hx5N7vNqOBwwPqwWE=", 365 | "dev": true 366 | }, 367 | "eslint-plugin-standard": { 368 | "version": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-1.3.3.tgz", 369 | "integrity": "sha1-owhUUVI0MedvQJxwy4+U4yvw7H8=", 370 | "dev": true 371 | }, 372 | "espree": { 373 | "version": "https://registry.npmjs.org/espree/-/espree-3.1.4.tgz", 374 | "integrity": "sha1-BybXrIOvl6fISY2ps2OjYJ0qaKE=", 375 | "dev": true 376 | }, 377 | "esprima": { 378 | "version": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", 379 | "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", 380 | "dev": true 381 | }, 382 | "esrecurse": { 383 | "version": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz", 384 | "integrity": "sha1-RxO2U2rffyrE8yfVWed1a/9kgiA=", 385 | "dev": true, 386 | "dependencies": { 387 | "estraverse": { 388 | "version": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz", 389 | "integrity": "sha1-9srKcokzqFDvkGYdDheYK6RxEaI=", 390 | "dev": true 391 | } 392 | } 393 | }, 394 | "estraverse": { 395 | "version": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 396 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", 397 | "dev": true 398 | }, 399 | "esutils": { 400 | "version": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 401 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 402 | "dev": true 403 | }, 404 | "etag": { 405 | "version": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz", 406 | "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=", 407 | "dev": true 408 | }, 409 | "event-emitter": { 410 | "version": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.4.tgz", 411 | "integrity": "sha1-jWPd+0z+H647MsomXExyAiIIC7U=", 412 | "dev": true 413 | }, 414 | "exit-hook": { 415 | "version": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", 416 | "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", 417 | "dev": true 418 | }, 419 | "express": { 420 | "version": "https://registry.npmjs.org/express/-/express-4.14.0.tgz", 421 | "integrity": "sha1-we4/Qs3Ikfs9xlCoki1R7IR9DWY=", 422 | "dev": true 423 | }, 424 | "express-graphql": { 425 | "version": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.5.4.tgz", 426 | "integrity": "sha1-QTR34+/abXQ354j372yZSRTnn4g=", 427 | "dev": true 428 | }, 429 | "extend": { 430 | "version": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", 431 | "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=", 432 | "dev": true 433 | }, 434 | "fast-levenshtein": { 435 | "version": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 436 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 437 | "dev": true 438 | }, 439 | "figures": { 440 | "version": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", 441 | "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", 442 | "dev": true, 443 | "dependencies": { 444 | "escape-string-regexp": { 445 | "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 446 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 447 | "dev": true 448 | } 449 | } 450 | }, 451 | "file-entry-cache": { 452 | "version": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz", 453 | "integrity": "sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g=", 454 | "dev": true 455 | }, 456 | "finalhandler": { 457 | "version": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.5.0.tgz", 458 | "integrity": "sha1-6VCKvs6bbbqHGmlCodeRG5GRGsc=", 459 | "dev": true 460 | }, 461 | "find-root": { 462 | "version": "https://registry.npmjs.org/find-root/-/find-root-1.0.0.tgz", 463 | "integrity": "sha1-li/yEaqyXGUg/u641ih/j26VgHo=", 464 | "dev": true 465 | }, 466 | "flat-cache": { 467 | "version": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", 468 | "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", 469 | "dev": true 470 | }, 471 | "form-data": { 472 | "version": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc4.tgz", 473 | "integrity": "sha1-BaxrwiIntD5EYfSIFhVUaZ1Pi14=", 474 | "dev": true 475 | }, 476 | "formidable": { 477 | "version": "https://registry.npmjs.org/formidable/-/formidable-1.0.17.tgz", 478 | "integrity": "sha1-71SRSQ+UM7cF+qdyScmQKa40hVk=", 479 | "dev": true 480 | }, 481 | "forwarded": { 482 | "version": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", 483 | "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=", 484 | "dev": true 485 | }, 486 | "fresh": { 487 | "version": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", 488 | "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=", 489 | "dev": true 490 | }, 491 | "fs.realpath": { 492 | "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 493 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 494 | "dev": true 495 | }, 496 | "generate-function": { 497 | "version": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", 498 | "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", 499 | "dev": true 500 | }, 501 | "generate-object-property": { 502 | "version": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", 503 | "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", 504 | "dev": true 505 | }, 506 | "get-stdin": { 507 | "version": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", 508 | "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", 509 | "dev": true 510 | }, 511 | "glob": { 512 | "version": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", 513 | "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", 514 | "dev": true 515 | }, 516 | "globals": { 517 | "version": "https://registry.npmjs.org/globals/-/globals-9.14.0.tgz", 518 | "integrity": "sha1-iFmTavADh0EmMFOznQ52yiQeQDQ=", 519 | "dev": true 520 | }, 521 | "globby": { 522 | "version": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", 523 | "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", 524 | "dev": true, 525 | "dependencies": { 526 | "glob": { 527 | "version": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 528 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 529 | "dev": true 530 | }, 531 | "minimatch": { 532 | "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", 533 | "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", 534 | "dev": true 535 | } 536 | } 537 | }, 538 | "graceful-fs": { 539 | "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 540 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 541 | "dev": true 542 | }, 543 | "graphql": { 544 | "version": "https://registry.npmjs.org/graphql/-/graphql-0.7.2.tgz", 545 | "integrity": "sha1-zIlKMoIzmbigywErnp7K01zQD3I=" 546 | }, 547 | "growl": { 548 | "version": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", 549 | "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", 550 | "dev": true 551 | }, 552 | "has-ansi": { 553 | "version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 554 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 555 | "dev": true 556 | }, 557 | "hoek": { 558 | "version": "https://registry.npmjs.org/hoek/-/hoek-4.1.0.tgz", 559 | "integrity": "sha1-SkVXRg9phC7UY6oAYozCbSaDr6c=", 560 | "dev": true 561 | }, 562 | "http-errors": { 563 | "version": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz", 564 | "integrity": "sha1-eIwNLB3iyBuebowBhDtrl+uSB1A=", 565 | "dev": true 566 | }, 567 | "iconv-lite": { 568 | "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", 569 | "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", 570 | "dev": true 571 | }, 572 | "ignore": { 573 | "version": "https://registry.npmjs.org/ignore/-/ignore-3.2.0.tgz", 574 | "integrity": "sha1-jYjwPDACoKxSEU2yXSxnOwvx5DU=", 575 | "dev": true 576 | }, 577 | "imurmurhash": { 578 | "version": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 579 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 580 | "dev": true 581 | }, 582 | "inflight": { 583 | "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 584 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 585 | "dev": true 586 | }, 587 | "inherits": { 588 | "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 589 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 590 | "dev": true 591 | }, 592 | "inquirer": { 593 | "version": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", 594 | "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", 595 | "dev": true 596 | }, 597 | "ipaddr.js": { 598 | "version": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.1.1.tgz", 599 | "integrity": "sha1-x5HZX1KynBJH1d+AraObinNkcjA=", 600 | "dev": true 601 | }, 602 | "is-fullwidth-code-point": { 603 | "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 604 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 605 | "dev": true 606 | }, 607 | "is-my-json-valid": { 608 | "version": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz", 609 | "integrity": "sha1-k27do8o8IR/ZjzstPgjaQ/eykVs=", 610 | "dev": true 611 | }, 612 | "is-path-cwd": { 613 | "version": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", 614 | "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", 615 | "dev": true 616 | }, 617 | "is-path-in-cwd": { 618 | "version": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", 619 | "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", 620 | "dev": true 621 | }, 622 | "is-path-inside": { 623 | "version": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", 624 | "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", 625 | "dev": true 626 | }, 627 | "is-property": { 628 | "version": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", 629 | "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", 630 | "dev": true 631 | }, 632 | "is-resolvable": { 633 | "version": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", 634 | "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", 635 | "dev": true 636 | }, 637 | "isarray": { 638 | "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 639 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 640 | "dev": true 641 | }, 642 | "isemail": { 643 | "version": "https://registry.npmjs.org/isemail/-/isemail-2.2.1.tgz", 644 | "integrity": "sha1-A1PT2aYpUQgMJiwqoKQrjqjp4qY=", 645 | "dev": true 646 | }, 647 | "items": { 648 | "version": "https://registry.npmjs.org/items/-/items-2.1.1.tgz", 649 | "integrity": "sha1-i9FtnIOxlSneWuoyGsqtp4NkoZg=", 650 | "dev": true 651 | }, 652 | "iterall": { 653 | "version": "https://registry.npmjs.org/iterall/-/iterall-1.0.2.tgz", 654 | "integrity": "sha1-QaLpbOntpeYcdn7l3DEjc7sEbpE=" 655 | }, 656 | "jade": { 657 | "version": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", 658 | "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", 659 | "dev": true, 660 | "dependencies": { 661 | "commander": { 662 | "version": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", 663 | "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", 664 | "dev": true 665 | }, 666 | "mkdirp": { 667 | "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", 668 | "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", 669 | "dev": true 670 | } 671 | } 672 | }, 673 | "joi": { 674 | "version": "https://registry.npmjs.org/joi/-/joi-9.2.0.tgz", 675 | "integrity": "sha1-M4WseQGSEwy+Iw6ALsAskhW7/to=", 676 | "dev": true 677 | }, 678 | "js-yaml": { 679 | "version": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", 680 | "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", 681 | "dev": true 682 | }, 683 | "json-stable-stringify": { 684 | "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", 685 | "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", 686 | "dev": true 687 | }, 688 | "jsonify": { 689 | "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", 690 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", 691 | "dev": true 692 | }, 693 | "jsonpointer": { 694 | "version": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", 695 | "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", 696 | "dev": true 697 | }, 698 | "jsx-ast-utils": { 699 | "version": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.3.5.tgz", 700 | "integrity": "sha1-m6YpcZjZ91RZTWLllJb/uSN3jdQ=", 701 | "dev": true 702 | }, 703 | "levn": { 704 | "version": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 705 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 706 | "dev": true 707 | }, 708 | "lodash": { 709 | "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.3.tgz", 710 | "integrity": "sha1-VX7X0qlDjKxf1aQwQ8pgy0VeAfc=" 711 | }, 712 | "lru-cache": { 713 | "version": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", 714 | "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", 715 | "dev": true 716 | }, 717 | "media-typer": { 718 | "version": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 719 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 720 | "dev": true 721 | }, 722 | "merge-descriptors": { 723 | "version": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 724 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", 725 | "dev": true 726 | }, 727 | "methods": { 728 | "version": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 729 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 730 | "dev": true 731 | }, 732 | "mime": { 733 | "version": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", 734 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", 735 | "dev": true 736 | }, 737 | "mime-db": { 738 | "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz", 739 | "integrity": "sha1-wY29fHOl2/b0SgJNwNFloeexw5I=", 740 | "dev": true 741 | }, 742 | "mime-types": { 743 | "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz", 744 | "integrity": "sha1-4HqqnGxrmnyjASxpADrSWjnpKog=", 745 | "dev": true 746 | }, 747 | "minimatch": { 748 | "version": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", 749 | "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", 750 | "dev": true 751 | }, 752 | "minimist": { 753 | "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 754 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 755 | "dev": true 756 | }, 757 | "mkdirp": { 758 | "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 759 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 760 | "dev": true 761 | }, 762 | "mocha": { 763 | "version": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", 764 | "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", 765 | "dev": true 766 | }, 767 | "moment": { 768 | "version": "https://registry.npmjs.org/moment/-/moment-2.17.1.tgz", 769 | "integrity": "sha1-/tlQYGPzaxDwZsi1mhRNf66+HYI=", 770 | "dev": true 771 | }, 772 | "ms": { 773 | "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 774 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", 775 | "dev": true 776 | }, 777 | "multiline": { 778 | "version": "https://registry.npmjs.org/multiline/-/multiline-1.0.2.tgz", 779 | "integrity": "sha1-abHyX/B00oKJBPJE3dBrfZbvbJM=", 780 | "dev": true 781 | }, 782 | "mute-stream": { 783 | "version": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", 784 | "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", 785 | "dev": true 786 | }, 787 | "negotiator": { 788 | "version": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 789 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", 790 | "dev": true 791 | }, 792 | "number-is-nan": { 793 | "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 794 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 795 | "dev": true 796 | }, 797 | "object-assign": { 798 | "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", 799 | "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", 800 | "dev": true 801 | }, 802 | "on-finished": { 803 | "version": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 804 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 805 | "dev": true 806 | }, 807 | "once": { 808 | "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 809 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 810 | "dev": true 811 | }, 812 | "onetime": { 813 | "version": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", 814 | "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", 815 | "dev": true 816 | }, 817 | "optionator": { 818 | "version": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 819 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 820 | "dev": true 821 | }, 822 | "os-homedir": { 823 | "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 824 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", 825 | "dev": true 826 | }, 827 | "parseurl": { 828 | "version": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", 829 | "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=", 830 | "dev": true 831 | }, 832 | "path-is-absolute": { 833 | "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 834 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 835 | "dev": true 836 | }, 837 | "path-is-inside": { 838 | "version": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 839 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 840 | "dev": true 841 | }, 842 | "path-to-regexp": { 843 | "version": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 844 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", 845 | "dev": true 846 | }, 847 | "pify": { 848 | "version": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 849 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 850 | "dev": true 851 | }, 852 | "pinkie": { 853 | "version": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 854 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 855 | "dev": true 856 | }, 857 | "pinkie-promise": { 858 | "version": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 859 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 860 | "dev": true 861 | }, 862 | "pkg-config": { 863 | "version": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", 864 | "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", 865 | "dev": true 866 | }, 867 | "pluralize": { 868 | "version": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", 869 | "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", 870 | "dev": true 871 | }, 872 | "prelude-ls": { 873 | "version": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 874 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 875 | "dev": true 876 | }, 877 | "process-nextick-args": { 878 | "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 879 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", 880 | "dev": true 881 | }, 882 | "progress": { 883 | "version": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", 884 | "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", 885 | "dev": true 886 | }, 887 | "proxy-addr": { 888 | "version": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.2.tgz", 889 | "integrity": "sha1-tMxfImENlTWCTBI675089zxAujc=", 890 | "dev": true 891 | }, 892 | "qs": { 893 | "version": "https://registry.npmjs.org/qs/-/qs-6.2.0.tgz", 894 | "integrity": "sha1-O3hIwDwt7OaalSKw+ujEEm10Xzs=", 895 | "dev": true 896 | }, 897 | "range-parser": { 898 | "version": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 899 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", 900 | "dev": true 901 | }, 902 | "raw-body": { 903 | "version": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", 904 | "integrity": "sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q=", 905 | "dev": true 906 | }, 907 | "readable-stream": { 908 | "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz", 909 | "integrity": "sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=", 910 | "dev": true 911 | }, 912 | "readline2": { 913 | "version": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", 914 | "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", 915 | "dev": true 916 | }, 917 | "require-uncached": { 918 | "version": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", 919 | "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", 920 | "dev": true 921 | }, 922 | "resolve-from": { 923 | "version": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", 924 | "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", 925 | "dev": true 926 | }, 927 | "restore-cursor": { 928 | "version": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", 929 | "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", 930 | "dev": true 931 | }, 932 | "rewire": { 933 | "version": "https://registry.npmjs.org/rewire/-/rewire-2.5.2.tgz", 934 | "integrity": "sha1-ZCfee3/u+n02QBUH62SlOFvFjcc=", 935 | "dev": true 936 | }, 937 | "rimraf": { 938 | "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", 939 | "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", 940 | "dev": true, 941 | "dependencies": { 942 | "glob": { 943 | "version": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 944 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 945 | "dev": true 946 | }, 947 | "minimatch": { 948 | "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", 949 | "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", 950 | "dev": true 951 | } 952 | } 953 | }, 954 | "run-async": { 955 | "version": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", 956 | "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", 957 | "dev": true 958 | }, 959 | "run-parallel": { 960 | "version": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.6.tgz", 961 | "integrity": "sha1-KQA8miFj4B4tLfyQV18sbB1hoDk=", 962 | "dev": true 963 | }, 964 | "rx-lite": { 965 | "version": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", 966 | "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", 967 | "dev": true 968 | }, 969 | "send": { 970 | "version": "https://registry.npmjs.org/send/-/send-0.14.1.tgz", 971 | "integrity": "sha1-qVSYQyU5L1FTKndgdg5FlZjIn3o=", 972 | "dev": true 973 | }, 974 | "serve-static": { 975 | "version": "https://registry.npmjs.org/serve-static/-/serve-static-1.11.1.tgz", 976 | "integrity": "sha1-1sznaTUF9zPHWd5Xvvwa92wPCAU=", 977 | "dev": true 978 | }, 979 | "setprototypeof": { 980 | "version": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz", 981 | "integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg=", 982 | "dev": true 983 | }, 984 | "shelljs": { 985 | "version": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.1.tgz", 986 | "integrity": "sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg=", 987 | "dev": true 988 | }, 989 | "should": { 990 | "version": "https://registry.npmjs.org/should/-/should-10.0.0.tgz", 991 | "integrity": "sha1-hjuCx5ImCnqm2lUDK9DYVKYxY/4=", 992 | "dev": true 993 | }, 994 | "should-equal": { 995 | "version": "https://registry.npmjs.org/should-equal/-/should-equal-1.0.1.tgz", 996 | "integrity": "sha1-C26VFvJgGp+wuy3MNpr6HH4gCvc=", 997 | "dev": true 998 | }, 999 | "should-format": { 1000 | "version": "https://registry.npmjs.org/should-format/-/should-format-2.0.0.tgz", 1001 | "integrity": "sha1-2UJoAejjLVv8ZcfkcJzXVIsr5cg=", 1002 | "dev": true 1003 | }, 1004 | "should-type": { 1005 | "version": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", 1006 | "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", 1007 | "dev": true 1008 | }, 1009 | "sigmund": { 1010 | "version": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", 1011 | "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", 1012 | "dev": true 1013 | }, 1014 | "slice-ansi": { 1015 | "version": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", 1016 | "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", 1017 | "dev": true 1018 | }, 1019 | "sprintf-js": { 1020 | "version": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1021 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1022 | "dev": true 1023 | }, 1024 | "standard": { 1025 | "version": "https://registry.npmjs.org/standard/-/standard-7.1.2.tgz", 1026 | "integrity": "sha1-QBZu7sJAUGXRpPDj8VurxuJ0YH4=", 1027 | "dev": true 1028 | }, 1029 | "standard-engine": { 1030 | "version": "https://registry.npmjs.org/standard-engine/-/standard-engine-4.1.3.tgz", 1031 | "integrity": "sha1-ejGq1U8D2fOTVfQzic4GlPQJQVU=", 1032 | "dev": true, 1033 | "dependencies": { 1034 | "minimist": { 1035 | "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 1036 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", 1037 | "dev": true 1038 | } 1039 | } 1040 | }, 1041 | "statuses": { 1042 | "version": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 1043 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", 1044 | "dev": true 1045 | }, 1046 | "string_decoder": { 1047 | "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1048 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 1049 | "dev": true 1050 | }, 1051 | "string-width": { 1052 | "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1053 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1054 | "dev": true 1055 | }, 1056 | "strip-ansi": { 1057 | "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1058 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1059 | "dev": true 1060 | }, 1061 | "strip-indent": { 1062 | "version": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", 1063 | "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", 1064 | "dev": true, 1065 | "dependencies": { 1066 | "get-stdin": { 1067 | "version": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", 1068 | "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", 1069 | "dev": true 1070 | } 1071 | } 1072 | }, 1073 | "strip-json-comments": { 1074 | "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", 1075 | "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", 1076 | "dev": true 1077 | }, 1078 | "superagent": { 1079 | "version": "https://registry.npmjs.org/superagent/-/superagent-2.3.0.tgz", 1080 | "integrity": "sha1-cDUpoHFOV+EjlZ3e+84ZOy5Q0RU=", 1081 | "dev": true 1082 | }, 1083 | "supports-color": { 1084 | "version": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", 1085 | "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", 1086 | "dev": true 1087 | }, 1088 | "table": { 1089 | "version": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", 1090 | "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", 1091 | "dev": true, 1092 | "dependencies": { 1093 | "is-fullwidth-code-point": { 1094 | "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1095 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1096 | "dev": true 1097 | }, 1098 | "string-width": { 1099 | "version": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", 1100 | "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=", 1101 | "dev": true 1102 | } 1103 | } 1104 | }, 1105 | "text-table": { 1106 | "version": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1107 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1108 | "dev": true 1109 | }, 1110 | "through": { 1111 | "version": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1112 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1113 | "dev": true 1114 | }, 1115 | "to-iso-string": { 1116 | "version": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", 1117 | "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", 1118 | "dev": true 1119 | }, 1120 | "topo": { 1121 | "version": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz", 1122 | "integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=", 1123 | "dev": true 1124 | }, 1125 | "tryit": { 1126 | "version": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", 1127 | "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", 1128 | "dev": true 1129 | }, 1130 | "type-check": { 1131 | "version": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1132 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1133 | "dev": true 1134 | }, 1135 | "type-is": { 1136 | "version": "https://registry.npmjs.org/type-is/-/type-is-1.6.14.tgz", 1137 | "integrity": "sha1-4hljnBfe0coHiQkt1UoDgmuBfLI=", 1138 | "dev": true 1139 | }, 1140 | "typedarray": { 1141 | "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1142 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 1143 | "dev": true 1144 | }, 1145 | "uniq": { 1146 | "version": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", 1147 | "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", 1148 | "dev": true 1149 | }, 1150 | "unpipe": { 1151 | "version": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1152 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 1153 | "dev": true 1154 | }, 1155 | "user-home": { 1156 | "version": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", 1157 | "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", 1158 | "dev": true 1159 | }, 1160 | "util-deprecate": { 1161 | "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1162 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 1163 | "dev": true 1164 | }, 1165 | "utils-merge": { 1166 | "version": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", 1167 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", 1168 | "dev": true 1169 | }, 1170 | "vary": { 1171 | "version": "https://registry.npmjs.org/vary/-/vary-1.1.0.tgz", 1172 | "integrity": "sha1-4eWv+70WrnaN0mdDlLmtMCJlMUA=", 1173 | "dev": true 1174 | }, 1175 | "wordwrap": { 1176 | "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1177 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1178 | "dev": true 1179 | }, 1180 | "wrappy": { 1181 | "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1182 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1183 | "dev": true 1184 | }, 1185 | "write": { 1186 | "version": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", 1187 | "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", 1188 | "dev": true 1189 | }, 1190 | "xtend": { 1191 | "version": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 1192 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", 1193 | "dev": true 1194 | } 1195 | } 1196 | } 1197 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joiql", 3 | "version": "0.1.5", 4 | "description": "Make GraphQL schema creation and data validation easy with Joi.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "standard && npm run mocha", 8 | "mocha": "mocha -r should", 9 | "b": "browserify script.js -o bundle.js -t [ babelify --presets [ es2015 ] ]" 10 | }, 11 | "author": "", 12 | "license": "MIT", 13 | "dependencies": { 14 | "graphql": "^0.11.7", 15 | "lodash": "^4.13.1" 16 | }, 17 | "peerDependencies": { 18 | "joi": "^9.0.0" 19 | }, 20 | "devDependencies": { 21 | "express": "^4.14.0", 22 | "express-graphql": "^0.5.3", 23 | "joi": "^9.2.0", 24 | "mocha": "^2.5.3", 25 | "rewire": "^2.5.2", 26 | "should": "^10.0.0", 27 | "standard": "^7.1.2", 28 | "superagent": "^2.1.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/descs-to-fields.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | const rewire = require('rewire') 3 | const descsToFields = rewire('../').__get__('descsToFields') 4 | const { 5 | string, 6 | number, 7 | object, 8 | array, 9 | boolean, 10 | date, 11 | alternatives 12 | } = require('joi') 13 | 14 | describe('descsToFields', () => { 15 | it('converts a joi string to GraphQL string', () => { 16 | const fields = descsToFields({ foo: string() }) 17 | fields.foo.type.name.should.equal('String') 18 | }) 19 | 20 | it('converts various scalar types', () => { 21 | const fields = descsToFields({ 22 | str: string(), 23 | int: number().integer(), 24 | float: number(), 25 | bool: boolean(), 26 | date: date() 27 | }) 28 | fields.str.type.name.should.equal('String') 29 | fields.int.type.name.should.equal('Int') 30 | fields.float.type.name.should.equal('Float') 31 | fields.bool.type.name.should.equal('Boolean') 32 | fields.date.type.name.should.equal('String') 33 | }) 34 | 35 | it('converts arrays to lists', () => { 36 | const fields = descsToFields({ 37 | arr: array().items(object({ 38 | a: string(), 39 | b: number() 40 | })) 41 | }) 42 | fields.arr.type.constructor.name.should.equal('GraphQLList') 43 | }) 44 | 45 | it('converts alternatives to a union', () => { 46 | const video = object({ 47 | type: string().valid('video'), 48 | url: string(), 49 | background_color: string() 50 | }) 51 | const image = object({ 52 | type: string().valid('image'), 53 | url: string(), 54 | position: string() 55 | }) 56 | const fields = descsToFields({ 57 | hero: alternatives().try(video, image) 58 | }) 59 | fields.hero.type.constructor.name.should.equal('GraphQLUnionType') 60 | }) 61 | 62 | it('names object types by the meta name', () => { 63 | const fields = descsToFields({ 64 | person: object({ 65 | name: string(), 66 | age: number() 67 | }).meta({ name: 'Person' }) 68 | }) 69 | fields.person.type.name.should.equal('Person') 70 | }) 71 | 72 | it('converts meta args to GraphQl arguments', () => { 73 | const fields = descsToFields({ 74 | person: object({ 75 | name: string(), 76 | age: number() 77 | }).meta({ args: { id: number().integer() } }) 78 | }) 79 | fields.person.args.id.type.name.should.equal('Int') 80 | }) 81 | 82 | it('converts required args to GraphQl NonNulls', () => { 83 | const fields = descsToFields({ 84 | person: object({ 85 | name: string(), 86 | age: number() 87 | }).meta({ args: { id: number().integer().required() } }) 88 | }) 89 | fields.person.args.id.type.constructor.name.should.equal('GraphQLNonNull') 90 | }) 91 | 92 | it('converts args to input', () => { 93 | const fields = descsToFields({ 94 | person: object({ 95 | name: string(), 96 | age: number() 97 | }).meta({ 98 | args: { 99 | address: object({ 100 | city: string().required(), 101 | country: string() 102 | }) 103 | } 104 | }) 105 | }) 106 | fields.person.args.address.type.constructor.name 107 | .should.equal('GraphQLInputObjectType') 108 | }) 109 | 110 | it('considers descriptions', () => { 111 | const fields = descsToFields({ 112 | foo: string().description('Just a foo') 113 | }) 114 | fields.foo.description.should.equal('Just a foo') 115 | }) 116 | 117 | it('creates a union type for a multi array input', () => { 118 | const fields = descsToFields({ 119 | article: object({ 120 | blocks: array().items( 121 | object({ 122 | type: string().valid('image'), 123 | size: number().integer() 124 | }), 125 | object({ 126 | type: string().valid('text'), 127 | body: string() 128 | }) 129 | ) 130 | }) 131 | }) 132 | const blocks = fields.article.type._typeConfig.fields().blocks 133 | blocks.type.constructor.name.should.equal('GraphQLList') 134 | blocks.type.ofType.constructor.name.should.equal('GraphQLUnionType') 135 | const [img, text] = blocks.type.ofType._typeConfig.types 136 | img._typeConfig.fields().size.type.name.should.equal('Int') 137 | text._typeConfig.fields().body.type.name.should.equal('String') 138 | }) 139 | }) 140 | -------------------------------------------------------------------------------- /test/integration.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | const { string, number, object, date, array, alternatives, lazy } = require('joi') 3 | const { graphql } = require('graphql') 4 | const joiql = require('../') 5 | 6 | const db = { 7 | hillary: { 8 | name: 'Hillary Clinton', 9 | age: 68, 10 | birthday: new Date(1947, 9, 26) 11 | }, 12 | elizabeth: { 13 | name: 'Elizabeth Warren', 14 | age: 67, 15 | birthday: new Date(1949, 5, 22) 16 | } 17 | } 18 | 19 | let Person = object({ 20 | id: string(), 21 | name: string(), 22 | birthday: date(), 23 | age: lazy(() => number().integer()) 24 | }).meta({ 25 | args: { id: string().required(), age: number().min(1).max(100) }, 26 | resolve: (source, args) => db[args.id] 27 | }) 28 | 29 | const schema = joiql({ 30 | query: { 31 | person: Person 32 | } 33 | }) 34 | 35 | describe('joiql', () => { 36 | it('converts a Joi schema into GraphQL', () => { 37 | const query = '{ person(id: "hillary") { name } }' 38 | return graphql(schema, query).then((res) => { 39 | res.data.person.name.should.equal('Hillary Clinton') 40 | }) 41 | }) 42 | 43 | it('validates args', () => { 44 | const query = '{ person(age: 0 id: "hillary") { name } }' 45 | return graphql(schema, query).then((res) => { 46 | res.errors[0].message.should.containEql('{\n "id": "hillary",\n "age" \u001b[31m[1]\u001b[0m: 0\n}\n\u001b[31m\n[1] "age" must be larger than or equal to 1\u001b[0m') 47 | }) 48 | }) 49 | 50 | it('works without args', () => { 51 | const schema = joiql({ 52 | query: { 53 | hello: string().meta({ resolve: () => 'world' }) 54 | } 55 | }) 56 | return graphql(schema, '{ hello }').then((res) => { 57 | res.data.hello.should.equal('world') 58 | }) 59 | }) 60 | 61 | it('works without args or resolves', () => { 62 | const schema = joiql({ 63 | query: { 64 | hello: string() 65 | } 66 | }) 67 | return graphql(schema, '{ hello }').then((res) => { 68 | (res.data.hello === null).should.be.ok() 69 | ;(typeof undefined).should.equal('undefined') 70 | }) 71 | }) 72 | 73 | it('omits forbidden fields', () => { 74 | const schema = joiql({ 75 | query: { 76 | a: string().forbidden(), 77 | b: string(), 78 | c: object({ 79 | d: string().forbidden(), 80 | e: string() 81 | }), 82 | f: array().items(object({ 83 | g: string().forbidden(), 84 | h: string() 85 | })), 86 | j: array().items( 87 | object({ k: string() }), 88 | object({ l: string() }).forbidden() 89 | ), 90 | m: alternatives( 91 | object({ n: string() }).meta({ name: 'N' }), 92 | object({ o: string() }).meta({ name: 'O' }).forbidden() 93 | ) 94 | } 95 | }) 96 | return graphql(schema, '{ a c { d } f { g } j { k l } m { o } }') 97 | .then((res) => { 98 | const errs = res.errors.map((e) => e.message).join('') 99 | errs.should.containEql('Cannot query field "a"') 100 | errs.should.containEql('Cannot query field "d"') 101 | errs.should.containEql('Cannot query field "g"') 102 | errs.should.containEql('Cannot query field "l"') 103 | errs.should.containEql('Cannot query field "o"') 104 | }) 105 | .then(() => 106 | graphql(schema, '{ a b c { e } f { h } j { k } m ... on N { n } }') 107 | ) 108 | .then((res) => { 109 | const errs = res.errors.map((e) => e.message).join('') 110 | errs.should.not.containEql('Cannot query field "b"') 111 | errs.should.not.containEql('Cannot query field "e"') 112 | errs.should.not.containEql('Cannot query field "h"') 113 | errs.should.not.containEql('Cannot query field "k"') 114 | errs.should.not.containEql('Cannot query field "n"') 115 | }) 116 | }) 117 | 118 | it('omits forbidden args', () => { 119 | const schema = joiql({ 120 | query: { 121 | a: string().meta({ 122 | args: { 123 | b: string().forbidden(), 124 | c: string(), 125 | d: object({ 126 | e: string().forbidden(), 127 | f: string() 128 | }), 129 | g: array().items(object({ 130 | h: string().forbidden(), 131 | i: string() 132 | })), 133 | j: array().items( 134 | object({ k: string() }), 135 | object({ l: string() }).forbidden() 136 | ), 137 | m: alternatives( 138 | object({ n: string() }).meta({ name: 'N' }), 139 | object({ o: string() }).meta({ name: 'O' }).forbidden() 140 | ) 141 | } 142 | }) 143 | } 144 | }) 145 | return graphql(schema, `{ 146 | a( 147 | b: "Foo" 148 | d: { e: "Foo" } 149 | g: { h: "Foo" } 150 | j: { l: "Foo" } 151 | m: { o: "Foo" } 152 | ) 153 | }`).then((res) => { 154 | const errs = res.errors.map((e) => e.message).join('') 155 | errs.should.containEql('Unknown argument "b"') 156 | errs.should.containEql('has invalid value {e') 157 | errs.should.containEql('has invalid value {h') 158 | errs.should.containEql('has invalid value {l') 159 | errs.should.containEql('has invalid value {o') 160 | }) 161 | }) 162 | }) 163 | --------------------------------------------------------------------------------