From 59b4d6b79e28eb326b9b6d2290b0432fab4cc896 Mon Sep 17 00:00:00 2001 From: Tobiah Date: Mon, 21 Oct 2019 23:42:28 -0500 Subject: [PATCH 1/8] feat: metrics stuff coolness --- .gitignore | 1 + .gitmodules | 4 + controllers/rss.js | 15 - controllers/twitter.js | 19 - lib/caches/RSSSocketEmitter.js | 67 - lib/caches/TwitterCache.js | 154 - lib/caches/WSCache.js | 47 - lib/resources/rssFeeds.json | 191 - lib/resources/tweeters.json | 17 - lib/utilities.js | 120 - package-lock.json | 2228 ++++-- package.json | 56 +- server.js | 45 - sockets/index.js | 19 - sockets/rss.js | 13 - sockets/twitter.js | 13 - sockets/worldstate/events/cycleLike.js | 27 - sockets/worldstate/events/nightwave.js | 29 - sockets/worldstate/events/objectLike.js | 19 - sockets/worldstate/index.js | 28 - sockets/worldstate/parseEvents.js | 131 - sockets/worldstate/wsSocketUtils.js | 64 - src/api-spec/openapi.json | 6682 +++++++++++++++++ {controllers => src/controllers}/drops.js | 8 +- {controllers => src/controllers}/heartbeat.js | 0 {controllers => src/controllers}/index.js | 4 +- .../controllers}/pricecheck.js | 9 +- {controllers => src/controllers}/rivens.js | 178 +- src/controllers/rss.js | 15 + .../controllers}/staticWfData.js | 230 +- src/controllers/twitter.js | 22 + {controllers => src/controllers}/wfItems.js | 156 +- .../controllers}/worldstate.js | 46 +- src/lib/addons.js | 51 + {lib => src/lib}/caches/Drops.js | 0 src/lib/logger.js | 50 + src/lib/meta/themes/dark.css | 186 + src/lib/utilities.js | 68 + src/main.js | 10 + src/server.js | 33 + src/socket.js | 40 + src/sockets/beater.js | 31 + src/sockets/index.js | 60 + test/tester.js | 52 + 44 files changed, 9053 insertions(+), 2185 deletions(-) create mode 100644 .gitmodules delete mode 100644 controllers/rss.js delete mode 100644 controllers/twitter.js delete mode 100644 lib/caches/RSSSocketEmitter.js delete mode 100644 lib/caches/TwitterCache.js delete mode 100644 lib/caches/WSCache.js delete mode 100644 lib/resources/rssFeeds.json delete mode 100644 lib/resources/tweeters.json delete mode 100644 lib/utilities.js delete mode 100644 server.js delete mode 100644 sockets/index.js delete mode 100644 sockets/rss.js delete mode 100644 sockets/twitter.js delete mode 100644 sockets/worldstate/events/cycleLike.js delete mode 100644 sockets/worldstate/events/nightwave.js delete mode 100644 sockets/worldstate/events/objectLike.js delete mode 100644 sockets/worldstate/index.js delete mode 100644 sockets/worldstate/parseEvents.js delete mode 100644 sockets/worldstate/wsSocketUtils.js create mode 100644 src/api-spec/openapi.json rename {controllers => src/controllers}/drops.js (89%) rename {controllers => src/controllers}/heartbeat.js (100%) rename {controllers => src/controllers}/index.js (86%) rename {controllers => src/controllers}/pricecheck.js (90%) rename {controllers => src/controllers}/rivens.js (91%) create mode 100644 src/controllers/rss.js rename {controllers => src/controllers}/staticWfData.js (91%) create mode 100644 src/controllers/twitter.js rename {controllers => src/controllers}/wfItems.js (75%) rename {controllers => src/controllers}/worldstate.js (63%) create mode 100644 src/lib/addons.js rename {lib => src/lib}/caches/Drops.js (100%) create mode 100644 src/lib/logger.js create mode 100644 src/lib/meta/themes/dark.css create mode 100644 src/lib/utilities.js create mode 100644 src/main.js create mode 100644 src/server.js create mode 100644 src/socket.js create mode 100644 src/sockets/beater.js create mode 100644 src/sockets/index.js create mode 100644 test/tester.js diff --git a/.gitignore b/.gitignore index d73831df2..8a8b2bada 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ tmp/ #nodemon dev config .nodemon nodemon.json +.env diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..b08c91e60 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "api-spec"] + path = api-spec + url = https://github.com/wfcd/api-spec + branch = gh-pages diff --git a/controllers/rss.js b/controllers/rss.js deleted file mode 100644 index 7558de34e..000000000 --- a/controllers/rss.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -const express = require('express'); - -const router = express.Router(); - -const { setHeadersAndJson } = require('../lib/utilities'); - -const rss = require('../lib/caches/RSSSocketEmitter'); - -router.get('/', (req, res) => { - setHeadersAndJson(res, rss.feeder.list.map(i => ({ url: i.url, items: i.items }))); -}); - -module.exports = router; diff --git a/controllers/twitter.js b/controllers/twitter.js deleted file mode 100644 index 0d80e7a70..000000000 --- a/controllers/twitter.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -const express = require('express'); - -const router = express.Router(); -const twitter = require('../lib/caches/TwitterCache'); - -const { - logger, setHeadersAndJson, cache, ah, -} = require('../lib/utilities'); - -if (twitter.clientInfoValid) { - router.get('/', cache('1 minute'), ah(async (req, res) => { - logger.silly(`Got ${req.originalUrl}`); - setHeadersAndJson(res, await twitter.getData()); - })); -} - -module.exports = router; diff --git a/lib/caches/RSSSocketEmitter.js b/lib/caches/RSSSocketEmitter.js deleted file mode 100644 index 779646a94..000000000 --- a/lib/caches/RSSSocketEmitter.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict'; - -const EventEmitter = require('events'); - -const RssFeedEmitter = require('rss-feed-emitter'); - -const feeds = require('../resources/rssFeeds.json'); - -const { logger } = require('../utilities'); - -class RSSSocketEmitter extends EventEmitter { - constructor() { - super(); - - this.logger = logger; - - this.feeder = new RssFeedEmitter({ userAgent: 'WFCD Feed Notifier' }); - - feeds.forEach((feed) => { - this.feeder.add({ url: feed.url, timeout: 30000 }); - }); - - this.logger.info('RSS Feed active'); - - this.start = Date.now(); - - this.feeder.on('error', this.logger.error); - - this.feeder.on('new-item', (item) => { - try { - if (Object.keys(item.image).length) { - this.logger.debug(JSON.stringify(item.image)); - } - - if (new Date(item.pubDate).getTime() > this.start) { - const feed = feeds.filter(feedEntry => feedEntry.url === item.meta.link)[0]; - let firstImg = ((item.description || '').match(//i) || [])[1]; - if (!firstImg) { - firstImg = feed.defaultAttach; - } else if (firstImg.startsWith('//')) { - firstImg = firstImg.replace('//', 'https://'); - } - - const rssSummary = { - body: (item.description || '\u200B').replace(/<(?:.|\n)*?>/gm, '').replace(/\n\n+\s*/gm, '\n\n'), - url: item.link, - timestamp: item.pubDate, - description: item.meta.description, - author: feed.author || { - name: 'Warframe Forums', - url: item['rss:link']['#'], - icon_url: 'https://i.imgur.com/hE2jdpv.png', - }, - title: item.title, - image: firstImg, - key: feed.key, - }; - this.emit('new-rss', rssSummary); - } - } catch (error) { - this.logger.error(error); - } - }); - } -} - -module.exports = new RSSSocketEmitter({ userAgent: 'WFCD Feed Notifier' }); diff --git a/lib/caches/TwitterCache.js b/lib/caches/TwitterCache.js deleted file mode 100644 index 8890a7aac..000000000 --- a/lib/caches/TwitterCache.js +++ /dev/null @@ -1,154 +0,0 @@ -'use strict'; - -const EventEmitter = require('events'); -const Twitter = require('twitter'); -const toWatch = require('../resources/tweeters.json'); - -const { logger } = require('../utilities'); - -const determineTweetType = (tweet) => { - if (tweet.in_reply_to_status_id) { - return ('reply'); - } - if (tweet.quoted_status_id) { - return ('quote'); - } - if (tweet.retweeted_status) { - return ('retweet'); - } - return ('tweet'); -}; - -/** - * Twitter event emitter - * @extends EventEmitter - */ -class TwitterCache extends EventEmitter { - constructor() { - super(); - this.timeout = process.env.TWITTER_TIMEOUT || 60000; - this.initTime = Date.now(); - - const clientInfo = { - consumer_key: process.env.TWITTER_KEY, - consumer_secret: process.env.TWITTER_SECRET, - bearer_token: process.env.TWITTER_BEARER_TOKEN, - }; - - this.clientInfoValid = clientInfo.consumer_key - && clientInfo.consumer_secret - && clientInfo.bearer_token; - - - try { - if (this.clientInfoValid) { - this.client = new Twitter(clientInfo); - - // don't attempt anything else if authentication fails - this.toWatch = toWatch; - this.currentData = null; - this.lastUpdated = Date.now() - 60000; - this.updateInterval = setInterval(() => this.update(), this.timeout); - this.update(); - } else { - logger.verbose(`client not initialized... invalid token: ${clientInfo.bearer_token}`); - } - } catch (err) { - this.client = undefined; - this.clientInfoValid = false; - logger.error(err); - } - } - - async update() { - if (!this.clientInfoValid) return undefined; - - if (!this.toWatch) { - logger.verbose('Not processing twitter, no data to watch.'); - return undefined; - } - - if (!this.client) { - logger.verbose('Not processing twitter, no client to connect.'); - return undefined; - } - - this.updating = new Promise(async (resolve) => { - logger.silly('Starting Twitter update...'); - const parsedData = []; - try { - for (const watchable of this.toWatch) { - const tweets = await this.client.get('statuses/user_timeline', { - screen_name: watchable.acc_name, - tweet_mode: 'extended', - count: 1, - }); - const [tweet] = tweets; - - const type = determineTweetType(tweet); - const parsedTweet = { - id: `twitter.${watchable.plain}.${type}`, - uniqueId: String(tweets[0].id_str), - text: tweet.full_text, - url: `https://twitter.com/${tweet.user.screen_name}/status/${tweet.id_str}`, - mediaUrl: tweet.entities.media ? tweet.entities.media[0].media_url : undefined, - isReply: tweet.in_reply_to_status_id ? true : false, - author: { - name: tweet.user.name, - handle: tweet.user.screen_name, - url: `https://twitter.com/${tweet.user.screen_name}`, - avatar: `${tweet.user.profile_image_url.replace('_normal.jpg', '.jpg')}`, - }, - quote: tweet.quoted_status - ? { - text: tweet.quoted_status.full_text, - author: { - name: tweet.quoted_status.user.name, - handle: tweet.quoted_status.user.screen_name, - }, - } - : undefined, - retweet: tweet.retweeted_status - ? { - text: tweet.retweeted_status.full_text, - author: { - name: tweet.retweeted_status.user.name, - handle: tweet.retweeted_status.user.screen_name, - }, - } - : undefined, - createdAt: new Date(tweet.created_at), - tweets, - }; - parsedData.push(parsedTweet); - - if (parsedTweet.createdAt.getTime() > this.lastUpdated) { - this.emit('tweet', parsedTweet); - } - } - } catch (error) { - if (error[0] && error[0].code === 32) { - this.clientInfoValid = false; - logger.info('wiping twitter client data, could not authenticate...'); - } else { - logger.debug(JSON.stringify(error)); - } - } - this.lastUpdated = Date.now(); - resolve(parsedData); - }); - - return this.updating; - } - - async getData() { - if (!this.clientInfoValid) return undefined; - - if (this.updating) { - return this.updating; - } - return this.currentData; - } -} - -module.exports = new TwitterCache(); diff --git a/lib/caches/WSCache.js b/lib/caches/WSCache.js deleted file mode 100644 index decd5a2c3..000000000 --- a/lib/caches/WSCache.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; - -const Worldstate = require('warframe-worldstate-parser'); -const EventEmitter = require('events'); - -class WSCache extends EventEmitter { - constructor(platform, language, kuvaCache, sentientCache) { - super(); - this.inner = null; - Object.defineProperty(this, 'inner', { enumerable: false, configurable: false }); - - this.kuvaCache = kuvaCache; - Object.defineProperty(this, 'kuvaCache', { enumerable: false, configurable: false }); - - this.sentientCache = sentientCache; - Object.defineProperty(this, 'sentientCache', { enumerable: false, configurable: false }); - - this.platform = platform; - this.language = language; - } - - get data() { - return this.inner; - } - - set data(newData) { - setTimeout(async () => { - const t = new Worldstate(newData, { - locale: this.language, - kuvaData: await this.kuvaCache.getData(), - sentientData: await this.sentientCache.getData(), - kuvaCache: this.kuvaCache, - sentientCache: this.sentientCache, - }); - if (!t.timestamp) return; - this.inner = t; - this.emit('update', newData); - }, 1000); - } - - set twitter(newTwitter) { - if (!this.inner || !(newTwitter && newTwitter.length)) return; - this.inner.twitter = newTwitter; - } -} - -module.exports = WSCache; diff --git a/lib/resources/rssFeeds.json b/lib/resources/rssFeeds.json deleted file mode 100644 index 483033dc5..000000000 --- a/lib/resources/rssFeeds.json +++ /dev/null @@ -1,191 +0,0 @@ -[ - { - "url": "https://forums.warframe.com/forum/38-players-helping-players.xml", - "key": "players_helping_players", - "defaultAttach": "https://i.imgur.com/cuk4ro9.png" - }, - { - "url": "https://forums.warframe.com/forum/3-pc-update-notes.xml", - "key": "forum.updates.pc", - "defaultAttach": "https://i.imgur.com/eY1NkzO.png" - }, - { - "url": "https://forums.warframe.com/forum/152-ps4-update-notes.xml", - "key": "forum.updates.ps4", - "defaultAttach": "https://i.imgur.com/eY1NkzO.png" - }, - { - "url": "https://forums.warframe.com/forum/253-xbox-one-update-notes.xml", - "key": "forum.updates.xb1", - "defaultAttach": "https://i.imgur.com/eY1NkzO.png" - }, - { - "url": "https://forums.warframe.com/forum/1196-nintendo-switch-update-notes.xml", - "key": "forum.updates.switch", - "defaultAttach": "https://i.imgur.com/eY1NkzO.png" - }, - { - "url": "https://forums.warframe.com/forum/2-pc-announcements.xml", - "defaultAttach": "https://i.imgur.com/CNrsc7V.png", - "key": "forum.announcements.pc" - }, - { - "url": "https://forums.warframe.com/forum/151-ps4-announcements.xml", - "defaultAttach": "https://i.imgur.com/CNrsc7V.png", - "key": "forum.announcements.ps4" - }, - { - "url": "https://forums.warframe.com/forum/252-xbox-one-announcements.xml", - "defaultAttach": "https://i.imgur.com/CNrsc7V.png", - "key": "forum.announcements.xb1" - }, - { - "url": "https://forums.warframe.com/forum/1198-nintendo-switch-announcements.xml", - "defaultAttach": "https://i.imgur.com/CNrsc7V.png", - "key": "forum.announcements.switch" - }, - { - "url": "https://forums.warframe.com/forum/170-announcements-events.xml", - "key": "forum.news", - "defaultAttach": "https://i.imgur.com/CNrsc7V.png" - }, - { - "url": "https://forums.warframe.com/forum/123-developer-workshop-update-notes.xml", - "key": "forum.workshop" - }, - { - "url": "https://forums.warframe.com/discover/837.xml", - "key": "forum.staff.megan", - "author": { - "name": "[DE]Megan", - "url": "https://forums.warframe.com/profile/384139-demegan/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2017_06/ezgif.com-crop.thumb.gif.e510920610e8489a54dd5dbe8b9fd4db.gif" - } - }, - { - "url": "https://forums.warframe.com/discover/839.xml", - "key": "forum.staff.rebecca", - "author": { - "name": "[DE]Rebecca", - "url": "https://forums.warframe.com/profile/4-derebecca/", - "icon_url": "https://content.invisioncic.com/Mwarframe/pages_media/1_PlayerAvatarsInkary.png" - } - }, - { - "url": "https://forums.warframe.com/discover/840.xml", - "key": "forum.staff.danielle", - "author": { - "name": "[DE]Danielle", - "url": "https://forums.warframe.com/profile/869879-dedanielle/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2018_04/profile.thumb.jpg.fe072e16d5b54892b95030ea410264e7.jpg" - } - }, - { - "url": "https://forums.warframe.com/discover/841.xml", - "key": "forum.staff.drew", - "author": { - "name": "[DE]Drew", - "url": "https://forums.warframe.com/profile/488958-dedrew/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2016_06/576c323f14e92_AnimatedAvatarMirrored.thumb.gif.f12f2373d0d4b91363647f35b30026e0.gif" - } - }, - { - "url": "https://forums.warframe.com/discover/842.xml", - "key": "forum.staff.glen", - "author": { - "name": "[DE]Glen", - "url": "https://forums.warframe.com/profile/10-deglen/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2016_05/57474a4312a72_GlensHorse.thumb.png.7b47cb0660f5af2e6101ed84d3192c03.png" - } - }, - { - "url": "https://forums.warframe.com/discover/1171.xml", - "key": "forum.staff.taylor", - "author": { - "name": "[DE]taylor", - "url": "https://forums.warframe.com/profile/1943322-detaylor/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2017_07/596ceb9b4e182_6FUC4s3%281%29.thumb.png.7045d25fc7b057c70ddcffe14cd1f43e.png" - } - }, - { - "url": "https://forums.warframe.com/discover/1777.xml", - "key": "forum.staff.steve", - "author": { - "name": "[DE]Steve", - "url": "https://forums.warframe.com/profile/24-desteve/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2016_06/575f13eaf3080_Pastedimageat2016_06_1304_11PM.thumb.png.fb98af24931a1820d00293a55c05baef.png" - } - }, - { - "url": "https://forums.warframe.com/discover/1291.xml", - "key": "forum.staff.helen", - "author": { - "name": "[DE]Helen", - "url": "https://forums.warframe.com/profile/2522846-dehelen/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2017_06/590e937e598f2_Helen-Icon.jpg.0ac6d3d86c1d48f3f3e1a035344ab87a.thumb.jpg.d2225fb0dcd980f081ddddbf46458072.jpg" - } - }, - { - "url": "https://forums.warframe.com/discover/1294.xml", - "key": "forum.staff.saske", - "author": { - "name": "[DE]Saske", - "url": "https://forums.warframe.com/profile/4513168-nswdesaske/", - "icon_url": "https://content.invisioncic.com/Mwarframe/pages_media/1_TennoCon2018Glyph.png" - } - }, - { - "url": "https://forums.warframe.com/discover/1295.xml", - "key": "forum.staff.syncrasis", - "author": { - "name": "[DE]Syncrasis", - "url": "https://forums.warframe.com/profile/2514676-desyncrasis//", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2017_02/aladv.thumb.jpg.a06cbfa091579eb9ce2ae96eb0b42e34.jpg" - } - }, - { - "url": "https://forums.warframe.com/discover/1299.xml", - "key": "forum.staff.pablo", - "author": { - "name": "[DE]Pablo", - "url": "https://forums.warframe.com/profile/217656-depablo/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2016_05/Pablo.thumb.png.35bb0384ef7b88e807d55ffc31af0896.png" - } - }, - { - "url": "https://forums.warframe.com/discover/1778.xml", - "key": "forum.staff.connor", - "author": { - "name": "[DE]Connor", - "url": "https://forums.warframe.com/profile/710227-deconnor/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2017_02/nezha_watermarked_120_by_kevin_glint-d9zkt5n.thumb.jpg.c9519505f40a304bece8eb435eb99ddd.jpg" - } - }, - { - "url": "https://forums.warframe.com/discover/1779.xml", - "key": "forum.staff.marcus", - "author": { - "name": "[DE]Marcus", - "url": "https://forums.warframe.com/profile/3443485-demarcus/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2018_01/5a55278d71caa_MarcusIIPrint.thumb.jpg.16cb689d778112333cffb6fe546b89ec.jpg" - } - }, - { - "url": "https://forums.warframe.com/discover/1780.xml", - "key": "forum.staff.george", - "author": { - "name": "[DE]George", - "url": "https://forums.warframe.com/profile/138665-degeorge/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2016_02/George_Forum_Photo.thumb.png.62acd07b2b2e40831eca57a0e230a2b2.png" - } - }, - { - "url": "https://forums.warframe.com/discover/1781.xml", - "key": "forum.staff.bear", - "author": { - "name": "[DE]Bear", - "url": "https://forums.warframe.com/profile/1589-debear/", - "icon_url": "https://content.invisioncic.com/Mwarframe/monthly_2018_02/juggerdaddy.thumb.gif.85061b940143c7310801ce00337636ac.gif" - } - } -] \ No newline at end of file diff --git a/lib/resources/tweeters.json b/lib/resources/tweeters.json deleted file mode 100644 index 19c73da8a..000000000 --- a/lib/resources/tweeters.json +++ /dev/null @@ -1,17 +0,0 @@ -[ - { "acc_name": "@playwarframe", "plain": "warframe" }, - { "acc_name": "@digitalextremes", "plain": "digitalextremes" }, - { "acc_name": "@PabloPoon", "plain": "pablo" }, - { "acc_name": "@Cam_Rogers", "plain": "cameron" }, - { "acc_name": "@rebbford", "plain": "rebecca" }, - { "acc_name": "@sj_sinclair", "plain": "steve" }, - { "acc_name": "@soelloo", "plain": "danielle" }, - { "acc_name": "@moitoi", "plain": "megan" }, - { "acc_name": "@GameSoundDesign", "plain": "george" }, - { "acc_name": "@msinilo", "plain": "maciej" }, - { "acc_name": "@sheldoncarter", "plain": "sheldon" }, - { "acc_name": "@narcbag", "plain": "narc" }, - { "acc_name": "@Helen_Heikkila", "plain": "helen" }, - { "acc_name": "@tobitenno", "plain": "tobiah" }, - { "acc_name": "@wfdiscord", "plain": "wfdiscord" } -] \ No newline at end of file diff --git a/lib/utilities.js b/lib/utilities.js deleted file mode 100644 index 019f4bfb0..000000000 --- a/lib/utilities.js +++ /dev/null @@ -1,120 +0,0 @@ -'use strict'; - -require('colors'); -const ah = require('express-async-handler'); -const warframeData = require('warframe-worldstate-data'); -const Items = require('warframe-items'); - -const { transports, createLogger, format } = require('winston'); -const apiCache = require('apicache'); - -const Cache = require('json-fetch-cache'); -const WSCache = require('./caches/WSCache'); - -const platforms = ['pc', 'ps4', 'xb1', 'swi']; -const platformAliases = ['ns']; -const worldStates = {}; - -const { - combine, label, printf, colorize, -} = format; - -/** - * Group an array by a field value - * @param {Object[]} array array of objects to broup - * @param {string} field field to group by - * @returns {Object} [description] - */ -const groupBy = (array, field) => { - const grouped = {}; - if (!array) return undefined; - array.forEach((item) => { - const fVal = item[field]; - if (!grouped[fVal]) { - grouped[fVal] = []; - } - grouped[fVal].push(item); - }); - return grouped; -}; - -/* Logger setup */ -const transport = new transports.Console({ colorize: true }); -const logFormat = printf(info => `[${info.label}] ${info.level}: ${info.message}`); -const logger = createLogger({ - format: combine( - colorize(), - label({ label: 'REST'.cyan }), - logFormat, - ), - transports: [transport], -}); -logger.level = process.env.LOG_LEVEL || 'error'; - -const socketLogger = createLogger({ - format: combine( - colorize(), - label({ label: 'SOCK'.yellow }), - logFormat, - ), - transports: [transport], -}); -socketLogger.level = process.env.LOG_LEVEL || 'error'; - -/* Warframe Data & Keys */ -delete warframeData.weapons; -delete warframeData.warframes; - -const kuvaCache = new Cache('https://10o.io/kuvalog.json', 300000, { logger, delayStart: true }); -const sentientCache = new Cache('https://semlar.com/anomaly.json', 300000, { logger, delayStart: true }); - -const wsTimeout = process.env.CACHE_TIMEOUT || 60000; -const wsRawCaches = {}; - -platforms.forEach((p) => { - const url = `http://content${p === 'pc' ? '' : `.${p}`}.warframe.com/dynamic/worldState.php`; - worldStates[p] = {}; - - warframeData.locales.forEach((locale) => { - worldStates[p][locale] = new WSCache(p, locale, kuvaCache, sentientCache); - }); - wsRawCaches[p] = new Cache(url, wsTimeout, { - delayStart: false, - parser: str => str, - useEmitter: true, - logger, - }); - wsRawCaches[p].on('update', (dataStr) => { - warframeData.locales.forEach((locale) => { - worldStates[p][locale].data = dataStr; - }); - }); -}); - -const titleCase = str => str.toLowerCase().replace(/\b\w/g, l => l.toUpperCase()); - -module.exports = { - setHeadersAndJson: (res, json) => { - res.setHeader('Content-Type', 'application/json'); - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'); - res.setHeader('Access-Control-Expose-Headers', 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'); - res.json(json); - }, - logger, - platforms, - platformAliases, - cache: apiCache.options({ - appendKey: req => `${req.platform}${req.language}` || '', - }).middleware, - Items, - warframeData, - solKeys: Object.keys(warframeData.solNodes), - worldStates, - ah, - titleCase, - socketLogger, - groupBy, - languages: warframeData.locales, -}; diff --git a/package-lock.json b/package-lock.json index eeb38af5a..81bd9b98b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,159 +35,119 @@ } }, "@sentry/apm": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/apm/-/apm-5.20.1.tgz", - "integrity": "sha512-oqfyYqRR1CaM/U5qZg3KY9MxCe4OWYs3uiOvVGMOHCyx50dYsDZziM5DDVUvi6pOuokLCNbyXO9xGROSmploBQ==", - "requires": { - "@sentry/browser": "5.20.1", - "@sentry/hub": "5.20.1", - "@sentry/minimal": "5.20.1", - "@sentry/types": "5.20.1", - "@sentry/utils": "5.20.1", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/apm/-/apm-5.20.0.tgz", + "integrity": "sha512-6zfMRYXG/9VzsmgQqYqFFvg/5XJYOimY/KIrJAijemMLb0Xhwu3xw/2eelWxkWilTBXUWO+dQel5P7JBA8QsKw==", + "requires": { + "@sentry/browser": "5.20.0", + "@sentry/hub": "5.20.0", + "@sentry/minimal": "5.20.0", + "@sentry/types": "5.20.0", + "@sentry/utils": "5.20.0", "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/types": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.1.tgz", - "integrity": "sha512-OU+i/lcjGpDJv0XkNpsKrI2r1VPp8qX0H6Knq8NuZrlZe3AbvO3jRJJK0pH14xFv8Xok5jbZZpKKoQLxYfxqsw==" - } } }, "@sentry/browser": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.20.1.tgz", - "integrity": "sha512-ClykuvrEsMKgAvifx5VHzRjchwYbJFX8YiIicYx+Wr3MXL2jLG6OEfHHJwJeyBL2C3vxd5O0KPK3pGMR9wPMLA==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.20.0.tgz", + "integrity": "sha512-xVPL7/RuAPcemfSzXiyPHAt4M+0BfzkdTlN+PZb6frCEo4k6E0UiN6WLsGj/iwa2gXhyfTQXtbTuP+tDuNPEJw==", "requires": { - "@sentry/core": "5.20.1", - "@sentry/types": "5.20.1", - "@sentry/utils": "5.20.1", + "@sentry/core": "5.20.0", + "@sentry/types": "5.20.0", + "@sentry/utils": "5.20.0", "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/types": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.1.tgz", - "integrity": "sha512-OU+i/lcjGpDJv0XkNpsKrI2r1VPp8qX0H6Knq8NuZrlZe3AbvO3jRJJK0pH14xFv8Xok5jbZZpKKoQLxYfxqsw==" - } } }, "@sentry/core": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.20.1.tgz", - "integrity": "sha512-gG622/UY2TePruF6iUzgVrbIX5vN8w2cjlWFo1Est8MvCfQsz8agGaLMCAyl5hCGJ6K2qTUZDOlbCNIKoMclxg==", - "requires": { - "@sentry/hub": "5.20.1", - "@sentry/minimal": "5.20.1", - "@sentry/types": "5.20.1", - "@sentry/utils": "5.20.1", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.20.0.tgz", + "integrity": "sha512-fzzWKEolc0O6H/phdDenzKs7JXDSb0sooxVn0QCUkwWSzACALQh+NR/UciOXyhyuoUiqu4zthYQx02qtGqizeQ==", + "requires": { + "@sentry/hub": "5.20.0", + "@sentry/minimal": "5.20.0", + "@sentry/types": "5.20.0", + "@sentry/utils": "5.20.0", "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/types": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.1.tgz", - "integrity": "sha512-OU+i/lcjGpDJv0XkNpsKrI2r1VPp8qX0H6Knq8NuZrlZe3AbvO3jRJJK0pH14xFv8Xok5jbZZpKKoQLxYfxqsw==" - } } }, "@sentry/hub": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.20.1.tgz", - "integrity": "sha512-Nv5BXf14BEc08acDguW6eSqkAJLVf8wki283FczEvTsQZZuSBHM9cJ5Hnehr6n+mr8wWpYLgUUYM0oXXigUmzQ==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.20.0.tgz", + "integrity": "sha512-DiU8fpjAAMOgSx5tsTekMtHPCAtSNWSNS91FFkDCqPn6fYG+/aK/hB5kTlJwr+GTM1815+WWrtXP6y2ecSmZuA==", "requires": { - "@sentry/types": "5.20.1", - "@sentry/utils": "5.20.1", + "@sentry/types": "5.20.0", + "@sentry/utils": "5.20.0", "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/types": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.1.tgz", - "integrity": "sha512-OU+i/lcjGpDJv0XkNpsKrI2r1VPp8qX0H6Knq8NuZrlZe3AbvO3jRJJK0pH14xFv8Xok5jbZZpKKoQLxYfxqsw==" - } } }, "@sentry/minimal": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.20.1.tgz", - "integrity": "sha512-2PeJKDTHNsUd1jtSLQBJ6oRI+xrIJrYDQmsyK/qs9D7HqHfs+zNAMUjYseiVeSAFGas5IcNSuZbPRV4BnuoZ0w==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.20.0.tgz", + "integrity": "sha512-oA+0g7p3bapzjgGKQIkSjcjA85VG1HPmjxBD9wpRvNjmYuVmm80Cl1H/P+xg/hupw/kNmASAX4IOd5Z9pEeboA==", "requires": { - "@sentry/hub": "5.20.1", - "@sentry/types": "5.20.1", + "@sentry/hub": "5.20.0", + "@sentry/types": "5.20.0", "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/types": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.1.tgz", - "integrity": "sha512-OU+i/lcjGpDJv0XkNpsKrI2r1VPp8qX0H6Knq8NuZrlZe3AbvO3jRJJK0pH14xFv8Xok5jbZZpKKoQLxYfxqsw==" - } } }, "@sentry/node": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.20.1.tgz", - "integrity": "sha512-43YFDnD7Rv+vGHV+Fmb3LaSSWrFzsPmFRu3wmf9eYMgWiuDks6c6/kWRCgkqX9Np9ImC89wcTZs/V6S4MlOm4g==", - "requires": { - "@sentry/apm": "5.20.1", - "@sentry/core": "5.20.1", - "@sentry/hub": "5.20.1", - "@sentry/types": "5.20.1", - "@sentry/utils": "5.20.1", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.20.0.tgz", + "integrity": "sha512-xOSP+sWptQff1dQR8G9DCpATT99odsnEpg+X/uqW6bUvjfgsabiPN4nc/orwkTNtm4MhffZiXVq48IAgl/x8Uw==", + "requires": { + "@sentry/apm": "5.20.0", + "@sentry/core": "5.20.0", + "@sentry/hub": "5.20.0", + "@sentry/types": "5.20.0", + "@sentry/utils": "5.20.0", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/types": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.1.tgz", - "integrity": "sha512-OU+i/lcjGpDJv0XkNpsKrI2r1VPp8qX0H6Knq8NuZrlZe3AbvO3jRJJK0pH14xFv8Xok5jbZZpKKoQLxYfxqsw==" - }, - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - } } }, "@sentry/types": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.6.1.tgz", - "integrity": "sha512-Kub8TETefHpdhvtnDj3kKfhCj0u/xn3Zi2zIC7PB11NJHvvPXENx97tciz4roJGp7cLRCJsFqCg4tHXniqDSnQ==", - "dev": true + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.0.tgz", + "integrity": "sha512-/9tiGiXBRsOKM66HeCpt0iSF0vnAIqHzXgC97icNQIstx/ZA8tcLs9540cHDeaN0cyZUyZF1o8ECqcLXGNODWQ==" }, "@sentry/utils": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.20.1.tgz", - "integrity": "sha512-dhK6IdO6g7Q2CoxCbB+q8gwUapDUH5VjraFg0UBzgkrtNhtHLylqmwx0sWQvXCcp14Q/3MuzEbb4euvoh8o8oA==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.20.0.tgz", + "integrity": "sha512-w0AeAzWEf35h9U9QL/4lgS9MqaTPjeSmQYNU/n4ef3FKr+u8HP68Ra7NZ0adiKgi67Yxr652kWopOLPl7CxvZg==", "requires": { - "@sentry/types": "5.20.1", + "@sentry/types": "5.20.0", "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/types": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.1.tgz", - "integrity": "sha512-OU+i/lcjGpDJv0XkNpsKrI2r1VPp8qX0H6Knq8NuZrlZe3AbvO3jRJJK0pH14xFv8Xok5jbZZpKKoQLxYfxqsw==" - } + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" } }, "@types/apicache": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/apicache/-/apicache-1.2.0.tgz", - "integrity": "sha512-8uatdizj2GbYHtS4u+x4k2aG1thG6JBWKRidcnauXav+Bxe3bHsWS8HSwcybuLE2q39/95cwb4hkHvqmP7ja2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/apicache/-/apicache-1.2.2.tgz", + "integrity": "sha512-+rjhMdbx7m7pKnNiQCSiKE21mGmMzsfCU871h5BCj4guhAj423j61Dq0Yrr9CnLiDwUhSKtkXWudr9SQE0/IoA==", "dev": true, "requires": { "@types/redis": "*" } }, "@types/body-parser": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", - "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", "dev": true, "requires": { "@types/connect": "*", @@ -200,39 +160,59 @@ "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", "dev": true }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/connect": { - "version": "3.4.32", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", - "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", "dev": true, "requires": { "@types/node": "*" } }, - "@types/events": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", - "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", - "dev": true + "@types/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-invOmosX0DqbpA+cE2yoHGUlF/blyf7nB0OGYBBiH27crcVm5NmFaZkLP4Ta1hGaesckCi5lVLlydNJCxkTOSg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/engine.io": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz", + "integrity": "sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w==", + "dev": true, + "requires": { + "@types/node": "*" + } }, "@types/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.1.tgz", - "integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==", + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz", + "integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==", "dev": true, "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "*", + "@types/qs": "*", "@types/serve-static": "*" } }, "@types/express-serve-static-core": { - "version": "4.16.9", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz", - "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.9.tgz", + "integrity": "sha512-DG0BYg6yO+ePW+XoDENYz8zhNGC3jDDEpComMYn7WJc4mY1Us8Rw9ax2YhJXxpyk2SF47PQAoQ0YyVT1a0bEkA==", "dev": true, "requires": { "@types/node": "*", + "@types/qs": "*", "@types/range-parser": "*" } }, @@ -254,10 +234,16 @@ "@types/express": "*" } }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/mime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", - "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", "dev": true }, "@types/node": { @@ -266,14 +252,11 @@ "integrity": "sha512-DOzWZKUnmFYG0KUOs+9HEBju2QhBU6oM2zeluunQNt0vnJvnkHvtDNlQPZDkTrkC5pZrNx1TPqeL137zciXZMQ==", "dev": true }, - "@types/node-fetch": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.2.tgz", - "integrity": "sha512-djYYKmdNRSBtL1x4CiE9UJb9yZhwtI1VC+UxZD0psNznrUj80ywsxKlEGAE+QL1qvLjPbfb24VosjkYM6W4RSQ==", - "dev": true, - "requires": { - "@types/node": "*" - } + "@types/qs": { + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz", + "integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==", + "dev": true }, "@types/range-parser": { "version": "1.2.3", @@ -282,12 +265,11 @@ "dev": true }, "@types/redis": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.6.tgz", - "integrity": "sha512-kaSI4XQwCfJtPiuyCXvLxCaw2N0fMZesdob3Jh01W20vNFct+3lfvJ/4yCJxbSopXOBOzpg+pGxkW6uWZrPZHA==", + "version": "2.8.22", + "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.22.tgz", + "integrity": "sha512-O21YLcAtcSzax8wy4CfxMNjIMNf5X2c1pKTXDWLMa2p77Igvy7wuNjWVv+Db93wTvRvLLev6oq3IE7gxNKFZyg==", "dev": true, "requires": { - "@types/events": "*", "@types/node": "*" } }, @@ -304,9 +286,9 @@ } }, "@types/serve-static": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", - "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.4.tgz", + "integrity": "sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug==", "dev": true, "requires": { "@types/express-serve-static-core": "*", @@ -314,11 +296,12 @@ } }, "@types/socket.io": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.2.tgz", - "integrity": "sha512-Ind+4qMNfQ62llyB4IMs1D8znMEBsMKohZBPqfBUIXqLQ9bdtWIbNTBWwtdcBWJKnokMZGcmWOOKslatni5vtA==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.10.tgz", + "integrity": "sha512-1fQMaDU/x2LPljEI/QI5IKl8sBYHM/zv32YYKvNrVEor7/1+MLqMqmWt8Bb8Vpf+PlIPBiTTC0BnrRx7ju3xOw==", "dev": true, "requires": { + "@types/engine.io": "*", "@types/node": "*" } }, @@ -338,6 +321,15 @@ "@types/request": "*" } }, + "@types/ws": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.6.tgz", + "integrity": "sha512-Q07IrQUSNpr+cXU4E4LtkSIBPie5GLZyyMC1QtQYRLWz701+XcoVygGUZgvLqElq1nU4ICldMYPnexlBsg3dqQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -370,11 +362,6 @@ "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-0.1.3.tgz", "integrity": "sha1-npq0PSV+GueE4d9fWAyfUkD1iHQ=" }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" - }, "agent-base": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", @@ -410,12 +397,40 @@ } }, "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", "dev": true, "requires": { - "string-width": "^2.0.0" + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "ansi-escapes": { @@ -469,13 +484,82 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + }, + "dependencies": { + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } } }, "array-indexofobject": { @@ -483,10 +567,83 @@ "resolved": "https://registry.npmjs.org/array-indexofobject/-/array-indexofobject-0.0.1.tgz", "integrity": "sha1-qqEo5iybPDWAlFaMIZ/2T+SJ1Co=" }, - "arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } }, "asn1": { "version": "0.2.4", @@ -517,21 +674,11 @@ "resolved": "https://registry.npmjs.org/async-delay-queue/-/async-delay-queue-1.0.11.tgz", "integrity": "sha512-6hvLDHWGy37Jui7X2yh9HbgoaQrai6h68jUTPdFtpZKFVaYPSknrhrz1ZLZFSaKOCS8R7Sso5LGMPQ36dOREWw==" }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "atom-ui-reporter": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/atom-ui-reporter/-/atom-ui-reporter-0.0.1.tgz", - "integrity": "sha1-Bc83LBdzMXzp42uhRqKfizcCgbw=" - }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -542,26 +689,26 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + "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" + }, + "dependencies": { + "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==" + } + } }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -571,24 +718,16 @@ "tweetnacl": "^0.14.3" } }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "requires": { - "callsite": "1.0.0" - } - }, "binary-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", "dev": true }, - "blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + "bintrees": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", + "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" }, "body-parser": { "version": "1.19.0", @@ -625,18 +764,109 @@ "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" }, "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "brace-expansion": { @@ -658,15 +888,51 @@ "fill-range": "^7.0.1" } }, + "bufferutil": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", + "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", + "optional": true, + "requires": { + "node-gyp-build": "~3.7.0" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } }, "callsites": { "version": "3.1.0", @@ -675,9 +941,9 @@ "dev": true }, "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "camelize": { @@ -685,12 +951,6 @@ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "dev": true - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -737,9 +997,9 @@ } }, "chokidar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", - "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -749,7 +1009,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.3.0" + "readdirp": "~3.4.0" } }, "chroma-js": { @@ -758,15 +1018,15 @@ "integrity": "sha512-jTwQiT859RTFN/vIf7s+Vl/Z2LcMrvMv3WUFmd/4u76AdlFC0NTNgqEEFPcRiHmAswPsMiQEDZLM8vX8qXpZNQ==" }, "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", "dev": true }, "cli-cursor": { @@ -784,6 +1044,15 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", @@ -837,21 +1106,6 @@ "delayed-stream": "~1.0.0" } }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -859,17 +1113,17 @@ "dev": true }, "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", "dev": true, "requires": { - "dot-prop": "^4.1.0", + "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" } }, "confusing-browser-globals": { @@ -910,27 +1164,43 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "requires": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dev": true, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "requires": { - "capture-stack-trace": "^1.0.0" + "object-assign": "^4", + "vary": "^1" } }, "cross-spawn": { @@ -947,9 +1217,9 @@ } }, "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, "css-select": { @@ -1018,6 +1288,15 @@ "ms": "2.0.0" } }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -1030,6 +1309,12 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, "define-properties": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", @@ -1101,14 +1386,20 @@ "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" }, "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "^2.0.0" } }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "dev": true + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -1130,67 +1421,28 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "engine.io": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz", - "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==", - "requires": { - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "0.3.1", - "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", - "ws": "^7.1.2" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "engine.io-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", - "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "ws": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz", - "integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==", - "requires": { - "async-limiter": "^1.0.0" - } - } + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" } }, "entities": { @@ -1240,6 +1492,12 @@ "is-symbol": "^1.0.2" } }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1330,42 +1588,44 @@ } }, "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "requires": { "debug": "^2.6.9", - "resolve": "^1.5.0" + "resolve": "^1.13.1" } }, "eslint-module-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz", - "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", "dev": true, "requires": { - "debug": "^2.6.8", + "debug": "^2.6.9", "pkg-dir": "^2.0.0" } }, "eslint-plugin-import": { - "version": "2.18.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", - "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", + "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", "dev": true, "requires": { - "array-includes": "^3.0.3", + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", "contains-path": "^0.1.0", "debug": "^2.6.9", "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.0", + "eslint-import-resolver-node": "^0.3.3", + "eslint-module-utils": "^2.6.0", "has": "^1.0.3", "minimatch": "^3.0.4", - "object.values": "^1.1.0", + "object.values": "^1.1.1", "read-pkg-up": "^2.0.0", - "resolve": "^1.11.0" + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" }, "dependencies": { "doctrine": { @@ -1463,34 +1723,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } - }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -1550,6 +1782,11 @@ "resolved": "https://registry.npmjs.org/express-async-handler/-/express-async-handler-1.1.4.tgz", "integrity": "sha512-HdmbVF4V4w1q/iz++RV7bUxIeepTukWewiJGkoCKQMtvPF11MLTa7It9PRc/reysXXZSEyD4Pthchju+IUbMiQ==" }, + "express-favicon-short-circuit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/express-favicon-short-circuit/-/express-favicon-short-circuit-1.2.0.tgz", + "integrity": "sha512-9JfQmCu6uXy9GdEODr0XTGiRRqr6mBx+47Q76Z+Zdw7o3eQ3rwZsdsg942GvBs/ui/sQazX6KmvfhY/+e3f7/A==" + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -1613,11 +1850,6 @@ "sax": "~0.6.0" }, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, "readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -1746,9 +1978,9 @@ "dev": true }, "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", "dev": true, "optional": true }, @@ -1803,10 +2035,13 @@ } }, "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } }, "get-svg-colors": { "version": "1.5.1", @@ -1852,12 +2087,12 @@ } }, "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", "dev": true, "requires": { - "ini": "^1.3.4" + "ini": "^1.3.5" } }, "globals": { @@ -1867,28 +2102,28 @@ "dev": true }, "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", "dev": true, "requires": { - "create-error-class": "^3.0.0", + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" } }, "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, "greenkeeper-lockfile": { @@ -1903,9 +2138,9 @@ }, "dependencies": { "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true } } @@ -1933,19 +2168,6 @@ "function-bind": "^1.1.1" } }, - "has-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "requires": { - "isarray": "2.0.1" - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1958,6 +2180,12 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, "helmet": { "version": "3.23.3", "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.23.3.tgz", @@ -2005,9 +2233,9 @@ "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "dev": true }, "hpkp": { @@ -2060,6 +2288,12 @@ } } }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -2204,11 +2438,6 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2316,12 +2545,12 @@ "dev": true }, "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, "requires": { - "ci-info": "^1.5.0" + "ci-info": "^2.0.0" } }, "is-date-object": { @@ -2352,19 +2581,19 @@ } }, "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" } }, "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", "dev": true }, "is-number": { @@ -2374,19 +2603,16 @@ "dev": true }, "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true }, "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true }, "is-promise": { "version": "2.1.0", @@ -2394,12 +2620,6 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -2409,16 +2629,15 @@ "has": "^1.0.1" } }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true - }, "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", "dev": true }, "is-svg": { @@ -2443,10 +2662,16 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "isexe": { "version": "2.0.0", @@ -2485,6 +2710,12 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, "json-fetch-cache": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/json-fetch-cache/-/json-fetch-cache-1.2.6.tgz", @@ -2516,6 +2747,15 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -2527,18 +2767,35 @@ "verror": "1.10.0" } }, + "keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "requires": { + "tsscmp": "1.0.6" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", "dev": true, "requires": { - "package-json": "^4.0.0" + "package-json": "^6.3.0" } }, "levn": { @@ -2574,9 +2831,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash.assignin": { @@ -2674,34 +2931,24 @@ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "lru_map": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" }, "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { - "pify": "^3.0.0" + "semver": "^6.0.0" }, "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -2745,6 +2992,12 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -2755,20 +3008,25 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, + "moment": { + "version": "2.25.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz", + "integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg==" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2838,15 +3096,21 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" }, + "node-gyp-build": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", + "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", + "optional": true + }, "node-md-config": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/node-md-config/-/node-md-config-2.0.1.tgz", "integrity": "sha1-zgydvCjZts8hFSG81Q1+xbzyAp0=" }, "nodemon": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz", - "integrity": "sha512-GWhYPMfde2+M0FsHnggIHXTqPDHXia32HRhh6H0d75Mt9FKUoCBvumNHr7LdrpPBTKxsWmIEOjoN+P4IU6Hcaw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", + "integrity": "sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==", "dev": true, "requires": { "chokidar": "^3.2.2", @@ -2858,7 +3122,7 @@ "supports-color": "^5.5.0", "touch": "^3.1.0", "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" + "update-notifier": "^4.0.0" }, "dependencies": { "debug": { @@ -2911,14 +3175,11 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true }, "nth-check": { "version": "1.0.2", @@ -2938,10 +3199,16 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true }, "object-keys": { "version": "1.0.11", @@ -2991,13 +3258,13 @@ } }, "object.values": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", - "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", "dev": true, "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", + "es-abstract": "^1.17.0-next.1", "function-bind": "^1.1.1", "has": "^1.0.3" }, @@ -3011,6 +3278,57 @@ "object-keys": "^1.0.12" } }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -3078,10 +3396,10 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true }, "p-limit": { @@ -3109,15 +3427,23 @@ "dev": true }, "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", "dev": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "parent-module": { @@ -3146,22 +3472,6 @@ "error-ex": "^1.2.0" } }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "requires": { - "better-assert": "~1.0.0" - } - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3248,9 +3558,9 @@ "dev": true }, "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, "process-nextick-args": { @@ -3264,6 +3574,14 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "prom-client": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.3.tgz", + "integrity": "sha512-iz22FmTbtkyL2vt0MdDFY+kWof+S9UB/NACxSn2aJcewtw+EERsen0urSkZ2WrHseNdydsvcxCTAnPcSMZZv4Q==", + "requires": { + "tdigest": "^0.1.1" + } + }, "proxy-addr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", @@ -3273,28 +3591,41 @@ "ipaddr.js": "1.9.0" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" }, "pstree.remy": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", - "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -3331,14 +3662,6 @@ "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } } }, "read-pkg": { @@ -3373,12 +3696,12 @@ } }, "readdirp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", - "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", "dev": true, "requires": { - "picomatch": "^2.0.7" + "picomatch": "^2.2.1" } }, "referrer-policy": { @@ -3393,22 +3716,21 @@ "dev": true }, "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", + "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", "dev": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "^1.2.8" } }, "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", "dev": true, "requires": { - "rc": "^1.0.1" + "rc": "^1.2.8" } }, "request": { @@ -3452,9 +3774,9 @@ "dev": true }, "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -3466,6 +3788,15 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -3491,12 +3822,60 @@ } }, "rss-feed-emitter": { - "version": "github:filipedeschamps/rss-feed-emitter#0c6beac0ca361265fb5c7a3d60955bed1369b9cc", - "from": "github:filipedeschamps/rss-feed-emitter#0c6beac", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/rss-feed-emitter/-/rss-feed-emitter-3.2.0.tgz", + "integrity": "sha512-Q9K0LvGuPHr/K3hRxCvrMOqLWg5CyZNQVZ9c/9Kg7KdnjeWze8cyP0Q33TjX0jisMybFMFjgjuAkvlLykPnUnw==", "requires": { - "atom-ui-reporter": "0.0.1", "feedparser": "1.1.4", - "request": "^2.88.0" + "request": "^2.88.2" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, "run-async": { @@ -3539,12 +3918,20 @@ "dev": true }, "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", "dev": true, "requires": { - "semver": "^5.0.3" + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "send": { @@ -3637,131 +4024,10 @@ "is-fullwidth-code-point": "^2.0.0" } }, - "socket.io": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", - "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", - "requires": { - "debug": "~4.1.0", - "engine.io": "~3.4.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.3.0", - "socket.io-parser": "~3.4.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "engine.io-client": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz", - "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==", - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "~6.1.0", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - } - }, - "engine.io-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", - "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "socket.io-client": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", - "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "engine.io-client": "~3.4.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.3.0", - "to-array": "0.1.4" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "socket.io-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", - "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", - "requires": { - "component-emitter": "1.2.1", - "debug": "~3.1.0", - "isarray": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } - } - } - }, - "socket.io-parser": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz", - "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==", - "requires": { - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "isarray": "2.0.1" - } - } - } - }, - "socket.io-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", - "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" - }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -3769,15 +4035,15 @@ } }, "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "dev": true }, "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -3832,6 +4098,162 @@ "strip-ansi": "^4.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -3855,12 +4277,6 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -3876,6 +4292,53 @@ "has-flag": "^3.0.0" } }, + "swagger-stats": { + "version": "0.95.17", + "resolved": "https://registry.npmjs.org/swagger-stats/-/swagger-stats-0.95.17.tgz", + "integrity": "sha512-3p+sWzWpxWES5SAM6GeKa6jWrMvh5gmczKdscdmOl4e6n9e4gfz0HioBZQqRgdLKgRR2ZeSR4f7+e3mMjqcEhQ==", + "requires": { + "basic-auth": "^2.0.1", + "cookies": "^0.8.0", + "debug": "^4.1.1", + "moment": "^2.24.0", + "path-to-regexp": "^6.1.0", + "prom-client": "^11.5.3", + "qs": "^6.9.1", + "request": "^2.88.0", + "send": "^0.17.1", + "uuid": "^3.4.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "path-to-regexp": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.1.0.tgz", + "integrity": "sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==" + }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, "table": { "version": "5.4.4", "resolved": "https://registry.npmjs.org/table/-/table-5.4.4.tgz", @@ -3928,15 +4391,20 @@ } } }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "dev": true, + "tdigest": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", + "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", "requires": { - "execa": "^0.7.0" + "bintrees": "1.0.1" } }, + "term-size": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", + "dev": true + }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -3953,12 +4421,6 @@ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -3968,10 +4430,11 @@ "os-tmpdir": "~1.0.2" } }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true }, "to-regex-range": { "version": "5.0.1", @@ -4010,11 +4473,28 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" }, + "tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4053,6 +4533,12 @@ "prelude-ls": "~1.1.2" } }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -4062,6 +4548,15 @@ "mime-types": "~2.1.24" } }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "undefsafe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", @@ -4077,12 +4572,12 @@ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" }, "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", "dev": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "^2.0.0" } }, "unpipe": { @@ -4090,28 +4585,77 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", + "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", "dev": true, "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "uri-js": { @@ -4130,12 +4674,21 @@ } }, "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "^2.0.0" + } + }, + "utf-8-validate": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", + "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", + "optional": true, + "requires": { + "node-gyp-build": "~3.7.0" } }, "util-deprecate": { @@ -4179,9 +4732,9 @@ } }, "warframe-items": { - "version": "1.1043.0", - "resolved": "https://registry.npmjs.org/warframe-items/-/warframe-items-1.1043.0.tgz", - "integrity": "sha512-q0257yCAkFarv6BRSsS4flxEyQeTeeNVbNDu244wHd7rTR028yURPLs834gtsGkhYKu1/QHZWyQqjfM4BtWPIA==" + "version": "1.1016.0", + "resolved": "https://registry.npmjs.org/warframe-items/-/warframe-items-1.1016.0.tgz", + "integrity": "sha512-UNfd8z2y4aWApwnCK15KzLOcjTbe1xiJ3ZESsf2sC5rSLkmEG2/cCpMGObf43jFcBTvXRX0Bv5occTokXoiszQ==" }, "warframe-nexus-query": { "version": "1.6.13", @@ -4196,13 +4749,6 @@ "node-fetch": "^2.6.0", "node-md-config": "^2.0.1", "numeral": "^2.0.6" - }, - "dependencies": { - "json-fetch-cache": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/json-fetch-cache/-/json-fetch-cache-1.2.5.tgz", - "integrity": "sha512-+PQVFQcgSxzGWecb3QFDdEtluI5GgV71NB8PlbylL8b8ooFxly5VqbsoJ8+Hd5ota2D6mSmZM06gHEqZUCI8zg==" - } } }, "warframe-worldstate-data": { @@ -4231,12 +4777,52 @@ } }, "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", "dev": true, "requires": { - "string-width": "^2.1.1" + "string-width": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } } }, "winston": { @@ -4253,13 +4839,6 @@ "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.4.0" - }, - "dependencies": { - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - } } }, "winston-transport": { @@ -4298,6 +4877,21 @@ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, + "worldstate-emitter": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/worldstate-emitter/-/worldstate-emitter-1.0.2.tgz", + "integrity": "sha512-ewuxtPh7SwOP+IJ8Bg8t2u05ajE/KX3rbdCUIS0T/cbQSQxho2mjoeVcnALSkSt4RKsTPvEpGFg/+KPaQp6kGQ==", + "requires": { + "@sentry/node": "^5.18.1", + "colors": "^1.4.0", + "json-fetch-cache": "^1.2.6", + "rss-feed-emitter": "^3.2.0", + "twitter": "^1.7.1", + "warframe-worldstate-data": "^1.7.10", + "warframe-worldstate-parser": "^2.14.2", + "winston": "^3.3.3" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4314,23 +4908,21 @@ } }, "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "ws": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", - "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", - "requires": { - "async-limiter": "~1.0.0" - } + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" }, "x-xss-protection": { "version": "1.3.0", @@ -4338,26 +4930,10 @@ "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" }, "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", - "dev": true - }, - "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" } } } diff --git a/package.json b/package.json index 0af221c8a..61d10476e 100644 --- a/package.json +++ b/package.json @@ -1,49 +1,58 @@ { "name": "warframe-status", "version": "1.0.2", - "author": "nspacestd", + "author": "tobitenno", "repository": "https://github.com/wfcd/warframe-status.git", "dependencies": { - "@sentry/node": "^5.20.1", + "@sentry/node": "^5.20.0", "apicache": "^1.5.3", "colors": "^1.4.0", + "cors": "^2.8.5", "express": "^4.17.1", "express-async-handler": "^1.1.3", + "express-favicon-short-circuit": "^1.1.1", "helmet": "^3.23.3", "json-fetch-cache": "^1.2.6", "nexushub-client": "^1.2.0", - "rss-feed-emitter": "github:filipedeschamps/rss-feed-emitter#0c6beac", - "socket.io": "^2.3.0", - "twitter": "^1.7.1", - "warframe-items": "^1.1043.0", + "swagger-stats": "^0.95.11", + "warframe-items": "^1.1016.0", "warframe-nexus-query": "^1.6.13", "warframe-worldstate-data": "^1.7.13", "warframe-worldstate-parser": "^2.14.2", - "winston": "^3.3.3" + "winston": "^3.3.3", + "worldstate-emitter": "^1.0.2", + "ws": "^7.3.1" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" }, "devDependencies": { - "@sentry/types": "^5.6.1", - "@types/apicache": "^1.2.0", - "@types/express": "^4.17.1", + "@sentry/types": "^5.20.0", + "@types/apicache": "^1.2.2", + "@types/cors": "^2.8.6", + "@types/express": "^4.17.7", "@types/helmet": "0.0.43", - "@types/node-fetch": "^2.5.2", - "@types/socket.io": "^2.1.2", + "@types/socket.io": "^2.1.10", "@types/twitter": "^1.7.0", + "@types/ws": "^7.2.6", + "dotenv": "^8.2.0", "eslint": "^5.16.0", "eslint-config-airbnb-base": "^13.2.0", - "eslint-plugin-import": "^2.18.2", + "eslint-plugin-import": "^2.22.0", "greenkeeper-lockfile": "^1.15.1", - "nodemon": "^2.0.2" + "nodemon": "^2.0.4" }, "scripts": { - "start": "node server.js", + "start": "node src/main.js", "dev": "nodemon", "dev:pm2": "pm2 start warframe-status.json", - "lint": "npx eslint server.js lib/", - "lint:fix": "npx eslint server.js lib/ --fix", + "lint": "npx eslint src/", + "lint:fix": "npx eslint src/ --fix", "test": "exit 0", "snyk-protect": "npx snyk protect", - "prepare": "npm run snyk-protect" + "prepare": "npm run snyk-protect", + "update:spec": "curl --url https://docs.warframestat.us/openapi.json -G -o src/api-spec/openapi.json --silent" }, "engines": { "node": "11.15.0", @@ -51,11 +60,16 @@ }, "license": "Apache-2.0", "nodemonConfig": { - "ignore": [], + "ignore": [ + "**/test/**.**" + ], "env": { - "LOG_LEVEL": "info", + "LOG_LEVEL": "verbose", "API_BASE_URL": "http://localhost:3001", - "NODE_ENV": "production" + "NODE_ENV": "development", + "PORT": 3001, + "CACHE_TIMEOUT": 30000, + "DISABLE_PRICECHECKS": "true" } }, "snyk": true diff --git a/server.js b/server.js deleted file mode 100644 index 6fdde5dee..000000000 --- a/server.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -const express = require('express'); - -const helmet = require('helmet'); - -const app = express(); -const server = require('http').Server(app); -const io = require('socket.io')(server); - -const { logger, socketLogger } = require('./lib/utilities'); - -if (!global.__basedir) { - global.__basedir = __dirname; -} - -logger.verbose('Setting up dependencies...'); - -/* Express setup */ -if (process.env.SENTRY_DSN) { - // eslint-disable-next-line global-require - const Sentry = require('@sentry/node'); - Sentry.init({ dsn: process.env.SENTRY_DSN }); - app.use(Sentry.Handlers.requestHandler()); - app.use(Sentry.Handlers.errorHandler()); -} - -app.use(helmet()); -app.use(express.json()); - -logger.verbose('Setting up routes...'); -app.use(require('./controllers')); - -// oh no, nothing -app.use((req, res) => { - res.status(404).json({ error: 'No such route.', code: 404 }).end(); -}); - -const port = process.env.PORT || 3001; -const host = process.env.HOSTNAME || process.env.HOST || process.env.IP || 'localhost'; -server.listen(port, host); - -socketLogger.verbose(`Started listening on ${host}:${port}`); - -io.on('connection', require('./sockets')); diff --git a/sockets/index.js b/sockets/index.js deleted file mode 100644 index 2aa0970c4..000000000 --- a/sockets/index.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -const { socketLogger: logger } = require('../lib/utilities'); - -const index = (socket) => { - // initial connection - socket.emit('connected', { status: 200 }); - logger.verbose(`socket ${socket.id} connected`); - - require('./worldstate')(socket); - require('./twitter')(socket); - require('./rss')(socket); - - socket.on('disconnect', (reason) => { - logger.warn(`socket ${socket.id} disconnected because ${reason}`); - }); -}; - -module.exports = index; diff --git a/sockets/rss.js b/sockets/rss.js deleted file mode 100644 index 5bef65835..000000000 --- a/sockets/rss.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -const { socketLogger: logger } = require('../lib/utilities'); -const rss = require('../lib/caches/RSSSocketEmitter'); - -const rssSock = (socket) => { - rss.on('new-rss', (rssItem) => { - logger.verbose(`Emitting 'rss' to ${socket.id}: ${rssItem.timestamp}`); - socket.emit('rss', rssItem); - }); -}; - -module.exports = rssSock; diff --git a/sockets/twitter.js b/sockets/twitter.js deleted file mode 100644 index 20ba4bede..000000000 --- a/sockets/twitter.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -const { socketLogger: logger } = require('../lib/utilities'); -const twitter = require('../lib/caches/TwitterCache'); - -const twitSock = (socket) => { - twitter.on('tweet', (tweet) => { - logger.debug(`Emitting 'tweet' to ${socket.id}: ${tweet.uniqueId}`); - socket.emit('tweet', tweet); - }); -}; - -module.exports = twitSock; diff --git a/sockets/worldstate/events/cycleLike.js b/sockets/worldstate/events/cycleLike.js deleted file mode 100644 index 2cfb4af6c..000000000 --- a/sockets/worldstate/events/cycleLike.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -const { - emit, between, lastUpdated, fromNow, -} = require('../wsSocketUtils'); - -module.exports = (cycleData, deps) => { - const packet = { - ...deps, - data: cycleData, - eventKey: `${deps.key.replace('Cycle', '')}.${cycleData.state}`, - }; - - const last = new Date(lastUpdated[deps.platform][deps.language]); - const activation = new Date(cycleData.activation); - const start = new Date(deps.cycleStart); - - if (between(last, activation, start)) { - emit(packet); - } - - const timePacket = { - ...packet, - eventKey: `${packet.eventKey}.${Math.round(fromNow(deps.data.expiry) / 60000)}`, - }; - emit(timePacket); -}; diff --git a/sockets/worldstate/events/nightwave.js b/sockets/worldstate/events/nightwave.js deleted file mode 100644 index 1638f6572..000000000 --- a/sockets/worldstate/events/nightwave.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -module.exports = (nightwave, deps) => { - const groups = { - daily: [], - weekly: [], - elite: [], - }; - - nightwave.activeChallenges.forEach((challenge) => { - if (challenge.isDaily) { - groups.daily.push(challenge); - } else if (challenge.isElite) { - groups.elite.push(challenge); - } else { - groups.weekly.push(challenge); - } - }); - - Object.keys(groups).forEach((group) => { - require('./objectLike')({ - ...nightwave, - activeChallenges: groups[group], - }, { - ...deps, - eventKey: `nightwave.${group}`, - }); - }); -}; diff --git a/sockets/worldstate/events/objectLike.js b/sockets/worldstate/events/objectLike.js deleted file mode 100644 index 3fa754a53..000000000 --- a/sockets/worldstate/events/objectLike.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -const { emit, between, lastUpdated } = require('../wsSocketUtils'); - -module.exports = (data, deps) => { - if (!data) return; - const last = new Date(lastUpdated[deps.platform][deps.language]); - const activation = new Date(data.activation); - const start = new Date(deps.cycleStart); - - if (between(last, activation, start)) { - const packet = { - ...deps, - data, - eventKey: deps.eventKey || deps.key, - }; - emit(packet); - } -}; diff --git a/sockets/worldstate/index.js b/sockets/worldstate/index.js deleted file mode 100644 index e12656a6e..000000000 --- a/sockets/worldstate/index.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -const { worldStates, socketLogger: logger, warframeData: { locales } } = require('../../lib/utilities'); - -const worldstate = (socket) => { - // on worldstate updates - const keys = Object.keys(worldStates); - keys.forEach((platform) => { - locales.forEach((locale) => { - worldStates[platform][locale].on('update', (data) => { - const packet = { platform, data, language: locale }; - socket.emit('ws-update', packet); - require('./parseEvents')({ worldstate: data, platform, socket }); - }); - }); - }); - - socket.on('ws-init', async ({ platform, language }) => { - logger.debug(`socket ${socket.id} sent 'ws-init'`); - socket.emit('ws-supply', { - platform, - language, - ws: await worldStates[platform || 'pc'][language || 'en'].data, - }); - }); -}; - -module.exports = worldstate; diff --git a/sockets/worldstate/parseEvents.js b/sockets/worldstate/parseEvents.js deleted file mode 100644 index da3604c1f..000000000 --- a/sockets/worldstate/parseEvents.js +++ /dev/null @@ -1,131 +0,0 @@ -'use strict'; - -const { lastUpdated, logger } = require('./wsSocketUtils'); -const { groupBy } = require('../../lib/utilities'); - -function fissureKey(fissure) { - return `fissures.t${fissure.tierNum}.${(fissure.missionType || '').toLowerCase()}`; -} - -function acolyteKey(acolyte) { - return { - eventKey: `enemies${acolyte.isDiscovered ? '' : '.departed'}`, - activation: acolyte.lastDiscoveredAt, - }; -} - -function arbiKey(arbitration) { - if (!(arbitration && arbitration.enemy)) return ''; - - let k; - try { - k = `arbitration.${arbitration.enemy.toLowerCase()}.${arbitration.type.replace(/\s/g, '').toLowerCase()}`; - } catch (e) { - logger.error(`${JSON.stringify(arbitration)}\n${e}`); - } - return k; -} - -const eKeyOverrides = { - events: 'operations', - persistentEnemies: 'enemies', - fissures: fissureKey, - enemies: acolyteKey, - arbitration: arbiKey, -}; - -const checkOverrides = (key, data) => { - if (typeof eKeyOverrides[key] === 'string') { - return eKeyOverrides[key]; - } - if (typeof eKeyOverrides[key] === 'function') { - return eKeyOverrides[key](data); - } - return key; -}; - -const parseNew = (deps) => { - if (!lastUpdated[deps.platform][deps.language]) { - lastUpdated[deps.platform][deps.language] = deps.cycleStart; - } - - // anything in the eKeyOverrides goes first, then anything uniform - switch (deps.key) { - case 'kuva': - if (!deps.data) break; - const data = groupBy(deps.data, 'type'); - Object.keys(data).forEach((type) => { - deps = { - ...deps, - data: data[type], - eventKey: `kuva.${data[type][0].type.replace(/\s/g, '').toLowerCase()}`, - }; - require('./events/objectLike')(deps.data, deps); - }); - break; - case 'events': - deps = { - ...deps, - eventKey: eKeyOverrides[deps.key], - }; - case 'alerts': - case 'conclaveChallenges': - case 'dailyDeals': - case 'flashSales': - case 'fissures': - case 'globalUpgrades': - case 'invasions': - case 'syndicateMissions': - case 'weeklyChallenges': - // arrayLike are all just arrays of objectLike - deps.data.forEach((arrayItem) => { - const k = checkOverrides(deps.key, arrayItem); - require('./events/objectLike')(arrayItem, { - ...deps, - eventKey: k, - }); - }); - break; - case 'cetusCycle': - case 'earthCycle': - case 'vallisCycle': - // these need special logic to make sure the extra time events fire - require('./events/cycleLike')(deps.data, deps); - break; - case 'sortie': - case 'voidTrader': - case 'arbitration': - // pretty straightforward, make sure the activation - // is between the last update and current cycle start - deps.eventKey = checkOverrides(deps.key, deps.data); - require('./events/objectLike')(deps.data, deps); - break; - case 'nightwave': - require('./events/nightwave')(deps.data, deps); - break; - case 'persistentEnemies': - // uhhh, gotta find a good activation for this.... - // might just have to send it all the time? - deps = { - ...deps, - ...checkOverrides(deps.key, deps.data), - }; - require('./events/objectLike')(deps.data, deps); - default: - break; - } -}; - -const parseEvents = ({ - socket, worldstate, platform, language = 'en', -}) => { - const cycleStart = Date.now(); - Object.keys(worldstate).forEach(async (key) => { - parseNew({ - data: worldstate[key], key, language, platform, socket, cycleStart, - }); - }); - lastUpdated[platform][language] = Date.now(); -}; - -module.exports = parseEvents; diff --git a/sockets/worldstate/wsSocketUtils.js b/sockets/worldstate/wsSocketUtils.js deleted file mode 100644 index e898c8c75..000000000 --- a/sockets/worldstate/wsSocketUtils.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; - -const { socketLogger: logger } = require('../../lib/utilities'); - - -/** - * Validate that b is between a and c - * @param {Date} a The first Date, should be the last time things were updated - * @param {Date} b The second Date, should be the activation time of an event - * @param {Date} c The third Date, should be the start time of this update cycle - * @returns {boolean} if b is between a and c - */ -const between = (a, b, c = new Date()) => ((b > a) && (b < c)); - -/** - * Emit an ws event with common emit event - * @param {Object} data worldstate data to emit - * @param {string} key key of worldstate - * @param {string} platform platform it was emitted for - * @param {string} language language it was emitted for - * @param {SocketIO.Server} socket socket to emit from - * @param {string} eventKey special key, such as `enemies.departed` - */ -const emit = ({ - data, key, platform, language, socket, eventKey, -}) => { - logger.debug(`emitting ${key} as ${eventKey} for ${language}`); - socket.emit('ws', { - event: key, platform, language, data, eventKey, - }); -}; - -/** - * Returns the number of milliseconds between now and a given date - * @param {string} d The date from which the current time will be subtracted - * @param {function} [now] A function that returns the current UNIX time in milliseconds - * @returns {number} - */ -function fromNow(d, now = Date.now) { - return new Date(d).getTime() - now(); -} - -/** - * Map of last updated dates/times - * @type {Object} - */ -const lastUpdated = { - pc: { - en: Date.now(), - }, - ps4: { - en: Date.now(), - }, - xb1: { - en: Date.now(), - }, - swi: { - en: Date.now(), - }, -}; - -module.exports = { - between, emit, lastUpdated, fromNow, logger, -}; diff --git a/src/api-spec/openapi.json b/src/api-spec/openapi.json new file mode 100644 index 000000000..f08d8daf6 --- /dev/null +++ b/src/api-spec/openapi.json @@ -0,0 +1,6682 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "WarframeStat.us API", + "description": "Simple API for data from the game Warframe.", + "contact": { + "email": "wf-com-dev@warframestat.us" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "living", + "x-logo": { + "url": "https://warframestat.us/wfcd_logo_color.png" + } + }, + "externalDocs": { + "description": "Find out more about Warframe", + "url": "https://warframe.com" + }, + "servers": [ + { + "url": "https://api.warframestat.us/", + "description": "Preferred production server. Others may be disallowed in the future." + }, + { + "url": "http://api.warframestat.us/", + "description": "Non-https production server" + }, + { + "url": "https://ws.warframestat.us/", + "description": "Deprecated (old) production subdomain" + }, + { + "url": "http://ws.warframestat.us/", + "description": "Deprecated (old) production subdomain, no https" + } + ], + "tags": [ + { + "name": "Warframe", + "description": "Everything about Warframe", + "externalDocs": { + "description": "Find out more", + "url": "https://warframe.com" + } + }, + { + "name": "Worldstate", + "description": "Warframe Worldstate Data", + "externalDocs": { + "description": "Find out more", + "url": "https://discord.gg/jGZxH9f" + } + } + ], + "paths": { + "/{platform}": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get Warframe Worldstate Data for the provided platform", + "description": "The full translated Warframe Worldstate", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ws" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/alerts": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Alerts data", + "description": "Description and rewards for Alerts", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/alert" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/arbitration": { + "get": { + "tags": [ + "Worldstate", + "Unstable" + ], + "summary": "[Unstable] Arbitration data", + "description": "Description of the currently active arbitration", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/arbitration" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/cetusCycle": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get Current Cetus Status", + "description": "Data on the Day/night cycle for Cetus on earth", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/cetusCycle" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/conclaveChallenges": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get Conclave Challenge Data", + "description": "Data on each day and week's conclave challenges", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/conclaveChallenges" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/constructionProgress": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get Construction Progress for Fomorians and Razorbacks", + "description": "Construction percentages for showing how far constructed the enemy fleets are.", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/construction" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/dailyDeals": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Daily Deal information from Darvo", + "description": "Darvo's Daily Deal details", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dailyDeals" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/darkSectors": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Dark Sector occupation and history", + "description": "Dark Sector (Rail Wars) data and history. Digital Extremes has emptied several of these.", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/darkSectors" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + }, + "deprecated": true + } + }, + "/{platform}/earthCycle": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get the current Earth rotation information", + "description": "The current Earth day/night cycle progress.", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/earthCycle" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/events": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Listing of ongoing events", + "description": "Events, such as Fomorian Attacks are included here", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/events" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/fissures": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Data on current fissures", + "description": "Information about current Void Fissure missions", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/fissures" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/flashSales": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Current Flash Sales from Darvo", + "description": "Popular Deals, discounts, featured deals.", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/flashSales" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/globalUpgrades": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Current Global Upgrades", + "description": "Any current modifiers applied to all users, such as double drops.", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/globalUpgrades" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/invasions": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Invasion Data", + "description": "Data on invasion missions, such as estimated completion time, rewards, etc.", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/invasions" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/kuva": { + "get": { + "tags": [ + "Worldstate", + "Unstable" + ], + "summary": "[Unstable] Current Kuva Mission listing", + "description": "Current Kuva Mission listing (provided by [semlar](https://10o.io/kuvalog)).", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/kuva" + } + } + } + } + } + } + } + }, + "/{platform}/news": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Current Listing of News items", + "description": "Translated News items from the worldstate", + "parameters": [ + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/news" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/nightwave": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get the current Nightwave state.", + "description": "The Current cycle and challenges of Nightwave, a battle-pass-esque rotation and challenge system", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/nightwave" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/persistentEnemies": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get Persistent Enemy Data", + "description": "Data about current acolytes attacking the Sol System", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/persistentEnemies" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/rivens": { + "get": { + "tags": [ + "Worldstate", + "Rivens" + ], + "summary": "Get Riven statistic data", + "description": "Data about averages, deviations, medians, miniums, and maxes for all rivens for the provided platform", + "parameters": [ + { + "$ref": "#/components/parameters/language" + }, + { + "$ref": "#/components/parameters/platform" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rivenType" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/rivens/search/{query}": { + "get": { + "tags": [ + "Worldstate", + "Rivens" + ], + "summary": "Get Riven statistic data", + "description": "Data about averages, deviations, medians, miniums, and maxes for rivens whose name match the query for the provided platform", + "parameters": [ + { + "$ref": "#/components/parameters/platform" + }, + { + "name": "query", + "in": "path", + "description": "Riven name to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/rivenType" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/sentientOutposts": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get the current Sentient Outpost, if any", + "description": "Status data for current Sentient Outpost, if any. Parsed source is combined data from DE\\'s worldstate and [semlar\\'s data](https://semlar.com/anomaly.json)", + "parameters": [ + { + "$ref": "#/components/parameters/platform" + }, + { + "$ref": "#/components/parameters/language" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "mission": { + "$ref": "#/components/schemas/mission" + }, + "active": { + "type": "boolean", + "description": "Whether or not the outpost is active" + }, + "id": { + "type": "string", + "description": "Identifier for the mission node with active indicator" + }, + "activation": { + "type": "string", + "format": "date-time", + "description": "When the mission became or becomes active" + }, + "expiry": { + "type": "string", + "format": "date-time", + "description": "When the mission became or becomes inactive" + }, + "previous": { + "description": "Estimation data for the last mission that was active. Could also be the current.", + "type": "object", + "properties": { + "activation": { + "type": "string", + "format": "date-time", + "description": "When the mission became or becomes active" + }, + "expiry": { + "type": "string", + "format": "date-time", + "description": "When the mission became or becomes inactive" + } + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/simaris": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get the current Sanctuary Status", + "description": "Status data for Simaris' Sanctuary", + "parameters": [ + { + "$ref": "#/components/parameters/platform" + }, + { + "$ref": "#/components/parameters/language" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/simaris" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/sortie": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Current Sortie Data", + "description": "Data about the missions for the current sortie", + "parameters": [ + { + "$ref": "#/components/parameters/platform" + }, + { + "$ref": "#/components/parameters/language" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/sortie" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/syndicateMissions": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Listing of Syndicate mission nodes", + "description": "Cycling through different nodes each day, these are a general listing of the nodes that each syndicate will use for the day.", + "parameters": [ + { + "$ref": "#/components/parameters/platform" + }, + { + "$ref": "#/components/parameters/language" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/syndicateMissions" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/timestamp": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get the timestamp that the current worldstate was generated at.", + "description": "The time that the worldstate was last generated", + "parameters": [ + { + "$ref": "#/components/parameters/platform" + }, + { + "$ref": "#/components/parameters/language" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/timestamp" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/vallisCycle": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get the current state of the Orb Vallis", + "description": "The current cycle of the Orb Vallis warm/cold cycle", + "parameters": [ + { + "$ref": "#/components/parameters/platform" + }, + { + "$ref": "#/components/parameters/language" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vallisCycle" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/{platform}/voidTrader": { + "get": { + "tags": [ + "Worldstate" + ], + "summary": "Get the current Void Trader Information", + "description": "Information on the current Void Trader offerings, or when he will arrive.", + "parameters": [ + { + "$ref": "#/components/parameters/platform" + }, + { + "$ref": "#/components/parameters/language" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "Content-Language": { + "$ref": "#/components/headers/Content-Language" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/voidTrader" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/arcanes": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Arcane Enhancement Data", + "description": "Available Arcane Enhancements", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/arcane" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/arcanes/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Arcane Enhancement Data based on the query", + "description": "Available Arcane Enhancements", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/arcane" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/conclave": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get conclave challenge data", + "description": "Data about conclave challenges", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/conclave" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/conclave/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get conclave challenge data based on the query", + "description": "Data about conclave challenges", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/conclave" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/drops/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Warframe Drops data", + "description": "Percentages for Warframe drops in different areas of the game", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/drops" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/events": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Event-specific Data", + "description": "Data about events", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/events/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Event-specific Data based on the query", + "description": "Data about events", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/factions": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Faction translation information.", + "description": "Strings for translating faction identifiers.", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/factions" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/factions/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Faction translation information based on the query.", + "description": "Strings for translating faction identifiers.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/factions" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/fissureModifiers": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Fissure Modifier translation data.", + "description": "Fissure translation identifiers", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/fissureModifiers" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/fissureModifiers/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Fissure Modifier translation data based on the query.", + "description": "Fissure translation identifiers", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/fissureModifiers" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/items/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Warframe Items data", + "description": "Item information, such as name, unique name, type, and image name.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/items" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/languages": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Language strings for Warframe.", + "description": "Get language strings to assist translation. (Prefer the /languages/search/:query route)", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/languages" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/languages/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Language strings for Warframe based on the query.", + "description": "Get language strings to assist translation.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/languages" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/locales": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Full list of supported locales", + "description": "Locales supported by the API", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/language" + } + } + } + } + } + } + } + }, + "/missionTypes": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get MissionType Translation Keys", + "description": "Mission Type information to aid translating identifiers", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/missionTypes" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/missionTypes/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get MissionType Translation Keys based on the query", + "description": "Mission Type information to aid translating identifiers", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/missionTypes" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/operationTypes": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get operation types data.", + "description": "Operation Types information to aid translating identifiers for global upgrades", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/operationTypes" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/operationTypes/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get operation types data based on the query.", + "description": "Operation Types information to aid translating identifiers for global upgrades", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/operationTypes" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/persistentEnemy": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Persistent Enemy translation data.", + "description": "Persistent Enemy translation information for aiding translation of identifiers.", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/persistentEnemies" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/persistentEnemy/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Persistent Enemy translation data based on the query.", + "description": "Persistent Enemy translation information for aiding translation of identifiers.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/persistentEnemies" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/solNodes": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Sol Node information and translation data.", + "description": "Sol Node translation information for aiding the translation of identifiers.", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/solNode" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/solNodes/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Sol Node information and translation data based on the query.", + "description": "Sol Node translation information for aiding the translation of identifiers.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/solNodeSearch" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/sortie": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Sortie translation information.", + "description": "Sortie translation information for assisting translation of identifiers.", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/sortieData" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/sortie/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Sortie translation information based on the query.", + "description": "Sortie translation information for assisting translation of identifiers.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/sortieData" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/syndicates": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Syndicate translation data.", + "description": "Information to assist translating syndicate identifiers.", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/syndicates" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/syndicates/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Syndicate translation data based on the query.", + "description": "Information to assist translating syndicate identifiers.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/syndicates" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/tutorials": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Tutorials Data", + "description": "Tutorials data from DE", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tutorials" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/tutorials/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Tutorials Data based on the query", + "description": "Tutorials data from DE", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tutorials" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/upgradeTypes": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get upgrade types data for global upgrades.", + "description": "Upgrade types for what can be changed by global modifiers, such as double credit weekends.", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/upgradeTypes" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/upgradeTypes/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get upgrade types data for global upgrades based on the query.", + "description": "Upgrade types for what can be changed by global modifiers, such as double credit weekends.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/upgradeTypes" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/warframes": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Warframe specs and data, such as polarities defenses, and profile.", + "description": "Warframe stats and general information.", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/warframes" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/warframes/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Warframe specs and data, such as polarities defenses, and profile based on the query.", + "description": "Warframe stats and general information.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/warframes" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/weapons": { + "get": { + "tags": [ + "Static Processing Data" + ], + "summary": "Get Weapon data and statistics.", + "description": "Weapon statistics.", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/weapons" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/weapons/search/{query}": { + "get": { + "tags": [ + "Searchable" + ], + "summary": "Get Weapon data and statistics based on the query.", + "description": "Weapon statistics.", + "parameters": [ + { + "name": "query", + "in": "path", + "description": "Keyword to search for", + "required": true, + "schema": { + "type": "string", + "format": "utf8" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/weapons" + } + } + } + }, + "400": { + "$ref": "#/components/responses/error" + }, + "500": { + "$ref": "#/components/responses/error" + } + } + } + } + }, + "components": { + "schemas": { + "alert": { + "type": "object", + "properties": { + "mission": { + "$ref": "#/components/schemas/mission" + }, + "expired": { + "type": "boolean" + }, + "eta": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "expiry": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "activation": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "rewardTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/rewardType" + } + } + }, + "required": [ + "activation", + "eta", + "expired", + "expiry", + "id" + ] + }, + "arbitration": { + "type": "object", + "properties": { + "activation": { + "type": "string", + "format": "date-time", + "description": "When the Arbitration becomes active" + }, + "expiry": { + "type": "string", + "format": "date-time", + "description": "When the Arbitration becomes inactive" + }, + "node": { + "type": "string", + "description": "Plain name for the node" + }, + "enemy": { + "$ref": "#/components/schemas/faction" + }, + "type": { + "type": "string", + "description": "Mission type" + }, + "archwing": { + "type": "boolean", + "description": "Whether or not this mission requires archwing" + }, + "sharkwing": { + "type": "boolean", + "description": "Whether or not this mission requires sharkwing" + } + } + }, + "arcane": { + "required": [ + "effect", + "info", + "location", + "name", + "rarity", + "regex", + "thumbnail" + ], + "type": "object", + "properties": { + "regex": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "effect": { + "minLength": 1, + "type": "string" + }, + "rarity": { + "minLength": 1, + "type": "string" + }, + "location": { + "minLength": 1, + "type": "string" + }, + "thumbnail": { + "minLength": 1, + "type": "string" + }, + "info": { + "minLength": 1, + "type": "string" + } + } + }, + "cetusCycle": { + "required": [ + "expiry", + "id", + "isCetus", + "isDay", + "timeLeft" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "expiry": { + "minLength": 1, + "type": "string" + }, + "activation": { + "type": "string" + }, + "isDay": { + "type": "boolean" + }, + "state": { + "type": "string", + "description": "Describes the current time. e.g. \"day\" or \"night\"" + }, + "timeLeft": { + "minLength": 1, + "type": "string" + }, + "isCetus": { + "type": "boolean" + }, + "shortString": { + "type": "string", + "description": "A short description of the remaining time until the next day/night change." + } + } + }, + "conclave": { + "required": [ + "categories", + "modes" + ], + "type": "object", + "properties": { + "modes": { + "required": [ + "PVPMODE_ALL", + "PVPMODE_CAPTURETHEFLAG", + "PVPMODE_DEATHMATCH", + "PVPMODE_NONE", + "PVPMODE_SPEEDBALL", + "PVPMODE_TEAMDEATHMATCH" + ], + "type": "object", + "properties": { + "PVPMODE_ALL": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "PVPMODE_TEAMDEATHMATCH": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "PVPMODE_NONE": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "PVPMODE_CAPTURETHEFLAG": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "PVPMODE_SPEEDBALL": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "PVPMODE_DEATHMATCH": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + } + } + }, + "categories": { + "required": [ + "PVPChallengeTypeCategory_DAILY", + "PVPChallengeTypeCategory_DAILY_ROOT", + "PVPChallengeTypeCategory_MODEAFFECTOR", + "PVPChallengeTypeCategory_WEEKLY", + "PVPChallengeTypeCategory_WEEKLY_ROOT" + ], + "type": "object", + "properties": { + "PVPChallengeTypeCategory_DAILY_ROOT": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "PVPChallengeTypeCategory_DAILY": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "PVPChallengeTypeCategory_WEEKLY": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "PVPChallengeTypeCategory_WEEKLY_ROOT": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "PVPChallengeTypeCategory_MODEAFFECTOR": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + } + } + } + } + }, + "conclaveChallenges": { + "minItems": 1, + "type": "array", + "items": { + "required": [ + "amount", + "asString", + "category", + "daily", + "description", + "endString", + "eta", + "expired", + "expiry", + "id", + "mode", + "rootChallenge" + ], + "type": "object", + "properties": { + "mode": { + "minLength": 1, + "type": "string" + }, + "amount": { + "type": "number" + }, + "eta": { + "minLength": 1, + "type": "string" + }, + "expired": { + "type": "boolean" + }, + "endString": { + "minLength": 1, + "type": "string" + }, + "daily": { + "type": "boolean" + }, + "description": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "expiry": { + "minLength": 1, + "type": "string" + }, + "asString": { + "minLength": 1, + "type": "string" + }, + "category": { + "minLength": 1, + "type": "string" + }, + "rootChallenge": { + "type": "boolean" + } + } + } + }, + "construction": { + "required": [ + "fomorianProgress", + "id", + "razorbackProgress", + "unknownProgress" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "fomorianProgress": { + "minLength": 1, + "type": "string" + }, + "razorbackProgress": { + "minLength": 1, + "type": "string" + }, + "unknownProgress": { + "minLength": 1, + "type": "string" + } + } + }, + "dailyDeals": { + "minItems": 1, + "type": "array", + "items": { + "required": [ + "discount", + "eta", + "expiry", + "id", + "item", + "originalPrice", + "salePrice", + "sold", + "total" + ], + "type": "object", + "properties": { + "sold": { + "type": "number" + }, + "item": { + "minLength": 1, + "type": "string" + }, + "total": { + "type": "number" + }, + "eta": { + "minLength": 1, + "type": "string" + }, + "originalPrice": { + "type": "number" + }, + "salePrice": { + "type": "number" + }, + "discount": { + "type": "number" + }, + "expiry": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + } + } + } + }, + "darkSectorHistory": { + "type": "object", + "description": "Describes a Dark Sector Historical Battle.", + "properties": { + "defender": { + "type": "string", + "description": "Clan or alliance that was defending." + }, + "defenderIsAlliance": { + "type": "boolean", + "description": "Whether or not the defender was an alliance." + }, + "attacker": { + "type": "string", + "description": "Clan or alliance that was attacking." + }, + "winner": { + "type": "string", + "description": "One of attacker or defender, whichever one won." + }, + "start": { + "type": "string", + "description": "When the battle started." + }, + "end": { + "type": "string", + "description": "When the battle ended." + } + } + }, + "darkSectors": { + "minItems": 1, + "type": "array", + "items": { + "required": [ + "defenderDeployemntActivation", + "defenderMOTD", + "defenderName", + "deployerClan", + "deployerName", + "id", + "isAlliance" + ], + "type": "object", + "properties": { + "defenderMOTD": { + "minLength": 1, + "type": "string" + }, + "deployerName": { + "minLength": 1, + "type": "string" + }, + "defenderDeploymentActivation": { + "type": "number" + }, + "defenderName": { + "minLength": 1, + "type": "string" + }, + "deployerClan": { + "minLength": 1, + "type": "string" + }, + "isAlliance": { + "type": "boolean" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "history": { + "type": "array", + "items": { + "$ref": "#/components/schemas/darkSectorHistory" + } + } + } + } + }, + "drops": { + "minItems": 1, + "type": "array", + "description": "", + "items": { + "required": [ + "chance", + "item", + "place", + "rarity" + ], + "type": "object", + "properties": { + "item": { + "minLength": 1, + "type": "string" + }, + "chance": { + "type": "number" + }, + "place": { + "minLength": 1, + "type": "string" + }, + "rarity": { + "minLength": 1, + "type": "string" + } + } + } + }, + "earthCycle": { + "required": [ + "expiry", + "id", + "isDay", + "timeLeft" + ], + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "expiry": { + "minLength": 1, + "type": "string" + }, + "isDay": { + "type": "boolean" + }, + "timeLeft": { + "minLength": 1, + "type": "string" + } + } + }, + "event": { + "type": "object", + "properties": { + "activation": { + "type": "string", + "description": "Timestamp of when the event started", + "format": "date-time" + }, + "expiry": { + "type": "string", + "description": "Timestamp of when the event ends", + "format": "date-time" + }, + "maximumScore": { + "type": "number", + "description": "Maximum score to complete the event" + }, + "currentScore": { + "type": "number", + "description": "The current score for the event" + }, + "smallInterval": { + "type": "number", + "description": "Interval for the first goal" + }, + "largeInterval": { + "type": "number", + "description": "Interval for the second intermediate score" + }, + "faction": { + "$ref": "#/components/schemas/faction" + }, + "description": { + "type": "string", + "description": "The description or \"subtitle\" for the event." + }, + "tooltip": { + "type": "string", + "description": "Tooltip for the event" + }, + "node": { + "type": "string", + "description": "Node that the event is taking place on" + }, + "concurrentNodes": { + "type": "array", + "description": "Nodes that the event is happening concurrently on", + "items": { + "type": "string" + } + }, + "victimNode": { + "type": "string", + "description": "Node that is being attacked & defended in the event." + }, + "scoreLocTag": { + "type": "string", + "description": "Localized tag for the event score" + }, + "rewards": { + "type": "array", + "items": { + "$ref": "#/components/schemas/reward" + } + }, + "expired": { + "type": "boolean", + "description": "Whether or not the event is expired" + }, + "health": { + "type": "number", + "description": "Amount of health remaining for the target" + }, + "affiliatedWith": { + "$ref": "#/components/schemas/syndicate" + }, + "jobs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/syndicateJob" + } + }, + "interimSteps": { + "type": "array", + "description": "Interim steps, marking progress towards the final goal.", + "items": { + "type": "object", + "properties": { + "goal": { + "type": "number", + "description": "Score to each to achieve this step" + }, + "reward": { + "$ref": "#/components/schemas/reward" + }, + "message": { + "type": "object", + "properties": { + "sender": { + "type": "string", + "description": "Who commissioned this reward" + }, + "subject": { + "type": "string", + "description": "Title of the in-game mail received for completing the step." + }, + "message": { + "type": "string", + "description": "Body of the in-game mail received for completing the step." + }, + "senderIcon": { + "type": "string", + "description": "Path to sender icon string." + }, + "attachments": { + "type": "array", + "description": "Attachments to the message. Unknown usage.", + "items": { + "type": "string" + } + } + } + }, + "winnerCount": { + "type": "number", + "description": "Suspected to be the number of persons who have completed this step." + } + } + } + }, + "progressSteps": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Steps in the progress to completion" + }, + "progressAmt": { + "type": "number", + "description": "Percent progress to completion" + } + } + }, + "progressTotal": { + "type": "number", + "description": "Total of progressSteps values." + }, + "showTotalAtEndOfMission": { + "type": "boolean", + "description": "Whether or not to show the total score at the end of the mission" + }, + "isPersonal": { + "type": "boolean", + "description": "Whether or not the event is personal" + }, + "isCommunity": { + "type": "boolean", + "description": "Whether or not the event is communal" + }, + "regionDrops": { + "type": "array", + "description": "Drops in the area around the event node", + "items": { + "type": "string" + } + }, + "archwingDrops": { + "type": "array", + "description": "Archwing Drops in effect while this event is active", + "items": { + "type": "string" + } + }, + "asString": { + "type": "string", + "description": "Attempt to summarize event in a short string. (Do not use)." + }, + "metadata": { + "type": "object", + "description": "Miscellaneous metadata in a string provided by Digital Extremes" + }, + "completionBonuses": { + "type": "array", + "items": { + "type": "number" + }, + "description": "Completion bonus amounts per-stage" + }, + "scoreVar": { + "type": "string", + "description": "Internal string used for unknown purpose" + }, + "altExpiry": { + "type": "string", + "format": "date-time", + "description": "Alternate Expiry. Use unknown." + }, + "altActivation": { + "type": "string", + "format": "date-time", + "description": "Alternate Activation. Use unknown." + }, + "nextAlt": { + "type": "object", + "properties": { + "expiry": { + "type": "string", + "format": "date-time", + "description": "Next alternate expiry. Use unknown." + }, + "activation": { + "type": "string", + "format": "date-time", + "description": "Next alternate activation. Use unknown." + } + } + } + } + }, + "events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/event" + } + }, + "faction": { + "type": "string", + "description": "A Faction in Warframe", + "enum": [ + "Orokin", + "Corrupted", + "Infested", + "Corpus", + "Grineer", + "Tenno" + ] + }, + "factions": { + "required": [ + "FC_CORPUS", + "FC_CORRUPTED", + "FC_GRINEER", + "FC_INFESTATION", + "FC_OROKIN" + ], + "type": "object", + "properties": { + "FC_GRINEER": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "FC_CORPUS": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "FC_INFESTATION": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "FC_CORRUPTED": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "FC_OROKIN": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + } + }, + "description": "" + }, + "fissure": { + "type": "object", + "required": [ + "activation", + "enemy", + "eta", + "expired", + "expiry", + "id", + "missionType", + "node", + "tier", + "tierNum" + ], + "properties": { + "node": { + "minLength": 1, + "type": "string" + }, + "expired": { + "type": "boolean" + }, + "eta": { + "minLength": 1, + "type": "string" + }, + "missionType": { + "minLength": 1, + "type": "string" + }, + "tier": { + "minLength": 1, + "type": "string" + }, + "tierNum": { + "type": "number" + }, + "enemy": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "expiry": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "activation": { + "minLength": 1, + "type": "string", + "format": "date-time" + } + } + }, + "fissureModifiers": { + "required": [ + "VoidT1", + "VoidT2", + "VoidT3", + "VoidT4" + ], + "type": "object", + "properties": { + "VoidT1": { + "required": [ + "num", + "value" + ], + "type": "object", + "properties": { + "num": { + "type": "number" + }, + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "VoidT2": { + "required": [ + "num", + "value" + ], + "type": "object", + "properties": { + "num": { + "type": "number" + }, + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "VoidT3": { + "required": [ + "num", + "value" + ], + "type": "object", + "properties": { + "num": { + "type": "number" + }, + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "VoidT4": { + "required": [ + "num", + "value" + ], + "type": "object", + "properties": { + "num": { + "type": "number" + }, + "value": { + "minLength": 1, + "type": "string" + } + } + } + }, + "description": "" + }, + "fissures": { + "minItems": 1, + "items": { + "$ref": "#/components/schemas/fissure" + } + }, + "flashSales": { + "minItems": 1, + "type": "array", + "items": { + "required": [ + "discount", + "eta", + "expired", + "expiry", + "id", + "isFeatured", + "isPopular", + "item", + "premiumOverride" + ], + "type": "object", + "properties": { + "item": { + "minLength": 1, + "type": "string" + }, + "expired": { + "type": "boolean" + }, + "eta": { + "minLength": 1, + "type": "string" + }, + "discount": { + "type": "number" + }, + "premiumOverride": { + "type": "number" + }, + "isPopular": { + "type": "boolean" + }, + "expiry": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "isFeatured": { + "type": "boolean" + } + } + } + }, + "globalUpgrades": { + "type": "array", + "items": { + "type": "object", + "properties": { + "start": { + "type": "string" + }, + "end": { + "type": "string" + }, + "upgrade": { + "type": "string" + }, + "operation": { + "description": "Operation descriptor", + "type": "string" + }, + "operationSymbol": { + "description": "Symbol corresponding to operation", + "type": "string" + }, + "upgradeOperationValue": { + "description": "Value corresponding to performing the operation", + "type": "number" + }, + "expired": { + "description": "Whether or not the upgrade has expired", + "type": "boolean" + }, + "eta": { + "description": "Formatted short string designating when the upgrade will expire.", + "type": "string" + }, + "desc": { + "description": "Plain text description of the global upgrade.", + "type": "string" + } + } + } + }, + "invasion": { + "required": [ + "activation", + "attackingFaction", + "completed", + "completion", + "count", + "defendingFaction", + "desc", + "eta", + "id", + "node", + "requiredRuns", + "vsInfestation" + ], + "type": "object", + "properties": { + "defenderReward": { + "$ref": "#/components/schemas/reward" + }, + "attackingFaction": { + "minLength": 1, + "type": "string" + }, + "completion": { + "type": "number" + }, + "attackerReward": { + "$ref": "#/components/schemas/reward" + }, + "count": { + "type": "number" + }, + "completed": { + "type": "boolean" + }, + "requiredRuns": { + "type": "number" + }, + "vsInfestation": { + "type": "boolean" + }, + "node": { + "minLength": 1, + "type": "string" + }, + "eta": { + "minLength": 1, + "type": "string" + }, + "defendingFaction": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "activation": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "rewardTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/rewardType" + } + }, + "desc": { + "minLength": 1, + "type": "string" + } + } + }, + "invasions": { + "minItems": 1, + "type": "array", + "items": { + "$ref": "#/components/schemas/invasion" + } + }, + "item": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uniqueName": { + "type": "string" + } + } + }, + "items": { + "minItems": 0, + "type": "array", + "description": "An item from Warframe", + "items": { + "$ref": "#/components/schemas/item" + } + }, + "kuva": { + "type": "object", + "properties": { + "activation": { + "type": "string", + "format": "date-time", + "description": "When the Kuva group becomes active" + }, + "expiry": { + "type": "string", + "format": "date-time", + "description": "When the Kuva group becomes inactive" + }, + "node": { + "type": "string", + "description": "Plain name for the node" + }, + "enemy": { + "$ref": "#/components/schemas/faction" + }, + "type": { + "type": "string", + "description": "Mission type" + }, + "node_type": { + "type": "string", + "description": "Internal node-type string for denoting what kind of node it is" + }, + "archwing": { + "type": "boolean", + "description": "Whether or not this mission requires archwing" + }, + "sharkwing": { + "type": "boolean", + "description": "Whether or not this mission requires sharkwing" + } + } + }, + "language": { + "type": "string", + "default": "en", + "enum": [ + "de", + "es", + "fr", + "it", + "ko", + "pl", + "pt", + "ru", + "zh", + "en" + ] + }, + "languages": { + "type": "object", + "properties": { + "languageKey": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + } + }, + "description": "" + }, + "meleeWeapon": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uniqueName": { + "type": "string" + }, + "secondsPerShot": { + "type": "number" + }, + "totalDamage": { + "type": "number" + }, + "damagePerSecond": { + "type": "number" + }, + "trigger": { + "type": "string" + }, + "description": { + "type": "string" + }, + "accuracy": { + "type": "number" + }, + "criticalChance": { + "type": "number" + }, + "criticalMultiplier": { + "type": "number" + }, + "procChance": { + "type": "number" + }, + "fireRate": { + "type": "number" + }, + "chargeAttack": { + "type": "number" + }, + "spinAttack": { + "type": "number" + }, + "leapAttack": { + "type": "number" + }, + "wallAttack": { + "type": "number" + }, + "slot": { + "type": "number" + }, + "noise": { + "type": "string" + }, + "sentinel": { + "type": "boolean" + }, + "masteryReq": { + "type": "number" + }, + "omegaAttenuation": { + "type": "number" + }, + "type": { + "type": "string" + }, + "buildPrice": { + "type": "number" + }, + "buildTime": { + "type": "number" + }, + "skipBuildTimePrice": { + "type": "number" + }, + "buildQuantity": { + "type": "number" + }, + "consumeOnBuild": { + "type": "boolean" + }, + "components": { + "$ref": "#/components/schemas/item" + }, + "imageName": { + "type": "string" + }, + "category": { + "type": "string" + }, + "tradable": { + "type": "boolean" + }, + "patchlogs": { + "$ref": "#/components/schemas/patchlog" + }, + "ammo": { + "type": "number" + }, + "damage": { + "type": "number" + }, + "damageTypes": { + "type": "object", + "properties": { + "impact": { + "type": "number" + }, + "puncture": { + "type": "number" + }, + "slash": { + "type": "number" + }, + "heat": { + "type": "number" + }, + "cold": { + "type": "number" + }, + "electric": { + "type": "number" + }, + "toxin": { + "type": "number" + }, + "gas": { + "type": "number" + }, + "viral": { + "type": "number" + }, + "corrosive": { + "type": "number" + }, + "blast": { + "type": "number" + }, + "magnetic": { + "type": "number" + }, + "radiation": { + "type": "number" + }, + "true": { + "type": "number" + }, + "void": { + "type": "number" + } + } + }, + "flight": { + "type": "number" + }, + "polarities": { + "$ref": "#/components/schemas/polarity" + }, + "projectile": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "vaulted": { + "type": "boolean" + }, + "wikiaThumbnail": { + "type": "string" + }, + "wikiaUrl": { + "type": "string" + }, + "disposition": { + "type": "number" + }, + "releaseDate": { + "type": "string" + }, + "vaultDate": { + "type": "string" + } + } + }, + "mission": { + "type": "object", + "required": [ + "archwingRequired", + "faction", + "maxEnemyLevel", + "maxWaveNum", + "minEnemyLevel", + "nightmare", + "node", + "reward", + "type" + ], + "properties": { + "reward": { + "$ref": "#/components/schemas/reward" + }, + "node": { + "minLength": 1, + "type": "string" + }, + "faction": { + "minLength": 1, + "type": "string" + }, + "maxEnemyLevel": { + "type": "number" + }, + "minEnemyLevel": { + "type": "number" + }, + "maxWaveNum": { + "type": "number" + }, + "type": { + "minLength": 1, + "type": "string" + }, + "nightmare": { + "type": "boolean" + }, + "archwingRequired": { + "type": "boolean", + "description": "Whether or not an Archwing is required for participating in the mision." + }, + "isSharkwing": { + "description": "Whether or not the mission takes place in a submerssible mission.", + "type": "boolean" + }, + "enemySpec": { + "type": "string", + "description": "Enemy specification for the mission" + }, + "levelOverride": { + "type": "string", + "description": "Override for the map on this mission" + }, + "advancedSpawners": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Array of strings denoting extra spawners for a mission" + }, + "requiredItems": { + "type": "array", + "description": "Items required to enter the mission", + "items": { + "type": "string" + } + }, + "consumeRequiredItems": { + "type": "boolean", + "description": "Whether or not the required items are consumed" + }, + "leadersAlwaysAllowed": { + "type": "boolean", + "description": "Whether or not leaders are always allowed" + }, + "levelAuras": { + "type": "array", + "description": "Affectors for this mission", + "items": { + "type": "string" + } + } + } + }, + "missionTypes": { + "required": [ + "MT_ARENA", + "MT_ASSASSINATION", + "MT_ASSAULT", + "MT_CAPTURE", + "MT_DEFENSE", + "MT_EVACUATION", + "MT_EXCAVATE", + "MT_EXTERMINATION", + "MT_HIVE", + "MT_INTEL", + "MT_MOBILE_DEFENSE", + "MT_PVP", + "MT_RESCUE", + "MT_RETRIEVAL", + "MT_SABOTAGE", + "MT_SECTOR", + "MT_SURVIVAL", + "MT_TERRITORY" + ], + "type": "object", + "properties": { + "MT_EXCAVATE": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_SABOTAGE": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_MOBILE_DEFENSE": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_ASSASSINATION": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_EXTERMINATION": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_HIVE": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_DEFENSE": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_TERRITORY": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_ARENA": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_PVP": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_RESCUE": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_INTEL": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_SURVIVAL": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_CAPTURE": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_SECTOR": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_RETRIEVAL": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_ASSAULT": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "MT_EVACUATION": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + } + }, + "description": "" + }, + "news": { + "minItems": 1, + "type": "array", + "items": { + "required": [ + "asString", + "date", + "eta", + "id", + "imageLink", + "link", + "message", + "primeAccess", + "priority", + "stream", + "update" + ], + "type": "object", + "properties": { + "date": { + "minLength": 1, + "type": "string" + }, + "imageLink": { + "minLength": 1, + "type": "string" + }, + "eta": { + "minLength": 1, + "type": "string" + }, + "primeAccess": { + "type": "boolean" + }, + "stream": { + "type": "boolean" + }, + "translations": { + "required": [ + "es" + ], + "type": "object", + "properties": { + "es": { + "minLength": 1, + "type": "string" + } + } + }, + "link": { + "minLength": 1, + "type": "string" + }, + "update": { + "type": "boolean" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "asString": { + "minLength": 1, + "type": "string" + }, + "message": { + "minLength": 1, + "type": "string" + }, + "priority": { + "type": "boolean" + } + } + } + }, + "nightwave": { + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "activation": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "expiry": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "params": { + "type": "object", + "properties": {} + }, + "rewardTypes": { + "type": "array", + "items": { + "type": "string" + } + }, + "season": { + "type": "number" + }, + "tag": { + "type": "string" + }, + "phase": { + "type": "number" + }, + "possibleChallenges": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nightwaveChallenge" + } + }, + "activeChallenges": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nightwaveChallenge" + } + } + } + }, + "nightwaveChallenge": { + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "activation": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "expiry": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "isDaily": { + "type": "boolean" + }, + "isElite": { + "type": "boolean" + }, + "title": { + "type": "string" + }, + "desc": { + "type": "string" + }, + "reputation": { + "type": "number" + } + } + }, + "operationTypes": { + "required": [ + "MULTIPLY" + ], + "type": "object", + "properties": { + "MULTIPLY": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + } + }, + "description": "" + }, + "patchlog": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "date": { + "type": "string", + "format": "date-time" + }, + "url": { + "type": "string" + }, + "additions": { + "type": "string" + }, + "changes": { + "type": "string" + }, + "fixes": { + "type": "string" + } + } + }, + "persistentEnemies": { + "type": "array", + "items": { + "type": "object", + "properties": { + "locationTag": { + "type": "string", + "description": "Location tag for Acolyte" + }, + "agentType": { + "type": "string", + "description": "Which acolyte it is" + }, + "rank": { + "type": "number", + "description": "Enemy level" + }, + "healthPercent": { + "type": "number", + "description": "Enemy's remaining health" + }, + "fleeDamage": { + "type": "number", + "description": "The percent damage that the enemy takes when it's defeated" + }, + "region": { + "type": "string", + "description": "The region in which the enemy is located" + }, + "lastDiscoveredTime": { + "type": "string", + "description": "The time at which the enemy was last discovered" + }, + "lastDiscoveredAt": { + "type": "string", + "description": "Node at which the enemy was last discovered" + }, + "isDiscovered": { + "type": "boolean", + "description": "Whether or not the enemy is currently discovered" + }, + "isUsingTicketing": { + "type": "boolean", + "description": "Whether or not the enemy is using 'ticketing'. Unknown usage." + }, + "pid": { + "type": "string", + "description": "faux-id incorporating the actual description and whether or not the enemy is discovered." + } + } + } + }, + "platform": { + "type": "string", + "enum": [ + "pc", + "ps4", + "xb1", + "swi" + ] + }, + "polarity": { + "type": "string", + "enum": [ + "Vazarin", + "Madurai", + "Naramon", + "Zenurik", + "Unairu", + "Penjaga", + "Unbra" + ] + }, + "primaryWeapon": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uniqueName": { + "type": "string" + }, + "secondsPerShot": { + "type": "number" + }, + "dmagePerShot": { + "type": "array", + "items": { + "type": "number" + } + }, + "magazineSize": { + "type": "number" + }, + "reloadTime": { + "type": "number" + }, + "totalDamage": { + "type": "number" + }, + "damagePerSecond": { + "type": "number" + }, + "trigger": { + "type": "string" + }, + "description": { + "type": "string" + }, + "accuracy": { + "type": "number" + }, + "criticalChance": { + "type": "number" + }, + "criticalMultiplier": { + "type": "number" + }, + "procChance": { + "type": "number" + }, + "fireRate": { + "type": "number" + }, + "slot": { + "type": "number" + }, + "noise": { + "type": "string" + }, + "sentinel": { + "type": "boolean" + }, + "masteryReq": { + "type": "number" + }, + "omegaAttenuation": { + "type": "number" + }, + "type": { + "type": "string" + }, + "buildPrice": { + "type": "number" + }, + "buildTime": { + "type": "number" + }, + "skipBuildTimePrice": { + "type": "number" + }, + "buildQuantity": { + "type": "number" + }, + "consumeOnBuild": { + "type": "boolean" + }, + "components": { + "$ref": "#/components/schemas/item" + }, + "imageName": { + "type": "string" + }, + "category": { + "type": "string" + }, + "tradable": { + "type": "boolean" + }, + "patchlogs": { + "$ref": "#/components/schemas/patchlog" + }, + "ammo": { + "type": "number" + }, + "damage": { + "type": "number" + }, + "damageTypes": { + "type": "object", + "properties": { + "impact": { + "type": "number" + }, + "puncture": { + "type": "number" + }, + "slash": { + "type": "number" + }, + "heat": { + "type": "number" + }, + "cold": { + "type": "number" + }, + "electric": { + "type": "number" + }, + "toxin": { + "type": "number" + }, + "gas": { + "type": "number" + }, + "viral": { + "type": "number" + }, + "corrosive": { + "type": "number" + }, + "blast": { + "type": "number" + }, + "magnetic": { + "type": "number" + }, + "radiation": { + "type": "number" + }, + "true": { + "type": "number" + }, + "void": { + "type": "number" + } + } + }, + "flight": { + "type": "number" + }, + "polarities": { + "$ref": "#/components/schemas/polarity" + }, + "projectile": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "vaulted": { + "type": "boolean" + }, + "wikiaThumbnail": { + "type": "string" + }, + "wikiaUrl": { + "type": "string" + }, + "disposition": { + "type": "number" + }, + "releaseDate": { + "type": "string" + }, + "vaultDate": { + "type": "string" + } + } + }, + "reward": { + "type": "object", + "required": [ + "asString", + "color", + "countedItems", + "credits", + "itemString", + "items", + "thumbnail" + ], + "properties": { + "countedItems": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "count", + "type" + ], + "type": "object", + "properties": { + "count": { + "type": "number" + }, + "type": { + "minLength": 1, + "type": "string" + } + } + } + }, + "thumbnail": { + "minLength": 1, + "type": "string" + }, + "color": { + "type": "number" + }, + "credits": { + "type": "number" + }, + "asString": { + "minLength": 1, + "type": "string" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } + }, + "itemString": { + "minLength": 1, + "type": "string" + } + } + }, + "rewardType": { + "type": "string", + "description": "One of the reward types", + "enum": [ + "vauban", + "vandal", + "wraith", + "skin", + "helmet", + "nitain", + "mutalist", + "weapon", + "fieldron", + "detonite", + "mutagen", + "aura", + "neuralSensors", + "orokinCell", + "alloyPlate", + "circuits", + "controlModule", + "ferrite", + "gallium", + "morphics", + "nanoSpores", + "oxium", + "rubedo", + "salvage", + "plastids", + "polymerBundle", + "argonCrystal", + "cryotic", + "tellurium", + "neurodes", + "nightmare", + "endo", + "reactor", + "catalyst", + "forma", + "synthula", + "exilus", + "riven", + "kavatGene", + "kubrowEgg", + "traces", + "other", + "credits" + ] + }, + "riven": { + "type": "object", + "properties": { + "rivenCompatability": { + "type": "object", + "description": "The name of the weapon that this riven is compatible with", + "additionalProperties": { + "type": "object", + "properties": { + "rolled": { + "$ref": "#/components/schemas/rivenStatistic" + }, + "unrolled": { + "$ref": "#/components/schemas/rivenStatistic" + } + } + }, + "example": { + "Corvas": { + "rerolled": { + "itemType": "Archgun Riven Mod", + "compatibility": "Corvas", + "rerolled": true, + "avg": 291.94, + "stddev": 346.96, + "min": 25, + "max": 1500, + "pop": 0.0536, + "median": 150 + } + } + } + } + } + }, + "rivenStatistic": { + "description": "A colleciton of rivens about a specific weapon's riven sales", + "type": "object", + "properties": { + "itemType": { + "type": "string" + }, + "compatability": { + "type": "string" + }, + "rerolled": { + "type": "boolean" + }, + "avg": { + "type": "number", + "format": "float" + }, + "stddev": { + "type": "number", + "format": "float" + }, + "min": { + "type": "number", + "format": "float" + }, + "max": { + "type": "number", + "format": "float" + }, + "pop": { + "type": "number", + "format": "float" + }, + "median": { + "type": "number", + "format": "float" + } + }, + "example": { + "rerolled": { + "itemType": "Archgun Riven Mod", + "compatibility": "Corvas", + "rerolled": true, + "avg": 291.94, + "stddev": 346.96, + "min": 25, + "max": 1500, + "pop": 0.0536, + "median": 150 + } + } + }, + "rivenType": { + "type": "object", + "description": "The type of riven that the child rivens belong to", + "additionalProperties": { + "$ref": "#/components/schemas/riven" + }, + "example": { + "Archgun Riven Mod": { + "Corvas": { + "rerolled": { + "itemType": "Archgun Riven Mod", + "compatibility": "Corvas", + "rerolled": true, + "avg": 291.94, + "stddev": 346.96, + "min": 25, + "max": 1500, + "pop": 0.0536, + "median": 150 + } + } + } + } + }, + "secondaryWeapon": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uniqueName": { + "type": "string" + }, + "secondsPerShot": { + "type": "number" + }, + "dmagePerShot": { + "type": "array", + "items": { + "type": "number" + } + }, + "magazineSize": { + "type": "number" + }, + "reloadTime": { + "type": "number" + }, + "totalDamage": { + "type": "number" + }, + "damagePerSecond": { + "type": "number" + }, + "trigger": { + "type": "string" + }, + "description": { + "type": "string" + }, + "accuracy": { + "type": "number" + }, + "criticalChance": { + "type": "number" + }, + "criticalMultiplier": { + "type": "number" + }, + "procChance": { + "type": "number" + }, + "fireRate": { + "type": "number" + }, + "slot": { + "type": "number" + }, + "noise": { + "type": "string" + }, + "sentinel": { + "type": "boolean" + }, + "masteryReq": { + "type": "number" + }, + "omegaAttenuation": { + "type": "number" + }, + "type": { + "type": "string" + }, + "buildPrice": { + "type": "number" + }, + "buildTime": { + "type": "number" + }, + "skipBuildTimePrice": { + "type": "number" + }, + "buildQuantity": { + "type": "number" + }, + "consumeOnBuild": { + "type": "boolean" + }, + "components": { + "$ref": "#/components/schemas/item" + }, + "imageName": { + "type": "string" + }, + "category": { + "type": "string" + }, + "tradable": { + "type": "boolean" + }, + "patchlogs": { + "$ref": "#/components/schemas/patchlog" + }, + "ammo": { + "type": "number" + }, + "damage": { + "type": "number" + }, + "damageTypes": { + "type": "object", + "properties": { + "impact": { + "type": "number" + }, + "puncture": { + "type": "number" + }, + "slash": { + "type": "number" + }, + "heat": { + "type": "number" + }, + "cold": { + "type": "number" + }, + "electric": { + "type": "number" + }, + "toxin": { + "type": "number" + }, + "gas": { + "type": "number" + }, + "viral": { + "type": "number" + }, + "corrosive": { + "type": "number" + }, + "blast": { + "type": "number" + }, + "magnetic": { + "type": "number" + }, + "radiation": { + "type": "number" + }, + "true": { + "type": "number" + }, + "void": { + "type": "number" + } + } + }, + "flight": { + "type": "number" + }, + "polarities": { + "$ref": "#/components/schemas/polarity" + }, + "projectile": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "vaulted": { + "type": "boolean" + }, + "wikiaThumbnail": { + "type": "string" + }, + "wikiaUrl": { + "type": "string" + }, + "disposition": { + "type": "number" + }, + "releaseDate": { + "type": "string" + }, + "vaultDate": { + "type": "string" + } + } + }, + "simaris": { + "required": [ + "asString", + "isTargetActive", + "target" + ], + "type": "object", + "properties": { + "target": { + "minLength": 1, + "type": "string" + }, + "isTargetActive": { + "type": "boolean" + }, + "asString": { + "minLength": 1, + "type": "string" + } + } + }, + "solNode": { + "type": "object", + "properties": { + "SolKey": { + "required": [ + "enemy", + "type", + "value" + ], + "type": "object", + "properties": { + "enemy": { + "minLength": 1, + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + }, + "value": { + "minLength": 1, + "type": "string" + } + } + } + }, + "description": "" + }, + "solNodeSearch": { + "minItems": 1, + "type": "array", + "description": "", + "items": { + "required": [ + "keys", + "nodes" + ], + "type": "object", + "properties": { + "nodes": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "enemy", + "type", + "value" + ], + "type": "object", + "properties": { + "enemy": { + "minLength": 1, + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + }, + "value": { + "minLength": 1, + "type": "string" + } + } + } + }, + "keys": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "sortie": { + "required": [ + "activation", + "boss", + "eta", + "expired", + "expiry", + "faction", + "id", + "rewardPool", + "variants" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "activation": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "expiry": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "rewardPool": { + "minLength": 1, + "type": "string" + }, + "variants": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "boss", + "missionType", + "modifier", + "modifierDescription", + "node", + "planet" + ], + "type": "object", + "properties": { + "node": { + "minLength": 1, + "type": "string" + }, + "boss": { + "minLength": 1, + "type": "string" + }, + "missionType": { + "minLength": 1, + "type": "string" + }, + "planet": { + "minLength": 1, + "type": "string" + }, + "modifier": { + "minLength": 1, + "type": "string" + }, + "modifierDescription": { + "minLength": 1, + "type": "string" + } + } + } + }, + "boss": { + "minLength": 1, + "type": "string" + }, + "faction": { + "minLength": 1, + "type": "string" + }, + "expired": { + "type": "boolean" + }, + "eta": { + "minLength": 1, + "type": "string" + } + } + }, + "sortieData": { + "required": [ + "bosses", + "endStates", + "modifierDescriptions", + "modifierTypes", + "modifiers" + ], + "type": "object", + "properties": { + "modifierTypes": { + "required": [ + "SORTIE_MODIFIER_ARMOR", + "SORTIE_MODIFIER_BOW_ONLY", + "SORTIE_MODIFIER_CORROSIVE", + "SORTIE_MODIFIER_ELECTRICITY", + "SORTIE_MODIFIER_EXIMUS", + "SORTIE_MODIFIER_EXPLOSION", + "SORTIE_MODIFIER_FIRE", + "SORTIE_MODIFIER_FREEZE", + "SORTIE_MODIFIER_GAS", + "SORTIE_MODIFIER_HAZARD_COLD", + "SORTIE_MODIFIER_HAZARD_FIRE", + "SORTIE_MODIFIER_HAZARD_FOG", + "SORTIE_MODIFIER_HAZARD_ICE", + "SORTIE_MODIFIER_HAZARD_MAGNETIC", + "SORTIE_MODIFIER_HAZARD_RADIATION", + "SORTIE_MODIFIER_IMPACT", + "SORTIE_MODIFIER_LOW_ENERGY", + "SORTIE_MODIFIER_MAGNETIC", + "SORTIE_MODIFIER_MELEE_ONLY", + "SORTIE_MODIFIER_POISON", + "SORTIE_MODIFIER_PUNCTURE", + "SORTIE_MODIFIER_RADIATION", + "SORTIE_MODIFIER_RIFLE_ONLY", + "SORTIE_MODIFIER_SECONDARY_ONLY", + "SORTIE_MODIFIER_SHIELDS", + "SORTIE_MODIFIER_SHOTGUN_ONLY", + "SORTIE_MODIFIER_SLASH", + "SORTIE_MODIFIER_SNIPER_ONLY", + "SORTIE_MODIFIER_TOXIN", + "SORTIE_MODIFIER_VIRAL" + ], + "type": "object", + "properties": { + "SORTIE_MODIFIER_IMPACT": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_COLD": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SECONDARY_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_TOXIN": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_ELECTRICITY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_FOG": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_MAGNETIC": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_ARMOR": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_CORROSIVE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_VIRAL": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_RADIATION": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SLASH": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_POISON": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_GAS": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_MAGNETIC": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_FIRE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SNIPER_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_PUNCTURE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_EXPLOSION": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_BOW_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_RADIATION": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SHIELDS": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_FREEZE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_FIRE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_EXIMUS": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SHOTGUN_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_MELEE_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_RIFLE_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_ICE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_LOW_ENERGY": { + "minLength": 1, + "type": "string" + } + } + }, + "modifierDescriptions": { + "required": [ + "SORTIE_MODIFIER_ARMOR", + "SORTIE_MODIFIER_BOW_ONLY", + "SORTIE_MODIFIER_CORROSIVE", + "SORTIE_MODIFIER_ELECTRICITY", + "SORTIE_MODIFIER_EXIMUS", + "SORTIE_MODIFIER_EXPLOSION", + "SORTIE_MODIFIER_FIRE", + "SORTIE_MODIFIER_FREEZE", + "SORTIE_MODIFIER_GAS", + "SORTIE_MODIFIER_HAZARD_COLD", + "SORTIE_MODIFIER_HAZARD_FIRE", + "SORTIE_MODIFIER_HAZARD_FOG", + "SORTIE_MODIFIER_HAZARD_ICE", + "SORTIE_MODIFIER_HAZARD_MAGNETIC", + "SORTIE_MODIFIER_HAZARD_RADIATION", + "SORTIE_MODIFIER_IMPACT", + "SORTIE_MODIFIER_LOW_ENERGY", + "SORTIE_MODIFIER_MAGNETIC", + "SORTIE_MODIFIER_MELEE_ONLY", + "SORTIE_MODIFIER_POISON", + "SORTIE_MODIFIER_PUNCTURE", + "SORTIE_MODIFIER_RADIATION", + "SORTIE_MODIFIER_RIFLE_ONLY", + "SORTIE_MODIFIER_SECONDARY_ONLY", + "SORTIE_MODIFIER_SHIELDS", + "SORTIE_MODIFIER_SHOTGUN_ONLY", + "SORTIE_MODIFIER_SLASH", + "SORTIE_MODIFIER_SNIPER_ONLY", + "SORTIE_MODIFIER_TOXIN", + "SORTIE_MODIFIER_VIRAL" + ], + "type": "object", + "properties": { + "SORTIE_MODIFIER_IMPACT": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_COLD": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SECONDARY_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_TOXIN": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_ELECTRICITY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_FOG": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_MAGNETIC": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_ARMOR": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_CORROSIVE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_VIRAL": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_RADIATION": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SLASH": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_POISON": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_GAS": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_MAGNETIC": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_FIRE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SNIPER_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_PUNCTURE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_EXPLOSION": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_BOW_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_RADIATION": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SHIELDS": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_FREEZE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_FIRE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_EXIMUS": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_SHOTGUN_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_MELEE_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_RIFLE_ONLY": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_HAZARD_ICE": { + "minLength": 1, + "type": "string" + }, + "SORTIE_MODIFIER_LOW_ENERGY": { + "minLength": 1, + "type": "string" + } + } + }, + "bosses": { + "required": [ + "SORTIE_BOSS_ALAD", + "SORTIE_BOSS_AMBULAS", + "SORTIE_BOSS_CORRUPTED_VOR", + "SORTIE_BOSS_HEK", + "SORTIE_BOSS_HYENA", + "SORTIE_BOSS_INFALAD", + "SORTIE_BOSS_JACKAL", + "SORTIE_BOSS_KELA", + "SORTIE_BOSS_KRIL", + "SORTIE_BOSS_LEPHANTIS", + "SORTIE_BOSS_NEF", + "SORTIE_BOSS_PHORID", + "SORTIE_BOSS_RAPTOR", + "SORTIE_BOSS_RUK", + "SORTIE_BOSS_TYL", + "SORTIE_BOSS_VOR" + ], + "type": "object", + "properties": { + "SORTIE_BOSS_KELA": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_AMBULAS": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_TYL": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_ALAD": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_RUK": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_HYENA": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_KRIL": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_CORRUPTED_VOR": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_INFALAD": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_PHORID": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_JACKAL": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_RAPTOR": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_VOR": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_HEK": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_NEF": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SORTIE_BOSS_LEPHANTIS": { + "required": [ + "faction", + "name" + ], + "type": "object", + "properties": { + "faction": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + } + } + } + }, + "endStates": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "bossName" + ], + "type": "object", + "properties": { + "regions": { + "minItems": 1, + "uniqueItems": true, + "type": "array", + "items": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "missions": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } + }, + "name": { + "minLength": 1, + "type": "string" + } + } + } + }, + "bossName": { + "minLength": 1, + "type": "string" + } + } + } + }, + "modifiers": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } + } + }, + "description": "" + }, + "syndicate": { + "type": "string", + "enum": [ + "Arbiters of Hexis", + "Cephalon Suda", + "Assassins", + "Nightwave", + "Ostrons", + "Vox Solaris", + "Solaris United", + "Perrin Sequence", + "Steel Meridian", + "Red Veil", + "New Loka" + ] + }, + "syndicateJob": { + "type": "object", + "description": "A Job for a syndicate. Often called a bounty.", + "properties": { + "activation": { + "type": "string", + "description": "Timestamp for when the job becomes active", + "format": "date-time" + }, + "expiry": { + "type": "string", + "description": "Timestamp for when the job becomes inactive", + "format": "date-time" + }, + "rewardPool": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Reward Pool for the job" + }, + "type": { + "type": "string", + "description": "What type of Job (Bounty) it is" + }, + "enemyLevels": { + "type": "array", + "description": "Array of enemy levels", + "items": { + "type": "number" + } + }, + "standingStages": { + "type": "array", + "items": { + "type": "number" + }, + "description": "Stages of standing rewards." + }, + "minMR": { + "type": "number", + "description": "Minimum Mastery Rank required to perform a job." + } + } + }, + "syndicateMission": { + "required": [ + "activation", + "eta", + "expiry", + "id", + "syndicate" + ], + "type": "object", + "properties": { + "nodes": { + "type": "array", + "items": { + "type": "string" + } + }, + "eta": { + "minLength": 1, + "type": "string" + }, + "jobs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/syndicateJob" + } + }, + "syndicate": { + "$ref": "#/components/schemas/syndicate" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "expiry": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "activation": { + "format": "date-time", + "minLength": 1, + "type": "string" + } + } + }, + "syndicateMissions": { + "minItems": 1, + "type": "array", + "items": { + "$ref": "#/components/schemas/syndicateMission" + } + }, + "syndicates": { + "required": [ + "ArbitersSyndicate", + "AssassinsSyndicate", + "CephalonSudaSyndicate", + "CetusSyndicate", + "EventSyndicate", + "NewLokaSyndicate", + "PerrinSyndicate", + "QuillsSyndicate", + "RedVeilSyndicate", + "SteelMeridianSyndicate" + ], + "type": "object", + "properties": { + "ArbitersSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "CephalonSudaSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "NewLokaSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "PerrinSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "SteelMeridianSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "RedVeilSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "CetusSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "QuillsSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "AssassinsSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + }, + "EventSyndicate": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "minLength": 1, + "type": "string" + } + } + } + }, + "description": "" + }, + "timestamp": { + "minLength": 1, + "type": "string" + }, + "tutorials": { + "minItems": 1, + "type": "array", + "description": "", + "items": { + "required": [ + "name", + "regex", + "url" + ], + "type": "object", + "properties": { + "regex": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "url": { + "minLength": 1, + "type": "string" + } + } + } + }, + "upgradeTypes": { + "required": [ + "GAMEPLAY_KILL_XP_AMOUNT", + "GAMEPLAY_MONEY_PICKUP_AMOUNT", + "GAMEPLAY_MONEY_REWARD_AMOUNT", + "GAMEPLAY_PICKUP_AMOUNT" + ], + "type": "object", + "properties": { + "GAMEPLAY_KILL_XP_AMOUNT": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "GAMEPLAY_PICKUP_AMOUNT": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "GAMEPLAY_MONEY_REWARD_AMOUNT": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + }, + "GAMEPLAY_MONEY_PICKUP_AMOUNT": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + } + } + }, + "description": "" + }, + "vallisCycle": { + "required": [ + "expiry", + "id", + "isWarm", + "timeLeft" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "expiry": { + "minLength": 1, + "type": "string" + }, + "timeLeft": { + "minLength": 1, + "type": "string" + }, + "isWarm": { + "type": "boolean" + } + } + }, + "voidTrader": { + "required": [ + "activation", + "active", + "character", + "endString", + "expiry", + "id", + "inventory", + "location", + "psId", + "startString" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "activation": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "expiry": { + "minLength": 1, + "type": "string", + "format": "date-time" + }, + "character": { + "minLength": 1, + "type": "string" + }, + "location": { + "minLength": 1, + "type": "string" + }, + "inventory": { + "type": "array", + "items": { + "type": "object", + "properties": { + "item": { + "type": "string", + "description": "Item that is being sold" + }, + "ducats": { + "type": "number", + "description": "Amount of ducats required to purchase item" + }, + "credits": { + "type": "number", + "description": "Amount of credits required to purchase item" + } + } + } + }, + "psId": { + "minLength": 1, + "type": "string" + }, + "active": { + "type": "boolean" + }, + "startString": { + "minLength": 1, + "type": "string" + }, + "endString": { + "minLength": 1, + "type": "string" + } + } + }, + "warframes": { + "minItems": 1, + "type": "array", + "description": "", + "items": { + "required": [ + "armor", + "aura", + "color", + "conclave", + "description", + "health", + "info", + "location", + "mr", + "name", + "polarities", + "power", + "regex", + "shield", + "speed", + "thumbnail", + "url" + ], + "type": "object", + "properties": { + "shield": { + "minLength": 1, + "type": "string" + }, + "polarities": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } + }, + "prime_power": { + "minLength": 1, + "type": "string" + }, + "prime_mr": { + "minLength": 1, + "type": "string" + }, + "color": { + "type": "number" + }, + "prime_polarities": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } + }, + "conclave": { + "minLength": 1, + "type": "string" + }, + "description": { + "minLength": 1, + "type": "string" + }, + "prime_armor": { + "minLength": 1, + "type": "string" + }, + "speed": { + "minLength": 1, + "type": "string" + }, + "aura": { + "minLength": 1, + "type": "string" + }, + "prime_url": { + "minLength": 1, + "type": "string" + }, + "prime_health": { + "type": "string" + }, + "power": { + "minLength": 1, + "type": "string" + }, + "prime_aura": { + "minLength": 1, + "type": "string" + }, + "info": { + "minLength": 1, + "type": "string" + }, + "thumbnail": { + "minLength": 1, + "type": "string" + }, + "mr": { + "minLength": 1, + "type": "string" + }, + "prime_shield": { + "type": "string" + }, + "health": { + "minLength": 1, + "type": "string" + }, + "prime_speed": { + "minLength": 1, + "type": "string" + }, + "url": { + "minLength": 1, + "type": "string" + }, + "regex": { + "minLength": 1, + "type": "string" + }, + "armor": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "location": { + "minLength": 1, + "type": "string" + }, + "prime_conclave": { + "type": "string" + } + } + } + }, + "weapons": { + "minItems": 1, + "type": "array", + "description": "", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/primaryWeapon" + }, + { + "$ref": "#/components/schemas/secondaryWeapon" + }, + { + "$ref": "#/components/schemas/meleeWeapon" + } + ] + } + }, + "ws": { + "required": [ + "alerts", + "cetusCycle", + "conclaveChallenges", + "constructionProgress", + "dailyDeals", + "darkSectors", + "earthCycle", + "events", + "fissures", + "flashSales", + "globalUpgrades", + "invasions", + "news", + "persistentEnemies", + "simaris", + "sortie", + "syndicateMissions", + "timestamp", + "voidTrader" + ], + "type": "object", + "properties": { + "timestamp": { + "$ref": "#/components/schemas/timestamp" + }, + "news": { + "$ref": "#/components/schemas/news" + }, + "events": { + "$ref": "#/components/schemas/events" + }, + "alerts": { + "$ref": "#/components/schemas/alert" + }, + "sortie": { + "$ref": "#/components/schemas/sortie" + }, + "syndicateMissions": { + "$ref": "#/components/schemas/syndicateMissions" + }, + "fissures": { + "$ref": "#/components/schemas/fissures" + }, + "globalUpgrades": { + "$ref": "#/components/schemas/globalUpgrades" + }, + "flashSales": { + "$ref": "#/components/schemas/flashSales" + }, + "invasions": { + "$ref": "#/components/schemas/invasions" + }, + "darkSectors": { + "$ref": "#/components/schemas/darkSectors" + }, + "voidTrader": { + "$ref": "#/components/schemas/voidTrader" + }, + "dailyDeals": { + "$ref": "#/components/schemas/dailyDeals" + }, + "simaris": { + "$ref": "#/components/schemas/simaris" + }, + "conclaveChallenges": { + "$ref": "#/components/schemas/conclaveChallenges" + }, + "persistentEnemies": { + "$ref": "#/components/schemas/persistentEnemies" + }, + "earthCycle": { + "$ref": "#/components/schemas/earthCycle" + }, + "cetusCycle": { + "$ref": "#/components/schemas/cetusCycle" + }, + "vallisCycle": { + "$ref": "#/components/schemas/vallisCycle" + }, + "constructionProgress": { + "$ref": "#/components/schemas/construction" + }, + "nightwave": { + "$ref": "#/components/schemas/nightwave" + }, + "arbitration": { + "$ref": "#/components/schemas/arbitration" + }, + "kuva": { + "$ref": "#/components/schemas/kuva" + } + }, + "description": "Full Worldstate Object" + } + }, + "responses": { + "error": { + "description": "An error occurred", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Description of error" + }, + "code": { + "type": "integer", + "description": "HTTP status code associated with error" + } + } + } + } + } + }, + "NotFound": { + "description": "Path or sub-path not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "Description of error", + "example": "Route not found" + }, + "code": { + "type": "integer", + "description": "HTTP status code associated with error", + "example": 404 + } + } + } + } + } + } + }, + "parameters": { + "language": { + "name": "Accept-Language", + "in": "header", + "description": "Language to retrieve", + "schema": { + "$ref": "#/components/schemas/language" + } + }, + "platform": { + "name": "platform", + "in": "path", + "description": "Platform to provide data for", + "required": true, + "schema": { + "$ref": "#/components/schemas/platform" + } + } + }, + "headers": { + "Content-Language": { + "description": "Language of the content", + "schema": { + "$ref": "#/components/schemas/language" + } + } + } + } +} diff --git a/controllers/drops.js b/src/controllers/drops.js similarity index 89% rename from controllers/drops.js rename to src/controllers/drops.js index 316df14cc..30fb0289c 100644 --- a/controllers/drops.js +++ b/src/controllers/drops.js @@ -2,9 +2,7 @@ const express = require('express'); -const { - logger, setHeadersAndJson, cache, ah, -} = require('../lib/utilities'); +const { logger, cache, ah } = require('../lib/utilities'); const dropCache = require('../lib/caches/Drops'); @@ -27,7 +25,7 @@ const groupLocation = (data) => { router.get('/', cache('24 hours'), ah(async (req, res) => { logger.silly(`Got ${req.originalUrl}`); - setHeadersAndJson(res, await dropCache.getData()); + res.json(await dropCache.getData()); })); router.get('/search/:query', cache('1 hour'), ah(async (req, res) => { @@ -56,7 +54,7 @@ router.get('/search/:query', cache('1 hour'), ah(async (req, res) => { } }); - setHeadersAndJson(res, results); + res.json(results); })); module.exports = router; diff --git a/controllers/heartbeat.js b/src/controllers/heartbeat.js similarity index 100% rename from controllers/heartbeat.js rename to src/controllers/heartbeat.js diff --git a/controllers/index.js b/src/controllers/index.js similarity index 86% rename from controllers/index.js rename to src/controllers/index.js index 571880501..1a98fad3b 100644 --- a/controllers/index.js +++ b/src/controllers/index.js @@ -3,12 +3,12 @@ const router = require('express').Router(); const { - logger, cache, setHeadersAndJson, platforms, warframeData, platformAliases, + logger, cache, platforms, warframeData, platformAliases, } = require('../lib/utilities'); router.get('/', cache('1 minute'), (req, res) => { logger.silly(`Got ${req.originalUrl}`); - setHeadersAndJson(res, { code: 200, message: 'OK' }); + res.json({ code: 200, message: 'OK' }); }); router.use(`/:platform(${platforms.join('|')}|${platformAliases.join('|')})`, require('./worldstate')); diff --git a/controllers/pricecheck.js b/src/controllers/pricecheck.js similarity index 90% rename from controllers/pricecheck.js rename to src/controllers/pricecheck.js index 81bf45074..17b23ac1f 100644 --- a/controllers/pricecheck.js +++ b/src/controllers/pricecheck.js @@ -5,7 +5,7 @@ const Nexus = require('warframe-nexus-query'); const NexusFetcher = require('nexushub-client'); const { - logger, setHeadersAndJson, ah, cache, + logger, ah, cache, noResult, } = require('../lib/utilities'); const router = express.Router(); @@ -63,12 +63,9 @@ router.get('/:type/:query', cache('1 hour'), ah(async (req, res) => { break; } if (value) { - setHeadersAndJson(res, value); + res.json(value); } else { - res.status(400).json({ - error: `Unable to pricecheck \`${req.params.query}\``, - code: 400, - }); + noResult(res); } } catch (error) { logger.error(error); diff --git a/controllers/rivens.js b/src/controllers/rivens.js similarity index 91% rename from controllers/rivens.js rename to src/controllers/rivens.js index b37cf339d..66986a034 100644 --- a/controllers/rivens.js +++ b/src/controllers/rivens.js @@ -1,89 +1,89 @@ -'use strict'; - -const express = require('express'); -const Cache = require('json-fetch-cache'); -const { - logger, setHeadersAndJson, ah, platforms, titleCase, -} = require('../lib/utilities'); - -const router = express.Router(); - -const rivenCaches = {}; - -const groupRivenData = (cacheStrData) => { - const parsed = JSON.parse(cacheStrData); - - const byType = {}; - parsed.forEach((rivenD) => { - if (rivenD.compatibility === null) { - rivenD.compatibility = `Veiled ${rivenD.itemType}`; - } - - rivenD.compatibility = titleCase(rivenD.compatibility.replace('', '').trim()); - - if (!byType[rivenD.itemType]) { - byType[rivenD.itemType] = {}; - } - if (!byType[rivenD.itemType][rivenD.compatibility]) { - byType[rivenD.itemType][rivenD.compatibility] = { - rerolled: null, - unrolled: null, - }; - } - - byType[rivenD.itemType][rivenD.compatibility][rivenD.rerolled ? 'rerolled' : 'unrolled'] = rivenD; - }); - - return byType; -}; - -platforms.forEach((platform) => { - const rCache = new Cache(`http://n9e5v4d8.ssl.hwcdn.net/repos/weeklyRivens${platform.toUpperCase()}.json`, 604800000, { - parser: groupRivenData, - logger, - delayStart: true, - }); - rCache.startUpdating(); - rivenCaches[platform] = rCache; -}); - -router.use((req, res, next) => { - req.platform = (req.baseUrl.replace('/', '').trim().split('/')[0] || '').toLowerCase(); - if (req.platform === 'ns') { - req.platform = 'swi'; - } - - if (!platforms.includes(req.platform)) { - if (req.header('platform')) { - req.platform = req.header('platform'); - } else { - req.platform = 'pc'; - } - } - next(); -}); - -router.get('/', /* cache('1 week'), */ ah(async (req, res) => { - logger.silly(`Got ${req.originalUrl}`); - const rC = rivenCaches[req.platform]; - setHeadersAndJson(res, await rC.getData()); -})); - -router.get('/search/:query', /* cache('10 hours'), */ ah(async (req, res) => { - logger.silly(`Got ${req.originalUrl}`); - const { query } = req.params; - const results = {}; - const rCache = await rivenCaches[req.platform].getData(); - - Object.keys(rCache).forEach((type) => { - Object.keys(rCache[type]).forEach((compatibility) => { - if (compatibility.toLowerCase().includes(query.toLowerCase())) { - results[compatibility] = rCache[type][compatibility]; - } - }); - }); - res.setHeader('Content-Language', req.language); - setHeadersAndJson(res, results); -})); - -module.exports = router; +'use strict'; + +const express = require('express'); +const Cache = require('json-fetch-cache'); +const { + logger, ah, platforms, titleCase, +} = require('../lib/utilities'); + +const router = express.Router(); + +const rivenCaches = {}; + +const groupRivenData = (cacheStrData) => { + const parsed = JSON.parse(cacheStrData); + + const byType = {}; + parsed.forEach((rivenD) => { + if (rivenD.compatibility === null) { + rivenD.compatibility = `Veiled ${rivenD.itemType}`; + } + + rivenD.compatibility = titleCase(rivenD.compatibility.replace('', '').trim()); + + if (!byType[rivenD.itemType]) { + byType[rivenD.itemType] = {}; + } + if (!byType[rivenD.itemType][rivenD.compatibility]) { + byType[rivenD.itemType][rivenD.compatibility] = { + rerolled: null, + unrolled: null, + }; + } + + byType[rivenD.itemType][rivenD.compatibility][rivenD.rerolled ? 'rerolled' : 'unrolled'] = rivenD; + }); + + return byType; +}; + +platforms.forEach((platform) => { + const rCache = new Cache(`http://n9e5v4d8.ssl.hwcdn.net/repos/weeklyRivens${platform.toUpperCase()}.json`, 604800000, { + parser: groupRivenData, + logger, + delayStart: true, + }); + rCache.startUpdating(); + rivenCaches[platform] = rCache; +}); + +router.use((req, res, next) => { + req.platform = (req.baseUrl.replace('/', '').trim().split('/')[0] || '').toLowerCase(); + if (req.platform === 'ns') { + req.platform = 'swi'; + } + + if (!platforms.includes(req.platform)) { + if (req.header('platform')) { + req.platform = req.header('platform'); + } else { + req.platform = 'pc'; + } + } + next(); +}); + +router.get('/', /* cache('1 week'), */ ah(async (req, res) => { + logger.silly(`Got ${req.originalUrl}`); + const rC = rivenCaches[req.platform]; + res.json(await rC.getData()); +})); + +router.get('/search/:query', /* cache('10 hours'), */ ah(async (req, res) => { + logger.silly(`Got ${req.originalUrl}`); + const { query } = req.params; + const results = {}; + const rCache = await rivenCaches[req.platform].getData(); + + Object.keys(rCache).forEach((type) => { + Object.keys(rCache[type]).forEach((compatibility) => { + if (compatibility.toLowerCase().includes(query.toLowerCase())) { + results[compatibility] = rCache[type][compatibility]; + } + }); + }); + res.setHeader('Content-Language', req.language); + res.json(results); +})); + +module.exports = router; diff --git a/src/controllers/rss.js b/src/controllers/rss.js new file mode 100644 index 000000000..498526d0b --- /dev/null +++ b/src/controllers/rss.js @@ -0,0 +1,15 @@ +'use strict'; + +const express = require('express'); + +const router = express.Router(); + +const { logger, worldState } = require('../lib/utilities'); + +router.get('/', (req, res) => { + logger.silly(`Got ${req.originalUrl}`); + + res.json(worldState.getRss()); +}); + +module.exports = router; diff --git a/controllers/staticWfData.js b/src/controllers/staticWfData.js similarity index 91% rename from controllers/staticWfData.js rename to src/controllers/staticWfData.js index 3a7cca310..2bc06da11 100644 --- a/controllers/staticWfData.js +++ b/src/controllers/staticWfData.js @@ -1,116 +1,114 @@ -'use strict'; - -const express = require('express'); - -const router = express.Router(); - -const { - logger, setHeadersAndJson, warframeData, solKeys, -} = require('../lib/utilities'); - -const dataKeys = Object.keys(warframeData); - -router.use((req, res, next) => { - req.key = (req.baseUrl.replace('/', '').trim().split('/')[0] || ''); - - dataKeys.forEach((dKey) => { - if (req.key.toLowerCase() === dKey.toLowerCase()) { - req.key = dKey; - } - }); - - if (!Object.keys(warframeData).includes(req.key)) { - req.key = undefined; - } - - next(); -}); - -router.get('/', /* cache('10 hours'), */ (req, res) => { - logger.silly(`Got ${req.originalUrl}`); - setHeadersAndJson(res, warframeData[req.key]); -}); - -router.get('/search/:query', /* cache('10 hours'), */ (req, res) => { - logger.silly(`Got ${req.originalUrl}`); - let values = []; - let results = []; - let keyResults = []; - const nodeResults = []; - const queries = req.params.query.split(',').map(q => q.trim()); - - queries.forEach(async (q) => { - const loweredQuery = q.toLowerCase(); - let value; - switch (req.key) { - case 'arcanes': - results = warframeData.arcanes - .filter(arcanes => (new RegExp(arcanes.regex)).test(loweredQuery) - || arcanes.name.toLowerCase().includes(loweredQuery.toLowerCase())); - value = results.length > 0 ? results : []; - break; - - case 'tutorials': - results = warframeData.tutorials - .filter(tutorial => (new RegExp(tutorial.regex)).test(loweredQuery) - || tutorial.name.toLowerCase().includes(loweredQuery)); - value = results.length > 0 ? results : []; - break; - - case 'solNodes': - keyResults = solKeys - .filter(solNodeKey => solNodeKey.toLowerCase().includes(loweredQuery)); - solKeys.forEach((solKey) => { - if (warframeData.solNodes[solKey] - && warframeData.solNodes[solKey].value.toLowerCase().includes(loweredQuery)) { - nodeResults.push(warframeData.solNodes[solKey]); - } - }); - if (values[0]) { - if (values[0].keys) { - values[0].keys = values[0].keys.concat(keyResults); - } - if (values[0].nodes) { - values[0].nodes = values[0].nodes.concat(nodeResults); - } - } else { - // eslint-disable-next-line no-case-declarations - value = { keys: keyResults, nodes: nodeResults }; - } - break; - - case 'synthTargets': - results = []; - // Loop through the synth targets, checking if the name contains the search string - warframeData.synthTargets.forEach((synth) => { - if (synth.name.toLowerCase().includes(loweredQuery)) { - results.push(synth); - } - }); - value = results; - break; - - default: - Object.keys(warframeData[req.key]).forEach((selectedDataKey) => { - if (selectedDataKey.toLowerCase().includes(loweredQuery)) { - results.push(warframeData[req.key][selectedDataKey]); - } - }); - value = results; - break; - } - if (value) { - values = values.concat(value); - } - }); - - if (req.key === 'solNodes' && values[0]) { - values[0] = { - keys: Array.from(new Set(values[0].keys)), - nodes: Array.from(new Set(values[0].nodes)), - }; - } - setHeadersAndJson(res, values); -}); - -module.exports = router; +'use strict'; + +const express = require('express'); + +const router = express.Router(); + +const { logger, warframeData, solKeys } = require('../lib/utilities'); + +const dataKeys = Object.keys(warframeData); + +router.use((req, res, next) => { + req.key = (req.baseUrl.replace('/', '').trim().split('/')[0] || ''); + + dataKeys.forEach((dKey) => { + if (req.key.toLowerCase() === dKey.toLowerCase()) { + req.key = dKey; + } + }); + + if (!Object.keys(warframeData).includes(req.key)) { + req.key = undefined; + } + + next(); +}); + +router.get('/', /* cache('10 hours'), */ (req, res) => { + logger.silly(`Got ${req.originalUrl}`); + res.json(warframeData[req.key]); +}); + +router.get('/search/:query', /* cache('10 hours'), */ (req, res) => { + logger.silly(`Got ${req.originalUrl}`); + let values = []; + let results = []; + let keyResults = []; + const nodeResults = []; + const queries = req.params.query.split(',').map(q => q.trim()); + + queries.forEach(async (q) => { + const loweredQuery = q.toLowerCase(); + let value; + switch (req.key) { + case 'arcanes': + results = warframeData.arcanes + .filter(arcanes => (new RegExp(arcanes.regex)).test(loweredQuery) + || arcanes.name.toLowerCase().includes(loweredQuery.toLowerCase())); + value = results.length > 0 ? results : []; + break; + + case 'tutorials': + results = warframeData.tutorials + .filter(tutorial => (new RegExp(tutorial.regex)).test(loweredQuery) + || tutorial.name.toLowerCase().includes(loweredQuery)); + value = results.length > 0 ? results : []; + break; + + case 'solNodes': + keyResults = solKeys + .filter(solNodeKey => solNodeKey.toLowerCase().includes(loweredQuery)); + solKeys.forEach((solKey) => { + if (warframeData.solNodes[solKey] + && warframeData.solNodes[solKey].value.toLowerCase().includes(loweredQuery)) { + nodeResults.push(warframeData.solNodes[solKey]); + } + }); + if (values[0]) { + if (values[0].keys) { + values[0].keys = values[0].keys.concat(keyResults); + } + if (values[0].nodes) { + values[0].nodes = values[0].nodes.concat(nodeResults); + } + } else { + // eslint-disable-next-line no-case-declarations + value = { keys: keyResults, nodes: nodeResults }; + } + break; + + case 'synthTargets': + results = []; + // Loop through the synth targets, checking if the name contains the search string + warframeData.synthTargets.forEach((synth) => { + if (synth.name.toLowerCase().includes(loweredQuery)) { + results.push(synth); + } + }); + value = results; + break; + + default: + Object.keys(warframeData[req.key]).forEach((selectedDataKey) => { + if (selectedDataKey.toLowerCase().includes(loweredQuery)) { + results.push(warframeData[req.key][selectedDataKey]); + } + }); + value = results; + break; + } + if (value) { + values = values.concat(value); + } + }); + + if (req.key === 'solNodes' && values[0]) { + values[0] = { + keys: Array.from(new Set(values[0].keys)), + nodes: Array.from(new Set(values[0].nodes)), + }; + } + res.json(values); +}); + +module.exports = router; diff --git a/src/controllers/twitter.js b/src/controllers/twitter.js new file mode 100644 index 000000000..17043fa2c --- /dev/null +++ b/src/controllers/twitter.js @@ -0,0 +1,22 @@ +'use strict'; + +const express = require('express'); + +const router = express.Router(); + +const { + logger, cache, ah, worldState, +} = require('../lib/utilities'); + +router.get('/', cache('1 minute'), ah(async (req, res) => { + logger.silly(`Got ${req.originalUrl}`); + + const twd = await worldState.getTwitter(); + if (twd) { + res.json(twd); + } else { + res.status(404).json({ code: 404, message: 'No Twitter Data' }); + } +})); + +module.exports = router; diff --git a/controllers/wfItems.js b/src/controllers/wfItems.js similarity index 75% rename from controllers/wfItems.js rename to src/controllers/wfItems.js index 7b07dd7ce..cb6f3edaf 100644 --- a/controllers/wfItems.js +++ b/src/controllers/wfItems.js @@ -1,79 +1,77 @@ -'use strict'; - -const express = require('express'); - -const router = express.Router(); - -const { - Items, logger, setHeadersAndJson, -} = require('../lib/utilities'); - -const wfItemData = { - weapons: { - items: new Items({ category: ['Primary', 'Secondary', 'Melee'] }), - name: 'Weapon', - }, - warframes: { - items: new Items({ category: ['Warframes'] }), - name: 'Warframe', - }, - items: { - items: new Items(), - name: 'Item', - }, - mods: { - items: new Items({ category: ['Mods'] }), - name: 'Mod', - }, -}; - -router.use((req, res, next) => { - req.items = (req.baseUrl.replace('/', '').trim().split('/')[0] || '').toLowerCase(); - - if (Object.keys(wfItemData).includes(req.items)) { - req.items = wfItemData[req.items]; - } - next(); -}); - - -router.get('/', /* cache('10 hours'), */ (req, res) => { - logger.silly(`Got ${req.originalUrl}`); - setHeadersAndJson(res, req.items.items); -}); - -router.get('/:item', /* cache('10 hours'),*/ (req, res) => { - logger.silly(`Got ${req.originalUrl}`); - let result; - let exact = false; - req.items.items.forEach((item) => { - if (item.name.toLowerCase() === req.params.item.toLowerCase()) { - result = item; - exact = true; - } - if (item.name.toLowerCase().indexOf(req.params.item.toLowerCase()) > -1 && !exact) { - result = item; - } - }); - if (result) { - setHeadersAndJson(res, result); - } else { - res.status(400).json({ error: `No such \`${req.items.name}\`.`, code: 400 }); - } -}); - -router.get('/search/:query', /* cache('10 hours'), */ (req, res) => { - logger.silly(`Got ${req.originalUrl}`); - const queries = req.params.query.trim().split(',').map(q => q.trim()); - const results = []; - queries.forEach((query) => { - req.items.items.forEach((item) => { - if (item.name.toLowerCase().indexOf(query) > -1) { - results.push(item); - } - }); - }); - setHeadersAndJson(res, Array.from(new Set(results))); -}); - -module.exports = router; +'use strict'; + +const express = require('express'); + +const router = express.Router(); + +const { Items, logger, noResult } = require('../lib/utilities'); + +const wfItemData = { + weapons: { + items: new Items({ category: ['Primary', 'Secondary', 'Melee'] }), + name: 'Weapon', + }, + warframes: { + items: new Items({ category: ['Warframes'] }), + name: 'Warframe', + }, + items: { + items: new Items(), + name: 'Item', + }, + mods: { + items: new Items({ category: ['Mods'] }), + name: 'Mod', + }, +}; + +router.use((req, res, next) => { + req.items = (req.baseUrl.replace('/', '').trim().split('/')[0] || '').toLowerCase(); + + if (Object.keys(wfItemData).includes(req.items)) { + req.items = wfItemData[req.items]; + } + next(); +}); + + +router.get('/', (req, res) => { + logger.silly(`Got ${req.originalUrl}`); + res.json(req.items.items); +}); + +router.get('/:item', (req, res) => { + logger.silly(`Got ${req.originalUrl}`); + let result; + let exact = false; + req.items.items.forEach((item) => { + if (item.name.toLowerCase() === req.params.item.toLowerCase()) { + result = item; + exact = true; + } + if (item.name.toLowerCase().indexOf(req.params.item.toLowerCase()) > -1 && !exact) { + result = item; + } + }); + if (result) { + res.json(result); + } else { + noResult(res); + } +}); + +router.get('/search/:query', /* cache('10 hours'), */ (req, res) => { + logger.silly(`Got ${req.originalUrl}`); + const queries = req.params.query.trim().split(',').map(q => q.trim()); + const results = []; + queries.forEach((query) => { + req.items.items.forEach((item) => { + if (item.name.toLowerCase().indexOf(query) > -1) { + results.push(item); + } + }); + }); + res.json(Array.from(new Set(results))); +}); + +module.exports = router; diff --git a/controllers/worldstate.js b/src/controllers/worldstate.js similarity index 63% rename from controllers/worldstate.js rename to src/controllers/worldstate.js index 1a9864d98..534d0a9c9 100644 --- a/controllers/worldstate.js +++ b/src/controllers/worldstate.js @@ -1,17 +1,17 @@ 'use strict'; const express = require('express'); - -const twitter = require('../lib/caches/TwitterCache'); - const { - logger, setHeadersAndJson, worldStates, ah, platforms, cache, languages, + logger, worldState, platforms, cache, languages, } = require('../lib/utilities'); -const get = async (platform, language) => { - const ws = worldStates[platform][language]; - ws.twitter = await twitter.getData(); // inject twitter data - return ws.data; +const get = (platform, language) => { + try { + return worldState.getWorldstate(platform, language); + } catch (e) { + logger.debug(e); + return undefined; + } }; const router = express.Router(); @@ -38,43 +38,43 @@ router.use((req, res, next) => { next(); }); -router.get('/', ah(async (req, res) => { - logger.silly(`Got ${req.originalUrl}`); - const ws = await get(req.platform, req.language); +router.get('/', (req, res) => { + logger.verbose(`Got ${req.originalUrl}`); + const ws = get(req.platform, req.language); res.setHeader('Content-Language', req.language); - setHeadersAndJson(res, ws); -})); + res.json(ws); +}); router.use('/rivens', require('./rivens')); -router.get('/:field', ah(async (req, res) => { +router.get('/:field', (req, res) => { logger.silly(`Got ${req.originalUrl}`); - const ws = await get(req.platform, req.language); + const ws = get(req.platform, req.language); if (ws && ws[req.params.field]) { res.setHeader('Content-Language', req.language); - setHeadersAndJson(res, ws[req.params.field]); + res.json(ws[req.params.field]); } else if (req.params.field && languages.includes(req.params.field.substr(0, 2).toLowerCase())) { - const ows = await get(req.platform, req.params.field.substr(0, 2).toLowerCase()); - setHeadersAndJson(res, ows); + const ows = get(req.platform, req.params.field.substr(0, 2).toLowerCase()); + res.json(ows); } else { res.status(400).json({ error: 'No such worldstate field', code: 400 }); } -})); +}); -router.get('/:language/:field', cache('1 minute'), ah(async (req, res) => { +router.get('/:language/:field', cache('1 minute'), (req, res) => { logger.silly(`Got ${req.originalUrl}`); if (languages.includes(req.params.language.substr(0, 2).toLowerCase())) { req.language = req.params.language.substr(0, 2).toLowerCase(); } - const ws = await get(req.platform, req.language); + const ws = get(req.platform, req.language); if (ws[req.params.field]) { res.setHeader('Content-Language', req.language); - setHeadersAndJson(res, ws[req.params.field]); + res.json(ws[req.params.field]); } else { res.status(400).json({ error: 'No such worldstate field', code: 400 }); } -})); +}); module.exports = router; diff --git a/src/lib/addons.js b/src/lib/addons.js new file mode 100644 index 000000000..0fd4938d4 --- /dev/null +++ b/src/lib/addons.js @@ -0,0 +1,51 @@ +'use strict'; + +// monitoring +const swagger = require('swagger-stats'); + +// security +const helmet = require('helmet'); +const cors = require('cors'); + +const spec = require('../api-spec/openapi.json'); + +// Some dependency/config stuff +const adminCred = { user: process.env.ADMIN_USER, pass: process.env.ADMIN_PASS }; +const isProd = process.env.NODE_ENV === 'production'; + +const initSentry = (app) => { + if (process.env.SENTRY_DSN) { + // eslint-disable-next-line global-require + const Sentry = require('@sentry/node'); + Sentry.init({ dsn: process.env.SENTRY_DSN }); + app.use(Sentry.Handlers.requestHandler()); + app.use(Sentry.Handlers.errorHandler()); + } +}; + +const initSecurity = (app) => { + app.use(cors()); + app.use(helmet()); +}; + +const initSwagger = (app) => { + // eslint-disable-next-line max-len + const swaggerAuth = (req, user, pass) => (!isProd || (user === adminCred.user && pass === adminCred.pass)); + const swaggConfig = { + swaggerSpec: spec, + uriPath: '/meta/status', + onAuthenticate: swaggerAuth, + authentication: isProd, + }; + app.use(swagger.getMiddleware(swaggConfig)); +}; + +const init = (app) => { + initSentry(app); + initSwagger(app); + initSecurity(app); + + app.use(require('express-favicon-short-circuit')); +}; + +module.exports = { init }; diff --git a/lib/caches/Drops.js b/src/lib/caches/Drops.js similarity index 100% rename from lib/caches/Drops.js rename to src/lib/caches/Drops.js diff --git a/src/lib/logger.js b/src/lib/logger.js new file mode 100644 index 000000000..745f8d2a7 --- /dev/null +++ b/src/lib/logger.js @@ -0,0 +1,50 @@ +'use strict'; + +require('colors'); +const { transports, createLogger, format } = require('winston'); + +const { + combine, label, printf, colorize, +} = format; + +const color = (scope = 'PROC') => { + let scoped; + switch (scope.toUpperCase()) { + case 'PROC': + scoped = 'PROC'.magenta; + break; + case 'REST': + scoped = 'REST'.cyan; + break; + case 'SOCK': + scoped = 'SOCK'.yellow; + break; + default: + scoped = scope.toUpperCase().red; + break; + } + return scoped; +}; + +/** + * Create a colorized scope + * @param {string} scope [description] + * @returns {Object} set up logger + */ +const setup = (scope = 'PROC') => { + /* Logger setup */ + const transport = new transports.Console({ colorize: true }); + const logFormat = printf(info => `[${info.label}] ${info.level}: ${info.message}`); + const logger = createLogger({ + format: combine( + colorize(), + label({ label: color(scope) }), + logFormat, + ), + transports: [transport], + }); + logger.level = process.env.LOG_LEVEL || 'error'; + return logger; +}; + +module.exports = setup; diff --git a/src/lib/meta/themes/dark.css b/src/lib/meta/themes/dark.css new file mode 100644 index 000000000..d1410d5af --- /dev/null +++ b/src/lib/meta/themes/dark.css @@ -0,0 +1,186 @@ +/* +https://github.com/RafalWilinski/express-status-monitor/blob/ce67cc1446d6e5b39063b4186cfd50a46428d8c2/src/public/stylesheets/default.css + +Largley using default.css +with some color changes +*/ +:root { + -internal-root-color: white; +} + +* { + font-family: Helvetica Neue, Helvetica, Arial, sans-serif; +} + +body.hide-cpu .container.cpu, +body.hide-mem .container.mem, +body.hide-load .container.load, +body.hide-responseTime .container.responseTime, +body.hide-rps .container.rps, +body.hide-statusCodes .container.statusCodes { + display: none; +} + +body { + background: #23272A; +} + +h1 { + font-size: 3em; + color: #DDD; + margin: 0; +} + +h5 { + margin: 0; + color: #888; +} + +h6 { + margin: 0; +} + +p { + font-size: 0.7em; + color: #888; +} + +span { + cursor: pointer; + font-size: 10px; + margin-left: 5px; + border: 1px solid #2C2F33; + padding: 3px 10px 4px 10px; + color: #ddd; +} + +canvas { + width: 400px; + height: 100px; +} + +.content { + width: 600px; + margin: auto; +} + +.active { + background: #111; + color: #fff; +} + +.stats-column { + flex: 0 0 200px; +} + +.header b { + color: #99AAB5; +} + +.container { + display: flex; + flex-direction: row; + margin-top: 20px; + height: 100px; +} + +.chart-container { + background-color: #23272A; + width: 400px; + height: 100px; +} + +.container.healthChecks { + display: block; + height: auto; +} + +.health-check-row { + align-items: center; + border: 1px solid #2C2F33; + border-radius: 4px; + display: flex; + margin: 0 0 10px 0; + width: 100%; +} + +.health-check-title-column { + flex: 0 0 400px; + display: flex; + align-items: center; + padding: 0 10px; +} + +.health-check-title-column h5 a { + color: #ddd; + cursor: pointer; + text-decoration: none; +} + +.health-check-title-column h5 a:hover { + text-decoration: underline; +} + +.health-check-status-container { + align-items: center; + border-radius: 0 4px 4px 0; + display: flex; + justify-content: center; + height: 2em; + text-align: center; + width: 200px; +} + +.health-check-status-container.ok { + background: #75D701; +} + +.health-check-status-container.failed { + background: #E53A40; +} + +.health-check-status-container h1 { + line-height: 2em; + font-size: 1.5em; + color: #2C2F33; + text-align: center; + text-transform: uppercase; +} + +.footer { + text-align: center; +} + +.span-controls { + float: right; +} + +.status-code { + margin-top: 2px; + color: white; +} + +.status-code:before { + content: ''; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 8px; + margin-right: 10px; +} + +.status-code-2xx:before { + background-color: #75D701; +} + +.status-code-3xx:before { + background-color: #47b8e0; +} + +.status-code-4xx:before { + background-color: #ffc952; +} + +.status-code-5xx:before { + background-color: #E53A40; +} diff --git a/src/lib/utilities.js b/src/lib/utilities.js new file mode 100644 index 000000000..f4a743c19 --- /dev/null +++ b/src/lib/utilities.js @@ -0,0 +1,68 @@ +'use strict'; + +require('colors'); +const ah = require('express-async-handler'); +const warframeData = require('warframe-worldstate-data'); +const Items = require('warframe-items'); +const WorldstateEmitter = require('worldstate-emitter'); +const apiCache = require('apicache'); + +const initLogger = require('./logger'); + +const platforms = ['pc', 'ps4', 'xb1', 'swi']; +const platformAliases = ['ns']; + +const worldState = new WorldstateEmitter({ platform: 'pc', locale: 'en' }); + +/** + * Group an array by a field value + * @param {Object[]} array array of objects to broup + * @param {string} field field to group by + * @returns {Object} [description] + */ +const groupBy = (array, field) => { + const grouped = {}; + if (!array) return undefined; + array.forEach((item) => { + const fVal = item[field]; + if (!grouped[fVal]) { + grouped[fVal] = []; + } + grouped[fVal].push(item); + }); + return grouped; +}; + +/* Logger setup */ +const logger = initLogger('REST'); + +const socketLogger = initLogger('SOCK'); + +/* Warframe Data & Keys */ +delete warframeData.weapons; +delete warframeData.warframes; + +const titleCase = str => str.toLowerCase().replace(/\b\w/g, l => l.toUpperCase()); + +const noResult = (res) => { + res.status(400).json({ error: 'No Result.', code: 400 }); +}; + +module.exports = { + logger, + platforms, + platformAliases, + cache: apiCache.options({ + appendKey: req => `${req.platform}${req.language}` || '', + }).middleware, + Items, + warframeData, + solKeys: Object.keys(warframeData.solNodes), + ah, + titleCase, + socketLogger, + groupBy, + languages: warframeData.locales, + worldState, + noResult, +}; diff --git a/src/main.js b/src/main.js new file mode 100644 index 000000000..0f2a24fa6 --- /dev/null +++ b/src/main.js @@ -0,0 +1,10 @@ +'use strict'; + +// Some dependency/config stuff +const port = process.env.PORT || 3001; +const host = process.env.HOSTNAME || process.env.HOST || process.env.IP || 'localhost'; + +const server = require('./server'); +require('./socket')(server); + +server.listen(port, host); diff --git a/src/server.js b/src/server.js new file mode 100644 index 000000000..8b63badeb --- /dev/null +++ b/src/server.js @@ -0,0 +1,33 @@ +'use strict'; + +// eslint-disable-next-line import/no-extraneous-dependencies +if (process.env.NODE_ENV === 'development') require('dotenv').config(); + +const express = require('express'); + +const app = express(); + +const server = require('http').createServer(app); + +const addons = require('./lib/addons'); + +const { logger } = require('./lib/utilities'); + +if (!global.__basedir) { + global.__basedir = __dirname; +} + +// middleware +app.use(express.json()); +addons.init(app); + +// actual api routes +logger.info('Setting up routes...'); +app.use(require('./controllers')); + +// oh no, nothing...fallback catch-all +app.use((req, res) => { + res.status(404).json({ error: 'No such route.', code: 404 }).end(); +}); + +module.exports = server; diff --git a/src/socket.js b/src/socket.js new file mode 100644 index 000000000..7241a2859 --- /dev/null +++ b/src/socket.js @@ -0,0 +1,40 @@ +'use strict'; + +const WebSocket = require('ws'); + +const { socketLogger: logger, worldState } = require('./lib/utilities'); + +const port = process.env.PORT || 3001; +const host = process.env.HOSTNAME || process.env.HOST || process.env.IP || 'localhost'; + +const handler = require('./sockets'); +const heartbeater = require('./sockets/beater'); + +const init = (server) => { + const wss = new WebSocket.Server({ server, path: '/socket' }); + + const broadcast = (event, packet) => { + wss.clients.forEach((client) => { + if (client.readyState === WebSocket.OPEN) { + client.send(JSON.stringify({ event, packet })); + } + }); + }; + + heartbeater(wss); + wss.on('connection', handler); + wss.on('error', logger.error); + + // subscribable events + worldState.on('tweet', packet => broadcast('tweet', packet)); + worldState.on('rss', packet => broadcast('rss', packet)); + worldState.on('ws:update:event', (packet) => { + broadcast('ws:event', packet); + broadcast(packet.key, packet); + }); + worldState.on('ws:update:parsed', packet => broadcast('ws:update', packet)); + + logger.info(`Started listening on wss://${host}:${port}/socket`); +}; + +module.exports = init; diff --git a/src/sockets/beater.js b/src/sockets/beater.js new file mode 100644 index 000000000..040a34799 --- /dev/null +++ b/src/sockets/beater.js @@ -0,0 +1,31 @@ +'use strict'; + +const { socketLogger: logger } = require('../lib/utilities'); + +const noop = () => {}; + +function heartbeat() { + this.isAlive = true; +} + +const init = (wss) => { + wss.on('connection', (ws) => { + ws.isAlive = true; + ws.on('pong', heartbeat); + }); + + const interval = setInterval(() => { + wss.clients.forEach((ws) => { + if (ws.isAlive === false) return ws.terminate(); + + ws.isAlive = false; + ws.ping(noop); + return 0; + }); + }, 30000); + + wss.on('close', () => clearInterval(interval)); + wss.on('pong', () => logger.debug('pong!')); +}; + +module.exports = init; diff --git a/src/sockets/index.js b/src/sockets/index.js new file mode 100644 index 000000000..e76cd2a1c --- /dev/null +++ b/src/sockets/index.js @@ -0,0 +1,60 @@ +'use strict'; + +const { socketLogger: logger, worldState } = require('../lib/utilities'); + +const safeParse = (data) => { + try { + return JSON.parse(data); + } catch (e) { + return {}; + } +}; + +const requestWS = ({ platform, language } = {}) => { + if (!platform && !language) { + return { code: 500, message: `Provided platform (${platform}) or language (${language}) not provided.` }; + } + try { + const ws = worldState.getWorldstate(platform, language); + return { platform, language, ws }; + } catch (e) { + return { code: 500, message: e.message.split('.')[0] }; + } +}; + +/** + * Handle websocket requests + * @param {WebSocket} socket ws to handled requests for + * @param {IncomingMessage} req incoming request + */ +const index = (socket, req) => { + logger.info(`socket connection established with ${req.socket.remoteAddress}`); + socket.send(JSON.stringify({ event: 'connected', status: 200 })); + + /** + * Handle message data + * @type {MessageEvent} + */ + socket.on('message', (data) => { + const request = safeParse(data); + logger.info(`socket received request for ${request.event}`); + + switch (request.event) { + case 'ws:req': + socket.send(JSON.stringify({ event: 'ws:provide', packet: requestWS(request.packet) })); + break; + case 'twitter': + socket.send(JSON.stringify({ event: 'twitter:provide', packet: worldState.getTwitter() })); + case 'rss': + socket.send(JSON.stringify({ event: 'rss:provide', packet: worldState.getRss() })); + default: + socket.send(JSON.stringify({ status: 400 })); + break; + } + }); + + socket.on('close', reason => logger.warn(`socket disconnected because ${reason}`)); + socket.on('error', logger.error); +}; + +module.exports = index; diff --git a/test/tester.js b/test/tester.js new file mode 100644 index 000000000..7c4ff24af --- /dev/null +++ b/test/tester.js @@ -0,0 +1,52 @@ +'use strict'; + +const WebSocket = require('ws'); + +const logger = require('../src/lib/logger')('PROC'); + +logger.level = 'debug'; + +const client = new WebSocket('ws://localhost:3001/socket'); + +let pingTimeout; + +function heartbeat() { + clearTimeout(pingTimeout); + pingTimeout = setTimeout(this.terminate, 30000 + 1000); +} + +client.on('open', heartbeat); +client.on('ping', heartbeat); +client.on('close', () => { clearTimeout(pingTimeout); }); + +client.on('message', (d) => { + const packed = JSON.parse(d); + switch (packed.event) { + case 'ws:provide': + logger.info(`${packed.packet.platform} ${packed.packet.language} ${packed.packet.ws.timestamp}`); + break; + case 'twitter:provide': + // do nothing + break; + case 'rss': + logger.info(packed.packet.title); + break; + case 'twitter': + logger.info(JSON.stringify(packed.packet.text)); + break; + case 'ws:update': + // do nothing + break; + default: + if (packed.status) { + logger.info(`${packed.event}: ${packed.status}`); + } else { + logger.info(packed.event); + logger.info(Object.keys(packed)); + // console.log(`${packed.platform} ${packed.langauge} ${packed.ws.timestamp}`); + } + break; + } +}); + +client.on('open', () => client.send(JSON.stringify({ event: 'ws:req', packet: { platform: 'pc', language: 'en' } }))); From f9b19557bcc45eb6704fe30d91680bc2765eac56 Mon Sep 17 00:00:00 2001 From: Tobiah Date: Thu, 23 Jul 2020 00:03:48 -0500 Subject: [PATCH 2/8] chore: docs --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/README.md b/README.md index c7bc599e7..81fa2003e 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,55 @@ Simple express app that parses worldState.php [![PS4 API Status](https://img.shields.io/website/https/api.warframestat.us/ps4.svg?down_message=down&label=ps4%20api&logo=playstation&up_message=up)](https://api.warframestat.us/ps4) [![XB1 API Status](https://img.shields.io/website/https/api.warframestat.us/xb1.svg?down_message=down&label=xb1%20api&logo=xbox&up_message=up)](https://api.warframestat.us/xb1) [![Switch API Status](https://img.shields.io/website/https/api.warframestat.us/swi.svg?down_message=down&label=switch%20api&logo=nintendo-switch&up_message=up)](https://api.warframestat.us/swi) + +## Access + +### REST-ish: + +- `http://$host:$port/$platform` +- `http://$host:$port/$platform/$child-item` +- `http://$host:$port/$a-bunch-of-static-data` + + +### Sockets: + +`ws://$host:$port/sockets` + +Requests taken as json strings in the socket packet. + +Consumers will need to parse responses and stringify requests yourself, as ws doesn't provide a way to automatically parse them. + +- ```json +{"event": "ws:req", "packet": { "platform": "$platform", "language": "$language" }} +``` +- ```json +{ "event": "twitter" } +``` +- ```json +{ "event": "rss" } +``` + + +connecting automatically subscribes the connection to events structured as: +```json +{ + "event": "twitter", + "packet": // tweets +} +``` + +```json +{ + "event": "ws:update", + "packet": // entire updated worldstate +} +``` + +```json +{ + "event": // worldstate key, + "packet": // worldstate key data +} +``` + +probably several others that can take some experimenting \ No newline at end of file From edba982192a654302e3c7e4d6acc1e425c6fed26 Mon Sep 17 00:00:00 2001 From: Tobiah Date: Mon, 10 Aug 2020 10:28:41 -0500 Subject: [PATCH 3/8] chore: dependencies --- package-lock.json | 122 +++++++++++++++++++++++----------------------- package.json | 10 ++-- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81bd9b98b..319a31f6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,71 +35,71 @@ } }, "@sentry/apm": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@sentry/apm/-/apm-5.20.0.tgz", - "integrity": "sha512-6zfMRYXG/9VzsmgQqYqFFvg/5XJYOimY/KIrJAijemMLb0Xhwu3xw/2eelWxkWilTBXUWO+dQel5P7JBA8QsKw==", - "requires": { - "@sentry/browser": "5.20.0", - "@sentry/hub": "5.20.0", - "@sentry/minimal": "5.20.0", - "@sentry/types": "5.20.0", - "@sentry/utils": "5.20.0", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@sentry/apm/-/apm-5.20.1.tgz", + "integrity": "sha512-oqfyYqRR1CaM/U5qZg3KY9MxCe4OWYs3uiOvVGMOHCyx50dYsDZziM5DDVUvi6pOuokLCNbyXO9xGROSmploBQ==", + "requires": { + "@sentry/browser": "5.20.1", + "@sentry/hub": "5.20.1", + "@sentry/minimal": "5.20.1", + "@sentry/types": "5.20.1", + "@sentry/utils": "5.20.1", "tslib": "^1.9.3" } }, "@sentry/browser": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.20.0.tgz", - "integrity": "sha512-xVPL7/RuAPcemfSzXiyPHAt4M+0BfzkdTlN+PZb6frCEo4k6E0UiN6WLsGj/iwa2gXhyfTQXtbTuP+tDuNPEJw==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.20.1.tgz", + "integrity": "sha512-ClykuvrEsMKgAvifx5VHzRjchwYbJFX8YiIicYx+Wr3MXL2jLG6OEfHHJwJeyBL2C3vxd5O0KPK3pGMR9wPMLA==", "requires": { - "@sentry/core": "5.20.0", - "@sentry/types": "5.20.0", - "@sentry/utils": "5.20.0", + "@sentry/core": "5.20.1", + "@sentry/types": "5.20.1", + "@sentry/utils": "5.20.1", "tslib": "^1.9.3" } }, "@sentry/core": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.20.0.tgz", - "integrity": "sha512-fzzWKEolc0O6H/phdDenzKs7JXDSb0sooxVn0QCUkwWSzACALQh+NR/UciOXyhyuoUiqu4zthYQx02qtGqizeQ==", - "requires": { - "@sentry/hub": "5.20.0", - "@sentry/minimal": "5.20.0", - "@sentry/types": "5.20.0", - "@sentry/utils": "5.20.0", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.20.1.tgz", + "integrity": "sha512-gG622/UY2TePruF6iUzgVrbIX5vN8w2cjlWFo1Est8MvCfQsz8agGaLMCAyl5hCGJ6K2qTUZDOlbCNIKoMclxg==", + "requires": { + "@sentry/hub": "5.20.1", + "@sentry/minimal": "5.20.1", + "@sentry/types": "5.20.1", + "@sentry/utils": "5.20.1", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.20.0.tgz", - "integrity": "sha512-DiU8fpjAAMOgSx5tsTekMtHPCAtSNWSNS91FFkDCqPn6fYG+/aK/hB5kTlJwr+GTM1815+WWrtXP6y2ecSmZuA==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.20.1.tgz", + "integrity": "sha512-Nv5BXf14BEc08acDguW6eSqkAJLVf8wki283FczEvTsQZZuSBHM9cJ5Hnehr6n+mr8wWpYLgUUYM0oXXigUmzQ==", "requires": { - "@sentry/types": "5.20.0", - "@sentry/utils": "5.20.0", + "@sentry/types": "5.20.1", + "@sentry/utils": "5.20.1", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.20.0.tgz", - "integrity": "sha512-oA+0g7p3bapzjgGKQIkSjcjA85VG1HPmjxBD9wpRvNjmYuVmm80Cl1H/P+xg/hupw/kNmASAX4IOd5Z9pEeboA==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.20.1.tgz", + "integrity": "sha512-2PeJKDTHNsUd1jtSLQBJ6oRI+xrIJrYDQmsyK/qs9D7HqHfs+zNAMUjYseiVeSAFGas5IcNSuZbPRV4BnuoZ0w==", "requires": { - "@sentry/hub": "5.20.0", - "@sentry/types": "5.20.0", + "@sentry/hub": "5.20.1", + "@sentry/types": "5.20.1", "tslib": "^1.9.3" } }, "@sentry/node": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.20.0.tgz", - "integrity": "sha512-xOSP+sWptQff1dQR8G9DCpATT99odsnEpg+X/uqW6bUvjfgsabiPN4nc/orwkTNtm4MhffZiXVq48IAgl/x8Uw==", - "requires": { - "@sentry/apm": "5.20.0", - "@sentry/core": "5.20.0", - "@sentry/hub": "5.20.0", - "@sentry/types": "5.20.0", - "@sentry/utils": "5.20.0", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.20.1.tgz", + "integrity": "sha512-43YFDnD7Rv+vGHV+Fmb3LaSSWrFzsPmFRu3wmf9eYMgWiuDks6c6/kWRCgkqX9Np9ImC89wcTZs/V6S4MlOm4g==", + "requires": { + "@sentry/apm": "5.20.1", + "@sentry/core": "5.20.1", + "@sentry/hub": "5.20.1", + "@sentry/types": "5.20.1", + "@sentry/utils": "5.20.1", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", @@ -107,16 +107,16 @@ } }, "@sentry/types": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.0.tgz", - "integrity": "sha512-/9tiGiXBRsOKM66HeCpt0iSF0vnAIqHzXgC97icNQIstx/ZA8tcLs9540cHDeaN0cyZUyZF1o8ECqcLXGNODWQ==" + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.20.1.tgz", + "integrity": "sha512-OU+i/lcjGpDJv0XkNpsKrI2r1VPp8qX0H6Knq8NuZrlZe3AbvO3jRJJK0pH14xFv8Xok5jbZZpKKoQLxYfxqsw==" }, "@sentry/utils": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.20.0.tgz", - "integrity": "sha512-w0AeAzWEf35h9U9QL/4lgS9MqaTPjeSmQYNU/n4ef3FKr+u8HP68Ra7NZ0adiKgi67Yxr652kWopOLPl7CxvZg==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.20.1.tgz", + "integrity": "sha512-dhK6IdO6g7Q2CoxCbB+q8gwUapDUH5VjraFg0UBzgkrtNhtHLylqmwx0sWQvXCcp14Q/3MuzEbb4euvoh8o8oA==", "requires": { - "@sentry/types": "5.20.0", + "@sentry/types": "5.20.1", "tslib": "^1.9.3" } }, @@ -176,9 +176,9 @@ } }, "@types/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-invOmosX0DqbpA+cE2yoHGUlF/blyf7nB0OGYBBiH27crcVm5NmFaZkLP4Ta1hGaesckCi5lVLlydNJCxkTOSg==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.7.tgz", + "integrity": "sha512-sOdDRU3oRS7LBNTIqwDkPJyq0lpHYcbMTt0TrjzsXbk/e37hcLTH6eZX7CdbDeN0yJJvzw9hFBZkbtCSbk/jAQ==", "dev": true, "requires": { "@types/express": "*" @@ -3822,9 +3822,9 @@ } }, "rss-feed-emitter": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/rss-feed-emitter/-/rss-feed-emitter-3.2.0.tgz", - "integrity": "sha512-Q9K0LvGuPHr/K3hRxCvrMOqLWg5CyZNQVZ9c/9Kg7KdnjeWze8cyP0Q33TjX0jisMybFMFjgjuAkvlLykPnUnw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/rss-feed-emitter/-/rss-feed-emitter-3.2.1.tgz", + "integrity": "sha512-aL4SEbC/xWweftbXeMWhmsYWJdCQTgnvU9H8Z5xbmkYnuZSsJqVTUvzmjdP2/JCHmSUA+5nHrqQY/5PnlOIx2Q==", "requires": { "feedparser": "1.1.4", "request": "^2.88.2" @@ -4732,9 +4732,9 @@ } }, "warframe-items": { - "version": "1.1016.0", - "resolved": "https://registry.npmjs.org/warframe-items/-/warframe-items-1.1016.0.tgz", - "integrity": "sha512-UNfd8z2y4aWApwnCK15KzLOcjTbe1xiJ3ZESsf2sC5rSLkmEG2/cCpMGObf43jFcBTvXRX0Bv5occTokXoiszQ==" + "version": "1.1049.0", + "resolved": "https://registry.npmjs.org/warframe-items/-/warframe-items-1.1049.0.tgz", + "integrity": "sha512-6CtO9XyBWiFLhDjsdjR6Zruhk1cfWgiwrORpjCWrzbCgL2bbvVBP4zR0bnduTRP/fnSTRLWsZPvv6/6hs+b7dg==" }, "warframe-nexus-query": { "version": "1.6.13", @@ -4878,9 +4878,9 @@ "dev": true }, "worldstate-emitter": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/worldstate-emitter/-/worldstate-emitter-1.0.2.tgz", - "integrity": "sha512-ewuxtPh7SwOP+IJ8Bg8t2u05ajE/KX3rbdCUIS0T/cbQSQxho2mjoeVcnALSkSt4RKsTPvEpGFg/+KPaQp6kGQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/worldstate-emitter/-/worldstate-emitter-1.0.3.tgz", + "integrity": "sha512-OoBGeZ8t7gj+k7q1s8CQNZvsW6ytUnGzKscYPz1n1isJJ2pfymMbeFr1/jb/BlKg5VsJT2trHFPOTZA7VYc+Ow==", "requires": { "@sentry/node": "^5.18.1", "colors": "^1.4.0", diff --git a/package.json b/package.json index 61d10476e..80cb598a9 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "author": "tobitenno", "repository": "https://github.com/wfcd/warframe-status.git", "dependencies": { - "@sentry/node": "^5.20.0", + "@sentry/node": "^5.20.1", "apicache": "^1.5.3", "colors": "^1.4.0", "cors": "^2.8.5", @@ -15,12 +15,12 @@ "json-fetch-cache": "^1.2.6", "nexushub-client": "^1.2.0", "swagger-stats": "^0.95.11", - "warframe-items": "^1.1016.0", + "warframe-items": "^1.1049.0", "warframe-nexus-query": "^1.6.13", "warframe-worldstate-data": "^1.7.13", "warframe-worldstate-parser": "^2.14.2", "winston": "^3.3.3", - "worldstate-emitter": "^1.0.2", + "worldstate-emitter": "^1.0.3", "ws": "^7.3.1" }, "optionalDependencies": { @@ -28,9 +28,9 @@ "utf-8-validate": "^5.0.2" }, "devDependencies": { - "@sentry/types": "^5.20.0", + "@sentry/types": "^5.20.1", "@types/apicache": "^1.2.2", - "@types/cors": "^2.8.6", + "@types/cors": "^2.8.7", "@types/express": "^4.17.7", "@types/helmet": "0.0.43", "@types/socket.io": "^2.1.10", From ccc447a0cba18e4612bfa1635edf57ecdbf29f31 Mon Sep 17 00:00:00 2001 From: Tobiah Date: Mon, 10 Aug 2020 10:52:45 -0500 Subject: [PATCH 4/8] chore: clean up code climate issues --- .codeclimate.yml | 24 ++++++++++++++++++++++++ src/controllers/rivens.js | 4 ++-- src/controllers/staticWfData.js | 14 ++++++++------ src/controllers/wfItems.js | 5 ++++- src/lib/utilities.js | 3 +++ 5 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 .codeclimate.yml diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 000000000..4e5672d05 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,24 @@ +--- +version: "2" +ratings: + paths: + - "src/**.js" +plugins: + eslint: + enabled: true + config: + config: ./.eslintrc.json + duplication: + enabled: false + checks: + Similar code: + enabled: false + config: + languages: + - javascript + fixme: + enabled: true +checks: + method-lines: + config: + threshold: 50 diff --git a/src/controllers/rivens.js b/src/controllers/rivens.js index 66986a034..5298a1e02 100644 --- a/src/controllers/rivens.js +++ b/src/controllers/rivens.js @@ -3,7 +3,7 @@ const express = require('express'); const Cache = require('json-fetch-cache'); const { - logger, ah, platforms, titleCase, + logger, ah, platforms, titleCase, trimPlatform, } = require('../lib/utilities'); const router = express.Router(); @@ -48,7 +48,7 @@ platforms.forEach((platform) => { }); router.use((req, res, next) => { - req.platform = (req.baseUrl.replace('/', '').trim().split('/')[0] || '').toLowerCase(); + req.platform = trimPlatform(req.baseUrl); if (req.platform === 'ns') { req.platform = 'swi'; } diff --git a/src/controllers/staticWfData.js b/src/controllers/staticWfData.js index 2bc06da11..977f28964 100644 --- a/src/controllers/staticWfData.js +++ b/src/controllers/staticWfData.js @@ -8,6 +8,12 @@ const { logger, warframeData, solKeys } = require('../lib/utilities'); const dataKeys = Object.keys(warframeData); +const overwriteResults = (parent, results) => { + if (parent) { + parent = parent.concat(results); + } +}; + router.use((req, res, next) => { req.key = (req.baseUrl.replace('/', '').trim().split('/')[0] || ''); @@ -65,12 +71,8 @@ router.get('/search/:query', /* cache('10 hours'), */ (req, res) => { } }); if (values[0]) { - if (values[0].keys) { - values[0].keys = values[0].keys.concat(keyResults); - } - if (values[0].nodes) { - values[0].nodes = values[0].nodes.concat(nodeResults); - } + overwriteResults(values[0].keys, keyResults); + overwriteResults(values[0].nodes, nodeResults); } else { // eslint-disable-next-line no-case-declarations value = { keys: keyResults, nodes: nodeResults }; diff --git a/src/controllers/wfItems.js b/src/controllers/wfItems.js index cb6f3edaf..940ffd959 100644 --- a/src/controllers/wfItems.js +++ b/src/controllers/wfItems.js @@ -4,7 +4,9 @@ const express = require('express'); const router = express.Router(); -const { Items, logger, noResult } = require('../lib/utilities'); +const { + Items, logger, noResult, trimPlatform, +} = require('../lib/utilities'); const wfItemData = { weapons: { @@ -26,6 +28,7 @@ const wfItemData = { }; router.use((req, res, next) => { + req.platform = trimPlatform(req.baseUrl); req.items = (req.baseUrl.replace('/', '').trim().split('/')[0] || '').toLowerCase(); if (Object.keys(wfItemData).includes(req.items)) { diff --git a/src/lib/utilities.js b/src/lib/utilities.js index f4a743c19..876349384 100644 --- a/src/lib/utilities.js +++ b/src/lib/utilities.js @@ -14,6 +14,8 @@ const platformAliases = ['ns']; const worldState = new WorldstateEmitter({ platform: 'pc', locale: 'en' }); +const trimPlatform = (path) => path.replace('/', '').trim().split('/')[0] || '').toLowerCase(); + /** * Group an array by a field value * @param {Object[]} array array of objects to broup @@ -65,4 +67,5 @@ module.exports = { languages: warframeData.locales, worldState, noResult, + trimPlatform, }; From 697e37fbf0fa9d677d303b40ff8508d562b5e0ce Mon Sep 17 00:00:00 2001 From: Tobiah Date: Mon, 10 Aug 2020 11:03:10 -0500 Subject: [PATCH 5/8] fix(style): fix bad parens --- src/lib/utilities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utilities.js b/src/lib/utilities.js index 876349384..0ff06fcb8 100644 --- a/src/lib/utilities.js +++ b/src/lib/utilities.js @@ -14,7 +14,7 @@ const platformAliases = ['ns']; const worldState = new WorldstateEmitter({ platform: 'pc', locale: 'en' }); -const trimPlatform = (path) => path.replace('/', '').trim().split('/')[0] || '').toLowerCase(); +const trimPlatform = path => (path.replace('/', '').trim().split('/')[0] || '').toLowerCase(); /** * Group an array by a field value From 973c52a0d7dc6341ab505a925afef0e0ba5e05fe Mon Sep 17 00:00:00 2001 From: Tobiah Date: Mon, 10 Aug 2020 11:19:34 -0500 Subject: [PATCH 6/8] chore: remove todo --- src/controllers/worldstate.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/worldstate.js b/src/controllers/worldstate.js index 534d0a9c9..15167107e 100644 --- a/src/controllers/worldstate.js +++ b/src/controllers/worldstate.js @@ -26,7 +26,6 @@ router.use((req, res, next) => { req.platform = 'pc'; } - // TODO: figure out how to keep this with the rest req.language = (req.header('Accept-Language') || 'en').substr(0, 2).toLowerCase(); req.language = (req.query.language || req.language || 'en').substr(0, 2); if (req.language !== 'en') { From d45bf770679bc7c34ebd5c16605625a5fd97eb72 Mon Sep 17 00:00:00 2001 From: Tobiah Date: Mon, 10 Aug 2020 11:21:13 -0500 Subject: [PATCH 7/8] chore: simplify for codeclimate --- src/controllers/pricecheck.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/controllers/pricecheck.js b/src/controllers/pricecheck.js index 17b23ac1f..3b2bc6b55 100644 --- a/src/controllers/pricecheck.js +++ b/src/controllers/pricecheck.js @@ -12,16 +12,15 @@ const router = express.Router(); let nexus; let nexusQuerier; -let nexusOptions; +const nexusOptions = { + user_key: process.env.NEXUSHUB_USER_KEY || undefined, + user_secret: process.env.NEXUSHUB_USER_SECRET || undefined, + api_url: process.env.NEXUS_API_OVERRIDE || undefined, + auth_url: process.env.NEXUS_AUTH_OVERRIDE || undefined, + ignore_limiter: true, +}; if (!process.env.DISABLE_PRICECHECKS) { - nexusOptions = { - user_key: process.env.NEXUSHUB_USER_KEY || undefined, - user_secret: process.env.NEXUSHUB_USER_SECRET || undefined, - api_url: process.env.NEXUS_API_OVERRIDE || undefined, - auth_url: process.env.NEXUS_AUTH_OVERRIDE || undefined, - ignore_limiter: true, - }; nexus = new NexusFetcher(nexusOptions.nexusKey && nexusOptions.nexusSecret ? nexusOptions : {}); From cafdd3452b32323bc02387b386760d4554816ac4 Mon Sep 17 00:00:00 2001 From: Tobiah Date: Mon, 10 Aug 2020 18:06:32 -0500 Subject: [PATCH 8/8] chore: update procfile for new start location --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index 489b2700a..063b78f46 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: node server.js +web: npm start