From 7490a2865d7be8e9442c01988002f9dafefcf7b6 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sat, 25 Mar 2017 14:27:03 -0400 Subject: [PATCH 01/62] Adds dep --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 2e5e7ece9d0..2aa1fc4f09e 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "commander": "2.17.1", "deepcopy": "1.0.0", "express": "4.16.2", + "graphql": "^0.9.1", "intersect": "1.0.1", "lodash": "4.17.5", "lru-cache": "4.1.3", From 1c2846c016fe1cbcb3301728c0a32776cf968e32 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Wed, 5 Apr 2017 12:11:06 -0400 Subject: [PATCH 02/62] WIP --- spec/graphQLBridge.spec.js | 132 ++++++++++++++++ src/graphql/ParseClass.js | 260 +++++++++++++++++++++++++++++++ src/graphql/ParseQuery.js | 74 +++++++++ src/graphql/Schema.js | 214 +++++++++++++++++++++++++ src/graphql/index.js | 0 src/graphql/types/ACL.js | 46 ++++++ src/graphql/types/Date.js | 20 +++ src/graphql/types/File.js | 20 +++ src/graphql/types/GeoPoint.js | 45 ++++++ src/graphql/types/JSONObject.js | 28 ++++ src/graphql/types/NumberQuery.js | 29 ++++ src/graphql/types/Pointer.js | 51 ++++++ src/graphql/types/StringQuery.js | 35 +++++ src/graphql/types/index.js | 123 +++++++++++++++ 14 files changed, 1077 insertions(+) create mode 100644 spec/graphQLBridge.spec.js create mode 100644 src/graphql/ParseClass.js create mode 100644 src/graphql/ParseQuery.js create mode 100644 src/graphql/Schema.js create mode 100644 src/graphql/index.js create mode 100644 src/graphql/types/ACL.js create mode 100644 src/graphql/types/Date.js create mode 100644 src/graphql/types/File.js create mode 100644 src/graphql/types/GeoPoint.js create mode 100644 src/graphql/types/JSONObject.js create mode 100644 src/graphql/types/NumberQuery.js create mode 100644 src/graphql/types/Pointer.js create mode 100644 src/graphql/types/StringQuery.js create mode 100644 src/graphql/types/index.js diff --git a/spec/graphQLBridge.spec.js b/spec/graphQLBridge.spec.js new file mode 100644 index 00000000000..656be665bf9 --- /dev/null +++ b/spec/graphQLBridge.spec.js @@ -0,0 +1,132 @@ +/*eslint-disable*/ +const GraphQLParseSchema = require('../src/graphql/Schema').GraphQLParseSchema; +const Config = require('../src/Config'); +const Auth = require('../src/Auth').Auth; +const { /*print, */printSchema, graphql } = require('graphql'); +//const { /*print, printSchema,*/ GraphQLID, GraphQLNonNull } = require('graphql'); + +let config; + +describe('graphQLbridge', () => { + + beforeEach(() => { + config = new Config('test'); + }); + + fit('base test', (done) => { + let schema; + config.database.loadSchema() + .then(dbSchema => { + schema = dbSchema; + return dbSchema.addClassIfNotExists('NewClass', { + foo: { type: 'String' }, + bar: { type: 'Boolean' }, + increment: { type: 'Number' } + }).then(() => { + return dbSchema.addClassIfNotExists('OtherClass', { + foo: { type: 'String' }, + baz: { type: 'Pointer', targetClass: 'NewClass' } + }) + }).then(() => { + return schema.getAllClasses(true); + }); + }) + .then((allClasses) => { + const fullSchema = allClasses.reduce((memo, classDef) => { + memo[classDef.className] = classDef; + return memo; + }, {}); + + const Schema = new GraphQLParseSchema(fullSchema); + const s = Schema.Schema(); + const root = Schema.Root(); + const context = { + config: config, + auth: new Auth({config}) + } + console.log(printSchema(s)); + /* + ` + mutation { + NewClass { + create(foo: "hello", bar: false, increment: 1) { + objectId, foo, bar, increment + } + } + }` + ...on NewClass { objectId, foo, bar, increment } + */ + + /*return graphql(s,` + mutation { + create(className: "NewClass", params: {foo: "Bar", bar: true, increment: 10}) { + objectId, + createdAt, + updatedAt, + ... on Object { data } + } + }`, root, context).then((res) => { + console.log(res); + const objectId = res.data.create.objectId; + return graphql(s, ` + mutation myMutation($id: ID!) { + NewClass { + update(objectId: $id, incrementKey: { key: "increment", value: 2 }) { + objectId, increment + } + } + }`, root, context, {id: objectId}); + })*/ + /* + query { + OtherClass { + objectId, + baz { + foo, + bar + } + }, + OtherClass { + foo, objectId + } + } + mutation { + NewClass: createNewClass(input: { + foo: "hello", + bar: false, + increment: 1 + }) { + objectId, foo, bar, increment + } + + NewClass: createNewClass(input: { + foo: "hello", + bar: false, + increment: 1 + }) { + objectId, foo, bar, increment + } + } + */ + + return graphql(s, ` + query { + NewClass(where: {foo: { regex: "hello" }}) { + objectId + } + } + `,root, context) + .then((res) => { + console.log(JSON.stringify(res, null, 2)); + done(); + }); + //console.log(printSchema(singleSchema)); + //done(); + }) + .catch((err) => { + console.error(err); + done.fail(); + }) + }); + +}); diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js new file mode 100644 index 00000000000..9c2f957d3c2 --- /dev/null +++ b/src/graphql/ParseClass.js @@ -0,0 +1,260 @@ +import { + GraphQLInterfaceType, + GraphQLObjectType, + GraphQLInputObjectType, + //GraphQLString, + GraphQLNonNull, + //GraphQLBoolean, + GraphQLID, +} from 'graphql' + +/* eslint-disable */ +import { + queryType, + inputType, + type, + //GraphQLGeoPoint, + GraphQLPointer, + GraphQLJSONObject +} from './types' + +import { + AtomicOps +} from './ParseQuery'; + +function graphQLField(fieldName, field) { + const gQLType = type(fieldName, field); + if (!gQLType) { + return; + } + const fieldDef = { + name: fieldName, + type: gQLType, + description: `Accessor for ${fieldName} (${field.type})`, + // resolve: () => { + // /* eslint-disable */ + // console.log(arguments); + // /* eslint-enable */ + // return arguments + // } + }; + if (gQLType === GraphQLPointer) { + fieldDef.args = { + objectId: { + type: new GraphQLNonNull(GraphQLID) + } + } + } + return fieldDef; +} + +function graphQLInputField(fieldName, field) { + const gQLType = inputType(fieldName, field); + if (!gQLType) { + return; + } + return { + name: fieldName, + type: gQLType, + description: `Setter for ${fieldName} (${field.type})`, + }; +} + +function graphQLQueryField(fieldName, field) { + const gQLType = queryType(fieldName, field); + if (!gQLType) { + return; + } + return { + name: fieldName, + type: gQLType, + description: `Query for ${fieldName} (${field.type})`, + }; +} + +const ParseClassCache = {}; + +export function loadClass(className, schema) { + if (!ParseClassCache[className]) { + const c = new ParseClass(className, schema); + const objectType = c.graphQLObjectType(); + const inputType = c.graphQLInputObjectType(); + const updateType = c.graphQLUpdateInputObjectType(); + const queryType = c.graphQLQueryInputObjectType() + ParseClassCache[className] = { objectType, inputType, updateType, queryType, class: c } + } + return ParseClassCache[className]; +} + +const reservedFieldNames = ['objectId', 'createdAt', 'updatedAt']; + +export const ParseObjectInterface = new GraphQLInterfaceType({ + name: 'ObjectType', + fields: { + objectId: { + type: type('objectId') + }, + createdAt: { + type: type(null, {type: 'Date'}) + }, + updatedAt: { + type: type(null, {type: 'Date'}) + }, + ACL: { + type: type(null, {type: 'ACL'}) + } + } +}); + +export const ParseObject = new GraphQLObjectType({ + name: 'Object', + interfaces: [ParseObjectInterface], + fields: { + objectId: { + type: type('objectId') + }, + createdAt: { + type: type(null, {type: 'Date'}) + }, + updatedAt: { + type: type(null, {type: 'Date'}) + }, + ACL: { + type: type(null, {type: 'ACL'}) + }, + data: { + type: GraphQLJSONObject + } + }, + isTypeOf: (args, context, info) => { + // Use that type when impossible to map to a Schema type + return typeof info.schema._typeMap[args.className] === 'undefined'; + }, + resolve: () => { + console.log('RESOLVE CALLED!'); + } +}); + +export class ParseClass { + schema; + className; + class; + + constructor(className, schema) { + this.className = className; + this.schema = schema; + this.class = this.schema[className]; + } + + graphQLConfig() { + const className = this.className; + return { + name: className, + description: `Parse Class ${className}`, + interfaces: [ParseObjectInterface], + fields: () => { + return this.buildFields(graphQLField); + }, + resolve: () => { + console.log('Resilt!'); + return; + }, + isTypeOf: function(a,b, c,d) { + console.log(a,b,c,d); + console.log('isTypeOf, '+className); + return a.className == className; + } + }; + } + + buildFields(mapper, filterReserved, defaultValues = {}) { + const fields = this.class.fields; + return Object.keys(fields).reduce((memo, fieldName) => { + if (filterReserved && reservedFieldNames.indexOf(fieldName) >= 0) { + return memo; + } + const field = fields[fieldName]; + const gQLField = mapper(fieldName, field); + if (!gQLField) { + if (field.type == 'Pointer') { + memo[fieldName] = { + type: loadClass(field.targetClass, this.schema).objectType + } + } + return memo; + } + memo[fieldName] = gQLField; + return memo; + }, defaultValues); + } + + graphQLInputConfig() { + const className = this.className; + return { + name: className + 'Input', + description: `Parse Class ${className} Input`, + fields: () => { + return this.buildFields(graphQLInputField, true); + }, + resolve: this.get.bind(this), + isTypeOf: function(a,b,c,d) { + return a.className == className; + } + }; + } + + graphQLQueryConfig() { + const className = this.className; + return { + name: className + 'Query', + description: `Parse Class ${className} Query`, + fields: () => { + return this.buildFields(graphQLQueryField, true); + }, + resolve: this.get.bind(this), + isTypeOf: function(a,b,c,d) { + return a.className == className; + } + }; + } + + graphQLUpdateInputConfig() { + const className = this.className; + return { + name: className + 'Update', + description: `Parse Class ${className} Update`, + fields: () => { + const fields = this.buildFields(graphQLInputField, true); + return Object.assign({}, fields, AtomicOps); + }, + resolve: this.get.bind(this), + isTypeOf: function(a,b,c,d) { + return a.className == className; + } + }; + } + + graphQLUpdateInputObjectType() { + return new GraphQLInputObjectType(this.graphQLUpdateInputConfig()); + } + + graphQLInputObjectType() { + return new GraphQLInputObjectType(this.graphQLInputConfig()); + } + + graphQLQueryInputObjectType() { + return new GraphQLInputObjectType(this.graphQLQueryConfig()); + } + + graphQLObjectType() { + return new GraphQLObjectType(this.graphQLConfig()); + } + + get(a,b,c) { + /*eslint-disable*/ + console.log('ParseClass resolve...'); + console.log(a,b,c); + return null; + } +} + diff --git a/src/graphql/ParseQuery.js b/src/graphql/ParseQuery.js new file mode 100644 index 00000000000..4370d6860c1 --- /dev/null +++ b/src/graphql/ParseQuery.js @@ -0,0 +1,74 @@ +import { + GraphQLInputObjectType, + GraphQLFloat, + GraphQLString, +} from 'graphql' + +const FloatKeyValIT = new GraphQLInputObjectType({ + name: 'FloatConstraint', + fields: { + key: { type: GraphQLString }, + value: { type: GraphQLFloat } + } +}); + +const StringKeyValIT = new GraphQLInputObjectType({ + name: 'StringConstraint', + fields: { + key: { type: GraphQLString }, + value: { type: GraphQLString } + } +}); + +export const ParseQuery = { + whereLessThan: { + type: FloatKeyValIT, + args: FloatKeyValIT.fields, + description: '' + }, + whereGreaterThan: { + type: FloatKeyValIT, + args: FloatKeyValIT.fields + }, + whereLessThanOrEqualTo: { + type: FloatKeyValIT, + args: FloatKeyValIT.fields + }, + whereGreaterThanOrEqualTo: { + type: FloatKeyValIT, + args: FloatKeyValIT.fields + }, + whereMatches: { + type: StringKeyValIT, + args: StringKeyValIT.fields + }, + whereExists: { + type: GraphQLString, + args: { key: GraphQLString } + }, + whereDoesNotExist: { + type: GraphQLString, + args: { key: GraphQLString } + }, + whereStartsWith: { + type: StringKeyValIT, + args: StringKeyValIT.fields + }, + whereEndsWith: { + type: StringKeyValIT, + args: StringKeyValIT.fields + } +}; + +/* eslint-disable */ + +export const AtomicOps = { + incrementKey: { + type: FloatKeyValIT, + args: FloatKeyValIT.fields + }, + unsetKey: { + type: GraphQLString, + args: { key: GraphQLString } + } +} diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js new file mode 100644 index 00000000000..b9f68137adb --- /dev/null +++ b/src/graphql/Schema.js @@ -0,0 +1,214 @@ +import { + ParseObject, + loadClass, +} from './ParseClass'; + +import { + GraphQLSchema, + GraphQLObjectType, + GraphQLNonNull, + GraphQLList, + GraphQLID, +} from 'graphql' + +// import { +// GraphQLJSONObject +// } from './types'; + +import rest from '../rest'; +/* eslint-disable */ + +export class GraphQLParseSchema { + schema; + types; + constructor(schema) { + this.schema = schema; + this.types = {}; + } + + Schema() { + return new GraphQLSchema({ + query: this.Query(), + mutation: this.Mutation(), + }); + } + + Query() { + const MainSchemaOptions = { + name: 'ParseSchema', + description: `The full parse schema`, + fields: {} + } + Object.keys(this.schema).forEach((className) => { + const { + queryType, objectType + } = loadClass(className, this.schema); + + MainSchemaOptions.fields[className] = { + type: new GraphQLList(objectType), + args: { + id: { type: GraphQLID, name: 'objectId' }, + where: { type: queryType } + }, + resolve: (root, args, context, info) => { + // console.log(className, info.fieldNodes); + // console.log(info.fieldName); + // console.log(info.fieldASTs); + + // function getSelections(info) { + // const fieldName = info.fieldName; + // const fieldNodes = info.fieldNodes; + // for(const i in fieldNodes) { + // const node = fieldNodes[i]; + // if (node.name.value == fieldName) { + // return node.selectionSet.selections; + // } + // } + // } + + // const selections = getSelections(info); + // console.log(selections); + // var all = []; + // selections.forEach((node) => { + // const key = node.name.value; + // if (node.selectionSet && node.selectionSet.selections) { + // node.selectionSet.selections.map((node) => { + // return node.name.value; + // }).forEach((subKey) => { + // all.push(key + '.' + subKey); + // }); + // } else { + // all.push(key); + // } + // }); + + // console.log(all); + console.log(args); + + // console.log(info); + // function flattenSelectionSets(nodes) { + // return nodes.reduce((node) => { + // const name = node.name.value; + // if (node.selectionSet && node.selectionSet.selections) { + // const descendants = flattenSelectionSets(node.selectionSet.selections); + // console.log(name, descendants); + // const results = []; + // descendants.forEach(descendant => { + // results.push() + // }); + // return results; + // } + // return name; + // }, []); + // } + // const selectedNodes = flattenSelectionSets(info.fieldNodes); + // console.log(selectedNodes); + // console.log(JSON.stringify(selectedNodes)); + // console.log(root, args, context, info); + // console.log(info.fieldNodes); + // console.log(info.operation.selectionSet.selections); + // info.fieldNodes.forEach((node) => { + // console.log(node.selectionSet.selections); + // }); + return rest.find(context.config, context.auth, className, args.where).then((restResponse) => { + //console.log(restResponse); + return restResponse.results; + }); + // return [{ + // className, + // foo: 'Hello', + // bar: true + // }, { + // className, + // foo: 'Hello', + // bar: false + // }] + } + }; + }); + MainSchemaOptions.fields['ParseObject'] = { type: ParseObject }; + return new GraphQLObjectType(MainSchemaOptions); + } + + Mutation() { + const MainSchemaMutationOptions = { + name: 'ParseSchemaMutation', + fields: {} + } + Object.keys(this.schema).forEach((className) => { + const { + inputType, objectType, updateType + } = loadClass(className, this.schema); + MainSchemaMutationOptions.fields['create' + className] = { + type: objectType, + fields: objectType.fields, + args: { input: { type: inputType }}, + name: 'create', + resolve: (a,data,context) => { + console.log('Create resolve ' + className); + //console.log(a,b,context); + return rest.create(context.config, context.auth, className, data).then((res) => { + console.log(res); + return Object.assign({className}, data, res.response); + }); + } + } + MainSchemaMutationOptions.fields['update' + className] = { + type: objectType, + args: { + objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' }, + input: { type: updateType } + }, + name: 'update', + resolve: (a, data, context) => { + console.log(a,data); + console.log('update resolve'); + const objectId = data.objectId; + delete data.objectId; + if (data.incrementKey) { + data[data.incrementKey.key] = {"__op":"Increment","amount":data.incrementKey.value}; + delete data.incrementKey; + } + return rest.update(context.config, context.auth, className, objectId, data).then((res) => { + console.log(res); + const response = Object.assign({className, objectId}, data, res.response); + console.log(response); + return response; + }); + } + } + MainSchemaMutationOptions.fields['destroy' + className] = { + type: objectType, + args: { + id: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' } + }, + name: 'destroy', + resolve: (a,b,c) => { + console.log('destroy resolve') + console.log(a,b,c); + return a; + } + } + MainSchemaMutationOptions.fields['destroyAll' + className] = { + type: objectType, + args: { + ids: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID))), name: 'objectIds' } + }, + name: 'destroyAll', + resolve: (a,b,c) => { + console.log('destroyAll resolve') + console.log(a,b,c); + return a; + } + } + }); + return new GraphQLObjectType(MainSchemaMutationOptions); + } + + Root() { + return Object.keys(this.schema).reduce((memo, className) => { + memo[className] = {} + return memo; + }, {}); + } +} diff --git a/src/graphql/index.js b/src/graphql/index.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/graphql/types/ACL.js b/src/graphql/types/ACL.js new file mode 100644 index 00000000000..cf3fe1cf0bf --- /dev/null +++ b/src/graphql/types/ACL.js @@ -0,0 +1,46 @@ +import { + GraphQLScalarType, + GraphQLList, + GraphQLString +} from 'graphql' + +export const GraphQLACL = new GraphQLScalarType({ + name: 'ACL', + fields: { + read: { + type: new GraphQLList(GraphQLString), + name: 'read', + description: 'Read access for the object' + }, + write: { + type: new GraphQLList(GraphQLString), + name: 'write', + description: 'Write access for the object' + } + }, + serialize: () => { + throw "not implemented" + }, + parseValue: () => { + throw "not implemented" + }, + parseLiteral: () => { + throw "not implemented" + } +}); + +export const GraphQLACLInput = GraphQLACL; /*new GraphQLInputObjectType({ + name: 'ACLInput', + fields: { + read: { + type: new GraphQLList(GraphQLString), + name: 'read', + description: 'Read access for the object' + }, + write: { + type: new GraphQLList(GraphQLString), + name: 'write', + description: 'Write access for the object' + } + } +});*/ diff --git a/src/graphql/types/Date.js b/src/graphql/types/Date.js new file mode 100644 index 00000000000..598d9a79383 --- /dev/null +++ b/src/graphql/types/Date.js @@ -0,0 +1,20 @@ +import { + GraphQLScalarType +} from 'graphql' + +// http://graphql.org/graphql-js/type/#graphqlscalartype +export const GraphQLDate = new GraphQLScalarType({ + name: 'Date', + serialize: (obj) => { + if (typeof a === 'string') { + return new Date(obj); + } + return obj; + }, + parseValue: () => { + throw "Date parseValue not implemented" + }, + parseLiteral: () => { + throw "Date parseLiteral not implemented" + } +}); diff --git a/src/graphql/types/File.js b/src/graphql/types/File.js new file mode 100644 index 00000000000..e10ee2343f1 --- /dev/null +++ b/src/graphql/types/File.js @@ -0,0 +1,20 @@ +import { + GraphQLObjectType, + GraphQLString +} from 'graphql' + +export const GraphQLFile = new GraphQLObjectType({ + name: 'File', + fields: { + name: { + type: GraphQLString, + name: 'name', + description: 'name of the file' + }, + url: { + type: GraphQLString, + name: 'url', + description: 'url of the file' + } + } +}); diff --git a/src/graphql/types/GeoPoint.js b/src/graphql/types/GeoPoint.js new file mode 100644 index 00000000000..e209366593c --- /dev/null +++ b/src/graphql/types/GeoPoint.js @@ -0,0 +1,45 @@ +import { + GraphQLScalarType, + GraphQLFloat +} from 'graphql' + +export const GraphQLGeoPoint = new GraphQLScalarType({ + name: 'GeoPoint', + fields: { + latitude: { + type: GraphQLFloat, + name: 'latitude', + description: 'laititude of the point, in degrees' + }, + longitude: { + type: GraphQLFloat, + name: 'latitude', + description: 'latitude of the point, in degrees' + } + }, + serialize: () => { + throw "not implemented" + }, + parseValue: () => { + throw "not implemented" + }, + parseLiteral: () => { + throw "not implemented" + } +}); + +export const GraphQLGeoPointInput = GraphQLGeoPoint; /*new GraphQLInputObjectType({ + name: 'GeoPointInput', + fields: { + latitude: { + type: GraphQLFloat, + name: 'latitude', + description: 'laititude of the point, in degrees' + }, + longitude: { + type: GraphQLFloat, + name: 'latitude', + description: 'latitude of the point, in degrees' + } + } +});*/ diff --git a/src/graphql/types/JSONObject.js b/src/graphql/types/JSONObject.js new file mode 100644 index 00000000000..271f39ac763 --- /dev/null +++ b/src/graphql/types/JSONObject.js @@ -0,0 +1,28 @@ +import { + GraphQLScalarType +} from 'graphql' +/* eslint-disable */ +// http://graphql.org/graphql-js/type/#graphqlscalartype +export const GraphQLJSONObject = new GraphQLScalarType({ + name: 'JSONObject', + serialize: (...options) => { + console.log(options); + throw "JSONObject serialize not implemented" + }, + parseValue: (...options) => { + console.log(options); + throw "JSONObject parseValue not implemented" + }, + parseLiteral: (litteral) => { + return litteral.fields.reduce((memo, field) => { + const value = field.value; + if (value.kind == 'IntValue') { + memo[field.name.value] = parseInt(value.value, 10); + } else { + memo[field.name.value] = value.value; + } + console.log(field); + return memo; + }, {}); + } +}); diff --git a/src/graphql/types/NumberQuery.js b/src/graphql/types/NumberQuery.js new file mode 100644 index 00000000000..d4f8b0cb0ee --- /dev/null +++ b/src/graphql/types/NumberQuery.js @@ -0,0 +1,29 @@ +import { + GraphQLScalarType, + Kind +} from 'graphql' + +export const NumberQuery = new GraphQLScalarType({ + name: 'NumberQuery', + serialize: () => { + throw "NumberQuery serialize not implemented" + }, + parseValue: () => { + throw "NumberQuery parseValue not implemented" + }, + parseLiteral: (ast) => { + if (ast.kind == Kind.OBJECT) { + const fields = ast.fields; + return fields.reduce((memo, field) => { + const operator = field.name.value; + const value = field.value.value; + memo['$' + operator] = parseFloat(value); + return memo; + }, {}); + } else if (ast.kind == Kind.INT || ast.kind == Kind.FLOAT) { + return parseFloat(ast.value); + } else { + throw 'Invalid literal for NumberQuery'; + } + } +}); diff --git a/src/graphql/types/Pointer.js b/src/graphql/types/Pointer.js new file mode 100644 index 00000000000..93c3ebad4bf --- /dev/null +++ b/src/graphql/types/Pointer.js @@ -0,0 +1,51 @@ +import { + //GraphQLObjectType, + //GraphQLInputObjectType, + GraphQLScalarType, + GraphQLID, + GraphQLString, +} from 'graphql' +/* eslint-disable */ +export const GraphQLPointer = new GraphQLScalarType({ + name: 'Pointer', + fields: { + objectId: { + type: GraphQLID, + name: 'objectId', + description: 'pointer\'s objectId' + }, + className: { + type: GraphQLString, + name: 'className', + description: 'pointer\'s className' + } + }, + serialize: () => { + throw "serialize not implemented" + }, + parseValue: () => { + throw "parseValue not implemented" + }, + parseLiteral: (l, a,b) => { + console.log('parse-literal!', l,a,b); + //console.log(a,b,c); + //throw "parseLiteral not implemented" + return {objectId: l.value }; + } +}); + +export const GraphQLPointerInput = GraphQLPointer; /*new GraphQLInputObjectType({ + name: 'PointerInput', + fields: { + objectId: { + type: GraphQLID, + name: 'objectId', + description: 'pointer\'s objectId' + }, + className: { + type: GraphQLString, + name: 'className', + description: 'pointer\'s className' + } + } +});*/ diff --git a/src/graphql/types/StringQuery.js b/src/graphql/types/StringQuery.js new file mode 100644 index 00000000000..7d54f499024 --- /dev/null +++ b/src/graphql/types/StringQuery.js @@ -0,0 +1,35 @@ +import { + GraphQLScalarType, + Kind +} from 'graphql' + +export const StringQuery = new GraphQLScalarType({ + name: 'StringQuery', + description: `# Query constraint on string parameters + Supported constraints: + + - key: "value" + - key: {$regex: "value"} + `, + serialize: () => { + throw "StringQuery serialize not implemented" + }, + parseValue: () => { + throw "StringQuery parseValue not implemented" + }, + parseLiteral: (ast) => { + if (ast.kind == Kind.OBJECT) { + const fields = ast.fields; + return fields.reduce((memo, field) => { + const operator = field.name.value; + const value = field.value.value; + memo['$' + operator] = value; + return memo; + }, {}); + } else if (ast.kind == Kind.STRING) { + return ast.value; + } else { + throw 'Invalid literal for StringQuery'; + } + } +}); diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js new file mode 100644 index 00000000000..0ab4b3d0360 --- /dev/null +++ b/src/graphql/types/index.js @@ -0,0 +1,123 @@ +import { + GraphQLString, + GraphQLFloat, + GraphQLNonNull, + GraphQLBoolean, + GraphQLID, +} from 'graphql' + +import { + GraphQLACL, + GraphQLACLInput +} from './ACL'; + +import { + GraphQLGeoPoint, + GraphQLGeoPointInput +} from './GeoPoint'; + +import { + GraphQLFile +} from './File'; + +import { + GraphQLDate +} from './Date'; + +import { + GraphQLPointer, + GraphQLPointerInput, +} from './Pointer'; + +import { + GraphQLJSONObject, +} from './JSONObject'; + +import { + StringQuery, +} from './StringQuery'; + +import { + NumberQuery, +} from './NumberQuery'; + +export { + GraphQLACL, + GraphQLACLInput, + GraphQLGeoPoint, + GraphQLGeoPointInput, + GraphQLFile, + GraphQLDate, + GraphQLPointer, + GraphQLJSONObject +} + +export function type(fieldName, field) { + if (fieldName === 'objectId') { + return new GraphQLNonNull(GraphQLID); + } + const type = field.type; + if (type == 'String') { + return GraphQLString; + } if (type == 'Number') { + return GraphQLFloat; + } if (type == 'Boolean') { + return GraphQLBoolean; + } if (type == 'GeoPoint') { + return GraphQLGeoPoint; + } if (type == 'File') { + return GraphQLFile; + } else if (type == 'ACL') { + return GraphQLACL; + } else if (type == 'Date') { + return GraphQLDate; + } +} + +export function inputType(fieldName, field) { + if (fieldName === 'objectId') { + return new GraphQLNonNull(GraphQLID); + } + const type = field.type; + if (type == 'String') { + return GraphQLString; + } if (type == 'Number') { + return GraphQLFloat; + } if (type == 'Boolean') { + return GraphQLBoolean; + } if (type == 'GeoPoint') { + return GraphQLGeoPointInput; + } if (type == 'File') { + return GraphQLFile; + } else if (type == 'ACL') { + return GraphQLACLInput; + } else if (type == 'Date') { + return GraphQLDate; + } else if (type == 'Pointer') { + return GraphQLPointerInput; + } +} + +export function queryType(fieldName, field) { + if (fieldName === 'objectId') { + return new GraphQLNonNull(GraphQLID); + } + const type = field.type; + if (type == 'String') { + return StringQuery; + } if (type == 'Number') { + return NumberQuery; + } if (type == 'Boolean') { + return GraphQLBoolean; + } if (type == 'GeoPoint') { + return GraphQLGeoPointInput; + } if (type == 'File') { + return GraphQLFile; + } else if (type == 'ACL') { + return GraphQLACLInput; + } else if (type == 'Date') { + return GraphQLDate; + } else if (type == 'Pointer') { + return GraphQLPointerInput; + } +} From e96a977def70a61d4612438a8ff7f16bd9b04db1 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 9 Apr 2017 18:36:44 -0400 Subject: [PATCH 03/62] wip --- spec/graphQLBridge.spec.js | 47 +++++- src/graphql/ParseClass.js | 62 +++----- src/graphql/ParseQuery.js | 74 --------- src/graphql/Schema.js | 220 +++++++++++++-------------- src/graphql/types/ACL.js | 16 +- src/graphql/types/GeoPoint.js | 16 +- src/graphql/types/JSONObject.js | 8 +- src/graphql/types/NumberInput.js | 39 +++++ src/graphql/types/NumberQuery.js | 9 ++ src/graphql/types/Pointer.js | 25 +-- src/graphql/types/QueryConstraint.js | 57 +++++++ src/graphql/types/StringQuery.js | 2 +- src/graphql/types/index.js | 11 +- 13 files changed, 290 insertions(+), 296 deletions(-) delete mode 100644 src/graphql/ParseQuery.js create mode 100644 src/graphql/types/NumberInput.js create mode 100644 src/graphql/types/QueryConstraint.js diff --git a/spec/graphQLBridge.spec.js b/spec/graphQLBridge.spec.js index 656be665bf9..fb1c320df61 100644 --- a/spec/graphQLBridge.spec.js +++ b/spec/graphQLBridge.spec.js @@ -3,6 +3,7 @@ const GraphQLParseSchema = require('../src/graphql/Schema').GraphQLParseSchema; const Config = require('../src/Config'); const Auth = require('../src/Auth').Auth; const { /*print, */printSchema, graphql } = require('graphql'); +const { addFunction } = require('../src/triggers'); //const { /*print, printSchema,*/ GraphQLID, GraphQLNonNull } = require('graphql'); let config; @@ -15,20 +16,33 @@ describe('graphQLbridge', () => { fit('base test', (done) => { let schema; + let nc; + let obj; + addFunction('MyFunction', ()=>{}, null, 'test'); + config.database.loadSchema() .then(dbSchema => { schema = dbSchema; return dbSchema.addClassIfNotExists('NewClass', { foo: { type: 'String' }, bar: { type: 'Boolean' }, - increment: { type: 'Number' } + increment: { type: 'Number' }, + other: { type: 'Pointer', targetClass: 'OtherClass' } }).then(() => { return dbSchema.addClassIfNotExists('OtherClass', { foo: { type: 'String' }, baz: { type: 'Pointer', targetClass: 'NewClass' } }) }).then(() => { - return schema.getAllClasses(true); + obj = new Parse.Object('OtherClass', {foo: 'baz'}); + nc = new Parse.Object('NewClass', {foo: 'hello'}); + return Parse.Object.saveAll([obj, nc]).then(() => { + nc.set('other', obj); + obj.set('baz', nc); + return Parse.Object.saveAll([obj, nc]); + }).then(() => { + return schema.getAllClasses(true); + }); }); }) .then((allClasses) => { @@ -37,7 +51,7 @@ describe('graphQLbridge', () => { return memo; }, {}); - const Schema = new GraphQLParseSchema(fullSchema); + const Schema = new GraphQLParseSchema(fullSchema, 'test'); const s = Schema.Schema(); const root = Schema.Root(); const context = { @@ -109,14 +123,35 @@ describe('graphQLbridge', () => { } */ - return graphql(s, ` + /*return graphql(s, ` query { - NewClass(where: {foo: { regex: "hello" }}) { + NewClass(where: {}) { objectId } + OtherClass(where: {}) { + objectId, + foo, + baz { + objectId, + increment, + other { + foo, + objectId, + baz { + objectId + } + } + } + } + } + `,root, context)*/ + return graphql(s, ` + mutation yMutation($id: ID!) { + destroyNewClass(objectId: $id) } - `,root, context) + `, root, context, {id: nc.id}) .then((res) => { + console.log('GOT RES!'); console.log(JSON.stringify(res, null, 2)); done(); }); diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 9c2f957d3c2..01dc0c4f515 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -3,12 +3,11 @@ import { GraphQLObjectType, GraphQLInputObjectType, //GraphQLString, - GraphQLNonNull, + //GraphQLNonNull, //GraphQLBoolean, - GraphQLID, + //GraphQLID, } from 'graphql' -/* eslint-disable */ import { queryType, inputType, @@ -18,34 +17,19 @@ import { GraphQLJSONObject } from './types' -import { - AtomicOps -} from './ParseQuery'; - function graphQLField(fieldName, field) { const gQLType = type(fieldName, field); if (!gQLType) { + /* eslint-disable */ + console.log('no type: ', fieldName, field); return; } - const fieldDef = { + const fieldType = (gQLType === GraphQLPointer ? `Pointer<${field.targetClass}>` : `${field.type}`); + return { name: fieldName, type: gQLType, - description: `Accessor for ${fieldName} (${field.type})`, - // resolve: () => { - // /* eslint-disable */ - // console.log(arguments); - // /* eslint-enable */ - // return arguments - // } + description: `Accessor for ${fieldName} (${fieldType})`, }; - if (gQLType === GraphQLPointer) { - fieldDef.args = { - objectId: { - type: new GraphQLNonNull(GraphQLID) - } - } - } - return fieldDef; } function graphQLInputField(fieldName, field) { @@ -53,10 +37,11 @@ function graphQLInputField(fieldName, field) { if (!gQLType) { return; } + const fieldType = (gQLType === GraphQLPointer ? `Pointer<${field.targetClass}>` : `${field.type}`); return { name: fieldName, type: gQLType, - description: `Setter for ${fieldName} (${field.type})`, + description: `Setter for ${fieldName} (${fieldType})`, }; } @@ -126,12 +111,14 @@ export const ParseObject = new GraphQLObjectType({ type: GraphQLJSONObject } }, - isTypeOf: (args, context, info) => { + isTypeOf: (args, context, info) => { // Use that type when impossible to map to a Schema type return typeof info.schema._typeMap[args.className] === 'undefined'; }, - resolve: () => { + resolve: () => { + /* eslint-disable */ console.log('RESOLVE CALLED!'); + /* eslint-enable */ } }); @@ -155,13 +142,10 @@ export class ParseClass { fields: () => { return this.buildFields(graphQLField); }, - resolve: () => { - console.log('Resilt!'); + resolve: () => { return; }, - isTypeOf: function(a,b, c,d) { - console.log(a,b,c,d); - console.log('isTypeOf, '+className); + isTypeOf: function(a) { return a.className == className; } }; @@ -197,8 +181,8 @@ export class ParseClass { return this.buildFields(graphQLInputField, true); }, resolve: this.get.bind(this), - isTypeOf: function(a,b,c,d) { - return a.className == className; + isTypeOf: function(input) { + return input.className == className; } }; } @@ -212,8 +196,8 @@ export class ParseClass { return this.buildFields(graphQLQueryField, true); }, resolve: this.get.bind(this), - isTypeOf: function(a,b,c,d) { - return a.className == className; + isTypeOf: function(input) { + return input.className == className; } }; } @@ -224,12 +208,11 @@ export class ParseClass { name: className + 'Update', description: `Parse Class ${className} Update`, fields: () => { - const fields = this.buildFields(graphQLInputField, true); - return Object.assign({}, fields, AtomicOps); + return this.buildFields(graphQLInputField, true); }, resolve: this.get.bind(this), - isTypeOf: function(a,b,c,d) { - return a.className == className; + isTypeOf: function(input) { + return input.className == className; } }; } @@ -254,6 +237,7 @@ export class ParseClass { /*eslint-disable*/ console.log('ParseClass resolve...'); console.log(a,b,c); + /* eslint-enable */ return null; } } diff --git a/src/graphql/ParseQuery.js b/src/graphql/ParseQuery.js deleted file mode 100644 index 4370d6860c1..00000000000 --- a/src/graphql/ParseQuery.js +++ /dev/null @@ -1,74 +0,0 @@ -import { - GraphQLInputObjectType, - GraphQLFloat, - GraphQLString, -} from 'graphql' - -const FloatKeyValIT = new GraphQLInputObjectType({ - name: 'FloatConstraint', - fields: { - key: { type: GraphQLString }, - value: { type: GraphQLFloat } - } -}); - -const StringKeyValIT = new GraphQLInputObjectType({ - name: 'StringConstraint', - fields: { - key: { type: GraphQLString }, - value: { type: GraphQLString } - } -}); - -export const ParseQuery = { - whereLessThan: { - type: FloatKeyValIT, - args: FloatKeyValIT.fields, - description: '' - }, - whereGreaterThan: { - type: FloatKeyValIT, - args: FloatKeyValIT.fields - }, - whereLessThanOrEqualTo: { - type: FloatKeyValIT, - args: FloatKeyValIT.fields - }, - whereGreaterThanOrEqualTo: { - type: FloatKeyValIT, - args: FloatKeyValIT.fields - }, - whereMatches: { - type: StringKeyValIT, - args: StringKeyValIT.fields - }, - whereExists: { - type: GraphQLString, - args: { key: GraphQLString } - }, - whereDoesNotExist: { - type: GraphQLString, - args: { key: GraphQLString } - }, - whereStartsWith: { - type: StringKeyValIT, - args: StringKeyValIT.fields - }, - whereEndsWith: { - type: StringKeyValIT, - args: StringKeyValIT.fields - } -}; - -/* eslint-disable */ - -export const AtomicOps = { - incrementKey: { - type: FloatKeyValIT, - args: FloatKeyValIT.fields - }, - unsetKey: { - type: GraphQLString, - args: { key: GraphQLString } - } -} diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index b9f68137adb..eb5c8f49bdf 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -11,18 +11,79 @@ import { GraphQLID, } from 'graphql' -// import { -// GraphQLJSONObject -// } from './types'; - import rest from '../rest'; -/* eslint-disable */ + +// Flatten all graphQL selections to the dot notation. +function reduceSelections(selections) { + return selections.reduce((memo, selection) => { + const value = selection.name.value; + if (selection.selectionSet === null) { + memo.push(value); + } else { + // Get the sub seletions and add on current key + const subSelections = reduceSelections(selection.selectionSet.selections); + memo = memo.concat(subSelections.map((key) => { + return value + '.' + key; + })); + } + return memo; + }, []); +} + +// Get the selections for the 1st node in a array of . separated keys +function getSelections(info) { + const node = info.fieldNodes[0]; + return reduceSelections(node.selectionSet.selections); +} + +function getQueryOptions(info) { + const selectedKeys = getSelections(info); + const keys = selectedKeys.join(','); + return { + keys, + include: keys + } +} + +function injectClassName(className, result) { + if (Array.isArray(result)) { + return result.map(injectClassName(className)); + } + return Object.assign({className}, result); +} + +function toGraphQLResult(className, singleResult) { + return (restResult) => { + const results = restResult.results; + if (singleResult) { + return injectClassName(className, results[0]); + } + return injectClassName(className, results); + } +} + +// Runs a find against the rest API +function runFind(context, info, className, where) { + const options = getQueryOptions(info); + return rest.find(context.config, context.auth, className, where, options) + .then(toGraphQLResult(className)); +} + +// runs a get against the rest API +function runGet(context, info, className, objectId) { + const options = getQueryOptions(info); + return rest.get(context.config, context.auth, className, objectId, options) + .then(toGraphQLResult(className, true)); +} export class GraphQLParseSchema { schema; types; - constructor(schema) { + applicationId; + + constructor(schema, applicationId) { this.schema = schema; + this.applicationId = applicationId; this.types = {}; } @@ -46,83 +107,21 @@ export class GraphQLParseSchema { MainSchemaOptions.fields[className] = { type: new GraphQLList(objectType), + description: `Use this endpoint to get or query ${className} objects`, args: { - id: { type: GraphQLID, name: 'objectId' }, + objectId: { type: GraphQLID, name: 'objectId' }, where: { type: queryType } }, resolve: (root, args, context, info) => { - // console.log(className, info.fieldNodes); - // console.log(info.fieldName); - // console.log(info.fieldASTs); - - // function getSelections(info) { - // const fieldName = info.fieldName; - // const fieldNodes = info.fieldNodes; - // for(const i in fieldNodes) { - // const node = fieldNodes[i]; - // if (node.name.value == fieldName) { - // return node.selectionSet.selections; - // } - // } - // } - - // const selections = getSelections(info); - // console.log(selections); - // var all = []; - // selections.forEach((node) => { - // const key = node.name.value; - // if (node.selectionSet && node.selectionSet.selections) { - // node.selectionSet.selections.map((node) => { - // return node.name.value; - // }).forEach((subKey) => { - // all.push(key + '.' + subKey); - // }); - // } else { - // all.push(key); - // } - // }); - - // console.log(all); - console.log(args); - - // console.log(info); - // function flattenSelectionSets(nodes) { - // return nodes.reduce((node) => { - // const name = node.name.value; - // if (node.selectionSet && node.selectionSet.selections) { - // const descendants = flattenSelectionSets(node.selectionSet.selections); - // console.log(name, descendants); - // const results = []; - // descendants.forEach(descendant => { - // results.push() - // }); - // return results; - // } - // return name; - // }, []); - // } - // const selectedNodes = flattenSelectionSets(info.fieldNodes); - // console.log(selectedNodes); - // console.log(JSON.stringify(selectedNodes)); - // console.log(root, args, context, info); - // console.log(info.fieldNodes); - // console.log(info.operation.selectionSet.selections); - // info.fieldNodes.forEach((node) => { - // console.log(node.selectionSet.selections); - // }); - return rest.find(context.config, context.auth, className, args.where).then((restResponse) => { - //console.log(restResponse); - return restResponse.results; - }); - // return [{ - // className, - // foo: 'Hello', - // bar: true - // }, { - // className, - // foo: 'Hello', - // bar: false - // }] + // Get the selections + let query = {}; + if (args.where) { + query = Object.assign(query, args.where); + } + if (args.objectId) { + query = Object.assign(query, { objectId: args.objectId }); + } + return runFind(context, info, className, query); } }; }); @@ -135,70 +134,57 @@ export class GraphQLParseSchema { name: 'ParseSchemaMutation', fields: {} } + // TODO: Refactor FunctionRouter to extract (as it's a new entry) + // TODO: Implement Functions as mutations + Object.keys(this.schema).forEach((className) => { const { inputType, objectType, updateType } = loadClass(className, this.schema); + MainSchemaMutationOptions.fields['create' + className] = { type: objectType, fields: objectType.fields, + description: `use this method to create a new ${className}`, args: { input: { type: inputType }}, name: 'create', - resolve: (a,data,context) => { - console.log('Create resolve ' + className); - //console.log(a,b,context); - return rest.create(context.config, context.auth, className, data).then((res) => { - console.log(res); - return Object.assign({className}, data, res.response); + resolve: (root, args, context, info) => { + return rest.create(context.config, context.auth, className, args).then((res) => { + // Run find to match graphQL style + return runGet(context, info, className, res.response.objectId); }); } } + MainSchemaMutationOptions.fields['update' + className] = { type: objectType, + description: `use this method to update an existing ${className}`, args: { objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' }, input: { type: updateType } }, name: 'update', - resolve: (a, data, context) => { - console.log(a,data); - console.log('update resolve'); - const objectId = data.objectId; - delete data.objectId; - if (data.incrementKey) { - data[data.incrementKey.key] = {"__op":"Increment","amount":data.incrementKey.value}; - delete data.incrementKey; - } - return rest.update(context.config, context.auth, className, objectId, data).then((res) => { - console.log(res); - const response = Object.assign({className, objectId}, data, res.response); - console.log(response); - return response; + resolve: (root, args, context, info) => { + const objectId = args.objectId; + const input = args.input; + return rest.update(context.config, context.auth, className, objectId, input).then(() => { + // Run find to match graphQL style + return runGet(context, info, className, objectId); }); } } + MainSchemaMutationOptions.fields['destroy' + className] = { - type: objectType, + type: GraphQLID, + description: `use this method to update delete an existing ${className}`, args: { - id: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' } + objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' } }, name: 'destroy', - resolve: (a,b,c) => { - console.log('destroy resolve') - console.log(a,b,c); - return a; - } - } - MainSchemaMutationOptions.fields['destroyAll' + className] = { - type: objectType, - args: { - ids: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID))), name: 'objectIds' } - }, - name: 'destroyAll', - resolve: (a,b,c) => { - console.log('destroyAll resolve') - console.log(a,b,c); - return a; + resolve: (root, args, context) => { + return rest.del(context.config, context.auth, className, args.objectId).then(() => { + return args.objectId; + }); } } }); diff --git a/src/graphql/types/ACL.js b/src/graphql/types/ACL.js index cf3fe1cf0bf..c90398a0b81 100644 --- a/src/graphql/types/ACL.js +++ b/src/graphql/types/ACL.js @@ -29,18 +29,4 @@ export const GraphQLACL = new GraphQLScalarType({ } }); -export const GraphQLACLInput = GraphQLACL; /*new GraphQLInputObjectType({ - name: 'ACLInput', - fields: { - read: { - type: new GraphQLList(GraphQLString), - name: 'read', - description: 'Read access for the object' - }, - write: { - type: new GraphQLList(GraphQLString), - name: 'write', - description: 'Write access for the object' - } - } -});*/ +export const GraphQLACLInput = GraphQLACL; diff --git a/src/graphql/types/GeoPoint.js b/src/graphql/types/GeoPoint.js index e209366593c..7a7c2dca855 100644 --- a/src/graphql/types/GeoPoint.js +++ b/src/graphql/types/GeoPoint.js @@ -28,18 +28,4 @@ export const GraphQLGeoPoint = new GraphQLScalarType({ } }); -export const GraphQLGeoPointInput = GraphQLGeoPoint; /*new GraphQLInputObjectType({ - name: 'GeoPointInput', - fields: { - latitude: { - type: GraphQLFloat, - name: 'latitude', - description: 'laititude of the point, in degrees' - }, - longitude: { - type: GraphQLFloat, - name: 'latitude', - description: 'latitude of the point, in degrees' - } - } -});*/ +export const GraphQLGeoPointInput = GraphQLGeoPoint; diff --git a/src/graphql/types/JSONObject.js b/src/graphql/types/JSONObject.js index 271f39ac763..764e00f9f22 100644 --- a/src/graphql/types/JSONObject.js +++ b/src/graphql/types/JSONObject.js @@ -1,16 +1,13 @@ import { GraphQLScalarType } from 'graphql' -/* eslint-disable */ // http://graphql.org/graphql-js/type/#graphqlscalartype export const GraphQLJSONObject = new GraphQLScalarType({ name: 'JSONObject', - serialize: (...options) => { - console.log(options); + serialize: () => { throw "JSONObject serialize not implemented" }, - parseValue: (...options) => { - console.log(options); + parseValue: () => { throw "JSONObject parseValue not implemented" }, parseLiteral: (litteral) => { @@ -21,7 +18,6 @@ export const GraphQLJSONObject = new GraphQLScalarType({ } else { memo[field.name.value] = value.value; } - console.log(field); return memo; }, {}); } diff --git a/src/graphql/types/NumberInput.js b/src/graphql/types/NumberInput.js new file mode 100644 index 00000000000..abb77fc98fc --- /dev/null +++ b/src/graphql/types/NumberInput.js @@ -0,0 +1,39 @@ +import { + GraphQLScalarType, + Kind +} from 'graphql' + +export const NumberInput = new GraphQLScalarType({ + name: 'NumberInput', + description: `Input for number + Supported schemas: + + - key: 1 + - key: {increment: 1} + `, + serialize: () => { + throw "NumberInput serialize not implemented" + }, + parseValue: () => { + throw "NumberInput parseValue not implemented" + }, + parseLiteral: (ast) => { + if (ast.kind == Kind.OBJECT) { + const fields = ast.fields; + if (fields.length != 1) { + throw 'Invalid NUmberInput'; + } + const field = fields[0]; + const operator = field.name.value; + if (operator != "increment") { + throw `the ${operator} operator is not supported`; + } + const value = field.value.value; + return {"__op":"Increment","amount": parseFloat(value)}; + } else if (ast.kind == Kind.INT || ast.kind == Kind.FLOAT) { + return parseFloat(ast.value); + } else { + throw 'Invalid literal for NumberInput'; + } + } +}); diff --git a/src/graphql/types/NumberQuery.js b/src/graphql/types/NumberQuery.js index d4f8b0cb0ee..019cb423723 100644 --- a/src/graphql/types/NumberQuery.js +++ b/src/graphql/types/NumberQuery.js @@ -5,6 +5,15 @@ import { export const NumberQuery = new GraphQLScalarType({ name: 'NumberQuery', + description: `Queries for number values + Supported constraints: + + - key: 1 + - key: {$lt: 1} # less than + - key: {$gt: 1} # greater than + - key: {$lte: 1} # less than or equal + - key: {$gte: 1} # greater than or equal + `, serialize: () => { throw "NumberQuery serialize not implemented" }, diff --git a/src/graphql/types/Pointer.js b/src/graphql/types/Pointer.js index 93c3ebad4bf..9a6219262fc 100644 --- a/src/graphql/types/Pointer.js +++ b/src/graphql/types/Pointer.js @@ -5,7 +5,7 @@ import { GraphQLID, GraphQLString, } from 'graphql' -/* eslint-disable */ + export const GraphQLPointer = new GraphQLScalarType({ name: 'Pointer', fields: { @@ -26,26 +26,9 @@ export const GraphQLPointer = new GraphQLScalarType({ parseValue: () => { throw "parseValue not implemented" }, - parseLiteral: (l, a,b) => { - console.log('parse-literal!', l,a,b); - //console.log(a,b,c); - //throw "parseLiteral not implemented" - return {objectId: l.value }; + parseLiteral: (litteral) => { + return { objectId: litteral.value }; } }); -export const GraphQLPointerInput = GraphQLPointer; /*new GraphQLInputObjectType({ - name: 'PointerInput', - fields: { - objectId: { - type: GraphQLID, - name: 'objectId', - description: 'pointer\'s objectId' - }, - className: { - type: GraphQLString, - name: 'className', - description: 'pointer\'s className' - } - } -});*/ +export const GraphQLPointerInput = GraphQLPointer; diff --git a/src/graphql/types/QueryConstraint.js b/src/graphql/types/QueryConstraint.js new file mode 100644 index 00000000000..e68322416be --- /dev/null +++ b/src/graphql/types/QueryConstraint.js @@ -0,0 +1,57 @@ +import { + GraphQLScalarType, + Kind +} from 'graphql' + +const supportedOperators = ['$eq', '$ne', '$in', '$nin', '$exists', '$select', '$dontSelect'] + +export const QueryConstraint = new GraphQLScalarType({ + name: 'QueryConstraint', + description: `Generic Query Constraint + Supported constraints: + + Equal To: + - key: "value" + - key: {$eq: "value"} + + Not Equal To + - key: {$ne: "value"} + + Contained in: + - key: {$in: ["value1", "value2"]} + Not Contained in: + - key: {$nin: ["value1", "value2"]} + + Exists: + - key: {$exists: true} + + This matches a value for a key in the result of a different query + - key: {$select: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} + + Requires that a key’s value not match a value for a key in the result of a different query + - key: {$dontSelect: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} + `, + serialize: () => { + throw "StringQuery serialize not implemented" + }, + parseValue: () => { + throw "StringQuery parseValue not implemented" + }, + parseLiteral: (ast) => { + if (ast.kind == Kind.OBJECT) { + const fields = ast.fields; + return fields.reduce((memo, field) => { + const operator = field.name.value; + if (supportedOperators.indexOf('$' + operator) > -1) { + const value = field.value.value; + memo['$' + operator] = value; + } + return memo; + }, {}); + } else if (ast.kind == Kind.STRING) { + return ast.value; + } else { + throw 'Invalid literal for StringQuery'; + } + } +}); diff --git a/src/graphql/types/StringQuery.js b/src/graphql/types/StringQuery.js index 7d54f499024..d9e3a378b03 100644 --- a/src/graphql/types/StringQuery.js +++ b/src/graphql/types/StringQuery.js @@ -5,7 +5,7 @@ import { export const StringQuery = new GraphQLScalarType({ name: 'StringQuery', - description: `# Query constraint on string parameters + description: `Query constraint on string parameters Supported constraints: - key: "value" diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index 0ab4b3d0360..f7cef23d1c3 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -41,6 +41,10 @@ import { NumberQuery, } from './NumberQuery'; +import { + NumberInput, +} from './NumberInput'; + export { GraphQLACL, GraphQLACLInput, @@ -71,6 +75,8 @@ export function type(fieldName, field) { return GraphQLACL; } else if (type == 'Date') { return GraphQLDate; + } else if (type == 'Pointer') { + return GraphQLPointer; } } @@ -82,7 +88,7 @@ export function inputType(fieldName, field) { if (type == 'String') { return GraphQLString; } if (type == 'Number') { - return GraphQLFloat; + return NumberInput; } if (type == 'Boolean') { return GraphQLBoolean; } if (type == 'GeoPoint') { @@ -114,7 +120,8 @@ export function queryType(fieldName, field) { } if (type == 'File') { return GraphQLFile; } else if (type == 'ACL') { - return GraphQLACLInput; + // cannot query on ACL! + return; } else if (type == 'Date') { return GraphQLDate; } else if (type == 'Pointer') { From ff4cc7af179386efc7b7e57304098dbc863044dd Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sat, 9 Dec 2017 17:16:20 -0500 Subject: [PATCH 04/62] Proper querying on all objects + tests --- spec/graphQLBridge.spec.js | 180 +++++++++++++++++++++++++------------ src/graphql/ParseClass.js | 30 +++---- src/graphql/Schema.js | 12 +-- 3 files changed, 144 insertions(+), 78 deletions(-) diff --git a/spec/graphQLBridge.spec.js b/spec/graphQLBridge.spec.js index fb1c320df61..dbe3687e2e8 100644 --- a/spec/graphQLBridge.spec.js +++ b/spec/graphQLBridge.spec.js @@ -8,57 +8,55 @@ const { addFunction } = require('../src/triggers'); let config; -describe('graphQLbridge', () => { +function setup(config) { + let schema; + return config.database.loadSchema() + .then(dbSchema => { + schema = dbSchema; + return dbSchema.addClassIfNotExists('NewClass', { + foo: { type: 'String' }, + bar: { type: 'Boolean' }, + increment: { type: 'Number' }, + other: { type: 'Pointer', targetClass: 'OtherClass' } + }).then(() => { + return dbSchema.addClassIfNotExists('OtherClass', { + foo: { type: 'String' }, + baz: { type: 'Pointer', targetClass: 'NewClass' } + }) + }).then(() => { + return schema.getAllClasses(true); + }); + }) + .then((allClasses) => { + const fullSchema = allClasses.reduce((memo, classDef) => { + memo[classDef.className] = classDef; + return memo; + }, {}); - beforeEach(() => { - config = new Config('test'); + const Schema = new GraphQLParseSchema(fullSchema, 'test'); + const s = Schema.Schema(); + const root = Schema.Root(); + return { + schema: s, + root, + } }); +} - fit('base test', (done) => { - let schema; - let nc; - let obj; - addFunction('MyFunction', ()=>{}, null, 'test'); - - config.database.loadSchema() - .then(dbSchema => { - schema = dbSchema; - return dbSchema.addClassIfNotExists('NewClass', { - foo: { type: 'String' }, - bar: { type: 'Boolean' }, - increment: { type: 'Number' }, - other: { type: 'Pointer', targetClass: 'OtherClass' } - }).then(() => { - return dbSchema.addClassIfNotExists('OtherClass', { - foo: { type: 'String' }, - baz: { type: 'Pointer', targetClass: 'NewClass' } - }) - }).then(() => { - obj = new Parse.Object('OtherClass', {foo: 'baz'}); - nc = new Parse.Object('NewClass', {foo: 'hello'}); - return Parse.Object.saveAll([obj, nc]).then(() => { - nc.set('other', obj); - obj.set('baz', nc); - return Parse.Object.saveAll([obj, nc]); - }).then(() => { - return schema.getAllClasses(true); - }); - }); - }) - .then((allClasses) => { - const fullSchema = allClasses.reduce((memo, classDef) => { - memo[classDef.className] = classDef; - return memo; - }, {}); +describe('graphQLbridge', () => { + let schema; + let root; + beforeEach((done) => { + config = Config.get('test'); + setup(config).then((result) => { + schema = result.schema; + root = result.root; + }).then(done); + }); - const Schema = new GraphQLParseSchema(fullSchema, 'test'); - const s = Schema.Schema(); - const root = Schema.Root(); - const context = { - config: config, - auth: new Auth({config}) - } - console.log(printSchema(s)); + fit('base test', (done) => { + console.log(printSchema(schema)); + done(); /* ` mutation { @@ -145,23 +143,89 @@ describe('graphQLbridge', () => { } } `,root, context)*/ - return graphql(s, ` - mutation yMutation($id: ID!) { - destroyNewClass(objectId: $id) + }); + + fit('test query', (done) => { + const context = { + config, + auth: new Auth({config}) + } + let obj = new Parse.Object('OtherClass', {foo: 'baz'}); + let nc = new Parse.Object('NewClass', {foo: 'hello'}); + return Parse.Object.saveAll([obj, nc]).then(() => { + nc.set('other', obj); + obj.set('baz', nc); + return Parse.Object.saveAll([obj, nc]); + }).then(() => { + return graphql(schema, ` + query { + NewClass { + objectId + } + OtherClass { + objectId, + foo, + baz { + objectId, + increment, + foo, + other { + foo, + objectId, + baz { + objectId + } + } + } + } + } + `,root, context) + }).then((res) => { + expect(res.data.NewClass).toBeDefined(); + expect(res.data.OtherClass).toBeDefined(); + expect(Array.isArray(res.data.NewClass)).toBeTruthy(); + expect(Array.isArray(res.data.OtherClass)).toBeTruthy(); + const newClasses = res.data.NewClass; + // Should only have objectId + newClasses.forEach((object) => { + expect(Object.keys(object).length).toBe(1); + expect(object.objectId).toBeDefined(); + }); + + const otherClasses = res.data.OtherClass; + const otherObject = otherClasses[0]; + expect(otherObject.objectId).toEqual(otherObject.baz.other.objectId); + expect(otherObject.baz.objectId).toEqual(otherObject.baz.other.baz.objectId); + }).then(done).catch(done.fail); + }) + + it('test destroy', (done) => { + const context = { + config, + auth: new Auth({config}) + } + let obj = new Parse.Object('OtherClass', {foo: 'baz'}); + let nc = new Parse.Object('NewClass', {foo: 'hello'}); + return Parse.Object.saveAll([obj, nc]).then(() => { + nc.set('other', obj); + obj.set('baz', nc); + return Parse.Object.saveAll([obj, nc]); + }).then(() => { + return graphql(schema, ` + mutation myMutation($id: ID!) { + destroyNewClass(objectId: $id) { + objectId, + bar + } } `, root, context, {id: nc.id}) - .then((res) => { + }).then((res) => { console.log('GOT RES!'); console.log(JSON.stringify(res, null, 2)); done(); - }); - //console.log(printSchema(singleSchema)); - //done(); - }) - .catch((err) => { + }).catch((err) => { console.error(err); done.fail(); - }) + }); }); - }); diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 01dc0c4f515..729ead816ac 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -2,17 +2,17 @@ import { GraphQLInterfaceType, GraphQLObjectType, GraphQLInputObjectType, - //GraphQLString, - //GraphQLNonNull, - //GraphQLBoolean, - //GraphQLID, + // GraphQLString, + // GraphQLNonNull, + // GraphQLBoolean, + // GraphQLID, } from 'graphql' import { queryType, inputType, type, - //GraphQLGeoPoint, + // GraphQLGeoPoint, GraphQLPointer, GraphQLJSONObject } from './types' @@ -59,7 +59,7 @@ function graphQLQueryField(fieldName, field) { const ParseClassCache = {}; -export function loadClass(className, schema) { +export function loadClass(className, schema) { if (!ParseClassCache[className]) { const c = new ParseClass(className, schema); const objectType = c.graphQLObjectType(); @@ -140,7 +140,7 @@ export class ParseClass { description: `Parse Class ${className}`, interfaces: [ParseObjectInterface], fields: () => { - return this.buildFields(graphQLField); + return this.buildFields(graphQLField, false, true); }, resolve: () => { return; @@ -151,25 +151,25 @@ export class ParseClass { }; } - buildFields(mapper, filterReserved, defaultValues = {}) { + buildFields(mapper, filterReserved = false, isQuery = false) { const fields = this.class.fields; return Object.keys(fields).reduce((memo, fieldName) => { if (filterReserved && reservedFieldNames.indexOf(fieldName) >= 0) { return memo; } const field = fields[fieldName]; - const gQLField = mapper(fieldName, field); - if (!gQLField) { - if (field.type == 'Pointer') { - memo[fieldName] = { - type: loadClass(field.targetClass, this.schema).objectType - } + let gQLField = mapper(fieldName, field); + if (field.type == 'Pointer' && isQuery) { + gQLField = { + type: loadClass(field.targetClass, this.schema).objectType } + } + if (!gQLField) { return memo; } memo[fieldName] = gQLField; return memo; - }, defaultValues); + }, {}); } graphQLInputConfig() { diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index eb5c8f49bdf..5452c32116c 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -47,7 +47,7 @@ function getQueryOptions(info) { function injectClassName(className, result) { if (Array.isArray(result)) { - return result.map(injectClassName(className)); + return result.map((res) => injectClassName(className, res)); } return Object.assign({className}, result); } @@ -175,15 +175,17 @@ export class GraphQLParseSchema { } MainSchemaMutationOptions.fields['destroy' + className] = { - type: GraphQLID, + type: objectType, description: `use this method to update delete an existing ${className}`, args: { objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' } }, name: 'destroy', - resolve: (root, args, context) => { - return rest.del(context.config, context.auth, className, args.objectId).then(() => { - return args.objectId; + resolve: (root, args, context, info) => { + return runGet(context, info, className, args.objectId).then((object) => { + return rest.del(context.config, context.auth, className, args.objectId).then(() => { + return object; + }); }); } } From a88f31d23d9aaadd6ef557cb4d612b20a3975fd5 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 16 Aug 2018 08:46:41 -0400 Subject: [PATCH 05/62] wip --- package-lock.json | 13 +++++++++++++ spec/graphQLBridge.spec.js | 12 ++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index abc7f22e3ca..4727ccf036f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5300,6 +5300,14 @@ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", "dev": true }, + "graphql": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.9.6.tgz", + "integrity": "sha1-UUQh6dIlwp38j9MFRZq65YgV7yw=", + "requires": { + "iterall": "^1.0.0" + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -6247,6 +6255,11 @@ "is-object": "1.0.1" } }, + "iterall": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", + "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" + }, "jasmine": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.1.0.tgz", diff --git a/spec/graphQLBridge.spec.js b/spec/graphQLBridge.spec.js index dbe3687e2e8..c10b183d2f5 100644 --- a/spec/graphQLBridge.spec.js +++ b/spec/graphQLBridge.spec.js @@ -1,9 +1,9 @@ /*eslint-disable*/ -const GraphQLParseSchema = require('../src/graphql/Schema').GraphQLParseSchema; -const Config = require('../src/Config'); -const Auth = require('../src/Auth').Auth; +const GraphQLParseSchema = require('../lib/graphql/Schema').GraphQLParseSchema; +const Config = require('../lib/Config'); +const Auth = require('../lib/Auth').Auth; const { /*print, */printSchema, graphql } = require('graphql'); -const { addFunction } = require('../src/triggers'); +const { addFunction } = require('../lib/triggers'); //const { /*print, printSchema,*/ GraphQLID, GraphQLNonNull } = require('graphql'); let config; @@ -54,7 +54,7 @@ describe('graphQLbridge', () => { }).then(done); }); - fit('base test', (done) => { + it('base test', (done) => { console.log(printSchema(schema)); done(); /* @@ -145,7 +145,7 @@ describe('graphQLbridge', () => { `,root, context)*/ }); - fit('test query', (done) => { + it('test query', (done) => { const context = { config, auth: new Auth({config}) From 91e229fb8daff802b8050326896ccd325e794177 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Wed, 22 Aug 2018 18:17:43 -0400 Subject: [PATCH 06/62] nits --- package-lock.json | 4785 +++++++++++++------------- package.json | 1 + src/ParseServer.js | 25 +- src/graphql/ParseClass.js | 4 +- src/graphql/Schema.js | 4 +- src/graphql/types/NumberQuery.js | 8 +- src/graphql/types/QueryConstraint.js | 24 +- src/graphql/types/StringQuery.js | 2 +- src/graphql/types/index.js | 2 + 9 files changed, 2427 insertions(+), 2428 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4727ccf036f..0eea06a3fa0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", "dev": true, "requires": { - "@babel/highlight": "7.0.0" + "@babel/highlight": "^7.0.0" } }, "@babel/generator": { @@ -19,11 +19,11 @@ "integrity": "sha512-/BM2vupkpbZXq22l1ALO7MqXJZH2k8bKVv8Y+pABFnzWdztDB/ZLveP5At21vLz5c2YtSE6p7j2FZEsqafMz5Q==", "dev": true, "requires": { - "@babel/types": "7.0.0", - "jsesc": "2.5.1", - "lodash": "4.17.10", - "source-map": "0.5.7", - "trim-right": "1.0.1" + "@babel/types": "^7.0.0", + "jsesc": "^2.5.1", + "lodash": "^4.17.10", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" }, "dependencies": { "jsesc": { @@ -52,9 +52,9 @@ "integrity": "sha512-Zo+LGvfYp4rMtz84BLF3bavFTdf8y4rJtMPTe2J+rxYmnDOIeH8le++VFI/pRJU+rQhjqiXxE4LMaIau28Tv1Q==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "7.0.0", - "@babel/template": "7.0.0", - "@babel/types": "7.0.0" + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/types": "^7.0.0" } }, "@babel/helper-get-function-arity": { @@ -63,7 +63,7 @@ "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", "dev": true, "requires": { - "@babel/types": "7.0.0" + "@babel/types": "^7.0.0" } }, "@babel/helper-split-export-declaration": { @@ -72,7 +72,7 @@ "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", "dev": true, "requires": { - "@babel/types": "7.0.0" + "@babel/types": "^7.0.0" } }, "@babel/highlight": { @@ -81,9 +81,9 @@ "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", "dev": true, "requires": { - "chalk": "2.4.1", - "esutils": "2.0.2", - "js-tokens": "4.0.0" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" }, "dependencies": { "ansi-styles": { @@ -92,7 +92,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -101,9 +101,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "js-tokens": { @@ -126,9 +126,9 @@ "integrity": "sha512-VLQZik/G5mjYJ6u19U3W2u7eM+rA/NGzH+GtHDFFkLTKLW66OasFrxZ/yK7hkyQcswrmvugFyZpDFRW0DjcjCw==", "dev": true, "requires": { - "@babel/code-frame": "7.0.0", - "@babel/parser": "7.0.0", - "@babel/types": "7.0.0" + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/types": "^7.0.0" }, "dependencies": { "@babel/parser": { @@ -145,15 +145,15 @@ "integrity": "sha512-ka/lwaonJZTlJyn97C4g5FYjPOx+Oxd3ab05hbDr1Mx9aP1FclJ+SUHyLx3Tx40sGmOVJApDxE6puJhd3ld2kw==", "dev": true, "requires": { - "@babel/code-frame": "7.0.0", - "@babel/generator": "7.0.0", - "@babel/helper-function-name": "7.0.0", - "@babel/helper-split-export-declaration": "7.0.0", - "@babel/parser": "7.0.0", - "@babel/types": "7.0.0", - "debug": "3.1.0", - "globals": "11.7.0", - "lodash": "4.17.10" + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.0.0", + "@babel/helper-function-name": "^7.0.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/types": "^7.0.0", + "debug": "^3.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" }, "dependencies": { "@babel/parser": { @@ -182,9 +182,9 @@ "integrity": "sha512-5tPDap4bGKTLPtci2SUl/B7Gv8RnuJFuQoWx26RJobS0fFrz4reUA3JnwIM+HVHEmWE0C1mzKhDtTp8NsWY02Q==", "dev": true, "requires": { - "esutils": "2.0.2", - "lodash": "4.17.10", - "to-fast-properties": "2.0.0" + "esutils": "^2.0.2", + "lodash": "^4.17.10", + "to-fast-properties": "^2.0.0" }, "dependencies": { "lodash": { @@ -217,8 +217,8 @@ "resolved": "https://registry.npmjs.org/@parse/node-gcm/-/node-gcm-0.14.12.tgz", "integrity": "sha512-fIXlY5LNR0VcPpFvwMvvczpByPNKxbCgMt/B3LwQqBjBCUJj/yEhBSePGUz2Kk+zOoj05v3KnG7ca+wZcAbh5A==", "requires": { - "debug": "3.1.0", - "lodash": "4.17.5", + "debug": "^3.1.0", + "lodash": "^4.17.5", "request": "2.85.0" }, "dependencies": { @@ -227,28 +227,28 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.8.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.1.2", - "stringstream": "0.0.6", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "hawk": "~6.0.2", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "stringstream": "~0.0.5", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" } } } @@ -258,10 +258,10 @@ "resolved": "https://registry.npmjs.org/@parse/push-adapter/-/push-adapter-3.0.0-alpha2.tgz", "integrity": "sha1-a7LNC/XbpL/8vzQykDDHPaOJ3mI=", "requires": { - "@parse/node-gcm": "0.14.12", - "apn": "3.0.0-alpha1", - "npmlog": "4.1.2", - "parse": "1.11.1" + "@parse/node-gcm": "^0.14.0", + "apn": "^3.0.0-alpha1", + "npmlog": "^4.0.2", + "parse": "^1.9.2" }, "dependencies": { "parse": { @@ -269,9 +269,9 @@ "resolved": "https://registry.npmjs.org/parse/-/parse-1.11.1.tgz", "integrity": "sha1-VY5TnULZ+4khDggiCdbzsD1frtU=", "requires": { - "babel-runtime": "6.26.0", - "ws": "3.3.3", - "xmlhttprequest": "1.8.0" + "babel-runtime": "^6.11.6", + "ws": "^3.3.1", + "xmlhttprequest": "^1.7.0" } }, "ws": { @@ -279,9 +279,9 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.2", - "ultron": "1.1.1" + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" } } } @@ -291,7 +291,7 @@ "resolved": "https://registry.npmjs.org/@parse/s3-files-adapter/-/s3-files-adapter-1.2.1.tgz", "integrity": "sha1-2dN8zoXj1CsogqX/J4m+wbF+xnU=", "requires": { - "aws-sdk": "2.235.1" + "aws-sdk": "^2.59.0" } }, "@parse/simple-mailgun-adapter": { @@ -314,15 +314,15 @@ "integrity": "sha512-XvW9mq4eegS5kQ4nNbgQjMC6AOO+9Oy6H+8iRTPCRkpJElhtecPUu/gqriZXEF/qqGXF891JKA3onCUjEwwWuQ==", "dev": true, "requires": { - "@semantic-release/error": "2.2.0", - "aggregate-error": "1.0.0", - "debug": "3.1.0", - "dir-glob": "2.0.0", - "execa": "0.10.0", - "fs-extra": "6.0.1", - "lodash": "4.17.5", - "micromatch": "3.1.10", - "p-reduce": "1.0.0" + "@semantic-release/error": "^2.1.0", + "aggregate-error": "^1.0.0", + "debug": "^3.1.0", + "dir-glob": "^2.0.0", + "execa": "^0.10.0", + "fs-extra": "^6.0.0", + "lodash": "^4.17.4", + "micromatch": "^3.1.4", + "p-reduce": "^1.0.0" }, "dependencies": { "arr-diff": { @@ -343,16 +343,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -361,7 +361,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -372,13 +372,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "debug": { @@ -396,7 +396,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -405,7 +405,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-accessor-descriptor": { @@ -414,7 +414,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -423,7 +423,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -434,7 +434,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -443,7 +443,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -454,9 +454,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { @@ -473,14 +473,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -489,7 +489,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -498,7 +498,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -509,10 +509,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -521,7 +521,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -532,9 +532,9 @@ "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "4.0.0", - "universalify": "0.1.2" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "is-accessor-descriptor": { @@ -543,7 +543,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -552,7 +552,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -561,9 +561,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "is-number": { @@ -572,7 +572,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -581,7 +581,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -604,19 +604,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } } } @@ -632,7 +632,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "requires": { - "mime-types": "2.1.18", + "mime-types": "~2.1.18", "negotiator": "0.6.1" } }, @@ -648,7 +648,7 @@ "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", "dev": true, "requires": { - "acorn": "5.7.1" + "acorn": "^5.0.3" } }, "agent-base": { @@ -656,7 +656,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", "requires": { - "es6-promisify": "5.0.0" + "es6-promisify": "^5.0.0" } }, "aggregate-error": { @@ -665,8 +665,8 @@ "integrity": "sha1-iINE2tAiCnLjr1CQYRf0h3GSX6w=", "dev": true, "requires": { - "clean-stack": "1.3.0", - "indent-string": "3.2.0" + "clean-stack": "^1.0.0", + "indent-string": "^3.0.0" } }, "ajv": { @@ -674,10 +674,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } }, "ajv-keywords": { @@ -692,21 +692,21 @@ "integrity": "sha1-9AK8LhgwX6vZldvc07cFe73X00c=", "dev": true, "requires": { - "ampersand-version": "1.0.2", - "lodash": "4.17.5" + "ampersand-version": "^1.0.2", + "lodash": "^4.6.1" } }, "ampersand-state": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/ampersand-state/-/ampersand-state-5.0.3.tgz", - "integrity": "sha1-M0AupgQ3WvZKJJ9mSgMSwRYNpHU=", + "integrity": "sha512-sr904K5zvw6mkGjFHhTcfBIdpoJ6mn/HrFg7OleRmBpw3apLb3Z0gVrgRTb7kK1wOLI34vs4S+IXqNHUeqWCzw==", "dev": true, "requires": { - "ampersand-events": "2.0.2", - "ampersand-version": "1.0.2", - "array-next": "0.0.1", - "key-tree-store": "1.3.0", - "lodash": "4.17.5" + "ampersand-events": "^2.0.1", + "ampersand-version": "^1.0.0", + "array-next": "~0.0.1", + "key-tree-store": "^1.3.0", + "lodash": "^4.12.0" } }, "ampersand-version": { @@ -715,8 +715,8 @@ "integrity": "sha1-/489TOrE0yzNg/a9Zpc5f3tZ4sA=", "dev": true, "requires": { - "find-root": "0.1.2", - "through2": "0.6.5" + "find-root": "^0.1.1", + "through2": "^0.6.3" } }, "ansi": { @@ -731,7 +731,7 @@ "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "dev": true, "requires": { - "string-width": "2.1.1" + "string-width": "^2.0.0" }, "dependencies": { "ansi-regex": { @@ -752,8 +752,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -762,7 +762,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -787,12 +787,12 @@ "anymatch": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "optional": true, "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" } }, "apn": { @@ -800,33 +800,33 @@ "resolved": "https://registry.npmjs.org/apn/-/apn-3.0.0-alpha1.tgz", "integrity": "sha512-o/wVNAxaQ7eegLZ69rtNEgiIQFngPClOAFFsVowsBDjVIaFsRccI+kfTda6rmVuiMSiGBMurlX01jyMNlO+AXQ==", "requires": { - "debug": "3.1.0", - "jsonwebtoken": "8.2.1", - "node-forge": "0.7.5", - "verror": "1.10.0" + "debug": "^3.1.0", + "jsonwebtoken": "^8.1.0", + "node-forge": "^0.7.1", + "verror": "^1.10.0" } }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=" + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "are-we-there-yet": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "sprintf-js": "1.0.3" + "sprintf-js": "~1.0.2" } }, "arr-diff": { @@ -836,13 +836,13 @@ "dev": true, "optional": true, "requires": { - "arr-flatten": "1.1.0" + "arr-flatten": "^1.0.1" } }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "arr-union": { @@ -868,7 +868,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "1.0.3" + "array-uniq": "^1.0.1" } }, "array-uniq": { @@ -895,7 +895,7 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": "~2.1.0" } }, "assert-plus": { @@ -919,7 +919,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", "requires": { - "lodash": "4.17.5" + "lodash": "^4.14.0" } }, "async-each": { @@ -931,7 +931,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=" + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, "asynckit": { "version": "0.4.0", @@ -939,9 +939,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "aws-sdk": { @@ -984,21 +984,21 @@ "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=", "dev": true, "requires": { - "babel-core": "6.26.0", - "babel-polyfill": "6.26.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "chokidar": "1.7.0", - "commander": "2.17.1", - "convert-source-map": "1.5.1", - "fs-readdir-recursive": "1.1.0", - "glob": "7.1.2", - "lodash": "4.17.5", - "output-file-sync": "1.1.2", - "path-is-absolute": "1.0.1", - "slash": "1.0.0", - "source-map": "0.5.7", - "v8flags": "2.1.1" + "babel-core": "^6.26.0", + "babel-polyfill": "^6.26.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "chokidar": "^1.6.1", + "commander": "^2.11.0", + "convert-source-map": "^1.5.0", + "fs-readdir-recursive": "^1.0.0", + "glob": "^7.1.2", + "lodash": "^4.17.4", + "output-file-sync": "^1.1.2", + "path-is-absolute": "^1.0.1", + "slash": "^1.0.0", + "source-map": "^0.5.6", + "v8flags": "^2.1.1" }, "dependencies": { "source-map": { @@ -1015,9 +1015,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, "babel-core": { @@ -1026,31 +1026,31 @@ "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-generator": "6.26.1", - "babel-helpers": "6.24.1", - "babel-messages": "6.23.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "convert-source-map": "1.5.1", - "debug": "2.6.9", - "json5": "0.5.1", - "lodash": "4.17.5", - "minimatch": "3.0.4", - "path-is-absolute": "1.0.1", - "private": "0.1.8", - "slash": "1.0.0", - "source-map": "0.5.7" + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.0", + "debug": "^2.6.8", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.7", + "slash": "^1.0.0", + "source-map": "^0.5.6" }, "dependencies": { "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -1070,12 +1070,12 @@ "integrity": "sha512-itv1MwE3TMbY0QtNfeL7wzak1mV47Uy+n6HtSOO4Xd7rvmO+tsGQSgyOEEgo6Y2vHZKZphaoelNeSVj4vkLA1g==", "dev": true, "requires": { - "@babel/code-frame": "7.0.0", - "@babel/parser": "7.0.0", - "@babel/traverse": "7.0.0", - "@babel/types": "7.0.0", + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", "eslint-scope": "3.7.1", - "eslint-visitor-keys": "1.0.0" + "eslint-visitor-keys": "^1.0.0" }, "dependencies": { "@babel/parser": { @@ -1089,17 +1089,17 @@ "babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha1-GERAjTuPDTWkBOp6wYDwh6YBvZA=", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.5", - "source-map": "0.5.7", - "trim-right": "1.0.1" + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" }, "dependencies": { "source-map": { @@ -1116,9 +1116,9 @@ "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", "dev": true, "requires": { - "babel-helper-explode-assignable-expression": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-helper-call-delegate": { @@ -1127,10 +1127,10 @@ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", "dev": true, "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-define-map": { @@ -1139,10 +1139,10 @@ "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", "dev": true, "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.5" + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" } }, "babel-helper-explode-assignable-expression": { @@ -1151,9 +1151,9 @@ "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-function-name": { @@ -1162,11 +1162,11 @@ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", "dev": true, "requires": { - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-get-function-arity": { @@ -1175,8 +1175,8 @@ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-helper-hoist-variables": { @@ -1185,8 +1185,8 @@ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-helper-optimise-call-expression": { @@ -1195,8 +1195,8 @@ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-helper-regex": { @@ -1205,9 +1205,9 @@ "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.5" + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" } }, "babel-helper-remap-async-to-generator": { @@ -1216,11 +1216,11 @@ "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", "dev": true, "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helper-replace-supers": { @@ -1229,12 +1229,12 @@ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", "dev": true, "requires": { - "babel-helper-optimise-call-expression": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-helpers": { @@ -1243,8 +1243,8 @@ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-messages": { @@ -1253,7 +1253,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-check-es2015-constants": { @@ -1262,7 +1262,7 @@ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-syntax-async-functions": { @@ -1301,9 +1301,9 @@ "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", "dev": true, "requires": { - "babel-helper-remap-async-to-generator": "6.24.1", - "babel-plugin-syntax-async-functions": "6.13.0", - "babel-runtime": "6.26.0" + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-arrow-functions": { @@ -1312,7 +1312,7 @@ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-block-scoped-functions": { @@ -1321,7 +1321,7 @@ "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-block-scoping": { @@ -1330,11 +1330,11 @@ "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.5" + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" } }, "babel-plugin-transform-es2015-classes": { @@ -1343,15 +1343,15 @@ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", "dev": true, "requires": { - "babel-helper-define-map": "6.26.0", - "babel-helper-function-name": "6.24.1", - "babel-helper-optimise-call-expression": "6.24.1", - "babel-helper-replace-supers": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-computed-properties": { @@ -1360,8 +1360,8 @@ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-destructuring": { @@ -1370,7 +1370,7 @@ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-duplicate-keys": { @@ -1379,8 +1379,8 @@ "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-for-of": { @@ -1389,7 +1389,7 @@ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-function-name": { @@ -1398,9 +1398,9 @@ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", "dev": true, "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-literals": { @@ -1409,7 +1409,7 @@ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-modules-amd": { @@ -1418,9 +1418,9 @@ "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-modules-commonjs": { @@ -1429,10 +1429,10 @@ "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", "dev": true, "requires": { - "babel-plugin-transform-strict-mode": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" } }, "babel-plugin-transform-es2015-modules-systemjs": { @@ -1441,9 +1441,9 @@ "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", "dev": true, "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-modules-umd": { @@ -1452,9 +1452,9 @@ "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-plugin-transform-es2015-object-super": { @@ -1463,8 +1463,8 @@ "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", "dev": true, "requires": { - "babel-helper-replace-supers": "6.24.1", - "babel-runtime": "6.26.0" + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-parameters": { @@ -1473,12 +1473,12 @@ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", "dev": true, "requires": { - "babel-helper-call-delegate": "6.24.1", - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-shorthand-properties": { @@ -1487,8 +1487,8 @@ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-spread": { @@ -1497,7 +1497,7 @@ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-sticky-regex": { @@ -1506,9 +1506,9 @@ "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", "dev": true, "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-plugin-transform-es2015-template-literals": { @@ -1517,7 +1517,7 @@ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-typeof-symbol": { @@ -1526,7 +1526,7 @@ "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-es2015-unicode-regex": { @@ -1535,9 +1535,9 @@ "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", "dev": true, "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "regexpu-core": "2.0.0" + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" } }, "babel-plugin-transform-exponentiation-operator": { @@ -1546,9 +1546,9 @@ "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", "dev": true, "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", - "babel-plugin-syntax-exponentiation-operator": "6.13.0", - "babel-runtime": "6.26.0" + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-flow-strip-types": { @@ -1557,8 +1557,8 @@ "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", "dev": true, "requires": { - "babel-plugin-syntax-flow": "6.18.0", - "babel-runtime": "6.26.0" + "babel-plugin-syntax-flow": "^6.18.0", + "babel-runtime": "^6.22.0" } }, "babel-plugin-transform-object-rest-spread": { @@ -1567,8 +1567,8 @@ "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", "dev": true, "requires": { - "babel-plugin-syntax-object-rest-spread": "6.13.0", - "babel-runtime": "6.26.0" + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" } }, "babel-plugin-transform-regenerator": { @@ -1577,7 +1577,7 @@ "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", "dev": true, "requires": { - "regenerator-transform": "0.10.1" + "regenerator-transform": "^0.10.0" } }, "babel-plugin-transform-strict-mode": { @@ -1586,8 +1586,8 @@ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" } }, "babel-polyfill": { @@ -1596,9 +1596,9 @@ "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "core-js": "2.5.6", - "regenerator-runtime": "0.10.5" + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" }, "dependencies": { "regenerator-runtime": { @@ -1612,39 +1612,39 @@ "babel-preset-env": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", - "integrity": "sha1-oYtWTMm5r99KrleuPBsNmRiOb0g=", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "6.22.0", - "babel-plugin-syntax-trailing-function-commas": "6.22.0", - "babel-plugin-transform-async-to-generator": "6.24.1", - "babel-plugin-transform-es2015-arrow-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoping": "6.26.0", - "babel-plugin-transform-es2015-classes": "6.24.1", - "babel-plugin-transform-es2015-computed-properties": "6.24.1", - "babel-plugin-transform-es2015-destructuring": "6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", - "babel-plugin-transform-es2015-for-of": "6.23.0", - "babel-plugin-transform-es2015-function-name": "6.24.1", - "babel-plugin-transform-es2015-literals": "6.22.0", - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", - "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.1", - "babel-plugin-transform-es2015-object-super": "6.24.1", - "babel-plugin-transform-es2015-parameters": "6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", - "babel-plugin-transform-es2015-spread": "6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "6.24.1", - "babel-plugin-transform-es2015-template-literals": "6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-exponentiation-operator": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0", - "browserslist": "2.11.3", - "invariant": "2.2.4", - "semver": "5.5.1" + "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^2.1.2", + "invariant": "^2.2.2", + "semver": "^5.3.0" } }, "babel-register": { @@ -1653,13 +1653,13 @@ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "6.26.0", - "babel-runtime": "6.26.0", - "core-js": "2.5.6", - "home-or-tmp": "2.0.0", - "lodash": "4.17.5", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18" + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" } }, "babel-runtime": { @@ -1667,8 +1667,8 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "requires": { - "core-js": "2.5.6", - "regenerator-runtime": "0.11.1" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, "babel-template": { @@ -1677,11 +1677,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.5" + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" } }, "babel-traverse": { @@ -1690,21 +1690,21 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.4", - "lodash": "4.17.5" + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" }, "dependencies": { "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -1718,16 +1718,16 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.5", - "to-fast-properties": "1.0.3" + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" } }, "babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, "balanced-match": { @@ -1739,16 +1739,16 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "dependencies": { "define-property": { @@ -1757,36 +1757,36 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "isobject": { @@ -1798,7 +1798,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } @@ -1820,75 +1820,75 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "resolved": false, "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.5" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "chownr": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "resolved": false, "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "optional": true }, "debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "resolved": false, "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "optional": true, "requires": { @@ -1897,172 +1897,172 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "optional": true, "requires": { - "minipass": "2.3.3" + "minipass": "^2.2.1" } }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "resolved": false, "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "optional": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "optional": true }, "iconv-lite": { "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "resolved": false, "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minipass": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.3.tgz", + "resolved": false, "integrity": "sha512-/jAn9/tEX4gnpyRATxgHEOV6xbcyxgT7iUnxo9Y3+OB0zX00TgKIv/2FZCf5brBbICcwbLqVv2ImjvWWrQMSYw==", "requires": { - "safe-buffer": "5.1.2", - "yallist": "3.0.2" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" }, "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "yallist": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" } } }, "minizlib": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "resolved": false, "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "optional": true, "requires": { - "minipass": "2.3.3" + "minipass": "^2.2.1" } }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -2070,145 +2070,145 @@ }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "resolved": false, "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "optional": true }, "needle": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.1.tgz", + "resolved": false, "integrity": "sha512-t/ZswCM9JTWjAdXS9VpvqhI2Ct2sL2MdY4fUXqGJaGBk13ge99ObqRksRTbBE56K+wxUXwwfZYOuZHifFW9q+Q==", "optional": true, "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.23", - "sax": "1.2.4" + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { "version": "0.10.2", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.2.tgz", + "resolved": false, "integrity": "sha512-16lql9QTqs6KsB9fl3neWyZm02KxIKdI9FlJjrB0y7eMTP5Nyz+xalwPbOlw3iw7EejllJPmlJSnY711PLD1ug==", "optional": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.1", - "nopt": "4.0.1", - "npm-packlist": "1.1.10", - "npmlog": "4.1.2", - "rc": "1.2.8", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "4.4.4" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", + "resolved": false, "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", "optional": true }, "npm-packlist": { "version": "1.1.10", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", + "resolved": false, "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "optional": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "optional": true, "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "optional": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "optional": true }, "rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "optional": true, "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "optional": true } @@ -2216,120 +2216,120 @@ }, "readable-stream": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", + "resolved": false, "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "rimraf": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "resolved": false, "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "optional": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "resolved": false, "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "optional": true }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "optional": true }, "semver": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "resolved": false, "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "resolved": false, "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "optional": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "optional": true }, "tar": { "version": "4.4.4", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.4.tgz", + "resolved": false, "integrity": "sha512-mq9ixIYfNF9SK0IS/h2HKMu8Q2iaCuhDDsZhdEag/FHv8fOaYld4vN7ouMgcSSt5WKZzPs8atclTcJm36OTh4w==", "optional": true, "requires": { - "chownr": "1.0.1", - "fs-minipass": "1.2.5", - "minipass": "2.3.3", - "minizlib": "1.1.0", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.2", - "yallist": "3.0.2" + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" }, "dependencies": { "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "optional": true }, "yallist": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "resolved": false, "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", "optional": true } @@ -2337,22 +2337,22 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "resolved": false, "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2 || 2" } }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } @@ -2369,7 +2369,7 @@ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "optional": true, "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "bcryptjs": { @@ -2389,14 +2389,14 @@ "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "dev": true, "requires": { - "readable-stream": "2.3.6", - "safe-buffer": "5.1.2" + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" } }, "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha1-2VUfnemPH82h5oPRfukaBgLuLrk=", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", "dev": true }, "body-parser": { @@ -2405,15 +2405,15 @@ "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", + "depd": "~1.1.2", + "http-errors": "~1.6.3", "iconv-lite": "0.4.23", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.2", "raw-body": "2.3.3", - "type-is": "1.6.16" + "type-is": "~1.6.16" }, "dependencies": { "debug": { @@ -2437,7 +2437,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "requires": { - "hoek": "4.2.1" + "hoek": "4.x.x" } }, "boxen": { @@ -2446,13 +2446,13 @@ "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "dev": true, "requires": { - "ansi-align": "2.0.0", - "camelcase": "4.1.0", - "chalk": "2.4.1", - "cli-boxes": "1.0.0", - "string-width": "2.1.1", - "term-size": "1.2.0", - "widest-line": "2.0.0" + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" }, "dependencies": { "ansi-regex": { @@ -2467,7 +2467,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -2476,9 +2476,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "is-fullwidth-code-point": { @@ -2493,8 +2493,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -2503,7 +2503,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -2511,10 +2511,10 @@ "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -2525,19 +2525,19 @@ "dev": true, "optional": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" } }, "browserslist": { "version": "2.11.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", - "integrity": "sha1-/jYWeu0bvN5IJ+v+cTR6LMcLmbI=", + "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", "dev": true, "requires": { - "caniuse-lite": "1.0.30000836", - "electron-to-chromium": "1.3.45" + "caniuse-lite": "^1.0.30000792", + "electron-to-chromium": "^1.3.30" } }, "bson": { @@ -2551,9 +2551,9 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "requires": { - "base64-js": "1.3.0", - "ieee754": "1.1.8", - "isarray": "1.0.0" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, "buffer-alloc": { @@ -2562,8 +2562,8 @@ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", "dev": true, "requires": { - "buffer-alloc-unsafe": "1.1.0", - "buffer-fill": "1.0.0" + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" } }, "buffer-alloc-unsafe": { @@ -2602,18 +2602,18 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", - "dev": true, - "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" }, "dependencies": { "isobject": { @@ -2630,7 +2630,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "0.2.0" + "callsites": "^0.2.0" } }, "callsites": { @@ -2668,19 +2668,19 @@ "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", "dev": true, "requires": { - "underscore-contrib": "0.3.0" + "underscore-contrib": "~0.3.0" } }, "caw": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", - "integrity": "sha1-bDygcfwZRyCIPC3F2psHS/x+npU=", + "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", "dev": true, "requires": { - "get-proxy": "2.1.0", - "isurl": "1.0.0", - "tunnel-agent": "0.6.0", - "url-to-options": "1.0.1" + "get-proxy": "^2.0.0", + "isurl": "^1.0.0-alpha5", + "tunnel-agent": "^0.6.0", + "url-to-options": "^1.0.1" } }, "chalk": { @@ -2689,11 +2689,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" }, "dependencies": { "supports-color": { @@ -2716,22 +2716,22 @@ "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", "dev": true, "requires": { - "css-select": "1.2.0", - "dom-serializer": "0.1.0", - "entities": "1.1.1", - "htmlparser2": "3.9.2", - "lodash.assignin": "4.2.0", - "lodash.bind": "4.2.1", - "lodash.defaults": "4.2.0", - "lodash.filter": "4.6.0", - "lodash.flatten": "4.4.0", - "lodash.foreach": "4.5.0", - "lodash.map": "4.6.0", - "lodash.merge": "4.6.1", - "lodash.pick": "4.4.0", - "lodash.reduce": "4.6.0", - "lodash.reject": "4.6.0", - "lodash.some": "4.6.0" + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" } }, "chokidar": { @@ -2741,15 +2741,15 @@ "dev": true, "optional": true, "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.2.3", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" } }, "ci-info": { @@ -2761,19 +2761,19 @@ "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha1-gVyZ6oT2gJUp0vRXkb34JxE1LWY=", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { "define-property": { @@ -2782,7 +2782,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "isobject": { @@ -2811,10 +2811,10 @@ "integrity": "sha1-dfpfcowwjMSsWUsF4GzF2A2szYY=", "dev": true, "requires": { - "d": "0.1.1", - "es5-ext": "0.10.46", - "memoizee": "0.3.10", - "timers-ext": "0.1.5" + "d": "~0.1.1", + "es5-ext": "~0.10.2", + "memoizee": "0.3.x", + "timers-ext": "0.1.x" } }, "cli-cursor": { @@ -2823,7 +2823,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "2.0.0" + "restore-cursor": "^2.0.0" } }, "cli-width": { @@ -2835,7 +2835,7 @@ "clui": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/clui/-/clui-0.3.6.tgz", - "integrity": "sha1-jh5c6nMypuVAg/WdoMy+HW8vp4c=", + "integrity": "sha512-Z4UbgZILlIAjkEkZiDOa2aoYjohKx7fa6DxIh6cE9A6WNWZ61iXfQc6CmdC9SKdS5nO0P0UyQ+WfoXfB65e3HQ==", "dev": true, "requires": { "cli-color": "0.3.2" @@ -2857,17 +2857,17 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "color-convert": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha1-wSYRB66y8pTr/+ye2eytUppgl+0=", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "^1.1.1" } }, "color-name": { @@ -2886,7 +2886,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "commander": { @@ -2912,8 +2912,8 @@ "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=", "dev": true, "requires": { - "ini": "1.3.5", - "proto-list": "1.2.4" + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, "configstore": { @@ -2922,12 +2922,12 @@ "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", "dev": true, "requires": { - "dot-prop": "4.2.0", - "graceful-fs": "4.1.11", - "make-dir": "1.2.0", - "unique-string": "1.0.0", - "write-file-atomic": "2.3.0", - "xdg-basedir": "3.0.0" + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" } }, "connected-domain": { @@ -2949,7 +2949,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "convert-source-map": { "version": "1.5.1", @@ -2989,7 +2989,7 @@ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { - "capture-stack-trace": "1.0.0" + "capture-stack-trace": "^1.0.0" } }, "cross-env": { @@ -2998,8 +2998,8 @@ "integrity": "sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==", "dev": true, "requires": { - "cross-spawn": "6.0.5", - "is-windows": "1.0.2" + "cross-spawn": "^6.0.5", + "is-windows": "^1.0.0" }, "dependencies": { "cross-spawn": { @@ -3008,24 +3008,26 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "1.0.4", - "path-key": "2.0.1", - "semver": "5.5.1", - "shebang-command": "1.2.0", - "which": "1.3.0" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } } } }, "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "lru-cache": "4.1.3", - "shebang-command": "1.2.0", - "which": "1.3.0" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, "cryptiles": { @@ -3033,15 +3035,15 @@ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "requires": { - "boom": "5.2.0" + "boom": "5.x.x" }, "dependencies": { "boom": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha1-XdnabuOl8wIHdDYpDLcX0/SlTgI=", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", "requires": { - "hoek": "4.2.1" + "hoek": "4.x.x" } } } @@ -3058,10 +3060,10 @@ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { - "boolbase": "1.0.0", - "css-what": "2.1.0", + "boolbase": "~1.0.0", + "css-what": "2.1", "domutils": "1.5.1", - "nth-check": "1.0.1" + "nth-check": "~1.0.1" } }, "css-what": { @@ -3081,7 +3083,7 @@ "integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=", "dev": true, "requires": { - "es5-ext": "0.10.46" + "es5-ext": "~0.10.2" } }, "dashdash": { @@ -3089,7 +3091,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "data-uri-to-buffer": { @@ -3100,7 +3102,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { "ms": "2.0.0" } @@ -3117,14 +3119,14 @@ "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", "dev": true, "requires": { - "decompress-tar": "4.1.1", - "decompress-tarbz2": "4.1.1", - "decompress-targz": "4.1.1", - "decompress-unzip": "4.0.1", - "graceful-fs": "4.1.11", - "make-dir": "1.2.0", - "pify": "2.3.0", - "strip-dirs": "2.1.0" + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" } }, "decompress-response": { @@ -3133,37 +3135,37 @@ "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, "requires": { - "mimic-response": "1.0.1" + "mimic-response": "^1.0.0" } }, "decompress-tar": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", - "integrity": "sha1-cYy9P8sWIJcW5womuE57pFkuWvE=", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", "dev": true, "requires": { - "file-type": "5.2.0", - "is-stream": "1.1.0", - "tar-stream": "1.6.1" + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" } }, "decompress-tarbz2": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", - "integrity": "sha1-MIKluIDqQEOBY0nzeLVsUWvho5s=", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", "dev": true, "requires": { - "decompress-tar": "4.1.1", - "file-type": "6.2.0", - "is-stream": "1.1.0", - "seek-bzip": "1.0.5", - "unbzip2-stream": "1.2.5" + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" }, "dependencies": { "file-type": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", - "integrity": "sha1-5QzXXTVv/tTjBtxPW89Sp5kDqRk=", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", "dev": true } } @@ -3171,12 +3173,12 @@ "decompress-targz": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", - "integrity": "sha1-wJvDXE0R894J8tLaU+neI+fOHu4=", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", "dev": true, "requires": { - "decompress-tar": "4.1.1", - "file-type": "5.2.0", - "is-stream": "1.1.0" + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" } }, "decompress-unzip": { @@ -3185,10 +3187,10 @@ "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", "dev": true, "requires": { - "file-type": "3.9.0", - "get-stream": "2.3.1", - "pify": "2.3.0", - "yauzl": "2.10.0" + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" }, "dependencies": { "file-type": { @@ -3203,8 +3205,8 @@ "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "dev": true, "requires": { - "object-assign": "4.1.1", - "pinkie-promise": "2.0.1" + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" } } } @@ -3231,7 +3233,7 @@ "resolved": "https://registry.npmjs.org/deepcopy/-/deepcopy-1.0.0.tgz", "integrity": "sha512-WJrecobaoqqgQHtvRI2/VCzWoWXPAnFYyAkF/spmL46lZMnd0gW0gLGuyeFVSrqt2B3s0oEEj6i+j2L/2QiS4g==", "requires": { - "type-detect": "4.0.8" + "type-detect": "^4.0.8" } }, "define-properties": { @@ -3240,47 +3242,47 @@ "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", "dev": true, "requires": { - "foreach": "2.0.5", - "object-keys": "1.0.12" + "foreach": "^2.0.5", + "object-keys": "^1.0.8" } }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "isobject": { @@ -3292,7 +3294,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } @@ -3302,9 +3304,9 @@ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", "requires": { - "ast-types": "0.11.5", - "escodegen": "1.11.0", - "esprima": "3.1.3" + "ast-types": "0.x.x", + "escodegen": "1.x.x", + "esprima": "3.x.x" } }, "del": { @@ -3313,13 +3315,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.1", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" } }, "delayed-stream": { @@ -3348,7 +3350,7 @@ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, "dir-glob": { @@ -3357,8 +3359,8 @@ "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", "dev": true, "requires": { - "arrify": "1.0.1", - "path-type": "3.0.0" + "arrify": "^1.0.1", + "path-type": "^3.0.0" } }, "docopt": { @@ -3370,10 +3372,10 @@ "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { - "esutils": "2.0.2" + "esutils": "^2.0.2" } }, "dom-serializer": { @@ -3382,8 +3384,8 @@ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" + "domelementtype": "~1.1.1", + "entities": "~1.1.1" }, "dependencies": { "domelementtype": { @@ -3406,7 +3408,7 @@ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", "dev": true, "requires": { - "domelementtype": "1.3.0" + "domelementtype": "1" } }, "domutils": { @@ -3415,8 +3417,8 @@ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "dev": true, "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" + "dom-serializer": "0", + "domelementtype": "1" } }, "dot-prop": { @@ -3425,7 +3427,7 @@ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { - "is-obj": "1.0.1" + "is-obj": "^1.0.0" } }, "double-ended-queue": { @@ -3439,13 +3441,13 @@ "integrity": "sha1-eQuwQkaJE2EVzpPyqhWUbG2AbQ4=", "dev": true, "requires": { - "extend": "3.0.1", - "graceful-fs": "4.1.11", - "limiter": "1.1.3", - "mkdirp": "0.5.1", - "npmlog": "2.0.4", - "request": "2.88.0", - "rimraf": "2.6.2" + "extend": "^3.0.0", + "graceful-fs": "^4.1.3", + "limiter": "^1.1.0", + "mkdirp": "^0.5.1", + "npmlog": "^2.0.3", + "request": "^2.69.0", + "rimraf": "^2.5.2" }, "dependencies": { "gauge": { @@ -3454,11 +3456,11 @@ "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", "dev": true, "requires": { - "ansi": "0.3.1", - "has-unicode": "2.0.1", - "lodash.pad": "4.5.1", - "lodash.padend": "4.6.1", - "lodash.padstart": "4.6.1" + "ansi": "^0.3.0", + "has-unicode": "^2.0.0", + "lodash.pad": "^4.1.0", + "lodash.padend": "^4.1.0", + "lodash.padstart": "^4.1.0" } }, "npmlog": { @@ -3467,9 +3469,9 @@ "integrity": "sha1-mLUlMPJRTKkNCexbIsiEZyI3VpI=", "dev": true, "requires": { - "ansi": "0.3.1", - "are-we-there-yet": "1.1.4", - "gauge": "1.2.7" + "ansi": "~0.3.1", + "are-we-there-yet": "~1.1.2", + "gauge": "~1.2.5" } } } @@ -3477,20 +3479,20 @@ "download": { "version": "6.2.5", "resolved": "https://registry.npmjs.org/download/-/download-6.2.5.tgz", - "integrity": "sha1-rNalQuTNC7Qspwz8mMnkOwcDlxQ=", + "integrity": "sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA==", "dev": true, "requires": { - "caw": "2.0.1", - "content-disposition": "0.5.2", - "decompress": "4.2.0", - "ext-name": "5.0.0", + "caw": "^2.0.0", + "content-disposition": "^0.5.2", + "decompress": "^4.0.0", + "ext-name": "^5.0.0", "file-type": "5.2.0", - "filenamify": "2.1.0", - "get-stream": "3.0.0", - "got": "7.1.0", - "make-dir": "1.2.0", - "p-event": "1.3.0", - "pify": "3.0.0" + "filenamify": "^2.0.0", + "get-stream": "^3.0.0", + "got": "^7.0.0", + "make-dir": "^1.0.0", + "p-event": "^1.0.0", + "pify": "^3.0.0" }, "dependencies": { "pify": { @@ -3519,8 +3521,8 @@ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "optional": true, "requires": { - "jsbn": "0.1.1", - "safer-buffer": "2.1.2" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "ecdsa-sig-formatter": { @@ -3528,7 +3530,7 @@ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "^5.0.1" } }, "ee-first": { @@ -3550,10 +3552,10 @@ "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { - "once": "1.4.0" + "once": "^1.4.0" } }, "entities": { @@ -3568,11 +3570,11 @@ "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.3", - "is-callable": "1.1.3", - "is-regex": "1.0.4" + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" } }, "es-to-primitive": { @@ -3581,9 +3583,9 @@ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "dev": true, "requires": { - "is-callable": "1.1.3", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" } }, "es5-ext": { @@ -3592,9 +3594,9 @@ "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", "dev": true, "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1", - "next-tick": "1.0.0" + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" } }, "es6-iterator": { @@ -3603,9 +3605,9 @@ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.46", - "es6-symbol": "3.1.1" + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" }, "dependencies": { "d": { @@ -3614,7 +3616,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "0.10.46" + "es5-ext": "^0.10.9" } } } @@ -3629,7 +3631,7 @@ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "requires": { - "es6-promise": "4.2.4" + "es6-promise": "^4.0.3" } }, "es6-symbol": { @@ -3638,8 +3640,8 @@ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.46" + "d": "1", + "es5-ext": "~0.10.14" }, "dependencies": { "d": { @@ -3648,7 +3650,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "0.10.46" + "es5-ext": "^0.10.9" } } } @@ -3659,10 +3661,10 @@ "integrity": "sha1-cGzvnpmqI2undmwjnIueKG6n0ig=", "dev": true, "requires": { - "d": "0.1.1", - "es5-ext": "0.10.46", - "es6-iterator": "0.1.3", - "es6-symbol": "2.0.1" + "d": "~0.1.1", + "es5-ext": "~0.10.6", + "es6-iterator": "~0.1.3", + "es6-symbol": "~2.0.1" }, "dependencies": { "es6-iterator": { @@ -3671,9 +3673,9 @@ "integrity": "sha1-1vWLjE/EE8JJtLqhl2j45NfIlE4=", "dev": true, "requires": { - "d": "0.1.1", - "es5-ext": "0.10.46", - "es6-symbol": "2.0.1" + "d": "~0.1.1", + "es5-ext": "~0.10.5", + "es6-symbol": "~2.0.1" } }, "es6-symbol": { @@ -3682,8 +3684,8 @@ "integrity": "sha1-dhtcZ8/U8dGK+yNPaR1nhoLLO/M=", "dev": true, "requires": { - "d": "0.1.1", - "es5-ext": "0.10.46" + "d": "~0.1.1", + "es5-ext": "~0.10.5" } } } @@ -3704,11 +3706,11 @@ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "requires": { - "esprima": "3.1.3", - "estraverse": "4.2.0", - "esutils": "2.0.2", - "optionator": "0.8.2", - "source-map": "0.6.1" + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" } }, "eslint": { @@ -3717,44 +3719,44 @@ "integrity": "sha512-MA0YWJLeK7BPEBxJCINvKnQdKpeTwbac3Xonh0PPFjWYZkowZf+Xl30lJWJ/BWOqFQdAdPcyOh0aBqlbH6ojAg==", "dev": true, "requires": { - "ajv": "6.5.1", - "babel-code-frame": "6.26.0", - "chalk": "2.4.1", - "cross-spawn": "6.0.5", - "debug": "3.1.0", - "doctrine": "2.1.0", - "eslint-scope": "4.0.0", - "eslint-visitor-keys": "1.0.0", - "espree": "4.0.0", - "esquery": "1.0.1", - "esutils": "2.0.2", - "file-entry-cache": "2.0.0", - "functional-red-black-tree": "1.0.1", - "glob": "7.1.2", - "globals": "11.7.0", - "ignore": "3.3.10", - "imurmurhash": "0.1.4", - "inquirer": "5.2.0", - "is-resolvable": "1.1.0", - "js-yaml": "3.12.0", - "json-stable-stringify-without-jsonify": "1.0.1", - "levn": "0.3.0", - "lodash": "4.17.5", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "optionator": "0.8.2", - "path-is-inside": "1.0.2", - "pluralize": "7.0.0", - "progress": "2.0.0", - "regexpp": "1.1.0", - "require-uncached": "1.0.3", - "semver": "5.5.1", - "string.prototype.matchall": "2.0.0", - "strip-ansi": "4.0.0", - "strip-json-comments": "2.0.1", - "table": "4.0.3", - "text-table": "0.2.0" + "ajv": "^6.5.0", + "babel-code-frame": "^6.26.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0", + "espree": "^4.0.0", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.5.0", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^5.2.0", + "is-resolvable": "^1.1.0", + "js-yaml": "^3.11.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.5", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^1.1.0", + "require-uncached": "^1.0.3", + "semver": "^5.5.0", + "string.prototype.matchall": "^2.0.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^4.0.3", + "text-table": "^0.2.0" }, "dependencies": { "ajv": { @@ -3763,10 +3765,10 @@ "integrity": "sha512-pgZos1vgOHDiC7gKNbZW8eKvCnNXARv2oqrGQT7Hzbq5Azp7aZG6DJzADnkuSq7RH6qkXp4J/m68yPX/2uBHyQ==", "dev": true, "requires": { - "fast-deep-equal": "2.0.1", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.4.1", - "uri-js": "4.2.2" + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.1" } }, "ansi-regex": { @@ -3781,7 +3783,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -3790,9 +3792,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "cross-spawn": { @@ -3801,11 +3803,11 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "1.0.4", - "path-key": "2.0.1", - "semver": "5.5.1", - "shebang-command": "1.2.0", - "which": "1.3.0" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, "eslint-scope": { @@ -3814,8 +3816,8 @@ "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", "dev": true, "requires": { - "esrecurse": "4.2.1", - "estraverse": "4.2.0" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, "fast-deep-equal": { @@ -3842,7 +3844,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -3851,7 +3853,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -3862,7 +3864,7 @@ "integrity": "sha512-VpnNeC4E6t2E2NCw8Oveda91p8CPEaujZURC1KhHe4lBRZJla3o0DVvZu1QGXQZO1ZJ4vUmy3TCp95PqGvIZgQ==", "dev": true, "requires": { - "lodash": "4.17.5" + "lodash": "^4.15.0" } }, "eslint-scope": { @@ -3871,14 +3873,14 @@ "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", "dev": true, "requires": { - "esrecurse": "4.2.1", - "estraverse": "4.2.0" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, "eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, "espree": { @@ -3887,8 +3889,8 @@ "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", "dev": true, "requires": { - "acorn": "5.7.1", - "acorn-jsx": "4.1.1" + "acorn": "^5.6.0", + "acorn-jsx": "^4.1.1" } }, "esprima": { @@ -3902,16 +3904,16 @@ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { - "estraverse": "4.2.0" + "estraverse": "^4.0.0" } }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "4.2.0" + "estraverse": "^4.1.0" } }, "estraverse": { @@ -3935,8 +3937,8 @@ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.46" + "d": "1", + "es5-ext": "~0.10.14" }, "dependencies": { "d": { @@ -3945,7 +3947,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "0.10.46" + "es5-ext": "^0.10.9" } } } @@ -3956,13 +3958,13 @@ "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { - "duplexer": "0.1.1", - "from": "0.1.7", - "map-stream": "0.1.0", + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", "pause-stream": "0.0.11", - "split": "0.3.3", - "stream-combiner": "0.0.4", - "through": "2.3.8" + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" }, "dependencies": { "split": { @@ -3971,7 +3973,7 @@ "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { - "through": "2.3.8" + "through": "2" } } } @@ -3987,28 +3989,13 @@ "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", "dev": true, "requires": { - "cross-spawn": "6.0.5", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "1.0.4", - "path-key": "2.0.1", - "semver": "5.5.1", - "shebang-command": "1.2.0", - "which": "1.3.0" - } - } + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } }, "expand-brackets": { @@ -4018,7 +4005,7 @@ "dev": true, "optional": true, "requires": { - "is-posix-bracket": "0.1.1" + "is-posix-bracket": "^0.1.0" } }, "expand-range": { @@ -4028,7 +4015,7 @@ "dev": true, "optional": true, "requires": { - "fill-range": "2.2.4" + "fill-range": "^2.1.0" } }, "express": { @@ -4036,36 +4023,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.4", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "1.0.4", + "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.1", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "finalhandler": "1.1.0", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.3", + "proxy-addr": "~2.0.2", "qs": "6.5.1", - "range-parser": "1.2.0", + "range-parser": "~1.2.0", "safe-buffer": "5.1.1", "send": "0.16.1", "serve-static": "1.13.1", "setprototypeof": "1.1.0", - "statuses": "1.3.1", - "type-is": "1.6.16", + "statuses": "~1.3.1", + "type-is": "~1.6.15", "utils-merge": "1.0.1", - "vary": "1.1.2" + "vary": "~1.1.2" }, "dependencies": { "body-parser": { @@ -4074,21 +4061,21 @@ "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", + "depd": "~1.1.1", + "http-errors": "~1.6.2", "iconv-lite": "0.4.19", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "1.6.16" + "type-is": "~1.6.15" } }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } @@ -4101,7 +4088,7 @@ "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=" + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, "raw-body": { "version": "2.3.2", @@ -4127,7 +4114,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": "1.3.1" + "statuses": ">= 1.3.1 < 2" } }, "setprototypeof": { @@ -4149,23 +4136,34 @@ } } }, + "express-graphql": { + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.6.12.tgz", + "integrity": "sha512-ouLWV0hRw4hnaLtXzzwhdC79ewxKbY2PRvm05mPc/zOH5W5WVCHDQ1SmNxEPBQdUeeSNh29aIqW9zEQkA3kMuA==", + "requires": { + "accepts": "^1.3.0", + "content-type": "^1.0.4", + "http-errors": "^1.3.0", + "raw-body": "^2.3.2" + } + }, "ext-list": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha1-C5jmTtgvWs8PKTG6v2khLvUt3Tc=", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", "dev": true, "requires": { - "mime-db": "1.33.0" + "mime-db": "^1.28.0" } }, "ext-name": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha1-cHgZgdGD7hXROZPIgiBFxQbI8KY=", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", "dev": true, "requires": { - "ext-list": "2.2.2", - "sort-keys-length": "1.0.1" + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" } }, "extend": { @@ -4179,17 +4177,17 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -4200,9 +4198,9 @@ "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { - "chardet": "0.4.2", - "iconv-lite": "0.4.23", - "tmp": "0.0.33" + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" } }, "extglob": { @@ -4212,7 +4210,7 @@ "dev": true, "optional": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "extsprintf": { @@ -4246,7 +4244,7 @@ "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, "requires": { - "pend": "1.2.0" + "pend": "~1.2.0" } }, "figures": { @@ -4255,7 +4253,7 @@ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5" + "escape-string-regexp": "^1.0.5" } }, "file-entry-cache": { @@ -4264,8 +4262,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "1.3.0", - "object-assign": "4.1.1" + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" } }, "file-type": { @@ -4298,9 +4296,9 @@ "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", "dev": true, "requires": { - "filename-reserved-regex": "2.0.0", - "strip-outer": "1.0.1", - "trim-repeated": "1.0.0" + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.0", + "trim-repeated": "^1.0.0" } }, "fill-range": { @@ -4310,11 +4308,11 @@ "dev": true, "optional": true, "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "3.0.0", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" } }, "finalhandler": { @@ -4323,18 +4321,18 @@ "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", "requires": { "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" }, "dependencies": { "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } @@ -4358,10 +4356,10 @@ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", "dev": true, "requires": { - "circular-json": "0.3.3", - "del": "2.2.2", - "graceful-fs": "4.1.11", - "write": "0.2.1" + "circular-json": "^0.3.1", + "del": "^2.0.2", + "graceful-fs": "^4.1.2", + "write": "^0.2.1" } }, "flow-bin": { @@ -4383,7 +4381,7 @@ "dev": true, "optional": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } }, "foreach": { @@ -4402,9 +4400,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { - "asynckit": "0.4.0", + "asynckit": "^0.4.0", "combined-stream": "1.0.6", - "mime-types": "2.1.18" + "mime-types": "^2.1.12" } }, "forwarded": { @@ -4418,7 +4416,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "0.2.2" + "map-cache": "^0.2.2" } }, "fresh": { @@ -4441,18 +4439,18 @@ "fs-extra": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha1-DYUhIuW8W+tFP7Ao6cDJvzY0DJQ=", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "4.0.0", - "universalify": "0.1.2" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha1-4y/AMKLM7kSmtTcTCNpUvgs5fSc=", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", "dev": true }, "fs.realpath": { @@ -4468,8 +4466,8 @@ "dev": true, "optional": true, "requires": { - "nan": "2.10.0", - "node-pre-gyp": "0.9.1" + "nan": "^2.9.2", + "node-pre-gyp": "^0.9.0" }, "dependencies": { "abbrev": { @@ -4499,8 +4497,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { @@ -4515,7 +4513,7 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -4589,7 +4587,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -4606,14 +4604,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { @@ -4623,12 +4621,12 @@ "dev": true, "optional": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -4645,7 +4643,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": "^2.1.0" } }, "ignore-walk": { @@ -4655,7 +4653,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -4665,8 +4663,8 @@ "dev": true, "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -4681,7 +4679,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -4697,7 +4695,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -4712,8 +4710,8 @@ "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, "requires": { - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" } }, "minizlib": { @@ -4723,7 +4721,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "mkdirp": { @@ -4749,9 +4747,9 @@ "dev": true, "optional": true, "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.21", - "sax": "1.2.4" + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { @@ -4761,16 +4759,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.0", - "nopt": "4.0.1", - "npm-packlist": "1.1.10", - "npmlog": "4.1.2", - "rc": "1.2.8", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "4.4.1" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { @@ -4780,8 +4778,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -4798,8 +4796,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { @@ -4809,10 +4807,10 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -4834,7 +4832,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -4858,8 +4856,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -4883,10 +4881,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -4905,13 +4903,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { @@ -4921,7 +4919,7 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { @@ -4971,9 +4969,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -4983,7 +4981,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { @@ -4992,7 +4990,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "tar": { @@ -5002,13 +5000,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "1.0.1", - "fs-minipass": "1.2.5", - "minipass": "2.2.4", - "minizlib": "1.1.0", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" } }, "util-deprecate": { @@ -5025,7 +5023,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2" } }, "wrappy": { @@ -5047,7 +5045,7 @@ "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", "requires": { - "readable-stream": "1.1.14", + "readable-stream": "1.1.x", "xregexp": "2.0.0" }, "dependencies": { @@ -5061,10 +5059,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -5091,14 +5089,14 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "gaze": { @@ -5107,7 +5105,7 @@ "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "dev": true, "requires": { - "globule": "1.2.0" + "globule": "^1.0.0" } }, "get-mongodb-version": { @@ -5116,11 +5114,11 @@ "integrity": "sha512-Y5CLUaeyW7VS5lVS2gVfwYynuKTJsxygSQlkwxpivSVN+d9hkPDRuOqRdIs0kpJ8PyzKHGuqZC/Rpb7kAm1Rcg==", "dev": true, "requires": { - "debug": "2.6.9", - "lodash.startswith": "4.2.1", - "minimist": "1.2.0", - "mongodb": "3.1.4", - "which": "1.3.0" + "debug": "^2.2.0", + "lodash.startswith": "^4.2.1", + "minimist": "^1.1.1", + "mongodb": "^3.1.4", + "which": "^1.1.1" }, "dependencies": { "debug": { @@ -5143,10 +5141,10 @@ "get-proxy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", - "integrity": "sha1-NJ8rTZHUTE1NTpy6KtkBQ/rF75M=", + "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", "dev": true, "requires": { - "npm-conf": "1.1.3" + "npm-conf": "^1.1.0" } }, "get-stream": { @@ -5160,12 +5158,12 @@ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.2.tgz", "integrity": "sha512-ZD325dMZOgerGqF/rF6vZXyFGTAay62svjQIT+X/oU2PtxYpFxvSkbsdi+oxIrsNxlZVd4y8wUDqkaExWTI/Cw==", "requires": { - "data-uri-to-buffer": "1.2.0", - "debug": "2.6.9", - "extend": "3.0.1", - "file-uri-to-path": "1.0.0", - "ftp": "0.3.10", - "readable-stream": "2.3.6" + "data-uri-to-buffer": "1", + "debug": "2", + "extend": "3", + "file-uri-to-path": "1", + "ftp": "~0.3.10", + "readable-stream": "2" }, "dependencies": { "debug": { @@ -5189,21 +5187,21 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-base": { @@ -5213,8 +5211,8 @@ "dev": true, "optional": true, "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" } }, "glob-parent": { @@ -5223,7 +5221,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^2.0.0" } }, "global-dirs": { @@ -5232,13 +5230,13 @@ "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "dev": true, "requires": { - "ini": "1.3.5" + "ini": "^1.3.4" } }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, "globby": { @@ -5247,12 +5245,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "globule": { @@ -5261,31 +5259,31 @@ "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", "dev": true, "requires": { - "glob": "7.1.2", - "lodash": "4.17.5", - "minimatch": "3.0.4" + "glob": "~7.1.1", + "lodash": "~4.17.4", + "minimatch": "~3.0.2" } }, "got": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", - "integrity": "sha1-BUUP2ECU5rvqVvRRpDqcKJFmOFo=", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", "dev": true, "requires": { - "decompress-response": "3.3.0", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "is-plain-obj": "1.1.0", - "is-retry-allowed": "1.1.0", - "is-stream": "1.1.0", - "isurl": "1.0.0", - "lowercase-keys": "1.0.1", - "p-cancelable": "0.3.0", - "p-timeout": "1.2.1", - "safe-buffer": "5.1.2", - "timed-out": "4.0.1", - "url-parse-lax": "1.0.0", - "url-to-options": "1.0.1" + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" } }, "graceful-fs": { @@ -5318,8 +5316,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" + "ajv": "^5.1.0", + "har-schema": "^2.0.0" } }, "has": { @@ -5328,7 +5326,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.1.1" } }, "has-ansi": { @@ -5337,7 +5335,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "has-flag": { @@ -5349,7 +5347,7 @@ "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha1-FAn5i8ACR9pF2mfO4KNvKC/yZFU=", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", "dev": true }, "has-symbols": { @@ -5361,10 +5359,10 @@ "has-to-string-tag-x": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha1-oEWrOD17SyASoAFIqwql8pAETU0=", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", "dev": true, "requires": { - "has-symbol-support-x": "1.4.2" + "has-symbol-support-x": "^1.4.1" } }, "has-unicode": { @@ -5378,9 +5376,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" }, "dependencies": { "isobject": { @@ -5397,8 +5395,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "is-number": { @@ -5407,7 +5405,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -5416,7 +5414,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -5427,7 +5425,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -5435,18 +5433,18 @@ "hawk": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha1-r02RTrBl+bXOTZ0RwcshJu7MMDg=", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.1", - "sntp": "2.1.0" + "boom": "4.x.x", + "cryptiles": "3.x.x", + "hoek": "4.x.x", + "sntp": "2.x.x" } }, "hoek": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha1-ljRQKqEsRF3Vp8VzS1cruHOKrLs=" + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" }, "home-or-tmp": { "version": "2.0.0", @@ -5454,8 +5452,8 @@ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" } }, "htmlparser2": { @@ -5464,12 +5462,12 @@ "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", "dev": true, "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.4.2", - "domutils": "1.5.1", - "entities": "1.1.1", - "inherits": "2.0.3", - "readable-stream": "2.3.6" + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" } }, "http-errors": { @@ -5477,10 +5475,10 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "requires": { - "depd": "1.1.2", + "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": "1.5.0" + "statuses": ">= 1.4.0 < 2" } }, "http-proxy-agent": { @@ -5488,7 +5486,7 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", "requires": { - "agent-base": "4.2.1", + "agent-base": "4", "debug": "3.1.0" } }, @@ -5497,9 +5495,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.14.2" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "https-proxy-agent": { @@ -5507,8 +5505,8 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", "requires": { - "agent-base": "4.2.1", - "debug": "3.1.0" + "agent-base": "^4.1.0", + "debug": "^3.1.0" } }, "iconv-lite": { @@ -5516,7 +5514,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } }, "ieee754": { @@ -5565,8 +5563,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -5577,7 +5575,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, "inquirer": { @@ -5586,19 +5584,19 @@ "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", "dev": true, "requires": { - "ansi-escapes": "3.1.0", - "chalk": "2.4.1", - "cli-cursor": "2.1.0", - "cli-width": "2.2.0", - "external-editor": "2.2.0", - "figures": "2.0.0", - "lodash": "4.17.5", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", "mute-stream": "0.0.7", - "run-async": "2.3.0", - "rxjs": "5.5.11", - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "through": "2.3.8" + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" }, "dependencies": { "ansi-regex": { @@ -5613,7 +5611,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -5622,9 +5620,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "is-fullwidth-code-point": { @@ -5639,8 +5637,8 @@ "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -5649,7 +5647,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -5658,7 +5656,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -5674,7 +5672,7 @@ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { - "loose-envify": "1.3.1" + "loose-envify": "^1.0.0" } }, "ip": { @@ -5693,7 +5691,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-binary-path": { @@ -5702,13 +5700,13 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "1.11.0" + "binary-extensions": "^1.0.0" } }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-callable": { @@ -5723,7 +5721,7 @@ "integrity": "sha512-plgvKjQtalH2P3Gytb7L61Lmz95g2DlpzFiQyRSFew8WoJKxtKRzrZMeyRN2supblm3Psc8OQGy7Xjb6XG11jw==", "dev": true, "requires": { - "ci-info": "1.4.0" + "ci-info": "^1.3.0" } }, "is-data-descriptor": { @@ -5732,7 +5730,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-date-object": { @@ -5744,18 +5742,18 @@ "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "dependencies": { "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -5774,7 +5772,7 @@ "dev": true, "optional": true, "requires": { - "is-primitive": "2.0.0" + "is-primitive": "^2.0.0" } }, "is-extendable": { @@ -5795,7 +5793,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-fullwidth-code-point": { @@ -5803,7 +5801,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-glob": { @@ -5812,7 +5810,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "is-installed-globally": { @@ -5821,8 +5819,8 @@ "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "dev": true, "requires": { - "global-dirs": "0.1.1", - "is-path-inside": "1.0.1" + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" } }, "is-mongodb-running": { @@ -5831,19 +5829,19 @@ "integrity": "sha1-7QdQhQ3uaj6X50ZX+6nm+jvTEmI=", "dev": true, "requires": { - "chalk": "1.1.3", - "debug": "2.6.9", - "figures": "1.7.0", - "lodash": "3.10.1", - "lsof": "0.1.0", - "minimist": "1.2.0", + "chalk": "^1.1.1", + "debug": "^2.2.0", + "figures": "^1.4.0", + "lodash": "^3.10.1", + "lsof": "^0.1.0", + "minimist": "^1.2.0", "ps-node": "0.0.5" }, "dependencies": { "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -5855,13 +5853,13 @@ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" } }, "lodash": { "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true }, @@ -5892,7 +5890,7 @@ "dev": true, "optional": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-obj": { @@ -5907,23 +5905,6 @@ "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", "dev": true }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha1-dkZiRnH9fqVYzNmieVGC8pWPGyQ=", - "dev": true, - "requires": { - "is-number": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", - "dev": true - } - } - }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -5936,7 +5917,7 @@ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", "dev": true, "requires": { - "is-path-inside": "1.0.1" + "is-path-inside": "^1.0.0" } }, "is-path-inside": { @@ -5945,7 +5926,7 @@ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { - "path-is-inside": "1.0.2" + "path-is-inside": "^1.0.1" } }, "is-plain-obj": { @@ -5957,10 +5938,10 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" }, "dependencies": { "isobject": { @@ -6003,13 +5984,13 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "1.0.3" + "has": "^1.0.1" } }, "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, "is-retry-allowed": { @@ -6037,7 +6018,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isarray": { @@ -6083,8 +6064,8 @@ "@babel/template": "7.0.0-beta.49", "@babel/traverse": "7.0.0-beta.49", "@babel/types": "7.0.0-beta.49", - "istanbul-lib-coverage": "1.2.0", - "semver": "5.5.1" + "istanbul-lib-coverage": "^1.2.0", + "semver": "^5.3.0" }, "dependencies": { "@babel/code-frame": { @@ -6103,10 +6084,10 @@ "dev": true, "requires": { "@babel/types": "7.0.0-beta.49", - "jsesc": "2.5.1", - "lodash": "4.17.5", - "source-map": "0.5.7", - "trim-right": "1.0.1" + "jsesc": "^2.5.1", + "lodash": "^4.17.5", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" } }, "@babel/helper-function-name": { @@ -6144,9 +6125,9 @@ "integrity": "sha1-lr3GtD4TSCASumaRsQGEktOWIsw=", "dev": true, "requires": { - "chalk": "2.4.1", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" } }, "@babel/template": { @@ -6158,7 +6139,7 @@ "@babel/code-frame": "7.0.0-beta.49", "@babel/parser": "7.0.0-beta.49", "@babel/types": "7.0.0-beta.49", - "lodash": "4.17.5" + "lodash": "^4.17.5" } }, "@babel/traverse": { @@ -6173,10 +6154,10 @@ "@babel/helper-split-export-declaration": "7.0.0-beta.49", "@babel/parser": "7.0.0-beta.49", "@babel/types": "7.0.0-beta.49", - "debug": "3.1.0", - "globals": "11.5.0", - "invariant": "2.2.4", - "lodash": "4.17.5" + "debug": "^3.1.0", + "globals": "^11.1.0", + "invariant": "^2.2.0", + "lodash": "^4.17.5" } }, "@babel/types": { @@ -6185,9 +6166,9 @@ "integrity": "sha1-t+Oxw/TUz+Eb34yJ8e/V4WF7h6Y=", "dev": true, "requires": { - "esutils": "2.0.2", - "lodash": "4.17.5", - "to-fast-properties": "2.0.0" + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" } }, "ansi-styles": { @@ -6196,7 +6177,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -6205,9 +6186,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "globals": { @@ -6234,7 +6215,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "to-fast-properties": { @@ -6248,11 +6229,11 @@ "isurl": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha1-sn9PSfPNqj6kSgpbfzRi5u3DnWc=", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", "dev": true, "requires": { - "has-to-string-tag-x": "1.4.1", - "is-object": "1.0.1" + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" } }, "iterall": { @@ -6266,8 +6247,8 @@ "integrity": "sha1-K9Wf1+xuwOistk4J9Fpo7SrRlSo=", "dev": true, "requires": { - "glob": "7.1.2", - "jasmine-core": "3.1.0" + "glob": "^7.0.6", + "jasmine-core": "~3.1.0" } }, "jasmine-core": { @@ -6279,7 +6260,7 @@ "jasmine-spec-reporter": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", - "integrity": "sha1-HWMq7ANBZwrTJPkrqEtLMrNeniI=", + "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", "dev": true, "requires": { "colors": "1.1.2" @@ -6310,8 +6291,8 @@ "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "requires": { - "argparse": "1.0.10", - "esprima": "4.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "dependencies": { "esprima": { @@ -6328,7 +6309,7 @@ "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", "dev": true, "requires": { - "xmlcreate": "1.0.2" + "xmlcreate": "^1.0.1" } }, "jsbn": { @@ -6344,17 +6325,17 @@ "dev": true, "requires": { "babylon": "7.0.0-beta.19", - "bluebird": "3.5.1", - "catharsis": "0.8.9", - "escape-string-regexp": "1.0.5", - "js2xmlparser": "3.0.0", - "klaw": "2.0.0", - "marked": "0.3.19", - "mkdirp": "0.5.1", - "requizzle": "0.2.1", - "strip-json-comments": "2.0.1", + "bluebird": "~3.5.0", + "catharsis": "~0.8.9", + "escape-string-regexp": "~1.0.5", + "js2xmlparser": "~3.0.0", + "klaw": "~2.0.0", + "marked": "~0.3.6", + "mkdirp": "~0.5.1", + "requizzle": "~0.2.1", + "strip-json-comments": "~2.0.1", "taffydb": "2.6.2", - "underscore": "1.8.3" + "underscore": "~1.8.3" }, "dependencies": { "babylon": { @@ -6369,7 +6350,7 @@ "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.9" } } } @@ -6380,8 +6361,8 @@ "integrity": "sha512-KF3WTPvoPYc8ZyXzC1m+vvwi+2VCKkqZX/NkqcE1tFephp8RnZAxG52QB/wvz/zoDS6XU28aM8NItMPMad50PA==", "dev": true, "requires": { - "jsdoc-regex": "1.0.1", - "lodash": "4.17.5" + "jsdoc-regex": "^1.0.1", + "lodash": "^4.13.1" } }, "jsdoc-regex": { @@ -6429,7 +6410,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" } }, "jsonwebtoken": { @@ -6437,16 +6418,16 @@ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.1.tgz", "integrity": "sha1-Mz7jmqjyOPMvpBaT56L7fkL4KzE=", "requires": { - "jws": "3.1.5", - "lodash.includes": "4.3.0", - "lodash.isboolean": "3.0.3", - "lodash.isinteger": "4.0.4", - "lodash.isnumber": "3.0.3", - "lodash.isplainobject": "4.0.6", - "lodash.isstring": "4.0.1", - "lodash.once": "4.1.1", - "ms": "2.1.1", - "xtend": "4.0.1" + "jws": "^3.1.4", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "xtend": "^4.0.1" }, "dependencies": { "ms": { @@ -6474,7 +6455,7 @@ "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.10", - "safe-buffer": "5.1.2" + "safe-buffer": "^5.0.1" } }, "jws": { @@ -6482,8 +6463,8 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", "requires": { - "jwa": "1.1.6", - "safe-buffer": "5.1.2" + "jwa": "^1.1.5", + "safe-buffer": "^5.0.1" } }, "key-tree-store": { @@ -6498,7 +6479,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } }, "klaw": { @@ -6507,7 +6488,7 @@ "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.9" } }, "latest-version": { @@ -6516,7 +6497,7 @@ "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { - "package-json": "4.0.1" + "package-json": "^4.0.0" } }, "levn": { @@ -6524,8 +6505,8 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, "limiter": { @@ -6537,7 +6518,7 @@ "lodash": { "version": "4.17.5", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha1-maktZcAnLevoyWtgV7yPv6O+1RE=" + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==" }, "lodash.assignin": { "version": "4.2.0", @@ -6626,7 +6607,7 @@ "lodash.merge": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", - "integrity": "sha1-rcJdnLmbk5HFliTzefu6YNcRHVQ=", + "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==", "dev": true }, "lodash.once": { @@ -6688,7 +6669,7 @@ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { - "js-tokens": "3.0.2" + "js-tokens": "^3.0.0" } }, "lowercase-keys": { @@ -6702,8 +6683,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "lru-queue": { @@ -6712,7 +6693,7 @@ "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", "dev": true, "requires": { - "es5-ext": "0.10.46" + "es5-ext": "~0.10.2" } }, "lsof": { @@ -6726,24 +6707,24 @@ "resolved": "https://registry.npmjs.org/mailgun-js/-/mailgun-js-0.18.0.tgz", "integrity": "sha512-o0P6jjZlx5CQj12tvVgDTbgjTqVN0+5h6/6P1+3c6xmozVKBwniQ6Qt3MkCSF0+ueVTbobAfWyGpWRZMJu8t1g==", "requires": { - "async": "2.6.0", - "debug": "3.1.0", - "form-data": "2.3.2", - "inflection": "1.12.0", - "is-stream": "1.1.0", - "path-proxy": "1.0.0", - "promisify-call": "2.0.4", - "proxy-agent": "3.0.1", - "tsscmp": "1.0.6" + "async": "~2.6.0", + "debug": "~3.1.0", + "form-data": "~2.3.0", + "inflection": "~1.12.0", + "is-stream": "^1.1.0", + "path-proxy": "~1.0.0", + "promisify-call": "^2.0.2", + "proxy-agent": "~3.0.0", + "tsscmp": "~1.0.0" } }, "make-dir": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", - "integrity": "sha1-bWpJ7q1KrilsU7vzoaAIvWyJRps=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { - "pify": "3.0.0" + "pify": "^3.0.0" }, "dependencies": { "pify": { @@ -6777,7 +6758,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "1.0.1" + "object-visit": "^1.0.0" } }, "marked": { @@ -6804,13 +6785,13 @@ "integrity": "sha1-TsoNiu057J0Bf0xcLy9kMvQuXI8=", "dev": true, "requires": { - "d": "0.1.1", - "es5-ext": "0.10.46", - "es6-weak-map": "0.1.4", - "event-emitter": "0.3.5", - "lru-queue": "0.1.0", - "next-tick": "0.2.2", - "timers-ext": "0.1.5" + "d": "~0.1.1", + "es5-ext": "~0.10.11", + "es6-weak-map": "~0.1.4", + "event-emitter": "~0.3.4", + "lru-queue": "0.1", + "next-tick": "~0.2.2", + "timers-ext": "0.1" }, "dependencies": { "next-tick": { @@ -6838,19 +6819,19 @@ "dev": true, "optional": true, "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" } }, "mime": { @@ -6861,20 +6842,20 @@ "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha1-o0kgUKXLm2NFBUHjnZeI0icng9s=" + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" }, "mime-types": { "version": "2.1.18", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha1-bzI/YKg9ERRvgx/xH9ZuL+VQO7g=", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "mimic-response": { @@ -6886,10 +6867,10 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -6900,20 +6881,20 @@ "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -6932,7 +6913,7 @@ "integrity": "sha512-BGUxo4a/p5KtZpOn6+z6iZXTHfDxKDvibHQap9uMJqQouwoszvTIO/QbVZkaSX3Spny0jtTEeHc0FwfpGbtEzA==", "requires": { "mongodb-core": "3.1.3", - "safe-buffer": "5.1.2" + "safe-buffer": "^5.1.2" } }, "mongodb-core": { @@ -6940,10 +6921,10 @@ "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.3.tgz", "integrity": "sha512-dISiV3zHGJTwZpg0xDhi9zCqFGMhA5kDPByHlcaEp09NSKfzHJ7XQbqVrL7qhki1U9PZHsmRfbFzco+6b1h2wA==", "requires": { - "bson": "1.1.0", - "require_optional": "1.0.1", - "safe-buffer": "5.1.2", - "saslprep": "1.0.1" + "bson": "^1.1.0", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" }, "dependencies": { "bson": { @@ -6959,11 +6940,11 @@ "integrity": "sha1-4BMsZ3sbncgwBFEW0Yrbf2kk8XU=", "dev": true, "requires": { - "async": "1.5.2", - "debug": "2.6.9", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "untildify": "1.0.0" + "async": "^1.4.0", + "debug": "^2.1.1", + "minimist": "^1.1.1", + "mkdirp": "^0.5.1", + "untildify": "^1.0.0" }, "dependencies": { "async": { @@ -6975,7 +6956,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -6993,7 +6974,7 @@ "integrity": "sha1-TYAx0YBvT718QrAjeq8hNoYmJjU=", "dev": true, "requires": { - "user-home": "1.1.1" + "user-home": "^1.0.0" } } } @@ -7004,19 +6985,19 @@ "integrity": "sha1-46ilSPE+sg9aDN+GPLwGNCGjk0w=", "dev": true, "requires": { - "async": "2.6.0", - "debug": "2.6.9", - "lodash.defaults": "4.2.0", - "minimist": "1.2.0", - "mongodb-version-list": "1.0.0", - "request": "2.88.0", - "semver": "5.5.1" + "async": "^2.1.2", + "debug": "^2.2.0", + "lodash.defaults": "^4.0.0", + "minimist": "^1.2.0", + "mongodb-version-list": "^1.0.0", + "request": "^2.65.0", + "semver": "^5.0.3" }, "dependencies": { "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -7036,33 +7017,65 @@ "integrity": "sha512-MHisvAAo+gEhhHBMNnYWr8+r8s1n9wf83Mbrs21ftFOg/uqF96KIR1CJMdvuVKNZNGjcsaLxtpZRrVPqqq4JfA==", "dev": true, "requires": { - "async": "2.6.0", - "clui": "0.3.6", - "debug": "3.1.0", - "fs-extra": "4.0.3", + "async": "^2.0.0", + "clui": "^0.3.1", + "debug": "^3.0.1", + "fs-extra": "^4.0.2", "is-mongodb-running": "0.0.1", - "lodash.defaults": "4.2.0", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "mongodb": "3.1.4", - "mongodb-dbpath": "0.0.1", + "lodash.defaults": "^4.0.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "mongodb": "^3.1.4", + "mongodb-dbpath": "^0.0.1", "mongodb-tools": "github:mongodb-js/mongodb-tools#df761df21175f2c521c78b8e011a1532569f0dca", - "mongodb-version-manager": "1.1.3", - "untildify": "3.0.3", - "which": "1.3.0" + "mongodb-version-manager": "^1.1.3", + "untildify": "^3.0.0", + "which": "^1.2.4" }, "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true + } + } + }, + "mongodb-tools": { + "version": "github:mongodb-js/mongodb-tools#df761df21175f2c521c78b8e011a1532569f0dca", + "from": "github:mongodb-js/mongodb-tools", + "dev": true, + "requires": { + "debug": "^2.2.0", + "lodash": "^3.10.1", + "mkdirp": "0.5.0", + "mongodb-core": "^2.1.8", + "rimraf": "2.2.6" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "lodash": { + "version": "3.10.1", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "mkdirp": { + "version": "0.5.0", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } }, "mongodb-core": { "version": "2.1.20", @@ -7070,50 +7083,13 @@ "integrity": "sha512-IN57CX5/Q1bhDq6ShAR6gIv4koFsZP7L8WOK1S0lR0pVDQaScffSMV5jxubLsmZ7J+UdqmykKw4r9hG3XQEGgQ==", "dev": true, "requires": { - "bson": "1.0.9", - "require_optional": "1.0.1" - } - }, - "mongodb-tools": { - "version": "github:mongodb-js/mongodb-tools#df761df21175f2c521c78b8e011a1532569f0dca", - "dev": true, - "requires": { - "debug": "2.6.9", - "lodash": "3.10.1", - "mkdirp": "0.5.0", - "mongodb-core": "2.1.20", - "rimraf": "2.2.6" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", - "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - } + "bson": "~1.0.4", + "require_optional": "~1.0.0" } }, "rimraf": { "version": "2.2.6", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz", + "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz", "integrity": "sha1-xZWXVpsU2VatKcrMQr3d9fDqT0w=", "dev": true } @@ -7125,18 +7101,18 @@ "integrity": "sha1-8lAxz83W8UWx3o/OKk6+wCiLtKQ=", "dev": true, "requires": { - "cheerio": "0.22.0", - "debug": "2.6.9", - "downcache": "0.0.9", - "fs-extra": "1.0.0", - "minimist": "1.2.0", - "semver": "5.5.1" + "cheerio": "^0.22.0", + "debug": "^2.2.0", + "downcache": "^0.0.9", + "fs-extra": "^1.0.0", + "minimist": "^1.1.1", + "semver": "^5.0.1" }, "dependencies": { "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -7148,9 +7124,9 @@ "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "2.4.0", - "klaw": "1.3.1" + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0" } }, "jsonfile": { @@ -7159,7 +7135,7 @@ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" } }, "minimist": { @@ -7176,22 +7152,22 @@ "integrity": "sha512-EInfE8Uu4umFHnFr1+kjvxaC8n0v2HR9WEJ659FxOITStOMzzvFvKw88j4m//rK85rAKk6aZ+Kx29tRTcatrkA==", "dev": true, "requires": { - "ampersand-state": "5.0.3", - "async": "2.6.0", - "chalk": "2.4.1", - "debug": "3.1.0", - "docopt": "0.6.2", - "download": "6.2.5", - "figures": "2.0.0", - "fs-extra": "4.0.3", - "get-mongodb-version": "1.1.0", - "lodash.defaults": "4.2.0", - "lodash.difference": "4.5.0", - "mongodb-download-url": "0.3.3", - "mongodb-version-list": "1.0.0", - "semver": "5.5.1", - "tildify": "1.2.0", - "untildify": "3.0.3" + "ampersand-state": "^5.0.1", + "async": "^2.1.2", + "chalk": "^2.1.0", + "debug": "^3.0.1", + "docopt": "^0.6.2", + "download": "^6.2.5", + "figures": "^2.0.0", + "fs-extra": "^4.0.2", + "get-mongodb-version": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.1.1", + "mongodb-download-url": "^0.3.3", + "mongodb-version-list": "^1.0.0", + "semver": "^5.3.0", + "tildify": "^1.2.0", + "untildify": "^3.0.2" }, "dependencies": { "ansi-styles": { @@ -7200,7 +7176,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -7209,9 +7185,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } } } @@ -7230,27 +7206,26 @@ "nan": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha1-ltDNYQ69WNS03pzAxoKM2pnHVI8=", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", "optional": true }, "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha1-h59xUMstq3pHElkGbBBO7m4Pp8I=", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "arr-diff": { @@ -7268,7 +7243,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } @@ -7304,7 +7279,7 @@ "node-forge": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha1-bBUsNFzhHFL0ZcKr2VfoY5zWdN8=" + "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==" }, "nodemon": { "version": "1.18.4", @@ -7312,16 +7287,16 @@ "integrity": "sha512-hyK6vl65IPnky/ee+D3IWvVGgJa/m3No2/Xc/3wanS6Ce1MWjCzH6NnhPJ/vZM+6JFym16jtHx51lmCMB9HDtg==", "dev": true, "requires": { - "chokidar": "2.0.4", - "debug": "3.1.0", - "ignore-by-default": "1.0.1", - "minimatch": "3.0.4", - "pstree.remy": "1.1.0", - "semver": "5.5.1", - "supports-color": "5.4.0", - "touch": "3.1.0", - "undefsafe": "2.0.2", - "update-notifier": "2.5.0" + "chokidar": "^2.0.2", + "debug": "^3.1.0", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.0", + "semver": "^5.5.0", + "supports-color": "^5.2.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^2.3.0" }, "dependencies": { "anymatch": { @@ -7330,8 +7305,8 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "3.1.10", - "normalize-path": "2.1.1" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, "arr-diff": { @@ -7352,16 +7327,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -7370,7 +7345,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -7381,19 +7356,19 @@ "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", "dev": true, "requires": { - "anymatch": "2.0.0", - "async-each": "1.0.1", - "braces": "2.3.2", - "fsevents": "1.2.3", - "glob-parent": "3.1.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "4.0.0", - "lodash.debounce": "4.0.8", - "normalize-path": "2.1.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0", - "upath": "1.1.0" + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" } }, "expand-brackets": { @@ -7402,13 +7377,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "debug": { @@ -7426,7 +7401,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -7435,7 +7410,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-accessor-descriptor": { @@ -7444,7 +7419,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -7453,7 +7428,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -7464,7 +7439,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -7473,7 +7448,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -7484,9 +7459,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { @@ -7503,14 +7478,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -7519,7 +7494,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -7528,7 +7503,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -7539,10 +7514,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -7551,7 +7526,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -7562,8 +7537,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" }, "dependencies": { "is-glob": { @@ -7572,7 +7547,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.0" } } } @@ -7583,7 +7558,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -7592,7 +7567,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -7601,9 +7576,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "is-extglob": { @@ -7618,7 +7593,7 @@ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.1" } }, "is-number": { @@ -7627,7 +7602,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -7636,7 +7611,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -7659,19 +7634,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } } } @@ -7682,7 +7657,7 @@ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "dev": true, "requires": { - "abbrev": "1.1.1" + "abbrev": "1" } }, "normalize-path": { @@ -7691,17 +7666,17 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "1.1.0" + "remove-trailing-separator": "^1.0.1" } }, "npm-conf": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", - "integrity": "sha1-JWzEe9DiGMJZxOlVC/QTvCGSr/k=", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", "dev": true, "requires": { - "config-chain": "1.1.11", - "pify": "3.0.0" + "config-chain": "^1.1.11", + "pify": "^3.0.0" }, "dependencies": { "pify": { @@ -7718,18 +7693,18 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "2.0.1" + "path-key": "^2.0.0" } }, "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "nth-check": { @@ -7738,7 +7713,7 @@ "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", "dev": true, "requires": { - "boolbase": "1.0.0" + "boolbase": "~1.0.0" } }, "number-is-nan": { @@ -7752,33 +7727,33 @@ "integrity": "sha1-ikpO1pCWbBHsWH/4fuoMEsl0upk=", "dev": true, "requires": { - "archy": "1.0.0", - "arrify": "1.0.1", - "caching-transform": "1.0.1", - "convert-source-map": "1.5.1", - "debug-log": "1.0.1", - "default-require-extensions": "1.0.0", - "find-cache-dir": "0.1.1", - "find-up": "2.1.0", - "foreground-child": "1.5.6", - "glob": "7.1.2", - "istanbul-lib-coverage": "1.2.0", - "istanbul-lib-hook": "1.1.0", - "istanbul-lib-instrument": "2.1.0", - "istanbul-lib-report": "1.1.3", - "istanbul-lib-source-maps": "1.2.5", - "istanbul-reports": "1.4.1", - "md5-hex": "1.3.0", - "merge-source-map": "1.1.0", - "micromatch": "3.1.10", - "mkdirp": "0.5.1", - "resolve-from": "2.0.0", - "rimraf": "2.6.2", - "signal-exit": "3.0.2", - "spawn-wrap": "1.4.2", - "test-exclude": "4.2.1", + "archy": "^1.0.0", + "arrify": "^1.0.1", + "caching-transform": "^1.0.0", + "convert-source-map": "^1.5.1", + "debug-log": "^1.0.1", + "default-require-extensions": "^1.0.0", + "find-cache-dir": "^0.1.1", + "find-up": "^2.1.0", + "foreground-child": "^1.5.3", + "glob": "^7.0.6", + "istanbul-lib-coverage": "^1.2.0", + "istanbul-lib-hook": "^1.1.0", + "istanbul-lib-instrument": "^2.1.0", + "istanbul-lib-report": "^1.1.3", + "istanbul-lib-source-maps": "^1.2.5", + "istanbul-reports": "^1.4.1", + "md5-hex": "^1.2.0", + "merge-source-map": "^1.1.0", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.0", + "resolve-from": "^2.0.0", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.1", + "spawn-wrap": "^1.4.2", + "test-exclude": "^4.2.0", "yargs": "11.1.0", - "yargs-parser": "8.1.0" + "yargs-parser": "^8.0.0" }, "dependencies": { "align-text": { @@ -7787,9 +7762,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" } }, "amdefine": { @@ -7810,7 +7785,7 @@ "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { - "default-require-extensions": "1.0.0" + "default-require-extensions": "^1.0.0" } }, "archy": { @@ -7879,13 +7854,13 @@ "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", "dev": true, "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "dependencies": { "define-property": { @@ -7894,7 +7869,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -7903,7 +7878,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -7912,7 +7887,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -7921,9 +7896,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "kind-of": { @@ -7940,7 +7915,7 @@ "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -7950,16 +7925,16 @@ "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -7968,7 +7943,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -7985,15 +7960,15 @@ "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", "dev": true, "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" } }, "caching-transform": { @@ -8002,9 +7977,9 @@ "integrity": "sha1-bb2y8g+Nj7znnz6U6dF0Lc31wKE=", "dev": true, "requires": { - "md5-hex": "1.3.0", - "mkdirp": "0.5.1", - "write-file-atomic": "1.3.4" + "md5-hex": "^1.2.0", + "mkdirp": "^0.5.1", + "write-file-atomic": "^1.1.4" } }, "camelcase": { @@ -8021,8 +7996,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" } }, "class-utils": { @@ -8031,10 +8006,10 @@ "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", "dev": true, "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { "define-property": { @@ -8043,7 +8018,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -8055,8 +8030,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", + "center-align": "^0.1.1", + "right-align": "^0.1.1", "wordwrap": "0.0.2" }, "dependencies": { @@ -8081,8 +8056,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "commondir": { @@ -8121,8 +8096,8 @@ "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { - "lru-cache": "4.1.3", - "which": "1.3.1" + "lru-cache": "^4.0.1", + "which": "^1.2.9" } }, "debug": { @@ -8158,7 +8133,7 @@ "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { - "strip-bom": "2.0.0" + "strip-bom": "^2.0.0" } }, "define-property": { @@ -8167,8 +8142,8 @@ "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "dev": true, "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -8177,7 +8152,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -8186,7 +8161,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -8195,9 +8170,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "kind-of": { @@ -8214,7 +8189,7 @@ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { - "is-arrayish": "0.2.1" + "is-arrayish": "^0.2.1" } }, "execa": { @@ -8223,13 +8198,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" }, "dependencies": { "cross-spawn": { @@ -8238,9 +8213,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "4.1.3", - "shebang-command": "1.2.0", - "which": "1.3.1" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } } } @@ -8251,13 +8226,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "debug": { @@ -8275,7 +8250,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -8284,7 +8259,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -8295,8 +8270,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -8305,7 +8280,7 @@ "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -8316,14 +8291,14 @@ "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -8332,7 +8307,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -8341,7 +8316,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-accessor-descriptor": { @@ -8350,7 +8325,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -8359,7 +8334,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -8368,9 +8343,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "kind-of": { @@ -8387,10 +8362,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -8399,7 +8374,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -8410,9 +8385,9 @@ "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", "dev": true, "requires": { - "commondir": "1.0.1", - "mkdirp": "0.5.1", - "pkg-dir": "1.0.0" + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" } }, "find-up": { @@ -8421,7 +8396,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "for-in": { @@ -8436,8 +8411,8 @@ "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", "dev": true, "requires": { - "cross-spawn": "4.0.2", - "signal-exit": "3.0.2" + "cross-spawn": "^4", + "signal-exit": "^3.0.0" } }, "fragment-cache": { @@ -8446,7 +8421,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "0.2.2" + "map-cache": "^0.2.2" } }, "fs.realpath": { @@ -8479,12 +8454,12 @@ "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "graceful-fs": { @@ -8499,10 +8474,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" }, "dependencies": { "source-map": { @@ -8511,7 +8486,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -8522,9 +8497,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" } }, "has-values": { @@ -8533,8 +8508,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "kind-of": { @@ -8543,7 +8518,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -8566,8 +8541,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -8588,7 +8563,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-arrayish": { @@ -8609,7 +8584,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "1.1.1" + "builtin-modules": "^1.0.0" } }, "is-data-descriptor": { @@ -8618,7 +8593,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-descriptor": { @@ -8627,9 +8602,9 @@ "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "dependencies": { "kind-of": { @@ -8658,7 +8633,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "is-odd": { @@ -8667,7 +8642,7 @@ "integrity": "sha1-dkZiRnH9fqVYzNmieVGC8pWPGyQ=", "dev": true, "requires": { - "is-number": "4.0.0" + "is-number": "^4.0.0" }, "dependencies": { "is-number": { @@ -8684,7 +8659,7 @@ "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" } }, "is-stream": { @@ -8735,7 +8710,7 @@ "integrity": "sha1-hTjZcDcss3FtU+VVI91UtVeo2Js=", "dev": true, "requires": { - "append-transform": "0.4.0" + "append-transform": "^0.4.0" } }, "istanbul-lib-report": { @@ -8744,10 +8719,10 @@ "integrity": "sha1-LfEhiMD6d5kMDSF20tC6M5QYglk=", "dev": true, "requires": { - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "path-parse": "1.0.5", - "supports-color": "3.2.3" + "istanbul-lib-coverage": "^1.1.2", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" }, "dependencies": { "has-flag": { @@ -8762,7 +8737,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "^1.0.0" } } } @@ -8773,11 +8748,11 @@ "integrity": "sha1-/+a+Tnq4bTYD5CkNVJkLFFBvybE=", "dev": true, "requires": { - "debug": "3.1.0", - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "source-map": "0.5.7" + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.2.0", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" } }, "istanbul-reports": { @@ -8786,7 +8761,7 @@ "integrity": "sha1-Ty6OkoqnoF0dpsQn1AmLJlXsczQ=", "dev": true, "requires": { - "handlebars": "4.0.11" + "handlebars": "^4.0.3" } }, "kind-of": { @@ -8795,7 +8770,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } }, "lazy-cache": { @@ -8811,7 +8786,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "1.0.0" + "invert-kv": "^1.0.0" } }, "load-json-file": { @@ -8820,11 +8795,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" } }, "locate-path": { @@ -8833,8 +8808,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" }, "dependencies": { "path-exists": { @@ -8857,8 +8832,8 @@ "integrity": "sha1-oRdc80lt/IQ2wVbDNLSVWZK85pw=", "dev": true, "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "map-cache": { @@ -8873,7 +8848,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "1.0.1" + "object-visit": "^1.0.0" } }, "md5-hex": { @@ -8882,7 +8857,7 @@ "integrity": "sha1-0sSv6YPENwZiF5uMrRRSGRNQRsQ=", "dev": true, "requires": { - "md5-o-matic": "0.1.1" + "md5-o-matic": "^0.1.1" } }, "md5-o-matic": { @@ -8897,7 +8872,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "merge-source-map": { @@ -8906,7 +8881,7 @@ "integrity": "sha1-L93n5gIJOfcJBqaPLXrmheTIxkY=", "dev": true, "requires": { - "source-map": "0.6.1" + "source-map": "^0.6.1" }, "dependencies": { "source-map": { @@ -8923,19 +8898,19 @@ "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" }, "dependencies": { "kind-of": { @@ -8958,7 +8933,7 @@ "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -8973,8 +8948,8 @@ "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", "dev": true, "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -8983,7 +8958,7 @@ "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -9009,18 +8984,18 @@ "integrity": "sha1-h59xUMstq3pHElkGbBBO7m4Pp8I=", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-odd": "^2.0.0", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "kind-of": { @@ -9037,10 +9012,10 @@ "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", "dev": true, "requires": { - "hosted-git-info": "2.6.0", - "is-builtin-module": "1.0.0", - "semver": "5.5.0", - "validate-npm-package-license": "3.0.3" + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, "npm-run-path": { @@ -9049,7 +9024,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "2.0.1" + "path-key": "^2.0.0" } }, "number-is-nan": { @@ -9070,9 +9045,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { "define-property": { @@ -9081,7 +9056,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -9092,7 +9067,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.0" } }, "object.pick": { @@ -9101,7 +9076,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" } }, "once": { @@ -9110,7 +9085,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "optimist": { @@ -9119,8 +9094,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } }, "os-homedir": { @@ -9135,9 +9110,9 @@ "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", "dev": true, "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" } }, "p-finally": { @@ -9152,7 +9127,7 @@ "integrity": "sha1-DpK2vty1nwIsE9DxlJ3ILRWQnxw=", "dev": true, "requires": { - "p-try": "1.0.0" + "p-try": "^1.0.0" } }, "p-locate": { @@ -9161,7 +9136,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "1.2.0" + "p-limit": "^1.1.0" } }, "p-try": { @@ -9176,7 +9151,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "1.3.1" + "error-ex": "^1.2.0" } }, "pascalcase": { @@ -9191,7 +9166,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "2.0.1" + "pinkie-promise": "^2.0.0" } }, "path-is-absolute": { @@ -9218,9 +9193,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "pify": { @@ -9241,7 +9216,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "2.0.4" + "pinkie": "^2.0.0" } }, "pkg-dir": { @@ -9250,7 +9225,7 @@ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { - "find-up": "1.1.2" + "find-up": "^1.0.0" }, "dependencies": { "find-up": { @@ -9259,8 +9234,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" } } } @@ -9283,9 +9258,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" } }, "read-pkg-up": { @@ -9294,8 +9269,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" }, "dependencies": { "find-up": { @@ -9304,8 +9279,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" } } } @@ -9316,8 +9291,8 @@ "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", "dev": true, "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, "repeat-element": { @@ -9369,7 +9344,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4" + "align-text": "^0.1.1" } }, "rimraf": { @@ -9378,7 +9353,7 @@ "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-regex": { @@ -9387,7 +9362,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "0.1.15" + "ret": "~0.1.10" } }, "semver": { @@ -9408,10 +9383,10 @@ "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -9420,7 +9395,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -9431,7 +9406,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -9458,14 +9433,14 @@ "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", "dev": true, "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.2", - "use": "3.1.0" + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" }, "dependencies": { "debug": { @@ -9483,7 +9458,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -9492,7 +9467,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -9503,9 +9478,9 @@ "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", "dev": true, "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" }, "dependencies": { "define-property": { @@ -9514,7 +9489,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -9523,7 +9498,7 @@ "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -9532,7 +9507,7 @@ "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -9541,9 +9516,9 @@ "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "kind-of": { @@ -9560,7 +9535,7 @@ "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.2.0" } }, "source-map": { @@ -9575,11 +9550,11 @@ "integrity": "sha1-cuLMNAlVQ+Q7LGKyxMENSpBU8lk=", "dev": true, "requires": { - "atob": "2.1.1", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, "source-map-url": { @@ -9594,12 +9569,12 @@ "integrity": "sha1-z/WOc6giRhe2Vhq9wyWG6gyCJIw=", "dev": true, "requires": { - "foreground-child": "1.5.6", - "mkdirp": "0.5.1", - "os-homedir": "1.0.2", - "rimraf": "2.6.2", - "signal-exit": "3.0.2", - "which": "1.3.1" + "foreground-child": "^1.5.6", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.2", + "which": "^1.3.0" } }, "spdx-correct": { @@ -9608,8 +9583,8 @@ "integrity": "sha1-BaW01xU6GVvJLDxCW2nzsqlSTII=", "dev": true, "requires": { - "spdx-expression-parse": "3.0.0", - "spdx-license-ids": "3.0.0" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, "spdx-exceptions": { @@ -9624,8 +9599,8 @@ "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", "dev": true, "requires": { - "spdx-exceptions": "2.1.0", - "spdx-license-ids": "3.0.0" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, "spdx-license-ids": { @@ -9640,7 +9615,7 @@ "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", "dev": true, "requires": { - "extend-shallow": "3.0.2" + "extend-shallow": "^3.0.0" } }, "static-extend": { @@ -9649,8 +9624,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { "define-property": { @@ -9659,7 +9634,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -9670,8 +9645,8 @@ "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -9680,7 +9655,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "strip-bom": { @@ -9689,7 +9664,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } }, "strip-eof": { @@ -9704,11 +9679,11 @@ "integrity": "sha1-36Ii8DSAvKaSB8pyizfXS0X3JPo=", "dev": true, "requires": { - "arrify": "1.0.1", - "micromatch": "3.1.10", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "require-main-filename": "1.0.1" + "arrify": "^1.0.1", + "micromatch": "^3.1.8", + "object-assign": "^4.1.0", + "read-pkg-up": "^1.0.1", + "require-main-filename": "^1.0.1" } }, "to-object-path": { @@ -9717,7 +9692,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "to-regex": { @@ -9726,10 +9701,10 @@ "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", "dev": true, "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" } }, "to-regex-range": { @@ -9738,8 +9713,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" } }, "uglify-js": { @@ -9749,9 +9724,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" }, "dependencies": { "yargs": { @@ -9761,9 +9736,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", "window-size": "0.1.0" } } @@ -9782,10 +9757,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" }, "dependencies": { "extend-shallow": { @@ -9794,7 +9769,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "set-value": { @@ -9803,10 +9778,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" } } } @@ -9817,8 +9792,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "dependencies": { "has-value": { @@ -9827,9 +9802,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "dependencies": { "isobject": { @@ -9863,7 +9838,7 @@ "integrity": "sha1-FHFr8D/f79AwQK71jYtLhfOnxUQ=", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.2" }, "dependencies": { "kind-of": { @@ -9880,8 +9855,8 @@ "integrity": "sha1-gWQ7y+8b3+zUYjeT3EZIlIupgzg=", "dev": true, "requires": { - "spdx-correct": "3.0.0", - "spdx-expression-parse": "3.0.0" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, "which": { @@ -9890,7 +9865,7 @@ "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "which-module": { @@ -9918,8 +9893,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" }, "dependencies": { "ansi-regex": { @@ -9934,7 +9909,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "string-width": { @@ -9943,9 +9918,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "strip-ansi": { @@ -9954,7 +9929,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } } } @@ -9971,9 +9946,9 @@ "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "slide": "1.1.6" + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" } }, "y18n": { @@ -9994,18 +9969,18 @@ "integrity": "sha1-kLhpk07W6HERXqL/WLA/RyTtLXc=", "dev": true, "requires": { - "cliui": "4.1.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" }, "dependencies": { "camelcase": { @@ -10020,9 +9995,9 @@ "integrity": "sha1-NIQi2+gtgAswIu709qwQvy5NG0k=", "dev": true, "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } }, "yargs-parser": { @@ -10031,7 +10006,7 @@ "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" } } } @@ -10042,7 +10017,7 @@ "integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" }, "dependencies": { "camelcase": { @@ -10071,9 +10046,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { "define-property": { @@ -10082,7 +10057,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -10099,7 +10074,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.0" }, "dependencies": { "isobject": { @@ -10117,8 +10092,8 @@ "dev": true, "optional": true, "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" } }, "object.pick": { @@ -10127,7 +10102,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" }, "dependencies": { "isobject": { @@ -10152,7 +10127,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "onetime": { @@ -10161,7 +10136,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "optionator": { @@ -10169,12 +10144,12 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" } }, "os-homedir": { @@ -10195,15 +10170,15 @@ "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "mkdirp": "0.5.1", - "object-assign": "4.1.1" + "graceful-fs": "^4.1.4", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.0" } }, "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha1-ueEjgAvOu3rBOkeb4ZW1B7mNMPo=", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", "dev": true }, "p-event": { @@ -10212,7 +10187,7 @@ "integrity": "sha1-jmtPT2XHK8W2/ii3XtqHT5akoIU=", "dev": true, "requires": { - "p-timeout": "1.2.1" + "p-timeout": "^1.1.1" } }, "p-finally": { @@ -10233,7 +10208,7 @@ "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", "dev": true, "requires": { - "p-finally": "1.0.0" + "p-finally": "^1.0.0" } }, "pac-proxy-agent": { @@ -10241,14 +10216,14 @@ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-2.0.2.tgz", "integrity": "sha512-cDNAN1Ehjbf5EHkNY5qnRhGPUCp6SnpyVof5fRzN800QV1Y2OkzbH9rmjZkbBRa8igof903yOnjIl6z0SlAhxA==", "requires": { - "agent-base": "4.2.1", - "debug": "3.1.0", - "get-uri": "2.0.2", - "http-proxy-agent": "2.1.0", - "https-proxy-agent": "2.2.1", - "pac-resolver": "3.0.0", - "raw-body": "2.3.3", - "socks-proxy-agent": "3.0.1" + "agent-base": "^4.2.0", + "debug": "^3.1.0", + "get-uri": "^2.0.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "pac-resolver": "^3.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "^3.0.0" }, "dependencies": { "socks-proxy-agent": { @@ -10256,8 +10231,8 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz", "integrity": "sha512-ZwEDymm204mTzvdqyUqOdovVr2YRd2NYskrYrF2LXyZ9qDiMAoFESGK8CRphiO7rtbo2Y757k2Nia3x2hGtalA==", "requires": { - "agent-base": "4.2.1", - "socks": "1.1.10" + "agent-base": "^4.1.0", + "socks": "^1.1.10" } } } @@ -10267,11 +10242,11 @@ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", "requires": { - "co": "4.6.0", - "degenerator": "1.0.4", - "ip": "1.1.5", - "netmask": "1.0.6", - "thunkify": "2.1.2" + "co": "^4.6.0", + "degenerator": "^1.0.4", + "ip": "^1.1.5", + "netmask": "^1.0.6", + "thunkify": "^2.1.2" } }, "package-json": { @@ -10280,10 +10255,10 @@ "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "dev": true, "requires": { - "got": "6.7.1", - "registry-auth-token": "3.3.2", - "registry-url": "3.1.0", - "semver": "5.5.1" + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" }, "dependencies": { "got": { @@ -10292,17 +10267,17 @@ "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { - "create-error-class": "3.0.2", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "is-redirect": "1.0.0", - "is-retry-allowed": "1.1.0", - "is-stream": "1.1.0", - "lowercase-keys": "1.0.1", - "safe-buffer": "5.1.2", - "timed-out": "4.0.1", - "unzip-response": "2.0.1", - "url-parse-lax": "1.0.0" + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" } } } @@ -10329,10 +10304,10 @@ "dev": true, "optional": true, "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" } }, "parseurl": { @@ -10375,7 +10350,7 @@ "resolved": "https://registry.npmjs.org/path-proxy/-/path-proxy-1.0.0.tgz", "integrity": "sha1-GOijaFn8nS8aU7SN7hOFQ8Ag3l4=", "requires": { - "inflection": "1.3.8" + "inflection": "~1.3.0" }, "dependencies": { "inflection": { @@ -10396,7 +10371,7 @@ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "3.0.0" + "pify": "^3.0.0" }, "dependencies": { "pify": { @@ -10413,7 +10388,7 @@ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { - "through": "2.3.8" + "through": "~2.3" } }, "pend": { @@ -10435,9 +10410,9 @@ "buffer-writer": "1.0.1", "packet-reader": "0.3.1", "pg-connection-string": "0.1.3", - "pg-pool": "2.0.3", - "pg-types": "1.12.1", - "pgpass": "1.0.2", + "pg-pool": "~2.0.3", + "pg-types": "~1.12.1", + "pgpass": "1.x", "semver": "4.3.2" }, "dependencies": { @@ -10456,7 +10431,7 @@ "pg-minify": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-0.5.4.tgz", - "integrity": "sha1-idUmHKz9RN15J/oFIiKkBOmyo8k=" + "integrity": "sha512-GHB2v4OiMHDgwiHH86ZWNfvgEPVijrnfuWLQocseX6Zlf30k+x0imA65zBy4skIpEwfBBEplIEEKP4n3q9KkVA==" }, "pg-pool": { "version": "2.0.3", @@ -10479,10 +10454,10 @@ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.12.1.tgz", "integrity": "sha1-1kCH45A7WP+q0nnnWVxSIIoUw9I=", "requires": { - "postgres-array": "1.0.2", - "postgres-bytea": "1.0.0", - "postgres-date": "1.0.3", - "postgres-interval": "1.1.2" + "postgres-array": "~1.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.0", + "postgres-interval": "^1.1.0" } }, "pgpass": { @@ -10490,7 +10465,7 @@ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", "requires": { - "split": "1.0.1" + "split": "^1.0.0" } }, "pify": { @@ -10511,13 +10486,13 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "2.0.4" + "pinkie": "^2.0.0" } }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha1-KYuJ34uTsCIdv0Ia0rGx6iP8Z3c=", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, "posix-character-classes": { @@ -10546,7 +10521,7 @@ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.2.tgz", "integrity": "sha512-fC3xNHeTskCxL1dC8KOtxXt7YeFmlbTYtn7ul8MkVERuTmf7pI4DrkAxcw3kh1fQ9uz4wQmd03a1mRiXUZChfQ==", "requires": { - "xtend": "4.0.1" + "xtend": "^4.0.0" } }, "prelude-ls": { @@ -10570,13 +10545,13 @@ "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=" + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "progress": { "version": "2.0.0", @@ -10589,7 +10564,7 @@ "resolved": "https://registry.npmjs.org/promisify-call/-/promisify-call-2.0.4.tgz", "integrity": "sha1-1IwtRWUszM1SgB3ey9UzptS9X7o=", "requires": { - "with-callback": "1.0.2" + "with-callback": "^1.0.2" } }, "proto-list": { @@ -10601,9 +10576,9 @@ "proxy-addr": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", - "integrity": "sha1-NV8mJQWmIWRrMTCnKOtkfiIFU0E=", + "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "requires": { - "forwarded": "0.1.2", + "forwarded": "~0.1.2", "ipaddr.js": "1.6.0" } }, @@ -10612,14 +10587,14 @@ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.1.tgz", "integrity": "sha512-mAZexaz9ZxQhYPWfAjzlrloEjW+JHiBFryE4AJXFDTnaXfmH/FKqC1swTRKuEPbHWz02flQNXFOyDUF7zfEG6A==", "requires": { - "agent-base": "4.2.1", - "debug": "3.1.0", - "http-proxy-agent": "2.1.0", - "https-proxy-agent": "2.2.1", - "lru-cache": "4.1.3", - "pac-proxy-agent": "2.0.2", - "proxy-from-env": "1.0.0", - "socks-proxy-agent": "4.0.1" + "agent-base": "^4.2.0", + "debug": "^3.1.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "pac-proxy-agent": "^2.0.1", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^4.0.1" } }, "proxy-from-env": { @@ -10633,7 +10608,7 @@ "integrity": "sha1-hWe8VKX4MbRd2eAOWpwJVqr58ZY=", "dev": true, "requires": { - "table-parser": "1.0.1" + "table-parser": "*" } }, "ps-tree": { @@ -10642,7 +10617,7 @@ "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", "dev": true, "requires": { - "event-stream": "3.3.4" + "event-stream": "~3.3.0" } }, "pseudomap": { @@ -10661,7 +10636,7 @@ "integrity": "sha512-q5I5vLRMVtdWa8n/3UEzZX7Lfghzrg9eG2IKk2ENLSofKRCXVqMvMUHxCKgXNaqH/8ebhBxrqftHWnyTFweJ5Q==", "dev": true, "requires": { - "ps-tree": "1.1.0" + "ps-tree": "^1.1.0" } }, "punycode": { @@ -10686,9 +10661,9 @@ "dev": true, "optional": true, "requires": { - "is-number": "4.0.0", - "kind-of": "6.0.2", - "math-random": "1.0.1" + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" }, "dependencies": { "is-number": { @@ -10729,10 +10704,10 @@ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -10748,13 +10723,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "readdirp": { @@ -10763,26 +10738,26 @@ "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.6", - "set-immediate-shim": "1.0.1" + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" } }, "redis": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha1-ICKI4/WMSfYHnZevehDhMDrhSwI=", + "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", "requires": { - "double-ended-queue": "2.1.0-0", - "redis-commands": "1.3.5", - "redis-parser": "2.6.0" + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" } }, "redis-commands": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.5.tgz", - "integrity": "sha1-RJWIlBTx6IYmEYCxRC5ylWAtg6I=" + "integrity": "sha512-foGF8u6MXGFF++1TZVC6icGXuMYPftKXt1FBT2vrfU9ZATNtZJ8duRC5d1lEfE8hyVe3jhelHGB91oB7I6qLsA==" }, "redis-parser": { "version": "2.6.0", @@ -10798,37 +10773,37 @@ "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=" + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" }, "regenerator-transform": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha1-HkmWg3Ix2ot/PPQRTXG1aRoGgN0=", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "private": "0.1.8" + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" } }, "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "optional": true, "requires": { - "is-equal-shallow": "0.1.3" + "is-equal-shallow": "^0.1.3" } }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, "regexp.prototype.flags": { @@ -10837,7 +10812,7 @@ "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", "dev": true, "requires": { - "define-properties": "1.1.2" + "define-properties": "^1.1.2" } }, "regexpp": { @@ -10852,9 +10827,9 @@ "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" } }, "registry-auth-token": { @@ -10863,8 +10838,8 @@ "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", "dev": true, "requires": { - "rc": "1.2.8", - "safe-buffer": "5.1.2" + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" } }, "registry-url": { @@ -10873,7 +10848,7 @@ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { - "rc": "1.2.8" + "rc": "^1.0.1" } }, "regjsgen": { @@ -10888,7 +10863,7 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "0.5.0" + "jsesc": "~0.5.0" }, "dependencies": { "jsesc": { @@ -10923,7 +10898,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "1.0.2" + "is-finite": "^1.0.0" } }, "request": { @@ -10931,26 +10906,26 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.8.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.2", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.1.0", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.19", - "oauth-sign": "0.9.0", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.1.2", - "tough-cookie": "2.4.3", - "tunnel-agent": "0.6.0", - "uuid": "3.3.2" + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" }, "dependencies": { "extend": { @@ -10963,8 +10938,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" + "ajv": "^5.3.0", + "har-schema": "^2.0.0" } }, "mime-db": { @@ -10977,7 +10952,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", "requires": { - "mime-db": "1.35.0" + "mime-db": "~1.35.0" } }, "oauth-sign": { @@ -10990,8 +10965,8 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { - "psl": "1.1.29", - "punycode": "1.4.1" + "psl": "^1.1.24", + "punycode": "^1.4.1" } }, "uuid": { @@ -11007,10 +10982,10 @@ "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=", "dev": true, "requires": { - "bluebird": "3.5.1", + "bluebird": "^3.5.0", "request-promise-core": "1.1.1", - "stealthy-require": "1.1.1", - "tough-cookie": "2.3.4" + "stealthy-require": "^1.1.0", + "tough-cookie": ">=2.3.3" } }, "request-promise-core": { @@ -11019,7 +10994,7 @@ "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "dev": true, "requires": { - "lodash": "4.17.5" + "lodash": "^4.13.1" } }, "require-uncached": { @@ -11028,8 +11003,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "0.1.0", - "resolve-from": "1.0.1" + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" }, "dependencies": { "resolve-from": { @@ -11043,10 +11018,10 @@ "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", - "integrity": "sha1-TPNaQkf2TKPfjC7yCMxJSxyo/C4=", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", "requires": { - "resolve-from": "2.0.0", - "semver": "5.5.1" + "resolve-from": "^2.0.0", + "semver": "^5.1.0" } }, "requizzle": { @@ -11055,7 +11030,7 @@ "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", "dev": true, "requires": { - "underscore": "1.6.0" + "underscore": "~1.6.0" }, "dependencies": { "underscore": { @@ -11083,23 +11058,23 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "2.0.1", - "signal-exit": "3.0.2" + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" } }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "run-async": { @@ -11108,7 +11083,7 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "2.1.0" + "is-promise": "^2.1.0" } }, "rxjs": { @@ -11131,13 +11106,13 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "0.1.15" + "ret": "~0.1.10" } }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "saslprep": { "version": "1.0.1", @@ -11156,7 +11131,7 @@ "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", "dev": true, "requires": { - "commander": "2.8.1" + "commander": "~2.8.1" }, "dependencies": { "commander": { @@ -11165,7 +11140,7 @@ "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, "requires": { - "graceful-readlink": "1.0.1" + "graceful-readlink": ">= 1.0.0" } } } @@ -11181,33 +11156,33 @@ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { - "semver": "5.5.1" + "semver": "^5.0.3" } }, "send": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha1-pw4coh0TgsEdDZ9iMd6ygQgNerM=", + "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", "requires": { "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.1", + "destroy": "~1.0.4", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.6.3", + "http-errors": "~1.6.2", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.3.1" + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.3.1" }, "dependencies": { "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } @@ -11215,7 +11190,7 @@ "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=" + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" }, "statuses": { "version": "1.3.1", @@ -11227,11 +11202,11 @@ "serve-static": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha1-TFfVNASnYdjy58HooYpH2/J4pxk=", + "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.2", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", "send": "0.16.1" } }, @@ -11249,13 +11224,13 @@ "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -11264,7 +11239,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -11280,7 +11255,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -11303,10 +11278,10 @@ "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha1-BE8aSdiEL/MHqta1Be0Xi9lQE00=", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0" + "is-fullwidth-code-point": "^2.0.0" }, "dependencies": { "is-fullwidth-code-point": { @@ -11325,23 +11300,23 @@ "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.1", - "use": "3.1.0" + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" }, "dependencies": { "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -11353,7 +11328,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -11362,7 +11337,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "source-map": { @@ -11376,12 +11351,12 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" }, "dependencies": { "define-property": { @@ -11390,36 +11365,36 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "isobject": { @@ -11431,7 +11406,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } @@ -11439,18 +11414,18 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.2.0" } }, "sntp": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha1-LGzsFP7cIiJznK+bXD2F0cxaLMg=", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "requires": { - "hoek": "4.2.1" + "hoek": "4.x.x" } }, "socks": { @@ -11458,8 +11433,8 @@ "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", "requires": { - "ip": "1.1.5", - "smart-buffer": "1.1.15" + "ip": "^1.1.4", + "smart-buffer": "^1.0.13" } }, "socks-proxy-agent": { @@ -11467,8 +11442,8 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", "requires": { - "agent-base": "4.2.1", - "socks": "2.2.1" + "agent-base": "~4.2.0", + "socks": "~2.2.0" }, "dependencies": { "smart-buffer": { @@ -11481,8 +11456,8 @@ "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.1.tgz", "integrity": "sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w==", "requires": { - "ip": "1.1.5", - "smart-buffer": "4.0.1" + "ip": "^1.1.5", + "smart-buffer": "^4.0.1" } } } @@ -11493,7 +11468,7 @@ "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, "requires": { - "is-plain-obj": "1.1.0" + "is-plain-obj": "^1.0.0" } }, "sort-keys-length": { @@ -11502,7 +11477,7 @@ "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", "dev": true, "requires": { - "sort-keys": "1.1.2" + "sort-keys": "^1.0.0" } }, "source-map": { @@ -11512,25 +11487,25 @@ "optional": true }, "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha1-etD1k/IoFZjoVN+A8ZquS5LXoRo=", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "atob": "2.1.1", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha1-Aoam3ovkJkEzhZTpfM6nXwosWF8=", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "0.5.7" + "source-map": "^0.5.6" }, "dependencies": { "source-map": { @@ -11550,23 +11525,23 @@ "spex": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/spex/-/spex-2.0.2.tgz", - "integrity": "sha1-6MjWM6TGevZC3e1wHsI1DJ3pZKA=" + "integrity": "sha512-LU6TS3qTEpRth+FnNs/fIWEmridYN7JmaN2k1Jk31XVC4ex7+wYxiHMnKguRxS7oKjbOFl4H6seeWNDFFgkVRg==" }, "split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha1-YFvZvjA6pZ+zX5Ip++oN3snqB9k=", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "requires": { - "through": "2.3.8" + "through": "2" } }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "3.0.2" + "extend-shallow": "^3.0.0" } }, "sprintf-js": { @@ -11580,15 +11555,15 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "requires": { - "asn1": "0.2.4", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.2", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.2", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "safer-buffer": "2.1.2", - "tweetnacl": "0.14.5" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" } }, "stack-trace": { @@ -11602,8 +11577,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { "define-property": { @@ -11612,7 +11587,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -11634,7 +11609,7 @@ "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { - "duplexer": "0.1.1" + "duplexer": "~0.1.1" } }, "string-width": { @@ -11642,9 +11617,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string.prototype.matchall": { @@ -11653,11 +11628,11 @@ "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", "dev": true, "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.12.0", - "function-bind": "1.1.1", - "has-symbols": "1.0.0", - "regexp.prototype.flags": "1.2.0" + "define-properties": "^1.1.2", + "es-abstract": "^1.10.0", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "regexp.prototype.flags": "^1.2.0" } }, "string_decoder": { @@ -11665,7 +11640,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } }, "stringstream": { @@ -11678,16 +11653,16 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", - "integrity": "sha1-SYdzYmT8NEzyD2w0rKnRPR1O1sU=", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", "dev": true, "requires": { - "is-natural-number": "4.0.1" + "is-natural-number": "^4.0.1" } }, "strip-eof": { @@ -11708,7 +11683,7 @@ "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", "dev": true, "requires": { - "escape-string-regexp": "1.0.5" + "escape-string-regexp": "^1.0.2" } }, "supports-color": { @@ -11717,7 +11692,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "symbol-observable": { @@ -11732,12 +11707,12 @@ "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "requires": { - "ajv": "6.5.1", - "ajv-keywords": "3.2.0", - "chalk": "2.4.1", - "lodash": "4.17.5", + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", "slice-ansi": "1.0.0", - "string-width": "2.1.1" + "string-width": "^2.1.1" }, "dependencies": { "ajv": { @@ -11746,10 +11721,10 @@ "integrity": "sha512-pgZos1vgOHDiC7gKNbZW8eKvCnNXARv2oqrGQT7Hzbq5Azp7aZG6DJzADnkuSq7RH6qkXp4J/m68yPX/2uBHyQ==", "dev": true, "requires": { - "fast-deep-equal": "2.0.1", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.4.1", - "uri-js": "4.2.2" + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.1" } }, "ansi-regex": { @@ -11764,7 +11739,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -11773,9 +11748,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "fast-deep-equal": { @@ -11802,8 +11777,8 @@ "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -11812,7 +11787,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "supports-color": { @@ -11821,7 +11796,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -11832,8 +11807,8 @@ "integrity": "sha512-LtOZVj3mBWk9ChtZmYy1NCSgWshClqoCzgcj0r0VRY+xm9N4ii2NZyLHVbFhGrWsxVt5uPiVeaZDePoD3krrGA==", "dev": true, "requires": { - "@semantic-release/git": "4.0.3", - "connected-domain": "1.0.0" + "@semantic-release/git": "^4.0.1", + "connected-domain": "^1.0.0" } }, "taffydb": { @@ -11848,13 +11823,13 @@ "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", "dev": true, "requires": { - "bl": "1.2.2", - "buffer-alloc": "1.2.0", - "end-of-stream": "1.4.1", - "fs-constants": "1.0.0", - "readable-stream": "2.3.6", - "to-buffer": "1.1.1", - "xtend": "4.0.1" + "bl": "^1.0.0", + "buffer-alloc": "^1.1.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.0", + "xtend": "^4.0.0" } }, "term-size": { @@ -11863,22 +11838,33 @@ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "dev": true, "requires": { - "execa": "0.7.0" + "execa": "^0.7.0" }, "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } } } @@ -11900,8 +11886,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": "1.0.34", - "xtend": "4.0.1" + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" }, "dependencies": { "isarray": { @@ -11916,10 +11902,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -11941,7 +11927,7 @@ "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", "dev": true, "requires": { - "os-homedir": "1.0.2" + "os-homedir": "^1.0.0" } }, "timed-out": { @@ -11956,17 +11942,17 @@ "integrity": "sha512-tsEStd7kmACHENhsUPaxb8Jf8/+GZZxyNFQbZD07HQOyooOa6At1rQqjffgvg7n+dxscQa9cjjMdWhJtsP2sxg==", "dev": true, "requires": { - "es5-ext": "0.10.46", - "next-tick": "1.0.0" + "es5-ext": "~0.10.14", + "next-tick": "1" } }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.2" } }, "to-buffer": { @@ -11987,19 +11973,19 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" } }, "to-regex-range": { @@ -12008,8 +11994,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" }, "dependencies": { "is-number": { @@ -12018,7 +12004,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } } } @@ -12029,15 +12015,15 @@ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, "requires": { - "nopt": "1.0.10" + "nopt": "~1.0.10" } }, "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha1-7GDO44rGdQY//JelwYlwV47oNlU=", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "requires": { - "punycode": "1.4.1" + "punycode": "^1.4.1" } }, "trim-repeated": { @@ -12046,7 +12032,7 @@ "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5" + "escape-string-regexp": "^1.0.2" } }, "trim-right": { @@ -12065,7 +12051,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "^5.0.1" } }, "tv4": { @@ -12084,7 +12070,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "requires": { - "prelude-ls": "1.1.2" + "prelude-ls": "~1.1.2" } }, "type-detect": { @@ -12095,25 +12081,25 @@ "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.18" + "mime-types": "~2.1.18" } }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=" + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" }, "unbzip2-stream": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz", - "integrity": "sha1-c6AzpWe7veWWVLGTxE1Ip+T0PEc=", + "integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==", "dev": true, "requires": { - "buffer": "3.6.0", - "through": "2.3.8" + "buffer": "^3.0.1", + "through": "^2.3.6" }, "dependencies": { "base64-js": { @@ -12129,8 +12115,8 @@ "dev": true, "requires": { "base64-js": "0.0.8", - "ieee754": "1.1.8", - "isarray": "1.0.0" + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } } } @@ -12141,7 +12127,7 @@ "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", "dev": true, "requires": { - "debug": "2.6.9" + "debug": "^2.2.0" }, "dependencies": { "debug": { @@ -12184,10 +12170,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" }, "dependencies": { "extend-shallow": { @@ -12196,7 +12182,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "set-value": { @@ -12205,10 +12191,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" } } } @@ -12219,7 +12205,7 @@ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "dev": true, "requires": { - "crypto-random-string": "1.0.0" + "crypto-random-string": "^1.0.0" } }, "universalify": { @@ -12239,8 +12225,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "dependencies": { "has-value": { @@ -12249,9 +12235,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "dependencies": { "isobject": { @@ -12303,16 +12289,16 @@ "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "dev": true, "requires": { - "boxen": "1.3.0", - "chalk": "2.4.1", - "configstore": "3.1.2", - "import-lazy": "2.1.0", - "is-ci": "1.2.0", - "is-installed-globally": "0.1.0", - "is-npm": "1.0.0", - "latest-version": "3.1.0", - "semver-diff": "2.1.0", - "xdg-basedir": "3.0.0" + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" }, "dependencies": { "ansi-styles": { @@ -12321,7 +12307,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -12330,9 +12316,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } } } @@ -12343,7 +12329,7 @@ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { - "punycode": "2.1.1" + "punycode": "^2.1.0" }, "dependencies": { "punycode": { @@ -12382,7 +12368,7 @@ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { - "prepend-http": "1.0.4" + "prepend-http": "^1.0.1" } }, "url-to-options": { @@ -12392,21 +12378,10 @@ "dev": true }, "use": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha1-FHFr8D/f79AwQK71jYtLhfOnxUQ=", - "dev": true, - "requires": { - "kind-of": "6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", - "dev": true - } - } + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true }, "user-home": { "version": "1.1.1", @@ -12427,7 +12402,7 @@ "uuid": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha1-EsUou51Y0LkmXZovbw/ovhf/HxQ=" + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" }, "uws": { "version": "10.148.1", @@ -12441,7 +12416,7 @@ "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", "dev": true, "requires": { - "user-home": "1.1.1" + "user-home": "^1.1.1" } }, "vary": { @@ -12454,9 +12429,9 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "1.0.0", + "assert-plus": "^1.0.0", "core-util-is": "1.0.2", - "extsprintf": "1.3.0" + "extsprintf": "^1.2.0" } }, "which": { @@ -12465,7 +12440,7 @@ "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "wide-align": { @@ -12473,7 +12448,7 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", "integrity": "sha1-Vx4PGwYEY268DfwhsDObvjE0FxA=", "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2" } }, "widest-line": { @@ -12482,7 +12457,7 @@ "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", "dev": true, "requires": { - "string-width": "2.1.1" + "string-width": "^2.1.1" }, "dependencies": { "ansi-regex": { @@ -12503,8 +12478,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -12513,7 +12488,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -12523,12 +12498,12 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.1.tgz", "integrity": "sha1-o6kmUQVWQmPGeFtFg7jIrKJv3tY=", "requires": { - "async": "1.0.0", - "colors": "1.0.3", - "cycle": "1.0.3", - "eyes": "0.1.8", - "isstream": "0.1.2", - "stack-trace": "0.0.10" + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" }, "dependencies": { "async": { @@ -12568,7 +12543,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "0.5.1" + "mkdirp": "^0.5.1" } }, "write-file-atomic": { @@ -12577,9 +12552,9 @@ "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "signal-exit": "3.0.2" + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" } }, "ws": { @@ -12587,7 +12562,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-6.0.0.tgz", "integrity": "sha512-c2UlYcAZp1VS8AORtpq6y4RJIkJ9dQz18W32SpR/qXGfLDZ2jU4y4wKvvZwqbi7U6gxFQTeE+urMbXU/tsDy4w==", "requires": { - "async-limiter": "1.0.0" + "async-limiter": "~1.0.0" } }, "xdg-basedir": { @@ -12601,8 +12576,8 @@ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", "requires": { - "sax": "1.2.1", - "xmlbuilder": "4.2.1" + "sax": ">=0.6.0", + "xmlbuilder": "^4.1.0" } }, "xmlbuilder": { @@ -12610,7 +12585,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", "requires": { - "lodash": "4.17.5" + "lodash": "^4.0.0" } }, "xmlcreate": { @@ -12645,8 +12620,8 @@ "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, "requires": { - "buffer-crc32": "0.2.13", - "fd-slicer": "1.1.0" + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" } } } diff --git a/package.json b/package.json index 2aa1fc4f09e..773a7830401 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "commander": "2.17.1", "deepcopy": "1.0.0", "express": "4.16.2", + "express-graphql": "^0.6.12", "graphql": "^0.9.1", "intersect": "1.0.1", "lodash": "4.17.5", diff --git a/src/ParseServer.js b/src/ParseServer.js index 81fb7f77e92..ef56c426ef0 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -6,7 +6,8 @@ var batch = require('./batch'), middlewares = require('./middlewares'), Parse = require('parse/node').Parse, path = require('path'); - +const graphqlHTTP = require('express-graphql'); +const GraphQLParseSchema = require('./graphql/Schema').GraphQLParseSchema; import { ParseServerOptions, LiveQueryServerOptions } from './Options'; import defaults from './defaults'; @@ -38,6 +39,8 @@ import { AggregateRouter } from './Routers/AggregateRouter'; import { ParseServerRESTController } from './ParseServerRESTController'; import * as controllers from './Controllers'; +import auth from './Auth'; + // Mutate the Parse object to add the Cloud Code handlers addParseCloud(); @@ -227,6 +230,26 @@ class ParseServer { */ start(options: ParseServerOptions, callback: ?()=>void) { const app = express(); + app.use(options.mountPath + '/graphql', graphqlHTTP(async (req) => { + // TODO: use middleware please :) + req.config = req.config || Config.get(Parse.applicationId); + req.auth = req.auth || new auth.Auth({ config: req.config }); + // TODO: only await perhaps once, and optimize perf + const schema = await Config.get(Parse.applicationId).database.loadSchema(); + const allClasses = await schema.getAllClasses(true); + const fullSchema = allClasses.reduce((memo, classDef) => { + memo[classDef.className] = classDef; + return memo; + }, {}); + const Schema = new GraphQLParseSchema(fullSchema, 'test'); + const s = Schema.Schema(); + const root = Schema.Root(); + return { + schema: s, + rootValue: root, + graphiql: true + } + })); if (options.middleware) { let middleware; if (typeof options.middleware == 'string') { diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 729ead816ac..dd7efced2a6 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -139,9 +139,7 @@ export class ParseClass { name: className, description: `Parse Class ${className}`, interfaces: [ParseObjectInterface], - fields: () => { - return this.buildFields(graphQLField, false, true); - }, + fields: this.buildFields(graphQLField, false, true), resolve: () => { return; }, diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 5452c32116c..0887411411c 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -149,7 +149,7 @@ export class GraphQLParseSchema { args: { input: { type: inputType }}, name: 'create', resolve: (root, args, context, info) => { - return rest.create(context.config, context.auth, className, args).then((res) => { + return rest.create(context.config, context.auth, className, args.input).then((res) => { // Run find to match graphQL style return runGet(context, info, className, res.response.objectId); }); @@ -167,7 +167,7 @@ export class GraphQLParseSchema { resolve: (root, args, context, info) => { const objectId = args.objectId; const input = args.input; - return rest.update(context.config, context.auth, className, objectId, input).then(() => { + return rest.update(context.config, context.auth, className, { objectId }, input).then(() => { // Run find to match graphQL style return runGet(context, info, className, objectId); }); diff --git a/src/graphql/types/NumberQuery.js b/src/graphql/types/NumberQuery.js index 019cb423723..347e2c963a9 100644 --- a/src/graphql/types/NumberQuery.js +++ b/src/graphql/types/NumberQuery.js @@ -9,10 +9,10 @@ export const NumberQuery = new GraphQLScalarType({ Supported constraints: - key: 1 - - key: {$lt: 1} # less than - - key: {$gt: 1} # greater than - - key: {$lte: 1} # less than or equal - - key: {$gte: 1} # greater than or equal + - key: {lt: 1} # less than + - key: {gt: 1} # greater than + - key: {lte: 1} # less than or equal + - key: {gte: 1} # greater than or equal `, serialize: () => { throw "NumberQuery serialize not implemented" diff --git a/src/graphql/types/QueryConstraint.js b/src/graphql/types/QueryConstraint.js index e68322416be..661db029bb5 100644 --- a/src/graphql/types/QueryConstraint.js +++ b/src/graphql/types/QueryConstraint.js @@ -3,7 +3,7 @@ import { Kind } from 'graphql' -const supportedOperators = ['$eq', '$ne', '$in', '$nin', '$exists', '$select', '$dontSelect'] +const supportedOperators = ['eq', 'ne', 'in', 'nin', 'exists', 'select', 'dontSelect'] export const QueryConstraint = new GraphQLScalarType({ name: 'QueryConstraint', @@ -12,37 +12,37 @@ export const QueryConstraint = new GraphQLScalarType({ Equal To: - key: "value" - - key: {$eq: "value"} + - key: {eq: "value"} Not Equal To - - key: {$ne: "value"} + - key: {ne: "value"} Contained in: - - key: {$in: ["value1", "value2"]} + - key: {in: ["value1", "value2"]} Not Contained in: - - key: {$nin: ["value1", "value2"]} + - key: {nin: ["value1", "value2"]} Exists: - - key: {$exists: true} + - key: {exists: true} This matches a value for a key in the result of a different query - - key: {$select: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} + - key: {select: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} Requires that a key’s value not match a value for a key in the result of a different query - - key: {$dontSelect: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} + - key: {dontSelect: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} `, serialize: () => { - throw "StringQuery serialize not implemented" + throw "QueryConstraint serialize not implemented" }, parseValue: () => { - throw "StringQuery parseValue not implemented" + throw "QueryConstraint parseValue not implemented" }, parseLiteral: (ast) => { if (ast.kind == Kind.OBJECT) { const fields = ast.fields; return fields.reduce((memo, field) => { const operator = field.name.value; - if (supportedOperators.indexOf('$' + operator) > -1) { + if (supportedOperators.indexOf(operator) > -1) { const value = field.value.value; memo['$' + operator] = value; } @@ -51,7 +51,7 @@ export const QueryConstraint = new GraphQLScalarType({ } else if (ast.kind == Kind.STRING) { return ast.value; } else { - throw 'Invalid literal for StringQuery'; + throw 'Invalid literal for QueryConstraint'; } } }); diff --git a/src/graphql/types/StringQuery.js b/src/graphql/types/StringQuery.js index d9e3a378b03..2931e16e00b 100644 --- a/src/graphql/types/StringQuery.js +++ b/src/graphql/types/StringQuery.js @@ -9,7 +9,7 @@ export const StringQuery = new GraphQLScalarType({ Supported constraints: - key: "value" - - key: {$regex: "value"} + - key: {regex: "value"} `, serialize: () => { throw "StringQuery serialize not implemented" diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index f7cef23d1c3..ac4385e63d1 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -77,6 +77,8 @@ export function type(fieldName, field) { return GraphQLDate; } else if (type == 'Pointer') { return GraphQLPointer; + } else if (type == 'Object') { + return GraphQLJSONObject; } } From 97f0fdced7b53510188464682cac218495ffd73a Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Wed, 22 Aug 2018 19:13:21 -0400 Subject: [PATCH 07/62] Adds common constraints on Number and String --- src/graphql/Schema.js | 4 +- src/graphql/types/NumberQuery.js | 15 +++++-- src/graphql/types/QueryConstraint.js | 59 ++++++++++++++-------------- src/graphql/types/StringQuery.js | 16 ++++++-- 4 files changed, 56 insertions(+), 38 deletions(-) diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 0887411411c..0a48e4533c3 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -104,7 +104,9 @@ export class GraphQLParseSchema { const { queryType, objectType } = loadClass(className, this.schema); - + if (className.startsWith('_')) { + className = className.slice(1).toLowerCase() + 's'; + } MainSchemaOptions.fields[className] = { type: new GraphQLList(objectType), description: `Use this endpoint to get or query ${className} objects`, diff --git a/src/graphql/types/NumberQuery.js b/src/graphql/types/NumberQuery.js index 347e2c963a9..1efae0e35e9 100644 --- a/src/graphql/types/NumberQuery.js +++ b/src/graphql/types/NumberQuery.js @@ -3,10 +3,17 @@ import { Kind } from 'graphql' +import QueryConstraint from './QueryConstraint'; + export const NumberQuery = new GraphQLScalarType({ name: 'NumberQuery', description: `Queries for number values - Supported constraints: + + Common Constraints: + + ${QueryConstraint.description()} + + Numeric constraints: - key: 1 - key: {lt: 1} # less than @@ -26,9 +33,11 @@ export const NumberQuery = new GraphQLScalarType({ return fields.reduce((memo, field) => { const operator = field.name.value; const value = field.value.value; - memo['$' + operator] = parseFloat(value); + if (['lt', 'gt', 'lte', 'gte'].includes(operator)) { + memo['$' + operator] = parseFloat(value); + } return memo; - }, {}); + }, QueryConstraint.parseFields(fields)); } else if (ast.kind == Kind.INT || ast.kind == Kind.FLOAT) { return parseFloat(ast.value); } else { diff --git a/src/graphql/types/QueryConstraint.js b/src/graphql/types/QueryConstraint.js index 661db029bb5..cd1c9d99f46 100644 --- a/src/graphql/types/QueryConstraint.js +++ b/src/graphql/types/QueryConstraint.js @@ -1,16 +1,11 @@ import { - GraphQLScalarType, Kind } from 'graphql' const supportedOperators = ['eq', 'ne', 'in', 'nin', 'exists', 'select', 'dontSelect'] -export const QueryConstraint = new GraphQLScalarType({ - name: 'QueryConstraint', - description: `Generic Query Constraint - Supported constraints: - - Equal To: +function description() { + return `Equal To: - key: "value" - key: {eq: "value"} @@ -30,28 +25,32 @@ export const QueryConstraint = new GraphQLScalarType({ Requires that a key’s value not match a value for a key in the result of a different query - key: {dontSelect: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} - `, - serialize: () => { - throw "QueryConstraint serialize not implemented" - }, - parseValue: () => { - throw "QueryConstraint parseValue not implemented" - }, - parseLiteral: (ast) => { - if (ast.kind == Kind.OBJECT) { - const fields = ast.fields; - return fields.reduce((memo, field) => { - const operator = field.name.value; - if (supportedOperators.indexOf(operator) > -1) { - const value = field.value.value; - memo['$' + operator] = value; - } - return memo; - }, {}); - } else if (ast.kind == Kind.STRING) { - return ast.value; - } else { - throw 'Invalid literal for QueryConstraint'; + `; +} + +const parseFields = (fields) => { + return fields.reduce((memo, field) => { + const operator = field.name.value; + if (supportedOperators.indexOf(operator) > -1) { + const value = field.value.value; + memo['$' + operator] = value; } + return memo; + }, {}); +} + +const parseLiteral = (ast) => { + if (ast.kind == Kind.OBJECT) { + return parseFields(ast.fields); + } else if (ast.kind == Kind.STRING) { + return ast.value; + } else { + throw 'Invalid literal for QueryConstraint'; } -}); +}; + +export default { + description, + parseLiteral, + parseFields, +} diff --git a/src/graphql/types/StringQuery.js b/src/graphql/types/StringQuery.js index 2931e16e00b..3d3ac0d929c 100644 --- a/src/graphql/types/StringQuery.js +++ b/src/graphql/types/StringQuery.js @@ -3,10 +3,17 @@ import { Kind } from 'graphql' +import QueryConstraint from './QueryConstraint'; + export const StringQuery = new GraphQLScalarType({ name: 'StringQuery', description: `Query constraint on string parameters - Supported constraints: + + Common Constraints: + + ${QueryConstraint.description()} + + String constraints: - key: "value" - key: {regex: "value"} @@ -22,10 +29,11 @@ export const StringQuery = new GraphQLScalarType({ const fields = ast.fields; return fields.reduce((memo, field) => { const operator = field.name.value; - const value = field.value.value; - memo['$' + operator] = value; + if (operator === 'regex') { + memo['$' + operator] = field.value.value; + } return memo; - }, {}); + }, QueryConstraint.parseFields(fields)); } else if (ast.kind == Kind.STRING) { return ast.value; } else { From 41ae482fa51df1ae90e48e4a0e330c840745deec Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Wed, 22 Aug 2018 19:49:34 -0400 Subject: [PATCH 08/62] Nicer display name for internal classes --- src/graphql/ParseClass.js | 14 +++++++++----- src/graphql/Schema.js | 14 +++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index dd7efced2a6..a68b7d46603 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -66,7 +66,7 @@ export function loadClass(className, schema) { const inputType = c.graphQLInputObjectType(); const updateType = c.graphQLUpdateInputObjectType(); const queryType = c.graphQLQueryInputObjectType() - ParseClassCache[className] = { objectType, inputType, updateType, queryType, class: c } + ParseClassCache[className] = { objectType, inputType, updateType, queryType, class: c, displayName: c.displayName } } return ParseClassCache[className]; } @@ -129,6 +129,10 @@ export class ParseClass { constructor(className, schema) { this.className = className; + this.displayName = className; + if (className.startsWith('_')) { + this.displayName = className.slice(1) + 's'; + } this.schema = schema; this.class = this.schema[className]; } @@ -136,7 +140,7 @@ export class ParseClass { graphQLConfig() { const className = this.className; return { - name: className, + name: this.className, description: `Parse Class ${className}`, interfaces: [ParseObjectInterface], fields: this.buildFields(graphQLField, false, true), @@ -173,7 +177,7 @@ export class ParseClass { graphQLInputConfig() { const className = this.className; return { - name: className + 'Input', + name: this.displayName + 'Input', description: `Parse Class ${className} Input`, fields: () => { return this.buildFields(graphQLInputField, true); @@ -188,7 +192,7 @@ export class ParseClass { graphQLQueryConfig() { const className = this.className; return { - name: className + 'Query', + name: this.displayName + 'Query', description: `Parse Class ${className} Query`, fields: () => { return this.buildFields(graphQLQueryField, true); @@ -203,7 +207,7 @@ export class ParseClass { graphQLUpdateInputConfig() { const className = this.className; return { - name: className + 'Update', + name: this.displayName + 'Update', description: `Parse Class ${className} Update`, fields: () => { return this.buildFields(graphQLInputField, true); diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 0a48e4533c3..6492ddcd09d 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -102,12 +102,12 @@ export class GraphQLParseSchema { } Object.keys(this.schema).forEach((className) => { const { - queryType, objectType + queryType, objectType, displayName } = loadClass(className, this.schema); if (className.startsWith('_')) { - className = className.slice(1).toLowerCase() + 's'; + className = className.slice(1) + 's'; } - MainSchemaOptions.fields[className] = { + MainSchemaOptions.fields[displayName] = { type: new GraphQLList(objectType), description: `Use this endpoint to get or query ${className} objects`, args: { @@ -141,10 +141,10 @@ export class GraphQLParseSchema { Object.keys(this.schema).forEach((className) => { const { - inputType, objectType, updateType + inputType, objectType, updateType, displayName } = loadClass(className, this.schema); - MainSchemaMutationOptions.fields['create' + className] = { + MainSchemaMutationOptions.fields['create' + displayName] = { type: objectType, fields: objectType.fields, description: `use this method to create a new ${className}`, @@ -158,7 +158,7 @@ export class GraphQLParseSchema { } } - MainSchemaMutationOptions.fields['update' + className] = { + MainSchemaMutationOptions.fields['update' + displayName] = { type: objectType, description: `use this method to update an existing ${className}`, args: { @@ -176,7 +176,7 @@ export class GraphQLParseSchema { } } - MainSchemaMutationOptions.fields['destroy' + className] = { + MainSchemaMutationOptions.fields['destroy' + displayName] = { type: objectType, description: `use this method to update delete an existing ${className}`, args: { From 687a8992c72d6e64483e0d9c2ccea355f340795b Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Wed, 22 Aug 2018 20:13:27 -0400 Subject: [PATCH 09/62] nit --- src/graphql/ParseClass.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index a68b7d46603..137de893b91 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -195,7 +195,7 @@ export class ParseClass { name: this.displayName + 'Query', description: `Parse Class ${className} Query`, fields: () => { - return this.buildFields(graphQLQueryField, true); + return this.buildFields(graphQLQueryField, false, true); }, resolve: this.get.bind(this), isTypeOf: function(input) { From 602055c146a0bc1f79e465a9829ce19d398e617b Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Wed, 22 Aug 2018 20:38:15 -0400 Subject: [PATCH 10/62] nit --- src/graphql/ParseClass.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 137de893b91..04f353c17ba 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -195,7 +195,9 @@ export class ParseClass { name: this.displayName + 'Query', description: `Parse Class ${className} Query`, fields: () => { - return this.buildFields(graphQLQueryField, false, true); + const fields = this.buildFields(graphQLQueryField, false, true); + delete fields.objectId; + return fields; }, resolve: this.get.bind(this), isTypeOf: function(input) { From a0d28ecc27902ee9d1805ed4cf5a2ae7a5cbabb8 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Wed, 22 Aug 2018 20:47:40 -0400 Subject: [PATCH 11/62] Ensure we cleanup the cache between each calls --- src/graphql/ParseClass.js | 6 +++++- src/graphql/Schema.js | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 04f353c17ba..13f28e5734f 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -57,7 +57,7 @@ function graphQLQueryField(fieldName, field) { }; } -const ParseClassCache = {}; +let ParseClassCache = {}; export function loadClass(className, schema) { if (!ParseClassCache[className]) { @@ -71,6 +71,10 @@ export function loadClass(className, schema) { return ParseClassCache[className]; } +export function clearCache() { + ParseClassCache = {}; +} + const reservedFieldNames = ['objectId', 'createdAt', 'updatedAt']; export const ParseObjectInterface = new GraphQLInterfaceType({ diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 6492ddcd09d..69f858b3bd0 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -1,6 +1,7 @@ import { ParseObject, loadClass, + clearCache, } from './ParseClass'; import { @@ -88,10 +89,12 @@ export class GraphQLParseSchema { } Schema() { - return new GraphQLSchema({ + const schema = new GraphQLSchema({ query: this.Query(), mutation: this.Mutation(), }); + clearCache(); + return schema; } Query() { From 6b41020992ce833d5f96624e4383df5b86463c6a Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 23 Aug 2018 08:20:10 -0400 Subject: [PATCH 12/62] nest responses --- src/graphql/ParseClass.js | 46 ++----------- src/graphql/Schema.js | 126 ++++++++++++++++++++++------------ src/graphql/types/GeoPoint.js | 7 +- 3 files changed, 92 insertions(+), 87 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 13f28e5734f..40e97022e3b 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -2,6 +2,7 @@ import { GraphQLInterfaceType, GraphQLObjectType, GraphQLInputObjectType, + GraphQLList, // GraphQLString, // GraphQLNonNull, // GraphQLBoolean, @@ -66,7 +67,7 @@ export function loadClass(className, schema) { const inputType = c.graphQLInputObjectType(); const updateType = c.graphQLUpdateInputObjectType(); const queryType = c.graphQLQueryInputObjectType() - ParseClassCache[className] = { objectType, inputType, updateType, queryType, class: c, displayName: c.displayName } + ParseClassCache[className] = { objectType, inputType, updateType, queryType, class: c } } return ParseClassCache[className]; } @@ -78,7 +79,7 @@ export function clearCache() { const reservedFieldNames = ['objectId', 'createdAt', 'updatedAt']; export const ParseObjectInterface = new GraphQLInterfaceType({ - name: 'ObjectType', + name: 'ParseObject', fields: { objectId: { type: type('objectId') @@ -95,37 +96,6 @@ export const ParseObjectInterface = new GraphQLInterfaceType({ } }); -export const ParseObject = new GraphQLObjectType({ - name: 'Object', - interfaces: [ParseObjectInterface], - fields: { - objectId: { - type: type('objectId') - }, - createdAt: { - type: type(null, {type: 'Date'}) - }, - updatedAt: { - type: type(null, {type: 'Date'}) - }, - ACL: { - type: type(null, {type: 'ACL'}) - }, - data: { - type: GraphQLJSONObject - } - }, - isTypeOf: (args, context, info) => { - // Use that type when impossible to map to a Schema type - return typeof info.schema._typeMap[args.className] === 'undefined'; - }, - resolve: () => { - /* eslint-disable */ - console.log('RESOLVE CALLED!'); - /* eslint-enable */ - } -}); - export class ParseClass { schema; className; @@ -133,10 +103,6 @@ export class ParseClass { constructor(className, schema) { this.className = className; - this.displayName = className; - if (className.startsWith('_')) { - this.displayName = className.slice(1) + 's'; - } this.schema = schema; this.class = this.schema[className]; } @@ -181,7 +147,7 @@ export class ParseClass { graphQLInputConfig() { const className = this.className; return { - name: this.displayName + 'Input', + name: this.className + 'Input', description: `Parse Class ${className} Input`, fields: () => { return this.buildFields(graphQLInputField, true); @@ -196,7 +162,7 @@ export class ParseClass { graphQLQueryConfig() { const className = this.className; return { - name: this.displayName + 'Query', + name: this.className + 'Query', description: `Parse Class ${className} Query`, fields: () => { const fields = this.buildFields(graphQLQueryField, false, true); @@ -213,7 +179,7 @@ export class ParseClass { graphQLUpdateInputConfig() { const className = this.className; return { - name: this.displayName + 'Update', + name: this.className + 'Update', description: `Parse Class ${className} Update`, fields: () => { return this.buildFields(graphQLInputField, true); diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 69f858b3bd0..fca6d0f7f49 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -15,14 +15,14 @@ import { import rest from '../rest'; // Flatten all graphQL selections to the dot notation. -function reduceSelections(selections) { +function reduceSelections(selections, topKey) { return selections.reduce((memo, selection) => { const value = selection.name.value; if (selection.selectionSet === null) { memo.push(value); } else { // Get the sub seletions and add on current key - const subSelections = reduceSelections(selection.selectionSet.selections); + const subSelections = reduceSelections(selection.selectionSet.selections, topKey); memo = memo.concat(subSelections.map((key) => { return value + '.' + key; })); @@ -32,13 +32,17 @@ function reduceSelections(selections) { } // Get the selections for the 1st node in a array of . separated keys -function getSelections(info) { - const node = info.fieldNodes[0]; - return reduceSelections(node.selectionSet.selections); +function getSelections(node, topKey) { + return reduceSelections(node.selectionSet.selections, topKey); } -function getQueryOptions(info) { - const selectedKeys = getSelections(info); +function getQueryOptions(info, listKey) { + const node = info.fieldNodes[0]; + const selections = node.selectionSet.selections; + const results = selections.filter((selection) => { + return selection.name.value == listKey; + }); + const selectedKeys = getSelections(results[0]); const keys = selectedKeys.join(','); return { keys, @@ -64,19 +68,30 @@ function toGraphQLResult(className, singleResult) { } // Runs a find against the rest API -function runFind(context, info, className, where) { - const options = getQueryOptions(info); +function runFind(context, info, className, where, listKey) { + const options = getQueryOptions(info, listKey); return rest.find(context.config, context.auth, className, where, options) .then(toGraphQLResult(className)); } // runs a get against the rest API -function runGet(context, info, className, objectId) { - const options = getQueryOptions(info); +function runGet(context, info, className, objectId, listKey) { + const options = getQueryOptions(info, listKey); return rest.get(context.config, context.auth, className, objectId, options) .then(toGraphQLResult(className, true)); } +function getResultKeyName(className, pluralize = false) { + let resultName = className.toLocaleLowerCase(); + if (resultName.indexOf('_') == 0) { + resultName = resultName.slice(1); + } + if (pluralize && resultName[resultName.length - 1] != 's') { + resultName += 's'; + } + return resultName; +} + export class GraphQLParseSchema { schema; types; @@ -99,25 +114,30 @@ export class GraphQLParseSchema { Query() { const MainSchemaOptions = { - name: 'ParseSchema', + name: 'Query', description: `The full parse schema`, fields: {} } Object.keys(this.schema).forEach((className) => { const { - queryType, objectType, displayName + queryType, objectType } = loadClass(className, this.schema); - if (className.startsWith('_')) { - className = className.slice(1) + 's'; - } - MainSchemaOptions.fields[displayName] = { - type: new GraphQLList(objectType), + const listKey = getResultKeyName(className, true); + const queryResultType = new GraphQLObjectType({ + name: `${className}QueryResponse`, + fields: { + [listKey]: { type: new GraphQLList(objectType) }, + } + }); + + MainSchemaOptions.fields[className] = { + type: queryResultType, description: `Use this endpoint to get or query ${className} objects`, args: { objectId: { type: GraphQLID, name: 'objectId' }, where: { type: queryType } }, - resolve: (root, args, context, info) => { + resolve: async (root, args, context, info) => { // Get the selections let query = {}; if (args.where) { @@ -126,17 +146,16 @@ export class GraphQLParseSchema { if (args.objectId) { query = Object.assign(query, { objectId: args.objectId }); } - return runFind(context, info, className, query); + return { [listKey] : await runFind(context, info, className, query, listKey) }; } }; }); - MainSchemaOptions.fields['ParseObject'] = { type: ParseObject }; return new GraphQLObjectType(MainSchemaOptions); } Mutation() { const MainSchemaMutationOptions = { - name: 'ParseSchemaMutation', + name: 'Mutation', fields: {} } // TODO: Refactor FunctionRouter to extract (as it's a new entry) @@ -144,54 +163,71 @@ export class GraphQLParseSchema { Object.keys(this.schema).forEach((className) => { const { - inputType, objectType, updateType, displayName + inputType, objectType, updateType } = loadClass(className, this.schema); + const resultKey = getResultKeyName(className); + const createResultType = new GraphQLObjectType({ + name: `${className}CreateCompletePayload`, + fields: { + object: { type: objectType } + } + }); + + const updateResultType = new GraphQLObjectType({ + name: `${className}UpdateCompletePayload`, + fields: { + [resultKey]: { type: objectType } + } + }); + + const deleteResultType = new GraphQLObjectType({ + name: `${className}DeleteCompletePayload`, + fields: { + [resultKey]: { type: objectType } + } + }); - MainSchemaMutationOptions.fields['create' + displayName] = { - type: objectType, + MainSchemaMutationOptions.fields['create' + className] = { + type: createResultType, fields: objectType.fields, description: `use this method to create a new ${className}`, args: { input: { type: inputType }}, name: 'create', - resolve: (root, args, context, info) => { - return rest.create(context.config, context.auth, className, args.input).then((res) => { - // Run find to match graphQL style - return runGet(context, info, className, res.response.objectId); - }); + resolve: async (root, args, context, info) => { + const res = await rest.create(context.config, context.auth, className, args.input); + // Run find to match graphQL style + return { [resultKey] : await runGet(context, info, className, res.response.objectId, resultKey) }; } } - MainSchemaMutationOptions.fields['update' + displayName] = { - type: objectType, + MainSchemaMutationOptions.fields['update' + className] = { + type: updateResultType, description: `use this method to update an existing ${className}`, args: { objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' }, input: { type: updateType } }, name: 'update', - resolve: (root, args, context, info) => { + resolve: async (root, args, context, info) => { const objectId = args.objectId; const input = args.input; - return rest.update(context.config, context.auth, className, { objectId }, input).then(() => { - // Run find to match graphQL style - return runGet(context, info, className, objectId); - }); + await rest.update(context.config, context.auth, className, { objectId }, input); + // Run find to match graphQL styl + return { [resultKey] : await runGet(context, info, className, objectId, resultKey) }; } } - MainSchemaMutationOptions.fields['destroy' + displayName] = { - type: objectType, + MainSchemaMutationOptions.fields['destroy' + className] = { + type: deleteResultType, description: `use this method to update delete an existing ${className}`, args: { objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' } }, name: 'destroy', - resolve: (root, args, context, info) => { - return runGet(context, info, className, args.objectId).then((object) => { - return rest.del(context.config, context.auth, className, args.objectId).then(() => { - return object; - }); - }); + resolve: async (root, args, context, info) => { + const object = await runGet(context, info, className, args.objectId); + await rest.del(context.config, context.auth, className, args.objectId); + return { [resultKey]: object } } } }); diff --git a/src/graphql/types/GeoPoint.js b/src/graphql/types/GeoPoint.js index 7a7c2dca855..150cf0a75ca 100644 --- a/src/graphql/types/GeoPoint.js +++ b/src/graphql/types/GeoPoint.js @@ -17,8 +17,11 @@ export const GraphQLGeoPoint = new GraphQLScalarType({ description: 'latitude of the point, in degrees' } }, - serialize: () => { - throw "not implemented" + serialize: (object) => { + return { + latitude: object.latitude, + longitude: object.longitude, + } }, parseValue: () => { throw "not implemented" From 31bc41788b14cfdf5129ab01b5197a1e4158052a Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 23 Aug 2018 10:17:53 -0400 Subject: [PATCH 13/62] Wrap all results in objects / object --- src/graphql/ParseClass.js | 25 ++++++++++++- src/graphql/Schema.js | 79 +++++++++++---------------------------- 2 files changed, 45 insertions(+), 59 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 40e97022e3b..f247be87d25 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -66,8 +66,10 @@ export function loadClass(className, schema) { const objectType = c.graphQLObjectType(); const inputType = c.graphQLInputObjectType(); const updateType = c.graphQLUpdateInputObjectType(); - const queryType = c.graphQLQueryInputObjectType() - ParseClassCache[className] = { objectType, inputType, updateType, queryType, class: c } + const queryType = c.graphQLQueryInputObjectType(); + const queryResultType = c.graphQLQueryResultType(objectType); + const mutationResultType = c.graphQLMutationResultType(objectType); + ParseClassCache[className] = { objectType, inputType, updateType, queryType, queryResultType, mutationResultType, class: c } } return ParseClassCache[className]; } @@ -203,6 +205,25 @@ export class ParseClass { return new GraphQLInputObjectType(this.graphQLQueryConfig()); } + graphQLQueryResultType(objectType) { + return new GraphQLObjectType({ + name: `${this.className}QueryResponse`, + fields: { + objects: { type: new GraphQLList(objectType) }, + } + }); + } + + graphQLMutationResultType(objectType) { + return new GraphQLObjectType({ + name: `${this.className}MutationCompletePayload`, + fields: { + object: { type: objectType } + } + }); + } + + graphQLObjectType() { return new GraphQLObjectType(this.graphQLConfig()); } diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index fca6d0f7f49..34acc8fec60 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -1,5 +1,4 @@ import { - ParseObject, loadClass, clearCache, } from './ParseClass'; @@ -8,7 +7,7 @@ import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, - GraphQLList, + GraphQLInt, GraphQLID, } from 'graphql' @@ -68,30 +67,19 @@ function toGraphQLResult(className, singleResult) { } // Runs a find against the rest API -function runFind(context, info, className, where, listKey) { - const options = getQueryOptions(info, listKey); +function runFind(context, info, className, where) { + const options = getQueryOptions(info, 'objects'); return rest.find(context.config, context.auth, className, where, options) .then(toGraphQLResult(className)); } // runs a get against the rest API -function runGet(context, info, className, objectId, listKey) { - const options = getQueryOptions(info, listKey); +function runGet(context, info, className, objectId) { + const options = getQueryOptions(info, 'object'); return rest.get(context.config, context.auth, className, objectId, options) .then(toGraphQLResult(className, true)); } -function getResultKeyName(className, pluralize = false) { - let resultName = className.toLocaleLowerCase(); - if (resultName.indexOf('_') == 0) { - resultName = resultName.slice(1); - } - if (pluralize && resultName[resultName.length - 1] != 's') { - resultName += 's'; - } - return resultName; -} - export class GraphQLParseSchema { schema; types; @@ -120,22 +108,17 @@ export class GraphQLParseSchema { } Object.keys(this.schema).forEach((className) => { const { - queryType, objectType + queryType, queryResultType } = loadClass(className, this.schema); - const listKey = getResultKeyName(className, true); - const queryResultType = new GraphQLObjectType({ - name: `${className}QueryResponse`, - fields: { - [listKey]: { type: new GraphQLList(objectType) }, - } - }); MainSchemaOptions.fields[className] = { type: queryResultType, description: `Use this endpoint to get or query ${className} objects`, args: { objectId: { type: GraphQLID, name: 'objectId' }, - where: { type: queryType } + where: { type: queryType }, + limit: { type: GraphQLInt }, + skip: { type: GraphQLInt } }, resolve: async (root, args, context, info) => { // Get the selections @@ -146,7 +129,8 @@ export class GraphQLParseSchema { if (args.objectId) { query = Object.assign(query, { objectId: args.objectId }); } - return { [listKey] : await runFind(context, info, className, query, listKey) }; + const objects = await runFind(context, info, className, query); + return { objects }; } }; }); @@ -163,45 +147,25 @@ export class GraphQLParseSchema { Object.keys(this.schema).forEach((className) => { const { - inputType, objectType, updateType + inputType, objectType, updateType, mutationResultType } = loadClass(className, this.schema); - const resultKey = getResultKeyName(className); - const createResultType = new GraphQLObjectType({ - name: `${className}CreateCompletePayload`, - fields: { - object: { type: objectType } - } - }); - - const updateResultType = new GraphQLObjectType({ - name: `${className}UpdateCompletePayload`, - fields: { - [resultKey]: { type: objectType } - } - }); - - const deleteResultType = new GraphQLObjectType({ - name: `${className}DeleteCompletePayload`, - fields: { - [resultKey]: { type: objectType } - } - }); MainSchemaMutationOptions.fields['create' + className] = { - type: createResultType, + type: mutationResultType, fields: objectType.fields, description: `use this method to create a new ${className}`, args: { input: { type: inputType }}, name: 'create', resolve: async (root, args, context, info) => { const res = await rest.create(context.config, context.auth, className, args.input); - // Run find to match graphQL style - return { [resultKey] : await runGet(context, info, className, res.response.objectId, resultKey) }; + // Run get to match graphQL style + const object = await runGet(context, info, className, res.response.objectId); + return { object }; } } MainSchemaMutationOptions.fields['update' + className] = { - type: updateResultType, + type: mutationResultType, description: `use this method to update an existing ${className}`, args: { objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' }, @@ -212,13 +176,14 @@ export class GraphQLParseSchema { const objectId = args.objectId; const input = args.input; await rest.update(context.config, context.auth, className, { objectId }, input); - // Run find to match graphQL styl - return { [resultKey] : await runGet(context, info, className, objectId, resultKey) }; + // Run get to match graphQL style + const object = await runGet(context, info, className, objectId); + return { object }; } } MainSchemaMutationOptions.fields['destroy' + className] = { - type: deleteResultType, + type: mutationResultType, description: `use this method to update delete an existing ${className}`, args: { objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' } @@ -227,7 +192,7 @@ export class GraphQLParseSchema { resolve: async (root, args, context, info) => { const object = await runGet(context, info, className, args.objectId); await rest.del(context.config, context.auth, className, args.objectId); - return { [resultKey]: object } + return { object } } } }); From 41430760bc2ff29292eb59a2e0e0f574b7f074e7 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 23 Aug 2018 10:52:56 -0400 Subject: [PATCH 14/62] Adds support for skip, limit, pointers --- src/graphql/ParseClass.js | 17 +++++++++++------ src/graphql/Schema.js | 26 ++++++++++++++++---------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index f247be87d25..6f78f4a6724 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -15,7 +15,6 @@ import { type, // GraphQLGeoPoint, GraphQLPointer, - GraphQLJSONObject } from './types' function graphQLField(fieldName, field) { @@ -115,7 +114,7 @@ export class ParseClass { name: this.className, description: `Parse Class ${className}`, interfaces: [ParseObjectInterface], - fields: this.buildFields(graphQLField, false, true), + fields: this.buildFields(graphQLField, false, false, true), resolve: () => { return; }, @@ -125,7 +124,7 @@ export class ParseClass { }; } - buildFields(mapper, filterReserved = false, isQuery = false) { + buildFields(mapper, filterReserved = false, isQuery = false, isObject = false) { const fields = this.class.fields; return Object.keys(fields).reduce((memo, fieldName) => { if (filterReserved && reservedFieldNames.indexOf(fieldName) >= 0) { @@ -133,9 +132,15 @@ export class ParseClass { } const field = fields[fieldName]; let gQLField = mapper(fieldName, field); - if (field.type == 'Pointer' && isQuery) { - gQLField = { - type: loadClass(field.targetClass, this.schema).objectType + if (field.type == 'Pointer') { + if (isQuery) { + gQLField = { + type: loadClass(field.targetClass, this.schema).queryType + } + } else if (isObject) { + gQLField = { + type: loadClass(field.targetClass, this.schema).objectType + } } } if (!gQLField) { diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 34acc8fec60..ab8623b00d2 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -67,9 +67,22 @@ function toGraphQLResult(className, singleResult) { } // Runs a find against the rest API -function runFind(context, info, className, where) { +function runFind(context, info, className, args) { + let query = {}; + if (args.where) { + query = Object.assign(query, args.where); + } + if (args.objectId) { + query = Object.assign(query, { objectId: args.objectId }); + } const options = getQueryOptions(info, 'objects'); - return rest.find(context.config, context.auth, className, where, options) + if (Object.prototype.hasOwnProperty.call(args, 'limit')) { + options.limit = args.limit; + } + if (Object.prototype.hasOwnProperty.call(args, 'skip')) { + options.skip = args.skip; + } + return rest.find(context.config, context.auth, className, query, options) .then(toGraphQLResult(className)); } @@ -122,14 +135,7 @@ export class GraphQLParseSchema { }, resolve: async (root, args, context, info) => { // Get the selections - let query = {}; - if (args.where) { - query = Object.assign(query, args.where); - } - if (args.objectId) { - query = Object.assign(query, { objectId: args.objectId }); - } - const objects = await runFind(context, info, className, query); + const objects = await runFind(context, info, className, args); return { objects }; } }; From 30a1e65981f655703ab64910027f302b2648f634 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 23 Aug 2018 11:35:40 -0400 Subject: [PATCH 15/62] Ensure files don't explode --- src/graphql/types/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index ac4385e63d1..e5bd15051a6 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -96,7 +96,8 @@ export function inputType(fieldName, field) { } if (type == 'GeoPoint') { return GraphQLGeoPointInput; } if (type == 'File') { - return GraphQLFile; + // TODO: How to set a file in an object + // return GraphQLFile; } else if (type == 'ACL') { return GraphQLACLInput; } else if (type == 'Date') { @@ -120,7 +121,8 @@ export function queryType(fieldName, field) { } if (type == 'GeoPoint') { return GraphQLGeoPointInput; } if (type == 'File') { - return GraphQLFile; + // Cannot query on files + return; } else if (type == 'ACL') { // cannot query on ACL! return; From 63c20315f7ab0d7c6526b59768e1b874308dd0e5 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 23 Aug 2018 12:59:36 -0400 Subject: [PATCH 16/62] Adds support for geopoint queries --- src/graphql/ParseClass.js | 1 - src/graphql/types/GeoPoint.js | 97 +++++++++++++++++++++++++++- src/graphql/types/NumberQuery.js | 19 ++++-- src/graphql/types/QueryConstraint.js | 47 +++++++++----- src/graphql/types/StringQuery.js | 11 ++-- src/graphql/types/index.js | 5 +- 6 files changed, 148 insertions(+), 32 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 6f78f4a6724..15aa11620bc 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -13,7 +13,6 @@ import { queryType, inputType, type, - // GraphQLGeoPoint, GraphQLPointer, } from './types' diff --git a/src/graphql/types/GeoPoint.js b/src/graphql/types/GeoPoint.js index 150cf0a75ca..6a7058d9ae4 100644 --- a/src/graphql/types/GeoPoint.js +++ b/src/graphql/types/GeoPoint.js @@ -1,8 +1,11 @@ import { GraphQLScalarType, - GraphQLFloat + GraphQLFloat, + Kind, } from 'graphql' +import QueryConstraint from './QueryConstraint'; + export const GraphQLGeoPoint = new GraphQLScalarType({ name: 'GeoPoint', fields: { @@ -31,4 +34,96 @@ export const GraphQLGeoPoint = new GraphQLScalarType({ } }); +function parseValue(value) { + if (!value) { + return value; + } + if (value.kind === Kind.INT) { + return parseInt(value.value); + } + if (value.kind === Kind.FLOAT) { + return parseFloat(value.value); + } + if (value.kind === Kind.LIST) { + return value.values.map((field) => { + return parseValue(field); + }); + } + + if (value.kind !== Kind.OBJECT) { + return value; + } + return value.fields.reduce((memo, field) => { + memo[field.name.value] = parseValue(field.value); + return memo; + }, {}); +} + +export const GraphQLGeoPointQuery = new GraphQLScalarType({ + name: 'GeoPointQuery', + description: `Queries for number values + + Common Constraints: + + ${QueryConstraint.description()} + + Numeric constraints: + + - key: 1 + - key: {lt: 1} # less than + - key: {gt: 1} # greater than + - key: {lte: 1} # less than or equal + - key: {gte: 1} # greater than or equal + `, + serialize: () => { + throw "NumberQuery serialize not implemented" + }, + parseValue: () => { + throw "NumberQuery parseValue not implemented" + }, + parseLiteral: (ast) => { + if (ast.kind == Kind.OBJECT) { + const fields = ast.fields; + return fields.reduce((memo, field) => { + const operator = field.name.value; + const value = parseValue(field.value); + if (operator === 'near') { + memo['$nearSphere'] = { + type: '__GeoPoint', + latitude: value.latitude, + longitude: value.longitude, + }; + } + if (operator === 'maxDistanceInMiles' || operator === 'maxDistanceInKilometers' || operator === 'maxDistanceInRadians') { + memo[`$${operator}`] = value; + } + if (operator === 'within') { + memo['$within'] = { + $box: value.map((val) => ({ + type: '__GeoPoint', + latitude: val.latitude, + longitude: val.longitude, + })) + }; + } + if (operator === 'geoWithin') { + memo['$geoWithin'] = { + $polygon: value.map((val) => ({ + type: '__GeoPoint', + latitude: val.latitude, + longitude: val.longitude, + })) + }; + } + return memo; + }, QueryConstraint.parseFields(fields)); + } else if (ast.kind == Kind.INT || ast.kind == Kind.FLOAT) { + return parseFloat(ast.value); + } else { + throw 'Invalid literal for NumberQuery'; + } + } +}); + + export const GraphQLGeoPointInput = GraphQLGeoPoint; diff --git a/src/graphql/types/NumberQuery.js b/src/graphql/types/NumberQuery.js index 1efae0e35e9..917f6437e66 100644 --- a/src/graphql/types/NumberQuery.js +++ b/src/graphql/types/NumberQuery.js @@ -9,17 +9,19 @@ export const NumberQuery = new GraphQLScalarType({ name: 'NumberQuery', description: `Queries for number values - Common Constraints: + # Common Constraints: ${QueryConstraint.description()} - Numeric constraints: + # Numeric constraints: - - key: 1 - - key: {lt: 1} # less than - - key: {gt: 1} # greater than - - key: {lte: 1} # less than or equal - - key: {gte: 1} # greater than or equal + \`\`\` + { key: 1 } + { key: { lt: 1 } } # less than + { key: { gt: 1 } } # greater than + { key: { lte: 1 } } # less than or equal + { key: { gte: 1 } } # greater than or equal + \`\`\` `, serialize: () => { throw "NumberQuery serialize not implemented" @@ -33,6 +35,9 @@ export const NumberQuery = new GraphQLScalarType({ return fields.reduce((memo, field) => { const operator = field.name.value; const value = field.value.value; + if (field.value.kind != Kind.INT && field.value.kind != Kind.FLOAT) { + throw `${value} should be an int or a float`; + } if (['lt', 'gt', 'lte', 'gte'].includes(operator)) { memo['$' + operator] = parseFloat(value); } diff --git a/src/graphql/types/QueryConstraint.js b/src/graphql/types/QueryConstraint.js index cd1c9d99f46..f2cbf02752b 100644 --- a/src/graphql/types/QueryConstraint.js +++ b/src/graphql/types/QueryConstraint.js @@ -5,26 +5,41 @@ import { const supportedOperators = ['eq', 'ne', 'in', 'nin', 'exists', 'select', 'dontSelect'] function description() { - return `Equal To: - - key: "value" - - key: {eq: "value"} + return `## Equal To: + \`\`\` + { key: "value" } + { key: {eq: "value"} } + \`\`\` - Not Equal To - - key: {ne: "value"} + ## Not Equal To + \`\`\` + { key: {ne: "value"} } + \`\`\` - Contained in: - - key: {in: ["value1", "value2"]} - Not Contained in: - - key: {nin: ["value1", "value2"]} + ## Contained in: + \`\`\` + { key: {in: ["value1", "value2"]} } + \`\`\` + + ## Not Contained in: + \`\`\` + { key: { nin: ["value1", "value2"] } } + \`\`\` - Exists: - - key: {exists: true} + ## Exists: + \`\`\` + { key: {exists: true} } + \`\`\` - This matches a value for a key in the result of a different query - - key: {select: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} - - Requires that a key’s value not match a value for a key in the result of a different query - - key: {dontSelect: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} + ## Match results from another query + ### This matches a value for a key in the result of a different query + \`\`\` + { key: {select: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} } + \`\`\` + ### Requires that a key’s value not match a value for a key in the result of a different query + \`\`\` + { key: {dontSelect: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} } + \`\`\` `; } diff --git a/src/graphql/types/StringQuery.js b/src/graphql/types/StringQuery.js index 3d3ac0d929c..be205143d67 100644 --- a/src/graphql/types/StringQuery.js +++ b/src/graphql/types/StringQuery.js @@ -9,14 +9,15 @@ export const StringQuery = new GraphQLScalarType({ name: 'StringQuery', description: `Query constraint on string parameters - Common Constraints: + # Common Constraints: ${QueryConstraint.description()} - String constraints: - - - key: "value" - - key: {regex: "value"} + # String constraints: + \`\`\` + { key: "value" } + { key: {regex: "value"}} + \`\`\` `, serialize: () => { throw "StringQuery serialize not implemented" diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index e5bd15051a6..bf20ddd7ab4 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -13,7 +13,8 @@ import { import { GraphQLGeoPoint, - GraphQLGeoPointInput + GraphQLGeoPointInput, + GraphQLGeoPointQuery } from './GeoPoint'; import { @@ -119,7 +120,7 @@ export function queryType(fieldName, field) { } if (type == 'Boolean') { return GraphQLBoolean; } if (type == 'GeoPoint') { - return GraphQLGeoPointInput; + return GraphQLGeoPointQuery; } if (type == 'File') { // Cannot query on files return; From 40e169d999bb18e0728729a1e7a766869c5fb0b7 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 23 Aug 2018 13:30:59 -0400 Subject: [PATCH 17/62] Support for arrays --- src/graphql/types/JSONObject.js | 62 +++++++++++++++++++++++---------- src/graphql/types/index.js | 5 +++ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/graphql/types/JSONObject.js b/src/graphql/types/JSONObject.js index 764e00f9f22..2e864e1e254 100644 --- a/src/graphql/types/JSONObject.js +++ b/src/graphql/types/JSONObject.js @@ -1,24 +1,48 @@ import { - GraphQLScalarType + GraphQLScalarType, + Kind, } from 'graphql' + +function parseLiteral(ast, variables) { + switch (ast.kind) { + case Kind.STRING: + case Kind.BOOLEAN: + return ast.value; + case Kind.INT: + case Kind.FLOAT: + return parseFloat(ast.value); + case Kind.OBJECT: { + const value = Object.create(null); + ast.fields.forEach(field => { + value[field.name.value] = parseLiteral(field.value, variables); + }); + + return value; + } + case Kind.LIST: + return ast.values.map(n => parseLiteral(n, variables)); + case Kind.NULL: + return null; + case Kind.VARIABLE: { + const name = ast.name.value; + return variables ? variables[name] : undefined; + } + default: + return undefined; + } +} + +const id = (value) => value; + +// https://github.com/taion/graphql-type-json/blob/master/src/index.js // http://graphql.org/graphql-js/type/#graphqlscalartype export const GraphQLJSONObject = new GraphQLScalarType({ - name: 'JSONObject', - serialize: () => { - throw "JSONObject serialize not implemented" - }, - parseValue: () => { - throw "JSONObject parseValue not implemented" - }, - parseLiteral: (litteral) => { - return litteral.fields.reduce((memo, field) => { - const value = field.value; - if (value.kind == 'IntValue') { - memo[field.name.value] = parseInt(value.value, 10); - } else { - memo[field.name.value] = value.value; - } - return memo; - }, {}); - } + name: 'JSON', + description: + 'The `JSON` scalar type represents JSON values as specified by ' + + '[ECMA-404](http://www.ecma-international.org/' + + 'publications/files/ECMA-ST/ECMA-404.pdf).', + serialize: id, + parseValue: id, + parseLiteral }); diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index bf20ddd7ab4..08c12829268 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -4,6 +4,7 @@ import { GraphQLNonNull, GraphQLBoolean, GraphQLID, + GraphQLList, } from 'graphql' import { @@ -80,6 +81,8 @@ export function type(fieldName, field) { return GraphQLPointer; } else if (type == 'Object') { return GraphQLJSONObject; + } else if (type === 'Array') { + return new GraphQLList(GraphQLJSONObject); } } @@ -105,6 +108,8 @@ export function inputType(fieldName, field) { return GraphQLDate; } else if (type == 'Pointer') { return GraphQLPointerInput; + } else if (type === 'Array') { + return new GraphQLList(GraphQLJSONObject); } } From 03dbea3d7848b13707fd52f32b80cc9586834624 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 23 Aug 2018 13:36:24 -0400 Subject: [PATCH 18/62] Adds ability to enable/disasble graphqli --- src/Options/Definitions.js | 14 +++++++++++++ src/Options/docs.js | 3 +++ src/Options/index.js | 7 +++++++ src/ParseServer.js | 42 ++++++++++++++++++++------------------ 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index a599344240b..30df6bf7243 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -286,6 +286,20 @@ module.exports.ParseServerOptions = { "action": parsers.numberParser("port"), "default": 1337 }, + "enableGraphQL": { + "env": "PARSE_SERVER_ENABLE_GRAPHQL", + "help": "Set to true to enable the graphql endpoint", + "required": true, + "action": parsers.booleanParser, + "default": false + }, + "enableGraphQLI": { + "env": "PARSE_SERVER_ENABLE_GRAPHQLI", + "help": "Set to true to enable the graphqli interface\n this will also enable graphql", + "required": true, + "action": parsers.booleanParser, + "default": false + }, "host": { "env": "PARSE_SERVER_HOST", "help": "The host to serve ParseServer on, defaults to 0.0.0.0", diff --git a/src/Options/docs.js b/src/Options/docs.js index adcfe816499..31919233dfc 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -54,6 +54,9 @@ * @property {Boolean} enableExpressErrorHandler Enables the default express error handler for all errors * @property {Number} objectIdSize Sets the number of characters in generated object id's, default 10 * @property {Number} port The port to run the ParseServer, defaults to 1337. + * @property {Boolean} enableGraphQL Set to true to enable the graphql endpoint + * @property {Boolean} enableGraphQLI Set to true to enable the graphqli interface + this will also enable graphql * @property {String} host The host to serve ParseServer on, defaults to 0.0.0.0 * @property {String} mountPath Mount path for the server, defaults to /parse * @property {Number|Boolean} cluster Run with cluster, optionally set the number of processes default to os.cpus().length diff --git a/src/Options/index.js b/src/Options/index.js index 9b55399d2b6..2bc29c2c7c1 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -132,6 +132,13 @@ export interface ParseServerOptions { /* The port to run the ParseServer, defaults to 1337. :ENV: PORT */ port: ?number; // = 1337 + /* Set to true to enable the graphql endpoint + :ENV: PARSE_SERVER_ENABLE_GRAPHQL */ + enableGraphQL: boolean; // = false + /* Set to true to enable the graphqli interface + this will also enable graphql + :ENV: PARSE_SERVER_ENABLE_GRAPHQLI */ + enableGraphQLI: boolean; // = false /* The host to serve ParseServer on, defaults to 0.0.0.0 */ host: ?string; // = 0.0.0.0 /* Mount path for the server, defaults to /parse */ diff --git a/src/ParseServer.js b/src/ParseServer.js index ef56c426ef0..a77d8a9d030 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -230,26 +230,28 @@ class ParseServer { */ start(options: ParseServerOptions, callback: ?()=>void) { const app = express(); - app.use(options.mountPath + '/graphql', graphqlHTTP(async (req) => { - // TODO: use middleware please :) - req.config = req.config || Config.get(Parse.applicationId); - req.auth = req.auth || new auth.Auth({ config: req.config }); - // TODO: only await perhaps once, and optimize perf - const schema = await Config.get(Parse.applicationId).database.loadSchema(); - const allClasses = await schema.getAllClasses(true); - const fullSchema = allClasses.reduce((memo, classDef) => { - memo[classDef.className] = classDef; - return memo; - }, {}); - const Schema = new GraphQLParseSchema(fullSchema, 'test'); - const s = Schema.Schema(); - const root = Schema.Root(); - return { - schema: s, - rootValue: root, - graphiql: true - } - })); + if (options.enableGraphQL || options.enableGraphQLI) { + app.use(options.mountPath + '/graphql', graphqlHTTP(async (req) => { + // TODO: use middleware please :) + req.config = req.config || Config.get(Parse.applicationId); + req.auth = req.auth || new auth.Auth({ config: req.config }); + // TODO: only await perhaps once, and optimize perf + const schema = await Config.get(Parse.applicationId).database.loadSchema(); + const allClasses = await schema.getAllClasses(true); + const fullSchema = allClasses.reduce((memo, classDef) => { + memo[classDef.className] = classDef; + return memo; + }, {}); + const Schema = new GraphQLParseSchema(fullSchema, 'test'); + const s = Schema.Schema(); + const root = Schema.Root(); + return { + schema: s, + rootValue: root, + graphiql: options.enableGraphQLI + } + })); + } if (options.middleware) { let middleware; if (typeof options.middleware == 'string') { From 4cf0ce2577059287f93484b13e0ae1ac94e8ca90 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 23 Aug 2018 17:01:59 -0400 Subject: [PATCH 19/62] bumps graphql, refactor scalars into proper types --- package-lock.json | 8 +- package.json | 2 +- src/graphql/ParseClass.js | 12 +- src/graphql/Schema.js | 94 +++++++++++---- src/graphql/types/Date.js | 19 ++- src/graphql/types/GeoPoint.js | 171 ++++++++++----------------- src/graphql/types/NumberQuery.js | 70 ++++------- src/graphql/types/Pointer.js | 42 ++++--- src/graphql/types/QueryConstraint.js | 29 ++++- src/graphql/types/StringQuery.js | 45 ++----- src/graphql/types/index.js | 17 +-- 11 files changed, 244 insertions(+), 265 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0eea06a3fa0..90a971278d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5299,11 +5299,11 @@ "dev": true }, "graphql": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.9.6.tgz", - "integrity": "sha1-UUQh6dIlwp38j9MFRZq65YgV7yw=", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.13.2.tgz", + "integrity": "sha512-QZ5BL8ZO/B20VA8APauGBg3GyEgZ19eduvpLWoq5x7gMmWnHoy8rlQWPLmWgFvo1yNgjSEFMesmS4R6pPr7xog==", "requires": { - "iterall": "^1.0.0" + "iterall": "^1.2.1" } }, "har-schema": { diff --git a/package.json b/package.json index 773a7830401..0c4ea9eafa8 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "deepcopy": "1.0.0", "express": "4.16.2", "express-graphql": "^0.6.12", - "graphql": "^0.9.1", + "graphql": "^0.13.2", "intersect": "1.0.1", "lodash": "4.17.5", "lru-cache": "4.1.3", diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 15aa11620bc..5cfdd25be27 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -115,6 +115,7 @@ export class ParseClass { interfaces: [ParseObjectInterface], fields: this.buildFields(graphQLField, false, false, true), resolve: () => { + console.log('RESOLVE'); return; }, isTypeOf: function(a) { @@ -158,7 +159,6 @@ export class ParseClass { fields: () => { return this.buildFields(graphQLInputField, true); }, - resolve: this.get.bind(this), isTypeOf: function(input) { return input.className == className; } @@ -175,7 +175,6 @@ export class ParseClass { delete fields.objectId; return fields; }, - resolve: this.get.bind(this), isTypeOf: function(input) { return input.className == className; } @@ -190,7 +189,6 @@ export class ParseClass { fields: () => { return this.buildFields(graphQLInputField, true); }, - resolve: this.get.bind(this), isTypeOf: function(input) { return input.className == className; } @@ -231,13 +229,5 @@ export class ParseClass { graphQLObjectType() { return new GraphQLObjectType(this.graphQLConfig()); } - - get(a,b,c) { - /*eslint-disable*/ - console.log('ParseClass resolve...'); - console.log(a,b,c); - /* eslint-enable */ - return null; - } } diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index ab8623b00d2..4a59b9bc02a 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -9,6 +9,8 @@ import { GraphQLNonNull, GraphQLInt, GraphQLID, + GraphQLObjectTypeConfig, + GraphQLFieldConfigMap, } from 'graphql' import rest from '../rest'; @@ -17,7 +19,7 @@ import rest from '../rest'; function reduceSelections(selections, topKey) { return selections.reduce((memo, selection) => { const value = selection.name.value; - if (selection.selectionSet === null) { + if (selection.selectionSet === null || selection.selectionSet === undefined) { memo.push(value); } else { // Get the sub seletions and add on current key @@ -66,8 +68,49 @@ function toGraphQLResult(className, singleResult) { } } +function transform(constraintKey, currentValue) { + let value = currentValue; + if (constraintKey === 'nearSphere') { + value = { + latitude: currentValue.point.latitude, + longitude: currentValue.point.longitude, + } + } + const key = `$${constraintKey}`; + + return { + key, + value, + } +} + +function transformQuery(query) { + Object.keys(query).forEach((key) => { + Object.keys(query[key]).forEach((constraintKey) => { + const constraint = query[key][constraintKey]; + delete query[key][constraintKey]; + const result = transform(constraintKey, constraint); + query[key][result.key] = result.value; + }); + }); + return query; +} + +function transformInput(input, schema) { + const { fields } = schema; + Object.keys(input).forEach((key) => { + const value = input[key]; + if (fields[key] && fields[key].type === 'Pointer') { + value.__type = 'Pointer'; + } else if (fields[key] && fields[key].type === 'GeoPoint') { + value.__type = 'GeoPoint'; + } + }); + return input; +} + // Runs a find against the rest API -function runFind(context, info, className, args) { +function runFind(context, info, className, args, schema) { let query = {}; if (args.where) { query = Object.assign(query, args.where); @@ -82,6 +125,7 @@ function runFind(context, info, className, args) { if (Object.prototype.hasOwnProperty.call(args, 'skip')) { options.skip = args.skip; } + query = transformQuery(query, schema); return rest.find(context.config, context.auth, className, query, options) .then(toGraphQLResult(className)); } @@ -114,17 +158,13 @@ export class GraphQLParseSchema { } Query() { - const MainSchemaOptions = { - name: 'Query', - description: `The full parse schema`, - fields: {} - } + const fields = {}; Object.keys(this.schema).forEach((className) => { const { queryType, queryResultType } = loadClass(className, this.schema); - MainSchemaOptions.fields[className] = { + const field: GraphQLFieldConfigMap = { type: queryResultType, description: `Use this endpoint to get or query ${className} objects`, args: { @@ -135,52 +175,52 @@ export class GraphQLParseSchema { }, resolve: async (root, args, context, info) => { // Get the selections - const objects = await runFind(context, info, className, args); + const objects = await runFind(context, info, className, args, this.schema[className]); return { objects }; } }; + fields[className] = field; + }); + return new GraphQLObjectType({ + name: 'Query', + description: `The full parse schema`, + fields, }); - return new GraphQLObjectType(MainSchemaOptions); } Mutation() { - const MainSchemaMutationOptions = { - name: 'Mutation', - fields: {} - } // TODO: Refactor FunctionRouter to extract (as it's a new entry) // TODO: Implement Functions as mutations - + const fields = {}; Object.keys(this.schema).forEach((className) => { const { inputType, objectType, updateType, mutationResultType } = loadClass(className, this.schema); - MainSchemaMutationOptions.fields['create' + className] = { + fields[`create${className}`] = { type: mutationResultType, fields: objectType.fields, description: `use this method to create a new ${className}`, args: { input: { type: inputType }}, - name: 'create', resolve: async (root, args, context, info) => { - const res = await rest.create(context.config, context.auth, className, args.input); + const input = transformInput(args.input, this.schema[className]); + const res = await rest.create(context.config, context.auth, className, input); // Run get to match graphQL style const object = await runGet(context, info, className, res.response.objectId); return { object }; } } - MainSchemaMutationOptions.fields['update' + className] = { + fields[`update${className}`] = { type: mutationResultType, description: `use this method to update an existing ${className}`, args: { - objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' }, + objectId: { type: new GraphQLNonNull(GraphQLID) }, input: { type: updateType } }, - name: 'update', resolve: async (root, args, context, info) => { const objectId = args.objectId; - const input = args.input; + const input = transformInput(args.input, this.schema[className]); await rest.update(context.config, context.auth, className, { objectId }, input); // Run get to match graphQL style const object = await runGet(context, info, className, objectId); @@ -188,13 +228,12 @@ export class GraphQLParseSchema { } } - MainSchemaMutationOptions.fields['destroy' + className] = { + fields[`destroy${className}`] = { type: mutationResultType, description: `use this method to update delete an existing ${className}`, args: { - objectId: { type: new GraphQLNonNull(GraphQLID), name: 'objectId' } + objectId: { type: new GraphQLNonNull(GraphQLID) } }, - name: 'destroy', resolve: async (root, args, context, info) => { const object = await runGet(context, info, className, args.objectId); await rest.del(context.config, context.auth, className, args.objectId); @@ -202,7 +241,10 @@ export class GraphQLParseSchema { } } }); - return new GraphQLObjectType(MainSchemaMutationOptions); + return new GraphQLObjectType({ + name: 'Mutation', + fields + }); } Root() { diff --git a/src/graphql/types/Date.js b/src/graphql/types/Date.js index 598d9a79383..b21b94a37e7 100644 --- a/src/graphql/types/Date.js +++ b/src/graphql/types/Date.js @@ -1,12 +1,16 @@ import { - GraphQLScalarType + GraphQLScalarType, + Kind } from 'graphql' -// http://graphql.org/graphql-js/type/#graphqlscalartype +import { + ComparableQuery +} from './NumberQuery'; + export const GraphQLDate = new GraphQLScalarType({ name: 'Date', serialize: (obj) => { - if (typeof a === 'string') { + if (typeof obj === 'string') { return new Date(obj); } return obj; @@ -14,7 +18,12 @@ export const GraphQLDate = new GraphQLScalarType({ parseValue: () => { throw "Date parseValue not implemented" }, - parseLiteral: () => { - throw "Date parseLiteral not implemented" + parseLiteral: (node) => { + if (node.kind === Kind.STRING) { + return new Date(node.value); + } + throw `Cannot parse date of type ${node.kind}`; } }); + +export const DateQuery = ComparableQuery('DateQuery', GraphQLDate); diff --git a/src/graphql/types/GeoPoint.js b/src/graphql/types/GeoPoint.js index 6a7058d9ae4..ef2a94e18a4 100644 --- a/src/graphql/types/GeoPoint.js +++ b/src/graphql/types/GeoPoint.js @@ -1,129 +1,82 @@ import { - GraphQLScalarType, GraphQLFloat, - Kind, + GraphQLObjectType, + GraphQLInputObjectType, + GraphQLNonNull, + GraphQLList, } from 'graphql' -import QueryConstraint from './QueryConstraint'; +import { BaseQuery } from './QueryConstraint'; +const geoPointFields = { + latitude: { + type: GraphQLFloat, + description: 'laititude of the point, in degrees' + }, + longitude: { + type: GraphQLFloat, + description: 'latitude of the point, in degrees' + } +}; -export const GraphQLGeoPoint = new GraphQLScalarType({ +export const GraphQLGeoPoint = new GraphQLObjectType({ name: 'GeoPoint', + fields: geoPointFields +}); + +export const GeoPoint = new GraphQLInputObjectType({ + name: 'GeoPointInput', + fields: geoPointFields +}); + +export const NearQueryType = new GraphQLInputObjectType({ + name: 'NearQuery', fields: { - latitude: { - type: GraphQLFloat, - name: 'latitude', - description: 'laititude of the point, in degrees' + point: { + type: new GraphQLNonNull(GeoPoint), }, - longitude: { - type: GraphQLFloat, - name: 'latitude', - description: 'latitude of the point, in degrees' - } - }, - serialize: (object) => { - return { - latitude: object.latitude, - longitude: object.longitude, + maxDistanceInMiles: { + type: GraphQLFloat + }, + maxDistanceInKilometers: { + type: GraphQLFloat + }, + maxDistanceInRadians: { + type: GraphQLFloat } - }, - parseValue: () => { - throw "not implemented" - }, - parseLiteral: () => { - throw "not implemented" } }); -function parseValue(value) { - if (!value) { - return value; - } - if (value.kind === Kind.INT) { - return parseInt(value.value); - } - if (value.kind === Kind.FLOAT) { - return parseFloat(value.value); - } - if (value.kind === Kind.LIST) { - return value.values.map((field) => { - return parseValue(field); - }); +export const WithinQueryType = new GraphQLInputObjectType({ + name: 'WithinQuery', + fields: { + box: { + type: new GraphQLList(GeoPoint), + }, } +}); - if (value.kind !== Kind.OBJECT) { - return value; +export const GeoWithinQueryType = new GraphQLInputObjectType({ + name: 'GeoWithinQuery', + fields: { + polygon: { + type: new GraphQLList(GeoPoint), + }, } - return value.fields.reduce((memo, field) => { - memo[field.name.value] = parseValue(field.value); - return memo; - }, {}); -} - -export const GraphQLGeoPointQuery = new GraphQLScalarType({ - name: 'GeoPointQuery', - description: `Queries for number values - - Common Constraints: - - ${QueryConstraint.description()} - - Numeric constraints: +}); - - key: 1 - - key: {lt: 1} # less than - - key: {gt: 1} # greater than - - key: {lte: 1} # less than or equal - - key: {gte: 1} # greater than or equal - `, - serialize: () => { - throw "NumberQuery serialize not implemented" - }, - parseValue: () => { - throw "NumberQuery parseValue not implemented" - }, - parseLiteral: (ast) => { - if (ast.kind == Kind.OBJECT) { - const fields = ast.fields; - return fields.reduce((memo, field) => { - const operator = field.name.value; - const value = parseValue(field.value); - if (operator === 'near') { - memo['$nearSphere'] = { - type: '__GeoPoint', - latitude: value.latitude, - longitude: value.longitude, - }; - } - if (operator === 'maxDistanceInMiles' || operator === 'maxDistanceInKilometers' || operator === 'maxDistanceInRadians') { - memo[`$${operator}`] = value; - } - if (operator === 'within') { - memo['$within'] = { - $box: value.map((val) => ({ - type: '__GeoPoint', - latitude: val.latitude, - longitude: val.longitude, - })) - }; - } - if (operator === 'geoWithin') { - memo['$geoWithin'] = { - $polygon: value.map((val) => ({ - type: '__GeoPoint', - latitude: val.latitude, - longitude: val.longitude, - })) - }; - } - return memo; - }, QueryConstraint.parseFields(fields)); - } else if (ast.kind == Kind.INT || ast.kind == Kind.FLOAT) { - return parseFloat(ast.value); - } else { - throw 'Invalid literal for NumberQuery'; +export const GraphQLGeoPointQuery = new GraphQLInputObjectType({ + name: "GeoQuery", + fields: Object.assign({}, BaseQuery(GeoPoint), { + nearSphere: { + type: NearQueryType + }, + within: { + type: WithinQueryType + }, + geoWithin: { + type: GeoWithinQueryType, } - } + }) }); - export const GraphQLGeoPointInput = GraphQLGeoPoint; diff --git a/src/graphql/types/NumberQuery.js b/src/graphql/types/NumberQuery.js index 917f6437e66..c4ab82e029d 100644 --- a/src/graphql/types/NumberQuery.js +++ b/src/graphql/types/NumberQuery.js @@ -1,52 +1,28 @@ import { - GraphQLScalarType, - Kind + GraphQLInputObjectType, + GraphQLFloat, } from 'graphql' -import QueryConstraint from './QueryConstraint'; +import { BaseQuery } from './QueryConstraint'; -export const NumberQuery = new GraphQLScalarType({ - name: 'NumberQuery', - description: `Queries for number values +export const ComparableQuery = (name, type) => { + return new GraphQLInputObjectType({ + name: name, + fields: Object.assign({}, BaseQuery(type), { + lt: { + type: type + }, + gt: { + type: type + }, + lte: { + type: type + }, + gte: { + type: type + } + }), + }); +}; - # Common Constraints: - - ${QueryConstraint.description()} - - # Numeric constraints: - - \`\`\` - { key: 1 } - { key: { lt: 1 } } # less than - { key: { gt: 1 } } # greater than - { key: { lte: 1 } } # less than or equal - { key: { gte: 1 } } # greater than or equal - \`\`\` - `, - serialize: () => { - throw "NumberQuery serialize not implemented" - }, - parseValue: () => { - throw "NumberQuery parseValue not implemented" - }, - parseLiteral: (ast) => { - if (ast.kind == Kind.OBJECT) { - const fields = ast.fields; - return fields.reduce((memo, field) => { - const operator = field.name.value; - const value = field.value.value; - if (field.value.kind != Kind.INT && field.value.kind != Kind.FLOAT) { - throw `${value} should be an int or a float`; - } - if (['lt', 'gt', 'lte', 'gte'].includes(operator)) { - memo['$' + operator] = parseFloat(value); - } - return memo; - }, QueryConstraint.parseFields(fields)); - } else if (ast.kind == Kind.INT || ast.kind == Kind.FLOAT) { - return parseFloat(ast.value); - } else { - throw 'Invalid literal for NumberQuery'; - } - } -}); +export const NumberQuery = ComparableQuery('NumberQuery', GraphQLFloat); diff --git a/src/graphql/types/Pointer.js b/src/graphql/types/Pointer.js index 9a6219262fc..f879a20a18b 100644 --- a/src/graphql/types/Pointer.js +++ b/src/graphql/types/Pointer.js @@ -1,34 +1,42 @@ import { - //GraphQLObjectType, - //GraphQLInputObjectType, - GraphQLScalarType, + GraphQLInputObjectType, GraphQLID, GraphQLString, + GraphQLNonNull, } from 'graphql' -export const GraphQLPointer = new GraphQLScalarType({ +export const GraphQLPointer = new GraphQLInputObjectType({ name: 'Pointer', fields: { objectId: { - type: GraphQLID, - name: 'objectId', + type: new GraphQLNonNull(GraphQLID), description: 'pointer\'s objectId' }, className: { type: GraphQLString, - name: 'className', description: 'pointer\'s className' } - }, - serialize: () => { - throw "serialize not implemented" - }, - parseValue: () => { - throw "parseValue not implemented" - }, - parseLiteral: (litteral) => { - return { objectId: litteral.value }; } }); -export const GraphQLPointerInput = GraphQLPointer; +const cache = {}; + +export const GraphQLPointerInput = (field) => { + if (!cache[field.targetClass]) { + cache[field.targetClass] = new GraphQLInputObjectType({ + name: `${field.targetClass}PointerInput`, + fields: { + objectId: { + type: new GraphQLNonNull(GraphQLID), + description: 'pointer\'s objectId' + }, + className: { + type: GraphQLString, + description: 'pointer\'s className', + defaultValue: field.targetClass + } + } + }); + } + return cache[field.targetClass]; +}; diff --git a/src/graphql/types/QueryConstraint.js b/src/graphql/types/QueryConstraint.js index f2cbf02752b..bad30308809 100644 --- a/src/graphql/types/QueryConstraint.js +++ b/src/graphql/types/QueryConstraint.js @@ -1,5 +1,8 @@ import { - Kind + Kind, + GraphQLInputType, + GraphQLList, + GraphQLBoolean } from 'graphql' const supportedOperators = ['eq', 'ne', 'in', 'nin', 'exists', 'select', 'dontSelect'] @@ -64,8 +67,32 @@ const parseLiteral = (ast) => { } }; +export const BaseQuery = (type) => { + return { + eq: { + type, + description: 'Test for equality', + }, + neq: { + type, + description: 'Test for non equality', + }, + in: { + type: new GraphQLList(type), + description: 'Test that the object is contained in', + }, + nin: { + type: new GraphQLList(type), + }, + exists: { + type: GraphQLBoolean, + } + }; +} + export default { description, parseLiteral, parseFields, + BaseQuery, } diff --git a/src/graphql/types/StringQuery.js b/src/graphql/types/StringQuery.js index be205143d67..4fac60b493c 100644 --- a/src/graphql/types/StringQuery.js +++ b/src/graphql/types/StringQuery.js @@ -1,44 +1,15 @@ import { - GraphQLScalarType, - Kind + GraphQLInputObjectType, + GraphQLString, } from 'graphql' -import QueryConstraint from './QueryConstraint'; +import { BaseQuery } from './QueryConstraint'; -export const StringQuery = new GraphQLScalarType({ +export const StringQuery = new GraphQLInputObjectType({ name: 'StringQuery', - description: `Query constraint on string parameters - - # Common Constraints: - - ${QueryConstraint.description()} - - # String constraints: - \`\`\` - { key: "value" } - { key: {regex: "value"}} - \`\`\` - `, - serialize: () => { - throw "StringQuery serialize not implemented" - }, - parseValue: () => { - throw "StringQuery parseValue not implemented" - }, - parseLiteral: (ast) => { - if (ast.kind == Kind.OBJECT) { - const fields = ast.fields; - return fields.reduce((memo, field) => { - const operator = field.name.value; - if (operator === 'regex') { - memo['$' + operator] = field.value.value; - } - return memo; - }, QueryConstraint.parseFields(fields)); - } else if (ast.kind == Kind.STRING) { - return ast.value; - } else { - throw 'Invalid literal for StringQuery'; + fields: Object.assign({}, BaseQuery(GraphQLString), { + regex: { + type: GraphQLString } - } + }), }); diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index 08c12829268..e35704eb2f9 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -5,6 +5,7 @@ import { GraphQLBoolean, GraphQLID, GraphQLList, + GraphQLUnionType, } from 'graphql' import { @@ -15,7 +16,8 @@ import { import { GraphQLGeoPoint, GraphQLGeoPointInput, - GraphQLGeoPointQuery + GraphQLGeoPointQuery, + GeoPoint, } from './GeoPoint'; import { @@ -23,7 +25,8 @@ import { } from './File'; import { - GraphQLDate + GraphQLDate, + DateQuery, } from './Date'; import { @@ -98,16 +101,16 @@ export function inputType(fieldName, field) { } if (type == 'Boolean') { return GraphQLBoolean; } if (type == 'GeoPoint') { - return GraphQLGeoPointInput; + return GeoPoint; } if (type == 'File') { // TODO: How to set a file in an object // return GraphQLFile; } else if (type == 'ACL') { return GraphQLACLInput; } else if (type == 'Date') { - return GraphQLDate; + return DateQuery; } else if (type == 'Pointer') { - return GraphQLPointerInput; + return GraphQLPointerInput(field); } else if (type === 'Array') { return new GraphQLList(GraphQLJSONObject); } @@ -133,8 +136,8 @@ export function queryType(fieldName, field) { // cannot query on ACL! return; } else if (type == 'Date') { - return GraphQLDate; + return DateQuery; } else if (type == 'Pointer') { - return GraphQLPointerInput; + return GraphQLPointerInput(field); } } From f045115f70f425f199a36465f41071584fda58f1 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 23 Aug 2018 22:34:16 -0400 Subject: [PATCH 20/62] Move to relay style Connection for list based responses --- src/graphql/ParseClass.js | 20 +++++---- src/graphql/Schema.js | 87 +++++++++++++++++++++++++++++++++------ 2 files changed, 87 insertions(+), 20 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 5cfdd25be27..b4f5070ef8d 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -114,13 +114,9 @@ export class ParseClass { description: `Parse Class ${className}`, interfaces: [ParseObjectInterface], fields: this.buildFields(graphQLField, false, false, true), - resolve: () => { - console.log('RESOLVE'); - return; - }, - isTypeOf: function(a) { + isTypeOf: (a) => { return a.className == className; - } + }, }; } @@ -209,9 +205,17 @@ export class ParseClass { graphQLQueryResultType(objectType) { return new GraphQLObjectType({ - name: `${this.className}QueryResponse`, + name: `${this.className}QueryConnection`, fields: { - objects: { type: new GraphQLList(objectType) }, + nodes: { type: new GraphQLList(objectType) }, + edges: { + type: new GraphQLList(new GraphQLObjectType({ + name: `${this.className}Edge`, + fields: () => ({ + node: { type: objectType } + }) + })) + } } }); } diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 4a59b9bc02a..3d0eea50fd3 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -9,7 +9,6 @@ import { GraphQLNonNull, GraphQLInt, GraphQLID, - GraphQLObjectTypeConfig, GraphQLFieldConfigMap, } from 'graphql' @@ -37,14 +36,57 @@ function getSelections(node, topKey) { return reduceSelections(node.selectionSet.selections, topKey); } -function getQueryOptions(info, listKey) { +function getFirstNode(node, matching) { + if (!node || !node.selectionSet || !node.selectionSet.selections) { + return; + } + let found; + for(const child of node.selectionSet.selections) { + found = matching(child, node); + getFirstNode(child, matching); + } + if (found) { + return; + } +} + +function flattenSelectionSet(nodes) { + const allSelections = nodes.map(getSelections).reduce((memo, keys) => { + return memo.concat(keys); + }, []); + return [...new Set(allSelections)].join(','); +} + +function getKeysForFind(info/*, schema, className*/) { + const node = info.fieldNodes[0]; + const nodes = []; + getFirstNode(node, (child, node) => { + if (child.name.value === 'nodes') { + nodes.push(child); + return true; + } + if (child.name.value === 'node' && node.name.value === 'edges') { + nodes.push(child); + return true; + } + }); + const keys = flattenSelectionSet(nodes); + return { + keys, + include: keys + } +} + +function getQueryOptions(info, parentNodeName) { const node = info.fieldNodes[0]; const selections = node.selectionSet.selections; - const results = selections.filter((selection) => { - return selection.name.value == listKey; + let nodes = selections.filter((selection) => { + return selection.name.value == parentNodeName; }); - const selectedKeys = getSelections(results[0]); - const keys = selectedKeys.join(','); + if (nodes.length == 0) { + nodes = [node]; + } + const keys = flattenSelectionSet(nodes); return { keys, include: keys @@ -61,6 +103,9 @@ function injectClassName(className, result) { function toGraphQLResult(className, singleResult) { return (restResult) => { const results = restResult.results; + if (results.length == 0) { + return; + } if (singleResult) { return injectClassName(className, results[0]); } @@ -118,7 +163,7 @@ function runFind(context, info, className, args, schema) { if (args.objectId) { query = Object.assign(query, { objectId: args.objectId }); } - const options = getQueryOptions(info, 'objects'); + const options = getKeysForFind(info, schema, className); if (Object.prototype.hasOwnProperty.call(args, 'limit')) { options.limit = args.limit; } @@ -161,10 +206,23 @@ export class GraphQLParseSchema { const fields = {}; Object.keys(this.schema).forEach((className) => { const { - queryType, queryResultType + queryType, queryResultType, objectType } = loadClass(className, this.schema); - const field: GraphQLFieldConfigMap = { + const get: GraphQLFieldConfigMap = { + type: objectType, + description: `Use this endpoint to get or query ${className} objects`, + args: { + objectId: { type: GraphQLID, name: 'objectId' }, + }, + resolve: async (root, args, context, info) => { + // Get the selections + return await runGet(context, info, className, args.objectId, this.schema); + } + }; + fields[`${className}`] = get; + + const findField: GraphQLFieldConfigMap = { type: queryResultType, description: `Use this endpoint to get or query ${className} objects`, args: { @@ -175,11 +233,16 @@ export class GraphQLParseSchema { }, resolve: async (root, args, context, info) => { // Get the selections - const objects = await runFind(context, info, className, args, this.schema[className]); - return { objects }; + const results = await runFind(context, info, className, args, this.schema); + return { + nodes: () => results, + edges: () => results.map((node) => { + return { node }; + }), + }; } }; - fields[className] = field; + fields[`find${className}`] = findField; }); return new GraphQLObjectType({ name: 'Query', From 8e080554ab655d318e7fffeb8b4f6e835e0dcd98 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 08:22:44 -0400 Subject: [PATCH 21/62] use id instead of objectId, use Node --- src/graphql/ParseClass.js | 18 ++++++++++++++---- src/graphql/Schema.js | 25 +++++++++++++++++-------- src/graphql/types/index.js | 6 +++--- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index b4f5070ef8d..c1c76bce24a 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -78,12 +78,18 @@ export function clearCache() { const reservedFieldNames = ['objectId', 'createdAt', 'updatedAt']; +export const Node = new GraphQLInterfaceType({ + name: 'Node', + fields: { + id: { + type: type('objectId') + } + } +}) + export const ParseObjectInterface = new GraphQLInterfaceType({ name: 'ParseObject', fields: { - objectId: { - type: type('objectId') - }, createdAt: { type: type(null, {type: 'Date'}) }, @@ -112,7 +118,7 @@ export class ParseClass { return { name: this.className, description: `Parse Class ${className}`, - interfaces: [ParseObjectInterface], + interfaces: [Node, ParseObjectInterface], fields: this.buildFields(graphQLField, false, false, true), isTypeOf: (a) => { return a.className == className; @@ -142,6 +148,10 @@ export class ParseClass { if (!gQLField) { return memo; } + // use id instead of objectId in the object + if (fieldName === 'objectId' && isObject) { + fieldName = 'id'; + } memo[fieldName] = gQLField; return memo; }, {}); diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 3d0eea50fd3..6e800a4fa81 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -93,23 +93,32 @@ function getQueryOptions(info, parentNodeName) { } } -function injectClassName(className, result) { +function transformResult(className, result, schema) { if (Array.isArray(result)) { - return result.map((res) => injectClassName(className, res)); + return result.map((res) => transformResult(className, res, schema)); } + const { fields } = schema[className]; + if (result.objectId) { + result.id = result.objectId; + } + Object.keys(result).forEach((key) => { + if (fields[key] && fields[key].type === 'Pointer') { + result[key] = transformResult(fields[key].targetClass, result[key], schema); + } + }); return Object.assign({className}, result); } -function toGraphQLResult(className, singleResult) { +function toGraphQLResult(className, singleResult, schema) { return (restResult) => { const results = restResult.results; if (results.length == 0) { return; } if (singleResult) { - return injectClassName(className, results[0]); + return transformResult(className, results[0], schema); } - return injectClassName(className, results); + return transformResult(className, results, schema); } } @@ -172,14 +181,14 @@ function runFind(context, info, className, args, schema) { } query = transformQuery(query, schema); return rest.find(context.config, context.auth, className, query, options) - .then(toGraphQLResult(className)); + .then(toGraphQLResult(className, false, schema)); } // runs a get against the rest API -function runGet(context, info, className, objectId) { +function runGet(context, info, className, objectId, schema) { const options = getQueryOptions(info, 'object'); return rest.get(context.config, context.auth, className, objectId, options) - .then(toGraphQLResult(className, true)); + .then(toGraphQLResult(className, true, schema)); } export class GraphQLParseSchema { diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index e35704eb2f9..d833a9dce6a 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -62,7 +62,7 @@ export { } export function type(fieldName, field) { - if (fieldName === 'objectId') { + if (fieldName === 'objectId' || fieldName === 'id') { return new GraphQLNonNull(GraphQLID); } const type = field.type; @@ -90,7 +90,7 @@ export function type(fieldName, field) { } export function inputType(fieldName, field) { - if (fieldName === 'objectId') { + if (fieldName === 'objectId' || fieldName === 'id') { return new GraphQLNonNull(GraphQLID); } const type = field.type; @@ -117,7 +117,7 @@ export function inputType(fieldName, field) { } export function queryType(fieldName, field) { - if (fieldName === 'objectId') { + if (fieldName === 'objectId' || fieldName === 'id') { return new GraphQLNonNull(GraphQLID); } const type = field.type; From b868609350a7082c7f46c8098c8271c94b2ff96b Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 10:20:08 -0400 Subject: [PATCH 22/62] Adds support for relations --- src/graphql/ParseClass.js | 163 +++++++++++++++---------- src/graphql/Schema.js | 165 +------------------------ src/graphql/execute.js | 176 +++++++++++++++++++++++++++ src/graphql/types/QueryConstraint.js | 1 - src/graphql/types/index.js | 1 - src/graphql/typesCache.js | 13 ++ 6 files changed, 292 insertions(+), 227 deletions(-) create mode 100644 src/graphql/execute.js create mode 100644 src/graphql/typesCache.js diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index c1c76bce24a..325e37b058f 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -1,12 +1,10 @@ +import { runFind } from './execute'; + import { GraphQLInterfaceType, GraphQLObjectType, GraphQLInputObjectType, GraphQLList, - // GraphQLString, - // GraphQLNonNull, - // GraphQLBoolean, - // GraphQLID, } from 'graphql' import { @@ -16,6 +14,13 @@ import { GraphQLPointer, } from './types' +import { + getOrElse, + clearCache, +} from './typesCache'; + +export { clearCache }; + function graphQLField(fieldName, field) { const gQLType = type(fieldName, field); if (!gQLType) { @@ -56,24 +61,15 @@ function graphQLQueryField(fieldName, field) { }; } -let ParseClassCache = {}; - export function loadClass(className, schema) { - if (!ParseClassCache[className]) { - const c = new ParseClass(className, schema); - const objectType = c.graphQLObjectType(); - const inputType = c.graphQLInputObjectType(); - const updateType = c.graphQLUpdateInputObjectType(); - const queryType = c.graphQLQueryInputObjectType(); - const queryResultType = c.graphQLQueryResultType(objectType); - const mutationResultType = c.graphQLMutationResultType(objectType); - ParseClassCache[className] = { objectType, inputType, updateType, queryType, queryResultType, mutationResultType, class: c } - } - return ParseClassCache[className]; -} - -export function clearCache() { - ParseClassCache = {}; + const c = getOrElse(className, () => new ParseClass(className, schema)); + const objectType = c.graphQLObjectType(); + const inputType = c.graphQLInputObjectType(); + const updateType = c.graphQLUpdateInputObjectType(); + const queryType = c.graphQLQueryInputObjectType(); + const queryResultType = c.graphQLQueryResultType(objectType); + const mutationResultType = c.graphQLMutationResultType(objectType); + return { objectType, inputType, updateType, queryType, queryResultType, mutationResultType, class: c } } const reservedFieldNames = ['objectId', 'createdAt', 'updatedAt']; @@ -113,19 +109,6 @@ export class ParseClass { this.class = this.schema[className]; } - graphQLConfig() { - const className = this.className; - return { - name: this.className, - description: `Parse Class ${className}`, - interfaces: [Node, ParseObjectInterface], - fields: this.buildFields(graphQLField, false, false, true), - isTypeOf: (a) => { - return a.className == className; - }, - }; - } - buildFields(mapper, filterReserved = false, isQuery = false, isObject = false) { const fields = this.class.fields; return Object.keys(fields).reduce((memo, fieldName) => { @@ -145,6 +128,32 @@ export class ParseClass { } } } + if (field.type == 'Relation' && isObject) { + gQLField = { + type: loadClass(field.targetClass, this.schema).queryResultType, + resolve: async (parent, args, context, info) => { + const query = { + $relatedTo: { + object: { + __type: 'Pointer', + className: parent.className, + objectId: parent.objectId + }, + key: fieldName, + } + } + args.redirectClassNameForKey = fieldName; + const results = await runFind(context, info, this.className, args, this.schema, query); + return { + nodes: () => results, + edges: () => results.map((node) => { + return { node }; + }), + }; + } + } + } + if (!gQLField) { return memo; } @@ -156,6 +165,18 @@ export class ParseClass { return memo; }, {}); } + graphQLConfig() { + const className = this.className; + return { + name: this.className, + description: `Parse Class ${className}`, + interfaces: [Node, ParseObjectInterface], + fields: () => this.buildFields(graphQLField, false, false, true), + isTypeOf: (a) => { + return a.className == className; + }, + }; + } graphQLInputConfig() { const className = this.className; @@ -202,46 +223,66 @@ export class ParseClass { } graphQLUpdateInputObjectType() { - return new GraphQLInputObjectType(this.graphQLUpdateInputConfig()); + if (!this.updateInputObjectType) { + this.updateInputObjectType = new GraphQLInputObjectType(this.graphQLUpdateInputConfig()); + } + return this.updateInputObjectType; } graphQLInputObjectType() { - return new GraphQLInputObjectType(this.graphQLInputConfig()); + if (!this.inputObjectType) { + this.inputObjectType = new GraphQLInputObjectType(this.graphQLInputConfig()); + } + return this.inputObjectType; } graphQLQueryInputObjectType() { - return new GraphQLInputObjectType(this.graphQLQueryConfig()); - } - - graphQLQueryResultType(objectType) { - return new GraphQLObjectType({ - name: `${this.className}QueryConnection`, - fields: { - nodes: { type: new GraphQLList(objectType) }, - edges: { - type: new GraphQLList(new GraphQLObjectType({ - name: `${this.className}Edge`, - fields: () => ({ - node: { type: objectType } - }) - })) + if (!this.queryInputObjectType) { + this.queryInputObjectType = new GraphQLInputObjectType(this.graphQLQueryConfig()); + } + return this.queryInputObjectType; + } + + graphQLQueryResultType() { + if (!this.queryResultObjectType) { + const objectType = this.graphQLObjectType(); + this.queryResultObjectType = new GraphQLObjectType({ + name: `${this.className}QueryConnection`, + fields: { + nodes: { type: new GraphQLList(objectType) }, + edges: { + type: new GraphQLList(new GraphQLObjectType({ + name: `${this.className}Edge`, + fields: () => ({ + node: { type: objectType } + }) + })) + } } - } - }); + }); + } + return this.queryResultObjectType; } - graphQLMutationResultType(objectType) { - return new GraphQLObjectType({ - name: `${this.className}MutationCompletePayload`, - fields: { - object: { type: objectType } - } - }); + graphQLMutationResultType() { + if (!this.mutationResultObjectType) { + const objectType = this.graphQLObjectType(); + this.mutationResultObjectType = new GraphQLObjectType({ + name: `${this.className}MutationCompletePayload`, + fields: { + object: { type: objectType } + } + }); + } + return this.mutationResultObjectType; } graphQLObjectType() { - return new GraphQLObjectType(this.graphQLConfig()); + if (!this.objectType) { + this.objectType = new GraphQLObjectType(this.graphQLConfig()); + } + return this.objectType; } } diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 6e800a4fa81..722fbda907c 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -1,3 +1,4 @@ +import { runFind, runGet } from './execute'; import { loadClass, clearCache, @@ -14,142 +15,6 @@ import { import rest from '../rest'; -// Flatten all graphQL selections to the dot notation. -function reduceSelections(selections, topKey) { - return selections.reduce((memo, selection) => { - const value = selection.name.value; - if (selection.selectionSet === null || selection.selectionSet === undefined) { - memo.push(value); - } else { - // Get the sub seletions and add on current key - const subSelections = reduceSelections(selection.selectionSet.selections, topKey); - memo = memo.concat(subSelections.map((key) => { - return value + '.' + key; - })); - } - return memo; - }, []); -} - -// Get the selections for the 1st node in a array of . separated keys -function getSelections(node, topKey) { - return reduceSelections(node.selectionSet.selections, topKey); -} - -function getFirstNode(node, matching) { - if (!node || !node.selectionSet || !node.selectionSet.selections) { - return; - } - let found; - for(const child of node.selectionSet.selections) { - found = matching(child, node); - getFirstNode(child, matching); - } - if (found) { - return; - } -} - -function flattenSelectionSet(nodes) { - const allSelections = nodes.map(getSelections).reduce((memo, keys) => { - return memo.concat(keys); - }, []); - return [...new Set(allSelections)].join(','); -} - -function getKeysForFind(info/*, schema, className*/) { - const node = info.fieldNodes[0]; - const nodes = []; - getFirstNode(node, (child, node) => { - if (child.name.value === 'nodes') { - nodes.push(child); - return true; - } - if (child.name.value === 'node' && node.name.value === 'edges') { - nodes.push(child); - return true; - } - }); - const keys = flattenSelectionSet(nodes); - return { - keys, - include: keys - } -} - -function getQueryOptions(info, parentNodeName) { - const node = info.fieldNodes[0]; - const selections = node.selectionSet.selections; - let nodes = selections.filter((selection) => { - return selection.name.value == parentNodeName; - }); - if (nodes.length == 0) { - nodes = [node]; - } - const keys = flattenSelectionSet(nodes); - return { - keys, - include: keys - } -} - -function transformResult(className, result, schema) { - if (Array.isArray(result)) { - return result.map((res) => transformResult(className, res, schema)); - } - const { fields } = schema[className]; - if (result.objectId) { - result.id = result.objectId; - } - Object.keys(result).forEach((key) => { - if (fields[key] && fields[key].type === 'Pointer') { - result[key] = transformResult(fields[key].targetClass, result[key], schema); - } - }); - return Object.assign({className}, result); -} - -function toGraphQLResult(className, singleResult, schema) { - return (restResult) => { - const results = restResult.results; - if (results.length == 0) { - return; - } - if (singleResult) { - return transformResult(className, results[0], schema); - } - return transformResult(className, results, schema); - } -} - -function transform(constraintKey, currentValue) { - let value = currentValue; - if (constraintKey === 'nearSphere') { - value = { - latitude: currentValue.point.latitude, - longitude: currentValue.point.longitude, - } - } - const key = `$${constraintKey}`; - - return { - key, - value, - } -} - -function transformQuery(query) { - Object.keys(query).forEach((key) => { - Object.keys(query[key]).forEach((constraintKey) => { - const constraint = query[key][constraintKey]; - delete query[key][constraintKey]; - const result = transform(constraintKey, constraint); - query[key][result.key] = result.value; - }); - }); - return query; -} - function transformInput(input, schema) { const { fields } = schema; Object.keys(input).forEach((key) => { @@ -163,34 +28,6 @@ function transformInput(input, schema) { return input; } -// Runs a find against the rest API -function runFind(context, info, className, args, schema) { - let query = {}; - if (args.where) { - query = Object.assign(query, args.where); - } - if (args.objectId) { - query = Object.assign(query, { objectId: args.objectId }); - } - const options = getKeysForFind(info, schema, className); - if (Object.prototype.hasOwnProperty.call(args, 'limit')) { - options.limit = args.limit; - } - if (Object.prototype.hasOwnProperty.call(args, 'skip')) { - options.skip = args.skip; - } - query = transformQuery(query, schema); - return rest.find(context.config, context.auth, className, query, options) - .then(toGraphQLResult(className, false, schema)); -} - -// runs a get against the rest API -function runGet(context, info, className, objectId, schema) { - const options = getQueryOptions(info, 'object'); - return rest.get(context.config, context.auth, className, objectId, options) - .then(toGraphQLResult(className, true, schema)); -} - export class GraphQLParseSchema { schema; types; diff --git a/src/graphql/execute.js b/src/graphql/execute.js new file mode 100644 index 00000000000..fb3a0a2e281 --- /dev/null +++ b/src/graphql/execute.js @@ -0,0 +1,176 @@ +import rest from '../rest'; + +// Flatten all graphQL selections to the dot notation. +function reduceSelections(selections, topKey) { + return selections.reduce((memo, selection) => { + const value = selection.name.value; + if (selection.selectionSet === null || selection.selectionSet === undefined) { + if (value === 'id') { + memo.push('objectId'); + } else { + memo.push(value); + } + } else { + // Get the sub seletions and add on current key + const subSelections = reduceSelections(selection.selectionSet.selections, topKey); + memo = memo.concat(subSelections.map((key) => { + return value + '.' + key; + })); + } + return memo; + }, []); +} + +// Get the selections for the 1st node in a array of . separated keys +function getSelections(node, topKey) { + return reduceSelections(node.selectionSet.selections, topKey); +} + +function getFirstNode(node, matching) { + if (!node || !node.selectionSet || !node.selectionSet.selections) { + return; + } + let found; + for(const child of node.selectionSet.selections) { + found = matching(child, node); + getFirstNode(child, matching); + } + if (found) { + return; + } +} + +function flattenSelectionSet(nodes) { + const allSelections = nodes.map(getSelections).reduce((memo, keys) => { + return memo.concat(keys); + }, []); + return [...new Set(allSelections)].join(','); +} + +function getKeysForFind(info/*, schema, className*/) { + const node = info.fieldNodes[0]; + const nodes = []; + getFirstNode(node, (child, node) => { + if (child.name.value === 'nodes') { + nodes.push(child); + return true; + } + if (child.name.value === 'node' && node.name.value === 'edges') { + nodes.push(child); + return true; + } + }); + const keys = flattenSelectionSet(nodes); + return { + keys, + include: keys + } +} + +function getQueryOptions(info, parentNodeName) { + const node = info.fieldNodes[0]; + const selections = node.selectionSet.selections; + let nodes = selections.filter((selection) => { + return selection.name.value == parentNodeName; + }); + if (nodes.length == 0) { + nodes = [node]; + } + const keys = flattenSelectionSet(nodes); + return { + keys, + include: keys + } +} + +function transformResult(className, result, schema) { + if (Array.isArray(result)) { + return result.map((res) => transformResult(className, res, schema)); + } + const { fields } = schema[className]; + if (result.objectId) { + result.id = result.objectId; + } + Object.keys(result).forEach((key) => { + if (fields[key] && fields[key].type === 'Pointer') { + result[key] = transformResult(fields[key].targetClass, result[key], schema); + } + }); + return Object.assign({className}, result); +} + +function toGraphQLResult(className, singleResult, schema) { + return (restResult) => { + const results = restResult.results; + if (results.length == 0) { + return []; + } + if (singleResult) { + return transformResult(className, results[0], schema); + } + return transformResult(className, results, schema); + } +} + +function transform(constraintKey, currentValue) { + let value = currentValue; + if (constraintKey === 'nearSphere') { + value = { + latitude: currentValue.point.latitude, + longitude: currentValue.point.longitude, + } + } + const key = `$${constraintKey}`; + + return { + key, + value, + } +} + +function transformQuery(query) { + Object.keys(query).forEach((key) => { + Object.keys(query[key]).forEach((constraintKey) => { + const constraint = query[key][constraintKey]; + delete query[key][constraintKey]; + const result = transform(constraintKey, constraint); + query[key][result.key] = result.value; + }); + }); + return query; +} + +// Runs a find against the rest API +export function runFind(context, info, className, args, schema, restQuery) { + let query = {}; + if (!restQuery) { + if (args.where) { + query = Object.assign(query, args.where); + } + if (args.objectId) { + query = Object.assign(query, { objectId: args.objectId }); + } + query = transformQuery(query, schema); + } else { + query = restQuery; + } + const options = getKeysForFind(info, schema, className); + if (Object.prototype.hasOwnProperty.call(args, 'limit')) { + options.limit = args.limit; + } + if (Object.prototype.hasOwnProperty.call(args, 'skip')) { + options.skip = args.skip; + } + if (Object.prototype.hasOwnProperty.call(args, 'redirectClassNameForKey')) { + options.redirectClassNameForKey = args.redirectClassNameForKey; + } + return rest.find(context.config, context.auth, className, query, options) + .then(toGraphQLResult(className, false, schema)); +} + +// runs a get against the rest API +export function runGet(context, info, className, objectId, schema) { + const options = getQueryOptions(info, 'object'); + return rest.get(context.config, context.auth, className, objectId, options) + .then(toGraphQLResult(className, true, schema)); +} diff --git a/src/graphql/types/QueryConstraint.js b/src/graphql/types/QueryConstraint.js index bad30308809..ccef3b880a8 100644 --- a/src/graphql/types/QueryConstraint.js +++ b/src/graphql/types/QueryConstraint.js @@ -1,6 +1,5 @@ import { Kind, - GraphQLInputType, GraphQLList, GraphQLBoolean } from 'graphql' diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index d833a9dce6a..2ac4cb2d8d9 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -5,7 +5,6 @@ import { GraphQLBoolean, GraphQLID, GraphQLList, - GraphQLUnionType, } from 'graphql' import { diff --git a/src/graphql/typesCache.js b/src/graphql/typesCache.js new file mode 100644 index 00000000000..daba092013c --- /dev/null +++ b/src/graphql/typesCache.js @@ -0,0 +1,13 @@ + +let cache = {}; + +export function getOrElse(key, handler) { + if (!cache[key]) { + cache[key] = handler(); + } + return cache[key]; +} + +export function clearCache() { + cache = {}; +} From d65d196bc994323937b96ec99ff85a9018280a3a Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 14:20:10 -0400 Subject: [PATCH 23/62] Removes the logic that walks down the selections and levergage resolvers --- src/graphql/execute.js | 115 +++++++---------------------------------- 1 file changed, 19 insertions(+), 96 deletions(-) diff --git a/src/graphql/execute.js b/src/graphql/execute.js index fb3a0a2e281..a96d527303c 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -1,91 +1,8 @@ import rest from '../rest'; -// Flatten all graphQL selections to the dot notation. -function reduceSelections(selections, topKey) { - return selections.reduce((memo, selection) => { - const value = selection.name.value; - if (selection.selectionSet === null || selection.selectionSet === undefined) { - if (value === 'id') { - memo.push('objectId'); - } else { - memo.push(value); - } - } else { - // Get the sub seletions and add on current key - const subSelections = reduceSelections(selection.selectionSet.selections, topKey); - memo = memo.concat(subSelections.map((key) => { - return value + '.' + key; - })); - } - return memo; - }, []); -} - -// Get the selections for the 1st node in a array of . separated keys -function getSelections(node, topKey) { - return reduceSelections(node.selectionSet.selections, topKey); -} - -function getFirstNode(node, matching) { - if (!node || !node.selectionSet || !node.selectionSet.selections) { - return; - } - let found; - for(const child of node.selectionSet.selections) { - found = matching(child, node); - getFirstNode(child, matching); - } - if (found) { - return; - } -} - -function flattenSelectionSet(nodes) { - const allSelections = nodes.map(getSelections).reduce((memo, keys) => { - return memo.concat(keys); - }, []); - return [...new Set(allSelections)].join(','); -} - -function getKeysForFind(info/*, schema, className*/) { - const node = info.fieldNodes[0]; - const nodes = []; - getFirstNode(node, (child, node) => { - if (child.name.value === 'nodes') { - nodes.push(child); - return true; - } - if (child.name.value === 'node' && node.name.value === 'edges') { - nodes.push(child); - return true; - } - }); - const keys = flattenSelectionSet(nodes); - return { - keys, - include: keys - } -} - -function getQueryOptions(info, parentNodeName) { - const node = info.fieldNodes[0]; - const selections = node.selectionSet.selections; - let nodes = selections.filter((selection) => { - return selection.name.value == parentNodeName; - }); - if (nodes.length == 0) { - nodes = [node]; - } - const keys = flattenSelectionSet(nodes); - return { - keys, - include: keys - } -} - -function transformResult(className, result, schema) { +function transformResult(className, result, schema, { context, info }) { if (Array.isArray(result)) { - return result.map((res) => transformResult(className, res, schema)); + return result.map((res) => transformResult(className, res, schema, { context, info })); } const { fields } = schema[className]; if (result.objectId) { @@ -93,22 +10,28 @@ function transformResult(className, result, schema) { } Object.keys(result).forEach((key) => { if (fields[key] && fields[key].type === 'Pointer') { - result[key] = transformResult(fields[key].targetClass, result[key], schema); + const pointer = result[key]; + result[key] = (parent, request, info) => { + const selections = info.fieldNodes[0].selectionSet.selections.map((field) => { + return field.name.value; + }); + if (selections.indexOf('id') < 0 || selections.length > 0) { + return runGet(context, info, pointer.className, pointer.objectId, schema); + } + return transformResult(fields[key].targetClass, pointer, schema, { context, info }); + } } }); return Object.assign({className}, result); } -function toGraphQLResult(className, singleResult, schema) { +function toGraphQLResult(className, schema, { context, info }) { return (restResult) => { const results = restResult.results; if (results.length == 0) { return []; } - if (singleResult) { - return transformResult(className, results[0], schema); - } - return transformResult(className, results, schema); + return transformResult(className, results, schema, { context, info }); } } @@ -154,7 +77,7 @@ export function runFind(context, info, className, args, schema, restQuery) { } else { query = restQuery; } - const options = getKeysForFind(info, schema, className); + const options = {}; if (Object.prototype.hasOwnProperty.call(args, 'limit')) { options.limit = args.limit; } @@ -165,12 +88,12 @@ export function runFind(context, info, className, args, schema, restQuery) { options.redirectClassNameForKey = args.redirectClassNameForKey; } return rest.find(context.config, context.auth, className, query, options) - .then(toGraphQLResult(className, false, schema)); + .then(toGraphQLResult(className, schema, { context, info })); } // runs a get against the rest API export function runGet(context, info, className, objectId, schema) { - const options = getQueryOptions(info, 'object'); - return rest.get(context.config, context.auth, className, objectId, options) - .then(toGraphQLResult(className, true, schema)); + return rest.get(context.config, context.auth, className, objectId, {}) + .then(toGraphQLResult(className, schema, { context, info })) + .then(results => results[0]); } From c302f2280bde14a164bca5eaa19f7051c333dbec Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 14:32:58 -0400 Subject: [PATCH 24/62] use resolver instead of includes - May need to cache some promises so we can reuse them in the same cycle --- src/graphql/ParseClass.js | 16 ++++++++++++++-- src/graphql/execute.js | 29 +++++++---------------------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 325e37b058f..127583a4bda 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -1,4 +1,4 @@ -import { runFind } from './execute'; +import { runFind, runGet, transformResult } from './execute'; import { GraphQLInterfaceType, @@ -123,12 +123,24 @@ export class ParseClass { type: loadClass(field.targetClass, this.schema).queryType } } else if (isObject) { + // TODO: move pointer resolver somewhere else gQLField = { - type: loadClass(field.targetClass, this.schema).objectType + type: loadClass(field.targetClass, this.schema).objectType, + resolve: (parent, args, context, info) => { + const object = parent[fieldName]; + const selections = info.fieldNodes[0].selectionSet.selections.map((field) => { + return field.name.value; + }); + if (selections.indexOf('id') < 0 || selections.length > 1) { + return runGet(context, info, object.className, object.objectId, this.schema); + } + return transformResult(fields[fieldName].targetClass, object, this.schema, { context, info }); + } } } } if (field.type == 'Relation' && isObject) { + // TODO: Move relation resolver somewhere else gQLField = { type: loadClass(field.targetClass, this.schema).queryResultType, resolve: async (parent, args, context, info) => { diff --git a/src/graphql/execute.js b/src/graphql/execute.js index a96d527303c..81eeeed1046 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -1,37 +1,22 @@ import rest from '../rest'; -function transformResult(className, result, schema, { context, info }) { +export function transformResult(className, result) { if (Array.isArray(result)) { - return result.map((res) => transformResult(className, res, schema, { context, info })); + return result.map((res) => transformResult(className, res)); } - const { fields } = schema[className]; if (result.objectId) { result.id = result.objectId; } - Object.keys(result).forEach((key) => { - if (fields[key] && fields[key].type === 'Pointer') { - const pointer = result[key]; - result[key] = (parent, request, info) => { - const selections = info.fieldNodes[0].selectionSet.selections.map((field) => { - return field.name.value; - }); - if (selections.indexOf('id') < 0 || selections.length > 0) { - return runGet(context, info, pointer.className, pointer.objectId, schema); - } - return transformResult(fields[key].targetClass, pointer, schema, { context, info }); - } - } - }); return Object.assign({className}, result); } -function toGraphQLResult(className, schema, { context, info }) { +function toGraphQLResult(className) { return (restResult) => { const results = restResult.results; if (results.length == 0) { return []; } - return transformResult(className, results, schema, { context, info }); + return transformResult(className, results); } } @@ -88,12 +73,12 @@ export function runFind(context, info, className, args, schema, restQuery) { options.redirectClassNameForKey = args.redirectClassNameForKey; } return rest.find(context.config, context.auth, className, query, options) - .then(toGraphQLResult(className, schema, { context, info })); + .then(toGraphQLResult(className)); } // runs a get against the rest API -export function runGet(context, info, className, objectId, schema) { +export function runGet(context, info, className, objectId) { return rest.get(context.config, context.auth, className, objectId, {}) - .then(toGraphQLResult(className, schema, { context, info })) + .then(toGraphQLResult(className)) .then(results => results[0]); } From a9adc1d41c487c4c6d0ab7204ad9c4f31b2d4ff2 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 15:56:03 -0400 Subject: [PATCH 25/62] Adds ability to execute queries on relations --- src/graphql/ParseClass.js | 9 ++++++++- src/graphql/Schema.js | 1 - src/graphql/execute.js | 19 +++++++++---------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 127583a4bda..7ca0147a6cb 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -5,6 +5,7 @@ import { GraphQLObjectType, GraphQLInputObjectType, GraphQLList, + GraphQLInt, } from 'graphql' import { @@ -141,8 +142,14 @@ export class ParseClass { } if (field.type == 'Relation' && isObject) { // TODO: Move relation resolver somewhere else + const { queryResultType, queryType } = loadClass(field.targetClass, this.schema); gQLField = { - type: loadClass(field.targetClass, this.schema).queryResultType, + type: queryResultType, + args: { + where: { type: queryType }, + limit: { type: GraphQLInt }, + skip: { type: GraphQLInt } + }, resolve: async (parent, args, context, info) => { const query = { $relatedTo: { diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 722fbda907c..8642b29e262 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -72,7 +72,6 @@ export class GraphQLParseSchema { type: queryResultType, description: `Use this endpoint to get or query ${className} objects`, args: { - objectId: { type: GraphQLID, name: 'objectId' }, where: { type: queryType }, limit: { type: GraphQLInt }, skip: { type: GraphQLInt } diff --git a/src/graphql/execute.js b/src/graphql/execute.js index 81eeeed1046..dfbe558d600 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -51,16 +51,15 @@ function transformQuery(query) { // Runs a find against the rest API export function runFind(context, info, className, args, schema, restQuery) { let query = {}; - if (!restQuery) { - if (args.where) { - query = Object.assign(query, args.where); - } - if (args.objectId) { - query = Object.assign(query, { objectId: args.objectId }); - } - query = transformQuery(query, schema); - } else { - query = restQuery; + if (args.where) { + query = Object.assign(query, args.where); + } + if (args.objectId) { + query = Object.assign(query, { objectId: args.objectId }); + } + query = transformQuery(query, schema); + if (restQuery) { + query = Object.assign({}, query, restQuery); } const options = {}; if (Object.prototype.hasOwnProperty.call(args, 'limit')) { From 61434c6304f6990d9e4b30e8f4b0d5b7e7fd8e38 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 18:30:40 -0400 Subject: [PATCH 26/62] Relay compliant global identifier --- src/graphql/ParseClass.js | 32 ++++++++++++------- src/graphql/Schema.js | 60 ++++++++++++++++++++++++++++++++--- src/graphql/execute.js | 19 ++++++++--- src/graphql/types/PageInfo.js | 17 ++++++++++ src/graphql/types/index.js | 7 +++- 5 files changed, 114 insertions(+), 21 deletions(-) create mode 100644 src/graphql/types/PageInfo.js diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 7ca0147a6cb..9a2d3f3995e 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -5,7 +5,7 @@ import { GraphQLObjectType, GraphQLInputObjectType, GraphQLList, - GraphQLInt, + GraphQLString } from 'graphql' import { @@ -13,6 +13,7 @@ import { inputType, type, GraphQLPointer, + PageInfo, } from './types' import { @@ -87,6 +88,9 @@ export const Node = new GraphQLInterfaceType({ export const ParseObjectInterface = new GraphQLInterfaceType({ name: 'ParseObject', fields: { + objectId: { + type: type('objectId') + }, createdAt: { type: type(null, {type: 'Date'}) }, @@ -112,7 +116,7 @@ export class ParseClass { buildFields(mapper, filterReserved = false, isQuery = false, isObject = false) { const fields = this.class.fields; - return Object.keys(fields).reduce((memo, fieldName) => { + const fieldsMap = Object.keys(fields).reduce((memo, fieldName) => { if (filterReserved && reservedFieldNames.indexOf(fieldName) >= 0) { return memo; } @@ -146,9 +150,7 @@ export class ParseClass { gQLField = { type: queryResultType, args: { - where: { type: queryType }, - limit: { type: GraphQLInt }, - skip: { type: GraphQLInt } + where: { type: queryType } }, resolve: async (parent, args, context, info) => { const query = { @@ -168,6 +170,12 @@ export class ParseClass { edges: () => results.map((node) => { return { node }; }), + pageInfo: () => { + return { + hasNextPage: false, + hasPreviousPage: false + } + } }; } } @@ -176,13 +184,13 @@ export class ParseClass { if (!gQLField) { return memo; } - // use id instead of objectId in the object - if (fieldName === 'objectId' && isObject) { - fieldName = 'id'; - } memo[fieldName] = gQLField; return memo; }, {}); + if (isObject) { + fieldsMap.id = mapper('objectId', fields['objectId']); + } + return fieldsMap; } graphQLConfig() { const className = this.className; @@ -273,10 +281,12 @@ export class ParseClass { type: new GraphQLList(new GraphQLObjectType({ name: `${this.className}Edge`, fields: () => ({ - node: { type: objectType } + node: { type: objectType }, + cursor: { type: GraphQLString } }) })) - } + }, + pageInfo: { type: PageInfo }, } }); } diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 8642b29e262..4bbd44c7b1e 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -1,5 +1,6 @@ import { runFind, runGet } from './execute'; import { + Node, loadClass, clearCache, } from './ParseClass'; @@ -11,10 +12,15 @@ import { GraphQLInt, GraphQLID, GraphQLFieldConfigMap, + GraphQLString, } from 'graphql' import rest from '../rest'; +function base64(string) { + return new Buffer(string).toString('base64') +} + function transformInput(input, schema) { const { fields } = schema; Object.keys(input).forEach((key) => { @@ -59,7 +65,7 @@ export class GraphQLParseSchema { type: objectType, description: `Use this endpoint to get or query ${className} objects`, args: { - objectId: { type: GraphQLID, name: 'objectId' }, + objectId: { type: GraphQLID }, }, resolve: async (root, args, context, info) => { // Get the selections @@ -73,22 +79,68 @@ export class GraphQLParseSchema { description: `Use this endpoint to get or query ${className} objects`, args: { where: { type: queryType }, - limit: { type: GraphQLInt }, - skip: { type: GraphQLInt } + first: { type: GraphQLInt }, + last: { type: GraphQLInt }, + after: { type: GraphQLString }, + before: { type: GraphQLString } }, resolve: async (root, args, context, info) => { // Get the selections + const pageSize = args.first || args.last || 100; const results = await runFind(context, info, className, args, this.schema); return { nodes: () => results, edges: () => results.map((node) => { - return { node }; + return { + cursor: base64(node.createdAt), + node + }; }), + pageInfo: () => { + const hasPreviousPage = () => { + if (args.last) { + return results.length === pageSize; + } + if (args.after) { + return true; + } + return false; + }; + const hasNextPage = () => { + if (args.first) { + return results.length === pageSize; + } + if (args.before) { + return true; + } + return false; + }; + return { + hasNextPage, + hasPreviousPage, + } + } }; } }; fields[`find${className}`] = findField; }); + + fields.node = { + type: Node, + description: `Commong endpoint`, + args: { + id: { type: new GraphQLNonNull(GraphQLID) }, + }, + resolve: async (root, args, context, info) => { + // Get the selections + const components = new Buffer(args.id, 'base64').toString('utf8').split('::'); + if (components.length != 2) { + throw new Error('Invalid ID'); + } + return await runGet(context, info, components[0], components[1], this.schema); + } + } return new GraphQLObjectType({ name: 'Query', description: `The full parse schema`, diff --git a/src/graphql/execute.js b/src/graphql/execute.js index dfbe558d600..d0145511b93 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -5,7 +5,8 @@ export function transformResult(className, result) { return result.map((res) => transformResult(className, res)); } if (result.objectId) { - result.id = result.objectId; + // Make a unique identifier for relay + result.id = new Buffer(`${className}::${result.objectId}`).toString('base64'); } return Object.assign({className}, result); } @@ -62,11 +63,19 @@ export function runFind(context, info, className, args, schema, restQuery) { query = Object.assign({}, query, restQuery); } const options = {}; - if (Object.prototype.hasOwnProperty.call(args, 'limit')) { - options.limit = args.limit; + if (Object.prototype.hasOwnProperty.call(args, 'first')) { + options.limit = args.first; + options.order = 'createdAt'; } - if (Object.prototype.hasOwnProperty.call(args, 'skip')) { - options.skip = args.skip; + if (Object.prototype.hasOwnProperty.call(args, 'last')) { + options.limit = args.last; + options.order = '-createdAt'; + } + if (Object.prototype.hasOwnProperty.call(args, 'after')) { + query.createdAt = { '$gt': new Date(new Buffer(args.after, 'base64').toString('utf8')) } + } + if (Object.prototype.hasOwnProperty.call(args, 'before')) { + query.createdAt = { '$lt': new Date(new Buffer(args.after, 'base64').toString('utf8')) } } if (Object.prototype.hasOwnProperty.call(args, 'redirectClassNameForKey')) { options.redirectClassNameForKey = args.redirectClassNameForKey; diff --git a/src/graphql/types/PageInfo.js b/src/graphql/types/PageInfo.js new file mode 100644 index 00000000000..bb79f7fed88 --- /dev/null +++ b/src/graphql/types/PageInfo.js @@ -0,0 +1,17 @@ +import { + GraphQLObjectType, + GraphQLBoolean, + GraphQLNonNull, + GraphQLString, +} from 'graphql'; + +export const PageInfo = new GraphQLObjectType({ + name: 'PageInfo', + description: 'Information about pagination in a connection.', + fields: { + hasNextPage: { type: new GraphQLNonNull(GraphQLBoolean) }, + hasPreviousPage: { type: new GraphQLNonNull(GraphQLBoolean) }, + startCursor: { type: GraphQLString }, + endCursor: { type: GraphQLString }, + } +}); diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index 2ac4cb2d8d9..2b2016a2783 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -49,6 +49,10 @@ import { NumberInput, } from './NumberInput'; +import { + PageInfo +} from './PageInfo'; + export { GraphQLACL, GraphQLACLInput, @@ -57,7 +61,8 @@ export { GraphQLFile, GraphQLDate, GraphQLPointer, - GraphQLJSONObject + GraphQLJSONObject, + PageInfo, } export function type(fieldName, field) { From 47b316e4748d4293d037e9974f5f0803da3b374e Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 19:25:01 -0400 Subject: [PATCH 27/62] Ensures that update mutations are relay compliant --- src/graphql/ParseClass.js | 25 ++++++++++++++----------- src/graphql/Schema.js | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 9a2d3f3995e..e38b301c4e1 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -5,7 +5,8 @@ import { GraphQLObjectType, GraphQLInputObjectType, GraphQLList, - GraphQLString + GraphQLString, + GraphQLID, } from 'graphql' import { @@ -26,11 +27,9 @@ export { clearCache }; function graphQLField(fieldName, field) { const gQLType = type(fieldName, field); if (!gQLType) { - /* eslint-disable */ - console.log('no type: ', fieldName, field); return; } - const fieldType = (gQLType === GraphQLPointer ? `Pointer<${field.targetClass}>` : `${field.type}`); + const fieldType = (gQLType === GraphQLPointer ? `Pointer<${field.targetClass}>` : `${field.type}`); return { name: fieldName, type: gQLType, @@ -170,7 +169,7 @@ export class ParseClass { edges: () => results.map((node) => { return { node }; }), - pageInfo: () => { + pageInfo: () => { return { hasNextPage: false, hasPreviousPage: false @@ -227,6 +226,7 @@ export class ParseClass { fields: () => { const fields = this.buildFields(graphQLQueryField, false, true); delete fields.objectId; + delete fields.id; return fields; }, isTypeOf: function(input) { @@ -236,15 +236,17 @@ export class ParseClass { } graphQLUpdateInputConfig() { - const className = this.className; return { name: this.className + 'Update', - description: `Parse Class ${className} Update`, + description: `Parse Class ${this.className} Update`, fields: () => { - return this.buildFields(graphQLInputField, true); + const fields = this.buildFields(graphQLInputField, true); + fields.id = { type: GraphQLID }; + fields.objectId = { type: GraphQLID }; + return fields; }, isTypeOf: function(input) { - return input.className == className; + return input.className == this.className; } }; } @@ -280,7 +282,7 @@ export class ParseClass { edges: { type: new GraphQLList(new GraphQLObjectType({ name: `${this.className}Edge`, - fields: () => ({ + fields: () => ({ node: { type: objectType }, cursor: { type: GraphQLString } }) @@ -299,7 +301,8 @@ export class ParseClass { this.mutationResultObjectType = new GraphQLObjectType({ name: `${this.className}MutationCompletePayload`, fields: { - object: { type: objectType } + object: { type: objectType }, + clientMutationId: { type: GraphQLString } } }); } diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 4bbd44c7b1e..41d25f23803 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -21,6 +21,18 @@ function base64(string) { return new Buffer(string).toString('base64') } +function parseID(base64String) { + // Get the selections + const components = new Buffer(base64String, 'base64').toString('utf8').split('::'); + if (components.length != 2) { + throw new Error('Invalid ID'); + } + return { + className: components[0], + objectId: components[1] + } +} + function transformInput(input, schema) { const { fields } = schema; Object.keys(input).forEach((key) => { @@ -133,12 +145,11 @@ export class GraphQLParseSchema { id: { type: new GraphQLNonNull(GraphQLID) }, }, resolve: async (root, args, context, info) => { - // Get the selections - const components = new Buffer(args.id, 'base64').toString('utf8').split('::'); - if (components.length != 2) { - throw new Error('Invalid ID'); - } - return await runGet(context, info, components[0], components[1], this.schema); + const { + className, + objectId + } = parseID(args.id); + return await runGet(context, info, className, objectId, this.schema); } } return new GraphQLObjectType({ @@ -175,11 +186,20 @@ export class GraphQLParseSchema { type: mutationResultType, description: `use this method to update an existing ${className}`, args: { - objectId: { type: new GraphQLNonNull(GraphQLID) }, input: { type: updateType } }, resolve: async (root, args, context, info) => { - const objectId = args.objectId; + if (!args.input.id && !args.input.objectId) { + throw 'id or objectId are required'; + } + let objectId; + if (args.input.objectId) { + objectId = args.input.objectId; + delete args.input.objectId; + } else { + objectId = parseID(args.input.id).objectId; + delete args.input.id; + } const input = transformInput(args.input, this.schema[className]); await rest.update(context.config, context.auth, className, { objectId }, input); // Run get to match graphQL style From 90da185e0243f700b4d2640e1ad928eae8c10514 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 20:06:19 -0400 Subject: [PATCH 28/62] refactor namings and other objects --- src/graphql/ParseClass.js | 64 ++++++-------- src/graphql/Schema.js | 5 +- src/graphql/types/ACL.js | 4 +- .../{QueryConstraint.js => BaseQuery.js} | 26 ------ src/graphql/types/Date.js | 8 +- src/graphql/types/File.js | 2 +- src/graphql/types/GeoPoint.js | 30 ++++--- src/graphql/types/JSONObject.js | 2 +- src/graphql/types/Node.js | 14 ++++ src/graphql/types/NumberQuery.js | 2 +- src/graphql/types/ParseObject.js | 27 ++++++ src/graphql/types/Pointer.js | 19 +++-- src/graphql/types/StringQuery.js | 2 +- src/graphql/types/index.js | 83 ++++++++----------- 14 files changed, 139 insertions(+), 149 deletions(-) rename src/graphql/types/{QueryConstraint.js => BaseQuery.js} (68%) create mode 100644 src/graphql/types/Node.js create mode 100644 src/graphql/types/ParseObject.js diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index e38b301c4e1..80dc6cf444e 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -1,12 +1,12 @@ import { runFind, runGet, transformResult } from './execute'; import { - GraphQLInterfaceType, GraphQLObjectType, GraphQLInputObjectType, GraphQLList, GraphQLString, GraphQLID, + GraphQLNonNull, } from 'graphql' import { @@ -17,6 +17,14 @@ import { PageInfo, } from './types' +import { + Node +} from './types/Node'; + +import { + ParseObjectInterface +} from './types/ParseObject'; + import { getOrElse, clearCache, @@ -24,8 +32,14 @@ import { export { clearCache }; +function handleIdField(fieldName) { + if (fieldName === 'objectId' || fieldName == 'id') { + return new GraphQLNonNull(GraphQLID); + } +} + function graphQLField(fieldName, field) { - const gQLType = type(fieldName, field); + const gQLType = handleIdField(fieldName) || type(field); if (!gQLType) { return; } @@ -38,7 +52,7 @@ function graphQLField(fieldName, field) { } function graphQLInputField(fieldName, field) { - const gQLType = inputType(fieldName, field); + const gQLType = handleIdField(fieldName) || inputType(field); if (!gQLType) { return; } @@ -51,7 +65,7 @@ function graphQLInputField(fieldName, field) { } function graphQLQueryField(fieldName, field) { - const gQLType = queryType(fieldName, field); + const gQLType = handleIdField(fieldName) || queryType(field); if (!gQLType) { return; } @@ -75,33 +89,6 @@ export function loadClass(className, schema) { const reservedFieldNames = ['objectId', 'createdAt', 'updatedAt']; -export const Node = new GraphQLInterfaceType({ - name: 'Node', - fields: { - id: { - type: type('objectId') - } - } -}) - -export const ParseObjectInterface = new GraphQLInterfaceType({ - name: 'ParseObject', - fields: { - objectId: { - type: type('objectId') - }, - createdAt: { - type: type(null, {type: 'Date'}) - }, - updatedAt: { - type: type(null, {type: 'Date'}) - }, - ACL: { - type: type(null, {type: 'ACL'}) - } - } -}); - export class ParseClass { schema; className; @@ -115,7 +102,14 @@ export class ParseClass { buildFields(mapper, filterReserved = false, isQuery = false, isObject = false) { const fields = this.class.fields; - const fieldsMap = Object.keys(fields).reduce((memo, fieldName) => { + const initial = {}; + if (isObject) { + initial.id = { + description: 'A globaly unique identifier.', + type: new GraphQLNonNull(GraphQLID) + }; + } + return Object.keys(fields).reduce((memo, fieldName) => { if (filterReserved && reservedFieldNames.indexOf(fieldName) >= 0) { return memo; } @@ -185,11 +179,7 @@ export class ParseClass { } memo[fieldName] = gQLField; return memo; - }, {}); - if (isObject) { - fieldsMap.id = mapper('objectId', fields['objectId']); - } - return fieldsMap; + }, initial); } graphQLConfig() { const className = this.className; diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 41d25f23803..85fd256326e 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -1,10 +1,13 @@ import { runFind, runGet } from './execute'; import { - Node, loadClass, clearCache, } from './ParseClass'; +import { + Node +} from './types/Node'; + import { GraphQLSchema, GraphQLObjectType, diff --git a/src/graphql/types/ACL.js b/src/graphql/types/ACL.js index c90398a0b81..3d593ae8237 100644 --- a/src/graphql/types/ACL.js +++ b/src/graphql/types/ACL.js @@ -4,7 +4,7 @@ import { GraphQLString } from 'graphql' -export const GraphQLACL = new GraphQLScalarType({ +export const ACL = new GraphQLScalarType({ name: 'ACL', fields: { read: { @@ -28,5 +28,3 @@ export const GraphQLACL = new GraphQLScalarType({ throw "not implemented" } }); - -export const GraphQLACLInput = GraphQLACL; diff --git a/src/graphql/types/QueryConstraint.js b/src/graphql/types/BaseQuery.js similarity index 68% rename from src/graphql/types/QueryConstraint.js rename to src/graphql/types/BaseQuery.js index ccef3b880a8..688c3e30503 100644 --- a/src/graphql/types/QueryConstraint.js +++ b/src/graphql/types/BaseQuery.js @@ -1,11 +1,8 @@ import { - Kind, GraphQLList, GraphQLBoolean } from 'graphql' -const supportedOperators = ['eq', 'ne', 'in', 'nin', 'exists', 'select', 'dontSelect'] - function description() { return `## Equal To: \`\`\` @@ -45,27 +42,6 @@ function description() { `; } -const parseFields = (fields) => { - return fields.reduce((memo, field) => { - const operator = field.name.value; - if (supportedOperators.indexOf(operator) > -1) { - const value = field.value.value; - memo['$' + operator] = value; - } - return memo; - }, {}); -} - -const parseLiteral = (ast) => { - if (ast.kind == Kind.OBJECT) { - return parseFields(ast.fields); - } else if (ast.kind == Kind.STRING) { - return ast.value; - } else { - throw 'Invalid literal for QueryConstraint'; - } -}; - export const BaseQuery = (type) => { return { eq: { @@ -91,7 +67,5 @@ export const BaseQuery = (type) => { export default { description, - parseLiteral, - parseFields, BaseQuery, } diff --git a/src/graphql/types/Date.js b/src/graphql/types/Date.js index b21b94a37e7..2cdee488d74 100644 --- a/src/graphql/types/Date.js +++ b/src/graphql/types/Date.js @@ -7,11 +7,11 @@ import { ComparableQuery } from './NumberQuery'; -export const GraphQLDate = new GraphQLScalarType({ +export const Date = new GraphQLScalarType({ name: 'Date', serialize: (obj) => { if (typeof obj === 'string') { - return new Date(obj); + return new global.Date(obj); } return obj; }, @@ -20,10 +20,10 @@ export const GraphQLDate = new GraphQLScalarType({ }, parseLiteral: (node) => { if (node.kind === Kind.STRING) { - return new Date(node.value); + return new global.Date(node.value); } throw `Cannot parse date of type ${node.kind}`; } }); -export const DateQuery = ComparableQuery('DateQuery', GraphQLDate); +export const DateQuery = ComparableQuery('DateQuery', Date); diff --git a/src/graphql/types/File.js b/src/graphql/types/File.js index e10ee2343f1..549f9c7698f 100644 --- a/src/graphql/types/File.js +++ b/src/graphql/types/File.js @@ -3,7 +3,7 @@ import { GraphQLString } from 'graphql' -export const GraphQLFile = new GraphQLObjectType({ +export const File = new GraphQLObjectType({ name: 'File', fields: { name: { diff --git a/src/graphql/types/GeoPoint.js b/src/graphql/types/GeoPoint.js index ef2a94e18a4..8907207940f 100644 --- a/src/graphql/types/GeoPoint.js +++ b/src/graphql/types/GeoPoint.js @@ -6,7 +6,7 @@ import { GraphQLList, } from 'graphql' -import { BaseQuery } from './QueryConstraint'; +import { BaseQuery } from './BaseQuery'; const geoPointFields = { latitude: { type: GraphQLFloat, @@ -18,21 +18,21 @@ const geoPointFields = { } }; -export const GraphQLGeoPoint = new GraphQLObjectType({ +export const GeoPoint = new GraphQLObjectType({ name: 'GeoPoint', fields: geoPointFields }); -export const GeoPoint = new GraphQLInputObjectType({ +export const GeoPointInput = new GraphQLInputObjectType({ name: 'GeoPointInput', fields: geoPointFields }); -export const NearQueryType = new GraphQLInputObjectType({ +export const NearQuery = new GraphQLInputObjectType({ name: 'NearQuery', fields: { point: { - type: new GraphQLNonNull(GeoPoint), + type: new GraphQLNonNull(GeoPointInput), }, maxDistanceInMiles: { type: GraphQLFloat @@ -46,37 +46,35 @@ export const NearQueryType = new GraphQLInputObjectType({ } }); -export const WithinQueryType = new GraphQLInputObjectType({ +export const WithinQuery = new GraphQLInputObjectType({ name: 'WithinQuery', fields: { box: { - type: new GraphQLList(GeoPoint), + type: new GraphQLList(GeoPointInput), }, } }); -export const GeoWithinQueryType = new GraphQLInputObjectType({ +export const GeoWithinQuery = new GraphQLInputObjectType({ name: 'GeoWithinQuery', fields: { polygon: { - type: new GraphQLList(GeoPoint), + type: new GraphQLList(GeoPointInput), }, } }); -export const GraphQLGeoPointQuery = new GraphQLInputObjectType({ +export const GeoPointQuery = new GraphQLInputObjectType({ name: "GeoQuery", - fields: Object.assign({}, BaseQuery(GeoPoint), { + fields: Object.assign({}, BaseQuery(GeoPointInput), { nearSphere: { - type: NearQueryType + type: NearQuery }, within: { - type: WithinQueryType + type: WithinQuery }, geoWithin: { - type: GeoWithinQueryType, + type: GeoWithinQuery, } }) }); - -export const GraphQLGeoPointInput = GraphQLGeoPoint; diff --git a/src/graphql/types/JSONObject.js b/src/graphql/types/JSONObject.js index 2e864e1e254..6017931d93c 100644 --- a/src/graphql/types/JSONObject.js +++ b/src/graphql/types/JSONObject.js @@ -36,7 +36,7 @@ const id = (value) => value; // https://github.com/taion/graphql-type-json/blob/master/src/index.js // http://graphql.org/graphql-js/type/#graphqlscalartype -export const GraphQLJSONObject = new GraphQLScalarType({ +export const JSONObject = new GraphQLScalarType({ name: 'JSON', description: 'The `JSON` scalar type represents JSON values as specified by ' + diff --git a/src/graphql/types/Node.js b/src/graphql/types/Node.js new file mode 100644 index 00000000000..fefd0ddfa07 --- /dev/null +++ b/src/graphql/types/Node.js @@ -0,0 +1,14 @@ +import { + GraphQLID, + GraphQLNonNull, + GraphQLInterfaceType, +} from 'graphql' + +export const Node = new GraphQLInterfaceType({ + name: 'Node', + fields: { + id: { + type: new GraphQLNonNull(GraphQLID) + } + } +}); diff --git a/src/graphql/types/NumberQuery.js b/src/graphql/types/NumberQuery.js index c4ab82e029d..36b500d9b2a 100644 --- a/src/graphql/types/NumberQuery.js +++ b/src/graphql/types/NumberQuery.js @@ -3,7 +3,7 @@ import { GraphQLFloat, } from 'graphql' -import { BaseQuery } from './QueryConstraint'; +import { BaseQuery } from './BaseQuery'; export const ComparableQuery = (name, type) => { return new GraphQLInputObjectType({ diff --git a/src/graphql/types/ParseObject.js b/src/graphql/types/ParseObject.js new file mode 100644 index 00000000000..8bdbed763f8 --- /dev/null +++ b/src/graphql/types/ParseObject.js @@ -0,0 +1,27 @@ +import { + GraphQLID, + GraphQLNonNull, + GraphQLInterfaceType, +} from 'graphql' + +import { + type +} from './index'; + +export const ParseObjectInterface = new GraphQLInterfaceType({ + name: 'ParseObject', + fields: { + objectId: { + type: new GraphQLNonNull(GraphQLID) + }, + createdAt: { + type: type({type: 'Date'}) + }, + updatedAt: { + type: type({type: 'Date'}) + }, + ACL: { + type: type({type: 'ACL'}) + } + } +}); diff --git a/src/graphql/types/Pointer.js b/src/graphql/types/Pointer.js index f879a20a18b..5eba65f6fe6 100644 --- a/src/graphql/types/Pointer.js +++ b/src/graphql/types/Pointer.js @@ -5,7 +5,11 @@ import { GraphQLNonNull, } from 'graphql' -export const GraphQLPointer = new GraphQLInputObjectType({ +import { + getOrElse, +} from '../typesCache'; + +export const Pointer = new GraphQLInputObjectType({ name: 'Pointer', fields: { objectId: { @@ -19,11 +23,10 @@ export const GraphQLPointer = new GraphQLInputObjectType({ } }); -const cache = {}; - -export const GraphQLPointerInput = (field) => { - if (!cache[field.targetClass]) { - cache[field.targetClass] = new GraphQLInputObjectType({ +export const PointerInput = (field) => { + const name = `${field.targetClass}PointerInput`; + return getOrElse(name, () => + new GraphQLInputObjectType({ name: `${field.targetClass}PointerInput`, fields: { objectId: { @@ -36,7 +39,5 @@ export const GraphQLPointerInput = (field) => { defaultValue: field.targetClass } } - }); - } - return cache[field.targetClass]; + })); }; diff --git a/src/graphql/types/StringQuery.js b/src/graphql/types/StringQuery.js index 4fac60b493c..5223a2d945a 100644 --- a/src/graphql/types/StringQuery.js +++ b/src/graphql/types/StringQuery.js @@ -3,7 +3,7 @@ import { GraphQLString, } from 'graphql' -import { BaseQuery } from './QueryConstraint'; +import { BaseQuery } from './BaseQuery'; export const StringQuery = new GraphQLInputObjectType({ name: 'StringQuery', diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index 2b2016a2783..a1d40c3505c 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -1,40 +1,36 @@ import { GraphQLString, GraphQLFloat, - GraphQLNonNull, GraphQLBoolean, - GraphQLID, GraphQLList, } from 'graphql' import { - GraphQLACL, - GraphQLACLInput + ACL, } from './ACL'; import { - GraphQLGeoPoint, - GraphQLGeoPointInput, - GraphQLGeoPointQuery, GeoPoint, + GeoPointInput, + GeoPointQuery, } from './GeoPoint'; import { - GraphQLFile + File } from './File'; import { - GraphQLDate, + Date, DateQuery, } from './Date'; import { - GraphQLPointer, - GraphQLPointerInput, + Pointer, + PointerInput, } from './Pointer'; import { - GraphQLJSONObject, + JSONObject, } from './JSONObject'; import { @@ -54,22 +50,17 @@ import { } from './PageInfo'; export { - GraphQLACL, - GraphQLACLInput, - GraphQLGeoPoint, - GraphQLGeoPointInput, - GraphQLFile, - GraphQLDate, - GraphQLPointer, - GraphQLJSONObject, + ACL, + GeoPoint, + GeoPointInput, + File, + Date, + Pointer, + JSONObject, PageInfo, } -export function type(fieldName, field) { - if (fieldName === 'objectId' || fieldName === 'id') { - return new GraphQLNonNull(GraphQLID); - } - const type = field.type; +export function type({ type }) { if (type == 'String') { return GraphQLString; } if (type == 'Number') { @@ -77,27 +68,24 @@ export function type(fieldName, field) { } if (type == 'Boolean') { return GraphQLBoolean; } if (type == 'GeoPoint') { - return GraphQLGeoPoint; + return GeoPoint; } if (type == 'File') { - return GraphQLFile; + return File; } else if (type == 'ACL') { - return GraphQLACL; + return ACL; } else if (type == 'Date') { - return GraphQLDate; + return Date; } else if (type == 'Pointer') { - return GraphQLPointer; + return Pointer; } else if (type == 'Object') { - return GraphQLJSONObject; + return JSONObject; } else if (type === 'Array') { - return new GraphQLList(GraphQLJSONObject); + return new GraphQLList(JSONObject); } } -export function inputType(fieldName, field) { - if (fieldName === 'objectId' || fieldName === 'id') { - return new GraphQLNonNull(GraphQLID); - } - const type = field.type; +export function inputType(field) { + const { type } = field; if (type == 'String') { return GraphQLString; } if (type == 'Number') { @@ -105,26 +93,23 @@ export function inputType(fieldName, field) { } if (type == 'Boolean') { return GraphQLBoolean; } if (type == 'GeoPoint') { - return GeoPoint; + return GeoPointInput; } if (type == 'File') { // TODO: How to set a file in an object // return GraphQLFile; } else if (type == 'ACL') { - return GraphQLACLInput; + return ACL; } else if (type == 'Date') { - return DateQuery; + return Date; } else if (type == 'Pointer') { - return GraphQLPointerInput(field); + return PointerInput(field); } else if (type === 'Array') { - return new GraphQLList(GraphQLJSONObject); + return new GraphQLList(JSONObject); } } -export function queryType(fieldName, field) { - if (fieldName === 'objectId' || fieldName === 'id') { - return new GraphQLNonNull(GraphQLID); - } - const type = field.type; +export function queryType(field) { + const { type } = field; if (type == 'String') { return StringQuery; } if (type == 'Number') { @@ -132,7 +117,7 @@ export function queryType(fieldName, field) { } if (type == 'Boolean') { return GraphQLBoolean; } if (type == 'GeoPoint') { - return GraphQLGeoPointQuery; + return GeoPointQuery; } if (type == 'File') { // Cannot query on files return; @@ -142,6 +127,6 @@ export function queryType(fieldName, field) { } else if (type == 'Date') { return DateQuery; } else if (type == 'Pointer') { - return GraphQLPointerInput(field); + return PointerInput(field); } } From 12d34a0ab2a151233c8654fa2c61cbd71fbb6f75 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 21:00:31 -0400 Subject: [PATCH 29/62] Continued refactoring --- src/ParseServer.js | 5 +- src/graphql/ParseClass.js | 223 +++++++++++++++++++++++++++----------- src/graphql/Schema.js | 218 +++++-------------------------------- src/graphql/execute.js | 55 +++++++++- src/graphql/node.js | 22 ++++ 5 files changed, 263 insertions(+), 260 deletions(-) create mode 100644 src/graphql/node.js diff --git a/src/ParseServer.js b/src/ParseServer.js index a77d8a9d030..c583b1faf56 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -238,11 +238,14 @@ class ParseServer { // TODO: only await perhaps once, and optimize perf const schema = await Config.get(Parse.applicationId).database.loadSchema(); const allClasses = await schema.getAllClasses(true); + const classNames = []; const fullSchema = allClasses.reduce((memo, classDef) => { memo[classDef.className] = classDef; + classNames.push(classDef.className); return memo; }, {}); - const Schema = new GraphQLParseSchema(fullSchema, 'test'); + fullSchema.__classNames = classNames; + const Schema = new GraphQLParseSchema(Object.freeze(fullSchema)); const s = Schema.Schema(); const root = Schema.Root(); return { diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index 80dc6cf444e..a6e9c28bf25 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -1,9 +1,10 @@ -import { runFind, runGet, transformResult } from './execute'; +import { runFind, runGet, rest, transformResult, connectionResultsArray, parseID } from './execute'; import { GraphQLObjectType, GraphQLInputObjectType, GraphQLList, + GraphQLInt, GraphQLString, GraphQLID, GraphQLNonNull, @@ -13,7 +14,7 @@ import { queryType, inputType, type, - GraphQLPointer, + Pointer, PageInfo, } from './types' @@ -38,15 +39,30 @@ function handleIdField(fieldName) { } } -function graphQLField(fieldName, field) { - const gQLType = handleIdField(fieldName) || type(field); +function graphQLField(fieldName, field, schema) { + let gQLType = handleIdField(fieldName) || type(field); if (!gQLType) { return; } - const fieldType = (gQLType === GraphQLPointer ? `Pointer<${field.targetClass}>` : `${field.type}`); + const fieldType = (gQLType === Pointer ? `Pointer<${field.targetClass}>` : `${field.type}`); + let gQLResolve; + if (field.type === 'Pointer') { + gQLType = loadClass(field.targetClass, schema).objectType, + gQLResolve = (parent, args, context, info) => { + const object = parent[fieldName]; + const selections = info.fieldNodes[0].selectionSet.selections.map((field) => { + return field.name.value; + }); + if (selections.indexOf('id') < 0 || selections.length > 1) { + return runGet(context, info, object.className, object.objectId, schema); + } + return transformResult(field.targetClass, object, schema, { context, info }); + } + } return { name: fieldName, type: gQLType, + resolve: gQLResolve, description: `Accessor for ${fieldName} (${fieldType})`, }; } @@ -56,7 +72,7 @@ function graphQLInputField(fieldName, field) { if (!gQLType) { return; } - const fieldType = (gQLType === GraphQLPointer ? `Pointer<${field.targetClass}>` : `${field.type}`); + const fieldType = (gQLType === Pointer ? `Pointer<${field.targetClass}>` : `${field.type}`); return { name: fieldName, type: gQLType, @@ -64,11 +80,14 @@ function graphQLInputField(fieldName, field) { }; } -function graphQLQueryField(fieldName, field) { - const gQLType = handleIdField(fieldName) || queryType(field); +function graphQLQueryField(fieldName, field, schema) { + let gQLType = handleIdField(fieldName) || queryType(field); if (!gQLType) { return; } + if (field.type == 'Pointer') { + gQLType = loadClass(field.targetClass, schema).queryType; + } return { name: fieldName, type: gQLType, @@ -76,6 +95,19 @@ function graphQLQueryField(fieldName, field) { }; } +function transformInput(input, schema) { + const { fields } = schema; + Object.keys(input).forEach((key) => { + const value = input[key]; + if (fields[key] && fields[key].type === 'Pointer') { + value.__type = 'Pointer'; + } else if (fields[key] && fields[key].type === 'GeoPoint') { + value.__type = 'GeoPoint'; + } + }); + return input; +} + export function loadClass(className, schema) { const c = getOrElse(className, () => new ParseClass(className, schema)); const objectType = c.graphQLObjectType(); @@ -84,7 +116,90 @@ export function loadClass(className, schema) { const queryType = c.graphQLQueryInputObjectType(); const queryResultType = c.graphQLQueryResultType(objectType); const mutationResultType = c.graphQLMutationResultType(objectType); - return { objectType, inputType, updateType, queryType, queryResultType, mutationResultType, class: c } + + const get = { + type: objectType, + description: `Use this endpoint to get or query ${className} objects`, + args: { + objectId: { type: GraphQLID }, + }, + resolve: async (root, args, context, info) => { + // Get the selections + return await runGet(context, info, className, args.objectId, schema); + } + }; + + const find = { + type: queryResultType, + description: `Use this endpoint to get or query ${className} objects`, + args: { + where: { type: queryType }, + first: { type: GraphQLInt }, + last: { type: GraphQLInt }, + after: { type: GraphQLString }, + before: { type: GraphQLString } + }, + resolve: async (root, args, context, info) => { + // Get the selections + const results = await runFind(context, info, className, args, schema); + return connectionResultsArray(results, args, 100); + } + }; + + const create = { + type: mutationResultType, + fields: objectType.fields, + description: `use this method to create a new ${className}`, + args: { input: { type: inputType }}, + resolve: async (root, args, context, info) => { + const input = transformInput(args.input, schema[className]); + const res = await rest.create(context.config, context.auth, className, input); + // Run get to match graphQL style + const object = await runGet(context, info, className, res.response.objectId); + return { object }; + } + }; + + const update = { + type: mutationResultType, + description: `use this method to update an existing ${className}`, + args: { + input: { type: updateType } + }, + resolve: async (root, args, context, info) => { + if (!args.input.id && !args.input.objectId) { + throw 'id or objectId are required'; + } + let objectId; + if (args.input.objectId) { + objectId = args.input.objectId; + delete args.input.objectId; + } else { + objectId = parseID(args.input.id).objectId; + delete args.input.id; + } + const input = transformInput(args.input, schema[className]); + await rest.update(context.config, context.auth, className, { objectId }, input); + // Run get to match graphQL style + const object = await runGet(context, info, className, objectId); + return { object }; + } + }; + + const destroy = { + type: mutationResultType, + description: `use this method to update delete an existing ${className}`, + args: { + objectId: { type: new GraphQLNonNull(GraphQLID) } + }, + resolve: async (root, args, context, info) => { + const object = await runGet(context, info, className, args.objectId); + await rest.del(context.config, context.auth, className, args.objectId); + return { object } + } + }; + + return { get, find, create, update, destroy, objectType, inputType, updateType, queryType, queryResultType, mutationResultType, class: c } } const reservedFieldNames = ['objectId', 'createdAt', 'updatedAt']; @@ -100,7 +215,7 @@ export class ParseClass { this.class = this.schema[className]; } - buildFields(mapper, filterReserved = false, isQuery = false, isObject = false) { + buildFields(mapper, filterReserved = false, isObject = false) { const fields = this.class.fields; const initial = {}; if (isObject) { @@ -114,64 +229,42 @@ export class ParseClass { return memo; } const field = fields[fieldName]; - let gQLField = mapper(fieldName, field); - if (field.type == 'Pointer') { - if (isQuery) { - gQLField = { - type: loadClass(field.targetClass, this.schema).queryType - } - } else if (isObject) { - // TODO: move pointer resolver somewhere else - gQLField = { - type: loadClass(field.targetClass, this.schema).objectType, - resolve: (parent, args, context, info) => { - const object = parent[fieldName]; - const selections = info.fieldNodes[0].selectionSet.selections.map((field) => { - return field.name.value; - }); - if (selections.indexOf('id') < 0 || selections.length > 1) { - return runGet(context, info, object.className, object.objectId, this.schema); - } - return transformResult(fields[fieldName].targetClass, object, this.schema, { context, info }); - } - } - } + let gQLField = mapper(fieldName, field, this.schema); + if (field.type == 'Pointer' && isObject) { + // TODO: move pointer resolver somewhere else + // gQLField = { + // type: loadClass(field.targetClass, this.schema).objectType, + // resolve: (parent, args, context, info) => { + // const object = parent[fieldName]; + // const selections = info.fieldNodes[0].selectionSet.selections.map((field) => { + // return field.name.value; + // }); + // if (selections.indexOf('id') < 0 || selections.length > 1) { + // return runGet(context, info, object.className, object.objectId, this.schema); + // } + // return transformResult(fields[fieldName].targetClass, object, this.schema, { context, info }); + // } + // }; } if (field.type == 'Relation' && isObject) { // TODO: Move relation resolver somewhere else - const { queryResultType, queryType } = loadClass(field.targetClass, this.schema); - gQLField = { - type: queryResultType, - args: { - where: { type: queryType } - }, - resolve: async (parent, args, context, info) => { - const query = { - $relatedTo: { - object: { - __type: 'Pointer', - className: parent.className, - objectId: parent.objectId - }, - key: fieldName, - } + const { find } = loadClass(field.targetClass, this.schema); + find.resolve = async (parent, args, context, info) => { + const query = { + $relatedTo: { + object: { + __type: 'Pointer', + className: parent.className, + objectId: parent.objectId + }, + key: fieldName, } - args.redirectClassNameForKey = fieldName; - const results = await runFind(context, info, this.className, args, this.schema, query); - return { - nodes: () => results, - edges: () => results.map((node) => { - return { node }; - }), - pageInfo: () => { - return { - hasNextPage: false, - hasPreviousPage: false - } - } - }; } - } + args.redirectClassNameForKey = fieldName; + const results = await runFind(context, info, this.className, args, this.schema, query); + return connectionResultsArray(results, args, 100); + }; + gQLField = find; } if (!gQLField) { @@ -187,7 +280,7 @@ export class ParseClass { name: this.className, description: `Parse Class ${className}`, interfaces: [Node, ParseObjectInterface], - fields: () => this.buildFields(graphQLField, false, false, true), + fields: () => this.buildFields(graphQLField, false, true), isTypeOf: (a) => { return a.className == className; }, @@ -214,7 +307,7 @@ export class ParseClass { name: this.className + 'Query', description: `Parse Class ${className} Query`, fields: () => { - const fields = this.buildFields(graphQLQueryField, false, true); + const fields = this.buildFields(graphQLQueryField, false); delete fields.objectId; delete fields.id; return fields; diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 85fd256326e..42dbf065449 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -1,63 +1,19 @@ -import { runFind, runGet } from './execute'; +import { getNode } from './node'; import { loadClass, clearCache, } from './ParseClass'; -import { - Node -} from './types/Node'; - import { GraphQLSchema, GraphQLObjectType, - GraphQLNonNull, - GraphQLInt, - GraphQLID, - GraphQLFieldConfigMap, - GraphQLString, } from 'graphql' -import rest from '../rest'; - -function base64(string) { - return new Buffer(string).toString('base64') -} - -function parseID(base64String) { - // Get the selections - const components = new Buffer(base64String, 'base64').toString('utf8').split('::'); - if (components.length != 2) { - throw new Error('Invalid ID'); - } - return { - className: components[0], - objectId: components[1] - } -} - -function transformInput(input, schema) { - const { fields } = schema; - Object.keys(input).forEach((key) => { - const value = input[key]; - if (fields[key] && fields[key].type === 'Pointer') { - value.__type = 'Pointer'; - } else if (fields[key] && fields[key].type === 'GeoPoint') { - value.__type = 'GeoPoint'; - } - }); - return input; -} - export class GraphQLParseSchema { schema; - types; - applicationId; - constructor(schema, applicationId) { + constructor(schema) { this.schema = schema; - this.applicationId = applicationId; - this.types = {}; } Schema() { @@ -70,168 +26,44 @@ export class GraphQLParseSchema { } Query() { - const fields = {}; - Object.keys(this.schema).forEach((className) => { - const { - queryType, queryResultType, objectType - } = loadClass(className, this.schema); - - const get: GraphQLFieldConfigMap = { - type: objectType, - description: `Use this endpoint to get or query ${className} objects`, - args: { - objectId: { type: GraphQLID }, - }, - resolve: async (root, args, context, info) => { - // Get the selections - return await runGet(context, info, className, args.objectId, this.schema); - } - }; - fields[`${className}`] = get; - - const findField: GraphQLFieldConfigMap = { - type: queryResultType, - description: `Use this endpoint to get or query ${className} objects`, - args: { - where: { type: queryType }, - first: { type: GraphQLInt }, - last: { type: GraphQLInt }, - after: { type: GraphQLString }, - before: { type: GraphQLString } - }, - resolve: async (root, args, context, info) => { - // Get the selections - const pageSize = args.first || args.last || 100; - const results = await runFind(context, info, className, args, this.schema); - return { - nodes: () => results, - edges: () => results.map((node) => { - return { - cursor: base64(node.createdAt), - node - }; - }), - pageInfo: () => { - const hasPreviousPage = () => { - if (args.last) { - return results.length === pageSize; - } - if (args.after) { - return true; - } - return false; - }; - const hasNextPage = () => { - if (args.first) { - return results.length === pageSize; - } - if (args.before) { - return true; - } - return false; - }; - return { - hasNextPage, - hasPreviousPage, - } - } - }; - } - }; - fields[`find${className}`] = findField; - }); - - fields.node = { - type: Node, - description: `Commong endpoint`, - args: { - id: { type: new GraphQLNonNull(GraphQLID) }, - }, - resolve: async (root, args, context, info) => { - const { - className, - objectId - } = parseID(args.id); - return await runGet(context, info, className, objectId, this.schema); - } - } return new GraphQLObjectType({ name: 'Query', description: `The full parse schema`, - fields, + fields: () => { + const fields = { node: getNode(this.schema) }; + this.schema.__classNames.forEach((className) => { + const { + get, find + } = loadClass(className, this.schema); + fields[`${className}`] = get; + fields[`find${className}`] = find; + }); + return fields; + }, }); } Mutation() { // TODO: Refactor FunctionRouter to extract (as it's a new entry) // TODO: Implement Functions as mutations - const fields = {}; - Object.keys(this.schema).forEach((className) => { - const { - inputType, objectType, updateType, mutationResultType - } = loadClass(className, this.schema); - - fields[`create${className}`] = { - type: mutationResultType, - fields: objectType.fields, - description: `use this method to create a new ${className}`, - args: { input: { type: inputType }}, - resolve: async (root, args, context, info) => { - const input = transformInput(args.input, this.schema[className]); - const res = await rest.create(context.config, context.auth, className, input); - // Run get to match graphQL style - const object = await runGet(context, info, className, res.response.objectId); - return { object }; - } - } - - fields[`update${className}`] = { - type: mutationResultType, - description: `use this method to update an existing ${className}`, - args: { - input: { type: updateType } - }, - resolve: async (root, args, context, info) => { - if (!args.input.id && !args.input.objectId) { - throw 'id or objectId are required'; - } - let objectId; - if (args.input.objectId) { - objectId = args.input.objectId; - delete args.input.objectId; - } else { - objectId = parseID(args.input.id).objectId; - delete args.input.id; - } - const input = transformInput(args.input, this.schema[className]); - await rest.update(context.config, context.auth, className, { objectId }, input); - // Run get to match graphQL style - const object = await runGet(context, info, className, objectId); - return { object }; - } - } - - fields[`destroy${className}`] = { - type: mutationResultType, - description: `use this method to update delete an existing ${className}`, - args: { - objectId: { type: new GraphQLNonNull(GraphQLID) } - }, - resolve: async (root, args, context, info) => { - const object = await runGet(context, info, className, args.objectId); - await rest.del(context.config, context.auth, className, args.objectId); - return { object } - } - } - }); return new GraphQLObjectType({ name: 'Mutation', - fields + fields: () => this.schema + .__classNames + .reduce((fields, className) => { + const { + create, update, destroy + } = loadClass(className, this.schema); + fields[`create${className}`] = create; + fields[`update${className}`] = update; + fields[`destroy${className}`] = destroy; + return fields; + }, {}) }); } Root() { - return Object.keys(this.schema).reduce((memo, className) => { + return this.schema.__classNames.reduce((memo, className) => { memo[className] = {} return memo; }, {}); diff --git a/src/graphql/execute.js b/src/graphql/execute.js index d0145511b93..7794ac973f0 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -1,5 +1,5 @@ import rest from '../rest'; - +export { rest }; export function transformResult(className, result) { if (Array.isArray(result)) { return result.map((res) => transformResult(className, res)); @@ -49,6 +49,59 @@ function transformQuery(query) { return query; } +export function base64(string) { + return new Buffer(string).toString('base64') +} + +export function parseID(base64String) { + // Get the selections + const components = new Buffer(base64String, 'base64').toString('utf8').split('::'); + if (components.length != 2) { + throw new Error('Invalid ID'); + } + return { + className: components[0], + objectId: components[1] + } +} + +export function connectionResultsArray(results, args, defaultPageSize) { + const pageSize = args.first || args.last || defaultPageSize; + return { + nodes: () => results, + edges: () => results.map((node) => { + return { + cursor: base64(node.createdAt), + node + }; + }), + pageInfo: () => { + const hasPreviousPage = () => { + if (args.last) { + return results.length === pageSize; + } + if (args.after) { + return true; + } + return false; + }; + const hasNextPage = () => { + if (args.first) { + return results.length === pageSize; + } + if (args.before) { + return true; + } + return false; + }; + return { + hasNextPage, + hasPreviousPage, + } + } + }; +} + // Runs a find against the rest API export function runFind(context, info, className, args, schema, restQuery) { let query = {}; diff --git a/src/graphql/node.js b/src/graphql/node.js new file mode 100644 index 00000000000..a3e1f7da7b7 --- /dev/null +++ b/src/graphql/node.js @@ -0,0 +1,22 @@ +import { runGet, parseID } from './execute'; +import { Node } from './types/Node'; + +import { + GraphQLID, + GraphQLNonNull, +} from 'graphql' + +export const getNode = (schema) => ({ + type: Node, + description: `Commong endpoint`, + args: { + id: { type: new GraphQLNonNull(GraphQLID) }, + }, + resolve: async (root, args, context, info) => { + const { + className, + objectId + } = parseID(args.id); + return await runGet(context, info, className, objectId, schema); + } +}); From 986423abcf64bfcee7d9dfb51e3a29231a9dc84c Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 22:16:11 -0400 Subject: [PATCH 30/62] Extracts login and verification into separate module --- src/Controllers/UserAuthentication.js | 131 +++++++++++++++++ src/Routers/UsersRouter.js | 197 ++++---------------------- 2 files changed, 159 insertions(+), 169 deletions(-) create mode 100644 src/Controllers/UserAuthentication.js diff --git a/src/Controllers/UserAuthentication.js b/src/Controllers/UserAuthentication.js new file mode 100644 index 00000000000..e2b9703f804 --- /dev/null +++ b/src/Controllers/UserAuthentication.js @@ -0,0 +1,131 @@ +const Parse = require('parse/node'); +import passwordCrypto from '../password'; +import AccountLockout from '../AccountLockout'; +import Auth from '../Auth'; + +export function removeHiddenProperties(obj) { + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + // Regexp comes from Parse.Object.prototype.validate + if (key !== "__type" && !(/^[A-Za-z][0-9A-Za-z_]*$/).test(key)) { + delete obj[key]; + } + } + } +} + +export async function verifyCredentials({ username, password, email }, config, auth) { + // TODO: use the right error codes / descriptions. + if (!username && !email) { + throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'username/email is required.'); + } + if (!password) { + throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required.'); + } + if (typeof password !== 'string' + || email && typeof email !== 'string' + || username && typeof username !== 'string') { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + } + + let user; + let isValidPassword = false; + let query; + if (email && username) { + query = { email, username }; + } else if (email) { + query = { email }; + } else { + query = { $or: [{ username }, { email: username }] }; + } + const results = await config.database.find('_User', query); + if (!results.length) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + } + + if (results.length > 1) { // corner case where user1 has username == user2 email + config.loggerController.warn('There is a user which email is the same as another user\'s username, logging in based on username'); + user = results.filter((user) => user.username === username)[0]; + } else { + user = results[0]; + } + + isValidPassword = await passwordCrypto.compare(password, user.password); + const accountLockoutPolicy = new AccountLockout(user, config); + await accountLockoutPolicy.handleLoginAttempt(isValidPassword); + if (!isValidPassword) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + } + // Ensure the user isn't locked out + // A locked out user won't be able to login + // To lock a user out, just set the ACL to `masterKey` only ({}). + // Empty ACL is OK + if (!auth.isMaster && user.ACL && Object.keys(user.ACL).length == 0) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + } + if (config.verifyUserEmails && config.preventLoginWithUnverifiedEmail && !user.emailVerified) { + throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.'); + } + + delete user.password; + + // Sometimes the authData still has null on that keys + // https://github.com/parse-community/parse-server/issues/935 + if (user.authData) { + Object.keys(user.authData).forEach((provider) => { + if (user.authData[provider] === null) { + delete user.authData[provider]; + } + }); + if (Object.keys(user.authData).length == 0) { + delete user.authData; + } + } + + return user; +} + +export async function logIn({ username, password, email }, config, auth, installationId) { + const user = await verifyCredentials({ username, password, email }, config, auth); + // handle password expiry policy + if (config.passwordPolicy && config.passwordPolicy.maxPasswordAge) { + let changedAt = user._password_changed_at; + + if (!changedAt) { + // password was created before expiry policy was enabled. + // simply update _User object so that it will start enforcing from now + changedAt = new Date(); + config.database.update('_User', { username: user.username }, + { _password_changed_at: Parse._encode(changedAt) }); + } else { + // check whether the password has expired + if (changedAt.__type == 'Date') { + changedAt = new Date(changedAt.iso); + } + // Calculate the expiry time. + const expiresAt = new Date(changedAt.getTime() + 86400000 * config.passwordPolicy.maxPasswordAge); + if (expiresAt < new Date()) // fail of current time is past password expiry time + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Your password has expired. Please reset your password.'); + } + } + + // Remove hidden properties. + removeHiddenProperties(user); + + const { + sessionData, + createSession + } = Auth.createSession(config, { + userId: user.objectId, createdWith: { + 'action': 'login', + 'authProvider': 'password' + }, installationId: installationId + }); + + user.sessionToken = sessionData.sessionToken; + + config.filesController.expandFilesInObject(config, user); + + await createSession(); + return user; +} diff --git a/src/Routers/UsersRouter.js b/src/Routers/UsersRouter.js index 3c8690ea507..6c71fef9176 100644 --- a/src/Routers/UsersRouter.js +++ b/src/Routers/UsersRouter.js @@ -2,11 +2,10 @@ import Parse from 'parse/node'; import Config from '../Config'; -import AccountLockout from '../AccountLockout'; import ClassesRouter from './ClassesRouter'; import rest from '../rest'; import Auth from '../Auth'; -import passwordCrypto from '../password'; +import { logIn, removeHiddenProperties, verifyCredentials } from '../Controllers/UserAuthentication'; export class UsersRouter extends ClassesRouter { @@ -19,113 +18,24 @@ export class UsersRouter extends ClassesRouter { * @param {Object} obj An object. */ static removeHiddenProperties(obj) { - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - // Regexp comes from Parse.Object.prototype.validate - if (key !== "__type" && !(/^[A-Za-z][0-9A-Za-z_]*$/).test(key)) { - delete obj[key]; - } - } - } + removeHiddenProperties(obj); } - /** - * Validates a password request in login and verifyPassword - * @param {Object} req The request - * @returns {Object} User object - * @private - */ - _authenticateUserFromRequest(req) { - return new Promise((resolve, reject) => { - // Use query parameters instead if provided in url - let payload = req.body; - if (!payload.username && req.query.username || !payload.email && req.query.email) { - payload = req.query; - } - const { - username, - email, - password, - } = payload; - - // TODO: use the right error codes / descriptions. - if (!username && !email) { - throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'username/email is required.'); - } - if (!password) { - throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required.'); - } - if (typeof password !== 'string' - || email && typeof email !== 'string' - || username && typeof username !== 'string') { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); - } - - let user; - let isValidPassword = false; - let query; - if (email && username) { - query = { email, username }; - } else if (email) { - query = { email }; - } else { - query = { $or: [{ username }, { email: username }] }; - } - return req.config.database.find('_User', query) - .then((results) => { - if (!results.length) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); - } - - if (results.length > 1) { // corner case where user1 has username == user2 email - req.config.loggerController.warn('There is a user which email is the same as another user\'s username, logging in based on username'); - user = results.filter((user) => user.username === username)[0]; - } else { - user = results[0]; - } - - return passwordCrypto.compare(password, user.password); - }) - .then((correct) => { - isValidPassword = correct; - const accountLockoutPolicy = new AccountLockout(user, req.config); - return accountLockoutPolicy.handleLoginAttempt(isValidPassword); - }) - .then(() => { - if (!isValidPassword) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); - } - // Ensure the user isn't locked out - // A locked out user won't be able to login - // To lock a user out, just set the ACL to `masterKey` only ({}). - // Empty ACL is OK - if (!req.auth.isMaster && user.ACL && Object.keys(user.ACL).length == 0) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); - } - if (req.config.verifyUserEmails && req.config.preventLoginWithUnverifiedEmail && !user.emailVerified) { - throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.'); - } - - delete user.password; - - // Sometimes the authData still has null on that keys - // https://github.com/parse-community/parse-server/issues/935 - if (user.authData) { - Object.keys(user.authData).forEach((provider) => { - if (user.authData[provider] === null) { - delete user.authData[provider]; - } - }); - if (Object.keys(user.authData).length == 0) { - delete user.authData; - } - } - - return resolve(user); - }).catch((error) => { - return reject(error); - }); - }); + static extractLoginPayload(req): {username: ?string, email: ?string, password: ?string} { + let payload = req.body; + if (!payload.username && req.query.username || !payload.email && req.query.email) { + payload = req.query; + } + const { + username, + email, + password, + } = payload; + return { + username, + email, + password + } } handleMe(req) { @@ -154,70 +64,19 @@ export class UsersRouter extends ClassesRouter { }); } - handleLogIn(req) { - let user; - return this._authenticateUserFromRequest(req) - .then((res) => { - - user = res; - - // handle password expiry policy - if (req.config.passwordPolicy && req.config.passwordPolicy.maxPasswordAge) { - let changedAt = user._password_changed_at; - - if (!changedAt) { - // password was created before expiry policy was enabled. - // simply update _User object so that it will start enforcing from now - changedAt = new Date(); - req.config.database.update('_User', { username: user.username }, - { _password_changed_at: Parse._encode(changedAt) }); - } else { - // check whether the password has expired - if (changedAt.__type == 'Date') { - changedAt = new Date(changedAt.iso); - } - // Calculate the expiry time. - const expiresAt = new Date(changedAt.getTime() + 86400000 * req.config.passwordPolicy.maxPasswordAge); - if (expiresAt < new Date()) // fail of current time is past password expiry time - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Your password has expired. Please reset your password.'); - } - } - - // Remove hidden properties. - UsersRouter.removeHiddenProperties(user); - - const { - sessionData, - createSession - } = Auth.createSession(req.config, { - userId: user.objectId, createdWith: { - 'action': 'login', - 'authProvider': 'password' - }, installationId: req.info.installationId - }); - - user.sessionToken = sessionData.sessionToken; - - req.config.filesController.expandFilesInObject(req.config, user); - - return createSession(); - }) - .then(() => { - return { response: user }; - }); + async handleLogIn(req) { + const payload = UsersRouter.extractLoginPayload(req); + const user = await logIn(payload, req.config, req.auth, req.info.installationId); + return { + response: user + }; } - handleVerifyPassword(req) { - return this._authenticateUserFromRequest(req) - .then((user) => { - - // Remove hidden properties. - UsersRouter.removeHiddenProperties(user); - - return { response: user }; - }).catch((error) => { - throw error; - }); + async handleVerifyPassword(req) { + const payload = UsersRouter.extractLoginPayload(req); + const user = await verifyCredentials(payload, req.config, req.auth, req.info.installationId); + UsersRouter.removeHiddenProperties(user); + return { response: user }; } handleLogOut(req) { From f7b17ba2c9a94c7187be9a4c9f0ff0c8321c99ea Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 24 Aug 2018 22:29:59 -0400 Subject: [PATCH 31/62] temporary login method --- src/graphql/Schema.js | 45 ++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 42dbf065449..591d0e1851b 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -7,8 +7,12 @@ import { import { GraphQLSchema, GraphQLObjectType, + GraphQLNonNull, + GraphQLString, } from 'graphql' +import { logIn } from '../Controllers/UserAuthentication'; + export class GraphQLParseSchema { schema; @@ -46,19 +50,38 @@ export class GraphQLParseSchema { Mutation() { // TODO: Refactor FunctionRouter to extract (as it's a new entry) // TODO: Implement Functions as mutations + const fields = this.schema + .__classNames + .reduce((fields, className) => { + const { + create, update, destroy + } = loadClass(className, this.schema); + fields[`create${className}`] = create; + fields[`update${className}`] = update; + fields[`destroy${className}`] = destroy; + return fields; + }, {}); + + fields.login = { + type: new GraphQLObjectType({ + name: 'login_payload_response', + fields: { + sessionToken: { type: GraphQLNonNull(GraphQLString) } + } + }), + args: { + username: { type: GraphQLString }, + password: { type: GraphQLNonNull(GraphQLString) } + }, + resolve: async (root, args, req, info) => { + const user = await logIn(args, req.config, req.auth, req.info && req.info.installationId); + return { sessionToken: user.sessionToken }; + } + } + return new GraphQLObjectType({ name: 'Mutation', - fields: () => this.schema - .__classNames - .reduce((fields, className) => { - const { - create, update, destroy - } = loadClass(className, this.schema); - fields[`create${className}`] = create; - fields[`update${className}`] = update; - fields[`destroy${className}`] = destroy; - return fields; - }, {}) + fields, }); } From 45cd4ce872da5cb1676957ddfc25f3c9345004be Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 25 Aug 2018 09:56:38 -0400 Subject: [PATCH 32/62] Better implementations --- src/graphql/ParseClass.js | 145 ++++++++++++++++++-------------------- src/graphql/Schema.js | 4 +- src/graphql/node.js | 2 +- 3 files changed, 73 insertions(+), 78 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index a6e9c28bf25..e9bd2b45df9 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -39,10 +39,34 @@ function handleIdField(fieldName) { } } +function getRelationField(fieldName, field, schema) { + if (field.type == 'Relation') { + const { find } = loadClass(field.targetClass, schema); + find.resolve = async (parent, args, context, info) => { + const query = { + $relatedTo: { + object: { + __type: 'Pointer', + className: parent.className, + objectId: parent.objectId + }, + key: fieldName, + } + } + args.redirectClassNameForKey = fieldName; + const results = await runFind(context, info, parent.className, args, schema, query); + return connectionResultsArray(results, args, 100); + }; + return find; + } +} + function graphQLField(fieldName, field, schema) { let gQLType = handleIdField(fieldName) || type(field); if (!gQLType) { - return; + // If it's not possible to resovle a simple type + // check if it's a proper relation field + return getRelationField(fieldName, field, schema); } const fieldType = (gQLType === Pointer ? `Pointer<${field.targetClass}>` : `${field.type}`); let gQLResolve; @@ -229,44 +253,7 @@ export class ParseClass { return memo; } const field = fields[fieldName]; - let gQLField = mapper(fieldName, field, this.schema); - if (field.type == 'Pointer' && isObject) { - // TODO: move pointer resolver somewhere else - // gQLField = { - // type: loadClass(field.targetClass, this.schema).objectType, - // resolve: (parent, args, context, info) => { - // const object = parent[fieldName]; - // const selections = info.fieldNodes[0].selectionSet.selections.map((field) => { - // return field.name.value; - // }); - // if (selections.indexOf('id') < 0 || selections.length > 1) { - // return runGet(context, info, object.className, object.objectId, this.schema); - // } - // return transformResult(fields[fieldName].targetClass, object, this.schema, { context, info }); - // } - // }; - } - if (field.type == 'Relation' && isObject) { - // TODO: Move relation resolver somewhere else - const { find } = loadClass(field.targetClass, this.schema); - find.resolve = async (parent, args, context, info) => { - const query = { - $relatedTo: { - object: { - __type: 'Pointer', - className: parent.className, - objectId: parent.objectId - }, - key: fieldName, - } - } - args.redirectClassNameForKey = fieldName; - const results = await runFind(context, info, this.className, args, this.schema, query); - return connectionResultsArray(results, args, 100); - }; - gQLField = find; - } - + const gQLField = mapper(fieldName, field, this.schema); if (!gQLField) { return memo; } @@ -274,6 +261,7 @@ export class ParseClass { return memo; }, initial); } + graphQLConfig() { const className = this.className; return { @@ -290,7 +278,7 @@ export class ParseClass { graphQLInputConfig() { const className = this.className; return { - name: this.className + 'Input', + name: `Create${this.className}Input`, description: `Parse Class ${className} Input`, fields: () => { return this.buildFields(graphQLInputField, true); @@ -307,7 +295,7 @@ export class ParseClass { name: this.className + 'Query', description: `Parse Class ${className} Query`, fields: () => { - const fields = this.buildFields(graphQLQueryField, false); + const fields = this.buildFields(graphQLQueryField); delete fields.objectId; delete fields.id; return fields; @@ -320,7 +308,7 @@ export class ParseClass { graphQLUpdateInputConfig() { return { - name: this.className + 'Update', + name: `Update${this.className}Input`, description: `Parse Class ${this.className} Update`, fields: () => { const fields = this.buildFields(graphQLInputField, true); @@ -334,6 +322,44 @@ export class ParseClass { }; } + graphQLQueryResultConfig() { + const objectType = this.graphQLObjectType(); + return { + name: `${this.className}QueryConnection`, + fields: { + nodes: { type: new GraphQLList(objectType) }, + edges: { + type: new GraphQLList(new GraphQLObjectType({ + name: `${this.className}Edge`, + fields: () => ({ + node: { type: objectType }, + cursor: { type: GraphQLString } + }) + })) + }, + pageInfo: { type: PageInfo }, + } + } + } + + graphQLMutationResultConfig() { + const objectType = this.graphQLObjectType(); + return { + name: `${this.className}MutationCompletePayload`, + fields: { + object: { type: objectType }, + clientMutationId: { type: GraphQLString } + } + } + } + + graphQLObjectType() { + if (!this.objectType) { + this.objectType = new GraphQLObjectType(this.graphQLConfig()); + } + return this.objectType; + } + graphQLUpdateInputObjectType() { if (!this.updateInputObjectType) { this.updateInputObjectType = new GraphQLInputObjectType(this.graphQLUpdateInputConfig()); @@ -357,47 +383,16 @@ export class ParseClass { graphQLQueryResultType() { if (!this.queryResultObjectType) { - const objectType = this.graphQLObjectType(); - this.queryResultObjectType = new GraphQLObjectType({ - name: `${this.className}QueryConnection`, - fields: { - nodes: { type: new GraphQLList(objectType) }, - edges: { - type: new GraphQLList(new GraphQLObjectType({ - name: `${this.className}Edge`, - fields: () => ({ - node: { type: objectType }, - cursor: { type: GraphQLString } - }) - })) - }, - pageInfo: { type: PageInfo }, - } - }); + this.queryResultObjectType = new GraphQLObjectType(this.graphQLQueryResultConfig()); } return this.queryResultObjectType; } graphQLMutationResultType() { if (!this.mutationResultObjectType) { - const objectType = this.graphQLObjectType(); - this.mutationResultObjectType = new GraphQLObjectType({ - name: `${this.className}MutationCompletePayload`, - fields: { - object: { type: objectType }, - clientMutationId: { type: GraphQLString } - } - }); + this.mutationResultObjectType = new GraphQLObjectType(this.graphQLMutationResultConfig()); } return this.mutationResultObjectType; } - - - graphQLObjectType() { - if (!this.objectType) { - this.objectType = new GraphQLObjectType(this.graphQLConfig()); - } - return this.objectType; - } } diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 591d0e1851b..0011ccfcc3d 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -32,7 +32,7 @@ export class GraphQLParseSchema { Query() { return new GraphQLObjectType({ name: 'Query', - description: `The full parse schema`, + description: `The query root of you Parse Server's graphQL interface`, fields: () => { const fields = { node: getNode(this.schema) }; this.schema.__classNames.forEach((className) => { @@ -73,7 +73,7 @@ export class GraphQLParseSchema { username: { type: GraphQLString }, password: { type: GraphQLNonNull(GraphQLString) } }, - resolve: async (root, args, req, info) => { + resolve: async (root, args, req) => { const user = await logIn(args, req.config, req.auth, req.info && req.info.installationId); return { sessionToken: user.sessionToken }; } diff --git a/src/graphql/node.js b/src/graphql/node.js index a3e1f7da7b7..8e666101340 100644 --- a/src/graphql/node.js +++ b/src/graphql/node.js @@ -8,7 +8,7 @@ import { export const getNode = (schema) => ({ type: Node, - description: `Commong endpoint`, + description: `Common endpoint`, args: { id: { type: new GraphQLNonNull(GraphQLID) }, }, From a3a64dfaf0af503fa6406cb4614f9af140f70065 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 25 Aug 2018 10:02:22 -0400 Subject: [PATCH 33/62] graphql endpoint: move into the static app method --- src/Options/Definitions.js | 2 +- src/Options/docs.js | 2 +- src/Options/index.js | 2 +- src/ParseServer.js | 54 ++++++++++++++++++++------------------ 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 30df6bf7243..2e0604fec44 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -293,7 +293,7 @@ module.exports.ParseServerOptions = { "action": parsers.booleanParser, "default": false }, - "enableGraphQLI": { + "enableGraphiQL": { "env": "PARSE_SERVER_ENABLE_GRAPHQLI", "help": "Set to true to enable the graphqli interface\n this will also enable graphql", "required": true, diff --git a/src/Options/docs.js b/src/Options/docs.js index 31919233dfc..759e50f6aed 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -55,7 +55,7 @@ * @property {Number} objectIdSize Sets the number of characters in generated object id's, default 10 * @property {Number} port The port to run the ParseServer, defaults to 1337. * @property {Boolean} enableGraphQL Set to true to enable the graphql endpoint - * @property {Boolean} enableGraphQLI Set to true to enable the graphqli interface + * @property {Boolean} enableGraphiQL Set to true to enable the graphqli interface this will also enable graphql * @property {String} host The host to serve ParseServer on, defaults to 0.0.0.0 * @property {String} mountPath Mount path for the server, defaults to /parse diff --git a/src/Options/index.js b/src/Options/index.js index 2bc29c2c7c1..d412ebc0510 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -138,7 +138,7 @@ export interface ParseServerOptions { /* Set to true to enable the graphqli interface this will also enable graphql :ENV: PARSE_SERVER_ENABLE_GRAPHQLI */ - enableGraphQLI: boolean; // = false + enableGraphiQL: boolean; // = false /* The host to serve ParseServer on, defaults to 0.0.0.0 */ host: ?string; // = 0.0.0.0 /* Mount path for the server, defaults to /parse */ diff --git a/src/ParseServer.js b/src/ParseServer.js index c583b1faf56..560d61c168d 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -138,10 +138,37 @@ class ParseServer { * @static * Create an express app for the parse server * @param {Object} options let you specify the maxUploadSize when creating the express app */ - static app({maxUploadSize = '20mb', appId}) { + static app({maxUploadSize = '20mb', appId, enableGraphQL, enableGraphiQL}) { // This app serves the Parse API directly. // It's the equivalent of https://api.parse.com/1 in the hosted Parse API. var api = express(); + + if (enableGraphQL || enableGraphiQL) { + api.use('/graphql', graphqlHTTP(async (req) => { + // TODO: use middleware please :) + req.config = req.config || Config.get(Parse.applicationId); + req.auth = req.auth || new auth.Auth({ config: req.config }); + // TODO: only await perhaps once, and optimize perf + const schema = await Config.get(Parse.applicationId).database.loadSchema(); + const allClasses = await schema.getAllClasses(true); + const classNames = []; + const fullSchema = allClasses.reduce((memo, classDef) => { + memo[classDef.className] = classDef; + classNames.push(classDef.className); + return memo; + }, {}); + fullSchema.__classNames = classNames; + const Schema = new GraphQLParseSchema(Object.freeze(fullSchema)); + const s = Schema.Schema(); + const root = Schema.Root(); + return { + schema: s, + rootValue: root, + graphiql: enableGraphiQL + } + })); + } + //api.use("/apps", express.static(__dirname + "/public")); // File handling needs to be before default middlewares are applied api.use('/', middlewares.allowCrossDomain, new FilesRouter().expressRouter({ @@ -230,31 +257,6 @@ class ParseServer { */ start(options: ParseServerOptions, callback: ?()=>void) { const app = express(); - if (options.enableGraphQL || options.enableGraphQLI) { - app.use(options.mountPath + '/graphql', graphqlHTTP(async (req) => { - // TODO: use middleware please :) - req.config = req.config || Config.get(Parse.applicationId); - req.auth = req.auth || new auth.Auth({ config: req.config }); - // TODO: only await perhaps once, and optimize perf - const schema = await Config.get(Parse.applicationId).database.loadSchema(); - const allClasses = await schema.getAllClasses(true); - const classNames = []; - const fullSchema = allClasses.reduce((memo, classDef) => { - memo[classDef.className] = classDef; - classNames.push(classDef.className); - return memo; - }, {}); - fullSchema.__classNames = classNames; - const Schema = new GraphQLParseSchema(Object.freeze(fullSchema)); - const s = Schema.Schema(); - const root = Schema.Root(); - return { - schema: s, - rootValue: root, - graphiql: options.enableGraphQLI - } - })); - } if (options.middleware) { let middleware; if (typeof options.middleware == 'string') { From f34a99ec45ffc562d89369254b88bdb9cfec9293 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 25 Aug 2018 10:06:43 -0400 Subject: [PATCH 34/62] Make the graphQL endpoint static and accessible --- src/ParseServer.js | 50 +++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index 560d61c168d..c752db2c76d 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -134,6 +134,32 @@ class ParseServer { } } + static graphqlHTTP({ graphiql }) { + return graphqlHTTP(async (req) => { + // TODO: use middleware please :) + req.config = req.config || Config.get(Parse.applicationId); + req.auth = req.auth || new auth.Auth({ config: req.config }); + // TODO: only await perhaps once, and optimize perf + const schema = await Config.get(Parse.applicationId).database.loadSchema(); + const allClasses = await schema.getAllClasses(true); + const classNames = []; + const fullSchema = allClasses.reduce((memo, classDef) => { + memo[classDef.className] = classDef; + classNames.push(classDef.className); + return memo; + }, {}); + fullSchema.__classNames = classNames; + const Schema = new GraphQLParseSchema(Object.freeze(fullSchema)); + const s = Schema.Schema(); + const root = Schema.Root(); + return { + schema: s, + rootValue: root, + graphiql + } + }); + } + /** * @static * Create an express app for the parse server @@ -144,29 +170,7 @@ class ParseServer { var api = express(); if (enableGraphQL || enableGraphiQL) { - api.use('/graphql', graphqlHTTP(async (req) => { - // TODO: use middleware please :) - req.config = req.config || Config.get(Parse.applicationId); - req.auth = req.auth || new auth.Auth({ config: req.config }); - // TODO: only await perhaps once, and optimize perf - const schema = await Config.get(Parse.applicationId).database.loadSchema(); - const allClasses = await schema.getAllClasses(true); - const classNames = []; - const fullSchema = allClasses.reduce((memo, classDef) => { - memo[classDef.className] = classDef; - classNames.push(classDef.className); - return memo; - }, {}); - fullSchema.__classNames = classNames; - const Schema = new GraphQLParseSchema(Object.freeze(fullSchema)); - const s = Schema.Schema(); - const root = Schema.Root(); - return { - schema: s, - rootValue: root, - graphiql: enableGraphiQL - } - })); + api.use('/graphql', ParseServer.graphqlHTTP({ graphiql: enableGraphiQL})); } //api.use("/apps", express.static(__dirname + "/public")); From 542e26011cee5f91ee280a5478a878e6011b82b9 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 25 Aug 2018 10:37:09 -0400 Subject: [PATCH 35/62] Use nice display names --- src/graphql/ParseClass.js | 18 +++++++++++------- src/graphql/Schema.js | 14 +++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/graphql/ParseClass.js b/src/graphql/ParseClass.js index e9bd2b45df9..5474f689438 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/ParseClass.js @@ -223,7 +223,7 @@ export function loadClass(className, schema) { } }; - return { get, find, create, update, destroy, objectType, inputType, updateType, queryType, queryResultType, mutationResultType, class: c } + return { displayName: c.displayName, get, find, create, update, destroy, objectType, inputType, updateType, queryType, queryResultType, mutationResultType, class: c } } const reservedFieldNames = ['objectId', 'createdAt', 'updatedAt']; @@ -235,6 +235,10 @@ export class ParseClass { constructor(className, schema) { this.className = className; + this.displayName = className; + if (this.className.indexOf('_') === 0) { + this.displayName = this.className.slice(1); + } this.schema = schema; this.class = this.schema[className]; } @@ -265,7 +269,7 @@ export class ParseClass { graphQLConfig() { const className = this.className; return { - name: this.className, + name: this.displayName, description: `Parse Class ${className}`, interfaces: [Node, ParseObjectInterface], fields: () => this.buildFields(graphQLField, false, true), @@ -278,7 +282,7 @@ export class ParseClass { graphQLInputConfig() { const className = this.className; return { - name: `Create${this.className}Input`, + name: `Create${this.displayName}Input`, description: `Parse Class ${className} Input`, fields: () => { return this.buildFields(graphQLInputField, true); @@ -292,7 +296,7 @@ export class ParseClass { graphQLQueryConfig() { const className = this.className; return { - name: this.className + 'Query', + name: this.displayName + 'Query', description: `Parse Class ${className} Query`, fields: () => { const fields = this.buildFields(graphQLQueryField); @@ -308,7 +312,7 @@ export class ParseClass { graphQLUpdateInputConfig() { return { - name: `Update${this.className}Input`, + name: `Update${this.displayName}Input`, description: `Parse Class ${this.className} Update`, fields: () => { const fields = this.buildFields(graphQLInputField, true); @@ -325,7 +329,7 @@ export class ParseClass { graphQLQueryResultConfig() { const objectType = this.graphQLObjectType(); return { - name: `${this.className}QueryConnection`, + name: `${this.displayName}QueryConnection`, fields: { nodes: { type: new GraphQLList(objectType) }, edges: { @@ -345,7 +349,7 @@ export class ParseClass { graphQLMutationResultConfig() { const objectType = this.graphQLObjectType(); return { - name: `${this.className}MutationCompletePayload`, + name: `${this.displayName}MutationCompletePayload`, fields: { object: { type: objectType }, clientMutationId: { type: GraphQLString } diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 0011ccfcc3d..c359460e26d 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -37,10 +37,10 @@ export class GraphQLParseSchema { const fields = { node: getNode(this.schema) }; this.schema.__classNames.forEach((className) => { const { - get, find + get, find, displayName, } = loadClass(className, this.schema); - fields[`${className}`] = get; - fields[`find${className}`] = find; + fields[`${displayName}`] = get; + fields[`find${displayName}`] = find; }); return fields; }, @@ -54,11 +54,11 @@ export class GraphQLParseSchema { .__classNames .reduce((fields, className) => { const { - create, update, destroy + create, update, destroy, displayName, } = loadClass(className, this.schema); - fields[`create${className}`] = create; - fields[`update${className}`] = update; - fields[`destroy${className}`] = destroy; + fields[`create${displayName}`] = create; + fields[`update${displayName}`] = update; + fields[`destroy${displayName}`] = destroy; return fields; }, {}); From 3c5a2368da07a412f80bb231ee8437ee5569d277 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 25 Aug 2018 12:04:27 -0400 Subject: [PATCH 36/62] Split in sub schemas with each exposing Query and Mutation --- src/graphql/Schema.js | 56 ++++++------------------ src/graphql/{node.js => schemas/Node.js} | 12 ++++- src/graphql/{ => schemas}/ParseClass.js | 43 +++++++++++++++--- src/graphql/schemas/UserAuth.js | 55 +++++++++++++++++++++++ 4 files changed, 115 insertions(+), 51 deletions(-) rename src/graphql/{node.js => schemas/Node.js} (69%) rename src/graphql/{ => schemas}/ParseClass.js (91%) create mode 100644 src/graphql/schemas/UserAuth.js diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index c359460e26d..19f97073f62 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -1,18 +1,18 @@ -import { getNode } from './node'; + import { - loadClass, clearCache, -} from './ParseClass'; +} from './typesCache'; + +import ParseClassSchema from './schemas/ParseClass'; + +import UserAuthSchema from './schemas/UserAuth'; +import NodeSchema from './schemas/Node'; import { GraphQLSchema, GraphQLObjectType, - GraphQLNonNull, - GraphQLString, } from 'graphql' -import { logIn } from '../Controllers/UserAuthentication'; - export class GraphQLParseSchema { schema; @@ -34,14 +34,9 @@ export class GraphQLParseSchema { name: 'Query', description: `The query root of you Parse Server's graphQL interface`, fields: () => { - const fields = { node: getNode(this.schema) }; - this.schema.__classNames.forEach((className) => { - const { - get, find, displayName, - } = loadClass(className, this.schema); - fields[`${displayName}`] = get; - fields[`find${displayName}`] = find; - }); + const fields = {}; + Object.assign(fields, NodeSchema.Query(this.schema)); + Object.assign(fields, ParseClassSchema.Query(this.schema)); return fields; }, }); @@ -50,34 +45,9 @@ export class GraphQLParseSchema { Mutation() { // TODO: Refactor FunctionRouter to extract (as it's a new entry) // TODO: Implement Functions as mutations - const fields = this.schema - .__classNames - .reduce((fields, className) => { - const { - create, update, destroy, displayName, - } = loadClass(className, this.schema); - fields[`create${displayName}`] = create; - fields[`update${displayName}`] = update; - fields[`destroy${displayName}`] = destroy; - return fields; - }, {}); - - fields.login = { - type: new GraphQLObjectType({ - name: 'login_payload_response', - fields: { - sessionToken: { type: GraphQLNonNull(GraphQLString) } - } - }), - args: { - username: { type: GraphQLString }, - password: { type: GraphQLNonNull(GraphQLString) } - }, - resolve: async (root, args, req) => { - const user = await logIn(args, req.config, req.auth, req.info && req.info.installationId); - return { sessionToken: user.sessionToken }; - } - } + const fields = {}; + Object.assign(fields, UserAuthSchema.Mutation(this.schema)); + Object.assign(fields, ParseClassSchema.Mutation(this.schema)); return new GraphQLObjectType({ name: 'Mutation', diff --git a/src/graphql/node.js b/src/graphql/schemas/Node.js similarity index 69% rename from src/graphql/node.js rename to src/graphql/schemas/Node.js index 8e666101340..c51df1de31a 100644 --- a/src/graphql/node.js +++ b/src/graphql/schemas/Node.js @@ -1,5 +1,5 @@ -import { runGet, parseID } from './execute'; -import { Node } from './types/Node'; +import { runGet, parseID } from '../execute'; +import { Node } from '../types/Node'; import { GraphQLID, @@ -20,3 +20,11 @@ export const getNode = (schema) => ({ return await runGet(context, info, className, objectId, schema); } }); + +export default { + Query: (schema) => { + return { + node: getNode(schema) + } + } +} diff --git a/src/graphql/ParseClass.js b/src/graphql/schemas/ParseClass.js similarity index 91% rename from src/graphql/ParseClass.js rename to src/graphql/schemas/ParseClass.js index 5474f689438..c744a885a29 100644 --- a/src/graphql/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -1,4 +1,4 @@ -import { runFind, runGet, rest, transformResult, connectionResultsArray, parseID } from './execute'; +import { runFind, runGet, rest, transformResult, connectionResultsArray, parseID } from '../execute'; import { GraphQLObjectType, @@ -16,20 +16,20 @@ import { type, Pointer, PageInfo, -} from './types' +} from '../types' import { Node -} from './types/Node'; +} from '../types/Node'; import { ParseObjectInterface -} from './types/ParseObject'; +} from '../types/ParseObject'; import { getOrElse, clearCache, -} from './typesCache'; +} from '../typesCache'; export { clearCache }; @@ -223,7 +223,7 @@ export function loadClass(className, schema) { } }; - return { displayName: c.displayName, get, find, create, update, destroy, objectType, inputType, updateType, queryType, queryResultType, mutationResultType, class: c } + return { displayName: c.displayName, get, find, create, update, destroy, objectType, inputType, updateType, queryType, queryResultType, mutationResultType, parseClass: c } } const reservedFieldNames = ['objectId', 'createdAt', 'updatedAt']; @@ -400,3 +400,34 @@ export class ParseClass { } } +export function getParseClassQueryFields(schema) { + return schema.__classNames.reduce((fields, className) => { + const { + get, find, displayName, + } = loadClass(className, schema); + return Object.assign(fields, { + [className]: get, + [`find${displayName}`]: find + }); + }, {}); +} + +export function getParseClassMutationFields(schema) { + return schema + .__classNames + .reduce((fields, className) => { + const { + create, update, destroy, displayName, + } = loadClass(className, schema); + return Object.assign(fields, { + [`create${displayName}`]: create, + [`update${displayName}`]: update, + [`destroy${displayName}`]: destroy, + }); + }, {}); +} + +export default { + Query: getParseClassQueryFields, + Mutation: getParseClassMutationFields, +} diff --git a/src/graphql/schemas/UserAuth.js b/src/graphql/schemas/UserAuth.js new file mode 100644 index 00000000000..821813741bd --- /dev/null +++ b/src/graphql/schemas/UserAuth.js @@ -0,0 +1,55 @@ +import { + GraphQLObjectType, + GraphQLInputObjectType, + GraphQLNonNull, + GraphQLString, +} from 'graphql' + +import { + transformResult +} from '../execute'; + +import { logIn } from '../../Controllers/UserAuthentication'; +import { loadClass } from './ParseClass'; + +const getLoginCompletePayload = (schema) => new GraphQLObjectType({ + name: 'LoginCompletePayload', + fields: () => { + const { parseClass } = loadClass('_User', schema); + const fields = parseClass.graphQLConfig().fields; + return Object.assign({}, fields(), { + sessionToken: { type: GraphQLNonNull(GraphQLString) } + }); + } +}); + +const LoginInput = new GraphQLInputObjectType({ + name: 'LoginInput', + fields: { + email: { type: GraphQLString, description: 'the email of the user. Either email or username should be provided' }, + username: { type: GraphQLString, description: 'the username of the user. Either email or username should be provided' }, + password: { type: GraphQLNonNull(GraphQLString) } + } +}); + +const login = (schema) => ({ + type: getLoginCompletePayload(schema), + args: { + input: { type: LoginInput } + }, + resolve: async (root, args, req) => { + const user = await logIn(args.input, req.config, req.auth, req.info && req.info.installationId); + return transformResult('_User', user); + } +}); + +export function getUserAuthMutationFields(schema) { + return { + login: login(schema) + }; +} + +export default { + Query: () => ({}), + Mutation: getUserAuthMutationFields, +} From 00f678989d514517a2aaca7530076b063b0b3be5 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 25 Aug 2018 14:33:08 -0400 Subject: [PATCH 37/62] Rename createObject to addObject --- src/graphql/schemas/ParseClass.js | 28 ++++++++++++++-------------- src/graphql/typesCache.js | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index c744a885a29..a2cee5b0fa9 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -279,13 +279,16 @@ export class ParseClass { }; } - graphQLInputConfig() { + graphQLQueryConfig() { const className = this.className; return { - name: `Create${this.displayName}Input`, - description: `Parse Class ${className} Input`, + name: this.displayName + 'Query', + description: `Parse Class ${className} Query`, fields: () => { - return this.buildFields(graphQLInputField, true); + const fields = this.buildFields(graphQLQueryField); + delete fields.objectId; + delete fields.id; + return fields; }, isTypeOf: function(input) { return input.className == className; @@ -293,16 +296,13 @@ export class ParseClass { }; } - graphQLQueryConfig() { + graphQLInputConfig() { const className = this.className; return { - name: this.displayName + 'Query', - description: `Parse Class ${className} Query`, + name: `Add${this.displayName}Input`, + description: `Parse Class ${className} Input`, fields: () => { - const fields = this.buildFields(graphQLQueryField); - delete fields.objectId; - delete fields.id; - return fields; + return this.buildFields(graphQLInputField, true); }, isTypeOf: function(input) { return input.className == className; @@ -334,7 +334,7 @@ export class ParseClass { nodes: { type: new GraphQLList(objectType) }, edges: { type: new GraphQLList(new GraphQLObjectType({ - name: `${this.className}Edge`, + name: `${this.displayName}Edge`, fields: () => ({ node: { type: objectType }, cursor: { type: GraphQLString } @@ -406,7 +406,7 @@ export function getParseClassQueryFields(schema) { get, find, displayName, } = loadClass(className, schema); return Object.assign(fields, { - [className]: get, + [displayName]: get, [`find${displayName}`]: find }); }, {}); @@ -420,7 +420,7 @@ export function getParseClassMutationFields(schema) { create, update, destroy, displayName, } = loadClass(className, schema); return Object.assign(fields, { - [`create${displayName}`]: create, + [`add${displayName}`]: create, [`update${displayName}`]: update, [`destroy${displayName}`]: destroy, }); diff --git a/src/graphql/typesCache.js b/src/graphql/typesCache.js index daba092013c..2530f77f59f 100644 --- a/src/graphql/typesCache.js +++ b/src/graphql/typesCache.js @@ -1,7 +1,7 @@ - +// @flow let cache = {}; -export function getOrElse(key, handler) { +export function getOrElse(key: string, handler: () => T): ?T { if (!cache[key]) { cache[key] = handler(); } From cd1d60469b317f9463d21b01043bfb30ec52a2a5 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 25 Aug 2018 14:39:13 -0400 Subject: [PATCH 38/62] Improve code --- src/graphql/execute.js | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/graphql/execute.js b/src/graphql/execute.js index 7794ac973f0..b8ecfa8de37 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -102,19 +102,8 @@ export function connectionResultsArray(results, args, defaultPageSize) { }; } -// Runs a find against the rest API -export function runFind(context, info, className, args, schema, restQuery) { - let query = {}; - if (args.where) { - query = Object.assign(query, args.where); - } - if (args.objectId) { - query = Object.assign(query, { objectId: args.objectId }); - } - query = transformQuery(query, schema); - if (restQuery) { - query = Object.assign({}, query, restQuery); - } +function parseArguments(args) { + const query = {}; const options = {}; if (Object.prototype.hasOwnProperty.call(args, 'first')) { options.limit = args.first; @@ -133,6 +122,26 @@ export function runFind(context, info, className, args, schema, restQuery) { if (Object.prototype.hasOwnProperty.call(args, 'redirectClassNameForKey')) { options.redirectClassNameForKey = args.redirectClassNameForKey; } + return { options, queryAdditions: query }; +} + +// Runs a find against the rest API +export function runFind(context, info, className, args, schema, restQuery) { + const query = {}; + if (args.where) { + Object.assign(query, args.where); + } + if (args.objectId) { + Object.assign(query, { objectId: args.objectId }); + } + transformQuery(query, schema); + if (restQuery) { + Object.assign(query, restQuery); + } + + const { options, queryAdditions } = parseArguments(args); + Object.assign(query, queryAdditions); + return rest.find(context.config, context.auth, className, query, options) .then(toGraphQLResult(className)); } From 1d47c2fe147d023fc12754fe24b1c3d0c9c8262e Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 25 Aug 2018 15:06:30 -0400 Subject: [PATCH 39/62] Improves interface for graphql parse schema --- src/ParseServer.js | 18 ++++-------------- src/graphql/Schema.js | 33 ++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index c752db2c76d..f29786b4866 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -140,21 +140,11 @@ class ParseServer { req.config = req.config || Config.get(Parse.applicationId); req.auth = req.auth || new auth.Auth({ config: req.config }); // TODO: only await perhaps once, and optimize perf - const schema = await Config.get(Parse.applicationId).database.loadSchema(); - const allClasses = await schema.getAllClasses(true); - const classNames = []; - const fullSchema = allClasses.reduce((memo, classDef) => { - memo[classDef.className] = classDef; - classNames.push(classDef.className); - return memo; - }, {}); - fullSchema.__classNames = classNames; - const Schema = new GraphQLParseSchema(Object.freeze(fullSchema)); - const s = Schema.Schema(); - const root = Schema.Root(); + const graphQLSchema = new GraphQLParseSchema(Parse.applicationId); + const { schema, rootValue } = await graphQLSchema.load(); return { - schema: s, - rootValue: root, + schema, + rootValue, graphiql } }); diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 19f97073f62..ca177108d41 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -1,32 +1,43 @@ +import { + GraphQLSchema, + GraphQLObjectType, +} from 'graphql' + import { clearCache, } from './typesCache'; +import Config from '../Config'; import ParseClassSchema from './schemas/ParseClass'; - import UserAuthSchema from './schemas/UserAuth'; import NodeSchema from './schemas/Node'; -import { - GraphQLSchema, - GraphQLObjectType, -} from 'graphql' - export class GraphQLParseSchema { schema; + applicationId; - constructor(schema) { - this.schema = schema; + constructor(applicationId) { + this.applicationId = applicationId; } - Schema() { - const schema = new GraphQLSchema({ + async load() { + const schema = await Config.get(this.applicationId).database.loadSchema(); + const allClasses = await schema.getAllClasses(true); + const classNames = []; + const fullSchema = allClasses.reduce((memo, classDef) => { + memo[classDef.className] = classDef; + classNames.push(classDef.className); + return memo; + }, {}); + fullSchema.__classNames = classNames; + this.schema = Object.freeze(fullSchema); + const graphQLSchema = new GraphQLSchema({ query: this.Query(), mutation: this.Mutation(), }); clearCache(); - return schema; + return { schema: graphQLSchema, rootValue: this.Root() }; } Query() { From 17937b4a6611aa5fe5de950ea18fb1486b4bac26 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sun, 26 Aug 2018 12:15:12 -0400 Subject: [PATCH 40/62] Make destroyClass compliant with full input --- src/graphql/schemas/ParseClass.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index a2cee5b0fa9..ea110cde939 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -214,7 +214,12 @@ export function loadClass(className, schema) { type: mutationResultType, description: `use this method to update delete an existing ${className}`, args: { - objectId: { type: new GraphQLNonNull(GraphQLID) } + input: { type: new GraphQLInputObjectType({ + name: `Destroy${c.displayName}Input`, + fields: { + id: { type: new GraphQLNonNull(GraphQLID) } + } + }) } }, resolve: async (root, args, context, info) => { const object = await runGet(context, info, className, args.objectId); From 96fa1ee5bddf0b40fc4b7a09084c5c0e4e0d334d Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sun, 26 Aug 2018 13:14:37 -0400 Subject: [PATCH 41/62] Remove ParseObject interface as it conflicts with Relay --- src/graphql/schemas/ParseClass.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index ea110cde939..21e92d2be41 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -22,9 +22,9 @@ import { Node } from '../types/Node'; -import { +/* import { ParseObjectInterface -} from '../types/ParseObject'; +} from '../types/ParseObject'; */ import { getOrElse, @@ -276,7 +276,8 @@ export class ParseClass { return { name: this.displayName, description: `Parse Class ${className}`, - interfaces: [Node, ParseObjectInterface], + // in relay, it's impossible to have 2 interfaces??? + interfaces: [Node, /* ParseObjectInterface */], fields: () => this.buildFields(graphQLField, false, true), isTypeOf: (a) => { return a.className == className; From ee18146d22bb04194d385f3eb0e7ba7560b05d0e Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sun, 26 Aug 2018 13:48:11 -0400 Subject: [PATCH 42/62] Adds ability to fetch currentUser, login returns a User --- src/graphql/Schema.js | 1 + src/graphql/schemas/ParseClass.js | 6 ++++++ src/graphql/schemas/UserAuth.js | 27 ++++++++++++++++----------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index ca177108d41..6b3d566318a 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -47,6 +47,7 @@ export class GraphQLParseSchema { fields: () => { const fields = {}; Object.assign(fields, NodeSchema.Query(this.schema)); + Object.assign(fields, UserAuthSchema.Query(this.schema)); Object.assign(fields, ParseClassSchema.Query(this.schema)); return fields; }, diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index 21e92d2be41..9a16ab26e97 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -257,6 +257,12 @@ export class ParseClass { type: new GraphQLNonNull(GraphQLID) }; } + if (this.className === '_User') { + initial.sessionToken = { + description: 'The session token for the user, set only when it makes sense.', + type: GraphQLString, + } + } return Object.keys(fields).reduce((memo, fieldName) => { if (filterReserved && reservedFieldNames.indexOf(fieldName) >= 0) { return memo; diff --git a/src/graphql/schemas/UserAuth.js b/src/graphql/schemas/UserAuth.js index 821813741bd..e17dda3937e 100644 --- a/src/graphql/schemas/UserAuth.js +++ b/src/graphql/schemas/UserAuth.js @@ -12,16 +12,7 @@ import { import { logIn } from '../../Controllers/UserAuthentication'; import { loadClass } from './ParseClass'; -const getLoginCompletePayload = (schema) => new GraphQLObjectType({ - name: 'LoginCompletePayload', - fields: () => { - const { parseClass } = loadClass('_User', schema); - const fields = parseClass.graphQLConfig().fields; - return Object.assign({}, fields(), { - sessionToken: { type: GraphQLNonNull(GraphQLString) } - }); - } -}); +const getLoginCompletePayload = (schema) => loadClass('_User', schema).objectType; const LoginInput = new GraphQLInputObjectType({ name: 'LoginInput', @@ -49,7 +40,21 @@ export function getUserAuthMutationFields(schema) { }; } +export function getUserAuthQueryFields(schema) { + return { + currentUser: { + type: getLoginCompletePayload(schema), + resolve: async (root, args, req) => { + if (!req.auth.user) { + throw new Error('You need to be logged in.'); + } + return transformResult('_User', req.auth.user); + } + } + }; +} + export default { - Query: () => ({}), + Query: getUserAuthQueryFields, Mutation: getUserAuthMutationFields, } From 0b3b7ec67444577917a6ac1282723d0f61cf1575 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sun, 26 Aug 2018 13:48:19 -0400 Subject: [PATCH 43/62] Adds CORS middleware before --- src/ParseServer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index f29786b4866..2e61ab2b836 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -158,7 +158,7 @@ class ParseServer { // This app serves the Parse API directly. // It's the equivalent of https://api.parse.com/1 in the hosted Parse API. var api = express(); - + api.use(middlewares.allowCrossDomain); if (enableGraphQL || enableGraphiQL) { api.use('/graphql', ParseServer.graphqlHTTP({ graphiql: enableGraphiQL})); } @@ -178,7 +178,6 @@ class ParseServer { api.use('/', bodyParser.urlencoded({extended: false}), new PublicAPIRouter().expressRouter()); api.use(bodyParser.json({ 'type': '*/*' , limit: maxUploadSize })); - api.use(middlewares.allowCrossDomain); api.use(middlewares.allowMethodOverride); api.use(middlewares.handleParseHeaders); From 2481fd867e4e682ad54afc7f088fa5b4a27f4fd6 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sun, 26 Aug 2018 14:14:35 -0400 Subject: [PATCH 44/62] small lint nit --- src/graphql/schemas/UserAuth.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/graphql/schemas/UserAuth.js b/src/graphql/schemas/UserAuth.js index e17dda3937e..af348078ab8 100644 --- a/src/graphql/schemas/UserAuth.js +++ b/src/graphql/schemas/UserAuth.js @@ -1,5 +1,4 @@ import { - GraphQLObjectType, GraphQLInputObjectType, GraphQLNonNull, GraphQLString, From 0de4d828377e5c32db8dea61b072990b40072856 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sun, 26 Aug 2018 14:14:56 -0400 Subject: [PATCH 45/62] Quick and dirty authentication for graphql --- src/ParseServer.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index 2e61ab2b836..b9448041214 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -138,7 +138,19 @@ class ParseServer { return graphqlHTTP(async (req) => { // TODO: use middleware please :) req.config = req.config || Config.get(Parse.applicationId); - req.auth = req.auth || new auth.Auth({ config: req.config }); + const sessionToken = req.get('x-parse-session-token'); + const installationId = req.get('x-parse-installation-id'); + req.info = { + installationId, + sessionToken, + } + if (sessionToken) { + req.auth = await auth.getAuthForSessionToken({ config: req.config, installationId: installationId, sessionToken: sessionToken }); + } + if (!req.auth) { + req.auth = new auth.Auth({ config: req.config }); + } + // TODO: only await perhaps once, and optimize perf const graphQLSchema = new GraphQLParseSchema(Parse.applicationId); const { schema, rootValue } = await graphQLSchema.load(); From a5bedd00461823ecd4a3b59830c42de3cbc1a24f Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sun, 26 Aug 2018 14:15:10 -0400 Subject: [PATCH 46/62] Adds clientMutationId for relay --- src/graphql/schemas/ParseClass.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index 9a16ab26e97..1e4b7f17bdc 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -177,10 +177,12 @@ export function loadClass(className, schema) { args: { input: { type: inputType }}, resolve: async (root, args, context, info) => { const input = transformInput(args.input, schema[className]); + const clientMutationId = input.clientMutationId; + delete input.clientMutationId; const res = await rest.create(context.config, context.auth, className, input); // Run get to match graphQL style const object = await runGet(context, info, className, res.response.objectId); - return { object }; + return { object, clientMutationId }; } }; @@ -202,11 +204,15 @@ export function loadClass(className, schema) { objectId = parseID(args.input.id).objectId; delete args.input.id; } + const input = transformInput(args.input, schema[className]); + const clientMutationId = input.clientMutationId; + delete input.clientMutationId; + await rest.update(context.config, context.auth, className, { objectId }, input); // Run get to match graphQL style const object = await runGet(context, info, className, objectId); - return { object }; + return { object, clientMutationId }; } }; @@ -217,14 +223,16 @@ export function loadClass(className, schema) { input: { type: new GraphQLInputObjectType({ name: `Destroy${c.displayName}Input`, fields: { - id: { type: new GraphQLNonNull(GraphQLID) } + id: { type: new GraphQLNonNull(GraphQLID) }, + clientMutationId: { type: GraphQLString } } }) } }, resolve: async (root, args, context, info) => { - const object = await runGet(context, info, className, args.objectId); - await rest.del(context.config, context.auth, className, args.objectId); - return { object } + const clientMutationId = args.input.clientMutationId; + const object = await runGet(context, info, className, args.input.objectId); + await rest.del(context.config, context.auth, className, args.input.objectId); + return { object, clientMutationId } } }; @@ -314,7 +322,9 @@ export class ParseClass { name: `Add${this.displayName}Input`, description: `Parse Class ${className} Input`, fields: () => { - return this.buildFields(graphQLInputField, true); + const fields = this.buildFields(graphQLInputField, true); + fields.clientMutationId = { type: GraphQLString }; + return fields; }, isTypeOf: function(input) { return input.className == className; @@ -330,6 +340,7 @@ export class ParseClass { const fields = this.buildFields(graphQLInputField, true); fields.id = { type: GraphQLID }; fields.objectId = { type: GraphQLID }; + fields.clientMutationId = { type: GraphQLString }; return fields; }, isTypeOf: function(input) { From 64daf00e7ed4b0d10bafd03adc310a6df0bdedbc Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sun, 26 Aug 2018 14:41:24 -0400 Subject: [PATCH 47/62] Properly fetch user when using currentUser --- src/graphql/schemas/UserAuth.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/graphql/schemas/UserAuth.js b/src/graphql/schemas/UserAuth.js index af348078ab8..7d1395fa681 100644 --- a/src/graphql/schemas/UserAuth.js +++ b/src/graphql/schemas/UserAuth.js @@ -5,7 +5,8 @@ import { } from 'graphql' import { - transformResult + transformResult, + runGet, } from '../execute'; import { logIn } from '../../Controllers/UserAuthentication'; @@ -43,11 +44,12 @@ export function getUserAuthQueryFields(schema) { return { currentUser: { type: getLoginCompletePayload(schema), - resolve: async (root, args, req) => { + resolve: async (root, args, req, info) => { if (!req.auth.user) { throw new Error('You need to be logged in.'); } - return transformResult('_User', req.auth.user); + const object = await runGet(req, info, '_User', req.auth.user.id); + return transformResult('_User', object); } } }; From 43983e087a96f3b81e1c44e31ffe746b2825bb01 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sun, 26 Aug 2018 16:24:04 -0400 Subject: [PATCH 48/62] Moves logOut logic into UserAuthentication, adds logOut to graphQL endpoints --- src/Controllers/UserAuthentication.js | 13 +++++++++++++ src/Routers/UsersRouter.js | 22 +++++++--------------- src/graphql/schemas/UserAuth.js | 14 ++++++++++++-- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/Controllers/UserAuthentication.js b/src/Controllers/UserAuthentication.js index e2b9703f804..99652b00d50 100644 --- a/src/Controllers/UserAuthentication.js +++ b/src/Controllers/UserAuthentication.js @@ -2,6 +2,7 @@ const Parse = require('parse/node'); import passwordCrypto from '../password'; import AccountLockout from '../AccountLockout'; import Auth from '../Auth'; +import rest from '../rest'; export function removeHiddenProperties(obj) { for (var key in obj) { @@ -129,3 +130,15 @@ export async function logIn({ username, password, email }, config, auth, install await createSession(); return user; } + +export async function logOut(sessionToken, config, clientSDK) { + const master = Auth.master(config); + const records = await rest.find(config, master, '_Session', + { sessionToken: sessionToken }, undefined, clientSDK + ); + if (records.results && records.results.length) { + await rest.del(config, master, '_Session', + records.results[0].objectId + ); + } +} diff --git a/src/Routers/UsersRouter.js b/src/Routers/UsersRouter.js index 6c71fef9176..f1f64678220 100644 --- a/src/Routers/UsersRouter.js +++ b/src/Routers/UsersRouter.js @@ -5,7 +5,7 @@ import Config from '../Config'; import ClassesRouter from './ClassesRouter'; import rest from '../rest'; import Auth from '../Auth'; -import { logIn, removeHiddenProperties, verifyCredentials } from '../Controllers/UserAuthentication'; +import { logIn, logOut, removeHiddenProperties, verifyCredentials } from '../Controllers/UserAuthentication'; export class UsersRouter extends ClassesRouter { @@ -79,23 +79,15 @@ export class UsersRouter extends ClassesRouter { return { response: user }; } - handleLogOut(req) { + async handleLogOut(req) { const success = { response: {} }; + const config = req.config; if (req.info && req.info.sessionToken) { - return rest.find(req.config, Auth.master(req.config), '_Session', - { sessionToken: req.info.sessionToken }, undefined, req.info.clientSDK - ).then((records) => { - if (records.results && records.results.length) { - return rest.del(req.config, Auth.master(req.config), '_Session', - records.results[0].objectId - ).then(() => { - return Promise.resolve(success); - }); - } - return Promise.resolve(success); - }); + const sessionToken = req.info.sessionToken; + const clientSDK = req.info.clientSDK; + logOut(sessionToken, config, clientSDK); } - return Promise.resolve(success); + return success; } _throwOnBadEmailConfig(req) { diff --git a/src/graphql/schemas/UserAuth.js b/src/graphql/schemas/UserAuth.js index 7d1395fa681..46086ab3080 100644 --- a/src/graphql/schemas/UserAuth.js +++ b/src/graphql/schemas/UserAuth.js @@ -2,6 +2,7 @@ import { GraphQLInputObjectType, GraphQLNonNull, GraphQLString, + GraphQLBoolean, } from 'graphql' import { @@ -9,7 +10,7 @@ import { runGet, } from '../execute'; -import { logIn } from '../../Controllers/UserAuthentication'; +import { logIn, logOut } from '../../Controllers/UserAuthentication'; import { loadClass } from './ParseClass'; const getLoginCompletePayload = (schema) => loadClass('_User', schema).objectType; @@ -34,9 +35,18 @@ const login = (schema) => ({ } }); +const logout = { + type: GraphQLBoolean, + resolve: async (root, args, req) => { + await logOut(req.info.sessionToken, req.config, req.info.clientSDK); + return true; + } +} + export function getUserAuthMutationFields(schema) { return { - login: login(schema) + login: login(schema), + logout, }; } From 8dc2d24616adebb2524cd45033d32df67664c1f4 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Mon, 27 Aug 2018 12:29:37 -0400 Subject: [PATCH 49/62] Properly use input object for destroy --- src/graphql/schemas/ParseClass.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index 1e4b7f17bdc..81ca48e78e7 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -223,15 +223,28 @@ export function loadClass(className, schema) { input: { type: new GraphQLInputObjectType({ name: `Destroy${c.displayName}Input`, fields: { - id: { type: new GraphQLNonNull(GraphQLID) }, + id: { type: GraphQLID, description: 'Use either the global id or objectId' }, + objectId: { type: GraphQLID, description: 'Use either the global id or objectId' }, clientMutationId: { type: GraphQLString } } }) } }, resolve: async (root, args, context, info) => { + if (!args.input.id && !args.input.objectId) { + throw 'id or objectId are required'; + } + let objectId; + if (args.input.objectId) { + objectId = args.input.objectId; + delete args.input.objectId; + } else { + objectId = parseID(args.input.id).objectId; + delete args.input.id; + } + const clientMutationId = args.input.clientMutationId; - const object = await runGet(context, info, className, args.input.objectId); - await rest.del(context.config, context.auth, className, args.input.objectId); + const object = await runGet(context, info, className, objectId); + await rest.del(context.config, context.auth, className, objectId); return { object, clientMutationId } } }; From 91a3075c7e92a54fde9522444c35118de8d901ed Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Mon, 27 Aug 2018 16:21:59 -0400 Subject: [PATCH 50/62] Fixes issue that would put the wrong unique ID --- src/graphql/execute.js | 7 ++++++- src/graphql/schemas/ParseClass.js | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/graphql/execute.js b/src/graphql/execute.js index b8ecfa8de37..694b8d8670e 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -1,12 +1,17 @@ import rest from '../rest'; export { rest }; + +export function getGloballyUniqueId(className, objectId) { + return new Buffer(`${className}::${objectId}`).toString('base64'); +} + export function transformResult(className, result) { if (Array.isArray(result)) { return result.map((res) => transformResult(className, res)); } if (result.objectId) { // Make a unique identifier for relay - result.id = new Buffer(`${className}::${result.objectId}`).toString('base64'); + result.id = getGloballyUniqueId(className, result.objectId) } return Object.assign({className}, result); } diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index 81ca48e78e7..8183de2f86f 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -1,4 +1,4 @@ -import { runFind, runGet, rest, transformResult, connectionResultsArray, parseID } from '../execute'; +import { runFind, runGet, rest, transformResult, connectionResultsArray, parseID, getGloballyUniqueId } from '../execute'; import { GraphQLObjectType, @@ -55,6 +55,9 @@ function getRelationField(fieldName, field, schema) { } args.redirectClassNameForKey = fieldName; const results = await runFind(context, info, parent.className, args, schema, query); + results.forEach((result) => { + result.id = getGloballyUniqueId(result.className, result.objectId); + }); return connectionResultsArray(results, args, 100); }; return find; From 72cd01d1f9bc9b0c161919b559b16d3248f6c612 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Mon, 27 Aug 2018 20:24:04 -0400 Subject: [PATCH 51/62] Adds ability to run functions as graphql mutations --- src/Controllers/HooksController.js | 2 +- src/Routers/FunctionsRouter.js | 34 ++++++++------ src/cloud-code/Parse.Cloud.js | 11 ++++- src/graphql/Schema.js | 6 +-- src/graphql/index.js | 17 +++++++ src/graphql/schemas/Functions.js | 73 ++++++++++++++++++++++++++++++ src/triggers.js | 28 ++++++++++-- 7 files changed, 146 insertions(+), 25 deletions(-) create mode 100644 src/graphql/schemas/Functions.js diff --git a/src/Controllers/HooksController.js b/src/Controllers/HooksController.js index 046cc1a295b..056185573df 100644 --- a/src/Controllers/HooksController.js +++ b/src/Controllers/HooksController.js @@ -96,7 +96,7 @@ export class HooksController { if (hook.className) { triggers.addTrigger(hook.triggerName, hook.className, wrappedFunction, this._applicationId) } else { - triggers.addFunction(hook.functionName, wrappedFunction, null, this._applicationId); + triggers.addFunction(hook.functionName, wrappedFunction, null, null, this._applicationId); } } diff --git a/src/Routers/FunctionsRouter.js b/src/Routers/FunctionsRouter.js index e4add79c615..d5a82499c01 100644 --- a/src/Routers/FunctionsRouter.js +++ b/src/Routers/FunctionsRouter.js @@ -110,24 +110,22 @@ export class FunctionsRouter extends PromiseRouter { } } - static handleCloudFunction(req) { - const functionName = req.params.functionName; - const applicationId = req.config.applicationId; + static runFunction(functionName, params, { config, auth, info }, applicationId) { + const theFunction = triggers.getFunction(functionName, applicationId); - const theValidator = triggers.getValidator(req.params.functionName, applicationId); + const theValidator = triggers.getValidator(functionName, applicationId); if (!theFunction) { throw new Parse.Error(Parse.Error.SCRIPT_FAILED, `Invalid function: "${functionName}"`); } - let params = Object.assign({}, req.body, req.query); - params = parseParams(params); + const request = { - params: params, - master: req.auth && req.auth.isMaster, - user: req.auth && req.auth.user, - installationId: req.info.installationId, - log: req.config.loggerController, - headers: req.config.headers, - ip: req.config.ip, + params, + master: auth && auth.isMaster, + user: auth && auth.user, + installationId: info && info.installationId, + log: config.loggerController, + headers: config.headers, + ip: config.ip, functionName }; @@ -139,7 +137,7 @@ export class FunctionsRouter extends PromiseRouter { } return new Promise(function (resolve, reject) { - const userString = (req.auth && req.auth.user) ? req.auth.user.id : undefined; + const userString = (auth && auth.user) ? auth.user.id : undefined; const cleanInput = logger.truncateLogMessage(JSON.stringify(params)); const { success, error, message } = FunctionsRouter.createResponseObject((result) => { try { @@ -177,4 +175,12 @@ export class FunctionsRouter extends PromiseRouter { }).then(success, error); }); } + + static handleCloudFunction(req) { + const functionName = req.params.functionName; + const applicationId = req.config.applicationId; + let params = Object.assign({}, req.body, req.query); + params = parseParams(params); + return FunctionsRouter.runFunction(functionName, params, req, applicationId); + } } diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 142929d7d64..70958d5974e 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -1,5 +1,6 @@ import { Parse } from 'parse/node'; import * as triggers from '../triggers'; +import * as graphQLUtils from '../graphql/index'; function getClassName(parseClass) { if (parseClass && parseClass.className) { @@ -32,7 +33,13 @@ var ParseCloud = {}; * @param {Function} data The Cloud Function to register. This function can be an async function and should take one parameter a {@link Parse.Cloud.FunctionRequest}. */ ParseCloud.define = function(functionName, handler, validationHandler) { - triggers.addFunction(functionName, handler, validationHandler, Parse.applicationId); + let options; + if (typeof handler === 'object') { + options = Object.assign({}, handler); + handler = options.handler; + delete options.handler; + } + triggers.addFunction(functionName, handler, validationHandler, options, Parse.applicationId); }; /** @@ -224,6 +231,8 @@ ParseCloud.useMasterKey = () => { ParseCloud.httpRequest = require("./httpRequest"); +ParseCloud.GraphQLUtils = graphQLUtils; + module.exports = ParseCloud; /** diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index 6b3d566318a..e4913b7df7c 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -4,14 +4,12 @@ import { GraphQLObjectType, } from 'graphql' -import { - clearCache, -} from './typesCache'; import Config from '../Config'; import ParseClassSchema from './schemas/ParseClass'; import UserAuthSchema from './schemas/UserAuth'; import NodeSchema from './schemas/Node'; +import FunctionsSchema from './schemas/Functions'; export class GraphQLParseSchema { schema; @@ -36,7 +34,6 @@ export class GraphQLParseSchema { query: this.Query(), mutation: this.Mutation(), }); - clearCache(); return { schema: graphQLSchema, rootValue: this.Root() }; } @@ -60,6 +57,7 @@ export class GraphQLParseSchema { const fields = {}; Object.assign(fields, UserAuthSchema.Mutation(this.schema)); Object.assign(fields, ParseClassSchema.Mutation(this.schema)); + Object.assign(fields, FunctionsSchema.Mutation(this.schema)); return new GraphQLObjectType({ name: 'Mutation', diff --git a/src/graphql/index.js b/src/graphql/index.js index e69de29bb2d..40ccdfbd9c8 100644 --- a/src/graphql/index.js +++ b/src/graphql/index.js @@ -0,0 +1,17 @@ +import { + loadClass +} from './schemas/ParseClass'; + +export function getObjectType(name) { + return loadClass(name).objectType; +} + +export function getCreateInputType(name) { + return loadClass(name).inputType; +} + +export function getUpdateInputType(name) { + return loadClass(name).updateType; +} + +export * from 'graphql'; diff --git a/src/graphql/schemas/Functions.js b/src/graphql/schemas/Functions.js new file mode 100644 index 00000000000..bb65fc0f099 --- /dev/null +++ b/src/graphql/schemas/Functions.js @@ -0,0 +1,73 @@ +import { getAllFunctions } from '../../triggers'; +import { FunctionsRouter } from '../../Routers/FunctionsRouter'; +import { GraphQLBoolean } from 'graphql'; +import { getGloballyUniqueId } from '../execute'; + +function setID(object) { + if (object.objectId && object.className) { + object.id = getGloballyUniqueId(object.className, object.objectId); + } +} + +function getFunctions() { + const functions = getAllFunctions(); + const fields = {}; + Object.keys(functions).forEach((name) => { + const options = functions[name].options || {}; + let type = GraphQLBoolean; + let inputType; + let useDefaultType = true; + if (options && options.type) { + type = options.type; + if (typeof type === 'function') { + type = type(); + } + useDefaultType = false; + } + + if (options && options.inputType) { + inputType = options.inputType; + if (typeof inputType === 'function') { + inputType = inputType(); + } + } + let args; + if (inputType) { + args = { input: { type: inputType }}; + } + const description = options.description || 'Calling this mutation will run the cloud function'; + fields[name] = { + type, + description, + args, + resolve: async (root, args, req) => { + const results = await FunctionsRouter.runFunction(name, args.input, req); + const result = results.response.result; + injectIdsInResults(result); + if (useDefaultType) { + return true; + } + return result; + } + } + }); + return fields; +} + +function injectIdsInResults(result) { + if (Array.isArray(result)) { + result.forEach(injectIdsInResults); + } else if (typeof result === 'object') { + if (result.objectId && result.className) { + setID(result); + } + Object.keys(result).forEach((key) => { + injectIdsInResults(result[key]); + }); + } + +} + +export default { + Mutation: getFunctions +} diff --git a/src/triggers.js b/src/triggers.js index c8c5435e426..30933533b49 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -14,6 +14,7 @@ export const Types = { const baseStore = function() { const Validators = {}; const Functions = {}; + const FunctionOptions = {}; const Jobs = {}; const LiveQuery = []; const Triggers = Object.keys(Types).reduce(function(base, key){ @@ -25,6 +26,7 @@ const baseStore = function() { Functions, Jobs, Validators, + FunctionOptions, Triggers, LiveQuery, }); @@ -49,16 +51,20 @@ const _triggerStore = {}; const Category = { Functions: 'Functions', Validators: 'Validators', + FunctionOptions: 'FunctionOptions', Jobs: 'Jobs', Triggers: 'Triggers' } -function getStore(category, name, applicationId) { - const path = name.split('.'); - path.splice(-1); // remove last component +function getStore(category, name = null, applicationId) { applicationId = applicationId || Parse.applicationId; _triggerStore[applicationId] = _triggerStore[applicationId] || baseStore(); let store = _triggerStore[applicationId][category]; + if (!name) { + return store; + } + const path = name.split('.'); + path.splice(-1); // remove last component for (const component of path) { store = store[component]; if (!store) { @@ -86,9 +92,10 @@ function get(category, name, applicationId) { return store[lastComponent]; } -export function addFunction(functionName, handler, validationHandler, applicationId) { +export function addFunction(functionName: string, handler: (any) => ?Promise, validationHandler: (any) => ?void, options: any, applicationId: string) { add(Category.Functions, functionName, handler, applicationId); add(Category.Validators, functionName, validationHandler, applicationId); + add(Category.FunctionOptions, functionName, options, applicationId); } export function addJob(jobName, handler, applicationId) { @@ -133,6 +140,18 @@ export function getFunction(functionName, applicationId) { return get(Category.Functions, functionName, applicationId); } +export function getAllFunctions(applicationId) { + const functionStore = getStore(Category.Functions, null, applicationId); + const optionsStore = getStore(Category.FunctionOptions, null, applicationId); + const validatorStore = getStore(Category.Validators, null, applicationId); + return Object.keys(functionStore).reduce((memo, functionName) => { + memo[functionName] = { handler: functionStore[functionName], + options: optionsStore[functionName], + validator: validatorStore[functionName] }; + return memo; + }, {}); +} + export function getJob(jobName, applicationId) { return get(Category.Jobs, jobName, applicationId); } @@ -145,7 +164,6 @@ export function getJobs(applicationId) { return undefined; } - export function getValidator(functionName, applicationId) { return get(Category.Validators, functionName, applicationId); } From 27fb0545ae462038698323ada0740b415f770365 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Mon, 27 Aug 2018 20:24:36 -0400 Subject: [PATCH 52/62] Adds async method to get inferred schema --- src/ParseServer.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index b9448041214..73073c0a28e 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -134,6 +134,11 @@ class ParseServer { } } + static async getGraphQLSchema() { + const graphQLSchema = new GraphQLParseSchema(Parse.applicationId); + return await graphQLSchema.load(); + } + static graphqlHTTP({ graphiql }) { return graphqlHTTP(async (req) => { // TODO: use middleware please :) @@ -152,8 +157,7 @@ class ParseServer { } // TODO: only await perhaps once, and optimize perf - const graphQLSchema = new GraphQLParseSchema(Parse.applicationId); - const { schema, rootValue } = await graphQLSchema.load(); + const { schema, rootValue } = await ParseServer.getGraphQLSchema(); return { schema, rootValue, From 6f6c0859e6a5929ebd2978405fb0e92c0299dc29 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Wed, 29 Aug 2018 14:13:40 -0400 Subject: [PATCH 53/62] moves export --- src/graphql/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graphql/index.js b/src/graphql/index.js index 40ccdfbd9c8..cf1c2694055 100644 --- a/src/graphql/index.js +++ b/src/graphql/index.js @@ -1,3 +1,5 @@ +export * from 'graphql'; + import { loadClass } from './schemas/ParseClass'; @@ -13,5 +15,3 @@ export function getCreateInputType(name) { export function getUpdateInputType(name) { return loadClass(name).updateType; } - -export * from 'graphql'; From 6ffaf67b128290e0e0708341eb24ae823f315f98 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Wed, 29 Aug 2018 22:46:47 -0400 Subject: [PATCH 54/62] Adds test suite for graphql --- package-lock.json | 2 +- spec/graphQLBridge.spec.js | 231 --------------------- spec/graphqlCRUD.spec.js | 320 ++++++++++++++++++++++++++++++ spec/graphqlFunctions.spec.js | 150 ++++++++++++++ spec/graphqlTypes.spec.js | 255 ++++++++++++++++++++++++ src/cloud-code/Parse.Cloud.js | 28 ++- src/graphql/Schema.js | 12 +- src/graphql/execute.js | 22 ++ src/graphql/schemas/Functions.js | 14 +- src/graphql/schemas/ParseClass.js | 134 ++++++------- src/graphql/types/ACL.js | 26 +-- src/graphql/types/BaseQuery.js | 40 ---- src/graphql/types/Date.js | 14 +- src/graphql/types/NumberInput.js | 12 +- src/graphql/types/index.js | 35 ++-- 15 files changed, 872 insertions(+), 423 deletions(-) delete mode 100644 spec/graphQLBridge.spec.js create mode 100644 spec/graphqlCRUD.spec.js create mode 100644 spec/graphqlFunctions.spec.js create mode 100644 spec/graphqlTypes.spec.js diff --git a/package-lock.json b/package-lock.json index 90a971278d7..64713a4bf08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5859,7 +5859,7 @@ }, "lodash": { "version": "3.10.1", - "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true }, diff --git a/spec/graphQLBridge.spec.js b/spec/graphQLBridge.spec.js deleted file mode 100644 index c10b183d2f5..00000000000 --- a/spec/graphQLBridge.spec.js +++ /dev/null @@ -1,231 +0,0 @@ -/*eslint-disable*/ -const GraphQLParseSchema = require('../lib/graphql/Schema').GraphQLParseSchema; -const Config = require('../lib/Config'); -const Auth = require('../lib/Auth').Auth; -const { /*print, */printSchema, graphql } = require('graphql'); -const { addFunction } = require('../lib/triggers'); -//const { /*print, printSchema,*/ GraphQLID, GraphQLNonNull } = require('graphql'); - -let config; - -function setup(config) { - let schema; - return config.database.loadSchema() - .then(dbSchema => { - schema = dbSchema; - return dbSchema.addClassIfNotExists('NewClass', { - foo: { type: 'String' }, - bar: { type: 'Boolean' }, - increment: { type: 'Number' }, - other: { type: 'Pointer', targetClass: 'OtherClass' } - }).then(() => { - return dbSchema.addClassIfNotExists('OtherClass', { - foo: { type: 'String' }, - baz: { type: 'Pointer', targetClass: 'NewClass' } - }) - }).then(() => { - return schema.getAllClasses(true); - }); - }) - .then((allClasses) => { - const fullSchema = allClasses.reduce((memo, classDef) => { - memo[classDef.className] = classDef; - return memo; - }, {}); - - const Schema = new GraphQLParseSchema(fullSchema, 'test'); - const s = Schema.Schema(); - const root = Schema.Root(); - return { - schema: s, - root, - } - }); -} - -describe('graphQLbridge', () => { - let schema; - let root; - beforeEach((done) => { - config = Config.get('test'); - setup(config).then((result) => { - schema = result.schema; - root = result.root; - }).then(done); - }); - - it('base test', (done) => { - console.log(printSchema(schema)); - done(); - /* - ` - mutation { - NewClass { - create(foo: "hello", bar: false, increment: 1) { - objectId, foo, bar, increment - } - } - }` - ...on NewClass { objectId, foo, bar, increment } - */ - - /*return graphql(s,` - mutation { - create(className: "NewClass", params: {foo: "Bar", bar: true, increment: 10}) { - objectId, - createdAt, - updatedAt, - ... on Object { data } - } - }`, root, context).then((res) => { - console.log(res); - const objectId = res.data.create.objectId; - return graphql(s, ` - mutation myMutation($id: ID!) { - NewClass { - update(objectId: $id, incrementKey: { key: "increment", value: 2 }) { - objectId, increment - } - } - }`, root, context, {id: objectId}); - })*/ - /* - query { - OtherClass { - objectId, - baz { - foo, - bar - } - }, - OtherClass { - foo, objectId - } - } - mutation { - NewClass: createNewClass(input: { - foo: "hello", - bar: false, - increment: 1 - }) { - objectId, foo, bar, increment - } - - NewClass: createNewClass(input: { - foo: "hello", - bar: false, - increment: 1 - }) { - objectId, foo, bar, increment - } - } - */ - - /*return graphql(s, ` - query { - NewClass(where: {}) { - objectId - } - OtherClass(where: {}) { - objectId, - foo, - baz { - objectId, - increment, - other { - foo, - objectId, - baz { - objectId - } - } - } - } - } - `,root, context)*/ - }); - - it('test query', (done) => { - const context = { - config, - auth: new Auth({config}) - } - let obj = new Parse.Object('OtherClass', {foo: 'baz'}); - let nc = new Parse.Object('NewClass', {foo: 'hello'}); - return Parse.Object.saveAll([obj, nc]).then(() => { - nc.set('other', obj); - obj.set('baz', nc); - return Parse.Object.saveAll([obj, nc]); - }).then(() => { - return graphql(schema, ` - query { - NewClass { - objectId - } - OtherClass { - objectId, - foo, - baz { - objectId, - increment, - foo, - other { - foo, - objectId, - baz { - objectId - } - } - } - } - } - `,root, context) - }).then((res) => { - expect(res.data.NewClass).toBeDefined(); - expect(res.data.OtherClass).toBeDefined(); - expect(Array.isArray(res.data.NewClass)).toBeTruthy(); - expect(Array.isArray(res.data.OtherClass)).toBeTruthy(); - const newClasses = res.data.NewClass; - // Should only have objectId - newClasses.forEach((object) => { - expect(Object.keys(object).length).toBe(1); - expect(object.objectId).toBeDefined(); - }); - - const otherClasses = res.data.OtherClass; - const otherObject = otherClasses[0]; - expect(otherObject.objectId).toEqual(otherObject.baz.other.objectId); - expect(otherObject.baz.objectId).toEqual(otherObject.baz.other.baz.objectId); - }).then(done).catch(done.fail); - }) - - it('test destroy', (done) => { - const context = { - config, - auth: new Auth({config}) - } - let obj = new Parse.Object('OtherClass', {foo: 'baz'}); - let nc = new Parse.Object('NewClass', {foo: 'hello'}); - return Parse.Object.saveAll([obj, nc]).then(() => { - nc.set('other', obj); - obj.set('baz', nc); - return Parse.Object.saveAll([obj, nc]); - }).then(() => { - return graphql(schema, ` - mutation myMutation($id: ID!) { - destroyNewClass(objectId: $id) { - objectId, - bar - } - } - `, root, context, {id: nc.id}) - }).then((res) => { - console.log('GOT RES!'); - console.log(JSON.stringify(res, null, 2)); - done(); - }).catch((err) => { - console.error(err); - done.fail(); - }); - }); -}); diff --git a/spec/graphqlCRUD.spec.js b/spec/graphqlCRUD.spec.js new file mode 100644 index 00000000000..60b056e9aff --- /dev/null +++ b/spec/graphqlCRUD.spec.js @@ -0,0 +1,320 @@ +const GraphQLParseSchema = require('../lib/graphql/Schema').GraphQLParseSchema; +const Config = require('../lib/Config'); +const Auth = require('../lib/Auth').Auth; +const { graphql } = require('graphql'); +const { containsOnlyIdFields } = require('../lib/graphql/execute'); + +let config; + +async function setup(config) { + const schema = await config.database.loadSchema(); + await schema.addClassIfNotExists('NewClass', { + stringValue: { type: 'String' }, + booleanValue: { type: 'Boolean' }, + numberValue: { type: 'Number' }, + arrayValue: { type: 'Array' }, + file: { type: 'File' }, + geoPointValue: { type: 'GeoPoint' }, + dateValue: { type: 'Date' }, + others: { type: 'Relation', targetClass: 'OtherClass' } + }); + await schema.addClassIfNotExists('OtherClass', { + otherString: { type: 'String' }, + newClass: { type: 'Pointer', targetClass: 'NewClass' } + }); + const Schema = new GraphQLParseSchema('test'); + return await Schema.load(); +} + +describe('graphQLCRUD', () => { + let schema; + let root; + beforeEach(async () => { + config = Config.get('test'); + const result = await setup(config); + schema = result.schema; + root = result.root; + }); + + it('Adds objects', async () => { + const context = { + config, + auth: new Auth({config}) + } + const ACL = new Parse.ACL(); + ACL.setPublicReadAccess(true); + ACL.setPublicWriteAccess(true); + const dateValue = new Date('2018-09-01'); + const input = { + newClass: { + stringValue: 'Hello World!', + booleanValue: true, + numberValue: -1, + dateValue, + ACL, + geoPointValue: { latitude: 20.0, longitude: 10.0 }, + } + }; + const createResult = await graphql(schema, ` + mutation myMutation($newClass: AddNewClassInput!) { + addNewClass(input: $newClass) { + object { + createdAt + updatedAt + objectId, + stringValue + numberValue, + booleanValue + dateValue, + ACL, + geoPointValue { + latitude + longitude + } + } + } + } + `, root, context, input); + expect(createResult.errors).not.toBeDefined(); + expect(createResult.data).not.toBeNull(); + const { object } = createResult.data.addNewClass; + expect(object).toBeDefined(); + expect(object.objectId).toBeDefined(); + expect(object.createdAt).toBeDefined(); + expect(object.createdAt instanceof Date).toBeTruthy(); + expect(object.updatedAt).toBeDefined(); + expect(object.updatedAt instanceof Date).toBeTruthy(); + expect(object).toEqual({ + objectId: object.objectId, + stringValue: 'Hello World!', + booleanValue: true, + numberValue: -1, + dateValue: dateValue, + ACL: { '*': { read: true, write: true }}, + createdAt: object.createdAt, + updatedAt: object.updatedAt, + geoPointValue: { + latitude: 20.0, + longitude: 10.0 + } + }); + }); + + it('Queries objects', (done) => { + const context = { + config, + auth: new Auth({config}) + } + const publicACL = new Parse.ACL(); + publicACL.setPublicReadAccess(true); + publicACL.setPublicWriteAccess(true); + const obj = new Parse.Object('OtherClass', {stringValue: 'baz'}); + const nc = new Parse.Object('NewClass', {stringValue: 'hello'}); + nc.setACL(publicACL); + return Parse.Object.saveAll([obj, nc]).then(() => { + nc.relation('others').add(obj); + obj.set('newClass', nc); + return Parse.Object.saveAll([obj, nc]); + }).then(() => { + return graphql(schema, ` + query { + NewClass: findNewClass { + nodes { + objectId + ACL + } + } + OtherClass: findOtherClass { + nodes { + objectId, + otherString, + newClass { + objectId, + stringValue, + booleanValue + numberValue, + others { + nodes { + otherString, + objectId, + newClass { + objectId + id + } + } + } + } + } + } + } + `,root, context) + }).then((res) => { + expect(res.data.NewClass).toBeDefined(); + expect(res.data.OtherClass).toBeDefined(); + expect(Array.isArray(res.data.NewClass.nodes)).toBeTruthy(); + expect(Array.isArray(res.data.OtherClass.nodes)).toBeTruthy(); + const newClasses = res.data.NewClass.nodes; + // Should only have objectId + newClasses.forEach((object) => { + expect(Object.keys(object).length).toBe(2); + expect(object.objectId).toBeDefined(); + expect(object.ACL).toEqual({ + '*': { 'read': true, 'write': true } + }) + }); + + const otherClasses = res.data.OtherClass.nodes; + const otherObject = otherClasses[0]; + expect(otherObject.objectId).not.toBeUndefined(); + expect(otherObject.newClass.objectId).not.toBeUndefined(); + expect(otherObject.objectId).toEqual(otherObject.newClass.others.nodes[0].objectId); + expect(otherObject.newClass.objectId).toEqual(otherObject.newClass.others.nodes[0].newClass.objectId); + }).then(done).catch(done.fail); + }); + + it('Gets object from Node', async () => { + const context = { + config, + auth: new Auth({config}) + } + const obj = new Parse.Object('OtherClass', {otherString: 'aStringValue'}); + await obj.save(); + const result = await graphql(schema, ` + query myQuery($id: ID!) { + node(id: $id) { + ... on OtherClass { + objectId, + otherString + } + } + } + `, root, context, { id: new Buffer(`OtherClass::${obj.id}`).toString('base64') }); + expect(result.errors).toBeUndefined(); + expect(result.data.node.otherString).toBe('aStringValue'); + expect(result.data.node.objectId).toBe(obj.id); + }); + + it('Updates objects', async () => { + const context = { + config, + auth: new Auth({config}) + } + const obj = new Parse.Object('OtherClass', {otherString: 'baz'}); + await obj.save(); + const result = await graphql(schema, ` + mutation myMutation($input: UpdateOtherClassInput!) { + updateOtherClass(input: $input) { + object { + objectId, + otherString + } + } + } + `, root, context, {input: { objectId: obj.id, otherString: 'newStringValue'}}); + expect(result.errors).toBeUndefined(); + expect(result.data.updateOtherClass.object.otherString).toBe('newStringValue'); + }); + + it('Updates objects with uniqueID', async () => { + const context = { + config, + auth: new Auth({config}) + } + const obj = new Parse.Object('OtherClass', {otherString: 'baz'}); + const nc = new Parse.Object('NewClass', {stringValue: 'aString'}); + await Parse.Object.saveAll([obj, nc]); + const input = { + id: new Buffer(`OtherClass::${obj.id}`).toString('base64'), + otherString: 'newStringValue', + newClass: { objectId: nc.id }, + }; + const result = await graphql(schema, ` + mutation myMutation($input: UpdateOtherClassInput!) { + updateOtherClass(input: $input) { + object { + objectId, + otherString + newClass { + stringValue + } + } + } + } + `, root, context, { input }); + expect(result.errors).toBeUndefined(); + expect(result.data.updateOtherClass.object.otherString).toBe('newStringValue'); + expect(result.data.updateOtherClass.object.objectId).toBe(obj.id); + expect(result.data.updateOtherClass.object.newClass.stringValue).toBe('aString'); + }); + + it('fails to update object without id', async () => { + const context = { + config, + auth: new Auth({config}) + } + + const result = await graphql(schema, ` + mutation myMutation($input: UpdateOtherClassInput!) { + updateOtherClass(input: $input) { + object { + objectId, + otherString + } + } + } + `, root, context, {input: {otherString: 'newStringValue'}}); + expect(result.errors).not.toBeUndefined(); + expect(result.errors.length).toBe(1); + expect(result.errors[0].message).toBe('id or objectId are required'); + }); + + it('Destroy objects', async (done) => { + const context = { + config, + auth: new Auth({config}) + } + const obj = new Parse.Object('OtherClass', {stringValue: 'baz'}); + const nc = new Parse.Object('NewClass', {stringValue: 'hello'}); + await Parse.Object.saveAll([obj, nc]) + nc.set('other', obj); + obj.set('baz', nc); + await Parse.Object.saveAll([obj, nc]); + const result = await graphql(schema, ` + mutation myMutation($id: ID!) { + destroyNewClass(input: { objectId: $id }) { + object { + objectId, + stringValue + } + } + } + `, root, context, {id: nc.id}); + expect(result.errors).toBeUndefined(); + expect(result.data.destroyNewClass.object).toEqual({ + objectId: nc.id, + stringValue: 'hello' + }); + + const newClassObject = await graphql(schema, ` + query getNewClass($id: ID!) { + NewClass(objectId: $id) { + objectId, + stringValue + } + } + `, root, context, {id: nc.id}); + expect(newClassObject.data.NewClass).toBeNull(); + done(); + }); +}); + +describe('Pointer fetching', () => { + it('ensures containsOnlyIdFields works', () => { + expect(containsOnlyIdFields(['id'])).toBeTruthy(); + expect(containsOnlyIdFields(['objectId'])).toBeTruthy() + expect(containsOnlyIdFields(['id', 'objectId'])).toBeTruthy(); + expect(containsOnlyIdFields(['objectId', 'yolo'])).toBeFalsy(); + expect(containsOnlyIdFields(['yolo'])).toBeFalsy(); + expect(containsOnlyIdFields(['yolo', 'id'])).toBeFalsy(); + }); +}); diff --git a/spec/graphqlFunctions.spec.js b/spec/graphqlFunctions.spec.js new file mode 100644 index 00000000000..b78dbeb63f7 --- /dev/null +++ b/spec/graphqlFunctions.spec.js @@ -0,0 +1,150 @@ +const GraphQLParseSchema = require('../lib/graphql/Schema').GraphQLParseSchema; +const Config = require('../lib/Config'); +const Auth = require('../lib/Auth').Auth; +const { graphql, GraphQLInt, GraphQLInputObjectType, GraphQLObjectType, GraphQLList } = require('graphql'); +let config; + +async function setup(config) { + const schema = await config.database.loadSchema(); + await schema.addClassIfNotExists('NewClass', { + stringValue: { type: 'String' }, + booleanValue: { type: 'Boolean' }, + numberValue: { type: 'Number' }, + arrayValue: { type: 'Array' }, + file: { type: 'File' }, + geoPointValue: { type: 'GeoPoint' }, + dateValue: { type: 'Date' }, + others: { type: 'Relation', targetClass: 'OtherClass' } + }); + await schema.addClassIfNotExists('OtherClass', { + otherString: { type: 'String' }, + newClass: { type: 'Pointer', targetClass: 'NewClass' } + }); +} + +describe('graphQLbridge', () => { + let schema; + let root; + async function reload() { + const Schema = new GraphQLParseSchema('test'); + const result = await Schema.load(); + schema = result.schema; + root = result.root; + } + beforeEach(async () => { + config = Config.get('test'); + await setup(config); + }); + + it('runs basic functions', async () => { + Parse.Cloud.define('health', () => { + return 'OK'; + }); + await reload(); + const context = { + config, + auth: new Auth({config}) + } + const result = await graphql(schema, ` + mutation callFunction { + health + } + `, root, context); + expect(result.errors).toBeUndefined(); + expect(result.data.health).toBe(true); + }); + + it('runs functions with params', async () => { + Parse.Cloud.define('squareUp', { + description: 'Call this function to square up!', + type: new GraphQLObjectType({ + name: 'CustomType', + fields: { + result: { type: GraphQLInt }, + } + }), + inputType: new GraphQLInputObjectType({ + name: 'CustomInputType', + fields: { + request: { type: GraphQLInt } + } + }), + handler: (req) => { + const { params } = req; + return { + result: params.request * params.request + } + } + }); + await reload(); + + const context = { + config, + auth: new Auth({config}) + } + const result = await graphql(schema, ` + mutation callFunction { + squareUp(input: { request: 15 }) { + result + } + } + `, root, context); + expect(result.errors).toBeUndefined(); + expect(result.data.squareUp.result).toBe(225); + }); + + it('runs functions that provide parse types', async () => { + Parse.Cloud.define('getAllNewClasses', { + description: 'Call this function to square up!', + type: new GraphQLObjectType({ + name: 'CustomType', + fields: () => ({ + nodes: { type: new GraphQLList(Parse.Cloud.GraphQLUtils.getObjectType('NewClass')) }, + }) + }), + inputType: new GraphQLInputObjectType({ + name: 'CustomInputType', + fields: { + min: { type: GraphQLInt } + } + }), + handler: async (req) => { + const query = new Parse.Query('NewClass'); + query.greaterThan('numberValue', req.params.min); + return { + nodes: await query.find() + } + } + }); + await reload(); + + const context = { + config, + auth: new Auth({config}) + } + const objects = []; + while (objects.length < 10) { + const obj = new Parse.Object('NewClass'); + obj.set('numberValue', objects.length); + objects.push(obj); + } + + await Parse.Object.saveAll(objects); + const result = await graphql(schema, ` + mutation callFunction { + getAllNewClasses(input: { min: 3 }) { + nodes { + id + objectId + numberValue + } + } + } + `, root, context); + expect(result.errors).toBeUndefined(); + expect(result.data.getAllNewClasses.nodes.length).toBe(6); + result.data.getAllNewClasses.nodes.forEach((node) => { + expect(node.numberValue).toBeGreaterThan(3); + }); + }); +}); diff --git a/spec/graphqlTypes.spec.js b/spec/graphqlTypes.spec.js new file mode 100644 index 00000000000..284a53765aa --- /dev/null +++ b/spec/graphqlTypes.spec.js @@ -0,0 +1,255 @@ +const { NumberInput } = require('../lib/graphql/types/NumberInput'); +const { ACL } = require('../lib/graphql/types/ACL'); +const { JSONObject } = require('../lib/graphql/types/JSONObject'); +const DateType = require('../lib/graphql/types/Date'); + +const { + Kind, +} = require('graphql'); + +describe('NumberInput', () => { + it('should parse litteral with regular values', () => { + let result = NumberInput.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ + name: { value: 'increment' }, + value: { value: 1 } + }] + }); + expect(result).toEqual({ + __op: 'Increment', + amount: 1 + }); + result = NumberInput.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ + name: { value: 'increment' }, + value: { value: '10' } + }] + }); + expect(result).toEqual({ + __op: 'Increment', + amount: 10 + }); + + result = NumberInput.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ + name: { value: 'increment' }, + value: { value: -2 } + }] + }); + expect(result).toEqual({ + __op: 'Increment', + amount: -2 + }); + + result = NumberInput.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ + name: { value: 'increment' }, + value: { value: '-5' } + }] + }); + expect(result).toEqual({ + __op: 'Increment', + amount: -5 + }); + }); + + it('should fail to parse litteral if kind is missing', () => { + expect(() => { + NumberInput.parseLiteral({ + fields: [{ + name: { value: 'increment' }, + value: { value: '-5' } + }] + }); + }).toThrow('Invalid literal for NumberInput'); + }); + + it('should fail to parse litteral if too many fields are passed', () => { + expect(() => { + NumberInput.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ + name: { value: 'increment' }, + value: { value: '-5' } + }, { + name: { value: 'other' }, + value: { value: '-5' } + }] + }); + }).toThrow('Invalid literal for NumberInput (too many fields)'); + }); + + it('should fail to parse litteral if the wrong operator is passed', () => { + expect(() => { + NumberInput.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ + name: { value: 'badOperator' }, + value: { value: '-5' } + }] + }); + }).toThrow('the badOperator operator is not supported'); + }); + + it('should parse int and floats as litteral values', () => { + expect(NumberInput.parseLiteral({ + kind: Kind.FLOAT, + value: 10 + })).toBe(10); + expect(NumberInput.parseLiteral({ + kind: Kind.INT, + value: 5 + })).toBe(5); + }); + + it('should return values using serialize and parseValue', () => { + const value = 10.34; + expect(NumberInput.parseValue(value)).toBe(value); + expect(NumberInput.serialize(value)).toBe(value); + }); +}); + +describe('ACL', () => { + it('should parse Parse.ACL', () => { + expect(ACL.parseValue(new Parse.ACL())).toEqual({}); + + const publicACL = new Parse.ACL(); + publicACL.setPublicReadAccess(true); + expect(ACL.parseValue(publicACL)).toEqual({ '*': { 'read': true }}); + + const userACL = new Parse.ACL(); + userACL.setReadAccess('abcdef', true); + userACL.setWriteAccess('abcdef', true); + expect(ACL.parseValue(userACL)).toEqual({ 'abcdef': { 'read': true, 'write': true }}); + + const roleACL = new Parse.ACL(); + roleACL.setReadAccess('abcdef', true); + roleACL.setRoleWriteAccess('Admin', true); + expect(ACL.parseValue(roleACL)).toEqual({ 'abcdef': { 'read': true }, 'role:Admin': { 'write': true }}); + }); + + it('should throw when passing bad values', () => { + expect(() => { + console.log(ACL.parseValue(null)); + }).toThrow('Invalid ACL value, should be a Parse.ACL'); + + expect(() => { + ACL.parseValue('hello world'); + }).toThrow('Invalid ACL value, should be a Parse.ACL'); + + expect(() => { + ACL.parseLiteral(); + }).toThrow('not implemented'); + }); +}); + +describe('JSONObject', () => { + it('should parse a JSONObject', () => { + expect(JSONObject.parseLiteral({ + kind: Kind.STRING, + value: 'Hello' + })).toBe('Hello'); + expect(JSONObject.parseLiteral({ + kind: Kind.BOOLEAN, + value: true + })).toBe(true); + expect(JSONObject.parseLiteral({ + kind: Kind.BOOLEAN, + value: false + })).toBe(false); + expect(JSONObject.parseLiteral({ + kind: Kind.INT, + value: 1 + })).toBe(parseFloat(1)); + expect(JSONObject.parseLiteral({ + kind: Kind.FLOAT, + value: 0.1 + 0.2 + })).toBe(parseFloat(0.1 + 0.2)); + expect(JSONObject.parseLiteral({ + kind: Kind.LIST, + values: [{ value: 'a', kind: Kind.STRING }, { value: 1.0, kind: Kind.FLOAT }] + })).toEqual(['a', 1.0]); + expect(JSONObject.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ name: { value: 'string' }, value: { value: 'a', kind: Kind.STRING } }, { name: { value: 'floatKey' }, value: { value: 1.0, kind: Kind.FLOAT } }] + })).toEqual({ + string: 'a', + floatKey: 1.0 + }); + expect(JSONObject.parseLiteral({ + kind: Kind.VARIABLE, + name: { value: 'myVariable' } + }, { myVariable: 'myValue' })).toEqual('myValue'); + expect(JSONObject.parseLiteral({ + kind: Kind.VARIABLE, + name: { value: 'myVariable' } + })).toBeUndefined(); + expect(JSONObject.parseLiteral({ + kind: Kind.NULL + })).toBe(null); + expect(JSONObject.parseLiteral({ + kind: 'unknown kind' + })).toBeUndefined(); + }); + + it('should use identity for parseValue and serialize', () => { + const anyObject = Object.create(null); + expect(JSONObject.serialize(anyObject)).toBe(anyObject); + expect(JSONObject.parseValue(anyObject)).toBe(anyObject); + }); +}); + +describe('Date', () => { + it('should parse date from parse style object', () => { + const isoDate = new Date().toISOString(); + expect(DateType.Date.serialize({ + __type: 'Date', + iso: isoDate + })).toEqual(new Date(isoDate)); + }); + + it('should parse date from iso string', () => { + const isoDate = new Date().toISOString(); + expect(DateType.Date.serialize(isoDate)) + .toEqual(new Date(isoDate)); + }); + + it('should parse date from timestamp', () => { + const ts = new Date().getTime(); + expect(DateType.Date.serialize(ts)) + .toEqual(new Date(ts)); + }); + + it('should throw an error when passing an invalid value', () => { + expect(() => DateType.Date.serialize(false)) + .toThrow('Cannot serialize date'); + }); + + it('should parse the date value from ISO string', () => { + const isoDate = new Date().toISOString(); + expect(DateType.Date.parseValue(isoDate)) + .toEqual({ __type: 'Date', iso: isoDate }); + }); + + it('should parse the date value from timestamp', () => { + const date = new Date(); + const ts = date.getTime(); + expect(DateType.Date.parseValue(ts)) + .toEqual({ __type: 'Date', iso: date.toISOString() }); + }); + + it('should parse from string litteral', () => { + const isoDate = new Date().toISOString(); + expect(DateType.Date.parseLiteral({ kind: Kind.STRING, value: isoDate })) + .toEqual({ __type: 'Date', iso: isoDate }); + }); + + it('should fail to parse from invalid litteral', () => { + expect(() => DateType.Date.parseLiteral({ kind: 'invalid type' })) + .toThrow('Cannot parse date of type invalid type') + }); +}); diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 70958d5974e..2fa8c981565 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -26,11 +26,11 @@ var ParseCloud = {}; * Defines a Cloud Function. * * **Available in Cloud Code only.** - - * @static - * @memberof Parse.Cloud + * @method define + * @name Parse.Cloud.define * @param {String} name The name of the Cloud Function - * @param {Function} data The Cloud Function to register. This function can be an async function and should take one parameter a {@link Parse.Cloud.FunctionRequest}. + * @param {Function|Parse.Cloud.FunctionHandler} handler The Cloud Function to register. This function can be an async function and should take one parameter a {@link Parse.Cloud.FunctionRequest}. + * The handler can also be a {@link Parse.Cloud.FunctionHandler} object, used when exposing GraphQL mutations */ ParseCloud.define = function(functionName, handler, validationHandler) { let options; @@ -282,8 +282,28 @@ module.exports = ParseCloud; * @property {Object} params The params passed to the cloud function. */ +/** + * @interface Parse.Cloud.FunctionHandler + * @property {Function} handler The function to be called. it takes a single {@link Parse.Cloud.FunctionRequest} argument. + * @property {external:GraphQLInputObjectType} inputType The type to expose to graphQL. This type is describing the params object passed to the function. + * @property {external:GraphQLObjectType} type The graphQL type returned by the function. + * @property {String} description The description used in the graphql schema. + */ + /** * @interface Parse.Cloud.JobRequest * @property {Object} params The params passed to the background job. * @property {function} message If message is called with a string argument, will update the current message to be stored in the job status. */ + +/** + * GraphQLInputObjectType + * @external GraphQLInputObjectType + * @see {@link https://graphql.org/graphql-js/type/#graphqlinputobjecttype} + */ + +/** + * GraphQLObjectType + * @external GraphQLObjectType + * @see {@link https://graphql.org/graphql-js/type/#graphqlobjecttype} + */ diff --git a/src/graphql/Schema.js b/src/graphql/Schema.js index e4913b7df7c..a06dea0c3f4 100644 --- a/src/graphql/Schema.js +++ b/src/graphql/Schema.js @@ -19,8 +19,8 @@ export class GraphQLParseSchema { this.applicationId = applicationId; } - async load() { - const schema = await Config.get(this.applicationId).database.loadSchema(); + static async loadSchemaFromDatabase(applicationId) { + const schema = await Config.get(applicationId).database.loadSchema(); const allClasses = await schema.getAllClasses(true); const classNames = []; const fullSchema = allClasses.reduce((memo, classDef) => { @@ -29,7 +29,11 @@ export class GraphQLParseSchema { return memo; }, {}); fullSchema.__classNames = classNames; - this.schema = Object.freeze(fullSchema); + return Object.freeze(fullSchema); + } + + async load() { + this.schema = await GraphQLParseSchema.loadSchemaFromDatabase(this.applicationId); const graphQLSchema = new GraphQLSchema({ query: this.Query(), mutation: this.Mutation(), @@ -52,8 +56,6 @@ export class GraphQLParseSchema { } Mutation() { - // TODO: Refactor FunctionRouter to extract (as it's a new entry) - // TODO: Implement Functions as mutations const fields = {}; Object.assign(fields, UserAuthSchema.Mutation(this.schema)); Object.assign(fields, ParseClassSchema.Mutation(this.schema)); diff --git a/src/graphql/execute.js b/src/graphql/execute.js index 694b8d8670e..93127ea41dd 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -157,3 +157,25 @@ export function runGet(context, info, className, objectId) { .then(toGraphQLResult(className)) .then(results => results[0]); } + +export function resolvePointer(targetClass, object, schema, context, info) { + const selections = info.fieldNodes[0].selectionSet.selections.map((field) => { + return field.name.value; + }); + if (containsOnlyIdFields(selections)) { + return transformResult(targetClass, object, schema, { context, info }); + } + + return runGet(context, info, object.className, object.objectId, schema); +} + +export function containsOnlyIdFields(selections) { + // id and objectId are always available + // In this case we avoid making a fetch + // as the caller doesn't need more info + const wantsId = selections.indexOf('id') >= 0; + const wantsObjectId = selections.indexOf('objectId') >= 0; + return wantsId && wantsObjectId && selections.length == 2 + || wantsId && selections.length == 1 + || wantsObjectId && selections.length == 1; +} diff --git a/src/graphql/schemas/Functions.js b/src/graphql/schemas/Functions.js index bb65fc0f099..f598d38702e 100644 --- a/src/graphql/schemas/Functions.js +++ b/src/graphql/schemas/Functions.js @@ -3,12 +3,6 @@ import { FunctionsRouter } from '../../Routers/FunctionsRouter'; import { GraphQLBoolean } from 'graphql'; import { getGloballyUniqueId } from '../execute'; -function setID(object) { - if (object.objectId && object.className) { - object.id = getGloballyUniqueId(object.className, object.objectId); - } -} - function getFunctions() { const functions = getAllFunctions(); const fields = {}; @@ -19,17 +13,11 @@ function getFunctions() { let useDefaultType = true; if (options && options.type) { type = options.type; - if (typeof type === 'function') { - type = type(); - } useDefaultType = false; } if (options && options.inputType) { inputType = options.inputType; - if (typeof inputType === 'function') { - inputType = inputType(); - } } let args; if (inputType) { @@ -59,7 +47,7 @@ function injectIdsInResults(result) { result.forEach(injectIdsInResults); } else if (typeof result === 'object') { if (result.objectId && result.className) { - setID(result); + result.id = getGloballyUniqueId(result.className, result.objectId); } Object.keys(result).forEach((key) => { injectIdsInResults(result[key]); diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index 8183de2f86f..4fe49738708 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -1,4 +1,4 @@ -import { runFind, runGet, rest, transformResult, connectionResultsArray, parseID, getGloballyUniqueId } from '../execute'; +import { runFind, runGet, resolvePointer, rest, connectionResultsArray, parseID, getGloballyUniqueId } from '../execute'; import { GraphQLObjectType, @@ -14,7 +14,6 @@ import { queryType, inputType, type, - Pointer, PageInfo, } from '../types' @@ -22,10 +21,6 @@ import { Node } from '../types/Node'; -/* import { - ParseObjectInterface -} from '../types/ParseObject'; */ - import { getOrElse, clearCache, @@ -40,50 +35,44 @@ function handleIdField(fieldName) { } function getRelationField(fieldName, field, schema) { - if (field.type == 'Relation') { - const { find } = loadClass(field.targetClass, schema); - find.resolve = async (parent, args, context, info) => { - const query = { - $relatedTo: { - object: { - __type: 'Pointer', - className: parent.className, - objectId: parent.objectId - }, - key: fieldName, - } + const { find } = loadClass(field.targetClass, schema); + find.resolve = async (parent, args, context, info) => { + const query = { + $relatedTo: { + object: { + __type: 'Pointer', + className: parent.className, + objectId: parent.objectId + }, + key: fieldName, } - args.redirectClassNameForKey = fieldName; - const results = await runFind(context, info, parent.className, args, schema, query); - results.forEach((result) => { - result.id = getGloballyUniqueId(result.className, result.objectId); - }); - return connectionResultsArray(results, args, 100); - }; - return find; - } + } + args.redirectClassNameForKey = fieldName; + const results = await runFind(context, info, parent.className, args, schema, query); + results.forEach((result) => { + result.id = getGloballyUniqueId(result.className, result.objectId); + }); + return connectionResultsArray(results, args, 100); + }; + return find; +} + +function getFieldType(field) { + return (field.type === 'Pointer' ? `Pointer<${field.targetClass}>` : `${field.type}`); } function graphQLField(fieldName, field, schema) { - let gQLType = handleIdField(fieldName) || type(field); - if (!gQLType) { - // If it's not possible to resovle a simple type - // check if it's a proper relation field + if (field.type == 'Relation') { return getRelationField(fieldName, field, schema); } - const fieldType = (gQLType === Pointer ? `Pointer<${field.targetClass}>` : `${field.type}`); + + let gQLType = handleIdField(fieldName) || type(field); + const fieldType = getFieldType(field); let gQLResolve; if (field.type === 'Pointer') { gQLType = loadClass(field.targetClass, schema).objectType, gQLResolve = (parent, args, context, info) => { - const object = parent[fieldName]; - const selections = info.fieldNodes[0].selectionSet.selections.map((field) => { - return field.name.value; - }); - if (selections.indexOf('id') < 0 || selections.length > 1) { - return runGet(context, info, object.className, object.objectId, schema); - } - return transformResult(field.targetClass, object, schema, { context, info }); + return resolvePointer(field.targetClass, parent[fieldName], schema, context, info); } } return { @@ -99,7 +88,7 @@ function graphQLInputField(fieldName, field) { if (!gQLType) { return; } - const fieldType = (gQLType === Pointer ? `Pointer<${field.targetClass}>` : `${field.type}`); + const fieldType = getFieldType(field); return { name: fieldName, type: gQLType, @@ -135,6 +124,21 @@ function transformInput(input, schema) { return input; } +function getObjectId(input) { + if (!input.id && !input.objectId) { + throw 'id or objectId are required'; + } + let objectId; + if (input.objectId) { + objectId = input.objectId; + delete input.objectId; + } else { + objectId = parseID(input.id).objectId; + delete input.id; + } + return objectId; +} + export function loadClass(className, schema) { const c = getOrElse(className, () => new ParseClass(className, schema)); const objectType = c.graphQLObjectType(); @@ -148,7 +152,7 @@ export function loadClass(className, schema) { type: objectType, description: `Use this endpoint to get or query ${className} objects`, args: { - objectId: { type: GraphQLID }, + objectId: { type: new GraphQLNonNull(GraphQLID) }, }, resolve: async (root, args, context, info) => { // Get the selections @@ -196,18 +200,7 @@ export function loadClass(className, schema) { input: { type: updateType } }, resolve: async (root, args, context, info) => { - if (!args.input.id && !args.input.objectId) { - throw 'id or objectId are required'; - } - let objectId; - if (args.input.objectId) { - objectId = args.input.objectId; - delete args.input.objectId; - } else { - objectId = parseID(args.input.id).objectId; - delete args.input.id; - } - + const objectId = getObjectId(args.input); const input = transformInput(args.input, schema[className]); const clientMutationId = input.clientMutationId; delete input.clientMutationId; @@ -233,18 +226,7 @@ export function loadClass(className, schema) { }) } }, resolve: async (root, args, context, info) => { - if (!args.input.id && !args.input.objectId) { - throw 'id or objectId are required'; - } - let objectId; - if (args.input.objectId) { - objectId = args.input.objectId; - delete args.input.objectId; - } else { - objectId = parseID(args.input.id).objectId; - delete args.input.id; - } - + const objectId = getObjectId(args.input); const clientMutationId = args.input.clientMutationId; const object = await runGet(context, info, className, objectId); await rest.del(context.config, context.auth, className, objectId); @@ -301,6 +283,10 @@ export class ParseClass { }, initial); } + isTypeOf(object) { + return object.className === this.className; + } + graphQLConfig() { const className = this.className; return { @@ -309,9 +295,7 @@ export class ParseClass { // in relay, it's impossible to have 2 interfaces??? interfaces: [Node, /* ParseObjectInterface */], fields: () => this.buildFields(graphQLField, false, true), - isTypeOf: (a) => { - return a.className == className; - }, + isTypeOf: this.isTypeOf.bind(this), }; } @@ -326,9 +310,7 @@ export class ParseClass { delete fields.id; return fields; }, - isTypeOf: function(input) { - return input.className == className; - } + isTypeOf: this.isTypeOf.bind(this) }; } @@ -342,9 +324,7 @@ export class ParseClass { fields.clientMutationId = { type: GraphQLString }; return fields; }, - isTypeOf: function(input) { - return input.className == className; - } + isTypeOf: this.isTypeOf.bind(this) }; } @@ -359,9 +339,7 @@ export class ParseClass { fields.clientMutationId = { type: GraphQLString }; return fields; }, - isTypeOf: function(input) { - return input.className == this.className; - } + isTypeOf: this.isTypeOf.bind(this) }; } diff --git a/src/graphql/types/ACL.js b/src/graphql/types/ACL.js index 3d593ae8237..8baa26c3c2d 100644 --- a/src/graphql/types/ACL.js +++ b/src/graphql/types/ACL.js @@ -1,28 +1,18 @@ import { GraphQLScalarType, - GraphQLList, - GraphQLString } from 'graphql' +import { Parse } from 'parse/node'; + +const id = (value) => value; export const ACL = new GraphQLScalarType({ name: 'ACL', - fields: { - read: { - type: new GraphQLList(GraphQLString), - name: 'read', - description: 'Read access for the object' - }, - write: { - type: new GraphQLList(GraphQLString), - name: 'write', - description: 'Write access for the object' + serialize: id, + parseValue: (value) => { + if (value && value instanceof Parse.ACL) { + return value.toJSON(); } - }, - serialize: () => { - throw "not implemented" - }, - parseValue: () => { - throw "not implemented" + throw 'Invalid ACL value, should be a Parse.ACL'; }, parseLiteral: () => { throw "not implemented" diff --git a/src/graphql/types/BaseQuery.js b/src/graphql/types/BaseQuery.js index 688c3e30503..9f225e2b489 100644 --- a/src/graphql/types/BaseQuery.js +++ b/src/graphql/types/BaseQuery.js @@ -3,45 +3,6 @@ import { GraphQLBoolean } from 'graphql' -function description() { - return `## Equal To: - \`\`\` - { key: "value" } - { key: {eq: "value"} } - \`\`\` - - ## Not Equal To - \`\`\` - { key: {ne: "value"} } - \`\`\` - - ## Contained in: - \`\`\` - { key: {in: ["value1", "value2"]} } - \`\`\` - - ## Not Contained in: - \`\`\` - { key: { nin: ["value1", "value2"] } } - \`\`\` - - ## Exists: - \`\`\` - { key: {exists: true} } - \`\`\` - - ## Match results from another query - ### This matches a value for a key in the result of a different query - \`\`\` - { key: {select: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} } - \`\`\` - ### Requires that a key’s value not match a value for a key in the result of a different query - \`\`\` - { key: {dontSelect: {"query": {"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}} } - \`\`\` - `; -} - export const BaseQuery = (type) => { return { eq: { @@ -66,6 +27,5 @@ export const BaseQuery = (type) => { } export default { - description, BaseQuery, } diff --git a/src/graphql/types/Date.js b/src/graphql/types/Date.js index 2cdee488d74..f301b2a5153 100644 --- a/src/graphql/types/Date.js +++ b/src/graphql/types/Date.js @@ -10,17 +10,21 @@ import { export const Date = new GraphQLScalarType({ name: 'Date', serialize: (obj) => { - if (typeof obj === 'string') { + if (typeof obj === 'object' && obj.__type === 'Date') { + return new global.Date(obj.iso); + } else if (typeof obj === 'string' || typeof obj === 'number') { return new global.Date(obj); } - return obj; + throw `Cannot serialize date`; }, - parseValue: () => { - throw "Date parseValue not implemented" + parseValue: (value) => { + const date = new global.Date(value); + return { iso: date.toISOString(), __type: 'Date' }; }, parseLiteral: (node) => { if (node.kind === Kind.STRING) { - return new global.Date(node.value); + const date = new global.Date(node.value); + return { iso: date.toISOString(), __type: 'Date' }; } throw `Cannot parse date of type ${node.kind}`; } diff --git a/src/graphql/types/NumberInput.js b/src/graphql/types/NumberInput.js index abb77fc98fc..611e2123e10 100644 --- a/src/graphql/types/NumberInput.js +++ b/src/graphql/types/NumberInput.js @@ -3,6 +3,8 @@ import { Kind } from 'graphql' +const id = (value) => value; + export const NumberInput = new GraphQLScalarType({ name: 'NumberInput', description: `Input for number @@ -11,17 +13,13 @@ export const NumberInput = new GraphQLScalarType({ - key: 1 - key: {increment: 1} `, - serialize: () => { - throw "NumberInput serialize not implemented" - }, - parseValue: () => { - throw "NumberInput parseValue not implemented" - }, + serialize: id, + parseValue: id, parseLiteral: (ast) => { if (ast.kind == Kind.OBJECT) { const fields = ast.fields; if (fields.length != 1) { - throw 'Invalid NUmberInput'; + throw 'Invalid literal for NumberInput (too many fields)'; } const field = fields[0]; const operator = field.name.value; diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index a1d40c3505c..ab2f4eba47b 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -60,28 +60,21 @@ export { PageInfo, } +const types = { + String: GraphQLString, + Number: GraphQLFloat, + Boolean: GraphQLBoolean, + GeoPoint, + File, + ACL, + Date, + Pointer, + Object: JSONObject, + Array: new GraphQLList(JSONObject) +} + export function type({ type }) { - if (type == 'String') { - return GraphQLString; - } if (type == 'Number') { - return GraphQLFloat; - } if (type == 'Boolean') { - return GraphQLBoolean; - } if (type == 'GeoPoint') { - return GeoPoint; - } if (type == 'File') { - return File; - } else if (type == 'ACL') { - return ACL; - } else if (type == 'Date') { - return Date; - } else if (type == 'Pointer') { - return Pointer; - } else if (type == 'Object') { - return JSONObject; - } else if (type === 'Array') { - return new GraphQLList(JSONObject); - } + return types[type]; } export function inputType(field) { From 65b7f0f1f81916592bf8b1ca44b1b5df9a0897b7 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 30 Aug 2018 09:41:36 -0400 Subject: [PATCH 55/62] Adds tests for UserAuth --- spec/graphqlUserAuth.spec.js | 162 ++++++++++++++++++++++++++++++ src/graphql/execute.js | 4 +- src/graphql/schemas/ParseClass.js | 3 - src/graphql/typesCache.js | 5 +- 4 files changed, 165 insertions(+), 9 deletions(-) create mode 100644 spec/graphqlUserAuth.spec.js diff --git a/spec/graphqlUserAuth.spec.js b/spec/graphqlUserAuth.spec.js new file mode 100644 index 00000000000..8702d5777ab --- /dev/null +++ b/spec/graphqlUserAuth.spec.js @@ -0,0 +1,162 @@ +const GraphQLParseSchema = require('../lib/graphql/Schema').GraphQLParseSchema; +const Config = require('../lib/Config'); +const Auth = require('../lib/Auth').Auth; +const { graphql } = require('graphql'); + + +describe('graphQLUserAuth', () => { + let schema; + let root; + let config; + async function reload() { + const Schema = new GraphQLParseSchema('test'); + const result = await Schema.load(); + schema = result.schema; + root = result.root; + } + + beforeEach(async () => { + config = Config.get('test'); + await reload(); + }); + + it('can login with username', async () => { + const user = new Parse.User(); + await user.save({ + username: 'luke_skywalker', + email: 'luke@therebellion', + password: 'strong the force is with me' + }); + const context = { + config, + auth: new Auth({config}) + } + const input = { + username: 'luke_skywalker', + password: 'strong the force is with me' + } + const result = await graphql(schema, ` + mutation logUserIn($input: LoginInput) { + login(input: $input) { + username + sessionToken + } + } + `, root, context, { input }); + expect(result.data.login.username).toBe('luke_skywalker'); + expect(result.data.login.sessionToken).toBeDefined(); + }); + + it('can login with email', async () => { + const user = new Parse.User(); + await user.save({ + username: 'luke_skywalker', + email: 'luke@therebellion', + password: 'strong the force is with me' + }); + const context = { + config, + auth: new Auth({config}), + info: { + installationId: 'my-installation-id' + } + } + const input = { + email: 'luke@therebellion', + password: 'strong the force is with me' + } + const result = await graphql(schema, ` + mutation logUserIn($input: LoginInput) { + login(input: $input) { + username + sessionToken + } + } + `, root, context, { input }); + expect(result.data.login.username).toBe('luke_skywalker'); + expect(result.data.login.sessionToken).toBeDefined(); + const sessions = await new Parse.Query(Parse.Session) + .equalTo('sessionToken', result.data.login.sessionToken) + .find({ useMasterKey: true }); + expect(sessions.length).toBe(1); + expect(sessions[0].get('installationId')).toBe('my-installation-id'); + }); + + it('can logout', async () => { + const user = new Parse.User(); + await user.save({ + username: 'luke_skywalker', + email: 'luke@therebellion', + password: 'strong the force is with me' + }); + const loggedInUser = await Parse.User.logIn('luke_skywalker', 'strong the force is with me'); + const sessionToken = loggedInUser.getSessionToken(); + let sessions = await new Parse.Query(Parse.Session).find({ useMasterKey: true }); + expect(sessions.length).toBe(1); + expect(sessionToken).toBeDefined(); + const context = { + config, + auth: new Auth({config, user: loggedInUser}), + info: { + sessionToken + } + }; + const result = await graphql(schema, ` + mutation logMeOut { + logout + } + `, root, context); + expect(result.data.logout).toBeTruthy(); + sessions = await new Parse.Query(Parse.Session).find({ useMasterKey: true }); + expect(sessions.length).toBe(0); + }); + + it('can get currentUser when logged in', async () => { + const user = new Parse.User(); + await user.save({ + username: 'luke_skywalker', + email: 'luke@therebellion', + password: 'strong the force is with me' + }); + const loggedInUser = await Parse.User.logIn('luke_skywalker', 'strong the force is with me'); + const sessionToken = loggedInUser.getSessionToken(); + const context = { + config, + auth: new Auth({config, user: loggedInUser}), + info: { + sessionToken + } + }; + const result = await graphql(schema, ` + query me { + currentUser { + username + password + email + } + } + `, root, context); + expect(result.data.currentUser.username).toBe('luke_skywalker'); + expect(result.data.currentUser.password).toBe(null); + expect(result.data.currentUser.email).toBe('luke@therebellion') + }); + + it('fails to get the currentUser when logged out', async () => { + const context = { + config, + auth: new Auth({ config }), + }; + const result = await graphql(schema, ` + query me { + currentUser { + username + password + email + } + } + `, root, context); + expect(result.data.currentUser).toBe(null); + expect(result.errors).not.toBeUndefined(); + expect(result.errors[0].message).toBe('You need to be logged in.'); + }); +}); diff --git a/src/graphql/execute.js b/src/graphql/execute.js index 93127ea41dd..ac9ef721067 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -2,7 +2,7 @@ import rest from '../rest'; export { rest }; export function getGloballyUniqueId(className, objectId) { - return new Buffer(`${className}::${objectId}`).toString('base64'); + return base64(`${className}::${objectId}`); } export function transformResult(className, result) { @@ -55,7 +55,7 @@ function transformQuery(query) { } export function base64(string) { - return new Buffer(string).toString('base64') + return new Buffer(string).toString('base64'); } export function parseID(base64String) { diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index 4fe49738708..68fc0c39759 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -23,11 +23,8 @@ import { import { getOrElse, - clearCache, } from '../typesCache'; -export { clearCache }; - function handleIdField(fieldName) { if (fieldName === 'objectId' || fieldName == 'id') { return new GraphQLNonNull(GraphQLID); diff --git a/src/graphql/typesCache.js b/src/graphql/typesCache.js index 2530f77f59f..60a7d21b1a7 100644 --- a/src/graphql/typesCache.js +++ b/src/graphql/typesCache.js @@ -1,5 +1,5 @@ // @flow -let cache = {}; +const cache = {}; export function getOrElse(key: string, handler: () => T): ?T { if (!cache[key]) { @@ -8,6 +8,3 @@ export function getOrElse(key: string, handler: () => T): ?T { return cache[key]; } -export function clearCache() { - cache = {}; -} From 3195dfe54b7ea890c0de2bbc6b7ffd983a3b52da Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 30 Aug 2018 10:47:12 -0400 Subject: [PATCH 56/62] Adds tests for relay pagination, fixes bug when using before --- spec/graphqlCRUD.spec.js | 280 ++++++++++++++++++++++++++++++--------- src/graphql/execute.js | 5 +- 2 files changed, 222 insertions(+), 63 deletions(-) diff --git a/spec/graphqlCRUD.spec.js b/spec/graphqlCRUD.spec.js index 60b056e9aff..3383abad897 100644 --- a/spec/graphqlCRUD.spec.js +++ b/spec/graphqlCRUD.spec.js @@ -29,18 +29,19 @@ async function setup(config) { describe('graphQLCRUD', () => { let schema; let root; + let context; beforeEach(async () => { config = Config.get('test'); const result = await setup(config); schema = result.schema; root = result.root; - }); - - it('Adds objects', async () => { - const context = { + context = { config, auth: new Auth({config}) } + }); + + it('Adds objects', async () => { const ACL = new Parse.ACL(); ACL.setPublicReadAccess(true); ACL.setPublicWriteAccess(true); @@ -100,23 +101,18 @@ describe('graphQLCRUD', () => { }); }); - it('Queries objects', (done) => { - const context = { - config, - auth: new Auth({config}) - } + it('Queries objects', async () => { const publicACL = new Parse.ACL(); publicACL.setPublicReadAccess(true); publicACL.setPublicWriteAccess(true); - const obj = new Parse.Object('OtherClass', {stringValue: 'baz'}); + const obj = new Parse.Object('OtherClass', {otherString: 'baz'}); const nc = new Parse.Object('NewClass', {stringValue: 'hello'}); nc.setACL(publicACL); - return Parse.Object.saveAll([obj, nc]).then(() => { - nc.relation('others').add(obj); - obj.set('newClass', nc); - return Parse.Object.saveAll([obj, nc]); - }).then(() => { - return graphql(schema, ` + await Parse.Object.saveAll([obj, nc]); + nc.relation('others').add(obj); + obj.set('newClass', nc); + await Parse.Object.saveAll([obj, nc]); + const res = await graphql(schema, ` query { NewClass: findNewClass { nodes { @@ -147,36 +143,219 @@ describe('graphQLCRUD', () => { } } } - `,root, context) - }).then((res) => { - expect(res.data.NewClass).toBeDefined(); - expect(res.data.OtherClass).toBeDefined(); - expect(Array.isArray(res.data.NewClass.nodes)).toBeTruthy(); - expect(Array.isArray(res.data.OtherClass.nodes)).toBeTruthy(); - const newClasses = res.data.NewClass.nodes; - // Should only have objectId - newClasses.forEach((object) => { - expect(Object.keys(object).length).toBe(2); - expect(object.objectId).toBeDefined(); - expect(object.ACL).toEqual({ - '*': { 'read': true, 'write': true } - }) - }); + `,root, context); - const otherClasses = res.data.OtherClass.nodes; - const otherObject = otherClasses[0]; - expect(otherObject.objectId).not.toBeUndefined(); - expect(otherObject.newClass.objectId).not.toBeUndefined(); - expect(otherObject.objectId).toEqual(otherObject.newClass.others.nodes[0].objectId); - expect(otherObject.newClass.objectId).toEqual(otherObject.newClass.others.nodes[0].newClass.objectId); - }).then(done).catch(done.fail); + expect(res.data.NewClass).toBeDefined(); + expect(res.data.OtherClass).toBeDefined(); + expect(Array.isArray(res.data.NewClass.nodes)).toBeTruthy(); + expect(Array.isArray(res.data.OtherClass.nodes)).toBeTruthy(); + const newClasses = res.data.NewClass.nodes; + // Should only have objectId + newClasses.forEach((object) => { + expect(Object.keys(object).length).toBe(2); + expect(object.objectId).toBeDefined(); + expect(object.ACL).toEqual({ + '*': { 'read': true, 'write': true } + }) + }); + + const otherClasses = res.data.OtherClass.nodes; + const otherObject = otherClasses[0]; + expect(otherObject.objectId).not.toBeUndefined(); + expect(otherObject.newClass.objectId).not.toBeUndefined(); + expect(otherObject.objectId).toEqual(otherObject.newClass.others.nodes[0].objectId); + expect(otherObject.newClass.objectId).toEqual(otherObject.newClass.others.nodes[0].newClass.objectId); }); - it('Gets object from Node', async () => { - const context = { - config, - auth: new Auth({config}) + it('finds object with queries', async () => { + const obj = new Parse.Object('NewClass', {stringValue: 'baz'}); + const obj2 = new Parse.Object('NewClass', {stringValue: 'foo'}); + const obj3 = new Parse.Object('NewClass', {stringValue: 'bar'}); + await Parse.Object.saveAll([ obj, obj2, obj3 ]); + const res = await graphql(schema, ` + query findThem { + findNewClass(where: { stringValue: { eq: "baz" } }) { + edges { + cursor + node { + id + objectId + stringValue + } + } + pageInfo { + hasNextPage + hasPreviousPage + } + } + } + `, root, context); + expect(res.errors).toBeUndefined(); + const { + edges, + pageInfo + } = res.data.findNewClass; + expect(edges.length).toBe(1); + expect(edges[0].cursor).toBeDefined(); + expect(edges[0].node.objectId).toBe(obj.id); + expect(pageInfo).toEqual({ + hasNextPage: false, + hasPreviousPage: false + }); + }); + + async function makeObjects(amount) { + const objects = []; + while (objects.length != amount) { + const obj = new Parse.Object('NewClass', {numberValue: objects.length}); + await obj.save(); + objects.push(obj); + } + return objects; + } + + it('can query with pagninations for firsts', async () => { + const objects = await makeObjects(20); + const res = await graphql(schema, ` + query findThem { + findNewClass(first: 5) { + edges { + node { + id + createdAt + } + } + pageInfo { + hasNextPage + hasPreviousPage + } + } + } + `, root, context); + expect(res.errors).toBeUndefined(); + const { + edges, + pageInfo + } = res.data.findNewClass; + expect(edges.length).toBe(5); + edges.forEach((edge, index) => { + const { node: { createdAt }} = edge; + expect(createdAt).toEqual(objects[index].createdAt); + }); + expect(pageInfo).toEqual({ + hasNextPage: true, + hasPreviousPage: false + }); + }); + + it('can query with pagninations for firsts', async () => { + const objects = await makeObjects(20); + const res = await graphql(schema, ` + query findThem { + findNewClass(last: 5) { + edges { + node { + id + createdAt + } + } + pageInfo { + hasNextPage + hasPreviousPage + } + } + } + `, root, context); + expect(res.errors).toBeUndefined(); + const { + edges, + pageInfo + } = res.data.findNewClass; + expect(edges.length).toBe(5); + edges.forEach((edge, index) => { + const { node: { createdAt }} = edge; + const idx = objects.length - 1 - index; + expect(createdAt).toEqual(objects[idx].createdAt); + }); + expect(pageInfo).toEqual({ + hasNextPage: false, + hasPreviousPage: true + }); + }); + + it('can query with pagninations with before', async () => { + const objects = await makeObjects(20); + const cursor = new Buffer(objects[5].createdAt.toISOString()).toString('base64'); + const res = await graphql(schema, ` + query findThem($cursor: String) { + findNewClass(before: $cursor) { + edges { + node { + objectId + createdAt + } + } + pageInfo { + hasNextPage + hasPreviousPage + } + } + } + `, root, context, { cursor }); + expect(res.errors).toBeUndefined(); + const { + edges, + pageInfo + } = res.data.findNewClass; + expect(edges.length).toBe(5); + edges.forEach((edge, index) => { + const { node: { createdAt, objectId }} = edge; + expect(createdAt).toEqual(objects[index].createdAt); + expect(objectId).toEqual(objects[index].id); + }); + expect(pageInfo).toEqual({ + hasNextPage: true, + hasPreviousPage: false + }); + }); + + it('can query with pagninations with after', async () => { + const objects = await makeObjects(20); + const cursor = new Buffer(objects[15].createdAt.toISOString()).toString('base64'); + const res = await graphql(schema, ` + query findThem($cursor: String) { + findNewClass(after: $cursor) { + edges { + node { + id + createdAt + } + } + pageInfo { + hasNextPage + hasPreviousPage + } + } } + `, root, context, { cursor }); + expect(res.errors).toBeUndefined(); + const { + edges, + pageInfo + } = res.data.findNewClass; + expect(edges.length).toBe(4); + edges.forEach((edge, index) => { + const { node: { createdAt }} = edge; + const idx = index + 16; + expect(createdAt).toEqual(objects[idx].createdAt); + }); + expect(pageInfo).toEqual({ + hasNextPage: false, + hasPreviousPage: true + }); + }); + + it('Gets object from Node', async () => { const obj = new Parse.Object('OtherClass', {otherString: 'aStringValue'}); await obj.save(); const result = await graphql(schema, ` @@ -195,10 +374,6 @@ describe('graphQLCRUD', () => { }); it('Updates objects', async () => { - const context = { - config, - auth: new Auth({config}) - } const obj = new Parse.Object('OtherClass', {otherString: 'baz'}); await obj.save(); const result = await graphql(schema, ` @@ -216,10 +391,6 @@ describe('graphQLCRUD', () => { }); it('Updates objects with uniqueID', async () => { - const context = { - config, - auth: new Auth({config}) - } const obj = new Parse.Object('OtherClass', {otherString: 'baz'}); const nc = new Parse.Object('NewClass', {stringValue: 'aString'}); await Parse.Object.saveAll([obj, nc]); @@ -248,11 +419,6 @@ describe('graphQLCRUD', () => { }); it('fails to update object without id', async () => { - const context = { - config, - auth: new Auth({config}) - } - const result = await graphql(schema, ` mutation myMutation($input: UpdateOtherClassInput!) { updateOtherClass(input: $input) { @@ -269,10 +435,6 @@ describe('graphQLCRUD', () => { }); it('Destroy objects', async (done) => { - const context = { - config, - auth: new Auth({config}) - } const obj = new Parse.Object('OtherClass', {stringValue: 'baz'}); const nc = new Parse.Object('NewClass', {stringValue: 'hello'}); await Parse.Object.saveAll([obj, nc]) diff --git a/src/graphql/execute.js b/src/graphql/execute.js index ac9ef721067..25bfaea68a3 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -122,7 +122,7 @@ function parseArguments(args) { query.createdAt = { '$gt': new Date(new Buffer(args.after, 'base64').toString('utf8')) } } if (Object.prototype.hasOwnProperty.call(args, 'before')) { - query.createdAt = { '$lt': new Date(new Buffer(args.after, 'base64').toString('utf8')) } + query.createdAt = { '$lt': new Date(new Buffer(args.before, 'base64').toString('utf8')) } } if (Object.prototype.hasOwnProperty.call(args, 'redirectClassNameForKey')) { options.redirectClassNameForKey = args.redirectClassNameForKey; @@ -136,9 +136,6 @@ export function runFind(context, info, className, args, schema, restQuery) { if (args.where) { Object.assign(query, args.where); } - if (args.objectId) { - Object.assign(query, { objectId: args.objectId }); - } transformQuery(query, schema); if (restQuery) { Object.assign(query, restQuery); From 91b2a97b24662fb6de2b6be985321d633fe739e8 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Thu, 30 Aug 2018 11:12:23 -0400 Subject: [PATCH 57/62] Improves testing for parsers and utilities functions --- spec/graphqlCRUD.spec.js | 71 +++++- spec/graphqlFunctions.spec.js | 14 +- spec/graphqlTypes.spec.js | 414 +++++++++++++++++----------------- spec/graphqlUserAuth.spec.js | 2 +- src/graphql/execute.js | 25 +- 5 files changed, 293 insertions(+), 233 deletions(-) diff --git a/spec/graphqlCRUD.spec.js b/spec/graphqlCRUD.spec.js index 3383abad897..d79fab25f44 100644 --- a/spec/graphqlCRUD.spec.js +++ b/spec/graphqlCRUD.spec.js @@ -2,7 +2,7 @@ const GraphQLParseSchema = require('../lib/graphql/Schema').GraphQLParseSchema; const Config = require('../lib/Config'); const Auth = require('../lib/Auth').Auth; const { graphql } = require('graphql'); -const { containsOnlyIdFields } = require('../lib/graphql/execute'); +const { containsOnlyIdFields, transformQueryConstraint, transformResult, parseID } = require('../lib/graphql/execute'); let config; @@ -26,7 +26,7 @@ async function setup(config) { return await Schema.load(); } -describe('graphQLCRUD', () => { +describe('graphQL CRUD operations', () => { let schema; let root; let context; @@ -468,15 +468,64 @@ describe('graphQLCRUD', () => { expect(newClassObject.data.NewClass).toBeNull(); done(); }); -}); -describe('Pointer fetching', () => { - it('ensures containsOnlyIdFields works', () => { - expect(containsOnlyIdFields(['id'])).toBeTruthy(); - expect(containsOnlyIdFields(['objectId'])).toBeTruthy() - expect(containsOnlyIdFields(['id', 'objectId'])).toBeTruthy(); - expect(containsOnlyIdFields(['objectId', 'yolo'])).toBeFalsy(); - expect(containsOnlyIdFields(['yolo'])).toBeFalsy(); - expect(containsOnlyIdFields(['yolo', 'id'])).toBeFalsy(); + describe('utilities', () => { + it('ensures containsOnlyIdFields works', () => { + expect(containsOnlyIdFields(['id'])).toBeTruthy(); + expect(containsOnlyIdFields(['objectId'])).toBeTruthy() + expect(containsOnlyIdFields(['id', 'objectId'])).toBeTruthy(); + expect(containsOnlyIdFields(['objectId', 'yolo'])).toBeFalsy(); + expect(containsOnlyIdFields(['yolo'])).toBeFalsy(); + expect(containsOnlyIdFields(['yolo', 'id'])).toBeFalsy(); + }); + + it('should transform key and not value', () => { + const anyObject = Object.create(null); + const { key, value } = transformQueryConstraint('anyKey', anyObject); + expect(key).toBe('$anyKey'); + expect(value).toBe(value); // this is not a copy! + }); + + it('should transform nearSphere and not value', () => { + const anyObject = Object.create( + { + point: { + latitude: 21, + longitude: 42, + } + } + ); + const { key, value } = transformQueryConstraint('nearSphere', anyObject); + expect(key).toBe('$nearSphere'); + expect(value).toEqual({ + latitude: 21, + longitude: 42, + }); // this is not a copy! + }); + + it('should not transform non object results', () => { + const result = transformResult('MyClassName', { + key: 'value', + }); + expect(result).toEqual({ + className: 'MyClassName', + key: 'value' + }); + }); + + it('should throw on invalid IDs with no separators', () => { + const invalidID = new Buffer('MyThingabc').toString('base64'); + expect(() => parseID(invalidID)).toThrowError('Invalid ID'); + }); + + it('should throw on invalid IDs with bad separators', () => { + const invalidID = new Buffer('MyThing-abc').toString('base64'); + expect(() => parseID(invalidID)).toThrowError('Invalid ID'); + }); + + it('should throw on invalid IDs with too many separators', () => { + const invalidID = new Buffer('MyThing::abc::').toString('base64'); + expect(() => parseID(invalidID)).toThrowError('Invalid ID'); + }); }); }); diff --git a/spec/graphqlFunctions.spec.js b/spec/graphqlFunctions.spec.js index b78dbeb63f7..3a3392c4aa2 100644 --- a/spec/graphqlFunctions.spec.js +++ b/spec/graphqlFunctions.spec.js @@ -22,7 +22,7 @@ async function setup(config) { }); } -describe('graphQLbridge', () => { +describe('graphQL Functions', () => { let schema; let root; async function reload() { @@ -147,4 +147,16 @@ describe('graphQLbridge', () => { expect(node.numberValue).toBeGreaterThan(3); }); }); + + it('exposes to cloud code proper object and input types', () => { + const objType = Parse.Cloud.GraphQLUtils.getObjectType('NewClass'); + const createType = Parse.Cloud.GraphQLUtils.getCreateInputType('NewClass'); + const updateType = Parse.Cloud.GraphQLUtils.getUpdateInputType('NewClass'); + expect(objType).not.toBeUndefined(); + expect(objType instanceof GraphQLObjectType).toBeTruthy(); + expect(createType).not.toBeUndefined(); + expect(createType instanceof GraphQLInputObjectType).toBeTruthy(); + expect(updateType).not.toBeUndefined(); + expect(updateType instanceof GraphQLInputObjectType).toBeTruthy(); + }); }); diff --git a/spec/graphqlTypes.spec.js b/spec/graphqlTypes.spec.js index 284a53765aa..9ec7d337c89 100644 --- a/spec/graphqlTypes.spec.js +++ b/spec/graphqlTypes.spec.js @@ -7,249 +7,251 @@ const { Kind, } = require('graphql'); -describe('NumberInput', () => { - it('should parse litteral with regular values', () => { - let result = NumberInput.parseLiteral({ - kind: Kind.OBJECT, - fields: [{ - name: { value: 'increment' }, - value: { value: 1 } - }] - }); - expect(result).toEqual({ - __op: 'Increment', - amount: 1 - }); - result = NumberInput.parseLiteral({ - kind: Kind.OBJECT, - fields: [{ - name: { value: 'increment' }, - value: { value: '10' } - }] - }); - expect(result).toEqual({ - __op: 'Increment', - amount: 10 - }); - - result = NumberInput.parseLiteral({ - kind: Kind.OBJECT, - fields: [{ - name: { value: 'increment' }, - value: { value: -2 } - }] - }); - expect(result).toEqual({ - __op: 'Increment', - amount: -2 - }); - - result = NumberInput.parseLiteral({ - kind: Kind.OBJECT, - fields: [{ - name: { value: 'increment' }, - value: { value: '-5' } - }] - }); - expect(result).toEqual({ - __op: 'Increment', - amount: -5 - }); - }); - - it('should fail to parse litteral if kind is missing', () => { - expect(() => { - NumberInput.parseLiteral({ +describe('graphQL types', () => { + describe('NumberInput', () => { + it('should parse litteral with regular values', () => { + let result = NumberInput.parseLiteral({ + kind: Kind.OBJECT, fields: [{ name: { value: 'increment' }, - value: { value: '-5' } + value: { value: 1 } }] }); - }).toThrow('Invalid literal for NumberInput'); - }); + expect(result).toEqual({ + __op: 'Increment', + amount: 1 + }); + result = NumberInput.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ + name: { value: 'increment' }, + value: { value: '10' } + }] + }); + expect(result).toEqual({ + __op: 'Increment', + amount: 10 + }); - it('should fail to parse litteral if too many fields are passed', () => { - expect(() => { - NumberInput.parseLiteral({ + result = NumberInput.parseLiteral({ kind: Kind.OBJECT, fields: [{ name: { value: 'increment' }, - value: { value: '-5' } - }, { - name: { value: 'other' }, - value: { value: '-5' } + value: { value: -2 } }] }); - }).toThrow('Invalid literal for NumberInput (too many fields)'); - }); + expect(result).toEqual({ + __op: 'Increment', + amount: -2 + }); - it('should fail to parse litteral if the wrong operator is passed', () => { - expect(() => { - NumberInput.parseLiteral({ + result = NumberInput.parseLiteral({ kind: Kind.OBJECT, fields: [{ - name: { value: 'badOperator' }, + name: { value: 'increment' }, value: { value: '-5' } }] }); - }).toThrow('the badOperator operator is not supported'); - }); + expect(result).toEqual({ + __op: 'Increment', + amount: -5 + }); + }); - it('should parse int and floats as litteral values', () => { - expect(NumberInput.parseLiteral({ - kind: Kind.FLOAT, - value: 10 - })).toBe(10); - expect(NumberInput.parseLiteral({ - kind: Kind.INT, - value: 5 - })).toBe(5); - }); + it('should fail to parse litteral if kind is missing', () => { + expect(() => { + NumberInput.parseLiteral({ + fields: [{ + name: { value: 'increment' }, + value: { value: '-5' } + }] + }); + }).toThrow('Invalid literal for NumberInput'); + }); + + it('should fail to parse litteral if too many fields are passed', () => { + expect(() => { + NumberInput.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ + name: { value: 'increment' }, + value: { value: '-5' } + }, { + name: { value: 'other' }, + value: { value: '-5' } + }] + }); + }).toThrow('Invalid literal for NumberInput (too many fields)'); + }); + + it('should fail to parse litteral if the wrong operator is passed', () => { + expect(() => { + NumberInput.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ + name: { value: 'badOperator' }, + value: { value: '-5' } + }] + }); + }).toThrow('the badOperator operator is not supported'); + }); + + it('should parse int and floats as litteral values', () => { + expect(NumberInput.parseLiteral({ + kind: Kind.FLOAT, + value: 10 + })).toBe(10); + expect(NumberInput.parseLiteral({ + kind: Kind.INT, + value: 5 + })).toBe(5); + }); - it('should return values using serialize and parseValue', () => { - const value = 10.34; - expect(NumberInput.parseValue(value)).toBe(value); - expect(NumberInput.serialize(value)).toBe(value); + it('should return values using serialize and parseValue', () => { + const value = 10.34; + expect(NumberInput.parseValue(value)).toBe(value); + expect(NumberInput.serialize(value)).toBe(value); + }); }); -}); -describe('ACL', () => { - it('should parse Parse.ACL', () => { - expect(ACL.parseValue(new Parse.ACL())).toEqual({}); + describe('ACL', () => { + it('should parse Parse.ACL', () => { + expect(ACL.parseValue(new Parse.ACL())).toEqual({}); - const publicACL = new Parse.ACL(); - publicACL.setPublicReadAccess(true); - expect(ACL.parseValue(publicACL)).toEqual({ '*': { 'read': true }}); + const publicACL = new Parse.ACL(); + publicACL.setPublicReadAccess(true); + expect(ACL.parseValue(publicACL)).toEqual({ '*': { 'read': true }}); - const userACL = new Parse.ACL(); - userACL.setReadAccess('abcdef', true); - userACL.setWriteAccess('abcdef', true); - expect(ACL.parseValue(userACL)).toEqual({ 'abcdef': { 'read': true, 'write': true }}); + const userACL = new Parse.ACL(); + userACL.setReadAccess('abcdef', true); + userACL.setWriteAccess('abcdef', true); + expect(ACL.parseValue(userACL)).toEqual({ 'abcdef': { 'read': true, 'write': true }}); - const roleACL = new Parse.ACL(); - roleACL.setReadAccess('abcdef', true); - roleACL.setRoleWriteAccess('Admin', true); - expect(ACL.parseValue(roleACL)).toEqual({ 'abcdef': { 'read': true }, 'role:Admin': { 'write': true }}); - }); + const roleACL = new Parse.ACL(); + roleACL.setReadAccess('abcdef', true); + roleACL.setRoleWriteAccess('Admin', true); + expect(ACL.parseValue(roleACL)).toEqual({ 'abcdef': { 'read': true }, 'role:Admin': { 'write': true }}); + }); - it('should throw when passing bad values', () => { - expect(() => { - console.log(ACL.parseValue(null)); - }).toThrow('Invalid ACL value, should be a Parse.ACL'); + it('should throw when passing bad values', () => { + expect(() => { + console.log(ACL.parseValue(null)); + }).toThrow('Invalid ACL value, should be a Parse.ACL'); - expect(() => { - ACL.parseValue('hello world'); - }).toThrow('Invalid ACL value, should be a Parse.ACL'); + expect(() => { + ACL.parseValue('hello world'); + }).toThrow('Invalid ACL value, should be a Parse.ACL'); - expect(() => { - ACL.parseLiteral(); - }).toThrow('not implemented'); + expect(() => { + ACL.parseLiteral(); + }).toThrow('not implemented'); + }); }); -}); -describe('JSONObject', () => { - it('should parse a JSONObject', () => { - expect(JSONObject.parseLiteral({ - kind: Kind.STRING, - value: 'Hello' - })).toBe('Hello'); - expect(JSONObject.parseLiteral({ - kind: Kind.BOOLEAN, - value: true - })).toBe(true); - expect(JSONObject.parseLiteral({ - kind: Kind.BOOLEAN, - value: false - })).toBe(false); - expect(JSONObject.parseLiteral({ - kind: Kind.INT, - value: 1 - })).toBe(parseFloat(1)); - expect(JSONObject.parseLiteral({ - kind: Kind.FLOAT, - value: 0.1 + 0.2 - })).toBe(parseFloat(0.1 + 0.2)); - expect(JSONObject.parseLiteral({ - kind: Kind.LIST, - values: [{ value: 'a', kind: Kind.STRING }, { value: 1.0, kind: Kind.FLOAT }] - })).toEqual(['a', 1.0]); - expect(JSONObject.parseLiteral({ - kind: Kind.OBJECT, - fields: [{ name: { value: 'string' }, value: { value: 'a', kind: Kind.STRING } }, { name: { value: 'floatKey' }, value: { value: 1.0, kind: Kind.FLOAT } }] - })).toEqual({ - string: 'a', - floatKey: 1.0 - }); - expect(JSONObject.parseLiteral({ - kind: Kind.VARIABLE, - name: { value: 'myVariable' } - }, { myVariable: 'myValue' })).toEqual('myValue'); - expect(JSONObject.parseLiteral({ - kind: Kind.VARIABLE, - name: { value: 'myVariable' } - })).toBeUndefined(); - expect(JSONObject.parseLiteral({ - kind: Kind.NULL - })).toBe(null); - expect(JSONObject.parseLiteral({ - kind: 'unknown kind' - })).toBeUndefined(); - }); + describe('JSONObject', () => { + it('should parse a JSONObject', () => { + expect(JSONObject.parseLiteral({ + kind: Kind.STRING, + value: 'Hello' + })).toBe('Hello'); + expect(JSONObject.parseLiteral({ + kind: Kind.BOOLEAN, + value: true + })).toBe(true); + expect(JSONObject.parseLiteral({ + kind: Kind.BOOLEAN, + value: false + })).toBe(false); + expect(JSONObject.parseLiteral({ + kind: Kind.INT, + value: 1 + })).toBe(parseFloat(1)); + expect(JSONObject.parseLiteral({ + kind: Kind.FLOAT, + value: 0.1 + 0.2 + })).toBe(parseFloat(0.1 + 0.2)); + expect(JSONObject.parseLiteral({ + kind: Kind.LIST, + values: [{ value: 'a', kind: Kind.STRING }, { value: 1.0, kind: Kind.FLOAT }] + })).toEqual(['a', 1.0]); + expect(JSONObject.parseLiteral({ + kind: Kind.OBJECT, + fields: [{ name: { value: 'string' }, value: { value: 'a', kind: Kind.STRING } }, { name: { value: 'floatKey' }, value: { value: 1.0, kind: Kind.FLOAT } }] + })).toEqual({ + string: 'a', + floatKey: 1.0 + }); + expect(JSONObject.parseLiteral({ + kind: Kind.VARIABLE, + name: { value: 'myVariable' } + }, { myVariable: 'myValue' })).toEqual('myValue'); + expect(JSONObject.parseLiteral({ + kind: Kind.VARIABLE, + name: { value: 'myVariable' } + })).toBeUndefined(); + expect(JSONObject.parseLiteral({ + kind: Kind.NULL + })).toBe(null); + expect(JSONObject.parseLiteral({ + kind: 'unknown kind' + })).toBeUndefined(); + }); - it('should use identity for parseValue and serialize', () => { - const anyObject = Object.create(null); - expect(JSONObject.serialize(anyObject)).toBe(anyObject); - expect(JSONObject.parseValue(anyObject)).toBe(anyObject); + it('should use identity for parseValue and serialize', () => { + const anyObject = Object.create(null); + expect(JSONObject.serialize(anyObject)).toBe(anyObject); + expect(JSONObject.parseValue(anyObject)).toBe(anyObject); + }); }); -}); -describe('Date', () => { - it('should parse date from parse style object', () => { - const isoDate = new Date().toISOString(); - expect(DateType.Date.serialize({ - __type: 'Date', - iso: isoDate - })).toEqual(new Date(isoDate)); - }); + describe('Date', () => { + it('should parse date from parse style object', () => { + const isoDate = new Date().toISOString(); + expect(DateType.Date.serialize({ + __type: 'Date', + iso: isoDate + })).toEqual(new Date(isoDate)); + }); - it('should parse date from iso string', () => { - const isoDate = new Date().toISOString(); - expect(DateType.Date.serialize(isoDate)) - .toEqual(new Date(isoDate)); - }); + it('should parse date from iso string', () => { + const isoDate = new Date().toISOString(); + expect(DateType.Date.serialize(isoDate)) + .toEqual(new Date(isoDate)); + }); - it('should parse date from timestamp', () => { - const ts = new Date().getTime(); - expect(DateType.Date.serialize(ts)) - .toEqual(new Date(ts)); - }); + it('should parse date from timestamp', () => { + const ts = new Date().getTime(); + expect(DateType.Date.serialize(ts)) + .toEqual(new Date(ts)); + }); - it('should throw an error when passing an invalid value', () => { - expect(() => DateType.Date.serialize(false)) - .toThrow('Cannot serialize date'); - }); + it('should throw an error when passing an invalid value', () => { + expect(() => DateType.Date.serialize(false)) + .toThrow('Cannot serialize date'); + }); - it('should parse the date value from ISO string', () => { - const isoDate = new Date().toISOString(); - expect(DateType.Date.parseValue(isoDate)) - .toEqual({ __type: 'Date', iso: isoDate }); - }); + it('should parse the date value from ISO string', () => { + const isoDate = new Date().toISOString(); + expect(DateType.Date.parseValue(isoDate)) + .toEqual({ __type: 'Date', iso: isoDate }); + }); - it('should parse the date value from timestamp', () => { - const date = new Date(); - const ts = date.getTime(); - expect(DateType.Date.parseValue(ts)) - .toEqual({ __type: 'Date', iso: date.toISOString() }); - }); + it('should parse the date value from timestamp', () => { + const date = new Date(); + const ts = date.getTime(); + expect(DateType.Date.parseValue(ts)) + .toEqual({ __type: 'Date', iso: date.toISOString() }); + }); - it('should parse from string litteral', () => { - const isoDate = new Date().toISOString(); - expect(DateType.Date.parseLiteral({ kind: Kind.STRING, value: isoDate })) - .toEqual({ __type: 'Date', iso: isoDate }); - }); + it('should parse from string litteral', () => { + const isoDate = new Date().toISOString(); + expect(DateType.Date.parseLiteral({ kind: Kind.STRING, value: isoDate })) + .toEqual({ __type: 'Date', iso: isoDate }); + }); - it('should fail to parse from invalid litteral', () => { - expect(() => DateType.Date.parseLiteral({ kind: 'invalid type' })) - .toThrow('Cannot parse date of type invalid type') + it('should fail to parse from invalid litteral', () => { + expect(() => DateType.Date.parseLiteral({ kind: 'invalid type' })) + .toThrow('Cannot parse date of type invalid type') + }); }); }); diff --git a/spec/graphqlUserAuth.spec.js b/spec/graphqlUserAuth.spec.js index 8702d5777ab..e5307c2075e 100644 --- a/spec/graphqlUserAuth.spec.js +++ b/spec/graphqlUserAuth.spec.js @@ -4,7 +4,7 @@ const Auth = require('../lib/Auth').Auth; const { graphql } = require('graphql'); -describe('graphQLUserAuth', () => { +describe('graphQL UserAuth', () => { let schema; let root; let config; diff --git a/src/graphql/execute.js b/src/graphql/execute.js index 25bfaea68a3..16f435409c9 100644 --- a/src/graphql/execute.js +++ b/src/graphql/execute.js @@ -26,29 +26,26 @@ function toGraphQLResult(className) { } } -function transform(constraintKey, currentValue) { - let value = currentValue; - if (constraintKey === 'nearSphere') { +export function transformQueryConstraint(key, value) { + if (key === 'nearSphere') { value = { - latitude: currentValue.point.latitude, - longitude: currentValue.point.longitude, + latitude: value.point.latitude, + longitude: value.point.longitude, } } - const key = `$${constraintKey}`; - return { - key, + key: `$${key}`, value, } } function transformQuery(query) { - Object.keys(query).forEach((key) => { - Object.keys(query[key]).forEach((constraintKey) => { - const constraint = query[key][constraintKey]; - delete query[key][constraintKey]; - const result = transform(constraintKey, constraint); - query[key][result.key] = result.value; + Object.keys(query).forEach((queryKey) => { + Object.keys(query[queryKey]).forEach((constraintKey) => { + const constraint = query[queryKey][constraintKey]; + delete query[queryKey][constraintKey]; + const { key, value } = transformQueryConstraint(constraintKey, constraint); + query[queryKey][key] = value; }); }); return query; From 708e39740df8ffa45908e2e8a0e424d7c79dc6c7 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 1 Sep 2018 09:50:32 -0400 Subject: [PATCH 58/62] Adds test for issue that prevents the user from having all its info --- spec/graphqlUserAuth.spec.js | 37 ++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/spec/graphqlUserAuth.spec.js b/spec/graphqlUserAuth.spec.js index e5307c2075e..8d05bc8b773 100644 --- a/spec/graphqlUserAuth.spec.js +++ b/spec/graphqlUserAuth.spec.js @@ -8,6 +8,7 @@ describe('graphQL UserAuth', () => { let schema; let root; let config; + let context; async function reload() { const Schema = new GraphQLParseSchema('test'); const result = await Schema.load(); @@ -18,6 +19,34 @@ describe('graphQL UserAuth', () => { beforeEach(async () => { config = Config.get('test'); await reload(); + context = { + config, + auth: new Auth({config}) + }; + }); + + it('can create a user and returns the email', async () => { + const input = { + username: 'luke_skywalker', + email: 'luke@therebellion', + password: 'strong the force is with me' + }; + const result = await graphql(schema, ` + mutation createUser($input: AddUserInput) { + addUser(input: $input) { + object { + username + email + sessionToken + password + } + } + } + `, root, context, { input }); + expect(result.data.addUser.object.username).toBe(input.username); + expect(result.data.addUser.object.email).toBe(input.email); + expect(result.data.addUser.object.sessionToken).toBeDefined(); + expect(result.data.addUser.object.password).toBe(null); }); it('can login with username', async () => { @@ -27,10 +56,6 @@ describe('graphQL UserAuth', () => { email: 'luke@therebellion', password: 'strong the force is with me' }); - const context = { - config, - auth: new Auth({config}) - } const input = { username: 'luke_skywalker', password: 'strong the force is with me' @@ -142,10 +167,6 @@ describe('graphQL UserAuth', () => { }); it('fails to get the currentUser when logged out', async () => { - const context = { - config, - auth: new Auth({ config }), - }; const result = await graphql(schema, ` query me { currentUser { From 0c207eaa05cd567cf828c6f2ca4a4c85632a1ea2 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 1 Sep 2018 09:56:32 -0400 Subject: [PATCH 59/62] Properly re-fetch the auth after creating a user --- src/graphql/schemas/ParseClass.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index 68fc0c39759..0519064b529 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -1,4 +1,5 @@ import { runFind, runGet, resolvePointer, rest, connectionResultsArray, parseID, getGloballyUniqueId } from '../execute'; +import { getAuthForSessionToken } from '../../Auth'; import { GraphQLObjectType, @@ -180,12 +181,21 @@ export function loadClass(className, schema) { description: `use this method to create a new ${className}`, args: { input: { type: inputType }}, resolve: async (root, args, context, info) => { + let { auth } = context; + const { config } = context; const input = transformInput(args.input, schema[className]); const clientMutationId = input.clientMutationId; delete input.clientMutationId; - const res = await rest.create(context.config, context.auth, className, input); + const res = await rest.create(config, auth, className, input); + if (className === '_User' && res.response && res.response.sessionToken) { + auth = await getAuthForSessionToken({ + config, + installationId: context.info && context.info.installationId, + sessionToken: res.response.sessionToken, + }); + } // Run get to match graphQL style - const object = await runGet(context, info, className, res.response.objectId); + const object = await runGet({ auth, config }, info, className, res.response.objectId); return { object, clientMutationId }; } }; From c314e7c4d6ceb55808575c51375d2f52b078404d Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 1 Sep 2018 09:56:51 -0400 Subject: [PATCH 60/62] Properly drop the DB on startup --- spec/helper.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/helper.js b/spec/helper.js index efa3f3f70a4..6e5daa34e50 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -156,7 +156,10 @@ const Parse = require('parse/node'); Parse.serverURL = 'http://localhost:' + port + '/1'; // 10 minutes timeout -beforeAll(startDB, 10 * 60 * 1000); +beforeAll(async () => { + await startDB(); + await databaseAdapter.deleteAllClasses(false); +}, 10 * 60 * 1000); afterAll(stopDB); From 0a5a203c3897acd2fb816bcb3362a0ece7b2a188 Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Sat, 1 Sep 2018 09:59:43 -0400 Subject: [PATCH 61/62] Adds warning about loading inexistant class --- src/graphql/schemas/ParseClass.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/graphql/schemas/ParseClass.js b/src/graphql/schemas/ParseClass.js index 0519064b529..7e73b45798c 100644 --- a/src/graphql/schemas/ParseClass.js +++ b/src/graphql/schemas/ParseClass.js @@ -259,9 +259,22 @@ export class ParseClass { } this.schema = schema; this.class = this.schema[className]; + if (!this.class) { + /* eslint-disable no-console */ + console.warn(`Attempting to load a class (${this.className}) that doesn't exist...`); + console.trace(); + /* eslint-enable no-console */ + } } buildFields(mapper, filterReserved = false, isObject = false) { + if (!this.class) { + /* eslint-disable no-console */ + console.warn(`Attempting to build fields a class (${this.className}) that doesn't exist...`); + console.trace(); + /* eslint-enable no-console */ + return; + } const fields = this.class.fields; const initial = {}; if (isObject) { From 0c9ab2ff273baf38400da8f6e967c2b4e36271e8 Mon Sep 17 00:00:00 2001 From: Miro Yovchev Date: Sat, 1 Sep 2018 20:00:30 +0300 Subject: [PATCH 62/62] Add formatError option --- src/ParseServer.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index 73073c0a28e..9a9774a556d 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -158,10 +158,19 @@ class ParseServer { // TODO: only await perhaps once, and optimize perf const { schema, rootValue } = await ParseServer.getGraphQLSchema(); + + const formatError = (error) => ({ + message: error.message, + code: error.originalError && error.originalError.code, + locations: error.locations, + path: error.path + }) + return { schema, rootValue, - graphiql + graphiql, + formatError } }); }