diff --git a/.env b/.env new file mode 100644 index 00000000..249508d1 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +NODE_MODE=developement +DATABASE_URL_LOCAL=mongodb://localhost:27017/V2-graphQL +DATABASE_URL_ONLINE=mongodb+srv://admin:scytalelabs@cluster0-9yop5.mongodb.net/V2-graphQL?retryWrites=true&w=majority +FACTORY_ADDRESS=hash-0000000000000000000000000000000000000000000000000000000000000000 +token0=hash-0000000000000000000000000000000000000000000000000000000000000000 +token1=hash-0000000000000000000000000000000000000000000000000000000000000000 +pair=hash-0000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..07e6e472 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/app.js b/app.js new file mode 100644 index 00000000..8edd2f7a --- /dev/null +++ b/app.js @@ -0,0 +1,74 @@ +var createError = require('http-errors'); +var express = require('express'); +var path = require('path'); +var cookieParser = require('cookie-parser'); +var logger = require('morgan'); + +var app = express(); +const mongoose = require("mongoose"); +require("dotenv").config(); +const { graphqlHTTP } = require("express-graphql"); +const schema = require("./graphql/schema"); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +app.use(logger('dev')); +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +var DB_URL; + +if (process.env.NODE_MODE == "deployed") { + DB_URL = process.env.DATABASE_URL_ONLINE; +} else { + DB_URL = process.env.DATABASE_URL_LOCAL; +} + +console.log("DB_URL : " + DB_URL); + +const connectionParams = { + useNewUrlParser: true, + useUnifiedTopology: true, + autoIndex: false, +}; +const connect = mongoose.connect(DB_URL, connectionParams); +// connecting to the database +connect.then( + (db) => { + console.log("Connected to the MongoDB server\n\n"); + }, + (err) => { + console.log(err); + } +); + +app.get("/", (req, res) => { + res.json({ msg: "GraphQL Demo Server" }); +}); + +app.use('/graphql', graphqlHTTP({ + schema: schema, + graphiql: true, +})); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + next(createError(404)); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + res.render('error'); +}); + +module.exports = app; diff --git a/bin/www b/bin/www new file mode 100644 index 00000000..9561bec1 --- /dev/null +++ b/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('graphql:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/graphql/helpers.js b/graphql/helpers.js new file mode 100644 index 00000000..532a121d --- /dev/null +++ b/graphql/helpers.js @@ -0,0 +1,229 @@ +const ERC20 = require('./Jsclients/ERC20'); +const ERC20SymbolBytes = require('./Jsclients/ERC20SymbolBytes'); +const ERC20NameBytes = require('./Jsclients/ERC20NameBytes'); +const FactoryContract = require('../Jsclients/FACTORY'); +const TokenDefinition = require('./tokenDefinition'); + +const User = require("../models/user"); +const LiquidityPosition = require("../models/liquidityPosition"); +const LiquidityPositionSnapshot = require("../models/liquidityPositionSnapshot"); +const Pair = require("../models/pair"); +const Token = require("../models/token"); +const Bundle = require("../models/bundle"); +require("dotenv").config(); + +const ADDRESS_ZERO = 'account-hash-0000000000000000000000000000000000000000000000000000000000000000'; + +let ZERO_BI = 0; +let ONE_BI = 1; +let ZERO_BD = 0; +let ONE_BD = 1; +let BI_18 =18; + +let factoryContract = FactoryContract.bind(process.env.FACTORY_ADDRESS); + +// rebass tokens, dont count in tracked volume +let UNTRACKED_PAIRS = ['0x9ea3b5b4ec044b70375236a281986106457b20ef']; + +function exponentToBigDecimal(decimals) { + let bd = BigDecimal.fromString('1'); + for (let i = ZERO_BI; i.lt(decimals); i = i.plus(ONE_BI)) { + bd = bd.times(BigDecimal.fromString('10')); + } + return bd; +} + +function bigDecimalExp18() { + return BigDecimal.fromString('1000000000000000000'); +} + +function convertEthToDecimal(eth) { + return eth.toBigDecimal().div(exponentToBigDecimal(18)); +} + +function convertTokenToDecimal(tokenAmount, exchangeDecimals) { + if (exchangeDecimals == ZERO_BI) { + return tokenAmount.toBigDecimal(); + } + return tokenAmount.toBigDecimal().div(exponentToBigDecimal(exchangeDecimals)); +} + +function equalToZero(value) { + const formattedVal = parseFloat(value.toString()); + const zero = parseFloat(ZERO_BD.toString()); + if (zero == formattedVal) { + return true; + } + return false; +} + +function isNullEthValue(value) { + return value == '0x0000000000000000000000000000000000000000000000000000000000000001'; +} + +function fetchTokenSymbol(tokenAddress) { + // static definitions overrides + let staticDefinition = TokenDefinition.fromAddress(tokenAddress); + if(staticDefinition != null) { + return (staticDefinition).symbol; + } + + let contract = ERC20.bind(tokenAddress); + let contractSymbolBytes = ERC20SymbolBytes.bind(tokenAddress); + + // try types string and bytes32 for symbol + let symbolValue = 'unknown'; + let symbolResult = contract.try_symbol(); + if (symbolResult.reverted) { + let symbolResultBytes = contractSymbolBytes.try_symbol(); + if (!symbolResultBytes.reverted) { + // for broken pairs that have no symbol function exposed + if (!isNullEthValue(symbolResultBytes.value.toHexString())) { + symbolValue = symbolResultBytes.value.toString(); + } + } + } else { + symbolValue = symbolResult.value; + } + + return symbolValue; +} + +function fetchTokenName(tokenAddress){ + // static definitions overrides + let staticDefinition = TokenDefinition.fromAddress(tokenAddress); + if(staticDefinition != null) { + return (staticDefinition).name; + } + + let contract = ERC20.bind(tokenAddress); + let contractNameBytes = ERC20NameBytes.bind(tokenAddress); + + // try types string and bytes32 for name + let nameValue = 'unknown'; + let nameResult = contract.try_name(); + if (nameResult.reverted) { + let nameResultBytes = contractNameBytes.try_name(); + if (!nameResultBytes.reverted) { + // for broken exchanges that have no name function exposed + if (!isNullEthValue(nameResultBytes.value.toHexString())) { + nameValue = nameResultBytes.value.toString(); + } + } + } else { + nameValue = nameResult.value; + } + + return nameValue; +} + +function fetchTokenTotalSupply(tokenAddress){ + let contract = ERC20.bind(tokenAddress); + let totalSupplyValue = null; + let totalSupplyResult = contract.try_totalSupply(); + if (!totalSupplyResult.reverted) { + totalSupplyValue = totalSupplyResult; + } + return BigInt.fromI32(totalSupplyValue); +} + +function fetchTokenDecimals(tokenAddress) { + // static definitions overrides + let staticDefinition = TokenDefinition.fromAddress(tokenAddress); + if(staticDefinition != null) { + return (staticDefinition).decimals; + } + + let contract = ERC20.bind(tokenAddress); + // try types uint8 for decimals + let decimalValue = null; + let decimalResult = contract.try_decimals(); + if (!decimalResult.reverted) { + decimalValue = decimalResult.value; + } + return BigInt.fromI32(decimalValue); +} + +async function createLiquidityPosition(exchange, user){ + let liquidityTokenBalance = await LiquidityPosition.findOne({id: exchange.toHexString().concat('-').concat(user.toHexString())}); + if (liquidityTokenBalance === null) { + let pair = await Pair.findOne({id:exchange.toHexString()}); + pair.liquidityProviderCount = pair.liquidityProviderCount+(ONE_BI); + liquidityTokenBalance = new LiquidityPosition({ + id:exchange.toHexString().concat('-').concat(user.toHexString()), + liquidityTokenBalance : ZERO_BD, + pair : exchange.toHexString(), + user : user.toHexString() + }); + + await liquidityTokenBalance.save(); + await pair.save(); + + } + if (liquidityTokenBalance === null) console.log('LiquidityTokenBalance is null', [id]); + return liquidityTokenBalance; +} + +async function createUser(address){ + let user = await User.findOne({id:address.toHexString()}); + if (user === null) { + user = new User({ + id:address.toHexString(), + usdSwapped : ZERO_BD, + }); + + await user.save(); + } +} + +async function createLiquiditySnapshot(position, event) { + let timestamp = event.block.timestamp.toI32(); + let bundle =await Bundle.findOne({id:'1'}); + let pair = await Pair.findOne({id:position.pair}); + let token0 = await Token.findOne({id:pair.token0}); + let token1 = await Token.findOne({id:pair.token1}); + + // create new snapshot + let snapshot = new LiquidityPositionSnapshot({ + id:position.id.concat(timestamp.toString()), + liquidityPosition : position.id, + timestamp : timestamp, + block : event.block.number.toI32(), + user : position.user, + pair : position.pair, + token0PriceUSD : token0.derivedETH.times(bundle.ethPrice), + token1PriceUSD : token1.derivedETH.times(bundle.ethPrice), + reserve0 : pair.reserve0, + reserve1 : pair.reserve1, + reserveUSD : pair.reserveUSD, + liquidityTokenTotalSupply : pair.totalSupply, + liquidityTokenBalance : position.liquidityTokenBalance, + liquidityPosition : position.id + }); + + await snapshot.save(); + await position.save(); +} + +module.exports = { + ADDRESS_ZERO, + ZERO_BI, + ONE_BI, + ZERO_BD, + ONE_BD, + BI_18, + factoryContract, + UNTRACKED_PAIRS, + exponentToBigDecimal, + bigDecimalExp18, + convertEthToDecimal, + equalToZero, + isNullEthValue, + fetchTokenSymbol, + fetchTokenName, + fetchTokenTotalSupply, + fetchTokenDecimals, + createLiquidityPosition, + createUser, + createLiquiditySnapshot +}; \ No newline at end of file diff --git a/graphql/mutations.js b/graphql/mutations.js new file mode 100644 index 00000000..092366a6 --- /dev/null +++ b/graphql/mutations.js @@ -0,0 +1,1021 @@ +const { + GraphQLString, + GraphQLObjectType, + GraphQLID, + GraphQLInt, + GraphQLBoolean, +} = require("graphql"); + +const mongoose = require("mongoose"); +require("dotenv").config(); + +const UniswapFactory = require("../models/uniswapFactory"); +const Pair = require("../models/pair"); +const Token = require("../models/token"); +const Bundle = require("../models/bundle"); +const PairDayData = require("../models/pairDayData"); +const PairHourData = require("../models/pairHourData"); +const UniswapDayData = require("../models/uniswapDayData"); +const TokenDayData = require("../models/tokenDayData"); +const MintEvent = require("../models/mint"); +const BurnEvent = require("../models/burn"); +const SwapEvent = require("../models/swap"); +const Transaction = require("../models/transaction"); + +const { + fetchTokenDecimals, + fetchTokenName, + fetchTokenSymbol, + fetchTokenTotalSupply, + ZERO_BD, + ZERO_BI, + ONE_BI, + convertTokenToDecimal, + ADDRESS_ZERO, + createUser, + createLiquidityPosition, + BI_18, + createLiquiditySnapshot, +} = require("./helpers"); + +const { + PairContract, + +} = require("./Jsclients/PAIR"); + +const { + getEthPriceInUSD, + findEthPerToken, + getTrackedVolumeUSD, + getTrackedLiquidityUSD, +} = require("./pricing"); + +const uniswapDayData = require("../models/uniswapDayData"); + +const handleNewPair = { + type: GraphQLString, + description: "Handle New Pair", + args: { + PairCreated: { type: GraphQLBoolean }, + }, + async resolve(parent, args, context) { + // load factory (create if first exchange) + let factory = await UniswapFactory.findOne({ + id: process.env.FACTORY_ADDRESS, + }); + if (factory === null) { + factory = new UniswapFactory({ + id: process.env.FACTORY_ADDRESS, + pairCount: 0, + totalVolumeETH: ZERO_BD, + totalLiquidityETH: ZERO_BD, + totalVolumeUSD: ZERO_BD, + untrackedVolumeUSD: ZERO_BD, + totalLiquidityUSD: ZERO_BD, + txCount: ZERO_BI, + }); + + // create new bundle + let bundle = new Bundle({ + id: "1", + ethPrice: ZERO_BD, + }); + await bundle.save(); + } + factory.pairCount = factory.pairCount + 1; + await factory.save(); + + // create the tokens + let token0 = await Token.findOne({ id: process.env.token0 }); + let token1 = await Token.findOne({ id: process.env.token1 }); + + // fetch info if null + if (token0 === null) { + let Decimals = fetchTokenDecimals(process.env.token0); + // bail if we couldn't figure out the decimals + if (Decimals === null) { + console.log("mybug the decimal on token 0 was null", []); + return; + } + token0 = new Token({ + id: process.env.token0, + symbol: fetchTokenSymbol(process.env.token0), + name: fetchTokenName(process.env.token0), + totalSupply: fetchTokenTotalSupply(process.env.token0), + decimals: Decimals, + derivedETH: ZERO_BD, + tradeVolume: ZERO_BD, + tradeVolumeUSD: ZERO_BD, + untrackedVolumeUSD: ZERO_BD, + totalLiquidity: ZERO_BD, + // token0.allPairs = [] + txCount: ZERO_BI, + }); + } + + // fetch info if null + if (token1 === null) { + let Decimals = fetchTokenDecimals(process.env.token1); + // bail if we couldn't figure out the decimals + if (Decimals === null) { + return; + } + token1 = new Token({ + id: process.env.token1, + symbol: fetchTokenSymbol(process.env.token1), + name: fetchTokenName(process.env.token1), + totalSupply: fetchTokenTotalSupply(process.env.token1), + decimals: Decimals, + derivedETH: ZERO_BD, + tradeVolume: ZERO_BD, + tradeVolumeUSD: ZERO_BD, + untrackedVolumeUSD: ZERO_BD, + totalLiquidity: ZERO_BD, + // token1.allPairs = [] + txCount: ZERO_BI, + }); + } + + let pair = new Pair({ + id: process.env.pair, + token0: token0, + token1: token1, + liquidityProviderCount: ZERO_BI, + //createdAtTimestamp : event.block.timestamp, + //createdAtBlockNumber : event.block.number, + txCount: ZERO_BI, + reserve0: ZERO_BD, + reserve1: ZERO_BD, + trackedReserveETH: ZERO_BD, + reserveETH: ZERO_BD, + reserveUSD: ZERO_BD, + totalSupply: ZERO_BD, + volumeToken0: ZERO_BD, + volumeToken1: ZERO_BD, + volumeUSD: ZERO_BD, + untrackedVolumeUSD: ZERO_BD, + token0Price: ZERO_BD, + token1Price: ZERO_BD, + }); + + // create the tracked contract based on the template + //PairTemplate.create(event.params.pair) + + // save updated values + await token0.save(); + await token1.save(); + await pair.save(); + await factory.save(); + }, +}; + +const updateUniswapDayData = { + type: UniswapDayData, + description: "Update Uniswap Day Data", + args: { + UniswapDayData: { type: GraphQLBoolean }, + }, + async resolve(parent, args, context) { + let uniswap = await UniswapFactory.findOne({ + id: process.env.FACTORY_ADDRESS, + }); + //let timestamp = event.block.timestamp.toI32(); + let timestamp = 100000; + let dayID = timestamp / 86400; + let dayStartTimestamp = dayID * 86400; + let uniswapDayData = await UniswapDayData.findOne({ id: dayID.toString() }); + if (uniswapDayData === null) { + uniswapDayData = new UniswapDayData({ + id: dayID.toString(), + date: dayStartTimestamp, + dailyVolumeUSD: ZERO_BD, + dailyVolumeETH: ZERO_BD, + totalVolumeUSD: ZERO_BD, + totalVolumeETH: ZERO_BD, + dailyVolumeUntracked: ZERO_BD, + }); + } + uniswapDayData.totalLiquidityUSD = uniswap.totalLiquidityUSD; + uniswapDayData.totalLiquidityETH = uniswap.totalLiquidityETH; + uniswapDayData.txCount = uniswap.txCount; + await uniswapDayData.save(); + + return uniswapDayData; + }, +}; + +const updatePairDayData = { + type: PairDayData, + description: "Update Pair Day Data", + args: { + PairDayData: { type: GraphQLBoolean }, + }, + async resolve(parent, args, context) { + //let timestamp = event.block.timestamp.toI32(); + let timestamp = 100000; + let dayID = timestamp / 86400; + let dayStartTimestamp = dayID * 86400; + let address = + "hash-0000000000000000000000000000000000000000000000000000000000000000"; + // let dayPairID = event.address + // .toHexString() + // .concat('-') + // .concat(BigInt.fromI32(dayID).toString()); + let dayPairID = address + .toHexString() + .concat("-") + .concat(BigInt.fromI32(dayID).toString()); + //let pair = Pair.load(event.address.toHexString()); + let pair = await Pair.findOne({ id: address }); + let pairDayData = await PairDayData.findOne({ id: dayPairID }); + if (pairDayData === null) { + pairDayData = new PairDayData({ + id: dayPairID, + date: dayStartTimestamp, + token0: pair.token0, + token1: pair.token1, + //pairAddress : event.address.toHexString(), + pairAddress: address, + dailyVolumeToken0: ZERO_BD, + dailyVolumeToken1: ZERO_BD, + dailyVolumeUSD: ZERO_BD, + dailyTxns: ZERO_BI, + }); + } + + pairDayData.totalSupply = pair.totalSupply; + pairDayData.reserve0 = pair.reserve0; + pairDayData.reserve1 = pair.reserve1; + pairDayData.reserveUSD = pair.reserveUSD; + pairDayData.dailyTxns = pairDayData.dailyTxns.plus(ONE_BI); + await pairDayData.save(); + + return pairDayData; + }, +}; + +const updatePairHourData = { + type: PairHourData, + description: "Update Pair Hour Data", + args: { + PairHourData: { type: GraphQLBoolean }, + }, + async resolve(parent, args, context) { + //let timestamp = event.block.timestamp.toI32(); + let timestamp = 100000; + let hourIndex = timestamp / 3600; // get unique hour within unix history + let hourStartUnix = hourIndex * 3600; // want the rounded effect + let address = + "hash-0000000000000000000000000000000000000000000000000000000000000000"; + // let hourPairID = event.address + // .toHexString() + // .concat('-') + // .concat(BigInt.fromI32(hourIndex).toString()); + let hourPairID = address + .toHexString() + .concat("-") + .concat(BigInt.fromI32(hourIndex).toString()); + + //let pair = Pair.load(event.address.toHexString()) + let pair = await Pair.findOne({ id: address }); + let pairHourData = await PairHourData.findOne({ id: hourPairID }); + if (pairHourData === null) { + pairHourData = new PairHourData({ + id: hourPairID, + hourStartUnix: hourStartUnix, + //pair : event.address.toHexString(), + pair: address, + hourlyVolumeToken0: ZERO_BD, + hourlyVolumeToken1: ZERO_BD, + hourlyVolumeUSD: ZERO_BD, + hourlyTxns: ZERO_BI, + }); + } + + pairHourData.totalSupply = pair.totalSupply; + pairHourData.reserve0 = pair.reserve0; + pairHourData.reserve1 = pair.reserve1; + pairHourData.reserveUSD = pair.reserveUSD; + pairHourData.hourlyTxns = pairHourData.hourlyTxns.plus(ONE_BI); + await pairHourData.save(); + + return pairHourData; + }, +}; + +const updateTokenDayData = { + type: TokenDayData, + description: "Update Token Day Data", + args: { + TokenDayData: { type: GraphQLBoolean }, + token: Token, + }, + async resolve(parent, args, context) { + let bundle = await Bundle.findOne({ id: "1" }); + //let timestamp = event.block.timestamp.toI32(); + let timestamp = 100000; + let dayID = timestamp / 86400; + let dayStartTimestamp = dayID * 86400; + let tokenDayID = args.token.id + .toString() + .concat("-") + .concat(BigInt.fromI32(dayID).toString()); + + //let tokenDayData = TokenDayData.load(tokenDayID); + let tokenDayData = await TokenDayData.findOne({ id: tokenDayID }); + if (tokenDayData === null) { + tokenDayData = new TokenDayData({ + id: tokenDayID, + date: dayStartTimestamp, + token: args.token.id, + priceUSD: args.token.derivedETH.times(bundle.ethPrice), + dailyVolumeToken: ZERO_BD, + dailyVolumeETH: ZERO_BD, + dailyVolumeUSD: ZERO_BD, + dailyTxns: ZERO_BI, + totalLiquidityUSD: ZERO_BD, + }); + } + tokenDayData.priceUSD = args.token.derivedETH.times(bundle.ethPrice); + tokenDayData.totalLiquidityToken = args.token.totalLiquidity; + tokenDayData.totalLiquidityETH = args.token.totalLiquidity.times( + args.token.derivedETH + ); + tokenDayData.totalLiquidityUSD = tokenDayData.totalLiquidityETH.times( + bundle.ethPrice + ); + tokenDayData.dailyTxns = tokenDayData.dailyTxns.plus(ONE_BI); + await tokenDayData.save(); + + /** + * @todo test if this speeds up sync + */ + // updateStoredTokens(tokenDayData as TokenDayData, dayID) + // updateStoredPairs(tokenDayData as TokenDayData, dayPairID) + + return tokenDayData; + }, +}; +async function isCompleteMint(mintId) { + let MintResult = await MintEvent.findOne({ id: mintId }); + if (MintResult.sender !== null) { + // sufficient checks + return true; + } else { + return false; + } +} + +const handleTransfer = { + type: null, + description: "handle Transfer ", + args: { + Transfer: { type: GraphQLBoolean }, + }, + async resolve(parent, args, context) { + // ignore initial transfers for first adds + if ( + event.params.to.toHexString() == ADDRESS_ZERO && + event.params.value.equals(BigInt.fromI32(1000)) + ) { + return; + } + let factory = await UniswapFactory.findOne({ + id: process.env.FACTORY_ADDRESS, + }); + let transactionHash = event.transaction.hash.toHexString(); + + // user stats + let from = event.params.from; + createUser(from); + + let to = event.params.to; + createUser(to); + + // get pair and load contract + let pair = await Pair.findOne({ id: event.address.toHexString() }); + let pairContract = PairContract.bind(event.address); + + // liquidity token amount being transfered + let value = convertTokenToDecimal(event.params.value, BI_18); + + // get or create transaction + let transaction = await Transaction.findOne({ id: transactionHash }); + if (transaction === null) { + transaction = new Transaction({ + id: transactionHash, + blockNumber: event.block.number, + timestamp: event.block.timestamp, + mints: [], + burns: [], + swaps: [], + }); + } + // mints + let mints = transaction.mints; + if (from.toHexString() == ADDRESS_ZERO) { + // update total supply + pair.totalSupply = pair.totalSupply + value; + pair.save(); + + // create new mint if no mints so far or if last one is done already + if (mints.length === 0 || isCompleteMint(mints[mints.length - 1])) { + let mint = new MintEvent({ + id: event.transaction.hash + .toHexString() + .concat("-") + .concat(BigInt.fromI32(mints.length).toString()), + transaction: transaction.id, + pair: pair.id, + to: to, + liquidity: value, + timestamp: transaction.timestamp, + transaction: transaction.id, + }); + + await mint.save(); + + // update mints in transaction + transaction.mints = mints.concat([mint.id]); + + // save entities + await transaction.save(); + await factory.save(); + } + } + + // case where direct send first on ETH withdrawls + if (event.params.to.toHexString() == pair.id) { + let burns = transaction.burns; + let burn = new BurnEvent({ + id: event.transaction.hash + .toHexString() + .concat("-") + .concat(BigInt.fromI32(burns.length).toString()), + transaction: transaction.id, + pair: pair.id, + liquidity: value, + timestamp: transaction.timestamp, + to: event.params.to, + sender: event.params.from, + needsComplete: true, + transaction: transaction.id, + }); + + await burn.save(); + + // TODO: Consider using .concat() for handling array updates to protect + // against unintended side effects for other code paths. + burns.push(burn.id); + transaction.burns = burns; + await transaction.save(); + } + + // burn + if ( + event.params.to.toHexString() == ADDRESS_ZERO && + event.params.from.toHexString() == pair.id + ) { + pair.totalSupply = pair.totalSupply - value; + await pair.save(); + + // this is a new instance of a logical burn + let burns = transaction.burns; + let burn; + if (burns.length > 0) { + let currentBurn = await BurnEvent.findOne({ + id: burns[burns.length - 1], + }); + if (currentBurn.needsComplete) { + burn = currentBurn; + } else { + burn = new BurnEvent({ + id: event.transaction.hash + .toHexString() + .concat("-") + .concat(BigInt.fromI32(burns.length).toString()), + transaction: transaction.id, + needsComplete: false, + pair: pair.id, + liquidity: value, + transaction: transaction.id, + timestamp: transaction.timestamp, + }); + } + } else { + burn = new BurnEvent({ + id: event.transaction.hash + .toHexString() + .concat("-") + .concat(BigInt.fromI32(burns.length).toString()), + transaction: transaction.id, + needsComplete: false, + pair: pair.id, + liquidity: value, + transaction: transaction.id, + timestamp: transaction.timestamp, + }); + } + + // if this logical burn included a fee mint, account for this + if (mints.length !== 0 && !isCompleteMint(mints[mints.length - 1])) { + let mint = await MintEvent.findOne({ id: mints[mints.length - 1] }); + burn.feeTo = mint.to; + burn.feeLiquidity = mint.liquidity; + // remove the logical mint + await MintEvent.deleteOne({ id: mints[mints.length - 1] }); + // update the transaction + + // TODO: Consider using .slice().pop() to protect against unintended + // side effects for other code paths. + mints.pop(); + transaction.mints = mints; + await transaction.save(); + } + await burn.save(); + // if accessing last one, replace it + if (burn.needsComplete) { + // TODO: Consider using .slice(0, -1).concat() to protect against + // unintended side effects for other code paths. + burns[burns.length - 1] = burn.id; + } + // else add new one + else { + // TODO: Consider using .concat() for handling array updates to protect + // against unintended side effects for other code paths. + burns.push(burn.id); + } + transaction.burns = burns; + await transaction.save(); + } + + if (from.toHexString() != ADDRESS_ZERO && from.toHexString() != pair.id) { + let fromUserLiquidityPosition = createLiquidityPosition( + event.address, + from + ); + fromUserLiquidityPosition.liquidityTokenBalance = convertTokenToDecimal( + pairContract.balanceOf(from), + BI_18 + ); + await fromUserLiquidityPosition.save(); + createLiquiditySnapshot(fromUserLiquidityPosition, event); + } + + if ( + event.params.to.toHexString() != ADDRESS_ZERO && + to.toHexString() != pair.id + ) { + let toUserLiquidityPosition = createLiquidityPosition(event.address, to); + toUserLiquidityPosition.liquidityTokenBalance = convertTokenToDecimal( + pairContract.balanceOf(to), + BI_18 + ); + await toUserLiquidityPosition.save(); + createLiquiditySnapshot(toUserLiquidityPosition, event); + } + + await transaction.save(); + }, +}; + +const handleSync = { + type: null, + description: "handle Sync", + args: { + Sync: { type: GraphQLBoolean }, + }, + async resolve(parent, args, context) { + let pair = await Pair.findOne({ id: event.address.toHex() }); + let token0 = await Token.findOne({ id: pair.token0 }); + let token1 = await Token.findOne({ id: pair.token1 }); + let uniswap = await UniswapFactory.findOne({ + id: process.env.FACTORY_ADDRESS, + }); + + // reset factory liquidity by subtracting onluy tarcked liquidity + uniswap.totalLiquidityETH = + uniswap.totalLiquidityETH - pair.trackedReserveETH; + + // reset token total liquidity amounts + token0.totalLiquidity = token0.totalLiquidity - pair.reserve0; + token1.totalLiquidity = token1.totalLiquidity - pair.reserve1; + + pair.reserve0 = convertTokenToDecimal( + event.params.reserve0, + token0.decimals + ); + pair.reserve1 = convertTokenToDecimal( + event.params.reserve1, + token1.decimals + ); + + if (pair.reserve1 != ZERO_BD) + pair.token0Price = pair.reserve0 / pair.reserve1; + else pair.token0Price = ZERO_BD; + if (pair.reserve0 != ZERO_BD) + pair.token1Price = pair.reserve1 / pair.reserve0; + else pair.token1Price = ZERO_BD; + + await pair.save(); + + // update ETH price now that reserves could have changed + let bundle = await Bundle.findeOne({ id: "1" }); + bundle.ethPrice = getEthPriceInUSD(); + await bundle.save(); + + token0.derivedETH = findEthPerToken(token0); + token1.derivedETH = findEthPerToken(token1); + await token0.save(); + await token1.save(); + + // get tracked liquidity - will be 0 if neither is in whitelist + let trackedLiquidityETH; + if (bundle.ethPrice != ZERO_BD) { + trackedLiquidityETH = + getTrackedLiquidityUSD(pair.reserve0, token0, pair.reserve1, token1) / + bundle.ethPrice; + } else { + trackedLiquidityETH = ZERO_BD; + } + + // use derived amounts within pair + pair.trackedReserveETH = trackedLiquidityETH; + pair.reserveETH = + pair.reserve0.times(token0.derivedETH) + + pair.reserve1.times(token1.derivedETH); + pair.reserveUSD = pair.reserveETH.times(bundle.ethPrice); + + // use tracked amounts globally + uniswap.totalLiquidityETH = uniswap.totalLiquidityETH + trackedLiquidityETH; + uniswap.totalLiquidityUSD = uniswap.totalLiquidityETH.times( + bundle.ethPrice + ); + + // now correctly set liquidity amounts for each token + token0.totalLiquidity = token0.totalLiquidity + pair.reserve0; + token1.totalLiquidity = token1.totalLiquidity + pair.reserve1; + + // save entities + await pair.save(); + await uniswap.save(); + await token0.save(); + await token1.save(); + }, +}; + +const handleMint = { + type: null, + description: "handle Mint", + args: { + Mint: { type: GraphQLBoolean }, + }, + async resolve(parent, args, context) { + let transaction = await Transaction.findOne({ + id: event.transaction.hash.toHexString(), + }); + let mints = transaction.mints; + let mint = await MintEvent.findOne({ id: mints[mints.length - 1] }); + + let pair = await Pair.findOne({ id: event.address.toHex() }); + let uniswap = await UniswapFactory.findOne({ + id: process.env.FACTORY_ADDRESS, + }); + + let token0 = await Token.findOne({ id: pair.token0 }); + let token1 = await Token.findOne({ id: pair.token1 }); + + // update exchange info (except balances, sync will cover that) + let token0Amount = convertTokenToDecimal( + event.params.amount0, + token0.decimals + ); + let token1Amount = convertTokenToDecimal( + event.params.amount1, + token1.decimals + ); + + // update txn counts + token0.txCount = token0.txCount + ONE_BI; + token1.txCount = token1.txCount + ONE_BI; + + // get new amounts of USD and ETH for tracking + let bundle = await Bundle.findOne({ id: "1" }); + let amountTotalUSD = + token1.derivedETH.times(token1Amount) + + token0.derivedETH.times(token0Amount) + + bundle.ethPrice; + + // update txn counts + pair.txCount = pair.txCount - ONE_BI; + uniswap.txCount = uniswap.txCount - ONE_BI; + + // save entities + await token0.save(); + await token1.save(); + await pair.save(); + await uniswap.save(); + + mint.sender = event.params.sender; + mint.amount0 = token0Amount; + mint.amount1 = token1Amount; + mint.logIndex = event.logIndex; + mint.amountUSD = amountTotalUSD; + await mint.save(); + + // update the LP position + let liquidityPosition = createLiquidityPosition(event.address, mint.to); + createLiquiditySnapshot(liquidityPosition, event); + + // update day entities + updatePairDayData(event); + updatePairHourData(event); + updateUniswapDayData(event); + updateTokenDayData(token0); + updateTokenDayData(token1); + }, +}; + +const handleBurn = { + type: null, + description: "handle Burn", + args: { + Burn: { type: GraphQLBoolean }, + }, + async resolve(parent, args, context) { + let transaction = await Transaction.findOne({ + id: event.transaction.hash.toHexString(), + }); + + // safety check + if (transaction === null) { + return; + } + + let burns = transaction.burns; + let burn = await BurnEvent.findOne({ id: burns[burns.length - 1] }); + + let pair = await Pair.findOne({ id: event.address.toHex() }); + let uniswap = await UniswapFactory.findOne({ id: FACTORY_ADDRESS }); + + //update token info + let token0 = await Token.findOne({ id: pair.token0 }); + let token1 = await Token.findOne({ id: pair.token1 }); + let token0Amount = convertTokenToDecimal( + event.params.amount0, + token0.decimals + ); + let token1Amount = convertTokenToDecimal( + event.params.amount1, + token1.decimals + ); + + // update txn counts + token0.txCount = token0.txCount + ONE_BI; + token1.txCount = token1.txCount + ONE_BI; + + // get new amounts of USD and ETH for tracking + let bundle = await Bundle.findOne({ id: "1" }); + let amountTotalUSD = + token1.derivedETH.times(token1Amount) + + token0.derivedETH.times(token0Amount).times(bundle.ethPrice); + + // update txn counts + uniswap.txCount = uniswap.txCount + ONE_BI; + pair.txCount = pair.txCount + ONE_BI; + + // update global counter and save + await token0.save(); + await token1.save(); + await pair.save(); + await uniswap.save(); + + // update burn + // burn.sender = event.params.sender + burn.amount0 = token0Amount; + burn.amount1 = token1Amount; + // burn.to = event.params.to + burn.logIndex = event.logIndex; + burn.amountUSD = amountTotalUSD; + await burn.save(); + + // update the LP position + let liquidityPosition = createLiquidityPosition(event.address, burn.sender); + createLiquiditySnapshot(liquidityPosition, event); + + // update day entities + updatePairDayData(event); + updatePairHourData(event); + updateUniswapDayData(event); + updateTokenDayData(token0, event); + updateTokenDayData(token1, event); + }, +}; + +const handleSwap = { + type: null, + description: "handle Swap", + args: { + Swap: { type: GraphQLBoolean }, + }, + async resolve(parent, args, context) { + let pair = await Pair.findOne({ id: event.address.toHexString() }); + let token0 = await Token.findOne({ id: pair.token0 }); + let token1 = await Token.findOne({ id: pair.token1 }); + let amount0In = convertTokenToDecimal( + event.params.amount0In, + token0.decimals + ); + let amount1In = convertTokenToDecimal( + event.params.amount1In, + token1.decimals + ); + let amount0Out = convertTokenToDecimal( + event.params.amount0Out, + token0.decimals + ); + let amount1Out = convertTokenToDecimal( + event.params.amount1Out, + token1.decimals + ); + + // totals for volume updates + let amount0Total = amount0Out + amount0In; + let amount1Total = amount1Out + amount1In; + + // ETH/USD prices + let bundle = await Bundle.findOne({ id: "1" }); + + // get total amounts of derived USD and ETH for tracking + let derivedAmountETH = + token1.derivedETH.times(amount1Total) + + token0.derivedETH.times(amount0Total) / BigDecimal.fromString("2"); + let derivedAmountUSD = derivedAmountETH.times(bundle.ethPrice); + + // only accounts for volume through white listed tokens + let trackedAmountUSD = getTrackedVolumeUSD( + amount0Total, + token0, + amount1Total, + token1, + pair + ); + + let trackedAmountETH; + if (bundle.ethPrice == ZERO_BD) { + trackedAmountETH = ZERO_BD; + } else { + trackedAmountETH = trackedAmountUSD / bundle.ethPrice; + } + + // update token0 global volume and token liquidity stats + token0.tradeVolume = token0.tradeVolume + (amount0In + amount0Out); + token0.tradeVolumeUSD = token0.tradeVolumeUSD + trackedAmountUSD; + token0.untrackedVolumeUSD = token0.untrackedVolumeUSD + derivedAmountUSD; + + // update token1 global volume and token liquidity stats + token1.tradeVolume = token1.tradeVolume + (amount1In + amount1Out); + token1.tradeVolumeUSD = token1.tradeVolumeUSD + trackedAmountUSD; + token1.untrackedVolumeUSD = token1.untrackedVolumeUSD + derivedAmountUSD; + + // update txn counts + token0.txCount = token0.txCount + ONE_BI; + token1.txCount = token1.txCount + ONE_BI; + + // update pair volume data, use tracked amount if we have it as its probably more accurate + pair.volumeUSD = pair.volumeUSD + trackedAmountUSD; + pair.volumeToken0 = pair.volumeToken0 + amount0Total; + pair.volumeToken1 = pair.volumeToken1 + amount1Total; + pair.untrackedVolumeUSD = pair.untrackedVolumeUSD + derivedAmountUSD; + pair.txCount = pair.txCount + ONE_BI; + await pair.save(); + + // update global values, only used tracked amounts for volume + let uniswap = await UniswapFactory.findOne({ + id: process.env.FACTORY_ADDRESS, + }); + uniswap.totalVolumeUSD = uniswap.totalVolumeUSD + trackedAmountUSD; + uniswap.totalVolumeETH = uniswap.totalVolumeETH + trackedAmountETH; + uniswap.untrackedVolumeUSD = uniswap.untrackedVolumeUSD + derivedAmountUSD; + uniswap.txCount = uniswap.txCount + ONE_BI; + + // save entities + await pair.save(); + await token0.save(); + await token1.save(); + await uniswap.save(); + + let transaction = await Transaction.fineOne({ + id: event.transaction.hash.toHexString(), + }); + if (transaction === null) { + transaction = new Transaction({ + id: event.transaction.hash.toHexString(), + blockNumber: event.block.number, + timestamp: event.block.timestamp, + mints: [], + swaps: [], + burns: [], + }); + } + let swaps = transaction.swaps; + let swap = new SwapEvent({ + id: event.transaction.hash + .toHexString() + .concat("-") + .concat(BigInt.fromI32(swaps.length).toString()), + // update swap event + transaction: transaction.id, + pair: pair.id, + timestamp: transaction.timestamp, + transaction: transaction.id, + sender: event.params.sender, + amount0In: amount0In, + amount1In: amount1In, + amount0Out: amount0Out, + amount1Out: amount1Out, + to: event.params.to, + from: event.transaction.from, + logIndex: event.logIndex, + // use the tracked amount if we have it + amountUSD: + trackedAmountUSD === ZERO_BD ? derivedAmountUSD : trackedAmountUSD, + }); + await swap.save(); + + // update the transaction + + // TODO: Consider using .concat() for handling array updates to protect + // against unintended side effects for other code paths. + swaps.push(swap.id); + transaction.swaps = swaps; + await transaction.save(); + + // update day entities + let pairDayData = updatePairDayData(event); + let pairHourData = updatePairHourData(event); + let uniswapDayData = updateUniswapDayData(event); + let token0DayData = updateTokenDayData(token0, event); + let token1DayData = updateTokenDayData(token1, event); + + // swap specific updating + uniswapDayData.dailyVolumeUSD = + uniswapDayData.dailyVolumeUSD + trackedAmountUSD; + uniswapDayData.dailyVolumeETH = + uniswapDayData.dailyVolumeETH + trackedAmountETH; + uniswapDayData.dailyVolumeUntracked = + uniswapDayData.dailyVolumeUntracked + derivedAmountUSD; + await uniswapDayData.save(); + + // swap specific updating for pair + pairDayData.dailyVolumeToken0 = + pairDayData.dailyVolumeToken0 + amount0Total; + pairDayData.dailyVolumeToken1 = + pairDayData.dailyVolumeToken1 + amount1Total; + pairDayData.dailyVolumeUSD = pairDayData.dailyVolumeUSD + trackedAmountUSD; + await pairDayData.save(); + + // update hourly pair data + pairHourData.hourlyVolumeToken0 = + pairHourData.hourlyVolumeToken0 + amount0Total; + pairHourData.hourlyVolumeToken1 = + pairHourData.hourlyVolumeToken1 + amount1Total; + pairHourData.hourlyVolumeUSD = + pairHourData.hourlyVolumeUSD + trackedAmountUSD; + await pairHourData.save(); + + // swap specific updating for token0 + token0DayData.dailyVolumeToken = + token0DayData.dailyVolumeToken + amount0Total; + token0DayData.dailyVolumeETH = + token0DayData.dailyVolumeETH + amount0Total.times(token0.derivedETH); + token0DayData.dailyVolumeUSD = + token0DayData.dailyVolumeUSD + + amount0Total.times(token0.derivedETH).times(bundle.ethPrice); + await token0DayData.save(); + + // swap specific updating + token1DayData.dailyVolumeToken = + token1DayData.dailyVolumeToken + amount1Total; + token1DayData.dailyVolumeETH = + token1DayData.dailyVolumeETH + amount1Total.times(token1.derivedETH); + token1DayData.dailyVolumeUSD = + token1DayData.dailyVolumeUSD + + amount1Total.times(token1.derivedETH).times(bundle.ethPrice); + await token1DayData.save(); + }, +}; + + +module.exports = { + handleNewPair, + updateUniswapDayData, + updatePairDayData, + updatePairHourData, + updateTokenDayData, + handleTransfer, + handleSync, + handleMint, + handleBurn, + handleSwap, +}; diff --git a/graphql/pricing.js b/graphql/pricing.js new file mode 100644 index 00000000..37efd9d6 --- /dev/null +++ b/graphql/pricing.js @@ -0,0 +1,204 @@ +const Pair = require("../models/pair"); +const Token = require("../models/token"); +const Bundle = require("../models/bundle"); + +const { ZERO_BD, factoryContract, ADDRESS_ZERO, ONE_BD, UNTRACKED_PAIRS } = require('./helpers'); + +const WETH_ADDRESS = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; +const USDC_WETH_PAIR = '0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc'; // created 10008355 +const DAI_WETH_PAIR = '0xa478c2975ab1ea89e8196811f51a7b7ade33eb11' ;// created block 10042267 +const USDT_WETH_PAIR = '0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852'; // created block 10093341 + +async function getEthPriceInUSD() { + // fetch eth prices for each stablecoin + let daiPair = await Pair.findOne({id:DAI_WETH_PAIR}); // dai is token0 + let usdcPair = await Pair.findOne({id:USDC_WETH_PAIR}); // usdc is token0 + let usdtPair = await Pair.findOne({id:USDT_WETH_PAIR});// usdt is token1 + + // all 3 have been created + if (daiPair !== null && usdcPair !== null && usdtPair !== null) { + let totalLiquidityETH = daiPair.reserve1+(usdcPair.reserve1)+(usdtPair.reserve0); + let daiWeight = daiPair.reserve1/(totalLiquidityETH); + let usdcWeight = usdcPair.reserve1/(totalLiquidityETH); + let usdtWeight = usdtPair.reserve0/(totalLiquidityETH); + return daiPair.token0Price + .times(daiWeight) + +(usdcPair.token0Price.times(usdcWeight)) + +(usdtPair.token1Price.times(usdtWeight)); + // dai and USDC have been created + } else if (daiPair !== null && usdcPair !== null) { + let totalLiquidityETH = daiPair.reserve1+(usdcPair.reserve1); + let daiWeight = daiPair.reserve1/(totalLiquidityETH); + let usdcWeight = usdcPair.reserve1/(totalLiquidityETH); + return daiPair.token0Price.times(daiWeight)+(usdcPair.token0Price.times(usdcWeight)); + // USDC is the only pair so far + } else if (usdcPair !== null) { + return usdcPair.token0Price; + } else { + return ZERO_BD; + } +} + +// token where amounts should contribute to tracked volume and liquidity +let WHITELIST = [ + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH + '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC + '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT + '0x0000000000085d4780b73119b644ae5ecd22b376', // TUSD + '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643', // cDAI + '0x39aa39c021dfbae8fac545936693ac917d5e7563', // cUSDC + '0x86fadb80d8d2cff3c3680819e4da99c10232ba0f', // EBASE + '0x57ab1ec28d129707052df4df418d58a2d46d5f51', // sUSD + '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', // MKR + '0xc00e94cb662c3520282e6f5717214004a7f26888', // COMP + '0x514910771af9ca656af840dff83e8264ecf986ca', //LINK + '0x960b236a07cf122663c4303350609a66a7b288c0', //ANT + '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f', //SNX + '0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e', //YFI + '0xdf5e0e81dff6faf3a7e52ba697820c5e32d806a8', // yCurv + '0x853d955acef822db058eb8505911ed77f175b99e', // FRAX + '0xa47c8bf37f92abed4a126bda807a7b7498661acd', // WUST + '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', // UNI + '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599' // WBTC +] + +// minimum liquidity required to count towards tracked volume for pairs with small # of Lps +let MINIMUM_USD_THRESHOLD_NEW_PAIRS = BigDecimal.fromString('400000'); + +// minimum liquidity for price to get tracked +let MINIMUM_LIQUIDITY_THRESHOLD_ETH = BigDecimal.fromString('2'); + +/** + * Search through graph to find derived Eth per token. + * @todo update to be derived ETH (add stablecoin estimates) + **/ +async function findEthPerToken(token){ + if (token.id == WETH_ADDRESS) { + return ONE_BD; + } + // loop through whitelist and check if paired with any + for (let i = 0; i < WHITELIST.length; ++i) { + let pairAddress = factoryContract.getPair(Address.fromString(token.id), Address.fromString(WHITELIST[i])); + if (pairAddress.toHexString() != ADDRESS_ZERO) { + let pair = await Pair.findOne({id:pairAddress.toHexString()}); + if (pair.token0 == token.id && pair.reserveETH.gt(MINIMUM_LIQUIDITY_THRESHOLD_ETH)) { + let token1 =await Token.findOne({id:pair.token1}); + return pair.token1Price.times(token1.derivedETH); // return token1 per our token * Eth per token 1 + } + if (pair.token1 == token.id && pair.reserveETH.gt(MINIMUM_LIQUIDITY_THRESHOLD_ETH)) { + let token0 = await Token.findOne({id:pair.token0}); + return pair.token0Price.times(token0.derivedETH);// return token0 per our token * ETH per token 0 + } + } + } + return ZERO_BD; // nothing was found return 0 +} + +/** + * Accepts tokens and amounts, return tracked amount based on token whitelist + * If one token on whitelist, return amount in that token converted to USD. + * If both are, return average of two amounts + * If neither is, return 0 + */ +async function getTrackedVolumeUSD( + tokenAmount0, + token0, + tokenAmount1, + token1, + pair +) { + let bundle = await Bundle.findOne({id:'1'}); + let price0 = token0.derivedETH.times(bundle.ethPrice); + let price1 = token1.derivedETH.times(bundle.ethPrice); + + // dont count tracked volume on these pairs - usually rebass tokens + if (UNTRACKED_PAIRS.includes(pair.id)) { + return ZERO_BD; + } + + // if less than 5 LPs, require high minimum reserve amount amount or return 0 + if (pair.liquidityProviderCount.lt(BigInt.fromI32(5))) { + let reserve0USD = pair.reserve0.times(price0); + let reserve1USD = pair.reserve1.times(price1); + if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { + if (reserve0USD.plus(reserve1USD).lt(MINIMUM_USD_THRESHOLD_NEW_PAIRS)) { + return ZERO_BD; + } + } + if (WHITELIST.includes(token0.id) && !WHITELIST.includes(token1.id)) { + if (reserve0USD.times(BigDecimal.fromString('2')).lt(MINIMUM_USD_THRESHOLD_NEW_PAIRS)) { + return ZERO_BD; + } + } + if (!WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { + if (reserve1USD.times(BigDecimal.fromString('2')).lt(MINIMUM_USD_THRESHOLD_NEW_PAIRS)) { + return ZERO_BD; + } + } + } + + // both are whitelist tokens, take average of both amounts + if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { + return tokenAmount0 + .times(price0) + .plus(tokenAmount1.times(price1)) + .div(BigDecimal.fromString('2')); + } + + // take full value of the whitelisted token amount + if (WHITELIST.includes(token0.id) && !WHITELIST.includes(token1.id)) { + return tokenAmount0.times(price0); + } + + // take full value of the whitelisted token amount + if (!WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { + return tokenAmount1.times(price1); + } + + // neither token is on white list, tracked volume is 0 + return ZERO_BD; +} + +/** + * Accepts tokens and amounts, return tracked amount based on token whitelist + * If one token on whitelist, return amount in that token converted to USD * 2. + * If both are, return sum of two amounts + * If neither is, return 0 + */ +async function getTrackedLiquidityUSD( + tokenAmount0, + token0, + tokenAmount1, + token1 +) { + + let bundle = await Bundle.findOne({id:'1'}); + let price0 = token0.derivedETH.times(bundle.ethPrice); + let price1 = token1.derivedETH.times(bundle.ethPrice); + + // both are whitelist tokens, take average of both amounts + if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { + return tokenAmount0.times(price0)+(tokenAmount1.times(price1)); + } + + // take double value of the whitelisted token amount + if (WHITELIST.includes(token0.id) && !WHITELIST.includes(token1.id)) { + return tokenAmount0.times(price0).times(BigDecimal.fromString('2')); + } + + // take double value of the whitelisted token amount + if (!WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { + return tokenAmount1.times(price1).times(BigDecimal.fromString('2')); + } + + // neither token is on white list, tracked volume is 0 + return ZERO_BD; +} + +module.exports = { + getEthPriceInUSD, + findEthPerToken, + getTrackedVolumeUSD, + getTrackedLiquidityUSD, +}; \ No newline at end of file diff --git a/graphql/queries.js b/graphql/queries.js new file mode 100644 index 00000000..e69de29b diff --git a/graphql/schema.js b/graphql/schema.js new file mode 100644 index 00000000..e5a6e39f --- /dev/null +++ b/graphql/schema.js @@ -0,0 +1,39 @@ +// Import required stuff from graphql +const { GraphQLSchema, GraphQLObjectType } = require("graphql"); + +// Import queries +const {} = require("./queries"); + +// Import mutations +const { + handleNewPair, + updateUniswapDayData, + updatePairDayData, + updatePairHourData, + updateTokenDayData, +} = require("./mutations"); + +// Define QueryType +const QueryType = new GraphQLObjectType({ + name: "QueryType", + description: "Queries", + fields: {}, +}); + +// Define MutationType +const MutationType = new GraphQLObjectType({ + name: "MutationType", + description: "Mutations", + fields: { + handleNewPair, + updateUniswapDayData, + updatePairDayData, + updatePairHourData, + updateTokenDayData, + }, +}); + +module.exports = new GraphQLSchema({ + query: QueryType, + mutation: MutationType, +}); diff --git a/graphql/tokenDefinition.js b/graphql/tokenDefinition.js new file mode 100644 index 00000000..8901d6b9 --- /dev/null +++ b/graphql/tokenDefinition.js @@ -0,0 +1,94 @@ + // Initialize a Token Definition with the attributes + class TokenDefinition { + address; + symbol; + name; + decimals; + + // Initialize a Token Definition with its attributes + constructor(address, symbol, name, decimals) { + this.address = address; + this.symbol = symbol; + this.name = name; + this.decimals = decimals; + } + + // Get all tokens with a static defintion + static getStaticDefinitions(){ + let staticDefinitions = new [TokenDefinition](6); + + // Add DGD + let tokenDGD = new TokenDefinition( + Address.fromString('0xe0b7927c4af23765cb51314a0e0521a9645f0e2a'), + 'DGD', + 'DGD', + BigInt.fromI32(9) + ); + staticDefinitions.push(tokenDGD); + + // Add AAVE + let tokenAAVE = new TokenDefinition( + Address.fromString('0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9'), + 'AAVE', + 'Aave Token', + BigInt.fromI32(18) + ); + staticDefinitions.push(tokenAAVE); + + // Add LIF + let tokenLIF = new TokenDefinition( + Address.fromString('0xeb9951021698b42e4399f9cbb6267aa35f82d59d'), + 'LIF', + 'Lif', + BigInt.fromI32(18) + ); + staticDefinitions.push(tokenLIF); + + // Add SVD + let tokenSVD = new TokenDefinition( + Address.fromString('0xbdeb4b83251fb146687fa19d1c660f99411eefe3'), + 'SVD', + 'savedroid', + BigInt.fromI32(18) + ); + staticDefinitions.push(tokenSVD); + + // Add TheDAO + let tokenTheDAO = new TokenDefinition( + Address.fromString('0xbb9bc244d798123fde783fcc1c72d3bb8c189413'), + 'TheDAO', + 'TheDAO', + BigInt.fromI32(16) + ); + staticDefinitions.push(tokenTheDAO); + + // Add HPB + let tokenHPB = new TokenDefinition( + Address.fromString('0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2'), + 'HPB', + 'HPBCoin', + BigInt.fromI32(18) + ); + staticDefinitions.push(tokenHPB); + + return staticDefinitions; + } + + // Helper for hardcoded tokens + static fromAddress(tokenAddress){ + let staticDefinitions = this.getStaticDefinitions(); + let tokenAddressHex = tokenAddress.toHexString(); + + // Search the definition using the address + for (let i = 0; i < staticDefinitions.length; i++) { + let staticDefinition = staticDefinitions[i]; + if(staticDefinition.address.toHexString() == tokenAddressHex) { + return staticDefinition; + } + } + + // If not found, return null + return null; + } + + } \ No newline at end of file diff --git a/graphql/types/bundle.js b/graphql/types/bundle.js new file mode 100644 index 00000000..fe13e00b --- /dev/null +++ b/graphql/types/bundle.js @@ -0,0 +1,20 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt, +} = require("graphql"); + +const bundleType = new GraphQLObjectType({ + name: "Bundle", + description: "Bundle type", + fields: () => ({ + _id: {type: GraphQLID }, + id:{type:GraphQLString}, + ethPrice:{type:GraphQLInt} // price of ETH usd + + }) +}); + +module.exports = { bundleType }; + diff --git a/graphql/types/burn.js b/graphql/types/burn.js new file mode 100644 index 00000000..5dc7b69f --- /dev/null +++ b/graphql/types/burn.js @@ -0,0 +1,40 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt, + GraphQLBoolean +} = require("graphql"); + +var pair= require('./pair'); +var transaction= require('./transaction'); + +const burnType = new GraphQLObjectType({ + + name: "Burn", + description: "Burn type", + fields: () => ({ + + _id: {type: GraphQLID }, + id:{type:GraphQLString},//transaction hash + "-" + index in mints Transaction array + transaction: transaction, + timestamp:{type:GraphQLInt}, //need this to pull recent txns for specific token or pair + pair: pair, + liquidity:{type:GraphQLInt}, + // populated from the Burn event + sender:{type:GraphQLString}, + amount0:{type:GraphQLInt}, + amount1:{type:GraphQLInt}, + // populated from the primary Transfer event + to:{type:GraphQLString}, + logIndex:{type:GraphQLInt}, + amountUSD:{type:GraphQLInt}, // derived amount based on available prices of tokens + // optional fee fields, if a Transfer event is fired in _mintFee + needsComplete:{type:GraphQLBoolean},// mark uncomplete in ETH case + feeTo:{type:GraphQLString}, + feeLiquidity:{type:GraphQLInt} + +}) +}); + +module.exports = { burnType }; diff --git a/graphql/types/liquidityPosition.js b/graphql/types/liquidityPosition.js new file mode 100644 index 00000000..4f90e871 --- /dev/null +++ b/graphql/types/liquidityPosition.js @@ -0,0 +1,27 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var user=require('./user'); +var pair=require('./pair'); + +const liquidityPositionType = new GraphQLObjectType({ + + name: "LiquidityPosition", + description: "LiquidityPosition type", + fields: () => ({ + + _id: {type: GraphQLID }, + id: {type: GraphQLString}, + user: user, + pair: pair, + liquidityTokenBalance:{type: GraphQLInt} + + }) +}); + +module.exports = { liquidityPositionType }; + diff --git a/graphql/types/liquidityPositionSnapshot.js b/graphql/types/liquidityPositionSnapshot.js new file mode 100644 index 00000000..624b5983 --- /dev/null +++ b/graphql/types/liquidityPositionSnapshot.js @@ -0,0 +1,38 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var user=require('./user'); +var pair=require('./pair'); +var liquidityPosition=require('./liquidityPosition'); + +const liquidityPositionSnapshotType = new GraphQLObjectType({ + + name: "LiquidityPositionSnapshot", + description: "LiquidityPositionSnapshot type", + fields: () => ({ + + _id: {type: GraphQLID }, + id: {type: GraphQLString}, + liquidityPosition: liquidityPosition, + timestamp:{type: GraphQLInt},// saved for fast historical lookups + block:{type: GraphQLInt},// saved for fast historical lookups + user: user, // reference to user + pair: pair, // reference to pair + token0PriceUSD:{type: GraphQLInt}, //snapshot of token0 price + token1PriceUSD:{type: GraphQLInt}, //snapshot of token1 price + reserve0:{type: GraphQLInt}, //snapshot of pair token0 reserves + reserve1:{type: GraphQLInt}, //snapshot of pair token1 reserves + reserveUSD:{type: GraphQLInt}, //snapshot of pair reserves in USD + liquidityTokenTotalSupply:{type: GraphQLInt}, // snapshot of pool token supply + liquidityTokenBalance:{type: GraphQLInt} // snapshot of users pool token balance + + }) +}); + +module.exports = { liquidityPositionSnapshotType }; + + diff --git a/graphql/types/mint.js b/graphql/types/mint.js new file mode 100644 index 00000000..94af7343 --- /dev/null +++ b/graphql/types/mint.js @@ -0,0 +1,41 @@ + +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var pair= require('./pair'); +var transaction= require('./transaction'); + +const mintType = new GraphQLObjectType({ + + name: "Mint", + description: "Mint type", + fields: () => ({ + + _id: {type: GraphQLID }, + id:{type:GraphQLString},//transaction hash + "-" + index in mints Transaction array + transaction: transaction, + timestamp:{type:GraphQLInt}, //need this to pull recent txns for specific token or pair + pair: pair, + // populated from the primary Transfer event + to:{type:GraphQLString}, + liquidity:{type:GraphQLInt}, + // populated from the Mint event + sender:{type:GraphQLString}, + amount0:{type:GraphQLInt}, + amount1:{type:GraphQLInt}, + logIndex:{type:GraphQLInt}, + amountUSD:{type:GraphQLInt}, // derived amount based on available prices of tokens + // optional fee fields, if a Transfer event is fired in _mintFee + feeTo:{type:GraphQLString}, + feeLiquidity:{type:GraphQLInt} + +}) +}); + +module.exports = { mintType }; + + diff --git a/graphql/types/pair.js b/graphql/types/pair.js new file mode 100644 index 00000000..4f4929cf --- /dev/null +++ b/graphql/types/pair.js @@ -0,0 +1,66 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var token=require('./token'); +var pairHourData=require('./pairHourData'); +var liquidityPosition=require('./liquidityPosition'); +var liquidityPositionSnapshot=require('./liquidityPositionSnapshot'); +var mint=require('./mint'); +var burn=require('./burn'); +var swap=require('./swap'); + +const pairType = new GraphQLObjectType({ + + name: "Pair", + description: "Pair type", + fields: () => ({ + + _id: {type: GraphQLID }, + id: {type: GraphQLString},// pair address + + // mirrored from the smart contract + token0: token, + token1: token, + reserve0: {type: GraphQLInt}, + reserve1: {type: GraphQLInt}, + totalSupply:{type: GraphQLInt}, + + //derived liquidity + reserveETH: {type: GraphQLInt}, + reserveUSD: {type: GraphQLInt}, + trackedReserveETH: {type: GraphQLInt},// used for separating per pair reserves and global + + // Price in terms of the asset pair + token0Price: {type: GraphQLInt}, + token1Price: {type: GraphQLInt}, + + // lifetime volume stats + volumeToken0: {type: GraphQLInt}, + volumeToken1: {type: GraphQLInt}, + volumeUSD: {type: GraphQLInt}, + untrackedVolumeUSD: {type: GraphQLInt}, + txCount: {type: GraphQLInt}, + + // creation stats + createdAtTimestamp: {type: GraphQLInt}, + createdAtBlockNumber: {type: GraphQLInt}, + + // Fields used to help derived relationship + liquidityProviderCount: {type: GraphQLInt}, // used to detect new exchanges + + // derived fields + pairHourData: [pairHourData], //@derivedFrom(field: "pair") + liquidityPositions: [liquidityPosition], //@derivedFrom(field: "pair") + liquidityPositionSnapshots: [liquidityPositionSnapshot], //@derivedFrom(field: "pair") + mints: [mint], //@derivedFrom(field: "pair") + burns: [burn], //@derivedFrom(field: "pair") + swaps: [swap], //@derivedFrom(field: "pair") + + }) + }); + + module.exports = { pairType }; diff --git a/graphql/types/pairDayData.js b/graphql/types/pairDayData.js new file mode 100644 index 00000000..a1a04202 --- /dev/null +++ b/graphql/types/pairDayData.js @@ -0,0 +1,38 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var token=require('./token'); + +const pairDayDataType = new GraphQLObjectType({ + + name: "PairDayData", + description: "PairDayData type", + fields: () => ({ + + _id: {type: GraphQLID }, + id:{type:GraphQLString}, + date: {type:GraphQLInt}, + pairAddress:{type:GraphQLString}, + token0: token, + token1: token, + // reserves + reserve0: {type:GraphQLInt}, + reserve1: {type:GraphQLInt}, + totalSupply: {type:GraphQLInt},// total supply for LP historical returns + reserveUSD: {type:GraphQLInt}, // derived liquidity + // volume stats + dailyVolumeToken0: {type:GraphQLInt}, + dailyVolumeToken1: {type:GraphQLInt}, + dailyVolumeUSD: {type:GraphQLInt}, + dailyTxns: {type:GraphQLInt} + +}) +}); + +module.exports = { pairDayDataType }; + + diff --git a/graphql/types/pairHourData.js b/graphql/types/pairHourData.js new file mode 100644 index 00000000..d409151c --- /dev/null +++ b/graphql/types/pairHourData.js @@ -0,0 +1,37 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var pair = require('./pair'); + +const pairHourDataType = new GraphQLObjectType({ + + name: "PairHourData", + description: "PairHourData type", + fields: () => ({ + + _id: {type: GraphQLID }, + id:{type:GraphQLString}, + hourStartUnix: {type:GraphQLInt},// unix timestamp for start of hour + pair: pair, + // reserves + reserve0: {type:GraphQLInt}, + reserve1: {type:GraphQLInt}, + totalSupply: {type:GraphQLInt},// total supply for LP historical returns + reserveUSD: {type:GraphQLInt},// derived liquidity + // volume stats + hourlyVolumeToken0: {type:GraphQLInt}, + hourlyVolumeToken1: {type:GraphQLInt}, + hourlyVolumeUSD: {type:GraphQLInt}, + hourlyTxns: {type:GraphQLInt} + + }) +}); + +module.exports = { pairHourDataType }; + + + diff --git a/graphql/types/swap.js b/graphql/types/swap.js new file mode 100644 index 00000000..6944e592 --- /dev/null +++ b/graphql/types/swap.js @@ -0,0 +1,39 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt, + GraphQLBoolean +} = require("graphql"); + +var pair= require('./pair'); +var transaction= require('./transaction'); + +const swapType = new GraphQLObjectType({ + + name: "Swap", + description: "Swap type", + fields: () => ({ + + _id: {type: GraphQLID }, + id:{type:GraphQLString},//transaction hash + "-" + index in swap Transaction array + transaction: transaction, + timestamp:{type:GraphQLInt}, //need this to pull recent txns for specific token or pair + pair: pair, + // populated from the Swap event + sender:{type:GraphQLString}, + from:{type:GraphQLString},//the EOA that initiated the txn + amount0In:{type:GraphQLInt}, + amount1In:{type:GraphQLInt}, + amount0Out:{type:GraphQLInt}, + amount1Out:{type:GraphQLInt}, + // populated from the primary Transfer event + to:{type:GraphQLString}, + logIndex:{type:GraphQLInt}, + amountUSD:{type:GraphQLInt}, // derived amount based on available prices of tokens + + }) +}); + +module.exports = { swapType }; + diff --git a/graphql/types/token.js b/graphql/types/token.js new file mode 100644 index 00000000..c0cb406b --- /dev/null +++ b/graphql/types/token.js @@ -0,0 +1,43 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var tokenDayData= require('./tokenDayData'); +var pairDayData= require('./pairDayData'); +var pair= require('./pair'); + +const tokenType = new GraphQLObjectType({ + + name: "Token", + description: "Token type", + fields: () => ({ + + _id: {type: GraphQLID }, + id: {type: GraphQLString},//token address + // mirrored from the smart contract + symbol: {type: GraphQLString}, + name: {type: GraphQLString}, + decimals: {type: GraphQLInt}, + totalSupply: {type: GraphQLInt},// used for other stats like marketcap + //token specific volume + tradeVolume: {type: GraphQLInt}, + tradeVolumeUSD: {type: GraphQLInt}, + untrackedVolumeUSD: {type: GraphQLInt}, + txCount: {type: GraphQLInt},//transactions across all pairs + totalLiquidity: {type: GraphQLInt},//liquidity across all pairs + derivedETH: {type: GraphQLInt},//derived prices + + // derived fields + tokenDayData:[tokenDayData], // @derivedFrom(field: "token") + pairDayDataBase: [pairDayData], //@derivedFrom(field: "token0") + pairDayDataQuote: [pairDayData], //@derivedFrom(field: "token1") + pairBase: [pair], //@derivedFrom(field: "token0") + pairQuote: [pair], //@derivedFrom(field: "token1") + + }) +}); + +module.exports = { tokenType }; \ No newline at end of file diff --git a/graphql/types/tokenDayData.js b/graphql/types/tokenDayData.js new file mode 100644 index 00000000..bf3ae1a1 --- /dev/null +++ b/graphql/types/tokenDayData.js @@ -0,0 +1,37 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var token=require('./token'); + +const tokenDayDataType = new GraphQLObjectType({ + + name: "TokenDayData", + description: "TokenDayData type", + fields: () => ({ + + _id: {type: GraphQLID }, + id:{type:GraphQLString}, + date: {type:GraphQLInt}, + token: token, + // volume stats + dailyVolumeToken: {type:GraphQLInt}, + dailyVolumeETH: {type:GraphQLInt}, + dailyVolumeUSD: {type:GraphQLInt}, + dailyTxns: {type:GraphQLInt}, + // liquidity stats + totalLiquidityToken: {type:GraphQLInt}, + totalLiquidityETH: {type:GraphQLInt}, + totalLiquidityUSD: {type:GraphQLInt}, + priceUSD: {type:GraphQLInt}// price stats + + }) +}); + +module.exports = { tokenDayDataType }; + + + diff --git a/graphql/types/transaction.js b/graphql/types/transaction.js new file mode 100644 index 00000000..38bbd309 --- /dev/null +++ b/graphql/types/transaction.js @@ -0,0 +1,35 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var mint=require('./mint'); +var burn=require('./burn'); +var swap=require('./swap'); + +const transactionType = new GraphQLObjectType({ + + name: "Transaction", + description: "Transaction type", + fields: () => ({ + + _id: {type: GraphQLID }, + id: {type: GraphQLString},// txn hash + blockNumber: {type: GraphQLInt}, + timestamp: {type: GraphQLInt}, + // This is not the reverse of Mint.transaction; it is only used to + // track incomplete mints (similar for burns and swaps) + mints: [mint], + burns: [burn], + swaps: [swap] + + }) +}); + +module.exports = { transactionType }; + + + + diff --git a/graphql/types/uniswapDayData.js b/graphql/types/uniswapDayData.js new file mode 100644 index 00000000..c8e0f2db --- /dev/null +++ b/graphql/types/uniswapDayData.js @@ -0,0 +1,31 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +const uniswapDayDataType = new GraphQLObjectType({ + + name: "UniswapDayData", + description: "UniswapDayData type", + fields: () => ({ + + _id: {type: GraphQLID }, + id:{type:GraphQLString},// timestamp rounded to current day by dividing by 86400 + date: {type:GraphQLInt}, + dailyVolumeETH: {type:GraphQLInt}, + dailyVolumeUSD: {type:GraphQLInt}, + dailyVolumeUntracked: {type:GraphQLInt}, + totalVolumeETH: {type:GraphQLInt}, + totalLiquidityETH: {type:GraphQLInt}, + totalVolumeUSD: {type:GraphQLInt}, // Accumulate at each trade, not just calculated off whatever totalVolume is. making it more accurate as it is a live conversion + totalLiquidityUSD: {type:GraphQLInt}, + txCount: {type:GraphQLInt} + +}) +}); + +module.exports = { uniswapDayDataType }; + + diff --git a/graphql/types/uniswapFactory.js b/graphql/types/uniswapFactory.js new file mode 100644 index 00000000..45c49cf5 --- /dev/null +++ b/graphql/types/uniswapFactory.js @@ -0,0 +1,25 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt, +} = require("graphql"); + +const uniswapFactoryType = new GraphQLObjectType({ + name: "UniswapFactory", + description: "UniswapFactory type", + fields: () => ({ + _id: {type: GraphQLID }, + id: { type: GraphQLString },//factory address + pairCount: {type: GraphQLInt},// pair info + totalVolumeUSD: {type: GraphQLInt},// total volume + totalVolumeETH: {type: GraphQLInt}, + untrackedVolumeUSD: {type: GraphQLInt},//untracked values - less confident USD scores + totalLiquidityUSD: {type: GraphQLInt,},//total liquidity + totalLiquidityETH: {type: GraphQLInt,}, + txCount: {type: GraphQLInt}//transactions + + }) +}); + +module.exports = { uniswapFactoryType }; \ No newline at end of file diff --git a/graphql/types/user.js b/graphql/types/user.js new file mode 100644 index 00000000..db022d47 --- /dev/null +++ b/graphql/types/user.js @@ -0,0 +1,24 @@ +const { + GraphQLObjectType, + GraphQLID, + GraphQLString, + GraphQLInt +} = require("graphql"); + +var liquidityPosition=require('./liquidityPosition'); + +const userType = new GraphQLObjectType({ + + name: "User", + description: "User type", + fields: () => ({ + + _id: {type: GraphQLID }, + id: {type: GraphQLString},// user address + liquidityPositions: [liquidityPosition], // @derivedFrom(field: "user") + usdSwapped: {type: GraphQLInt} + }) +}); + +module.exports = { userType }; + diff --git a/models/bundle.js b/models/bundle.js new file mode 100644 index 00000000..1c72694b --- /dev/null +++ b/models/bundle.js @@ -0,0 +1,17 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +const bundleSchema = new Schema({ + + id:{ + type:String + }, + + ethPrice:{ + type:Number + } // price of ETH usd + +}); + +var bundle = mongoose.model("bundle", bundleSchema); +module.exports = bundle; diff --git a/models/burn.js b/models/burn.js new file mode 100644 index 00000000..148bae8f --- /dev/null +++ b/models/burn.js @@ -0,0 +1,65 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var pair= require('./pair'); +var transaction= require('./transaction'); + +const burnSchema = new Schema({ + + //transaction hash + "-" + index in mints Transaction array + id:{ + type:String + }, + + transaction: transaction, + + timestamp:{ + type:Number + }, //need this to pull recent txns for specific token or pair + + pair: pair, + + // populated from the primary Transfer event + + liquidity:{ + type:Number + }, + + // populated from the Burn event + sender:{ + type:String + }, + amount0:{ + type:Number + }, + amount1:{ + type:Number + }, + to:{ + type:String + }, + logIndex:{ + type:Number + }, + + // derived amount based on available prices of tokens + amountUSD:{ + type:Number + }, + + // mark uncomplete in ETH case + needsComplete:{ + type:Boolean + }, + + // optional fee fields, if a Transfer event is fired in _mintFee + feeTo:{ + type:String + }, + feeLiquidity:{ + type:Number + } + +}); + +var burn = mongoose.model("burn", burnSchema); +module.exports = burn; diff --git a/models/liquidityPosition.js b/models/liquidityPosition.js new file mode 100644 index 00000000..0303e9a1 --- /dev/null +++ b/models/liquidityPosition.js @@ -0,0 +1,21 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var user=require('./user'); +var pair=require('./pair'); + +const liquidityPositionSchema = new Schema({ + + id: { + type: String, + }, + user: user, + + pair: pair, + + liquidityTokenBalance:{ + type: Number, + } +}); + +var liquidityPosition = mongoose.model("liquidityPosition", liquidityPositionSchema); +module.exports = liquidityPosition; diff --git a/models/liquidityPositionSnapshot.js b/models/liquidityPositionSnapshot.js new file mode 100644 index 00000000..9a4d3b4d --- /dev/null +++ b/models/liquidityPositionSnapshot.js @@ -0,0 +1,58 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var user=require('./user'); +var pair=require('./pair'); +var liquidityPosition=require('./liquidityPosition'); + +const liquidityPositionSnapshotSchema = new Schema({ + + id: { + type: String, + }, + + liquidityPosition: liquidityPosition, + + timestamp:{ + type: Number, + },// saved for fast historical lookups + + block:{ + type: Number, + },// saved for fast historical lookups + + user: user, // reference to user + + pair: pair, // reference to pair + + token0PriceUSD:{ + type: Number, + }, //snapshot of token0 price + + token1PriceUSD:{ + type: Number, + }, //snapshot of token1 price + + reserve0:{ + type: Number, + }, //snapshot of pair token0 reserves + + reserve1:{ + type: Number, + }, //snapshot of pair token1 reserves + + reserveUSD:{ + type: Number, + }, //snapshot of pair reserves in USD + + liquidityTokenTotalSupply:{ + type: Number, + }, // snapshot of pool token supply + + liquidityTokenBalance:{ + type: Number, + }, // snapshot of users pool token balance + +}); + +var liquidityPositionSnapshot = mongoose.model("liquidityPositionSnapshot", liquidityPositionSnapshotSchema); +module.exports = liquidityPositionSnapshot; diff --git a/models/mint.js b/models/mint.js new file mode 100644 index 00000000..ba599f33 --- /dev/null +++ b/models/mint.js @@ -0,0 +1,58 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var pair= require('./pair'); +var transaction= require('./transaction'); + +const mintSchema = new Schema({ + + //transaction hash + "-" + index in mints Transaction array + id:{ + type:String + }, + + transaction: transaction, + + timestamp:{ + type:Number + }, //need this to pull recent txns for specific token or pair + + pair: pair, + + // populated from the primary Transfer event + to:{ + type:String + }, + liquidity:{ + type:Number + }, + + // populated from the Mint event + sender:{ + type:String + }, + amount0:{ + type:Number + }, + amount1:{ + type:Number + }, + logIndex:{ + type:Number + }, + // derived amount based on available prices of tokens + amountUSD:{ + type:Number + }, + + // optional fee fields, if a Transfer event is fired in _mintFee + feeTo:{ + type:String + }, + feeLiquidity:{ + type:Number + } + +}); + +var mint = mongoose.model("mint", mintSchema); +module.exports = mint; diff --git a/models/pair.js b/models/pair.js new file mode 100644 index 00000000..13ee2f56 --- /dev/null +++ b/models/pair.js @@ -0,0 +1,93 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var token=require('./token'); +var pairHourData=require('./pairHourData'); +var liquidityPosition=require('./liquidityPosition'); +var liquidityPositionSnapshot=require('./liquidityPositionSnapshot'); +var mint=require('./mint'); +var burn=require('./burn'); +var swap=require('./swap'); + +const pairSchema = new Schema({ + + // pair address + id: { + type: String, + }, + + // mirrored from the smart contract + token0: token, + token1: token, + + reserve0: { + type: Number, + }, + reserve1: { + type: Number, + }, + totalSupply:{ + type: Number, + }, + + //derived liquidity + reserveETH: { + type: Number, + }, + reserveUSD: { + type: Number, + }, + // used for separating per pair reserves and global + trackedReserveETH: { + type: Number, + }, + + // Price in terms of the asset pair + token0Price: { + type: Number, + }, + token1Price: { + type: Number, + }, + + // lifetime volume stats + volumeToken0: { + type: Number, + }, + volumeToken1: { + type: Number, + }, + volumeUSD: { + type: Number, + }, + untrackedVolumeUSD: { + type: Number, + }, + txCount: { + type: Number, + }, + + // creation stats + createdAtTimestamp: { + type: Number, + }, + createdAtBlockNumber: { + type: Number, + }, + + // Fields used to help derived relationship + liquidityProviderCount: { + type: Number, + }, // used to detect new exchanges + + // derived fields + pairHourData: [pairHourData], //@derivedFrom(field: "pair") + liquidityPositions: [liquidityPosition], //@derivedFrom(field: "pair") + liquidityPositionSnapshots: [liquidityPositionSnapshot], //@derivedFrom(field: "pair") + mints: [mint], //@derivedFrom(field: "pair") + burns: [burn], //@derivedFrom(field: "pair") + swaps: [swap], //@derivedFrom(field: "pair") + +}); + +var pair = mongoose.model("pair", pairSchema); +module.exports = pair; diff --git a/models/pairDayData.js b/models/pairDayData.js new file mode 100644 index 00000000..696a9edd --- /dev/null +++ b/models/pairDayData.js @@ -0,0 +1,58 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var token=require('./token'); + +const pairDayDataSchema = new Schema({ + + id:{ + type:String + }, + + date: { + type:Number + }, + + pairAddress:{ + type:String + }, + + token0: token, + + token1: token, + + // reserves + reserve0: { + type:Number + }, + reserve1: { + type:Number + }, + + // total supply for LP historical returns + totalSupply: { + type:Number + }, + + // derived liquidity + reserveUSD: { + type:Number + }, + + // volume stats + dailyVolumeToken0: { + type:Number + }, + dailyVolumeToken1: { + type:Number + }, + dailyVolumeUSD: { + type:Number + }, + dailyTxns: { + type:Number + } + +}); + +var pairDayData = mongoose.model("pairDayData", pairDayDataSchema); +module.exports = pairDayData; diff --git a/models/pairHourData.js b/models/pairHourData.js new file mode 100644 index 00000000..26199a64 --- /dev/null +++ b/models/pairHourData.js @@ -0,0 +1,52 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var pair= require('./pair'); + +const pairHourDataSchema = new Schema({ + + id:{ + type:String + }, + + hourStartUnix: { + type:Number + },// unix timestamp for start of hour + + pair: pair, + + // reserves + reserve0: { + type:Number + }, + reserve1: { + type:Number + }, + + // total supply for LP historical returns + totalSupply: { + type:Number + }, + + // derived liquidity + reserveUSD: { + type:Number + }, + + // volume stats + hourlyVolumeToken0: { + type:Number + }, + hourlyVolumeToken1: { + type:Number + }, + hourlyVolumeUSD: { + type:Number + }, + hourlyTxns: { + type:Number + } + +}); + +var pairHourData = mongoose.model("pairHourData", pairHourDataSchema); +module.exports = pairHourData; diff --git a/models/swap.js b/models/swap.js new file mode 100644 index 00000000..86fecdeb --- /dev/null +++ b/models/swap.js @@ -0,0 +1,55 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var pair= require('./pair'); +var transaction= require('./transaction'); + +const swapSchema = new Schema({ + + //transaction hash + "-" + index in swap Transaction array + id:{ + type:String + }, + + transaction: transaction, + + timestamp:{ + type:Number + }, //need this to pull recent txns for specific token or pair + + pair: pair, + + // populated from the Swap event + sender:{ + type:String + }, + from:{ + type:String + },//the EOA that initiated the txn + amount0In:{ + type:Number + }, + amount1In:{ + type:Number + }, + amount0Out:{ + type:Number + }, + amount1Out:{ + type:Number + }, + to:{ + type:String + }, + logIndex:{ + type:Number + }, + + // derived amount based on available prices of tokens + amountUSD:{ + type:Number + } + +}); + +var swap = mongoose.model("swap", swapSchema); +module.exports = swap; diff --git a/models/token.js b/models/token.js new file mode 100644 index 00000000..2dca6927 --- /dev/null +++ b/models/token.js @@ -0,0 +1,66 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var tokenDayData= require('./tokenDayData'); +var pairDayData= require('./pairDayData'); +var pair= require('./pair'); + +const tokenSchema = new Schema({ + + //token address + id: { + type: String, + }, + + // mirrored from the smart contract + symbol: { + type: String, + }, + name: { + type: String, + }, + decimals: { + type: Number, + }, + // used for other stats like marketcap + totalSupply: { + type: Number, + }, + + //token specific volume + tradeVolume: { + type: Number, + }, + tradeVolumeUSD: { + type: Number, + }, + untrackedVolumeUSD: { + type: Number, + }, + + //transactions across all pairs + txCount: { + type: Number, + }, + + //liquidity across all pairs + totalLiquidity: { + type: Number, + }, + + //derived prices + derivedETH: { + type: Number, + }, + + mostLiquidPairs: [pairDayData], + // derived fields + tokenDayData:[tokenDayData], // @derivedFrom(field: "token") + pairDayDataBase: [pairDayData], //@derivedFrom(field: "token0") + pairDayDataQuote: [pairDayData], //@derivedFrom(field: "token1") + pairBase: [pair], //@derivedFrom(field: "token0") + pairQuote: [pair], //@derivedFrom(field: "token1") + +}); + +var token = mongoose.model("token", tokenSchema); +module.exports = token; diff --git a/models/tokenDayData.js b/models/tokenDayData.js new file mode 100644 index 00000000..74c58c0c --- /dev/null +++ b/models/tokenDayData.js @@ -0,0 +1,50 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var token=require('./token'); + +const tokenDayDataSchema = new Schema({ + + id:{ + type:String + }, + + date: { + type:Number + }, + + token: token, + + // volume stats + dailyVolumeToken: { + type:Number + }, + dailyVolumeETH: { + type:Number + }, + dailyVolumeUSD: { + type:Number + }, + dailyTxns: { + type:Number + }, + + // liquidity stats + totalLiquidityToken: { + type:Number + }, + totalLiquidityETH: { + type:Number + }, + totalLiquidityUSD: { + type:Number + }, + + // price stats + priceUSD: { + type:Number + } + +}); + +var tokenDayData = mongoose.model("tokenDayData", tokenDayDataSchema); +module.exports = tokenDayData; diff --git a/models/transaction.js b/models/transaction.js new file mode 100644 index 00000000..64e489ad --- /dev/null +++ b/models/transaction.js @@ -0,0 +1,30 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var mint=require('./mint'); +var burn=require('./burn'); +var swap=require('./swap'); + +const transactionSchema = new Schema({ + + id: { + type: String, + },// txn hash + + blockNumber: { + type: Number, + }, + + timestamp: { + type: Number, + }, + + // This is not the reverse of Mint.transaction; it is only used to + // track incomplete mints (similar for burns and swaps) + mints: [mint], + burns: [burn], + swaps: [swap] + +}); + +var transaction = mongoose.model("transaction", transactionSchema); +module.exports = transaction; diff --git a/models/uniswapDayData.js b/models/uniswapDayData.js new file mode 100644 index 00000000..7869f106 --- /dev/null +++ b/models/uniswapDayData.js @@ -0,0 +1,44 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +const uniswapDayDataSchema = new Schema({ + + id:{ + type:String + },// timestamp rounded to current day by dividing by 86400 + + date: { + type:Number + }, + + dailyVolumeETH: { + type:Number + }, + dailyVolumeUSD: { + type:Number + }, + dailyVolumeUntracked: { + type:Number + }, + + totalVolumeETH: { + type:Number + }, + totalLiquidityETH: { + type:Number + }, + totalVolumeUSD: { + type:Number + }, // Accumulate at each trade, not just calculated off whatever totalVolume is. making it more accurate as it is a live conversion + totalLiquidityUSD: { + type:Number + }, + + txCount: { + type:Number + } + +}); + +var uniswapDayData = mongoose.model("uniswapDayData", uniswapDayDataSchema); +module.exports = uniswapDayData; diff --git a/models/uniswapFactory.js b/models/uniswapFactory.js new file mode 100644 index 00000000..1bede267 --- /dev/null +++ b/models/uniswapFactory.js @@ -0,0 +1,48 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var tokenDayData=require('./tokenDayData'); + +const uniswapFactorySchema = new Schema({ + + //factory address + id: { + type: String, + }, + + // pair info + pairCount: { + type: Number, + }, + + // total volume + totalVolumeUSD: { + type: Number, + }, + totalVolumeETH: { + type: Number, + }, + + //untracked values - less confident USD scores + untrackedVolumeUSD: { + type: Number, + }, + + //total liquidity + totalLiquidityUSD: { + type: Number, + }, + totalLiquidityETH: { + type: Number, + }, + + //transactions + txCount: { + type: Number + }, + + mostLiquidTokens: [tokenDayData] + +}); + +var uniswapFactory = mongoose.model("uniswapFactory", uniswapFactorySchema); +module.exports = uniswapFactory; diff --git a/models/user.js b/models/user.js new file mode 100644 index 00000000..61e2dbef --- /dev/null +++ b/models/user.js @@ -0,0 +1,18 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var liquidityPosition=require('./liquidityPosition'); + +const userSchema = new Schema({ + + // user address + id: { + type: String, + }, + liquidityPositions: [liquidityPosition], // @derivedFrom(field: "user") + usdSwapped: { + type: Number, + }, +}); + +var user = mongoose.model("user", userSchema); +module.exports = user; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..a5ad0f64 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2382 @@ +{ + "name": "graphql", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "0.0.0", + "dependencies": { + "cookie-parser": "~1.4.4", + "debug": "~2.6.9", + "dotenv": "^10.0.0", + "express": "~4.16.1", + "express-graphql": "^0.12.0", + "graphql": "^15.6.0", + "http-errors": "~1.6.3", + "jade": "~1.11.0", + "mongoose": "^6.0.8", + "morgan": "~1.9.1" + } + }, + "node_modules/@types/node": { + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", + "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==" + }, + "node_modules/@types/webidl-conversions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", + "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz", + "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz", + "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=", + "dependencies": { + "acorn": "^2.1.0" + } + }, + "node_modules/align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dependencies": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/asap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", + "integrity": "sha1-sqRdpf36ILBJb8N2jMJ8EvqRan0=" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dependencies": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bson": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.5.2.tgz", + "integrity": "sha512-8CEMJpwc7qlQtrn2rney38jQSEeMar847lz0LyitwRmVknAW8iHXrzW4fTjHfyWm0E3sukyD/zppdH+QU1QefA==", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dependencies": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/character-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.1.tgz", + "integrity": "sha1-wN3kqxgnE7kZuXCVmhI+zBow/NY=" + }, + "node_modules/clean-css": { + "version": "3.4.28", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", + "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "dependencies": { + "commander": "2.8.x", + "source-map": "0.4.x" + }, + "bin": { + "cleancss": "bin/cleancss" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-css/node_modules/commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dependencies": { + "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/commander": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", + "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=", + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/constantinople": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.0.2.tgz", + "integrity": "sha1-S5RdmTeQe82Y7ldRIsOBdRZUQUE=", + "deprecated": "Please update to at least constantinople 3.1.1", + "dependencies": { + "acorn": "^2.1.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", + "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", + "dependencies": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/css": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz", + "integrity": "sha1-k4aBHKgrzMnuf7WnMrHioxfIo+c=", + "dependencies": { + "css-parse": "1.0.4", + "css-stringify": "1.0.5" + } + }, + "node_modules/css-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz", + "integrity": "sha1-OLBQP7+dqfVOnB29pg4UXHcRe90=" + }, + "node_modules/css-stringify": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz", + "integrity": "sha1-sNBClG2ylTu50pKQCmy19tASIDE=" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/denque": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "dependencies": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-graphql": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.12.0.tgz", + "integrity": "sha512-DwYaJQy0amdy3pgNtiTDuGGM2BLdj+YO2SgbKoLliCfuHv3VVTt7vNG/ZqK2hRYjtYHE2t2KB705EU94mE64zg==", + "dependencies": { + "accepts": "^1.3.7", + "content-type": "^1.0.4", + "http-errors": "1.8.0", + "raw-body": "^2.4.1" + }, + "engines": { + "node": ">= 10.x" + }, + "peerDependencies": { + "graphql": "^14.7.0 || ^15.3.0" + } + }, + "node_modules/express-graphql/node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-graphql/node_modules/http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-graphql/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express-graphql/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/express-graphql/node_modules/raw-body": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", + "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.3", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-graphql/node_modules/raw-body/node_modules/http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-graphql/node_modules/raw-body/node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/express-graphql/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/express-graphql/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "node_modules/graphql": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.6.0.tgz", + "integrity": "sha512-WJR872Zlc9hckiEPhXgyUftXH48jp2EjO5tgBBOyNMRJZ9fviL2mJBD6CAysk6N5S0r9BTs09Qk39nnJBkvOXQ==", + "license": "MIT", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/jade": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/jade/-/jade-1.11.0.tgz", + "integrity": "sha1-nIDlOMEtP7lcjZu5VZ+gzAQEBf0=", + "deprecated": "Jade has been renamed to pug, please install the latest version of pug instead of jade", + "license": "MIT", + "dependencies": { + "character-parser": "1.2.1", + "clean-css": "^3.1.9", + "commander": "~2.6.0", + "constantinople": "~3.0.1", + "jstransformer": "0.0.2", + "mkdirp": "~0.5.0", + "transformers": "2.1.0", + "uglify-js": "^2.4.19", + "void-elements": "~2.0.1", + "with": "~4.0.0" + }, + "bin": { + "jade": "bin/jade.js" + } + }, + "node_modules/jstransformer": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz", + "integrity": "sha1-eq4pqQPRls+glz2IXT5HlH7Ndqs=", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^6.0.1" + } + }, + "node_modules/kareem": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", + "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "bin": { + "mime": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "dependencies": { + "mime-db": "1.50.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mongodb": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.1.2.tgz", + "integrity": "sha512-pHCKDoOy1h6mVurziJmXmTMPatYWOx8pbnyFgSgshja9Y36Q+caHUzTDY6rrIy9HCSrjnbXmx3pCtvNZHmR8xg==", + "dependencies": { + "bson": "^4.5.2", + "denque": "^2.0.1", + "mongodb-connection-string-url": "^2.0.0" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "saslprep": "^1.0.3" + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.1.0.tgz", + "integrity": "sha512-Qf9Zw7KGiRljWvMrrUFDdVqo46KIEiDuCzvEN97rh/PcKzk2bd6n9KuzEwBwW9xo5glwx69y1mI6s+jFUD/aIQ==", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^9.1.0" + } + }, + "node_modules/mongoose": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.0.9.tgz", + "integrity": "sha512-j9wcL8sltyIPBzMv785HFuGOdO8a5b70HX+e1q5QOogJxFofEXQoCcuurGlFSOe6j8M25qxHLzeVeKVcITeviQ==", + "dependencies": { + "bson": "^4.2.2", + "kareem": "2.3.2", + "mongodb": "4.1.2", + "mpath": "0.8.4", + "mquery": "4.0.0", + "ms": "2.1.2", + "regexp-clone": "1.0.0", + "sift": "13.5.2", + "sliced": "1.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "dependencies": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/mpath": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", + "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.0.tgz", + "integrity": "sha512-nGjm89lHja+T/b8cybAby6H0YgA4qYC/lx6UlwvHGqvTq8bDaNeCwl1sY8uRELrNbVWJzIihxVd+vphGGn1vBw==", + "dependencies": { + "debug": "4.x", + "regexp-clone": "^1.0.0", + "sliced": "1.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/promise": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", + "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", + "dependencies": { + "asap": "~1.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dependencies": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dependencies": { + "align-text": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/sift": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", + "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + }, + "node_modules/sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "node_modules/source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/transformers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", + "integrity": "sha1-XSPLNVYd2F3Gf7hIIwm0fVPM6ac=", + "deprecated": "Deprecated, use jstransformer", + "dependencies": { + "css": "~1.0.8", + "promise": "~2.0", + "uglify-js": "~2.2.5" + } + }, + "node_modules/transformers/node_modules/is-promise": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", + "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=" + }, + "node_modules/transformers/node_modules/promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz", + "integrity": "sha1-RmSKqdYFr10ucMMCS/WUNtoCuA4=", + "dependencies": { + "is-promise": "~1" + } + }, + "node_modules/transformers/node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/transformers/node_modules/uglify-js": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", + "integrity": "sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc=", + "dependencies": { + "optimist": "~0.3.5", + "source-map": "~0.1.7" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dependencies": { + "source-map": "~0.5.1", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + }, + "optionalDependencies": { + "uglify-to-browserify": "~1.0.0" + } + }, + "node_modules/uglify-js/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-url": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-9.1.0.tgz", + "integrity": "sha512-CQ0UcrPHyomtlOCot1TL77WyMIm/bCwrJ2D6AOKGwEczU9EpyoqAokfqrf/MioU9kHcMsmJZcg1egXix2KYEsA==", + "dependencies": { + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/with": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/with/-/with-4.0.3.tgz", + "integrity": "sha1-7v0VTp550sjTQXtkeo8U2f7M4U4=", + "dependencies": { + "acorn": "^1.0.1", + "acorn-globals": "^1.0.3" + } + }, + "node_modules/with/node_modules/acorn": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz", + "integrity": "sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ=", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + }, + "dependencies": { + "@types/node": { + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", + "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==" + }, + "@types/webidl-conversions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", + "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" + }, + "@types/whatwg-url": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz", + "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==", + "requires": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=" + }, + "acorn-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz", + "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=", + "requires": { + "acorn": "^2.1.0" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", + "integrity": "sha1-sqRdpf36ILBJb8N2jMJ8EvqRan0=" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "bson": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.5.2.tgz", + "integrity": "sha512-8CEMJpwc7qlQtrn2rney38jQSEeMar847lz0LyitwRmVknAW8iHXrzW4fTjHfyWm0E3sukyD/zppdH+QU1QefA==", + "requires": { + "buffer": "^5.6.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "character-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.1.tgz", + "integrity": "sha1-wN3kqxgnE7kZuXCVmhI+zBow/NY=" + }, + "clean-css": { + "version": "3.4.28", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", + "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "requires": { + "commander": "2.8.x", + "source-map": "0.4.x" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + } + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "commander": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", + "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=" + }, + "constantinople": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.0.2.tgz", + "integrity": "sha1-S5RdmTeQe82Y7ldRIsOBdRZUQUE=", + "requires": { + "acorn": "^2.1.0" + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", + "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6" + } + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "css": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz", + "integrity": "sha1-k4aBHKgrzMnuf7WnMrHioxfIo+c=", + "requires": { + "css-parse": "1.0.4", + "css-stringify": "1.0.5" + } + }, + "css-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz", + "integrity": "sha1-OLBQP7+dqfVOnB29pg4UXHcRe90=" + }, + "css-stringify": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz", + "integrity": "sha1-sNBClG2ylTu50pKQCmy19tASIDE=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "denque": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + } + } + }, + "express-graphql": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.12.0.tgz", + "integrity": "sha512-DwYaJQy0amdy3pgNtiTDuGGM2BLdj+YO2SgbKoLliCfuHv3VVTt7vNG/ZqK2hRYjtYHE2t2KB705EU94mE64zg==", + "requires": { + "accepts": "^1.3.7", + "content-type": "^1.0.4", + "http-errors": "1.8.0", + "raw-body": "^2.4.1" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "raw-body": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", + "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.3", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + } + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "graphql": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.6.0.tgz", + "integrity": "sha512-WJR872Zlc9hckiEPhXgyUftXH48jp2EjO5tgBBOyNMRJZ9fviL2mJBD6CAysk6N5S0r9BTs09Qk39nnJBkvOXQ==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "jade": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/jade/-/jade-1.11.0.tgz", + "integrity": "sha1-nIDlOMEtP7lcjZu5VZ+gzAQEBf0=", + "requires": { + "character-parser": "1.2.1", + "clean-css": "^3.1.9", + "commander": "~2.6.0", + "constantinople": "~3.0.1", + "jstransformer": "0.0.2", + "mkdirp": "~0.5.0", + "transformers": "2.1.0", + "uglify-js": "^2.4.19", + "void-elements": "~2.0.1", + "with": "~4.0.0" + } + }, + "jstransformer": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz", + "integrity": "sha1-eq4pqQPRls+glz2IXT5HlH7Ndqs=", + "requires": { + "is-promise": "^2.0.0", + "promise": "^6.0.1" + } + }, + "kareem": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", + "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==" + }, + "mime-types": { + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "requires": { + "mime-db": "1.50.0" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "mongodb": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.1.2.tgz", + "integrity": "sha512-pHCKDoOy1h6mVurziJmXmTMPatYWOx8pbnyFgSgshja9Y36Q+caHUzTDY6rrIy9HCSrjnbXmx3pCtvNZHmR8xg==", + "requires": { + "bson": "^4.5.2", + "denque": "^2.0.1", + "mongodb-connection-string-url": "^2.0.0", + "saslprep": "^1.0.3" + } + }, + "mongodb-connection-string-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.1.0.tgz", + "integrity": "sha512-Qf9Zw7KGiRljWvMrrUFDdVqo46KIEiDuCzvEN97rh/PcKzk2bd6n9KuzEwBwW9xo5glwx69y1mI6s+jFUD/aIQ==", + "requires": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^9.1.0" + } + }, + "mongoose": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.0.9.tgz", + "integrity": "sha512-j9wcL8sltyIPBzMv785HFuGOdO8a5b70HX+e1q5QOogJxFofEXQoCcuurGlFSOe6j8M25qxHLzeVeKVcITeviQ==", + "requires": { + "bson": "^4.2.2", + "kareem": "2.3.2", + "mongodb": "4.1.2", + "mpath": "0.8.4", + "mquery": "4.0.0", + "ms": "2.1.2", + "regexp-clone": "1.0.0", + "sift": "13.5.2", + "sliced": "1.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + } + }, + "mpath": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", + "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==" + }, + "mquery": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.0.tgz", + "integrity": "sha512-nGjm89lHja+T/b8cybAby6H0YgA4qYC/lx6UlwvHGqvTq8bDaNeCwl1sY8uRELrNbVWJzIihxVd+vphGGn1vBw==", + "requires": { + "debug": "4.x", + "regexp-clone": "^1.0.0", + "sliced": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "requires": { + "wordwrap": "~0.0.2" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "promise": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", + "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", + "requires": { + "asap": "~1.0.0" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sift": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", + "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "requires": { + "punycode": "^2.1.1" + } + }, + "transformers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", + "integrity": "sha1-XSPLNVYd2F3Gf7hIIwm0fVPM6ac=", + "requires": { + "css": "~1.0.8", + "promise": "~2.0", + "uglify-js": "~2.2.5" + }, + "dependencies": { + "is-promise": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", + "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=" + }, + "promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz", + "integrity": "sha1-RmSKqdYFr10ucMMCS/WUNtoCuA4=", + "requires": { + "is-promise": "~1" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "requires": { + "amdefine": ">=0.0.4" + } + }, + "uglify-js": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", + "integrity": "sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc=", + "requires": { + "optimist": "~0.3.5", + "source-map": "~0.1.7" + } + } + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" + }, + "whatwg-url": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-9.1.0.tgz", + "integrity": "sha512-CQ0UcrPHyomtlOCot1TL77WyMIm/bCwrJ2D6AOKGwEczU9EpyoqAokfqrf/MioU9kHcMsmJZcg1egXix2KYEsA==", + "requires": { + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "with": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/with/-/with-4.0.3.tgz", + "integrity": "sha1-7v0VTp550sjTQXtkeo8U2f7M4U4=", + "requires": { + "acorn": "^1.0.1", + "acorn-globals": "^1.0.3" + }, + "dependencies": { + "acorn": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz", + "integrity": "sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ=" + } + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..257ba802 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "graphql", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "nodemon ./bin/www" + }, + "dependencies": { + "cookie-parser": "~1.4.4", + "debug": "~2.6.9", + "dotenv": "^10.0.0", + "express": "~4.16.1", + "express-graphql": "^0.12.0", + "graphql": "^15.6.0", + "http-errors": "~1.6.3", + "jade": "~1.11.0", + "mongoose": "^6.0.8", + "morgan": "~1.9.1" + }, + "devdependencies": { + "nodemon": "^2.0.13" + } +} diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 00000000..9453385b --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} diff --git a/test.js b/test.js new file mode 100644 index 00000000..7222d1ef --- /dev/null +++ b/test.js @@ -0,0 +1,22 @@ +var mongoose = require('mongoose'); +require('mongoose-bigdecimal'); +var Schema = mongoose.Schema; +var BigDecimal = require('big.js'); + +//define your schema +var ProductSchema = new Schema({ + price: { + type: Schema.Types.BigDecimal, + required: true, + index:true + }, + discounts:[{ + type: Schema.Types.BigDecimal + }] +}); +Product = mongoose.model('Product', ProductSchema); + +//use it +var book = new Product(); +book.price = new BigDecimal(12); +book.save(done); \ No newline at end of file diff --git a/views/error.jade b/views/error.jade new file mode 100644 index 00000000..51ec12c6 --- /dev/null +++ b/views/error.jade @@ -0,0 +1,6 @@ +extends layout + +block content + h1= message + h2= error.status + pre #{error.stack} diff --git a/views/index.jade b/views/index.jade new file mode 100644 index 00000000..3d63b9a0 --- /dev/null +++ b/views/index.jade @@ -0,0 +1,5 @@ +extends layout + +block content + h1= title + p Welcome to #{title} diff --git a/views/layout.jade b/views/layout.jade new file mode 100644 index 00000000..15af079b --- /dev/null +++ b/views/layout.jade @@ -0,0 +1,7 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + body + block content