├── .babelrc ├── .gitignore ├── README.md ├── __test__ ├── apollo │ ├── .env │ ├── .gitignore │ ├── config │ │ └── db.js │ ├── index.js │ ├── models │ │ ├── index.js │ │ └── listing.js │ ├── package-lock.json │ ├── package.json │ ├── resolvers │ │ ├── index.js │ │ ├── mutations │ │ │ ├── createListing.js │ │ │ ├── deleteListing.js │ │ │ ├── index.js │ │ │ └── updateListing.js │ │ └── queries │ │ │ ├── index.js │ │ │ ├── listing.js │ │ │ └── listings.js │ ├── sample_queries │ │ ├── create-listing.js │ │ ├── delete-listing.js │ │ ├── read-listings.js │ │ ├── sample-query.js │ │ └── update-listing.js │ └── types │ │ ├── Listing.js │ │ ├── index.js │ │ └── typesExport.js ├── enzyme │ ├── Containers.test.js │ └── GraphContext.test.js └── express-server │ ├── .gitignore │ ├── app.js │ ├── db │ ├── comments-models.js │ └── movie-models.js │ ├── package-lock.json │ ├── package.json │ ├── sample_queries │ ├── comment-mutation.js │ ├── main-comment-query.js │ ├── main-movie-query.js │ └── min-movie-query.js │ └── schema │ ├── schema.js │ └── types.js ├── contributing.md ├── dist └── bundle.js ├── index.html ├── jest.config.js ├── nuqleus_npm_package ├── .gitignore ├── README.md ├── index.html ├── main.js ├── nuqleus.js ├── package-lock.json ├── package.json ├── public │ ├── client │ │ └── dist │ │ │ └── bundle.js │ └── src │ │ └── assets │ │ └── images │ │ ├── nuQLeus_favicon_32x32.png │ │ ├── nuQLeus_logo.png │ │ ├── nuQLeus_logo_black_background.png │ │ └── nuQLeus_logo_reversed.png └── server.js ├── package-lock.json ├── package.json ├── src ├── assets │ ├── gifs │ │ └── nuqleus_showcase.gif │ └── images │ │ ├── nuQLeus_favicon_32x32.png │ │ ├── nuQLeus_logo.png │ │ ├── nuQLeus_logo_black_background.png │ │ └── nuQLeus_logo_reversed.png └── client │ ├── App.jsx │ ├── components │ ├── EmptyTracingData.jsx │ ├── NavBar.jsx │ ├── OutputDisplay.jsx │ ├── QueryField.jsx │ ├── ServerField.jsx │ ├── VariableField.jsx │ └── VisualDisplay.jsx │ ├── containers │ ├── LeftContainer.jsx │ ├── MainContainer.jsx │ ├── MiddleContainer.jsx │ └── RightContainer.jsx │ ├── contexts │ └── GraphContext.jsx │ ├── index.jsx │ └── stylesheets │ ├── editor-theme.css │ └── styles.css ├── tsconfig.json └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ds_store 2 | node_modules/ 3 | .env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nuQLeus 2 | 3 | Boost GraphQL endpoint testing capabilities with resolver-level performance metrics. 4 | 5 |
6 | 7 |
8 | 9 | 10 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/oslabs-beta/NuQLeus) 11 | ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg) 12 | 13 | ## Table of Contents 14 | 15 | - [Description](#description) 16 | - [Motivation](#motivation) 17 | - [Features](#features) 18 | - [Getting Started with nuQLeus](#Getting-Started-with-nuQLeus) 19 | - [Install nuQLeus](#Install-nuQLeus) 20 | - [Apollo Server Setup](#Apollo-Server-Setup) 21 | - [Express-GraphQL Setup](#Express-GraphQL-Setup) 22 | - [Using the nuQLeus GUI](#Using-the-nuQLeus-GUI) 23 | - [Technologies](#technologies) 24 | - [Contributing](#contributing) 25 | - [Areas for Improvement](#areasforimprovement) 26 | - [Authors](#authors) 27 | - [License](#license) 28 | - [Links/Contact](Links/Contacts) 29 | 30 |
31 | 32 | ## Description 33 | 34 | NuQLeus is a lightweight, self-contained, GraphQL endpoint testing GUI that extracts resolver-level tracing performance metrics from GraphQL queries and mutations and allows an enduser to easily identify specific resolvers that are having a detrimental impact on application performance. 35 | 36 |
37 | 38 | ## Motivation 39 | 40 | 1. There is a considerable amount of overhead associated with building out a GraphQL API for the first time. 41 | 2. GraphQL APIs can be created using a multitude of different libraries such as Apollo Server and Express-GraphQL. 42 | 3. The library that is selected to build a GraphQL API can limit what features and metadata are accessible. 43 | 44 | The motivation for nuQLeus stems from the fact that there are a multitude of different libraries, each with varying sets of features, that can be used to implement a GraphQL API. The library that an enduser selected when first creating a GraphQL API can limit which metadata they will have access to; for example, GraphQL APIs setup via Apollo Server have access to resolver-level tracing metrics whle servers created with Express-GraphQL will not. nuQLeus seeks to help bridge this feature gap and enable an enduser to extract resolver-level tracing data from queries and mutations, regardless of which server they used to initially create their API. 45 | 46 |
47 | 48 | ## Features 49 | 50 | * Simulate GraphQL queries and mutations in the nuQLeus environment 51 | * Real-time tracing durations at individual resolver-level for GraphQL queries and mutations 52 | * Processing and visualization of tracing data to help identify performance drop-offs at a glance 53 | 54 |
55 | 56 | ## Getting Started With nuQLeus 57 | 58 | ### **Install nuQLeus** 59 | 60 | ```javascript 61 | npm install --save-dev nuqleus 62 | ``` 63 | 64 |
65 | 66 | ### **Apollo Server Setup** 67 | 68 | The Apollo Server constructor function accepts an object with properties that convey schema options. Simply pass the existing options object into the `nuqleus.ApolloWrapOptions` method to return a new options object to be passed into the `ApolloServer` constructor. 69 | 70 | ```javascript 71 | const { ApolloServer } = require('apollo-server'); 72 | const connectDb = require('./config/db'); 73 | const typeDefs = require('./types'); 74 | const resolvers = require('./resolvers'); 75 | const models = require('./models'); 76 | import nuqleus from 'nuqleus'; 77 | 78 | connectDb(); 79 | 80 | // 1. Define your context either as an object or function // 81 | // Add your models in here, if any // 82 | const userContext = { 83 | models, 84 | sampleObject: {}, 85 | }; 86 | 87 | // 2. Define your formatResponse, if any, as an object // 88 | const userFormatResponse = { 89 | formatResponse: (res, reqContext) => { 90 | res.http = { 91 | someUserHttp: { 92 | startTime: new Date().toISOString(), 93 | endTime: new Date(Date.now()).toISOString(), 94 | } 95 | }; 96 | } 97 | }; 98 | 99 | // 3. Create a nuqleusServerObject using the ApolloWrapOptions method // 100 | // Allowable inputs: typeDefs, resolvers, context, formatResponse, ...resolverInputs // 101 | const nuqleusServerObject = nuqleus.ApolloWrapOptions( 102 | typeDefs, resolvers, userContext, userFormatResponse 103 | ); 104 | 105 | /** 106 | * If any resolverInputs will be inputted, they will be wrapped after 107 | * the nuQLeusTraceResolver around your schema with the applyMiddleware method. 108 | * 109 | * const schemaWithMiddleWare = applyMiddleware(clientSchema, nuqleusTraceResolvers, ...clientInputs); 110 | */ 111 | 112 | // 4. Spread the nuqleusServerObject into your new ApolloServer instance // 113 | // If you have any add'l options, add them in after 114 | const server = new ApolloServer({ 115 | ...nuqleusServerObject, 116 | // add'l user options, 117 | // add'l user options, 118 | }); 119 | 120 | // 5. Run your server / 121 | server.listen(4001).then(({ url }) => { 122 | console.log(`🚀 Server ready at ${url}`); 123 | }); 124 | ``` 125 | 126 |
127 | 128 | ### **Express-GraphQL Setup** 129 | 130 | Express-graphql's `graphqlHTTP` function accepts an options parameter that can either be an object or a function. Simply pass existing options into `nuqleus.WrapOptions` function to return a new options callback to pass into `graphqlHTTP`. 131 | 132 | If users have `extensions`, simply pass the `extensions` callback into `nuqleus.WrapOptions` as a second optional argument. 133 | 134 | ```js 135 | const express = require('express'); 136 | const { graphqlHTTP } = require('express-graphql'); 137 | const schema = require('./schema'); 138 | const models = require('./models'); 139 | const nuqleus = require('./nuqleus'); 140 | 141 | const extensions = ({ document, variables, operationName, result, context }) => ({ 142 | hello: context.hello, 143 | }); 144 | 145 | const options = { 146 | schema, 147 | context: { models, hello: 'world' }, 148 | graphiql: true, 149 | }; 150 | 151 | const newOptions = nuqleus.WrapOptions(options, extensions); 152 | 153 | app.use('/graphql', graphqlHTTP(newOptions)); 154 | 155 | app.listen(4000, () => { 156 | console.log('Listening on port 4000'); 157 | }); 158 | 159 | ``` 160 | 161 |
162 | 163 | ### **Using the nuQLeus GUI** 164 | The nuQLeus wrapper methods instantiate a server and serve the nuQLeus GUI whenever a user's GraphQL server is initialized. In order to access the nuQLeus GUI, navigate to http://localhost:3030/nuqleus while your server is running. 165 | 166 |
167 | 168 |
169 | 170 |
171 | 172 | ## Technologies 173 | 174 | * GraphQL 175 | * React 176 | * Codemirror 177 | * Graphql-middleware 178 | * Victory 179 | * Express & Node.js 180 | * Jest 181 | * Supertest 182 | * Enzyme 183 | * Webpack 184 | * MongoDB 185 | 186 |
187 | 188 | ## Contributing 189 | 190 | Development of nuQLeus is open source on Hithub through the tech accelerator umbrella OS Labs. We are grateful for the community's contribution to the project. Please read the [contribution documentation](contributing.md) to learn more on how you can participate in improvements. 191 | 192 |
193 | 194 | ## Areas for Improvement 195 | 196 | * Functionality 197 | * Include additional GraphQL servers into nuQLeus coverage 198 | * Ex. graphql-yoga or graphql-helix 199 | * Add tracing capability for GraphQL subscriptions 200 | * Set up a database to store historical user inputs and results 201 | * Allow performance metric comparisons between past and present queries and mutations 202 | * Alternatively, use localStorage for comparisons 203 | * Add onChange user input checks so only valid body/variables can be submitted 204 | * Add introspection capability for users to break down their schemas/queries/mutations in the frontend 205 | * UI/UX 206 | * Use React Router (or other method) to show multiple windows in the 207 | * Include additional visualizations of relevant information users may find helpful 208 | * Convert the Victory graph library to a more widely-used alternative such as D3 209 | * Code Base 210 | * Convert .js and .jsx code to .ts and .tsx 211 | * Implement CORS policy in wrapper for pre-set headers 212 | * Prevent the need for users to have to install and enable CORS 213 | * Refactor and improve code as seen 214 | 215 |
216 | 217 | ## Authors 218 | 219 | * Daniel Perez [Daniel-P3](https://github.com/Daniel-P3) 220 | * Jenny Hai [jhai420](https://github.com/jhai420) 221 | * Joshua Kim [jkim000](https://github.com/jkim000) 222 | * Zach Brucker [zbrucker](https://github.com/zbrucker) 223 | 224 |
225 | 226 | ## License 227 | 228 | [MIT](https://opensource.org/licenses/mit-license.php) 229 | 230 |
231 | 232 | ## Links/Contacts 233 | 234 | * Email: nuqleusjs@gmail.com 235 | * [Landing Page](http://www.nuqleus.io/) 236 | * [npm](https://www.npmjs.com/package/nuqleus) 237 | * [LinkedIn](https://www.linkedin.com/company/nuqleus) 238 | 239 |
240 | 241 | # 242 | ##### [Return to Top](#nuQLeus) -------------------------------------------------------------------------------- /__test__/apollo/.env: -------------------------------------------------------------------------------- 1 | APOLLO_KEY=service:My-Graph-j33azt:8xr9d_mXPg2o0Hjzy2urHQ 2 | APOLLO_GRAPH_VARIANT=current 3 | DATABASE_URL='mongodb+srv://danny:Codesmith41@cluster0.nbdhe.mongodb.net/sample_airbnb?retryWrites=true&w=majority' -------------------------------------------------------------------------------- /__test__/apollo/.gitignore: -------------------------------------------------------------------------------- 1 | .ds_store 2 | node_modules/ 3 | .env -------------------------------------------------------------------------------- /__test__/apollo/config/db.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const mongoose = require('mongoose'); 3 | const DATABASE_URL = process.env.DATABASE_URL; 4 | 5 | //Connect to Database 6 | const connectDb = () => { 7 | return mongoose.connect( 8 | DATABASE_URL, 9 | { 10 | useUnifiedTopology: true, 11 | useNewUrlParser: true, 12 | }, 13 | (err) => { 14 | if (err) { 15 | console.log('Connection to Database failed.'); 16 | } else { 17 | console.log('Database connection successful.'); 18 | } 19 | } 20 | ); 21 | }; 22 | 23 | const db = mongoose.connection; 24 | 25 | db.on('error', console.error.bind(console, 'MongoDB connection error')); 26 | 27 | module.exports = connectDb; 28 | -------------------------------------------------------------------------------- /__test__/apollo/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { ApolloServer } = require('apollo-server'); 3 | const { makeExecutableSchema } = require('@graphql-tools/schema') 4 | const { applyMiddleware } = require('graphql-middleware'); 5 | const connectDb = require('./config/db'); 6 | const typeDefs = require('./types'); 7 | const resolvers = require('./resolvers'); 8 | const models = require('./models'); 9 | const nuqleus = require('nuqleus'); 10 | 11 | connectDb(); 12 | 13 | // SAMPLE TEST INPUTS // 14 | const contextObject = { 15 | models, 16 | joshTest: {}, 17 | }; 18 | // SAMPLE TEST INPUTS // 19 | const contextFunction = ({ req, res }) => ({ 20 | models, 21 | joshText: {}, 22 | }); 23 | // SAMPLE TEST INPUTS // 24 | const fResponse = { 25 | formatResponse: (res, reqContext) => { 26 | res.http = { 27 | joshTracing: { 28 | startTime: new Date(reqContext.context.nuqleusStartTime).toISOString(), 29 | endTime: new Date(Date.now()).toISOString(), 30 | } 31 | }; 32 | } 33 | }; 34 | 35 | // WHAT THE USER NEEDS TO CREATE 36 | const nuqleusServerObject = nuqleus.ApolloWrapOptions( 37 | typeDefs, resolvers, contextFunction, fResponse 38 | ); 39 | 40 | const server = new ApolloServer({ 41 | ...nuqleusServerObject, 42 | }); 43 | 44 | server.listen(4001).then(({ url }) => { 45 | console.log(`🚀 Server ready at ${url}`); 46 | }); 47 | -------------------------------------------------------------------------------- /__test__/apollo/models/index.js: -------------------------------------------------------------------------------- 1 | const { Listing } = require('./listing'); 2 | 3 | module.exports = { 4 | Listing, 5 | }; 6 | -------------------------------------------------------------------------------- /__test__/apollo/models/listing.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { Schema } = mongoose; 3 | 4 | const ListingAndReviewsSchema = new Schema({ 5 | _id: { 6 | type: String, 7 | trim: true, 8 | }, 9 | 10 | listing_url: { 11 | type: String, 12 | trim: true, 13 | }, 14 | 15 | name: { 16 | type: String, 17 | trim: true, 18 | }, 19 | 20 | summary: { 21 | type: String, 22 | trim: true, 23 | }, 24 | 25 | space: { 26 | type: String, 27 | trim: true, 28 | }, 29 | 30 | description: { 31 | type: String, 32 | trim: true, 33 | }, 34 | 35 | neighborhood_overview: { 36 | type: String, 37 | trim: true, 38 | }, 39 | 40 | notes: { 41 | type: String, 42 | trim: true, 43 | }, 44 | 45 | transit: { 46 | type: String, 47 | trim: true, 48 | }, 49 | 50 | access: { 51 | type: String, 52 | trim: true, 53 | }, 54 | 55 | interaction: { 56 | type: String, 57 | trim: true, 58 | }, 59 | 60 | house_rules: { 61 | type: String, 62 | trim: true, 63 | }, 64 | 65 | property_type: { 66 | type: String, 67 | trim: true, 68 | }, 69 | 70 | room_type: { 71 | type: String, 72 | trim: true 73 | }, 74 | 75 | bed_type: { 76 | type: String, 77 | trim: true 78 | }, 79 | 80 | minimum_nights: { 81 | type: String, 82 | trim: true 83 | }, 84 | 85 | maximum_nights: { 86 | type: String, 87 | trim: true 88 | }, 89 | 90 | cancellation_policy: { 91 | type: String, 92 | trim: true 93 | }, 94 | 95 | last_scraped: { 96 | type: Date, 97 | }, 98 | 99 | calendar_last_scraped: { 100 | type: Date, 101 | }, 102 | 103 | accommodates: { 104 | type: Number 105 | }, 106 | 107 | bedrooms: { 108 | type: Number 109 | }, 110 | 111 | beds: { 112 | type: Number 113 | }, 114 | 115 | number_of_reviews: { 116 | type: Number, 117 | }, 118 | 119 | bathrooms: { 120 | type: Number, 121 | }, 122 | 123 | amenities: { 124 | type: [String], 125 | }, 126 | 127 | price: { 128 | type: Number, 129 | }, 130 | 131 | extra_people: { 132 | type: Number, 133 | }, 134 | 135 | guests_included: { 136 | type: Number, 137 | }, 138 | 139 | reviews: { 140 | type: [String], 141 | }, 142 | 143 | images: { 144 | type: Object, 145 | }, 146 | 147 | host: { 148 | type: Object, 149 | }, 150 | 151 | address: { 152 | type: Object, 153 | }, 154 | 155 | availability: { 156 | type: Object, 157 | }, 158 | 159 | review_scores: { 160 | type: [String], 161 | }, 162 | }); 163 | 164 | const Listing = mongoose.model('listing', ListingAndReviewsSchema); 165 | 166 | module.exports = { Listing }; 167 | -------------------------------------------------------------------------------- /__test__/apollo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@graphql-tools/schema": "^7.1.3", 14 | "apollo-server": "^2.21.0", 15 | "apollo-server-express": "^2.21.0", 16 | "dotenv": "^8.2.0", 17 | "express": "^4.17.1", 18 | "graphql": "^15.5.0", 19 | "graphql-middleware": "^6.0.4", 20 | "mongoose": "^5.11.18", 21 | "nodemon": "^2.0.7", 22 | "nuqleus": "^0.1.0", 23 | "open": "^8.0.4" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /__test__/apollo/resolvers/index.js: -------------------------------------------------------------------------------- 1 | const mutations = require('./mutations'); 2 | const queries = require('./queries'); 3 | 4 | module.exports = { 5 | Mutation: { 6 | ...mutations, 7 | }, 8 | Query: { 9 | ...queries, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /__test__/apollo/resolvers/mutations/createListing.js: -------------------------------------------------------------------------------- 1 | const { ApolloError } = require('apollo-server'); 2 | 3 | //Take inputs from models folder and create a new item when the user on nuQLeus creates one 4 | module.exports = async (_, { input }, { models }) => { 5 | try { 6 | const newListing = await models.Listing.create(input); 7 | return newListing; 8 | } catch (e) { 9 | throw new ApolloError(e); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /__test__/apollo/resolvers/mutations/deleteListing.js: -------------------------------------------------------------------------------- 1 | const { ApolloError } = require('apollo-server'); 2 | 3 | //Take the id the user writes down, and delete the item matching the id in the database 4 | module.exports = async (_, { id }, { models }) => { 5 | const deleteListing = await models.Listing.deleteOne({ _id: id }); 6 | 7 | if (deleteListing.deletedCount) return { id: id }; 8 | else throw new ApolloError(`Failed to delete address.`); 9 | }; 10 | -------------------------------------------------------------------------------- /__test__/apollo/resolvers/mutations/index.js: -------------------------------------------------------------------------------- 1 | const createListing = require('./createListing'); 2 | const updateListing = require('./updateListing'); 3 | const deleteListing = require('./deleteListing'); 4 | 5 | module.exports = { 6 | createListing, 7 | updateListing, 8 | deleteListing, 9 | }; 10 | -------------------------------------------------------------------------------- /__test__/apollo/resolvers/mutations/updateListing.js: -------------------------------------------------------------------------------- 1 | const { ApolloError } = require('apollo-server'); 2 | 3 | //Take id and input from the models and update that id with the new input 4 | module.exports = async (_, { id, input }, { models }) => { 5 | try { 6 | const listingToUpdate = await models.Listing.findOne({ _id: id }); 7 | 8 | if (!listingToUpdate) throw new ApolloError(`Could not find listing with id: '${id}'.`, 400); 9 | 10 | //create an array of object keys and for each,replace the current id with the new id 11 | Object.keys(input).forEach((value) => { 12 | listingToUpdate[value] = input[value]; 13 | }); 14 | 15 | const updatedListing = await listingToUpdate.save(); 16 | 17 | return updatedListing; 18 | } catch (e) { 19 | throw new ApolloError(e); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /__test__/apollo/resolvers/queries/index.js: -------------------------------------------------------------------------------- 1 | const listing = require('./listing'); 2 | const listings = require('./listings'); 3 | 4 | module.exports = { 5 | listing, 6 | listings, 7 | }; 8 | -------------------------------------------------------------------------------- /__test__/apollo/resolvers/queries/listing.js: -------------------------------------------------------------------------------- 1 | const { ApolloError } = require('apollo-server'); 2 | 3 | //Given an id from models, search for the appropriate item in the database. 4 | module.exports = async (_, { _id }, { models }) => { 5 | try { 6 | const listingToRead = await models.Listing.findOne({ _id: _id }); 7 | if (!listingToRead) throw new ApolloError(`Could not find listing with id: '${_id}'.`, 400); 8 | return listingToRead; 9 | } 10 | catch (e) { 11 | throw new ApolloError(e); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /__test__/apollo/resolvers/queries/listings.js: -------------------------------------------------------------------------------- 1 | //Search for all items in the database 2 | module.exports = async (_, {}, { models }) => { 3 | return await models.Listing.find(); 4 | }; 5 | -------------------------------------------------------------------------------- /__test__/apollo/sample_queries/create-listing.js: -------------------------------------------------------------------------------- 1 | mutation createListing { 2 | createListing(input: { name: "DogNamedFido", _id: "533327" }) { 3 | name 4 | _id 5 | } 6 | } -------------------------------------------------------------------------------- /__test__/apollo/sample_queries/delete-listing.js: -------------------------------------------------------------------------------- 1 | mutation deleteListing { 2 | deleteListing(id:"533327"){ 3 | id 4 | } 5 | } -------------------------------------------------------------------------------- /__test__/apollo/sample_queries/read-listings.js: -------------------------------------------------------------------------------- 1 | query listings { 2 | listings { 3 | name 4 | _id 5 | } 6 | } 7 | 8 | 9 | // ----- 10 | // query body 11 | query AnyQueryName($_id: ID!) { 12 | listing(_id: $_id) { 13 | name 14 | _id 15 | } 16 | } 17 | // variables 18 | { 19 | "_id": "1003530" 20 | } 21 | // ----- 22 | -------------------------------------------------------------------------------- /__test__/apollo/sample_queries/sample-query.js: -------------------------------------------------------------------------------- 1 | query listings { 2 | listings { 3 | name 4 | price 5 | } 6 | } -------------------------------------------------------------------------------- /__test__/apollo/sample_queries/update-listing.js: -------------------------------------------------------------------------------- 1 | mutation updateListing { 2 | updateListing(id:533327,input:{ 3 | name: "magic" 4 | }){ 5 | name 6 | } 7 | } -------------------------------------------------------------------------------- /__test__/apollo/types/Listing.js: -------------------------------------------------------------------------------- 1 | const { gql } = require('apollo-server'); 2 | 3 | module.exports = gql` 4 | type Listing { 5 | _id: ID! 6 | listing_url: String 7 | name: String! 8 | summary: String 9 | space: String 10 | description: String 11 | neighborhood_overview: String 12 | notes: String 13 | transit: String 14 | access: String 15 | interaction: String 16 | house_rules: String 17 | property_type: String 18 | room_type: String 19 | bed_type: String 20 | minimum_nights: String 21 | maximum_nights: String 22 | cancellation_policy: String 23 | last_scraped: String 24 | calendar_last_scraped: String 25 | accommodates: Int 26 | bedrooms: Int 27 | beds: Int 28 | number_of_reviews: Int 29 | bathrooms: Float 30 | amenities: [String] 31 | price: Float 32 | extra_people: Float 33 | guests_included: Int 34 | reviews: [String] 35 | } 36 | 37 | input CreateListingInput { 38 | _id: ID! 39 | listing_url: String 40 | name: String! 41 | summary: String 42 | space: String 43 | description: String 44 | neighborhood_overview: String 45 | notes: String 46 | transit: String 47 | access: String 48 | interaction: String 49 | house_rules: String 50 | property_type: String 51 | room_type: String 52 | bed_type: String 53 | minimum_nights: String 54 | maximum_nights: String 55 | cancellation_policy: String 56 | last_scraped: String 57 | calendar_last_scraped: String 58 | accommodates: Int 59 | bedrooms: Int 60 | beds: Int 61 | number_of_reviews: Int 62 | bathrooms: Float 63 | amenities: [String] 64 | price: Float 65 | extra_people: Float 66 | guests_included: Int 67 | reviews: [String] 68 | } 69 | 70 | input UpdateListingInput { 71 | listing_url: String 72 | name: String! 73 | summary: String 74 | space: String 75 | description: String 76 | neighborhood_overview: String 77 | notes: String 78 | transit: String 79 | access: String 80 | interaction: String 81 | house_rules: String 82 | property_type: String 83 | room_type: String 84 | bed_type: String 85 | minimum_nights: String 86 | maximum_nights: String 87 | cancellation_policy: String 88 | last_scraped: String 89 | calendar_last_scraped: String 90 | accommodates: Int 91 | bedrooms: Int 92 | beds: Int 93 | number_of_reviews: Int 94 | bathrooms: Float 95 | amenities: [String] 96 | price: Float 97 | extra_people: Float 98 | guests_included: Int 99 | reviews: [String] 100 | } 101 | 102 | input DeleteListingInput { 103 | id: ID! 104 | listing_url: String 105 | name: String! 106 | summary: String 107 | space: String 108 | description: String 109 | neighborhood_overview: String 110 | notes: String 111 | transit: String 112 | access: String 113 | interaction: String 114 | house_rules: String 115 | property_type: String 116 | room_type: String 117 | bed_type: String 118 | minimum_nights: String 119 | maximum_nights: String 120 | cancellation_policy: String 121 | last_scraped: String 122 | calendar_last_scraped: String 123 | accommodates: Int 124 | bedrooms: Int 125 | beds: Int 126 | number_of_reviews: Int 127 | bathrooms: Float 128 | amenities: [String] 129 | price: Float 130 | extra_people: Float 131 | guests_included: Int 132 | reviews: [String] 133 | } 134 | 135 | type DeletePayload { 136 | id: ID! 137 | listing_url: String 138 | name: String! 139 | summary: String 140 | space: String 141 | description: String 142 | neighborhood_overview: String 143 | notes: String 144 | transit: String 145 | access: String 146 | interaction: String 147 | house_rules: String 148 | property_type: String 149 | room_type: String 150 | bed_type: String 151 | minimum_nights: String 152 | maximum_nights: String 153 | cancellation_policy: String 154 | last_scraped: String 155 | calendar_last_scraped: String 156 | accommodates: Int 157 | bedrooms: Int 158 | beds: Int 159 | number_of_reviews: Int 160 | bathrooms: Float 161 | amenities: [String] 162 | price: Float 163 | extra_people: Float 164 | guests_included: Int 165 | reviews: [String] 166 | } 167 | 168 | #For a query,listing, take in an id, and return that listing 169 | #For listings, return an array of all listings 170 | type Query { 171 | listing(_id: ID!): Listing 172 | listings: [Listing] 173 | } 174 | 175 | #For createListing, take in a required input of what you want to create given the schema 176 | #Given the id and an input according to the schema, return an updated value for that listing 177 | #for an id, delete the payload written by the user 178 | type Mutation { 179 | createListing(input: CreateListingInput!): Listing! 180 | updateListing(id: ID!, input: UpdateListingInput!): Listing! 181 | deleteListing(id: ID!): DeletePayload! 182 | } 183 | `; 184 | -------------------------------------------------------------------------------- /__test__/apollo/types/index.js: -------------------------------------------------------------------------------- 1 | const Listing = require('./Listing'); 2 | 3 | module.exports = [Listing]; 4 | -------------------------------------------------------------------------------- /__test__/apollo/types/typesExport.js: -------------------------------------------------------------------------------- 1 | const Listing = require("./Listing"); 2 | 3 | module.exports = [Listing]; 4 | -------------------------------------------------------------------------------- /__test__/enzyme/Containers.test.js: -------------------------------------------------------------------------------- 1 | // const serverField = require('../ServerField'); 2 | import MainContainer from '../../src/client/containers/MainContainer'; 3 | import LeftContainer from '../../src/client/containers/LeftContainer'; 4 | import MiddleContainer from '../../src/client/containers/MiddleContainer'; 5 | import RightContainer from '../../src/client/containers/RightContainer'; 6 | import Enzyme, { shallow, mount, render } from 'enzyme'; 7 | 8 | // Enzyme test to check MainContainer rendering 9 | describe('Should render MainContainer', () => { 10 | let container = shallow(); 11 | 12 | it('Renders a div and three other container divs', () => { 13 | expect(container.type()).toEqual('div'); 14 | expect(container.find('LeftContainer').length === 1, true); 15 | expect(container.find('RightContainer').length === 1, true); 16 | expect(container.find('MiddleContainer').length === 1, true); 17 | }) 18 | 19 | // it('render correctly text component', () => { 20 | // const TextInputComponent = renderer.create().toJSON(); 21 | // expect(TextInputComponent).toMatchSnapshot(); 22 | // }); 23 | 24 | }); 25 | 26 | // Enzyme test to check LeftContainer rendering 27 | xdescribe('Should render LeftContainer', () => { 28 | 29 | 30 | }); 31 | // Enzyme test to check MiddleContainer rendering 32 | xdescribe('Should render MiddleContainer', () => { 33 | 34 | }); 35 | 36 | // Enzyme test to check RightContainer rendering 37 | xdescribe('Should render RightContainer', () => { 38 | 39 | }); 40 | -------------------------------------------------------------------------------- /__test__/enzyme/GraphContext.test.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { GraphContext, GraphContextProvider } from '../../src/clie/G/contextsraphContext'; 3 | import Enzyme, { mount } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | 6 | Enzyme.configure({ adapter: new Adapter() }); 7 | 8 | describe('Check the server input and updated value after using setInfo', () => { 9 | const TestComponent = () => { 10 | // const [info, setInfo] = useContext(GraphContext); 11 | const { info, setInfo } = useContext(GraphContext); 12 | const testSetInfo = () => { 13 | setInfo(() => ({ 14 | ...info, 15 | uri: 'http://localhost:1337', 16 | response: {resTest: 'some updated response data'}, 17 | extensions: {extTest: 'some random test object'}, 18 | queryTime: '6ms', 19 | })) 20 | }; 21 | 22 | return ( 23 | <> 24 |
{info.uri.toString()}
25 | 26 | 27 | ); 28 | }; 29 | 30 | const wrapper = mount( 31 | 32 | 33 | 34 | ); 35 | 36 | expect(wrapper.find('[data-testid="value"]').text().toEqual('http://localhost:4001')); 37 | 38 | wrapper.find('button').simulate('click'); 39 | 40 | expect(wrapper.find('[data-testid="value"]').text().toEqual('http://localhost:1337')); 41 | }); 42 | -------------------------------------------------------------------------------- /__test__/express-server/.gitignore: -------------------------------------------------------------------------------- 1 | .ds_store 2 | node_modules/ 3 | .env -------------------------------------------------------------------------------- /__test__/express-server/app.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const express = require('express'); 3 | const { graphqlHTTP } = require('express-graphql'); 4 | const cors = require('cors'); 5 | const mongoose = require('mongoose'); 6 | const schema = require('./schema/schema'); 7 | const app = express(); 8 | 9 | /** Require in nuQLeus package **/ 10 | 11 | const nuqleus = require('nuqleus'); 12 | 13 | 14 | /** Initialize cors and process requests as a JSON **/ 15 | 16 | app.use(cors()); 17 | app.use(express.json()); 18 | 19 | /** 20 | * @params { document, variables, operationName, result, context } 21 | * @returns optional extensions object to be passed in options object 22 | * 23 | **/ 24 | const extensions = ({ document, variables, operationName, result, context }) => ({ 25 | runTime: context.startTime, 26 | query: context.query, 27 | }); 28 | 29 | /** 30 | * @params request, response, graphQLParams 31 | * @returns required options object or callback to be passed in graphqlHTTP function 32 | * 33 | **/ 34 | const options = (request, response, graphQLParams) => ({ 35 | schema, 36 | context: { startTime: 300, query: graphQLParams.query }, 37 | graphiql: true, 38 | }); 39 | 40 | 41 | /** 42 | * @params options, extensions(optional) 43 | * @returns new options object to be passed in graphQLHTTP function 44 | * nuQleus.WrapOptions adds middleware to the schema and modfies the context 45 | * and extensions fields to enable tracing data and visualization 46 | * 47 | **/ 48 | const newOptions = nuqleus.WrapOptions(options, extensions); 49 | 50 | /** Initiate graphQL route, passing in new options created by nuQLeus **/ 51 | 52 | app.use('/graphql', graphqlHTTP(newOptions)); 53 | 54 | /** Connect to MongoDB **/ 55 | 56 | mongoose 57 | .connect(process.env.MONGO_URI, 58 | { 59 | // options for the connect method to parse the URI 60 | useNewUrlParser: true, 61 | useUnifiedTopology: true, 62 | } 63 | ) 64 | .then(() => console.log('Connected to Mongo DB.')) 65 | .catch((err) => console.log(err)); 66 | 67 | /** Start server on PORT 4000 **/ 68 | 69 | app.listen(4000, () => { 70 | console.log('Listening on port 4000'); 71 | }); 72 | -------------------------------------------------------------------------------- /__test__/express-server/db/comments-models.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const commentSchema = new Schema({ 5 | name: String, 6 | email: String, 7 | movie_id: { type: Schema.Types.ObjectId, ref: 'movie' }, 8 | text: String, 9 | date: Date, 10 | }) 11 | 12 | module.exports = mongoose.model('comment', commentSchema); -------------------------------------------------------------------------------- /__test__/express-server/db/movie-models.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const movieSchema = new Schema({ 5 | title: String, 6 | year: Number, 7 | runtime: Number, 8 | released: Date, 9 | poster: String, 10 | rated: String, 11 | cast: [String], 12 | plot: String, 13 | fullplot: String, 14 | lastupdated: Date, 15 | type: String, 16 | directors: [String], 17 | writers: [String], 18 | imdb: Object, 19 | awards: Object, 20 | languages: [String], 21 | countries: [String], 22 | genres: [String], 23 | tomatoes: Object, 24 | num_mflix_comments: Number 25 | }) 26 | 27 | module.exports = mongoose.model('movie', movieSchema); -------------------------------------------------------------------------------- /__test__/express-server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@ardatan/aggregate-error": { 8 | "version": "0.0.6", 9 | "resolved": "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz", 10 | "integrity": "sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ==", 11 | "requires": { 12 | "tslib": "~2.0.1" 13 | }, 14 | "dependencies": { 15 | "tslib": { 16 | "version": "2.0.3", 17 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", 18 | "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" 19 | } 20 | } 21 | }, 22 | "@graphql-tools/batch-execute": { 23 | "version": "7.1.0", 24 | "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-7.1.0.tgz", 25 | "integrity": "sha512-Yb4QRpHZqDk24+T4K3ARk/KFU26Dyl30XcbYeVvIrgIKcmeON/p3DfSeiB0+MaxYlsv+liQKvlxNbeC2hD31pA==", 26 | "requires": { 27 | "@graphql-tools/utils": "^7.7.0", 28 | "dataloader": "2.0.0", 29 | "is-promise": "4.0.0", 30 | "tslib": "~2.1.0" 31 | } 32 | }, 33 | "@graphql-tools/delegate": { 34 | "version": "7.1.1", 35 | "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-7.1.1.tgz", 36 | "integrity": "sha512-+uV0KZPI070sEykf3uxy+AhirHOqZnqbVqTqcfhH8/97+vdoLPE5oVceCTvMQsC7bDapbcbNiwcpYd8T6OQ4KQ==", 37 | "requires": { 38 | "@ardatan/aggregate-error": "0.0.6", 39 | "@graphql-tools/batch-execute": "^7.1.0", 40 | "@graphql-tools/schema": "^7.0.0", 41 | "@graphql-tools/utils": "^7.7.1", 42 | "dataloader": "2.0.0", 43 | "is-promise": "4.0.0", 44 | "tslib": "~2.1.0" 45 | } 46 | }, 47 | "@graphql-tools/schema": { 48 | "version": "7.1.3", 49 | "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-7.1.3.tgz", 50 | "integrity": "sha512-ZY76hmcJlF1iyg3Im0sQ3ASRkiShjgv102vLTVcH22lEGJeCaCyyS/GF1eUHom418S60bS8Th6+autRUxfBiBg==", 51 | "requires": { 52 | "@graphql-tools/utils": "^7.1.2", 53 | "tslib": "~2.1.0" 54 | } 55 | }, 56 | "@graphql-tools/utils": { 57 | "version": "7.7.1", 58 | "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.7.1.tgz", 59 | "integrity": "sha512-SFT4/dTfrwWer1wSOLU+jqgv3oa/xTR8q+MiNbE9nCH2FXyMsqIOaXKm9wHfKIWFWHozqBdcnwFkQZrdD7H2TQ==", 60 | "requires": { 61 | "@ardatan/aggregate-error": "0.0.6", 62 | "camel-case": "4.1.2", 63 | "tslib": "~2.1.0" 64 | } 65 | }, 66 | "@types/bson": { 67 | "version": "4.0.3", 68 | "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", 69 | "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", 70 | "requires": { 71 | "@types/node": "*" 72 | } 73 | }, 74 | "@types/mongodb": { 75 | "version": "3.6.10", 76 | "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.10.tgz", 77 | "integrity": "sha512-BkwAHFiZSSWdTIqbUVGmgvIsiXXjqAketeK7Izy7oSs6G3N8Bn993tK9eq6QEovQDx6OQ2FGP2KWDDxBzdlJ6Q==", 78 | "requires": { 79 | "@types/bson": "*", 80 | "@types/node": "*" 81 | } 82 | }, 83 | "@types/node": { 84 | "version": "14.14.35", 85 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.35.tgz", 86 | "integrity": "sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==" 87 | }, 88 | "accepts": { 89 | "version": "1.3.7", 90 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 91 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 92 | "requires": { 93 | "mime-types": "~2.1.24", 94 | "negotiator": "0.6.2" 95 | } 96 | }, 97 | "array-flatten": { 98 | "version": "1.1.1", 99 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 100 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 101 | }, 102 | "atomic-sleep": { 103 | "version": "1.0.0", 104 | "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", 105 | "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" 106 | }, 107 | "bl": { 108 | "version": "2.2.1", 109 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", 110 | "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", 111 | "requires": { 112 | "readable-stream": "^2.3.5", 113 | "safe-buffer": "^5.1.1" 114 | } 115 | }, 116 | "bluebird": { 117 | "version": "3.5.1", 118 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 119 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" 120 | }, 121 | "body-parser": { 122 | "version": "1.19.0", 123 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 124 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 125 | "requires": { 126 | "bytes": "3.1.0", 127 | "content-type": "~1.0.4", 128 | "debug": "2.6.9", 129 | "depd": "~1.1.2", 130 | "http-errors": "1.7.2", 131 | "iconv-lite": "0.4.24", 132 | "on-finished": "~2.3.0", 133 | "qs": "6.7.0", 134 | "raw-body": "2.4.0", 135 | "type-is": "~1.6.17" 136 | } 137 | }, 138 | "bson": { 139 | "version": "1.1.6", 140 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", 141 | "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" 142 | }, 143 | "bytes": { 144 | "version": "3.1.0", 145 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 146 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 147 | }, 148 | "camel-case": { 149 | "version": "4.1.2", 150 | "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", 151 | "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", 152 | "requires": { 153 | "pascal-case": "^3.1.2", 154 | "tslib": "^2.0.3" 155 | } 156 | }, 157 | "content-disposition": { 158 | "version": "0.5.3", 159 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 160 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 161 | "requires": { 162 | "safe-buffer": "5.1.2" 163 | } 164 | }, 165 | "content-type": { 166 | "version": "1.0.4", 167 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 168 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 169 | }, 170 | "cookie": { 171 | "version": "0.4.0", 172 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 173 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 174 | }, 175 | "cookie-signature": { 176 | "version": "1.0.6", 177 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 178 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 179 | }, 180 | "core-util-is": { 181 | "version": "1.0.2", 182 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 183 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 184 | }, 185 | "cors": { 186 | "version": "2.8.5", 187 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 188 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 189 | "requires": { 190 | "object-assign": "^4", 191 | "vary": "^1" 192 | } 193 | }, 194 | "dataloader": { 195 | "version": "2.0.0", 196 | "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz", 197 | "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==" 198 | }, 199 | "debug": { 200 | "version": "2.6.9", 201 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 202 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 203 | "requires": { 204 | "ms": "2.0.0" 205 | } 206 | }, 207 | "define-lazy-prop": { 208 | "version": "2.0.0", 209 | "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", 210 | "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" 211 | }, 212 | "denque": { 213 | "version": "1.5.0", 214 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", 215 | "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" 216 | }, 217 | "depd": { 218 | "version": "1.1.2", 219 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 220 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 221 | }, 222 | "destroy": { 223 | "version": "1.0.4", 224 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 225 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 226 | }, 227 | "dotenv": { 228 | "version": "8.2.0", 229 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", 230 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" 231 | }, 232 | "ee-first": { 233 | "version": "1.1.1", 234 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 235 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 236 | }, 237 | "encodeurl": { 238 | "version": "1.0.2", 239 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 240 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 241 | }, 242 | "escape-html": { 243 | "version": "1.0.3", 244 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 245 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 246 | }, 247 | "etag": { 248 | "version": "1.8.1", 249 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 250 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 251 | }, 252 | "express": { 253 | "version": "4.17.1", 254 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 255 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 256 | "requires": { 257 | "accepts": "~1.3.7", 258 | "array-flatten": "1.1.1", 259 | "body-parser": "1.19.0", 260 | "content-disposition": "0.5.3", 261 | "content-type": "~1.0.4", 262 | "cookie": "0.4.0", 263 | "cookie-signature": "1.0.6", 264 | "debug": "2.6.9", 265 | "depd": "~1.1.2", 266 | "encodeurl": "~1.0.2", 267 | "escape-html": "~1.0.3", 268 | "etag": "~1.8.1", 269 | "finalhandler": "~1.1.2", 270 | "fresh": "0.5.2", 271 | "merge-descriptors": "1.0.1", 272 | "methods": "~1.1.2", 273 | "on-finished": "~2.3.0", 274 | "parseurl": "~1.3.3", 275 | "path-to-regexp": "0.1.7", 276 | "proxy-addr": "~2.0.5", 277 | "qs": "6.7.0", 278 | "range-parser": "~1.2.1", 279 | "safe-buffer": "5.1.2", 280 | "send": "0.17.1", 281 | "serve-static": "1.14.1", 282 | "setprototypeof": "1.1.1", 283 | "statuses": "~1.5.0", 284 | "type-is": "~1.6.18", 285 | "utils-merge": "1.0.1", 286 | "vary": "~1.1.2" 287 | } 288 | }, 289 | "express-graphql": { 290 | "version": "0.12.0", 291 | "resolved": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.12.0.tgz", 292 | "integrity": "sha512-DwYaJQy0amdy3pgNtiTDuGGM2BLdj+YO2SgbKoLliCfuHv3VVTt7vNG/ZqK2hRYjtYHE2t2KB705EU94mE64zg==", 293 | "requires": { 294 | "accepts": "^1.3.7", 295 | "content-type": "^1.0.4", 296 | "http-errors": "1.8.0", 297 | "raw-body": "^2.4.1" 298 | }, 299 | "dependencies": { 300 | "http-errors": { 301 | "version": "1.8.0", 302 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", 303 | "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", 304 | "requires": { 305 | "depd": "~1.1.2", 306 | "inherits": "2.0.4", 307 | "setprototypeof": "1.2.0", 308 | "statuses": ">= 1.5.0 < 2", 309 | "toidentifier": "1.0.0" 310 | } 311 | }, 312 | "inherits": { 313 | "version": "2.0.4", 314 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 315 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 316 | }, 317 | "raw-body": { 318 | "version": "2.4.1", 319 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", 320 | "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", 321 | "requires": { 322 | "bytes": "3.1.0", 323 | "http-errors": "1.7.3", 324 | "iconv-lite": "0.4.24", 325 | "unpipe": "1.0.0" 326 | }, 327 | "dependencies": { 328 | "http-errors": { 329 | "version": "1.7.3", 330 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", 331 | "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", 332 | "requires": { 333 | "depd": "~1.1.2", 334 | "inherits": "2.0.4", 335 | "setprototypeof": "1.1.1", 336 | "statuses": ">= 1.5.0 < 2", 337 | "toidentifier": "1.0.0" 338 | } 339 | }, 340 | "setprototypeof": { 341 | "version": "1.1.1", 342 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 343 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 344 | } 345 | } 346 | }, 347 | "setprototypeof": { 348 | "version": "1.2.0", 349 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 350 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 351 | } 352 | } 353 | }, 354 | "fast-redact": { 355 | "version": "3.0.0", 356 | "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.0.tgz", 357 | "integrity": "sha512-a/S/Hp6aoIjx7EmugtzLqXmcNsyFszqbt6qQ99BdG61QjBZF6shNis0BYR6TsZOQ1twYc0FN2Xdhwwbv6+KD0w==" 358 | }, 359 | "fast-safe-stringify": { 360 | "version": "2.0.7", 361 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", 362 | "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" 363 | }, 364 | "finalhandler": { 365 | "version": "1.1.2", 366 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 367 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 368 | "requires": { 369 | "debug": "2.6.9", 370 | "encodeurl": "~1.0.2", 371 | "escape-html": "~1.0.3", 372 | "on-finished": "~2.3.0", 373 | "parseurl": "~1.3.3", 374 | "statuses": "~1.5.0", 375 | "unpipe": "~1.0.0" 376 | } 377 | }, 378 | "flatstr": { 379 | "version": "1.0.12", 380 | "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz", 381 | "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" 382 | }, 383 | "forwarded": { 384 | "version": "0.1.2", 385 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 386 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 387 | }, 388 | "fresh": { 389 | "version": "0.5.2", 390 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 391 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 392 | }, 393 | "graphql": { 394 | "version": "15.5.0", 395 | "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.5.0.tgz", 396 | "integrity": "sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA==" 397 | }, 398 | "graphql-middleware": { 399 | "version": "6.0.5", 400 | "resolved": "https://registry.npmjs.org/graphql-middleware/-/graphql-middleware-6.0.5.tgz", 401 | "integrity": "sha512-wd1WO3ngF4ERUqN4uVAxynhHzfrNAJcfIXbp1BlIHZOTKCKE7OohyPhjyNXnsouaMUUYBAiUvLZMNunbJ2d7sA==", 402 | "requires": { 403 | "@graphql-tools/delegate": "^7.1.1", 404 | "@graphql-tools/schema": "^7.1.3" 405 | } 406 | }, 407 | "http-errors": { 408 | "version": "1.7.2", 409 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 410 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 411 | "requires": { 412 | "depd": "~1.1.2", 413 | "inherits": "2.0.3", 414 | "setprototypeof": "1.1.1", 415 | "statuses": ">= 1.5.0 < 2", 416 | "toidentifier": "1.0.0" 417 | } 418 | }, 419 | "iconv-lite": { 420 | "version": "0.4.24", 421 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 422 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 423 | "requires": { 424 | "safer-buffer": ">= 2.1.2 < 3" 425 | } 426 | }, 427 | "inherits": { 428 | "version": "2.0.3", 429 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 430 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 431 | }, 432 | "ipaddr.js": { 433 | "version": "1.9.1", 434 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 435 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 436 | }, 437 | "is-docker": { 438 | "version": "2.1.1", 439 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", 440 | "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==" 441 | }, 442 | "is-promise": { 443 | "version": "4.0.0", 444 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", 445 | "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" 446 | }, 447 | "is-wsl": { 448 | "version": "2.2.0", 449 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", 450 | "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", 451 | "requires": { 452 | "is-docker": "^2.0.0" 453 | } 454 | }, 455 | "isarray": { 456 | "version": "1.0.0", 457 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 458 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 459 | }, 460 | "kareem": { 461 | "version": "2.3.2", 462 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", 463 | "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" 464 | }, 465 | "lower-case": { 466 | "version": "2.0.2", 467 | "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", 468 | "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", 469 | "requires": { 470 | "tslib": "^2.0.3" 471 | } 472 | }, 473 | "media-typer": { 474 | "version": "0.3.0", 475 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 476 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 477 | }, 478 | "memory-pager": { 479 | "version": "1.5.0", 480 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 481 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 482 | "optional": true 483 | }, 484 | "merge-descriptors": { 485 | "version": "1.0.1", 486 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 487 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 488 | }, 489 | "methods": { 490 | "version": "1.1.2", 491 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 492 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 493 | }, 494 | "mime": { 495 | "version": "1.6.0", 496 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 497 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 498 | }, 499 | "mime-db": { 500 | "version": "1.46.0", 501 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", 502 | "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" 503 | }, 504 | "mime-types": { 505 | "version": "2.1.29", 506 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", 507 | "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", 508 | "requires": { 509 | "mime-db": "1.46.0" 510 | } 511 | }, 512 | "mongodb": { 513 | "version": "3.6.5", 514 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.5.tgz", 515 | "integrity": "sha512-mQlYKw1iGbvJJejcPuyTaytq0xxlYbIoVDm2FODR+OHxyEiMR021vc32bTvamgBjCswsD54XIRwhg3yBaWqJjg==", 516 | "requires": { 517 | "bl": "^2.2.1", 518 | "bson": "^1.1.4", 519 | "denque": "^1.4.1", 520 | "require_optional": "^1.0.1", 521 | "safe-buffer": "^5.1.2", 522 | "saslprep": "^1.0.0" 523 | } 524 | }, 525 | "mongoose": { 526 | "version": "5.12.2", 527 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.12.2.tgz", 528 | "integrity": "sha512-kT9t6Nvu9WPsfssn7Gzke446Il8UdMilY7Sa5vALtwoOoNOGtZEVjekZBFwsBFzTWtBA/x5gBmJoYFP+1LeDlg==", 529 | "requires": { 530 | "@types/mongodb": "^3.5.27", 531 | "bson": "^1.1.4", 532 | "kareem": "2.3.2", 533 | "mongodb": "3.6.5", 534 | "mongoose-legacy-pluralize": "1.0.2", 535 | "mpath": "0.8.3", 536 | "mquery": "3.2.4", 537 | "ms": "2.1.2", 538 | "regexp-clone": "1.0.0", 539 | "safe-buffer": "5.2.1", 540 | "sift": "7.0.1", 541 | "sliced": "1.0.1" 542 | }, 543 | "dependencies": { 544 | "ms": { 545 | "version": "2.1.2", 546 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 547 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 548 | }, 549 | "safe-buffer": { 550 | "version": "5.2.1", 551 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 552 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 553 | } 554 | } 555 | }, 556 | "mongoose-legacy-pluralize": { 557 | "version": "1.0.2", 558 | "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", 559 | "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" 560 | }, 561 | "mpath": { 562 | "version": "0.8.3", 563 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", 564 | "integrity": "sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA==" 565 | }, 566 | "mquery": { 567 | "version": "3.2.4", 568 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.4.tgz", 569 | "integrity": "sha512-uOLpp7iRX0BV1Uu6YpsqJ5b42LwYnmu0WeF/f8qgD/On3g0XDaQM6pfn0m6UxO6SM8DioZ9Bk6xxbWIGHm2zHg==", 570 | "requires": { 571 | "bluebird": "3.5.1", 572 | "debug": "3.1.0", 573 | "regexp-clone": "^1.0.0", 574 | "safe-buffer": "5.1.2", 575 | "sliced": "1.0.1" 576 | }, 577 | "dependencies": { 578 | "debug": { 579 | "version": "3.1.0", 580 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 581 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 582 | "requires": { 583 | "ms": "2.0.0" 584 | } 585 | } 586 | } 587 | }, 588 | "ms": { 589 | "version": "2.0.0", 590 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 591 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 592 | }, 593 | "negotiator": { 594 | "version": "0.6.2", 595 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 596 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 597 | }, 598 | "no-case": { 599 | "version": "3.0.4", 600 | "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", 601 | "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", 602 | "requires": { 603 | "lower-case": "^2.0.2", 604 | "tslib": "^2.0.3" 605 | } 606 | }, 607 | "node-fetch": { 608 | "version": "2.6.1", 609 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 610 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" 611 | }, 612 | "nuqleus": { 613 | "version": "0.1.0", 614 | "resolved": "https://registry.npmjs.org/nuqleus/-/nuqleus-0.1.0.tgz", 615 | "integrity": "sha512-KpCFxVIAppD++j7K1ibVJXe74ciQeghGFZgtMWkGrtI19t5hqkXv8h2Eb/7Ro9XoCXQINh4WDdKSGLSzZykyDw==", 616 | "requires": { 617 | "@graphql-tools/schema": "^7.1.3", 618 | "express": "^4.17.1", 619 | "graphql-middleware": "^6.0.4" 620 | } 621 | }, 622 | "object-assign": { 623 | "version": "4.1.1", 624 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 625 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 626 | }, 627 | "on-finished": { 628 | "version": "2.3.0", 629 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 630 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 631 | "requires": { 632 | "ee-first": "1.1.1" 633 | } 634 | }, 635 | "open": { 636 | "version": "8.0.4", 637 | "resolved": "https://registry.npmjs.org/open/-/open-8.0.4.tgz", 638 | "integrity": "sha512-Txc9FOcvjrr5Kv+Zb3w89uKMKiP7wH8mLdYj1xJa+YnhhntEYhbB6cQHjS4O6P+jFwMEzEQVVcpfnu9WkKNuLQ==", 639 | "requires": { 640 | "define-lazy-prop": "^2.0.0", 641 | "is-docker": "^2.1.1", 642 | "is-wsl": "^2.2.0" 643 | } 644 | }, 645 | "parseurl": { 646 | "version": "1.3.3", 647 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 648 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 649 | }, 650 | "pascal-case": { 651 | "version": "3.1.2", 652 | "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", 653 | "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", 654 | "requires": { 655 | "no-case": "^3.0.4", 656 | "tslib": "^2.0.3" 657 | } 658 | }, 659 | "path-to-regexp": { 660 | "version": "0.1.7", 661 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 662 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 663 | }, 664 | "pino": { 665 | "version": "6.11.2", 666 | "resolved": "https://registry.npmjs.org/pino/-/pino-6.11.2.tgz", 667 | "integrity": "sha512-bmzxwbrIPxQUlAuMkF4PWVErUGERU4z37HazlhflKFg08crsNE3fACGN6gPwg5xtKOK47Ux5cZm8YCuLV4wWJg==", 668 | "requires": { 669 | "fast-redact": "^3.0.0", 670 | "fast-safe-stringify": "^2.0.7", 671 | "flatstr": "^1.0.12", 672 | "pino-std-serializers": "^3.1.0", 673 | "quick-format-unescaped": "4.0.1", 674 | "sonic-boom": "^1.0.2" 675 | } 676 | }, 677 | "pino-std-serializers": { 678 | "version": "3.2.0", 679 | "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz", 680 | "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==" 681 | }, 682 | "process-nextick-args": { 683 | "version": "2.0.1", 684 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 685 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 686 | }, 687 | "proxy-addr": { 688 | "version": "2.0.6", 689 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 690 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 691 | "requires": { 692 | "forwarded": "~0.1.2", 693 | "ipaddr.js": "1.9.1" 694 | } 695 | }, 696 | "qs": { 697 | "version": "6.7.0", 698 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 699 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 700 | }, 701 | "quick-format-unescaped": { 702 | "version": "4.0.1", 703 | "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.1.tgz", 704 | "integrity": "sha512-RyYpQ6Q5/drsJyOhrWHYMWTedvjTIat+FTwv0K4yoUxzvekw2aRHMQJLlnvt8UantkZg2++bEzD9EdxXqkWf4A==" 705 | }, 706 | "range-parser": { 707 | "version": "1.2.1", 708 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 709 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 710 | }, 711 | "raw-body": { 712 | "version": "2.4.0", 713 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 714 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 715 | "requires": { 716 | "bytes": "3.1.0", 717 | "http-errors": "1.7.2", 718 | "iconv-lite": "0.4.24", 719 | "unpipe": "1.0.0" 720 | } 721 | }, 722 | "readable-stream": { 723 | "version": "2.3.7", 724 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 725 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 726 | "requires": { 727 | "core-util-is": "~1.0.0", 728 | "inherits": "~2.0.3", 729 | "isarray": "~1.0.0", 730 | "process-nextick-args": "~2.0.0", 731 | "safe-buffer": "~5.1.1", 732 | "string_decoder": "~1.1.1", 733 | "util-deprecate": "~1.0.1" 734 | } 735 | }, 736 | "regexp-clone": { 737 | "version": "1.0.0", 738 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", 739 | "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" 740 | }, 741 | "require_optional": { 742 | "version": "1.0.1", 743 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 744 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 745 | "requires": { 746 | "resolve-from": "^2.0.0", 747 | "semver": "^5.1.0" 748 | } 749 | }, 750 | "resolve-from": { 751 | "version": "2.0.0", 752 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 753 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 754 | }, 755 | "safe-buffer": { 756 | "version": "5.1.2", 757 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 758 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 759 | }, 760 | "safer-buffer": { 761 | "version": "2.1.2", 762 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 763 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 764 | }, 765 | "saslprep": { 766 | "version": "1.0.3", 767 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 768 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 769 | "optional": true, 770 | "requires": { 771 | "sparse-bitfield": "^3.0.3" 772 | } 773 | }, 774 | "semver": { 775 | "version": "5.7.1", 776 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 777 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 778 | }, 779 | "send": { 780 | "version": "0.17.1", 781 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 782 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 783 | "requires": { 784 | "debug": "2.6.9", 785 | "depd": "~1.1.2", 786 | "destroy": "~1.0.4", 787 | "encodeurl": "~1.0.2", 788 | "escape-html": "~1.0.3", 789 | "etag": "~1.8.1", 790 | "fresh": "0.5.2", 791 | "http-errors": "~1.7.2", 792 | "mime": "1.6.0", 793 | "ms": "2.1.1", 794 | "on-finished": "~2.3.0", 795 | "range-parser": "~1.2.1", 796 | "statuses": "~1.5.0" 797 | }, 798 | "dependencies": { 799 | "ms": { 800 | "version": "2.1.1", 801 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 802 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 803 | } 804 | } 805 | }, 806 | "serve-static": { 807 | "version": "1.14.1", 808 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 809 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 810 | "requires": { 811 | "encodeurl": "~1.0.2", 812 | "escape-html": "~1.0.3", 813 | "parseurl": "~1.3.3", 814 | "send": "0.17.1" 815 | } 816 | }, 817 | "setprototypeof": { 818 | "version": "1.1.1", 819 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 820 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 821 | }, 822 | "sift": { 823 | "version": "7.0.1", 824 | "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", 825 | "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" 826 | }, 827 | "sliced": { 828 | "version": "1.0.1", 829 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 830 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" 831 | }, 832 | "sonic-boom": { 833 | "version": "1.4.0", 834 | "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.0.tgz", 835 | "integrity": "sha512-1xUAszhQBOrjk7uisbStQZYkZxD3vkYlCUw5qzOblWQ1ILN5v0dVPAs+QPgszzoPmbdWx6jyT9XiLJ95JdlLiQ==", 836 | "requires": { 837 | "atomic-sleep": "^1.0.0", 838 | "flatstr": "^1.0.12" 839 | } 840 | }, 841 | "sparse-bitfield": { 842 | "version": "3.0.3", 843 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 844 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 845 | "optional": true, 846 | "requires": { 847 | "memory-pager": "^1.0.2" 848 | } 849 | }, 850 | "statuses": { 851 | "version": "1.5.0", 852 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 853 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 854 | }, 855 | "string_decoder": { 856 | "version": "1.1.1", 857 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 858 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 859 | "requires": { 860 | "safe-buffer": "~5.1.0" 861 | } 862 | }, 863 | "toidentifier": { 864 | "version": "1.0.0", 865 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 866 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 867 | }, 868 | "tslib": { 869 | "version": "2.1.0", 870 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", 871 | "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" 872 | }, 873 | "type-is": { 874 | "version": "1.6.18", 875 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 876 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 877 | "requires": { 878 | "media-typer": "0.3.0", 879 | "mime-types": "~2.1.24" 880 | } 881 | }, 882 | "unpipe": { 883 | "version": "1.0.0", 884 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 885 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 886 | }, 887 | "util-deprecate": { 888 | "version": "1.0.2", 889 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 890 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 891 | }, 892 | "utils-merge": { 893 | "version": "1.0.1", 894 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 895 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 896 | }, 897 | "vary": { 898 | "version": "1.1.2", 899 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 900 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 901 | } 902 | } 903 | } 904 | -------------------------------------------------------------------------------- /__test__/express-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon app.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cors": "^2.8.5", 14 | "dotenv": "^8.2.0", 15 | "express": "^4.17.1", 16 | "express-graphql": "^0.12.0", 17 | "graphql": "^15.5.0", 18 | "graphql-middleware": "^6.0.4", 19 | "mongoose": "^5.11.18", 20 | "node-fetch": "^2.6.1", 21 | "nuqleus": "^0.1.0", 22 | "open": "^8.0.4", 23 | "pino": "^6.11.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /__test__/express-server/sample_queries/comment-mutation.js: -------------------------------------------------------------------------------- 1 | // addComment mutation: 2 | 3 | mutation { 4 | addComment (name: "test", email: "test@gmail.com", movie_id: "573a1390f29313caabcd5293", text: "hello world", date:"03-04-2021") { 5 | _id 6 | text 7 | } 8 | } -------------------------------------------------------------------------------- /__test__/express-server/sample_queries/main-comment-query.js: -------------------------------------------------------------------------------- 1 | // Single comment 2 | query { 3 | comment(id: "5a9427648b0beebeb6957b18") { 4 | _id 5 | name 6 | email 7 | text 8 | date 9 | movie_id 10 | movie { 11 | title 12 | } 13 | } 14 | } 15 | 16 | // First 100 comments 17 | query comments { 18 | comments(first: 100) { 19 | _id 20 | name 21 | email 22 | text 23 | date 24 | movie_id 25 | movie { 26 | title 27 | } 28 | } 29 | } 30 | 31 | // Variable Example 32 | query comments ($id: ID!) { 33 | comment(id: $id) { 34 | _id 35 | name 36 | email 37 | text 38 | date 39 | movie_id 40 | movie { 41 | title 42 | cast 43 | } 44 | } 45 | } 46 | 47 | {"id": "5a9427648b0beebeb6957b18"} -------------------------------------------------------------------------------- /__test__/express-server/sample_queries/main-movie-query.js: -------------------------------------------------------------------------------- 1 | // First five full movie query: 2 | 3 | query movies { 4 | movies (first: 4) { 5 | _id 6 | title 7 | year 8 | runtime 9 | released 10 | poster 11 | rated 12 | cast 13 | plot 14 | fullplot 15 | lastupdated 16 | type 17 | directors 18 | writers 19 | imdb { 20 | id 21 | } 22 | awards { 23 | wins 24 | nominations 25 | text 26 | } 27 | tomatoes { 28 | lastUpdated 29 | } 30 | languages 31 | countries 32 | genres 33 | num_mflix_comments 34 | comments { 35 | name 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /__test__/express-server/sample_queries/min-movie-query.js: -------------------------------------------------------------------------------- 1 | // First 10 movies with only title, cast, and comments fields 2 | query movies { 3 | movies (first: 10) { 4 | title 5 | cast 6 | comments { 7 | name 8 | } 9 | } 10 | } 11 | 12 | // First 5 movies with only title and cast fields 13 | query movies { 14 | movies (first: 5) { 15 | title 16 | cast 17 | } 18 | } -------------------------------------------------------------------------------- /__test__/express-server/schema/schema.js: -------------------------------------------------------------------------------- 1 | const graphql = require('graphql'); 2 | const Movie = require('../db/movie-models'); 3 | const Comment = require('../db/comments-models'); 4 | const types = require('./types'); 5 | 6 | const { GraphQLObjectType, GraphQLString, GraphQLID, GraphQLInt, GraphQLList, 7 | GraphQLNonNull, GraphQLInputObjectType, GraphQLFloat, GraphQLSchema } = graphql; 8 | 9 | //Schema defines data on the Graph like object types(book type), relation between 10 | //these object types and descibes how it can reach into the graph to interact with 11 | //the data to retrieve or mutate the data 12 | 13 | // const fakeBookDatabase = [ 14 | // { name:"Book 1", pages:432 , id:1}, 15 | // { name: "Book 2", pages: 32, id: 2}, 16 | // { name: "Book 3", pages: 532, id: 3 } 17 | // ] 18 | 19 | //RootQuery describe how users can use the graph and grab data. 20 | //E.g Root query to get all authors, get all books, get a particular book 21 | //or get a particular author. 22 | 23 | const RootQuery = new GraphQLObjectType({ 24 | name: 'RootQueryType', 25 | fields: { 26 | movie: { 27 | type: types.MovieType, 28 | //args passed by user while making query 29 | args: { id: { type: GraphQLID } }, 30 | resolve(parent, args) { 31 | // Define how to get data from a database source 32 | return Movie.findById(args.id); 33 | } 34 | }, 35 | movies: { 36 | type: new GraphQLList(types.MovieType), 37 | args: { first: { type: GraphQLInt } }, 38 | resolve(parent, args) { 39 | return Movie.find({}).limit(args.first); 40 | } 41 | }, 42 | comment: { 43 | type: types.CommentType, 44 | args: { id: { type: GraphQLID } }, 45 | resolve(parent, args) { 46 | return Comment.findById(args.id); 47 | } 48 | }, 49 | comments: { 50 | type: new GraphQLList(types.CommentType), 51 | args: { first: { type: GraphQLInt } }, 52 | resolve(parent, args) { 53 | return Comment.find({}).limit(args.first); 54 | } 55 | } 56 | } 57 | }); 58 | 59 | const Mutation = new GraphQLObjectType({ 60 | name: 'Mutation', 61 | fields: { 62 | addMovie: { 63 | type: types.MovieType, 64 | args: { 65 | title: { type: new GraphQLNonNull(GraphQLString) }, 66 | year: { type: new GraphQLNonNull(GraphQLInt) }, 67 | runtime: { type: new GraphQLNonNull(GraphQLInt) }, 68 | released: { type: new GraphQLNonNull(GraphQLString) }, 69 | poster: { type: GraphQLString }, 70 | rated: { type: GraphQLString }, 71 | cast: { type: new GraphQLList(GraphQLString) }, 72 | plot: { type: GraphQLString }, 73 | fullplot: { type: GraphQLString }, 74 | lastupdated: { type: GraphQLString }, 75 | type: { type: GraphQLString }, 76 | directors: { type: new GraphQLList(GraphQLString) }, 77 | writers: { type: new GraphQLList(GraphQLString) }, 78 | imdb: { type: new GraphQLInputObjectType({ 79 | name: 'ImdbInput', 80 | fields: { 81 | rating: { type: GraphQLInt }, 82 | votes: { type: GraphQLInt }, 83 | id: { type: GraphQLInt } 84 | } 85 | })}, 86 | awards: { type: new GraphQLInputObjectType({ 87 | name: 'AwardsInput', 88 | fields: { 89 | wins: { type: GraphQLInt }, 90 | nominations: { type: GraphQLInt }, 91 | text: { type: GraphQLString }, 92 | }, 93 | }) }, 94 | tomatoes: { type: new GraphQLInputObjectType({ 95 | name: 'TomatoesInput', 96 | fields: { 97 | viewer: { 98 | type: new GraphQLInputObjectType({ 99 | name: 'ViewerInput', 100 | fields: () => ({ 101 | rating: { type: GraphQLInt }, 102 | numReviews: { type: GraphQLInt }, 103 | meter: { type: GraphQLInt } 104 | }) 105 | }) 106 | }, 107 | lastUpdated: { type: GraphQLString } 108 | } 109 | }) }, 110 | languages: { type: new GraphQLList(GraphQLString) }, 111 | countries: { type: new GraphQLList(GraphQLString) }, 112 | genres: { type: new GraphQLList(GraphQLString) }, 113 | num_mflix_comments: { type: GraphQLInt }, 114 | }, 115 | resolve(parent, args) { 116 | let movie = new Movie({ 117 | title: args.title, 118 | year: args.year, 119 | runtime: args.runtime, 120 | released: args.released, 121 | poster: args.poster, 122 | rated: args.rated, 123 | cast: args.cast, 124 | plot: args.plot, 125 | fullplot: args.fullplot, 126 | lastupdated: args.lastupdated, 127 | type: args.type, 128 | directors: args.directors, 129 | writers: args.writers, 130 | imdb: args.imdb, 131 | awards: args.awards, 132 | tomatoes: args.tomatoes, 133 | languages: args.languages, 134 | countries: args.countries, 135 | genres: args.genres, 136 | num_mflix_comments: args.num_mflix_comments 137 | }); 138 | return movie.save(); 139 | } 140 | }, 141 | addComment: { 142 | type: types.CommentType, 143 | args: { 144 | // _id: { type: new GraphQLNonNull(GraphQLID) }, 145 | name: { type: new GraphQLNonNull(GraphQLString) }, 146 | email: { type: new GraphQLNonNull(GraphQLString) }, 147 | movie_id: { type: new GraphQLNonNull(GraphQLID)}, 148 | text: { type: new GraphQLNonNull(GraphQLString) }, 149 | date: { 150 | type: GraphQLString, 151 | resolve() { 152 | return Date.now(); 153 | } 154 | }, 155 | }, 156 | resolve(parent, args) { 157 | let comment = new Comment({ 158 | //_id: args._id, 159 | name: args.name, 160 | email: args.email, 161 | movie_id: args.movie_id, 162 | text: args.text, 163 | date: args.date 164 | }) 165 | return comment.save(); 166 | } 167 | } 168 | } 169 | }); 170 | 171 | //Creating a new GraphQL Schema, with options query which defines query 172 | //we will allow users to use when they are making request. 173 | 174 | module.exports = new GraphQLSchema({ 175 | query: RootQuery, 176 | mutation: Mutation 177 | }); 178 | -------------------------------------------------------------------------------- /__test__/express-server/schema/types.js: -------------------------------------------------------------------------------- 1 | const graphql = require('graphql'); 2 | const Movie = require('../db/movie-models'); 3 | const Comment = require('../db/comments-models'); 4 | 5 | const { 6 | GraphQLObjectType, 7 | GraphQLString, 8 | GraphQLID, 9 | GraphQLInt, 10 | GraphQLList, 11 | GraphQLNonNull, 12 | GraphQLInputObjectType, 13 | GraphQLFloat, 14 | GraphQLSchema 15 | } = graphql; 16 | 17 | const ImdbType = new GraphQLObjectType({ 18 | name: 'imdb', 19 | fields: () => ({ 20 | rating: { type: GraphQLString }, 21 | votes: { type: GraphQLString }, 22 | id: { type: GraphQLInt } 23 | }) 24 | }); 25 | 26 | const AwardsType = new GraphQLObjectType({ 27 | name: 'awards', 28 | fields: () => ({ 29 | wins: { type: GraphQLInt }, 30 | nominations: { type: GraphQLInt }, 31 | text: { type: GraphQLString }, 32 | }) 33 | }); 34 | 35 | const ViewerType = new GraphQLObjectType({ 36 | name: 'viewer', 37 | fields: () => ({ 38 | rating: { type: GraphQLFloat }, 39 | numReviews: { type: GraphQLInt }, 40 | meter: { type: GraphQLInt } 41 | }) 42 | }); 43 | 44 | const TomatoesType = new GraphQLObjectType({ 45 | name: 'tomatoes', 46 | fields: () => ({ 47 | viewer: { type: ViewerType }, 48 | lastUpdated: { type: GraphQLString } 49 | }) 50 | }); 51 | 52 | const MovieType = new GraphQLObjectType({ 53 | name: 'Movie', 54 | fields: () => ({ 55 | _id: { type: GraphQLID }, 56 | title: { type: GraphQLString }, 57 | year: { type: GraphQLInt }, 58 | runtime: { type: GraphQLInt }, 59 | released: { type: GraphQLString }, 60 | poster: { type: GraphQLString }, 61 | rated: { type: GraphQLString }, 62 | cast: { type: GraphQLList(GraphQLString) }, 63 | plot: { type: GraphQLString }, 64 | fullplot: { type: GraphQLString }, 65 | lastupdated: { type: GraphQLString }, 66 | type: { type: GraphQLString }, 67 | directors: { type: GraphQLList(GraphQLString) }, 68 | writers: { type: GraphQLList(GraphQLString) }, 69 | imdb: { type: ImdbType }, 70 | awards: { type: AwardsType }, 71 | tomatoes: { type: TomatoesType }, 72 | languages: { type: GraphQLList(GraphQLString) }, 73 | countries: { type: GraphQLList(GraphQLString) }, 74 | genres: { type: GraphQLList(GraphQLString) }, 75 | num_mflix_comments: { type: GraphQLInt }, 76 | comments: { 77 | type: new GraphQLList(CommentType), 78 | async resolve(parent, args) { 79 | return await Comment.find({ movie_id: parent._id }) 80 | } 81 | } 82 | }) 83 | }); 84 | 85 | const CommentType = new GraphQLObjectType({ 86 | name: 'Comment', 87 | fields: () => ({ 88 | _id: { type: GraphQLID }, 89 | name: { type: GraphQLString }, 90 | email: { type: GraphQLString }, 91 | movie_id: { type: GraphQLString }, 92 | movie: { 93 | type: MovieType, 94 | async resolve(parent, args) { 95 | return await Movie.findById(parent.movie_id) 96 | } 97 | }, 98 | text: { type: GraphQLString }, 99 | date: { type: GraphQLString }, 100 | }) 101 | }); 102 | 103 | const InputMovieType = new GraphQLInputObjectType({ 104 | name: 'MovieInput', 105 | fields: { 106 | title: { type: new GraphQLNonNull(GraphQLString) }, 107 | year: { type: new GraphQLNonNull(GraphQLInt) }, 108 | runtime: { type: new GraphQLNonNull(GraphQLInt) }, 109 | released: { type: new GraphQLNonNull(GraphQLString) }, 110 | poster: { type: GraphQLString }, 111 | rated: { type: GraphQLString }, 112 | cast: { type: new GraphQLList(GraphQLString) }, 113 | plot: { type: GraphQLString }, 114 | fullplot: { type: GraphQLString }, 115 | lastupdated: { type: GraphQLString }, 116 | type: { type: GraphQLString }, 117 | directors: { type: new GraphQLList(GraphQLString) }, 118 | writers: { type: new GraphQLList(GraphQLString) }, 119 | imdb: { type: new GraphQLInputObjectType({ 120 | name: 'ImdbInput', 121 | fields: { 122 | rating: { type: GraphQLInt }, 123 | votes: { type: GraphQLInt }, 124 | id: { type: GraphQLInt } 125 | } 126 | })}, 127 | awards: { type: new GraphQLInputObjectType({ 128 | name: 'AwardsInput', 129 | fields: { 130 | wins: { type: GraphQLInt }, 131 | nominations: { type: GraphQLInt }, 132 | text: { type: GraphQLString }, 133 | }, 134 | }) }, 135 | tomatoes: { type: new GraphQLInputObjectType({ 136 | name: 'TomatoesInput', 137 | fields: { 138 | viewer: { 139 | type: new GraphQLInputObjectType({ 140 | name: 'ViewerInput', 141 | fields: () => ({ 142 | rating: { type: GraphQLInt }, 143 | numReviews: { type: GraphQLInt }, 144 | meter: { type: GraphQLInt } 145 | }) 146 | }) 147 | }, 148 | lastUpdated: { type: GraphQLString } 149 | } 150 | }) }, 151 | languages: { type: new GraphQLList(GraphQLString) }, 152 | countries: { type: new GraphQLList(GraphQLString) }, 153 | genres: { type: new GraphQLList(GraphQLString) }, 154 | num_mflix_comments: { type: GraphQLInt }, 155 | } 156 | }); 157 | 158 | module.exports = { ImdbType, TomatoesType, AwardsType, MovieType, CommentType, InputMovieType }; 159 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | nuQLeus encourages open source contributions and feedback to this product. 4 | 5 | ## Pull Requests 6 | 7 | nuQLeus welcomes all pull requests. 8 | 9 | 1. Fork the repo and create a working branch from `main`. 10 | 2. If you've added any code that requires testing, add tests. 11 | 3. Make sure code is formatted with `prettier` and follows the [Airbnb React/JSX Style Guide](https://github.com/airbnb/javascript/blob/master/react/README.md). 12 | 4. Create a pull request to `staging`. 13 | 14 | ## Issues 15 | 16 | Please do not hesitate to file issues. nuQLeus is based off of community feedback and is always looking for ways to get better. The team behind nuQLeus is interested to hear about your experience and how we can improve it. 17 | 18 | Please do not hesitate to submit issues that promote bugs or offer ways to enhance to nuQLeus. When submitting issues, ensure your description is clear and has instructions to be able to reproduce the issue. 19 | 20 | ## Get in touch 21 | 22 | We use GitHub as a platform of communication between users and developers to promote transparency. We thrive off of the community support and feedback. Mostly. -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | nuQLeus 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nuqleus_npm_package/.gitignore: -------------------------------------------------------------------------------- 1 | .ds_store 2 | node_modules/ -------------------------------------------------------------------------------- /nuqleus_npm_package/README.md: -------------------------------------------------------------------------------- 1 | # nuQLeus 2 | 3 | Boost GraphQL endpoint testing capabilities with resolver-level performance metrics. 4 | 5 |
6 | 7 |
8 | 9 | 10 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/oslabs-beta/NuQLeus) 11 | ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg) 12 | 13 | ## Table of Contents 14 | 15 | - [Description](#description) 16 | - [Features](#features) 17 | - [Getting Started with nuQLeus](#Getting-Started-with-nuQLeus) 18 | - [Install nuQLeus](#Install-nuQLeus) 19 | - [Apollo Server Setup](#Apollo-Server-Setup) 20 | - [Express-GraphQL Setup](#Express-GraphQL-Setup) 21 | - [Using the nuQLeus GUI](#Using-the-nuQLeus-GUI) 22 | - [Contributing](#contributing) 23 | - [Authors](#authors) 24 | - [License](#license) 25 | - [Links/Contact](Links/Contacts) 26 | 27 |
28 | 29 | ## Description 30 | 31 | NuQLeus is a lightweight, self-contained, GraphQL endpoint testing GUI that extracts resolver-level tracing performance metrics from GraphQL queries and mutations and allows an enduser to easily identify specific resolvers that are having a detrimental impact on application performance. 32 | 33 |
34 | 35 | ## Features 36 | 37 | * Simulate GraphQL queries and mutations in the nuQLeus environment 38 | * Real-time tracing durations at individual resolver-level for GraphQL queries and mutations 39 | * Processing and visualization of tracing data to help identify performance drop-offs at a glance 40 | 41 |
42 | 43 | ## Getting Started With nuQLeus 44 | 45 | ### **Install nuQLeus** 46 | 47 | ```javascript 48 | npm install --save-dev nuqleus 49 | ``` 50 | 51 |
52 | 53 | ### **Apollo Server Setup** 54 | 55 | The Apollo Server constructor function accepts an object with properties that convey schema options. Simply pass the existing options object into the `nuqleus.ApolloWrapOptions` method to return a new options object to be passed into the `ApolloServer` constructor. 56 | 57 | ```javascript 58 | const { ApolloServer } = require('apollo-server'); 59 | const connectDb = require('./config/db'); 60 | const typeDefs = require('./types'); 61 | const resolvers = require('./resolvers'); 62 | const models = require('./models'); 63 | import nuqleus from 'nuqleus'; 64 | 65 | connectDb(); 66 | 67 | // 1. Define your context either as an object or function // 68 | // Add your models in here, if any // 69 | const userContext = { 70 | models, 71 | sampleObject: {}, 72 | }; 73 | 74 | // 2. Define your formatResponse, if any, as an object // 75 | const userFormatResponse = { 76 | formatResponse: (res, reqContext) => { 77 | res.http = { 78 | someUserHttp: { 79 | startTime: new Date().toISOString(), 80 | endTime: new Date(Date.now()).toISOString(), 81 | } 82 | }; 83 | } 84 | }; 85 | 86 | // 3. Create a nuqleusServerObject using the ApolloWrapOptions method // 87 | // Allowable inputs: typeDefs, resolvers, context, formatResponse, ...resolverInputs // 88 | const nuqleusServerObject = nuqleus.ApolloWrapOptions( 89 | typeDefs, resolvers, userContext, userFormatResponse 90 | ); 91 | 92 | /** 93 | * If any resolverInputs will be inputted, they will be wrapped after 94 | * the nuQLeusTraceResolver around your schema with the applyMiddleware method. 95 | * 96 | * const schemaWithMiddleWare = applyMiddleware(clientSchema, nuqleusTraceResolvers, ...clientInputs); 97 | */ 98 | 99 | // 4. Spread the nuqleusServerObject into your new ApolloServer instance // 100 | // If you have any add'l options, add them in after 101 | const server = new ApolloServer({ 102 | ...nuqleusServerObject, 103 | // add'l user options, 104 | // add'l user options, 105 | }); 106 | 107 | // 5. Run your server / 108 | server.listen(4001).then(({ url }) => { 109 | console.log(`🚀 Server ready at ${url}`); 110 | }); 111 | ``` 112 | 113 |
114 | 115 | ### **Express-GraphQL Setup** 116 | 117 | Express-graphql's `graphqlHTTP` function accepts an options parameter that can either be an object or a function. Simply pass existing options into `nuqleus.WrapOptions` function to return a new options callback to pass into `graphqlHTTP`. 118 | 119 | If users have `extensions`, simply pass the `extensions` callback into `nuqleus.WrapOptions` as a second optional argument. 120 | 121 | ```js 122 | const express = require('express'); 123 | const { graphqlHTTP } = require('express-graphql'); 124 | const schema = require('./schema'); 125 | const models = require('./models'); 126 | const nuqleus = require('./nuqleus'); 127 | 128 | const extensions = ({ document, variables, operationName, result, context }) => ({ 129 | hello: context.hello, 130 | }); 131 | 132 | const options = { 133 | schema, 134 | context: { models, hello: 'world' }, 135 | graphiql: true, 136 | }; 137 | 138 | const newOptions = nuqleus.WrapOptions(options, extensions); 139 | 140 | app.use('/graphql', graphqlHTTP(newOptions)); 141 | 142 | app.listen(4000, () => { 143 | console.log('Listening on port 4000'); 144 | }); 145 | 146 | ``` 147 | 148 |
149 | 150 | ### **Using the nuQLeus GUI** 151 | The nuQLeus wrapper methods instantiate a server and serve the nuQLeus GUI whenever a user's GraphQL server is initialized. In order to access the nuQLeus GUI, navigate to http://localhost:3030/nuqleus while your server is running. 152 | 153 |
154 | 155 |
156 | 157 |
158 | 159 | ## Contributing 160 | 161 | Development of nuQLeus is open source on Hithub through the tech accelerator umbrella OS Labs. We are grateful for the community's contribution to the project. Please read the [contribution documentation](contributing.md) to learn more on how you can participate in improvements. 162 | 163 |
164 | 165 | ## Authors 166 | 167 | * Daniel Perez [Daniel-P3](https://github.com/Daniel-P3) 168 | * Jenny Hai [jhai420](https://github.com/jhai420) 169 | * Joshua Kim [jkim000](https://github.com/jkim000) 170 | * Zach Brucker [zbrucker](https://github.com/zbrucker) 171 | 172 |
173 | 174 | ## License 175 | 176 | [MIT](https://opensource.org/licenses/mit-license.php) 177 | 178 |
179 | 180 | ## Links/Contacts 181 | 182 | * Email: nuqleusjs@gmail.com 183 | * [Landing Page](http://www.nuqleus.io/) 184 | * [npm](https://www.npmjs.com/package/nuqleus) 185 | * [LinkedIn](https://www.linkedin.com/company/nuqleus) 186 | 187 |
188 | 189 | # 190 | ##### [Return to Top](#nuQLeus) -------------------------------------------------------------------------------- /nuqleus_npm_package/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | nuQLeus 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /nuqleus_npm_package/main.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').exec; 2 | exec('node server.js', { encoding: 'utf-8' }); 3 | -------------------------------------------------------------------------------- /nuqleus_npm_package/nuqleus.js: -------------------------------------------------------------------------------- 1 | const { makeExecutableSchema } = require('@graphql-tools/schema'); 2 | const { applyMiddleware } = require('graphql-middleware'); 3 | const { exec } = require('child_process'); 4 | 5 | const nuqleus = {}; 6 | 7 | const connectToNuqleus = () => { 8 | exec('node server.js', { encoding: 'utf-8' }); 9 | }; 10 | 11 | /** 12 | * @param {*} options can be an object or a callback function 13 | * @param {*} clientExtensions optional callback function 14 | * @returns new options callback containing nuQleus tracing data 15 | */ 16 | nuqleus.WrapOptions = (options, clientExtensions) => { 17 | // connectToNuqleus() starts an express server and serves nuQLeus GUI to localhost:3030/nuqleus 18 | connectToNuqleus(); 19 | 20 | const traceResolvers = async (resolve, root, args, context, info) => { 21 | const startTime = Date.now(); 22 | const result = await resolve(root, args, context, info); 23 | const endTime = Date.now(); 24 | 25 | const pathArray = []; 26 | let curPath = info.path; 27 | do { 28 | pathArray.push(curPath.key); 29 | curPath = curPath.prev; 30 | } while (curPath); 31 | 32 | const resolverData = { 33 | path: pathArray.reverse(), 34 | startTime: new Date(startTime).toISOString(), 35 | endTime: new Date(endTime).toISOString(), 36 | fieldName: info.fieldName, 37 | duration: endTime - startTime, 38 | operation: info.operation.operation, 39 | parentType: info.parentType, 40 | returnType: info.returnType, 41 | }; 42 | 43 | context.nuqleusQueryTimes.push(resolverData); 44 | return result; 45 | }; 46 | 47 | const extensions = ({ document, variables, operationName, result, context }) => { 48 | // Create another callback that modifies that OGExt and return a single object 49 | const nuqleusExt = ({ document, variables, operationName, result, context }) => ({ 50 | nuQLeusTracing: { 51 | startTime: new Date(context.nuqleusStartTime).toISOString(), 52 | endTime: new Date(Date.now()).toISOString(), 53 | duration: Date.now() - context.nuqleusStartTime, 54 | resolvers: context.nuqleusQueryTimes, 55 | }, 56 | }); 57 | 58 | const newExt = nuqleusExt({ document, variables, operationName, result, context }); 59 | 60 | // If clientExtensions does not exist, return nuqleus-tracing extensions 61 | if (!clientExtensions) return newExt; 62 | 63 | // If clientExtensions do exist, then process existing clientExtensions and combine into a single object with nuqleus-tracing extensions 64 | const originalExt = clientExtensions({ document, variables, operationName, result, context }); 65 | return { 66 | ...originalExt, 67 | ...newExt, 68 | }; 69 | }; 70 | 71 | if (typeof options === 'object') { 72 | return (request, response, graphQLParams) => ({ 73 | ...options, 74 | schema: applyMiddleware(options.schema, traceResolvers), 75 | context: options.context 76 | ? { ...options.context, nuqleusStartTime: Date.now(), nuqleusQueryTimes: [] } 77 | : { nuqleusStartTime: Date.now(), nuqleusQueryTimes: [] }, 78 | extensions, 79 | }); 80 | } 81 | if (typeof options === 'function') { 82 | return (request, response, graphQLParams) => { 83 | const originalOptions = options(request, response, graphQLParams); 84 | return { 85 | ...originalOptions, 86 | schema: applyMiddleware(originalOptions.schema, traceResolvers), 87 | context: originalOptions.context 88 | ? { ...originalOptions.context, nuqleusStartTime: Date.now(), nuqleusQueryTimes: [] } 89 | : { nuqleusStartTime: Date.now(), nuqleusQueryTimes: [] }, 90 | extensions, 91 | }; 92 | }; 93 | } 94 | }; 95 | 96 | /** 97 | * @param {*} typeDefs required 98 | * @param {*} resolvers required 99 | * @param {*} clientContext optional; can be object or callback 100 | * @param {*} clientFormatResponse option; must be object with a formatResponse key 101 | * @param {*} clientInputs optional; can be spread with multiple callbacks 102 | * @returns object to be spread into the client's new ApolloServer() instance 103 | */ 104 | nuqleus.ApolloWrapOptions = ( 105 | typeDefs, 106 | resolvers, 107 | clientContext, 108 | clientFormatResponse, 109 | ...clientInputs 110 | ) => { 111 | // connectToNuqleus() starts an express server and serves nuQLeus GUI to localhost:3030/nuqleus 112 | connectToNuqleus(); 113 | // create executableSchema 114 | const schema = makeExecutableSchema({ 115 | typeDefs, 116 | resolvers, 117 | }); 118 | 119 | // resolver level metrics invoked at each call 120 | const traceResolvers = async (resolve, root, args, context, info) => { 121 | const startTime = Date.now(); 122 | const result = await resolve(root, args, context, info); 123 | const endTime = Date.now(); 124 | 125 | const pathArray = []; 126 | let curPath = info.path; 127 | do { 128 | pathArray.push(curPath.key); 129 | curPath = curPath.prev; 130 | } while (curPath); 131 | 132 | const resolverData = { 133 | path: pathArray.reverse(), 134 | startTime: new Date(startTime).toISOString(), 135 | endTime: new Date(endTime).toISOString(), 136 | fieldName: info.fieldName, 137 | duration: endTime - startTime, 138 | operation: info.operation.operation, 139 | parentType: info.parentType, 140 | returnType: info.returnType, 141 | }; 142 | 143 | context.nuqleusQueryTimes.push(resolverData); 144 | return result; 145 | }; 146 | 147 | // apply middleware to schema 148 | const schemaWithMiddleWare = applyMiddleware(schema, traceResolvers, ...clientInputs); 149 | 150 | /** 151 | * @returns must be a function that returns an object 152 | * 153 | * fuse client context object with nuQLeus context 154 | */ 155 | const fuseContexts = () => { 156 | if (typeof clientContext === 'object') { 157 | return async ({ req, res }) => ({ 158 | ...clientContext, 159 | nuqleusStartTime: Date.now(), 160 | nuqleusQueryTimes: [], 161 | }); 162 | } 163 | if (typeof clientContext === 'function') { 164 | return async ({ req, res }) => ({ 165 | ...clientContext({ req, res }), 166 | nuqleusStartTime: Date.now(), 167 | nuqleusQueryTimes: [], 168 | }); 169 | } 170 | }; 171 | 172 | /** 173 | * @returns must be a function that returns the response with an extensions object 174 | * 175 | * fuse client formatResponse function with nuQLeus formatResponse 176 | */ 177 | const nuqleusFormatResponse = (response, requestContext) => { 178 | if (response.extensions) { 179 | response.extensions.nuQLeusTracing = { 180 | startTime: new Date(requestContext.context.nuqleusStartTime).toISOString(), 181 | endTime: new Date(Date.now()).toISOString(), 182 | duration: Date.now() - requestContext.context.nuqleusStartTime, 183 | resolvers: requestContext.context.nuqleusQueryTimes, 184 | }; 185 | } else { 186 | response.extensions = { 187 | nuQLeusTracing: { 188 | startTime: new Date(requestContext.context.nuqleusStartTime).toISOString(), 189 | endTime: new Date(Date.now()).toISOString(), 190 | duration: Date.now() - requestContext.context.nuqleusStartTime, 191 | resolvers: requestContext.context.nuqleusQueryTimes, 192 | }, 193 | }; 194 | } 195 | }; 196 | 197 | const fuseFormatResponse = () => (response, requestContext) => { 198 | clientFormatResponse.formatResponse(response, requestContext); 199 | nuqleusFormatResponse(response, requestContext); 200 | }; 201 | 202 | /** 203 | * @returns object with schema, context, and formatResponse 204 | * 205 | * drop this object output into the client's new ApolloServer() instance 206 | */ 207 | return { 208 | schema: schemaWithMiddleWare, 209 | context: fuseContexts(), 210 | formatResponse: fuseFormatResponse(), 211 | }; 212 | }; 213 | 214 | module.exports = nuqleus; 215 | -------------------------------------------------------------------------------- /nuqleus_npm_package/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuqleus", 3 | "version": "0.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "nuqleus", 9 | "version": "0.1.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@graphql-tools/schema": "^7.1.3", 13 | "express": "^4.17.1", 14 | "graphql": "^15.5.0", 15 | "graphql-middleware": "^6.0.4", 16 | "open": "^8.0.4" 17 | } 18 | }, 19 | "node_modules/@ardatan/aggregate-error": { 20 | "version": "0.0.6", 21 | "resolved": "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz", 22 | "integrity": "sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ==", 23 | "dependencies": { 24 | "tslib": "~2.0.1" 25 | }, 26 | "engines": { 27 | "node": ">=8" 28 | } 29 | }, 30 | "node_modules/@ardatan/aggregate-error/node_modules/tslib": { 31 | "version": "2.0.3", 32 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", 33 | "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" 34 | }, 35 | "node_modules/@graphql-tools/batch-execute": { 36 | "version": "7.0.0", 37 | "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-7.0.0.tgz", 38 | "integrity": "sha512-+ywPfK6N2Ddna6oOa5Qb1Mv7EA8LOwRNOAPP9dL37FEhksJM9pYqPSceUcqMqg7S9b0+Cgr78s408rgvurV3/Q==", 39 | "dependencies": { 40 | "@graphql-tools/utils": "^7.0.0", 41 | "dataloader": "2.0.0", 42 | "is-promise": "4.0.0", 43 | "tslib": "~2.0.1" 44 | } 45 | }, 46 | "node_modules/@graphql-tools/batch-execute/node_modules/tslib": { 47 | "version": "2.0.3", 48 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", 49 | "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" 50 | }, 51 | "node_modules/@graphql-tools/delegate": { 52 | "version": "7.0.10", 53 | "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-7.0.10.tgz", 54 | "integrity": "sha512-6Di9ia5ohoDvrHuhj2cak1nJGhIefJmUsd3WKZcJ2nu2yZAFawWMxGvQImqv3N7iyaWKiVhrrK8Roi/JrYhdKg==", 55 | "dependencies": { 56 | "@ardatan/aggregate-error": "0.0.6", 57 | "@graphql-tools/batch-execute": "^7.0.0", 58 | "@graphql-tools/schema": "^7.0.0", 59 | "@graphql-tools/utils": "^7.1.6", 60 | "dataloader": "2.0.0", 61 | "is-promise": "4.0.0", 62 | "tslib": "~2.1.0" 63 | } 64 | }, 65 | "node_modules/@graphql-tools/schema": { 66 | "version": "7.1.3", 67 | "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-7.1.3.tgz", 68 | "integrity": "sha512-ZY76hmcJlF1iyg3Im0sQ3ASRkiShjgv102vLTVcH22lEGJeCaCyyS/GF1eUHom418S60bS8Th6+autRUxfBiBg==", 69 | "dependencies": { 70 | "@graphql-tools/utils": "^7.1.2", 71 | "tslib": "~2.1.0" 72 | } 73 | }, 74 | "node_modules/@graphql-tools/utils": { 75 | "version": "7.6.0", 76 | "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", 77 | "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", 78 | "dependencies": { 79 | "@ardatan/aggregate-error": "0.0.6", 80 | "camel-case": "4.1.2", 81 | "tslib": "~2.1.0" 82 | } 83 | }, 84 | "node_modules/accepts": { 85 | "version": "1.3.7", 86 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 87 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 88 | "dependencies": { 89 | "mime-types": "~2.1.24", 90 | "negotiator": "0.6.2" 91 | }, 92 | "engines": { 93 | "node": ">= 0.6" 94 | } 95 | }, 96 | "node_modules/array-flatten": { 97 | "version": "1.1.1", 98 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 99 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 100 | }, 101 | "node_modules/body-parser": { 102 | "version": "1.19.0", 103 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 104 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 105 | "dependencies": { 106 | "bytes": "3.1.0", 107 | "content-type": "~1.0.4", 108 | "debug": "2.6.9", 109 | "depd": "~1.1.2", 110 | "http-errors": "1.7.2", 111 | "iconv-lite": "0.4.24", 112 | "on-finished": "~2.3.0", 113 | "qs": "6.7.0", 114 | "raw-body": "2.4.0", 115 | "type-is": "~1.6.17" 116 | }, 117 | "engines": { 118 | "node": ">= 0.8" 119 | } 120 | }, 121 | "node_modules/bytes": { 122 | "version": "3.1.0", 123 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 124 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", 125 | "engines": { 126 | "node": ">= 0.8" 127 | } 128 | }, 129 | "node_modules/camel-case": { 130 | "version": "4.1.2", 131 | "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", 132 | "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", 133 | "dependencies": { 134 | "pascal-case": "^3.1.2", 135 | "tslib": "^2.0.3" 136 | } 137 | }, 138 | "node_modules/content-disposition": { 139 | "version": "0.5.3", 140 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 141 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 142 | "dependencies": { 143 | "safe-buffer": "5.1.2" 144 | }, 145 | "engines": { 146 | "node": ">= 0.6" 147 | } 148 | }, 149 | "node_modules/content-type": { 150 | "version": "1.0.4", 151 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 152 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 153 | "engines": { 154 | "node": ">= 0.6" 155 | } 156 | }, 157 | "node_modules/cookie": { 158 | "version": "0.4.0", 159 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 160 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", 161 | "engines": { 162 | "node": ">= 0.6" 163 | } 164 | }, 165 | "node_modules/cookie-signature": { 166 | "version": "1.0.6", 167 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 168 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 169 | }, 170 | "node_modules/dataloader": { 171 | "version": "2.0.0", 172 | "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz", 173 | "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==" 174 | }, 175 | "node_modules/debug": { 176 | "version": "2.6.9", 177 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 178 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 179 | "dependencies": { 180 | "ms": "2.0.0" 181 | } 182 | }, 183 | "node_modules/define-lazy-prop": { 184 | "version": "2.0.0", 185 | "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", 186 | "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", 187 | "engines": { 188 | "node": ">=8" 189 | } 190 | }, 191 | "node_modules/depd": { 192 | "version": "1.1.2", 193 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 194 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 195 | "engines": { 196 | "node": ">= 0.6" 197 | } 198 | }, 199 | "node_modules/destroy": { 200 | "version": "1.0.4", 201 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 202 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 203 | }, 204 | "node_modules/ee-first": { 205 | "version": "1.1.1", 206 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 207 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 208 | }, 209 | "node_modules/encodeurl": { 210 | "version": "1.0.2", 211 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 212 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 213 | "engines": { 214 | "node": ">= 0.8" 215 | } 216 | }, 217 | "node_modules/escape-html": { 218 | "version": "1.0.3", 219 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 220 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 221 | }, 222 | "node_modules/etag": { 223 | "version": "1.8.1", 224 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 225 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 226 | "engines": { 227 | "node": ">= 0.6" 228 | } 229 | }, 230 | "node_modules/express": { 231 | "version": "4.17.1", 232 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 233 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 234 | "dependencies": { 235 | "accepts": "~1.3.7", 236 | "array-flatten": "1.1.1", 237 | "body-parser": "1.19.0", 238 | "content-disposition": "0.5.3", 239 | "content-type": "~1.0.4", 240 | "cookie": "0.4.0", 241 | "cookie-signature": "1.0.6", 242 | "debug": "2.6.9", 243 | "depd": "~1.1.2", 244 | "encodeurl": "~1.0.2", 245 | "escape-html": "~1.0.3", 246 | "etag": "~1.8.1", 247 | "finalhandler": "~1.1.2", 248 | "fresh": "0.5.2", 249 | "merge-descriptors": "1.0.1", 250 | "methods": "~1.1.2", 251 | "on-finished": "~2.3.0", 252 | "parseurl": "~1.3.3", 253 | "path-to-regexp": "0.1.7", 254 | "proxy-addr": "~2.0.5", 255 | "qs": "6.7.0", 256 | "range-parser": "~1.2.1", 257 | "safe-buffer": "5.1.2", 258 | "send": "0.17.1", 259 | "serve-static": "1.14.1", 260 | "setprototypeof": "1.1.1", 261 | "statuses": "~1.5.0", 262 | "type-is": "~1.6.18", 263 | "utils-merge": "1.0.1", 264 | "vary": "~1.1.2" 265 | }, 266 | "engines": { 267 | "node": ">= 0.10.0" 268 | } 269 | }, 270 | "node_modules/finalhandler": { 271 | "version": "1.1.2", 272 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 273 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 274 | "dependencies": { 275 | "debug": "2.6.9", 276 | "encodeurl": "~1.0.2", 277 | "escape-html": "~1.0.3", 278 | "on-finished": "~2.3.0", 279 | "parseurl": "~1.3.3", 280 | "statuses": "~1.5.0", 281 | "unpipe": "~1.0.0" 282 | }, 283 | "engines": { 284 | "node": ">= 0.8" 285 | } 286 | }, 287 | "node_modules/forwarded": { 288 | "version": "0.1.2", 289 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 290 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", 291 | "engines": { 292 | "node": ">= 0.6" 293 | } 294 | }, 295 | "node_modules/fresh": { 296 | "version": "0.5.2", 297 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 298 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 299 | "engines": { 300 | "node": ">= 0.6" 301 | } 302 | }, 303 | "node_modules/graphql": { 304 | "version": "15.5.0", 305 | "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.5.0.tgz", 306 | "integrity": "sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA==", 307 | "engines": { 308 | "node": ">= 10.x" 309 | } 310 | }, 311 | "node_modules/graphql-middleware": { 312 | "version": "6.0.4", 313 | "resolved": "https://registry.npmjs.org/graphql-middleware/-/graphql-middleware-6.0.4.tgz", 314 | "integrity": "sha512-T49lpvrRHpUzBXUupNnBx82WAmWBoTmsWcc+XdblYClj9WnRMrtxeunQlTsbsSCSD7ZahN1MELd0xKfC2hfwUw==", 315 | "dependencies": { 316 | "@graphql-tools/delegate": "^7.0.10", 317 | "@graphql-tools/schema": "^7.1.3" 318 | } 319 | }, 320 | "node_modules/http-errors": { 321 | "version": "1.7.2", 322 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 323 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 324 | "dependencies": { 325 | "depd": "~1.1.2", 326 | "inherits": "2.0.3", 327 | "setprototypeof": "1.1.1", 328 | "statuses": ">= 1.5.0 < 2", 329 | "toidentifier": "1.0.0" 330 | }, 331 | "engines": { 332 | "node": ">= 0.6" 333 | } 334 | }, 335 | "node_modules/iconv-lite": { 336 | "version": "0.4.24", 337 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 338 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 339 | "dependencies": { 340 | "safer-buffer": ">= 2.1.2 < 3" 341 | }, 342 | "engines": { 343 | "node": ">=0.10.0" 344 | } 345 | }, 346 | "node_modules/inherits": { 347 | "version": "2.0.3", 348 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 349 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 350 | }, 351 | "node_modules/ipaddr.js": { 352 | "version": "1.9.1", 353 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 354 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 355 | "engines": { 356 | "node": ">= 0.10" 357 | } 358 | }, 359 | "node_modules/is-docker": { 360 | "version": "2.1.1", 361 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", 362 | "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", 363 | "bin": { 364 | "is-docker": "cli.js" 365 | }, 366 | "engines": { 367 | "node": ">=8" 368 | } 369 | }, 370 | "node_modules/is-promise": { 371 | "version": "4.0.0", 372 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", 373 | "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" 374 | }, 375 | "node_modules/is-wsl": { 376 | "version": "2.2.0", 377 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", 378 | "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", 379 | "dependencies": { 380 | "is-docker": "^2.0.0" 381 | }, 382 | "engines": { 383 | "node": ">=8" 384 | } 385 | }, 386 | "node_modules/lower-case": { 387 | "version": "2.0.2", 388 | "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", 389 | "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", 390 | "dependencies": { 391 | "tslib": "^2.0.3" 392 | } 393 | }, 394 | "node_modules/media-typer": { 395 | "version": "0.3.0", 396 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 397 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 398 | "engines": { 399 | "node": ">= 0.6" 400 | } 401 | }, 402 | "node_modules/merge-descriptors": { 403 | "version": "1.0.1", 404 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 405 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 406 | }, 407 | "node_modules/methods": { 408 | "version": "1.1.2", 409 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 410 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 411 | "engines": { 412 | "node": ">= 0.6" 413 | } 414 | }, 415 | "node_modules/mime": { 416 | "version": "1.6.0", 417 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 418 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 419 | "bin": { 420 | "mime": "cli.js" 421 | }, 422 | "engines": { 423 | "node": ">=4" 424 | } 425 | }, 426 | "node_modules/mime-db": { 427 | "version": "1.46.0", 428 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", 429 | "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", 430 | "engines": { 431 | "node": ">= 0.6" 432 | } 433 | }, 434 | "node_modules/mime-types": { 435 | "version": "2.1.29", 436 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", 437 | "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", 438 | "dependencies": { 439 | "mime-db": "1.46.0" 440 | }, 441 | "engines": { 442 | "node": ">= 0.6" 443 | } 444 | }, 445 | "node_modules/ms": { 446 | "version": "2.0.0", 447 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 448 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 449 | }, 450 | "node_modules/negotiator": { 451 | "version": "0.6.2", 452 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 453 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 454 | "engines": { 455 | "node": ">= 0.6" 456 | } 457 | }, 458 | "node_modules/no-case": { 459 | "version": "3.0.4", 460 | "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", 461 | "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", 462 | "dependencies": { 463 | "lower-case": "^2.0.2", 464 | "tslib": "^2.0.3" 465 | } 466 | }, 467 | "node_modules/on-finished": { 468 | "version": "2.3.0", 469 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 470 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 471 | "dependencies": { 472 | "ee-first": "1.1.1" 473 | }, 474 | "engines": { 475 | "node": ">= 0.8" 476 | } 477 | }, 478 | "node_modules/open": { 479 | "version": "8.0.4", 480 | "resolved": "https://registry.npmjs.org/open/-/open-8.0.4.tgz", 481 | "integrity": "sha512-Txc9FOcvjrr5Kv+Zb3w89uKMKiP7wH8mLdYj1xJa+YnhhntEYhbB6cQHjS4O6P+jFwMEzEQVVcpfnu9WkKNuLQ==", 482 | "dependencies": { 483 | "define-lazy-prop": "^2.0.0", 484 | "is-docker": "^2.1.1", 485 | "is-wsl": "^2.2.0" 486 | }, 487 | "engines": { 488 | "node": ">=12" 489 | } 490 | }, 491 | "node_modules/parseurl": { 492 | "version": "1.3.3", 493 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 494 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 495 | "engines": { 496 | "node": ">= 0.8" 497 | } 498 | }, 499 | "node_modules/pascal-case": { 500 | "version": "3.1.2", 501 | "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", 502 | "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", 503 | "dependencies": { 504 | "no-case": "^3.0.4", 505 | "tslib": "^2.0.3" 506 | } 507 | }, 508 | "node_modules/path-to-regexp": { 509 | "version": "0.1.7", 510 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 511 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 512 | }, 513 | "node_modules/proxy-addr": { 514 | "version": "2.0.6", 515 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 516 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 517 | "dependencies": { 518 | "forwarded": "~0.1.2", 519 | "ipaddr.js": "1.9.1" 520 | }, 521 | "engines": { 522 | "node": ">= 0.10" 523 | } 524 | }, 525 | "node_modules/qs": { 526 | "version": "6.7.0", 527 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 528 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", 529 | "engines": { 530 | "node": ">=0.6" 531 | } 532 | }, 533 | "node_modules/range-parser": { 534 | "version": "1.2.1", 535 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 536 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 537 | "engines": { 538 | "node": ">= 0.6" 539 | } 540 | }, 541 | "node_modules/raw-body": { 542 | "version": "2.4.0", 543 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 544 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 545 | "dependencies": { 546 | "bytes": "3.1.0", 547 | "http-errors": "1.7.2", 548 | "iconv-lite": "0.4.24", 549 | "unpipe": "1.0.0" 550 | }, 551 | "engines": { 552 | "node": ">= 0.8" 553 | } 554 | }, 555 | "node_modules/safe-buffer": { 556 | "version": "5.1.2", 557 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 558 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 559 | }, 560 | "node_modules/safer-buffer": { 561 | "version": "2.1.2", 562 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 563 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 564 | }, 565 | "node_modules/send": { 566 | "version": "0.17.1", 567 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 568 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 569 | "dependencies": { 570 | "debug": "2.6.9", 571 | "depd": "~1.1.2", 572 | "destroy": "~1.0.4", 573 | "encodeurl": "~1.0.2", 574 | "escape-html": "~1.0.3", 575 | "etag": "~1.8.1", 576 | "fresh": "0.5.2", 577 | "http-errors": "~1.7.2", 578 | "mime": "1.6.0", 579 | "ms": "2.1.1", 580 | "on-finished": "~2.3.0", 581 | "range-parser": "~1.2.1", 582 | "statuses": "~1.5.0" 583 | }, 584 | "engines": { 585 | "node": ">= 0.8.0" 586 | } 587 | }, 588 | "node_modules/send/node_modules/ms": { 589 | "version": "2.1.1", 590 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 591 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 592 | }, 593 | "node_modules/serve-static": { 594 | "version": "1.14.1", 595 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 596 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 597 | "dependencies": { 598 | "encodeurl": "~1.0.2", 599 | "escape-html": "~1.0.3", 600 | "parseurl": "~1.3.3", 601 | "send": "0.17.1" 602 | }, 603 | "engines": { 604 | "node": ">= 0.8.0" 605 | } 606 | }, 607 | "node_modules/setprototypeof": { 608 | "version": "1.1.1", 609 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 610 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 611 | }, 612 | "node_modules/statuses": { 613 | "version": "1.5.0", 614 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 615 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 616 | "engines": { 617 | "node": ">= 0.6" 618 | } 619 | }, 620 | "node_modules/toidentifier": { 621 | "version": "1.0.0", 622 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 623 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", 624 | "engines": { 625 | "node": ">=0.6" 626 | } 627 | }, 628 | "node_modules/tslib": { 629 | "version": "2.1.0", 630 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", 631 | "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" 632 | }, 633 | "node_modules/type-is": { 634 | "version": "1.6.18", 635 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 636 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 637 | "dependencies": { 638 | "media-typer": "0.3.0", 639 | "mime-types": "~2.1.24" 640 | }, 641 | "engines": { 642 | "node": ">= 0.6" 643 | } 644 | }, 645 | "node_modules/unpipe": { 646 | "version": "1.0.0", 647 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 648 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 649 | "engines": { 650 | "node": ">= 0.8" 651 | } 652 | }, 653 | "node_modules/utils-merge": { 654 | "version": "1.0.1", 655 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 656 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 657 | "engines": { 658 | "node": ">= 0.4.0" 659 | } 660 | }, 661 | "node_modules/vary": { 662 | "version": "1.1.2", 663 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 664 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 665 | "engines": { 666 | "node": ">= 0.8" 667 | } 668 | } 669 | }, 670 | "dependencies": { 671 | "@ardatan/aggregate-error": { 672 | "version": "0.0.6", 673 | "resolved": "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz", 674 | "integrity": "sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ==", 675 | "requires": { 676 | "tslib": "~2.0.1" 677 | }, 678 | "dependencies": { 679 | "tslib": { 680 | "version": "2.0.3", 681 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", 682 | "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" 683 | } 684 | } 685 | }, 686 | "@graphql-tools/batch-execute": { 687 | "version": "7.0.0", 688 | "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-7.0.0.tgz", 689 | "integrity": "sha512-+ywPfK6N2Ddna6oOa5Qb1Mv7EA8LOwRNOAPP9dL37FEhksJM9pYqPSceUcqMqg7S9b0+Cgr78s408rgvurV3/Q==", 690 | "requires": { 691 | "@graphql-tools/utils": "^7.0.0", 692 | "dataloader": "2.0.0", 693 | "is-promise": "4.0.0", 694 | "tslib": "~2.0.1" 695 | }, 696 | "dependencies": { 697 | "tslib": { 698 | "version": "2.0.3", 699 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", 700 | "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" 701 | } 702 | } 703 | }, 704 | "@graphql-tools/delegate": { 705 | "version": "7.0.10", 706 | "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-7.0.10.tgz", 707 | "integrity": "sha512-6Di9ia5ohoDvrHuhj2cak1nJGhIefJmUsd3WKZcJ2nu2yZAFawWMxGvQImqv3N7iyaWKiVhrrK8Roi/JrYhdKg==", 708 | "requires": { 709 | "@ardatan/aggregate-error": "0.0.6", 710 | "@graphql-tools/batch-execute": "^7.0.0", 711 | "@graphql-tools/schema": "^7.0.0", 712 | "@graphql-tools/utils": "^7.1.6", 713 | "dataloader": "2.0.0", 714 | "is-promise": "4.0.0", 715 | "tslib": "~2.1.0" 716 | } 717 | }, 718 | "@graphql-tools/schema": { 719 | "version": "7.1.3", 720 | "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-7.1.3.tgz", 721 | "integrity": "sha512-ZY76hmcJlF1iyg3Im0sQ3ASRkiShjgv102vLTVcH22lEGJeCaCyyS/GF1eUHom418S60bS8Th6+autRUxfBiBg==", 722 | "requires": { 723 | "@graphql-tools/utils": "^7.1.2", 724 | "tslib": "~2.1.0" 725 | } 726 | }, 727 | "@graphql-tools/utils": { 728 | "version": "7.6.0", 729 | "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.6.0.tgz", 730 | "integrity": "sha512-YCZDDdhfb4Yhie0IH031eGdvQG8C73apDuNg6lqBNbauNw45OG/b8wi3+vuMiDnJTJN32GQUb1Gt9gxDKoRDKw==", 731 | "requires": { 732 | "@ardatan/aggregate-error": "0.0.6", 733 | "camel-case": "4.1.2", 734 | "tslib": "~2.1.0" 735 | } 736 | }, 737 | "accepts": { 738 | "version": "1.3.7", 739 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 740 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 741 | "requires": { 742 | "mime-types": "~2.1.24", 743 | "negotiator": "0.6.2" 744 | } 745 | }, 746 | "array-flatten": { 747 | "version": "1.1.1", 748 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 749 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 750 | }, 751 | "body-parser": { 752 | "version": "1.19.0", 753 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 754 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 755 | "requires": { 756 | "bytes": "3.1.0", 757 | "content-type": "~1.0.4", 758 | "debug": "2.6.9", 759 | "depd": "~1.1.2", 760 | "http-errors": "1.7.2", 761 | "iconv-lite": "0.4.24", 762 | "on-finished": "~2.3.0", 763 | "qs": "6.7.0", 764 | "raw-body": "2.4.0", 765 | "type-is": "~1.6.17" 766 | } 767 | }, 768 | "bytes": { 769 | "version": "3.1.0", 770 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 771 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 772 | }, 773 | "camel-case": { 774 | "version": "4.1.2", 775 | "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", 776 | "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", 777 | "requires": { 778 | "pascal-case": "^3.1.2", 779 | "tslib": "^2.0.3" 780 | } 781 | }, 782 | "content-disposition": { 783 | "version": "0.5.3", 784 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 785 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 786 | "requires": { 787 | "safe-buffer": "5.1.2" 788 | } 789 | }, 790 | "content-type": { 791 | "version": "1.0.4", 792 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 793 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 794 | }, 795 | "cookie": { 796 | "version": "0.4.0", 797 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 798 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 799 | }, 800 | "cookie-signature": { 801 | "version": "1.0.6", 802 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 803 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 804 | }, 805 | "dataloader": { 806 | "version": "2.0.0", 807 | "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz", 808 | "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==" 809 | }, 810 | "debug": { 811 | "version": "2.6.9", 812 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 813 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 814 | "requires": { 815 | "ms": "2.0.0" 816 | } 817 | }, 818 | "define-lazy-prop": { 819 | "version": "2.0.0", 820 | "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", 821 | "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" 822 | }, 823 | "depd": { 824 | "version": "1.1.2", 825 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 826 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 827 | }, 828 | "destroy": { 829 | "version": "1.0.4", 830 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 831 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 832 | }, 833 | "ee-first": { 834 | "version": "1.1.1", 835 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 836 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 837 | }, 838 | "encodeurl": { 839 | "version": "1.0.2", 840 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 841 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 842 | }, 843 | "escape-html": { 844 | "version": "1.0.3", 845 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 846 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 847 | }, 848 | "etag": { 849 | "version": "1.8.1", 850 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 851 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 852 | }, 853 | "express": { 854 | "version": "4.17.1", 855 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 856 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 857 | "requires": { 858 | "accepts": "~1.3.7", 859 | "array-flatten": "1.1.1", 860 | "body-parser": "1.19.0", 861 | "content-disposition": "0.5.3", 862 | "content-type": "~1.0.4", 863 | "cookie": "0.4.0", 864 | "cookie-signature": "1.0.6", 865 | "debug": "2.6.9", 866 | "depd": "~1.1.2", 867 | "encodeurl": "~1.0.2", 868 | "escape-html": "~1.0.3", 869 | "etag": "~1.8.1", 870 | "finalhandler": "~1.1.2", 871 | "fresh": "0.5.2", 872 | "merge-descriptors": "1.0.1", 873 | "methods": "~1.1.2", 874 | "on-finished": "~2.3.0", 875 | "parseurl": "~1.3.3", 876 | "path-to-regexp": "0.1.7", 877 | "proxy-addr": "~2.0.5", 878 | "qs": "6.7.0", 879 | "range-parser": "~1.2.1", 880 | "safe-buffer": "5.1.2", 881 | "send": "0.17.1", 882 | "serve-static": "1.14.1", 883 | "setprototypeof": "1.1.1", 884 | "statuses": "~1.5.0", 885 | "type-is": "~1.6.18", 886 | "utils-merge": "1.0.1", 887 | "vary": "~1.1.2" 888 | } 889 | }, 890 | "finalhandler": { 891 | "version": "1.1.2", 892 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 893 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 894 | "requires": { 895 | "debug": "2.6.9", 896 | "encodeurl": "~1.0.2", 897 | "escape-html": "~1.0.3", 898 | "on-finished": "~2.3.0", 899 | "parseurl": "~1.3.3", 900 | "statuses": "~1.5.0", 901 | "unpipe": "~1.0.0" 902 | } 903 | }, 904 | "forwarded": { 905 | "version": "0.1.2", 906 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 907 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 908 | }, 909 | "fresh": { 910 | "version": "0.5.2", 911 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 912 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 913 | }, 914 | "graphql": { 915 | "version": "15.5.0", 916 | "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.5.0.tgz", 917 | "integrity": "sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA==" 918 | }, 919 | "graphql-middleware": { 920 | "version": "6.0.4", 921 | "resolved": "https://registry.npmjs.org/graphql-middleware/-/graphql-middleware-6.0.4.tgz", 922 | "integrity": "sha512-T49lpvrRHpUzBXUupNnBx82WAmWBoTmsWcc+XdblYClj9WnRMrtxeunQlTsbsSCSD7ZahN1MELd0xKfC2hfwUw==", 923 | "requires": { 924 | "@graphql-tools/delegate": "^7.0.10", 925 | "@graphql-tools/schema": "^7.1.3" 926 | } 927 | }, 928 | "http-errors": { 929 | "version": "1.7.2", 930 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 931 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 932 | "requires": { 933 | "depd": "~1.1.2", 934 | "inherits": "2.0.3", 935 | "setprototypeof": "1.1.1", 936 | "statuses": ">= 1.5.0 < 2", 937 | "toidentifier": "1.0.0" 938 | } 939 | }, 940 | "iconv-lite": { 941 | "version": "0.4.24", 942 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 943 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 944 | "requires": { 945 | "safer-buffer": ">= 2.1.2 < 3" 946 | } 947 | }, 948 | "inherits": { 949 | "version": "2.0.3", 950 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 951 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 952 | }, 953 | "ipaddr.js": { 954 | "version": "1.9.1", 955 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 956 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 957 | }, 958 | "is-docker": { 959 | "version": "2.1.1", 960 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", 961 | "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==" 962 | }, 963 | "is-promise": { 964 | "version": "4.0.0", 965 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", 966 | "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" 967 | }, 968 | "is-wsl": { 969 | "version": "2.2.0", 970 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", 971 | "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", 972 | "requires": { 973 | "is-docker": "^2.0.0" 974 | } 975 | }, 976 | "lower-case": { 977 | "version": "2.0.2", 978 | "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", 979 | "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", 980 | "requires": { 981 | "tslib": "^2.0.3" 982 | } 983 | }, 984 | "media-typer": { 985 | "version": "0.3.0", 986 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 987 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 988 | }, 989 | "merge-descriptors": { 990 | "version": "1.0.1", 991 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 992 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 993 | }, 994 | "methods": { 995 | "version": "1.1.2", 996 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 997 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 998 | }, 999 | "mime": { 1000 | "version": "1.6.0", 1001 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1002 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 1003 | }, 1004 | "mime-db": { 1005 | "version": "1.46.0", 1006 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", 1007 | "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" 1008 | }, 1009 | "mime-types": { 1010 | "version": "2.1.29", 1011 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", 1012 | "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", 1013 | "requires": { 1014 | "mime-db": "1.46.0" 1015 | } 1016 | }, 1017 | "ms": { 1018 | "version": "2.0.0", 1019 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1020 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1021 | }, 1022 | "negotiator": { 1023 | "version": "0.6.2", 1024 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 1025 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 1026 | }, 1027 | "no-case": { 1028 | "version": "3.0.4", 1029 | "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", 1030 | "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", 1031 | "requires": { 1032 | "lower-case": "^2.0.2", 1033 | "tslib": "^2.0.3" 1034 | } 1035 | }, 1036 | "on-finished": { 1037 | "version": "2.3.0", 1038 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1039 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1040 | "requires": { 1041 | "ee-first": "1.1.1" 1042 | } 1043 | }, 1044 | "open": { 1045 | "version": "8.0.4", 1046 | "resolved": "https://registry.npmjs.org/open/-/open-8.0.4.tgz", 1047 | "integrity": "sha512-Txc9FOcvjrr5Kv+Zb3w89uKMKiP7wH8mLdYj1xJa+YnhhntEYhbB6cQHjS4O6P+jFwMEzEQVVcpfnu9WkKNuLQ==", 1048 | "requires": { 1049 | "define-lazy-prop": "^2.0.0", 1050 | "is-docker": "^2.1.1", 1051 | "is-wsl": "^2.2.0" 1052 | } 1053 | }, 1054 | "parseurl": { 1055 | "version": "1.3.3", 1056 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1057 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 1058 | }, 1059 | "pascal-case": { 1060 | "version": "3.1.2", 1061 | "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", 1062 | "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", 1063 | "requires": { 1064 | "no-case": "^3.0.4", 1065 | "tslib": "^2.0.3" 1066 | } 1067 | }, 1068 | "path-to-regexp": { 1069 | "version": "0.1.7", 1070 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1071 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 1072 | }, 1073 | "proxy-addr": { 1074 | "version": "2.0.6", 1075 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 1076 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 1077 | "requires": { 1078 | "forwarded": "~0.1.2", 1079 | "ipaddr.js": "1.9.1" 1080 | } 1081 | }, 1082 | "qs": { 1083 | "version": "6.7.0", 1084 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 1085 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 1086 | }, 1087 | "range-parser": { 1088 | "version": "1.2.1", 1089 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1090 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 1091 | }, 1092 | "raw-body": { 1093 | "version": "2.4.0", 1094 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 1095 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 1096 | "requires": { 1097 | "bytes": "3.1.0", 1098 | "http-errors": "1.7.2", 1099 | "iconv-lite": "0.4.24", 1100 | "unpipe": "1.0.0" 1101 | } 1102 | }, 1103 | "safe-buffer": { 1104 | "version": "5.1.2", 1105 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1106 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1107 | }, 1108 | "safer-buffer": { 1109 | "version": "2.1.2", 1110 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1111 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1112 | }, 1113 | "send": { 1114 | "version": "0.17.1", 1115 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 1116 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 1117 | "requires": { 1118 | "debug": "2.6.9", 1119 | "depd": "~1.1.2", 1120 | "destroy": "~1.0.4", 1121 | "encodeurl": "~1.0.2", 1122 | "escape-html": "~1.0.3", 1123 | "etag": "~1.8.1", 1124 | "fresh": "0.5.2", 1125 | "http-errors": "~1.7.2", 1126 | "mime": "1.6.0", 1127 | "ms": "2.1.1", 1128 | "on-finished": "~2.3.0", 1129 | "range-parser": "~1.2.1", 1130 | "statuses": "~1.5.0" 1131 | }, 1132 | "dependencies": { 1133 | "ms": { 1134 | "version": "2.1.1", 1135 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1136 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 1137 | } 1138 | } 1139 | }, 1140 | "serve-static": { 1141 | "version": "1.14.1", 1142 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 1143 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 1144 | "requires": { 1145 | "encodeurl": "~1.0.2", 1146 | "escape-html": "~1.0.3", 1147 | "parseurl": "~1.3.3", 1148 | "send": "0.17.1" 1149 | } 1150 | }, 1151 | "setprototypeof": { 1152 | "version": "1.1.1", 1153 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 1154 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 1155 | }, 1156 | "statuses": { 1157 | "version": "1.5.0", 1158 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1159 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 1160 | }, 1161 | "toidentifier": { 1162 | "version": "1.0.0", 1163 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 1164 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 1165 | }, 1166 | "tslib": { 1167 | "version": "2.1.0", 1168 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", 1169 | "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" 1170 | }, 1171 | "type-is": { 1172 | "version": "1.6.18", 1173 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1174 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1175 | "requires": { 1176 | "media-typer": "0.3.0", 1177 | "mime-types": "~2.1.24" 1178 | } 1179 | }, 1180 | "unpipe": { 1181 | "version": "1.0.0", 1182 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1183 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 1184 | }, 1185 | "utils-merge": { 1186 | "version": "1.0.1", 1187 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1188 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 1189 | }, 1190 | "vary": { 1191 | "version": "1.1.2", 1192 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1193 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 1194 | } 1195 | } 1196 | } 1197 | -------------------------------------------------------------------------------- /nuqleus_npm_package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuqleus", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "nuqleus.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@graphql-tools/schema": "^7.1.3", 14 | "express": "^4.17.1", 15 | "graphql": "^15.5.0", 16 | "graphql-middleware": "^6.0.4", 17 | "open": "^8.0.4" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /nuqleus_npm_package/public/src/assets/images/nuQLeus_favicon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/nuQLeus/4c7da39122e60631c9516663021874d39fefa255/nuqleus_npm_package/public/src/assets/images/nuQLeus_favicon_32x32.png -------------------------------------------------------------------------------- /nuqleus_npm_package/public/src/assets/images/nuQLeus_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/nuQLeus/4c7da39122e60631c9516663021874d39fefa255/nuqleus_npm_package/public/src/assets/images/nuQLeus_logo.png -------------------------------------------------------------------------------- /nuqleus_npm_package/public/src/assets/images/nuQLeus_logo_black_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/nuQLeus/4c7da39122e60631c9516663021874d39fefa255/nuqleus_npm_package/public/src/assets/images/nuQLeus_logo_black_background.png -------------------------------------------------------------------------------- /nuqleus_npm_package/public/src/assets/images/nuQLeus_logo_reversed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/nuQLeus/4c7da39122e60631c9516663021874d39fefa255/nuqleus_npm_package/public/src/assets/images/nuQLeus_logo_reversed.png -------------------------------------------------------------------------------- /nuqleus_npm_package/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | Creates an Express server that serves static files for the nuQLeus GUI to: localhost:3030/nuqleus 3 | when the nuqleus wrapper methods are invoked on an enduser's server. 4 | */ 5 | 6 | const path = require('path'); 7 | const express = require('express'); 8 | const app = express(); 9 | const PORT = 3030; 10 | 11 | app.use(express.json()); 12 | app.use(express.urlencoded({ extended: true })); 13 | 14 | app.use(express.static('.')); 15 | 16 | app.get('/nuqleus', (req, res) => { 17 | res.status(200).sendFile(path.join(__dirname, './index.html')); 18 | }); 19 | 20 | app.listen(PORT, () => { 21 | console.log(`Listening on port ${PORT}`); 22 | }); 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuqleus", 3 | "version": "0.1.0", 4 | "description": "Boost GraphQL endpoint testing capabilities with resolver-level performance metrics", 5 | "main": "main.js", 6 | "scripts": { 7 | "build": "webpack --config webpack.config.js", 8 | "dev": "webpack serve --open --hot" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/oslabs-beta/NuQLeus" 13 | }, 14 | "author": "", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/oslabs-beta/NuQLeus/issues" 18 | }, 19 | "homepage": "https://github.com/oslabs-beta/NuQLeus#readme", 20 | "jest": { 21 | "moduleFileExtensions": ["js", "jsx"], 22 | "moduleDirectories": ["node_modules", "bower_components", "shared"], 23 | "moduleNameMapper": { 24 | "\\.(css|less)$": "/__mocks__/styleMock.js" 25 | }, 26 | "transformIgnorePatterns": [ 27 | "/node_modules/codemirror/lib.+\\.css$" 28 | ], 29 | "^react(.*)$": "/vendor/react-master$1", 30 | "^config$": "/configs/app-config.js" 31 | }, 32 | "dependencies": { 33 | "@tsconfig/recommended": "^1.0.1", 34 | "@wojtekmaj/enzyme-adapter-react-17": "^0.6.1", 35 | "codemirror": "^5.59.4", 36 | "codemirror-graphql": "^1.0.0", 37 | "graphql": "^15.5.0", 38 | "react": "^16.14.0", 39 | "react-codemirror2": "^7.2.1", 40 | "react-dom": "^16.14.0", 41 | "react-router": "^5.2.0", 42 | "react-router-dom": "^5.2.0", 43 | "victory": "^35.4.11" 44 | }, 45 | "devDependencies": { 46 | "@babel/cli": "^7.1.0", 47 | "@babel/core": "^7.1.0", 48 | "@babel/plugin-proposal-class-properties": "^7.13.0", 49 | "@babel/plugin-proposal-decorators": "^7.13.5", 50 | "@babel/plugin-transform-runtime": "^7.12.15", 51 | "@babel/preset-env": "^7.1.0", 52 | "@babel/preset-react": "^7.0.0", 53 | "@testing-library/jest-dom": "^5.11.9", 54 | "@testing-library/react": "^11.2.5", 55 | "@types/jest": "^26.0.23", 56 | "@types/react": "^17.0.2", 57 | "@types/react-dom": "^17.0.1", 58 | "babel-jest": "^26.6.3", 59 | "babel-loader": "^8.0.2", 60 | "css-loader": "^1.0.0", 61 | "enzyme": "^3.11.0", 62 | "jest": "^26.6.3", 63 | "style-loader": "^0.23.0", 64 | "ts-loader": "^8.0.17", 65 | "typescript": "^4.2.2", 66 | "webpack": "^4.46.0", 67 | "webpack-cli": "^4.5.0", 68 | "webpack-dev-server": "^3.11.2" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/assets/gifs/nuqleus_showcase.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/nuQLeus/4c7da39122e60631c9516663021874d39fefa255/src/assets/gifs/nuqleus_showcase.gif -------------------------------------------------------------------------------- /src/assets/images/nuQLeus_favicon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/nuQLeus/4c7da39122e60631c9516663021874d39fefa255/src/assets/images/nuQLeus_favicon_32x32.png -------------------------------------------------------------------------------- /src/assets/images/nuQLeus_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/nuQLeus/4c7da39122e60631c9516663021874d39fefa255/src/assets/images/nuQLeus_logo.png -------------------------------------------------------------------------------- /src/assets/images/nuQLeus_logo_black_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/nuQLeus/4c7da39122e60631c9516663021874d39fefa255/src/assets/images/nuQLeus_logo_black_background.png -------------------------------------------------------------------------------- /src/assets/images/nuQLeus_logo_reversed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/nuQLeus/4c7da39122e60631c9516663021874d39fefa255/src/assets/images/nuQLeus_logo_reversed.png -------------------------------------------------------------------------------- /src/client/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GraphContextProvider } from './contexts/GraphContext'; 3 | import MainContainer from './containers/MainContainer'; 4 | import NavBar from './components/NavBar'; 5 | import "./stylesheets/styles.css"; 6 | 7 | const App = () => ( 8 |
9 | 10 | 11 | 12 | 13 |
14 | ); 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /src/client/components/EmptyTracingData.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { GraphContext } from '../contexts/GraphContext'; 3 | 4 | const EmptyTracingData = () => { 5 | const [info, setInfo] = useContext(GraphContext); 6 | 7 | const message = info.extensions === '' ? '' : 'No tracing data available'; 8 | return ( 9 |
10 |

{message}

11 |
12 | ); 13 | }; 14 | 15 | export default EmptyTracingData; 16 | -------------------------------------------------------------------------------- /src/client/components/NavBar.jsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | const NavBar = () => { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | }; 10 | 11 | export default NavBar; 12 | -------------------------------------------------------------------------------- /src/client/components/OutputDisplay.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { Controlled as CodeMirror } from 'react-codemirror2'; 3 | import { GraphContext } from '../contexts/GraphContext'; 4 | 5 | require('codemirror/mode/xml/xml'); 6 | require('codemirror/mode/javascript/javascript'); 7 | require('codemirror-graphql/mode'); 8 | require('codemirror/mode/jsx/jsx'); 9 | require('codemirror-graphql/hint'); 10 | require('codemirror-graphql/lint'); 11 | require('codemirror/lib/codemirror.css'); 12 | require('../stylesheets/editor-theme.css'); 13 | require('codemirror/addon/edit/closebrackets'); 14 | 15 | const OutputDisplay = () => { 16 | const [info, setInfo] = useContext(GraphContext); 17 | 18 | const DEFAULT_JSX_OPTIONS = { 19 | theme: 'custom-0', 20 | autoCloseBrackets: true, 21 | cursorScrollMargin: 48, 22 | mode: 'javascript', 23 | lineNumbers: true, 24 | indentUnit: 2, 25 | tabSize: 2, 26 | styleActiveLine: true, 27 | viewportMargin: 99, 28 | }; 29 | 30 | return ( 31 | <> 32 | { 37 | value = info.response; 38 | }} 39 | onChange={(editor, metadata, value) => { 40 | value = info.response; 41 | }} 42 | /> 43 | 44 | ); 45 | }; 46 | 47 | export default OutputDisplay; 48 | -------------------------------------------------------------------------------- /src/client/components/QueryField.jsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useContext } from 'react'; 3 | import { Controlled as CodeMirror } from 'react-codemirror2'; 4 | import { GraphContext } from '../contexts/GraphContext'; 5 | 6 | require('codemirror/mode/xml/xml'); 7 | require('codemirror/mode/javascript/javascript'); 8 | require('codemirror-graphql/mode'); 9 | require('codemirror/mode/jsx/jsx'); 10 | require('codemirror-graphql/hint'); 11 | require('codemirror-graphql/lint'); 12 | require('codemirror/lib/codemirror.css'); 13 | require('../stylesheets/editor-theme.css'); 14 | require('codemirror/addon/edit/closebrackets'); 15 | 16 | const QueryEditor = () => { 17 | const [info, setInfo] = useContext(GraphContext); 18 | 19 | const DEFAULT_JSX_OPTIONS = { 20 | theme: 'custom-0', 21 | autoCloseBrackets: true, 22 | cursorScrollMargin: 48, 23 | mode: 'graphql', 24 | lineNumbers: true, 25 | indentUnit: 2, 26 | tabSize: 2, 27 | styleActiveLine: true, 28 | viewportMargin: 99, 29 | placeholder: 'Enter query or mutation' 30 | }; 31 | 32 | const onChange = (editor, data, value) => { 33 | setInfo(() => ({ 34 | ...info, 35 | body: value, 36 | })); 37 | }; 38 | 39 | return ( 40 | <> 41 |

Body

42 | { 47 | // final value, no need to setState here 48 | }} 49 | /> 50 | 51 | ); 52 | }; 53 | 54 | export default QueryEditor; 55 | -------------------------------------------------------------------------------- /src/client/components/ServerField.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { GraphContext } from '../contexts/GraphContext'; 3 | 4 | const ServerField = () => { 5 | // Pull state into component from ApolloContext using 'useContext' hook 6 | const [info, setInfo] = useContext(GraphContext); 7 | 8 | // Invokes query to the GraphQL server/API 9 | function queryTime(extensions) { 10 | // Grab variables from nuQLeusTracing field: 11 | const { startTime, endTime, duration } = extensions.nuQLeusTracing; 12 | return { startTime, endTime, duration }; 13 | }; 14 | 15 | // Resolvers argument should be an array of resolver objects 16 | function resolverTime(resolvers) { 17 | const averageResolverResponse = []; 18 | const cache = {}; 19 | 20 | for (let i = 0; i < resolvers.length; i++) { 21 | const { parentType } = resolvers[i]; 22 | const { fieldName } = resolvers[i]; 23 | const key = `${parentType}-${fieldName}`; 24 | if (!cache[key]) cache[key] = []; 25 | cache[key].push(resolvers[i].duration); 26 | }; 27 | 28 | for (const key in cache) { 29 | const keys = key.split('-'); 30 | const obj = { 31 | parentType: keys[0], 32 | fieldName: keys[1], 33 | durations: cache[key], 34 | average: Math.round((cache[key].reduce((a, b) => a + b) / cache[key].length) * 100) / 100, 35 | }; 36 | averageResolverResponse.push(obj); 37 | }; 38 | 39 | return averageResolverResponse; 40 | }; 41 | 42 | function updateGraphData(queryTime, resolverTime) { 43 | const data = []; 44 | 45 | data.push({x: 'Total Query Time', y: queryTime.duration, label: queryTime.duration + 'ms'}); 46 | 47 | resolverTime.forEach((ele) => { 48 | const coordinate = {}; 49 | coordinate.x = ele.parentType + ' : ' + ele.fieldName; 50 | coordinate.y = ele.average; 51 | coordinate.label = ele.average + 'ms'; 52 | 53 | data.push(coordinate); 54 | }) 55 | 56 | // Reverse bar chart to show highest values on top 57 | data.reverse(); 58 | return data; 59 | }; 60 | 61 | // Invokes query to the Apollo client 62 | function handleClick(e) { 63 | e.preventDefault(); 64 | 65 | // Gather user input from 'Server', 'Query', and 'Variables' input fields; determine request 'type' 66 | const userURI = document.getElementById('input-link').value; 67 | let userVariables; 68 | if (info.variables === '') userVariables = {}; 69 | else userVariables = JSON.parse(info.variables); 70 | 71 | // Function to send the user's query to the GraphQL server/API 72 | const handleRequest = () => { 73 | fetch(`${userURI}`, { 74 | method: 'POST', 75 | headers: { 76 | 'Content-Type': 'application/json', 77 | }, 78 | body: JSON.stringify({ 79 | query: `${info.body}`, 80 | variables: userVariables, 81 | }), 82 | }) 83 | .then((res) => res.json()) 84 | .then((res) => { 85 | const extensionsExist = res.extensions ? true : false; 86 | 87 | if (extensionsExist && res.extensions.nuQLeusTracing) { 88 | const queryTimeData = queryTime(res.extensions); 89 | const resolverTimeData = resolverTime(res.extensions.nuQLeusTracing.resolvers); 90 | const graphData = updateGraphData(queryTimeData, resolverTimeData); 91 | 92 | setInfo(() => ({ 93 | ...info, 94 | response: res.data, 95 | extensions: res.extensions, 96 | queryTime: queryTimeData, 97 | resolverTime: resolverTimeData, 98 | graphData 99 | })); 100 | } else { 101 | setInfo(() => ({ 102 | ...info, 103 | response: res.data ? res.data : res, 104 | extensions: null 105 | })); 106 | } 107 | }) 108 | .catch((err) => { 109 | setInfo(() => ({ 110 | ...info, 111 | response: 'Request to server failed.', 112 | })); 113 | }) 114 | }; 115 | 116 | // Function to handle invalid user input 117 | const handleInvalid = () => { 118 | setInfo(() => ({ 119 | ...info, 120 | response: 'Invalid Syntax', 121 | })); 122 | }; 123 | 124 | // Validate Input 125 | if ( 126 | info.body.substring(0, 5).toLowerCase() === 'query' 127 | || info.body.substring(0, 5).toLowerCase() === 'mutat' 128 | || info.body[0] === '{' 129 | ) { 130 | handleRequest(); 131 | } else { 132 | handleInvalid(); 133 | }; 134 | }; 135 | 136 | return ( 137 |
138 |

Server:

139 |
140 |
141 |
142 | ); 143 | }; 144 | 145 | export default ServerField; 146 | -------------------------------------------------------------------------------- /src/client/components/VariableField.jsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useContext } from 'react'; 3 | import { Controlled as CodeMirror } from 'react-codemirror2'; 4 | import { GraphContext } from '../contexts/GraphContext'; 5 | 6 | require('codemirror/mode/xml/xml'); 7 | require('codemirror/mode/javascript/javascript'); 8 | require('codemirror-graphql/mode'); 9 | require('codemirror/mode/jsx/jsx'); 10 | require('codemirror-graphql/hint'); 11 | require('codemirror-graphql/lint'); 12 | require('codemirror/lib/codemirror.css'); 13 | require('../stylesheets/editor-theme.css'); 14 | require('codemirror/addon/edit/closebrackets'); 15 | require('codemirror/addon/display/placeholder'); 16 | 17 | const VariableField = () => { 18 | const [info, setInfo] = useContext(GraphContext); 19 | const DEFAULT_JSX_OPTIONS = { 20 | theme: 'custom-0', 21 | autoCloseBrackets: true, 22 | cursorScrollMargin: 48, 23 | mode: 'javascript', 24 | lineNumbers: true, 25 | indentUnit: 2, 26 | tabSize: 2, 27 | styleActiveLine: true, 28 | viewportMargin: 99, 29 | placeholder: 'Variables must be in JSON format', 30 | }; 31 | 32 | const onChange = (editor, data, value) => { 33 | setInfo(() => ({ 34 | ...info, 35 | variables: value, 36 | })); 37 | }; 38 | 39 | return ( 40 | <> 41 |

Variables

42 | { 47 | // final value, no need to setState here 48 | }} 49 | /> 50 | 51 | ); 52 | }; 53 | 54 | export default VariableField; 55 | -------------------------------------------------------------------------------- /src/client/components/VisualDisplay.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useContext } from 'react'; 2 | import { VictoryBar, VictoryChart, VictoryTheme, VictoryLabel, VictoryGroup, VictoryAxis, VictoryTooltip } from 'victory'; 3 | import { GraphContext } from '../contexts/GraphContext'; 4 | 5 | const VisualDisplay = () => { 6 | const [info, setInfo] = useContext(GraphContext); 7 | const [data, setData] = useState(info.graphData); 8 | // Write out the axis labels into arrays 9 | const labelsData = []; 10 | const labelLenData = []; 11 | // Use values from state (updated from ServerField to GraphContext) to update [x, y] coordinates 12 | for (let i = 0; i < data.length; ++i) { 13 | labelsData.push(data[i].x); 14 | labelLenData.push(i); 15 | }; 16 | 17 | const [labels, setLabels] = useState(labelsData); 18 | const [labelLen, setLabelLen] = useState(labelLenData); 19 | 20 | // Whenever a query is made and state gets updated, 21 | useEffect(() => { 22 | setData(info.graphData); 23 | }, [info.response]); 24 | 25 | useEffect(() => { 26 | const labels = []; 27 | const labelLen = []; 28 | 29 | for (let i = 0; i < data.length; ++i) { 30 | labels.push(data[i].x); 31 | labelLen.push(i); 32 | } 33 | 34 | setLabels(labels); 35 | setLabelLen(labelLen); 36 | }, [data]) 37 | 38 | // Conditiionally adjust height according to number of rows: 39 | let height = 300; 40 | if (labelLen.length > 7) height = 600; 41 | 42 | return ( 43 | <> 44 | 55 | ({ 64 | _y: 0, 65 | }) 66 | } 67 | }} 68 | labelComponent={ 69 | 77 | } 78 | /> 79 | 87 | (`${x}ms`))} 90 | style={{ 91 | axis: { stroke: 'white', }, 92 | tickLabels: { fill: 'white', }, 93 | }} 94 | fixLabelOverlap={true} 95 | /> 96 | 97 | 98 | ); 99 | }; 100 | 101 | export default VisualDisplay; 102 | -------------------------------------------------------------------------------- /src/client/containers/LeftContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ServerField from '../components/ServerField'; 3 | import VariableField from '../components/VariableField'; 4 | import QueryField from '../components/QueryField'; 5 | 6 | const LeftContainer = () => ( 7 |
8 | 9 | 10 | 11 |
12 | ); 13 | 14 | export default LeftContainer; 15 | -------------------------------------------------------------------------------- /src/client/containers/MainContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import LeftContainer from './LeftContainer'; 3 | import MiddleContainer from './MiddleContainer'; 4 | import RightContainer from './RightContainer'; 5 | 6 | const MainContainer = () => ( 7 |
8 | 9 | 10 | 11 |
12 | ); 13 | 14 | export default MainContainer; 15 | -------------------------------------------------------------------------------- /src/client/containers/MiddleContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import OutputDisplay from '../components/OutputDisplay'; 3 | 4 | const MiddleContainer = () => ( 5 |
6 |

Response

7 | 8 |
9 | ); 10 | 11 | export default MiddleContainer; 12 | -------------------------------------------------------------------------------- /src/client/containers/RightContainer.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import VisualDisplay from '../components/VisualDisplay'; 3 | import EmptyTracingData from '../components/EmptyTracingData'; 4 | import { GraphContext } from '../contexts/GraphContext'; 5 | 6 | const RightContainer = () => { 7 | const [info, setInfo] = useContext(GraphContext); 8 | 9 | const conditionRender = info.extensions ? : ; 10 | 11 | return ( 12 |
13 |

Tracing Data

14 | {conditionRender} 15 |
16 | ) 17 | }; 18 | 19 | export default RightContainer; 20 | -------------------------------------------------------------------------------- /src/client/contexts/GraphContext.jsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useState } from 'react'; 2 | 3 | export const GraphContext = createContext(); 4 | 5 | export const GraphContextProvider = (props) => { 6 | const [info, setInfo] = useState({ 7 | uri: '', 8 | body: '', 9 | variables: '', 10 | response: '', 11 | extensions: '', 12 | queryTime: '', 13 | resolverTime: '', 14 | graphData: [] 15 | }); 16 | 17 | return {props.children}; 18 | }; 19 | -------------------------------------------------------------------------------- /src/client/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /src/client/stylesheets/editor-theme.css: -------------------------------------------------------------------------------- 1 | .cm-s-custom-0 { 2 | background: rgb(19, 19, 37); 3 | color: rgb(173, 170, 204); 4 | font-weight: 300; 5 | line-height: 1.4; 6 | font-size: 14px; 7 | font-family: "IBM Plex Mono", monospace; 8 | letter-spacing: 0.41px; 9 | } 10 | 11 | .cm-s-custom-0 .CodeMirror-cursor { 12 | border-color: #fff; 13 | outline: 1px solid #fff; 14 | } 15 | 16 | .cm-s-custom-0 .CodeMirror-activeline-background { 17 | background: rgba(240, 240, 255, 0.8); 18 | } 19 | 20 | .cm-s-custom-0 .CodeMirror-selected { 21 | background: rgba(55, 55, 77, 0.3); 22 | } 23 | 24 | .cm-s-custom-0 .cm-comment { 25 | font-style: italic; 26 | color: #676b79; 27 | } 28 | 29 | .cm-s-custom-0 .cm-operator { 30 | color: #f3f3f3; 31 | } 32 | 33 | .cm-s-custom-0 .cm-string { 34 | color: #c267f9; 35 | } 36 | 37 | .cm-s-custom-0 .cm-string-2 { 38 | color: #ffb86c; 39 | } 40 | 41 | .cm-s-custom-0 .cm-tag { 42 | color: #ff2c6d; 43 | } 44 | 45 | .cm-s-custom-0 .cm-meta { 46 | color: #b084eb; 47 | } 48 | 49 | .cm-s-custom-0 .cm-number { 50 | color: #ffb86c; 51 | } 52 | 53 | .cm-s-custom-0 .cm-atom { 54 | color: #ff2c6d; 55 | } 56 | 57 | .cm-s-custom-0 .cm-keyword { 58 | color: #b095e4; 59 | } 60 | 61 | .cm-s-custom-0 .cm-variable { 62 | color: #f73c91; 63 | } 64 | 65 | .cm-s-custom-0 .cm-variable-2 { 66 | color: #ff9ac1; 67 | } 68 | 69 | .cm-s-custom-0 .cm-variable-3 .cm-s-custom-0 .cm-type { 70 | color: #ff9ac1; 71 | } 72 | 73 | .cm-s-custom-0 .cm-def { 74 | color: #e6e6e6; 75 | } 76 | 77 | .cm-s-custom-0 .cm-property { 78 | color: #f3f3f3; 79 | } 80 | 81 | .cm-s-custom-0 .cm-unit { 82 | color: #ffb86c; 83 | } 84 | 85 | .cm-s-custom-0 .cm-attribute { 86 | color: #d8baea; 87 | } 88 | 89 | .cm-s-custom-0 .CodeMirror-matchingbracket { 90 | border-bottom: 1px dotted #19f9d8; 91 | padding-bottom: 2px; 92 | color: #e6e6e6; 93 | } 94 | 95 | .cm-s-custom-0 .CodeMirror-gutters { 96 | background: rgb(19, 19, 37); 97 | border-right-color: rgb(19, 19, 37); 98 | width: 40px; 99 | margin-right: 5px; 100 | } 101 | 102 | .cm-s-custom-0 .CodeMirror-linenumber { 103 | color: rgb(173, 170, 204); 104 | opacity: 0.75; 105 | background: rgb(19, 19, 37); 106 | padding: 0 8px 0 4px; 107 | font-size: 14px; 108 | border-top: 2px solid transparent; 109 | } 110 | 111 | .cm-s-custom-0 .CodeMirror-lines .CodeMirror-code div { 112 | padding-left: 2px; 113 | }; 114 | -------------------------------------------------------------------------------- /src/client/stylesheets/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system,BlinkMacSystemFont,segoe ui,roboto,oxygen,ubuntu,cantarell,fira sans,droid sans,helvetica neue,sans-serif; 3 | background-color: rgb(1, 2, 11); 4 | color: rgb(195, 194, 197); 5 | } 6 | 7 | input:focus, 8 | select:focus, 9 | textarea:focus, 10 | button:focus { 11 | outline: none; 12 | } 13 | 14 | /***** TITLES *****/ 15 | h3 { 16 | margin-block-start: 0em; 17 | } 18 | 19 | .no-tracing { 20 | display: flex; 21 | flex-direction: column; 22 | justify-content: center; 23 | align-items: center; 24 | height: 100%; 25 | } 26 | 27 | .notracing .no-tracing-msg { 28 | align-self: center; 29 | } 30 | 31 | .no-tracing-msg { 32 | color: #3e3a41; 33 | } 34 | 35 | /***** CONTAINERS *****/ 36 | #main-container { 37 | display: flex; 38 | flex-direction: row; 39 | justify-content: space-evenly; 40 | } 41 | 42 | #left-container, 43 | #middle-container, 44 | #right-container { 45 | width: 33%; 46 | min-height: 95%; 47 | } 48 | 49 | 50 | /***** CODEMIRROR *****/ 51 | 52 | .CodeMirror { 53 | min-height: 100%; 54 | /* font-family: 'Ubuntu Mono', monospace !important; */ 55 | /* font-family: "IBM Plex Mono", monospace; */ 56 | } 57 | 58 | .react-codemirror2 { 59 | width: 95%; 60 | } 61 | 62 | .Resizer { 63 | height: 100vh; 64 | min-width: 2px; 65 | background: #333; 66 | cursor: row-resize; 67 | } 68 | 69 | /***** NAVBAR *****/ 70 | 71 | .logo-img { 72 | width: 250px 73 | } 74 | 75 | .nav-bar { 76 | margin-bottom: 5px; 77 | } 78 | 79 | /***** INPUTS *****/ 80 | .input { 81 | overflow-wrap: break-word; 82 | } 83 | 84 | #input-link { 85 | height: 1.2em; 86 | width: 100%; 87 | background-color: rgb(19, 19, 37); 88 | border: none; 89 | color: white; 90 | padding: 10px; 91 | /* margin: 0px 10px; */ 92 | } 93 | 94 | .btn-gray { 95 | background-color: transparent; 96 | text-indent: 0px; 97 | text-shadow: none; 98 | border:none; 99 | margin-left: 30px; 100 | background-color: #1c232f; 101 | padding: 12px 30px; 102 | text-align: center; 103 | text-transform: uppercase; 104 | transition: 0.5s; 105 | background-size: 200% auto; 106 | color: #bec0c2; 107 | border-radius: 10px; 108 | display: inline; 109 | } 110 | 111 | .btn-gray:hover { 112 | background-color: #141a24; 113 | } 114 | 115 | /* #query-input { 116 | height: 6em; 117 | width: 95%; 118 | } 119 | 120 | #variable-input { 121 | height: 6em; 122 | width: 95%; 123 | } */ 124 | 125 | /***** OUTPUTS *****/ 126 | 127 | .output-display { 128 | height: 95%; 129 | width: 95%; 130 | /* overflow: auto; 131 | overflow-wrap: break-word; 132 | border: 1px solid purple; */ 133 | } 134 | 135 | .server-field { 136 | display: inline-flex; 137 | width: 95%; 138 | align-items: center; 139 | justify-content: space-between; 140 | } 141 | 142 | .server-title, .server-btn { 143 | flex-grow: 1 144 | } 145 | 146 | .server-input { 147 | flex-grow: 40 148 | } 149 | 150 | /***** RESPONSIVE CSS *****/ 151 | 152 | @media only screen and (max-width: 768px) { 153 | /* For mobile phones: */ 154 | #main-container { 155 | display: flex; 156 | flex-direction: column; 157 | justify-content: space-evenly; 158 | } 159 | 160 | #left-container, 161 | #middle-container, 162 | #right-container { 163 | width: 100%; 164 | min-height: 50%; 165 | margin-bottom: 20px; 166 | } 167 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | // "outDir": "./", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true, /* Enable all strict type-checking options. */ 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 44 | 45 | /* Module Resolution Options */ 46 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 47 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 48 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 49 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 50 | // "typeRoots": [], /* List of folders to include type definitions from. */ 51 | // "types": [], /* Type declaration files to be included in compilation. */ 52 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 53 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 54 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 55 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 56 | 57 | /* Source Map Options */ 58 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 61 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 62 | 63 | /* Experimental Options */ 64 | "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 65 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 66 | 67 | /* Advanced Options */ 68 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 69 | "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/client/index.jsx', 5 | mode: 'production', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.tsx?$/, 10 | exclude: /node_modules/, 11 | include: [path.resolve(__dirname, 'src')], 12 | use: 'ts-loader', 13 | }, 14 | { 15 | test: /\.(jsx?)$/, 16 | exclude: /node_modules/, 17 | loader: 'babel-loader', 18 | options: { 19 | presets: ['@babel/preset-env', '@babel/preset-react'], 20 | }, 21 | }, 22 | { 23 | test: /\.(css)$/, 24 | // exclude: /node_modules/, 25 | use: ['style-loader', 'css-loader'], 26 | }, 27 | ], 28 | }, 29 | resolve: { 30 | extensions: ['.ts', '.js', '.tsx', '.jsx'], 31 | }, 32 | output: { 33 | publicPath: '/dist/', 34 | path: path.resolve(__dirname, 'dist'), 35 | filename: 'bundle.js', 36 | }, 37 | devServer: { 38 | publicPath: '/dist/', 39 | hot: true, 40 | }, 41 | }; 42 | --------------------------------------------------------------------------------