From 8ae2dc83c9efd523cef9d61bf9a8f2bba906bcfc Mon Sep 17 00:00:00 2001 From: IoTcat Date: Mon, 15 Mar 2021 10:03:03 +0800 Subject: [PATCH] ini --- package.json | 13 ++ src/.gitignore | 1 + src/index.js | 59 ++++++ src/modules/api.js | 140 ++++++++++++ src/modules/log.js | 29 +++ src/modules/node.js | 281 ++++++++++++++++++++++++ src/modules/nodetable.js | 269 +++++++++++++++++++++++ src/modules/ns.js | 74 +++++++ yarn.lock | 446 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 1312 insertions(+) create mode 100644 package.json create mode 100644 src/.gitignore create mode 100644 src/index.js create mode 100644 src/modules/api.js create mode 100644 src/modules/log.js create mode 100644 src/modules/node.js create mode 100644 src/modules/nodetable.js create mode 100644 src/modules/ns.js create mode 100644 yarn.lock diff --git a/package.json b/package.json new file mode 100644 index 0000000..8876315 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "wiot-director", + "version": "0.0.1", + "description": "Director for wIoT systems.", + "main": "index.js", + "repository": "https://wiot.js.org", + "author": "iotcat", + "license": "MIT", + "dependencies": { + "express": "^4.17.1", + "log4js": "^6.3.0" + } +} diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..8fce603 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +data/ diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..0e4efc7 --- /dev/null +++ b/src/index.js @@ -0,0 +1,59 @@ +const logger = require(__dirname + '/modules/log.js')(); +const nodetable = require(__dirname + '/modules/nodetable.js')(logger); +const node = require(__dirname + '/modules/node.js')(logger, nodetable); +const ns = require(__dirname + '/modules/ns.js')(logger, node); +const api = require(__dirname + '/modules/api.js')(logger, node, ns); + + +let OnlineBoard = []; + +setInterval(async ()=>{ + //console.log(await node.good.restart()); + //console.log('ddd') + //console.log(await node.good.refresh()); + //console.log(await node.good.setNS('ccc', '192.168.3.253', 6789)); + //console.log(await node.good.checkNS('ccc', '111.111.11.11', 6789)); + //console.log(await node.good.setFunc('333Func', 'tmr.create():alarm(3000, tmr.ALARM_AUTO, function() print("hello") end)')); + + /*l = await ns.get(['good', 'good3'], { + 'good': { + 'good3': { + port: 22, + ip: '111.222.333.44' + } + } + })*/ + + let online = []; + + Object.keys(node).forEach(nid => { + if(node[nid].status){ + online.push(nid); + } + }); + + if(online.some(nid => OnlineBoard.indexOf(nid) == -1)){ + OnlineBoard = online; + await ns.set(await ns.get(online/*, { + 'good': { + 'good3': { + port: 22, + ip: '111.222.333.44' + } + }, + 'good3': { + 'good': { + port: 22, + ip: '111.222.333.44' + } + }, + }*/)) + } + + + +},15000) + + + + diff --git a/src/modules/api.js b/src/modules/api.js new file mode 100644 index 0000000..4dd711d --- /dev/null +++ b/src/modules/api.js @@ -0,0 +1,140 @@ +module.exports = (logger, node, ns) => { + + + const log = logger.getLogger('weblog'); + const express = require('express'); + const app = express() + const port = 3001; + + app.get('/', (req, res) => { + res.send('Hello World!') + }) + + app.listen(port, () => { + log.info('API web server begin at ', port); + }) + +function isJson(str) { + try { + if (typeof JSON.parse(str) == "object") { + return true; + } + } catch(e) { + } + return false; + } + + app.get('/status', (req, res) => { + if(!req.query.hasOwnProperty('query')){ + res.status(500).send(); + return; + } + if(!isJson(req.query.query)){ + res.status(500).send(); + return; + } + + let query = JSON.parse(req.query.query); + + let resData = {}; + query.forEach(nid => { + if(node.hasOwnProperty(nid)){ + resData[nid] = {}; + resData[nid].status = node[nid].status; + resData[nid].funcID = node[nid].info.funcID; + resData[nid].port = node[nid].info.localport; + resData[nid].ip = node[nid].info.localip; + resData[nid].ns = node[nid].ns; + resData[nid].heap = node[nid].info.heap; + resData[nid].spiff = node[nid].info.spiff; + resData[nid].HeartbeatInterval = node[nid].info.HeartbeatInterval; + resData[nid].LastUpTime = node[nid].info.LastUpTime; + resData[nid].LastActiveTime = node[nid].info.LastActiveTime; + resData[nid].LastRefreshTime = node[nid].LastRefreshTime; + resData[nid].LastRestartTime = node[nid].LastRestartTime; + } + }); + + res.send(resData); + log.info('[status]', JSON.stringify(req.query.query)); + }) + + + + app.get('/setFunc', async (req, res) => { + if(!req.query.hasOwnProperty('nid') || !req.query.hasOwnProperty('funcID') || !req.query.hasOwnProperty('func')){ + res.status(500).send(); + return; + } + + let nid = req.query.nid, + funcID = req.query.funcID, + func = new Buffer(req.query.func, 'base64').toString() + + if(!node.hasOwnProperty(nid)){ + res.status(404).send(); + return; + } + + + if(!node[nid].status){ + res.status(503).send(); + return; + } + + + let status = await node[nid].setFunc(funcID, func); + + + + res.send({status: status}); + log.info('[setFunc]', '<'+nid+'>', funcID, status); + }) + + + + app.get('/restart', async (req, res) => { + if(!req.query.hasOwnProperty('nid')){ + res.status(500).send(); + return; + } + + let nid = req.query.nid; + + if(!node.hasOwnProperty(nid)){ + res.status(404).send(); + return; + } + + + if(!node[nid].status){ + res.status(503).send(); + return; + } + + + let status = await node[nid].restart(); + res.send({status: status}); + log.info('[restart]', '<'+nid+'>', status); + }) + + + + + + app.get('/log', (req, res) => { + if(!req.query.hasOwnProperty('type') || !req.query.hasOwnProperty('start')){ + res.status(500).send(); + return; + } + + res.send(); + log.info('[log]', req.query.type); + }) + + + + + + return null; +} \ No newline at end of file diff --git a/src/modules/log.js b/src/modules/log.js new file mode 100644 index 0000000..30af0c6 --- /dev/null +++ b/src/modules/log.js @@ -0,0 +1,29 @@ +module.exports = (node, nodetable) => { + + const LOG_PATH = __dirname + '/../data/log/'; + + + const log4js = require('log4js'); + + + log4js.configure({ + appenders: { + flow: {type: 'file', filename: LOG_PATH + 'flow.log'}, + access: {type: 'file', filename: LOG_PATH + 'access.log'}, + event: {type: 'file', filename: LOG_PATH + 'event.log'}, + nslog: {type: 'file', filename: LOG_PATH + 'ns.log'}, + weblog: {type: 'file', filename: LOG_PATH + 'web.log'}, + console: { type: 'console' } + }, + categories: { + flow: {appenders: [/*'flow',*/ 'console'], level: 'info' }, + //access: { appenders: ['access'], level: 'info' }, + event: {appenders: [/*'event', */'console'], level: 'info' }, + nslog: {appenders: [/*'nslog',*/ 'console'], level: 'info' }, + weblog: {appenders: [/*'weblog',*/ 'console'], level: 'info' }, + default: { appenders: ['console'], level: 'info' } + } + }); + + return log4js; +} \ No newline at end of file diff --git a/src/modules/node.js b/src/modules/node.js new file mode 100644 index 0000000..0a35ff1 --- /dev/null +++ b/src/modules/node.js @@ -0,0 +1,281 @@ +module.exports = (logger, nodetable) => { + + + let nodes = {} + const CMD_DELAY = 200; + + let delay = (time_ms) => new Promise(resolve => { + setTimeout(()=>{ + resolve(); + }, time_ms); + }); + + + + const event = logger.getLogger('event'); + + + + + + + nodetable.newNode((nid, info) => { + nodes[nid] = { + send: (name, body, isudp) => { + return nodetable.outgo(nid, { + to: nid, + from: "director", + name: name, + body: body + }, isudp); + }, + triggers: { + income: [ + function(name, body){ + if(name == '__getInfo'){ + nodes[nid].info.localport = body.port; + nodes[nid].info.localip = body.ip; + nodes[nid].info.funcID = body.funcID; + nodes[nid].info.HeartbeatInterval = body.HeartbeatInterval; + nodes[nid].ns = body.ns; + nodes[nid].LastRefreshTime = new Date().valueOf(); + } + }, + function(name, body){ + if(name == '__checkNS'){ + if(!nodes[nid].rns.hasOwnProperty(body.from) || nodes[nid].rns[body.from].ip != body.ip || nodes[nid].rns[body.from].port != body.port){ + nodes[nid].rns[body.from] = body; + nodes[nid].rns[body.from].updated = true; + } + nodes[nid].rns[body.from].LastCheckTime = new Date().valueOf(); + } + } + ], + connect: [], + disconnect: [], + restart: [], + error: [] + }, + LastRefreshTime: 0, + LastRestartTime: 0, + ns: {}, + rns: {}, + on: (type, cb) => { + nodes[nid].triggers[type].push(cb); + }, + restart: () => new Promise((resolve) => { + let restartTime = new Date().valueOf(); + let counter = 100; + nodes[nid].send('__restart', ''); + let timer = () => { + setTimeout(()=>{ + if(nodes[nid].info.LastUpTime < restartTime){ + if(counter){ + if(nodes[nid].info.LastActiveTime > restartTime + 500){ + nodes[nid].send('__restart', ''); + restartTime = new Date().valueOf(); + counter = 100; + } + timer(); + counter --; + }else{ + resolve(false); + return; + } + }else{ + event.info('[CMD]', '<'+nid+'>', '__restart'); + resolve(true); + return; + } + }, 300); + }; + timer(); + }), + refresh: () => new Promise((resolve) => { + let startTime = new Date().valueOf(); + let counter = 100; + nodes[nid].send('__getInfo', ''); + let timer = () => { + setTimeout(async ()=>{ + if(nodes[nid].LastRefreshTime < startTime){ + if(counter){ + if(counter == 70 || counter == 40){ + nodes[nid].send('__getInfo', ''); + } + timer(); + counter--; + }else{ + resolve(false); + return; + } + }else{ + await delay(CMD_DELAY) + event.info('[CMD]', '<'+nid+'>', '__refresh'); + resolve(true); + return; + } + }, 300); + }; + timer(); + }), + setNS: (nsArr) => new Promise(async (resolve) => { + + + let checkLocalNS = () => Object.keys(nodes[nid].ns).every(id => { + if(nsArr.hasOwnProperty(id) && nodes[nid].ns[id].ip == nsArr[id].ip && nodes[nid].ns[id].port == nsArr[id].port){ + return true; + }else{ + return false; + } + }) && Object.keys(nsArr).every(id => { + if(nodes[nid].ns.hasOwnProperty(id) && nodes[nid].ns[id].ip == nsArr[id].ip && nodes[nid].ns[id].port == nsArr[id].port){ + return true; + }else{ + return false; + } + }); + + + + if(checkLocalNS()){ + resolve(true); + return; + } + + + let body = {}; + Object.keys(nsArr).forEach(id => { + body[id] = { + ip: nsArr[id].ip, + port: nsArr[id].port + } + }); + + + nodes[nid].send('__setNS', body); + + + await delay(CMD_DELAY); + + if(checkLocalNS()){ + event.info('[CMD]', '<'+nid+'>', '__setNS', JSON.stringify(Object.keys(body))); + resolve(true); + }else{ + resolve(false); + } + + }), + checkNS: (id, ip, port) => new Promise(async (resolve) => { + + nodes[nid].send('__checkNS', { + to: id, + from: nid, + ip: ip, + port: port + }, true); + + await delay(CMD_DELAY); + + nodes[nid].send('__checkNS', { + to: id, + from: nid, + ip: ip, + port: port + },true); + + await delay(CMD_DELAY); + + event.info('[CMD]', '<'+nid+'>', '__checkNS', id); + resolve(); + + }), + setFunc: (id, func) => new Promise(async resolve => { + let restartTime = new Date().valueOf(); + let counter = 100; + if(nodes[nid].info.funcID == id) { + resolve(true); + return; + } + nodes[nid].send('__setFunc', { + func: { + id: id, + online: func + } + }); + let timer = () => { + setTimeout(()=>{ + if(nodes[nid].info.LastUpTime < restartTime || nodes[nid].info.funcID != id){ + if(counter){ + if(nodes[nid].info.LastActiveTime > restartTime + 500){ + nodes[nid].send('__setFunc', { + func: { + id: id, + online: func + } + }); + restartTime = new Date().valueOf(); + //counter = 100; + } + timer(); + counter --; + }else{ + resolve(false); + return; + } + }else{ + event.info('[CMD]', '<'+nid+'>', '__setFunc', id, func); + resolve(true); + return; + } + }, 300); + }; + timer(); + }) + }; + nodes[nid].info = info; + }); + + + nodetable.connect((nid, info) => { + nodes[nid].status = true; + if(new Date().valueOf() < nodes[nid].info.LastUpTime + nodes[nid].info.HeartbeatInterval){ + nodes[nid].triggers.restart.forEach(cb => { + cb(); + }); + nodes[nid].LastRestartTime = new Date().valueOf(); + event.info('[RESTART]', '<'+nid+'>', '{'+info.funcID+'}'); + } + nodes[nid].triggers.connect.forEach(cb => { + cb(); + }); + event.info('[CONNECT]', '<'+nid+'>', '{'+info.funcID+'}'); + + }); + + + nodetable.disconnect((nid, info) => { + nodes[nid].status = false; + nodes[nid].triggers.disconnect.forEach(cb => { + cb(); + }); + event.info('[DISCONNECT]', '<'+nid+'>', '{'+info.funcID+'}'); + }); + + nodetable.income((nid, info, data) => { + nodes[nid].status = true; + nodes[nid].triggers.income.forEach(cb => { + cb(data.name, data.body); + }); + }); + + + nodetable.error((nid, info) => { + nodes[nid].triggers.error.forEach(cb => { + cb(info.error); + }); + event.error('[ERROR]', '<'+nid+'>', '{'+info.funcID+'}', info.error); + }); + + + return nodes; +} \ No newline at end of file diff --git a/src/modules/nodetable.js b/src/modules/nodetable.js new file mode 100644 index 0000000..84d9423 --- /dev/null +++ b/src/modules/nodetable.js @@ -0,0 +1,269 @@ +module.exports = (logger, host = '0.0.0.0', port = 6789) => { + + let cbArr = { + income: [], + outgo: [], + forward: [], + newNode: [], + connect: [], + disconnect: [], + error: [] + }; + + + let o = { + income: cb => { //cb(nid, info{funcID, error}, data) + cbArr.income.push(cb); + }, + forward: cb => { //cb(nid, info{funcID, error}, data) + cbArr.forward.push(cb); + }, + outgo: (nid, data, isudp) => { + if(!nidtable.hasOwnProperty(nid) || !nidtable[nid].socket || (!nidtable[nid].status && !isudp)){ + flow.error('[OUTGOING]', '', data.to+'<--'+data.from, data.name, data.body); + return false; + } + try{ + let raw = JSON.stringify(data); + if(raw.length>1300){ + let rawArr = []; + let flag = require('md5')(Math.random()).substring(0,1); + let n = 1300; + for (let i = 0; i < raw.length/n; i++) { + let prefix = '&'; + if(i == 0){ + prefix = '^'; + } + if(i >= raw.length/n - 1){ + prefix = '$'; + } + setTimeout(()=>{ + console.log(prefix + flag + raw.slice(n*i, n*(i+1))) + nidtable[nid].socket.write(prefix + flag + raw.slice(n*i, n*(i+1))); + }, i*2); + } + + + }else{ + nidtable[nid].socket.write(raw); + } + if(!isudp) nidtable[nid].status = false; + }catch(e){ + flow.error('[OUTGOING]', '', data.to+'<--'+data.from, data.name, data.body); + } + + cbArr.outgo.forEach(cb => { + cb(nid, nidtable[nid], data); + }); + flow.log('[OUTGOING]', data.from + '-->' + data.to, data.name, data.body); + return true; + }, + newNode: cb => { + cbArr.newNode.push(cb); + }, + connect: cb => { + cbArr.connect.push(cb); + }, + disconnect: cb => { + cbArr.disconnect.push(cb); + }, + error: cb => { + cbArr.error.push(cb); + } + } + + + + let nidtable = {} + + + +function isJson(str) { + try { + if (typeof JSON.parse(str) == "object") { + return true; + } + } catch(e) { + } + return false; + } + + + const net = require('net'); + + + + const flow = logger.getLogger('flow'); + const access = logger.getLogger('access'); + + + const server = net.createServer((socket) => { + access.trace('Unspecified', socket.remoteAddress+':'+socket.remotePort, 'New TCP request.'); + socket.setKeepAlive(true); + let nid = null; + socket.on('data', (data) => { + data = data.toString(); + if(!isJson(data)){ + access.error(nid, socket.remoteAddress+':'+socket.remotePort, 'Data is not JSON! Data::'+data); + return; + } + data = JSON.parse(data); + + if(data.hasOwnProperty('nid') && data.hasOwnProperty('funcID') && data.hasOwnProperty('port') && data.hasOwnProperty('ip') && data.hasOwnProperty('HeartbeatInterval') && data.hasOwnProperty('uptime')){ + nid = data.nid; + let isNew = false; + if(!nidtable.hasOwnProperty(nid)){ + nidtable[nid] = {}; + isNew = true; + } + if(nidtable[nid].socket){ + nidtable[nid].socket.destroy(); + nidtable[nid].socket = null; + nidtable[nid].ip = null; + nidtable[nid].port = null; + access.info(nid, socket.remoteAddress+':'+socket.remotePort, '[CLOSE]'); + cbArr.disconnect.forEach(cb => { + cb(nid, nidtable[nid]); + }); + } + nidtable[nid].status = true; + nidtable[nid].funcID = data.funcID; + nidtable[nid].localport = data.port; + nidtable[nid].localip = data.ip; + nidtable[nid].HeartbeatInterval = data.HeartbeatInterval; + nidtable[nid].LastUpTime = new Date().valueOf() - data.uptime*1000; + nidtable[nid].LastActiveTime = new Date().valueOf(); + nidtable[nid].ip = socket.remoteAddress; + nidtable[nid].port = socket.remotePort; + nidtable[nid].socket = socket; + + socket.setTimeout(2.2 * nidtable[nid].HeartbeatInterval); + + + if(isNew){ + cbArr.newNode.forEach(cb => { + cb(nid, nidtable[nid]); + }); + } + if(data.hasOwnProperty('error') && data.error){ + nidtable[nid].error = data.error; + cbArr.error.forEach(cb => { + cb(nid, nidtable[nid]); + }); + } + cbArr.connect.forEach(cb => { + cb(nid, nidtable[nid]); + }); + + + + access.info(nid, socket.remoteAddress+':'+socket.remotePort, 'New TCP connection. Data::', JSON.stringify(data)); + return; + } + + if(!nid){ + access.error(nid, socket.remoteAddress+':'+socket.remotePort, 'No nid is specified. Data::', data); + return; + } + nidtable[nid].LastActiveTime = new Date().valueOf(); + nidtable[nid].status = true; + + if(data.hasOwnProperty('uptime') && data.hasOwnProperty('heap') && data.hasOwnProperty('spiff')){ + nidtable[nid].LastUpTime = new Date().valueOf() - data.uptime*1000; + nidtable[nid].heap = data.heap; + nidtable[nid].spiff = data.spiff; + access.info(nid, socket.remoteAddress+':'+socket.remotePort, '[HEARTBEAT]', data); + return; + } + + if(!data.hasOwnProperty('from') || !data.hasOwnProperty('to') || !data.hasOwnProperty('name') || !data.hasOwnProperty('body')){ + access.error(nid, socket.remoteAddress+':'+socket.remotePort, 'Illegal package format. Data::', data); + return; + } + + + if(data.to != 'director'){ + if(nidtable.hasOwnProperty(data.to)){ + try{ + nidtable[data.to].socket.write(JSON.stringify(data)); + flow.log('[FORWARD]', data.to+'<--'+data.from, data.name, data.body); + }catch(e){console.error(e)} + }else{ + flow.error('[FORWARD]', '', data.to+'<--'+data.from, data.name, data.body); + } + cbArr.forward.forEach(cb => { + cb(nid, nidtable[nid], data); + }); + return; + } + + + if(data.to == 'director'){ + cbArr.income.forEach(cb => { + cb(nid, nidtable[nid], data); + }); + flow.log('[INCOMING]', data.to+'<--'+data.from, data.name, data.body ); + return; + } + + + + }); + + socket.setTimeout(60 * 1000); + socket.on('timeout', () => { + access.info(nid, socket.remoteAddress+':'+socket.remotePort, '[TIMEOUT]', 'After', socket.timeout, 'ms'); + try{ + nidtable[nid].socket = null; + nidtable[nid].ip = null; + nidtable[nid].port = null; + nidtable[nid].status = false; + access.info(nid, socket.remoteAddress+':'+socket.remotePort, '[CLOSE]'); + cbArr.disconnect.forEach(cb => { + cb(nid, nidtable[nid]); + }); + }catch(e){ + delete nidtable[nid]; + } + + socket.destroy(); + }); + + socket.on('error', function(err){ + access.error(nid, socket.remoteAddress+':'+socket.remotePort, '[ERROR]', 'Exist unsent package'); + }); + + socket.on('close', function(err){ + if(nid && nidtable[nid].socket === socket){ + nidtable[nid].socket = null; + nidtable[nid].ip = null; + nidtable[nid].port = null; + nidtable[nid].status = false; + access.info(nid, socket.remoteAddress+':'+socket.remotePort, '[CLOSE]'); + cbArr.disconnect.forEach(cb => { + cb(nid, nidtable[nid]); + }); + } + }) + }).on('error', (err) => { + + access.error('[ERROR]', 'Exist unsent package'); + }); + + // Grab an arbitrary unused port. + server.listen({ + host: host, + port: port, + exclusive: true + }, () => { + access.info('TCP Server Begin at ', host + ':' + port); + }); + + + + + return o; + + + +} \ No newline at end of file diff --git a/src/modules/ns.js b/src/modules/ns.js new file mode 100644 index 0000000..f4f5531 --- /dev/null +++ b/src/modules/ns.js @@ -0,0 +1,74 @@ +module.exports = (logger, node, nodetable) => { + + + const log = logger.getLogger('nslog'); + + + + const NS_DELAY = 2000; + + + let delay = (time_ms) => new Promise(resolve => { + setTimeout(()=>{ + resolve(); + }, time_ms); + }); + + + + let o = { + get: (nodeArr, refer) => new Promise(async resolve => { + let list = []; + nodeArr.forEach(nid => { + if(node.hasOwnProperty(nid)){ + list.push(nid); + } + }); + + let nsList = {}; + let beginTime = new Date().valueOf(); + for(nid1 of list){ + for(nid2 of list){ + if(nid1 != nid2){ + log.info('[CHECK]', nid2, '-->', nid1); + await node[nid2].checkNS(nid1, refer && refer[nid2] && refer[nid2][nid1] && refer[nid2][nid1].ip || node[nid1].info.localip, refer && refer[nid2] && refer[nid2][nid1] && refer[nid2][nid1].port || node[nid1].info.localport) + } + } + }; + + await delay(NS_DELAY); + + list.forEach(nid => { + Object.keys(node[nid].rns).forEach(nid2 => { + let rns = node[nid].rns[nid2]; + if(rns.LastCheckTime > beginTime){ + log.info('[VERIFIED]', nid2, '-->', nid); + if(!nsList.hasOwnProperty(nid2)){ + nsList[nid2] = {}; + } + nsList[nid2][nid] = { + ip: rns.ip, + port: rns.port + } + } + }); + }); + + + resolve(nsList); + + }), + set: (nsList) => new Promise(async resolve => { + let status = true; + Object.keys(nsList).forEach(async nidf => { + log.info('[SET]', nidf, '::', JSON.stringify(nsList[nidf])); + status = status && await node[nidf].setNS(nsList[nidf]); + }); + resolve(status); + }) + } + + + + return o; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..84cf1b8 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,446 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +date-format@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" + integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== + +date-format@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" + integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +flatted@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inherits@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +log4js@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb" + integrity sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw== + dependencies: + date-format "^3.0.0" + debug "^4.1.1" + flatted "^2.0.1" + rfdc "^1.1.4" + streamroller "^2.2.4" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +mime-db@1.46.0: + version "1.46.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" + integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== + +mime-types@~2.1.24: + version "2.1.29" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2" + integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ== + dependencies: + mime-db "1.46.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rfdc@^1.1.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +streamroller@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.4.tgz#c198ced42db94086a6193608187ce80a5f2b0e53" + integrity sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ== + dependencies: + date-format "^2.1.0" + debug "^4.1.1" + fs-extra "^8.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=