refactor: squash commits

This commit is contained in:
dec0dOS 2021-03-21 22:25:13 +03:00
commit 1e6e237aa3
107 changed files with 20077 additions and 0 deletions

41
backend/.gitignore vendored Normal file
View file

@ -0,0 +1,41 @@
# Data
data
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Dependency directories
node_modules/
jspm_packages/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

75
backend/app.js Normal file
View file

@ -0,0 +1,75 @@
const express = require("express");
const path = require("path");
const logger = require("morgan");
const compression = require("compression");
const bearerToken = require("express-bearer-token");
const helmet = require("helmet");
const db = require("./utils/db");
const initAdmin = require("./utils/init-admin");
const authRoutes = require("./routes/auth");
const networkRoutes = require("./routes/network");
const memberRoutes = require("./routes/member");
const controllerRoutes = require("./routes/controller");
const app = express();
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(
bearerToken({
headerKey: "Bearer",
})
);
if (
process.env.NODE_ENV === "production" &&
process.env.ZU_SECURE_HEADERS !== "false"
) {
app.use(helmet());
}
if (
process.env.NODE_ENV === "production" &&
process.env.ZU_SERVE_FRONTEND !== "false"
) {
app.use(compression());
app.use(
["/app", "/app/*"],
express.static(path.join(__dirname, "..", "frontend", "build"))
);
app.get(["/app/network/*"], function (req, res) {
res.sendFile(path.join(__dirname, "..", "frontend", "build", "index.html"));
});
app.get("/", function (req, res) {
res.redirect("/app");
});
}
initAdmin().then(function (admin) {
db.defaults({ users: [admin], networks: {} }).write();
});
const routerAPI = express.Router();
const routerController = express.Router();
routerAPI.use("/network", networkRoutes);
routerAPI.use("/network/:nwid/member", memberRoutes);
routerController.use("", controllerRoutes);
app.use("/auth", authRoutes);
app.use("/api", routerAPI); // offical SaaS API compatible
app.use("/controller", routerController); // other controller-specific routes
// error handlers
app.get("*", async function (req, res) {
res.status(404).json({ error: "404 Not found" });
});
app.use(async function (err, req, res) {
console.error(err.stack); // TODO: replace with production logger
res.status(500).json({ error: "500 Internal server error" });
});
module.exports = app;

87
backend/bin/www Executable file
View file

@ -0,0 +1,87 @@
#!/usr/bin/env node
require("dotenv").config();
/**
* Module dependencies.
*/
var app = require("../app");
var debug = require("debug")("zero-ui:server");
var http = require("http");
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || "4000");
app.set("port", port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on("error", onError);
server.on("listening", onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== "listen") {
throw error;
}
var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case "EACCES":
console.error(bind + " requires elevated privileges");
process.exit(1);
break;
case "EADDRINUSE":
console.error(bind + " is already in use");
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
debug("Listening on " + bind);
}

6
backend/jsconfig.json Normal file
View file

@ -0,0 +1,6 @@
{
"exclude": ["node_modules", "**/node_modules/*"],
"typeAcquisition": {
"exclude": ["dotenv"]
}
}

21
backend/package.json Normal file
View file

@ -0,0 +1,21 @@
{
"name": "zero-ui-backend",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"axios": "^0.21.1",
"compression": "^1.7.4",
"debug": "~4.3.1",
"dotenv": "^8.2.0",
"express": "~4.17.1",
"express-bearer-token": "^2.4.0",
"helmet": "^4.4.1",
"lodash": "^4.17.21",
"lowdb": "^1.0.0",
"morgan": "~1.10.0",
"p-debounce": "^3.0.1",
"pbkdf2-wrapper": "^1.3.2"
}
}

22
backend/routes/auth.js Normal file
View file

@ -0,0 +1,22 @@
const express = require("express");
const router = express.Router();
const auth = require("../services/auth");
router.post("/login", async function (req, res) {
if (req.body.username && req.body.password) {
auth.authorize(req.body.username, req.body.password, function (err, user) {
if (user) {
res.send({ token: user["token"] });
} else {
res.status(401).send({
error: err.message,
});
}
});
} else {
res.status(400).send({ error: "Specify username and password" });
}
});
module.exports = router;

View file

@ -0,0 +1,13 @@
const express = require("express");
const router = express.Router();
const auth = require("../services/auth");
const api = require("../utils/controller-api");
router.get("/status", auth.isAuthorized, async function (req, res) {
api.get("status").then(function (controllerRes) {
res.send(controllerRes.data);
});
});
module.exports = router;

85
backend/routes/member.js Normal file
View file

@ -0,0 +1,85 @@
const express = require("express");
const router = express.Router({ mergeParams: true });
const auth = require("../services/auth");
const member = require("../services/member");
const api = require("../utils/controller-api");
// get all members
router.get("/", auth.isAuthorized, async function (req, res) {
const nwid = req.params.nwid;
api
.get("controller/network/" + nwid + "/member")
.then(async function (controllerRes) {
const mids = Object.keys(controllerRes.data);
const data = await member.getMembersData(nwid, mids);
res.send(data);
})
.catch(function () {
res.status(404).send({ error: "Network not found" });
});
});
// get member
router.get("/:mid", auth.isAuthorized, async function (req, res) {
const nwid = req.params.nwid;
const mid = req.params.mid;
const data = await member.getMembersData(nwid, [mid]);
if (data[0]) {
res.send(data[0]);
} else {
res.status(404).send({ error: "Member not found" });
}
});
// update member
router.post("/:mid", auth.isAuthorized, async function (req, res) {
const nwid = req.params.nwid;
const mid = req.params.mid;
member.updateMemberAdditionalData(nwid, mid, req.body);
if (req.body.config) {
api
.post("controller/network/" + nwid + "/member/" + mid, req.body.config)
.then(async function () {
const data = await member.getMembersData(nwid, [mid]);
res.send(data[0]);
})
.catch(function (err) {
res.status(500).send({ error: err.message });
});
} else {
const data = await member.getMembersData(nwid, [mid]);
res.send(data[0]);
}
});
// delete member
router.delete("/:mid", auth.isAuthorized, async function (req, res) {
const nwid = req.params.nwid;
const mid = req.params.mid;
member.deleteMemberAdditionalData(nwid, mid);
api
.delete("controller/network/" + nwid + "/member/" + mid)
.then(function () {})
.catch(function (err) {
res.status(500).send({ error: err.message });
});
// Need this to fix ZT controller bug https://github.com/zerotier/ZeroTierOne/issues/859
const defaultConfig = {
authorized: false,
ipAssignments: [],
capabilities: [],
tags: [],
};
api
.post("controller/network/" + nwid + "/member/" + mid, defaultConfig)
.then(function (controllerRes) {
res.status(controllerRes.status).send("");
})
.catch(function (err) {
res.status(500).send({ error: err.message });
});
});
module.exports = router;

90
backend/routes/network.js Normal file
View file

@ -0,0 +1,90 @@
const express = require("express");
const router = express.Router();
const auth = require("../services/auth");
const network = require("../services/network");
const api = require("../utils/controller-api");
const constants = require("../utils/constants");
const getZTAddress = require("../utils/zt-address");
let ZT_ADDRESS = null;
getZTAddress().then(function (address) {
ZT_ADDRESS = address;
});
// get all networks
router.get("/", auth.isAuthorized, async function (req, res) {
api.get("controller/network").then(async function (controllerRes) {
const nwids = controllerRes.data;
const data = await network.getNetworksData(nwids);
res.send(data);
});
});
// create new network
router.post("/", auth.isAuthorized, async function (req, res) {
let reqData = req.body;
if (reqData.config) {
const config = reqData.config;
delete reqData.config;
reqData = config;
reqData.rules = JSON.parse(constants.defaultRules);
} else {
res.status(400).send({ error: "Bad request" });
}
api
.post("controller/network/" + ZT_ADDRESS + "______", reqData)
.then(async function (controllerRes) {
await network.createNetworkAdditionalData(controllerRes.data);
const data = await network.getNetworksData([controllerRes.data.id]);
res.send(data[0]);
});
});
// get network
router.get("/:nwid", auth.isAuthorized, async function (req, res) {
const nwid = req.params.nwid;
const data = await network.getNetworksData([nwid]);
if (data[0]) {
res.send(data[0]);
} else {
res.status(404).send({ error: "Network not found" });
}
});
// update network
router.post("/:nwid", auth.isAuthorized, async function (req, res) {
const nwid = req.params.nwid;
network.updateNetworkAdditionalData(nwid, req.body);
if (req.body.config) {
api
.post("controller/network/" + nwid, req.body.config)
.then(async function () {
const data = await network.getNetworksData([nwid]);
res.send(data[0]);
})
.catch(function (err) {
res.status(500).send({ error: err.message });
});
} else {
const data = await network.getNetworksData([nwid]);
res.send(data[0]);
}
});
// delete network
router.delete("/:nwid", auth.isAuthorized, async function (req, res) {
const nwid = req.params.nwid;
network.deleteNetworkAdditionalData(nwid);
api
.delete("controller/network/" + nwid)
.then(function (controllerRes) {
res.status(controllerRes.status).send("");
})
.catch(function (err) {
res.status(500).send({ error: err.message });
});
});
module.exports = router;

33
backend/services/auth.js Normal file
View file

@ -0,0 +1,33 @@
const db = require("../utils/db");
const verifyHash = require("pbkdf2-wrapper/verifyHash");
exports.authorize = authorize;
async function authorize(username, password, callback) {
try {
var users = await db.get("users");
} catch (err) {
throw err;
}
const user = users.find({ username: username });
if (!user.value()) return callback(new Error("Cannot find user"));
const verified = await verifyHash(password, user.value()["password_hash"]);
if (verified) {
return callback(null, user.value());
} else {
return callback(new Error("Invalid password"));
}
}
exports.isAuthorized = isAuthorized;
async function isAuthorized(req, res, next) {
if (req.token) {
const user = await db.get("users").find({ token: req.token }).value();
if (user) {
next();
} else {
res.status(403).send({ error: "Invalid token" });
}
} else {
res.status(401).send({ error: "Specify token" });
}
}

172
backend/services/member.js Normal file
View file

@ -0,0 +1,172 @@
const _ = require("lodash");
const axios = require("axios");
const api = require("../utils/controller-api");
const db = require("../utils/db");
const getZTAddress = require("../utils/zt-address");
let ZT_ADDRESS = null;
getZTAddress().then(function (address) {
ZT_ADDRESS = address;
});
async function getPeer(mid) {
try {
const peer = await api.get("peer/" + mid);
return peer.data;
} catch (err) {
return;
}
}
async function getMemberAdditionalData(data) {
const additionalData = db
.get("networks")
.find({ id: data.nwid })
.get("members")
.find({ id: data.id })
.get("additionalConfig")
.value();
const peer = await getPeer(data.id);
let peerData = {};
if (peer) {
peerData.latency = peer.latency;
peerData.online = peer.latency !== -1;
peerData.clientVersion = peer.version;
if (peer.paths[0]) {
peerData.lastOnline = peer.paths[0].lastReceive;
peerData.physicalAddress = peer.paths[0].address.split("/")[0];
}
}
delete data.lastAuthorizedCredential;
delete data.lastAuthorizedCredentialType;
delete data.objtype;
delete data.remoteTraceLevel;
delete data.remoteTraceTarget;
return {
id: data.nwid + "-" + data.id,
type: "Member",
clock: Math.floor(new Date().getTime() / 1000),
networkId: data.nwid,
nodeId: data.id,
controllerId: ZT_ADDRESS,
...additionalData,
...peerData,
config: data,
};
}
async function filterDeleted(nwid, mid) {
const member = db
.get("networks")
.find({ id: nwid })
.get("members")
.find({ id: mid });
if (!member.get("deleted").value()) return mid;
else return;
}
exports.getMembersData = getMembersData;
async function getMembersData(nwid, mids) {
const prefix = "/controller/network/" + nwid + "/member/";
const filtered = (
await Promise.all(mids.map(async (mid) => await filterDeleted(nwid, mid)))
).filter((item) => item !== undefined);
const links = filtered.map((mid) => prefix + mid);
const multipleRes = await axios
.all(links.map((l) => api.get(l)))
.then(
axios.spread(function (...res) {
return res;
})
)
.catch(function () {
return [];
});
let data = Promise.all(
multipleRes.map((el) => {
return getMemberAdditionalData(el.data);
})
);
return data;
}
exports.updateMemberAdditionalData = updateMemberAdditionalData;
async function updateMemberAdditionalData(nwid, mid, data) {
if (data.config && data.config.authorized) {
db.get("networks")
.filter({ id: nwid })
.map("members")
.first()
.filter({ id: mid })
.first()
.set("deleted", false)
.write();
}
let additionalData = {};
if (data.hasOwnProperty("name")) {
additionalData.name = data.name;
}
if (data.hasOwnProperty("description")) {
additionalData.description = data.description;
}
if (additionalData) {
const member = db
.get("networks")
.filter({ id: nwid })
.map("members")
.first()
.filter({ id: mid });
if (member.value().length) {
member
.map("additionalConfig")
.map((additionalConfig) => _.assign(additionalConfig, additionalData))
.write();
} else {
additionalData = { name: "", description: "" };
if (data.hasOwnProperty("name")) {
additionalData.name = data.name;
}
if (data.hasOwnProperty("description")) {
additionalData.description = data.description;
}
db.get("networks")
.filter({ id: nwid })
.map("members")
.first()
.push({ id: mid, additionalConfig: additionalData })
.write();
}
}
}
exports.deleteMemberAdditionalData = deleteMemberAdditionalData;
async function deleteMemberAdditionalData(nwid, mid) {
// ZT controller bug
/* db.get("networks")
.find({ id: nwid })
.get("members")
.remove({ id: mid })
.write();
*/
db.get("networks")
.filter({ id: nwid })
.map("members")
.first()
.filter({ id: mid })
.first()
.set("deleted", true)
.write();
}

View file

@ -0,0 +1,94 @@
const _ = require("lodash");
const axios = require("axios");
const api = require("../utils/controller-api");
const db = require("../utils/db");
const constants = require("../utils/constants");
async function getNetworkAdditionalData(data) {
let additionalData = db
.get("networks")
.find({ id: data.id })
.get("additionalConfig");
if (!additionalData.value()) {
createNetworkAdditionalData(data);
}
delete data.rulesSource;
delete data.objtype;
delete data.revision;
delete data.remoteTraceLevel;
delete data.remoteTraceTarget;
return {
id: data.id,
type: "Network",
clock: Math.floor(new Date().getTime() / 1000),
...additionalData.value(),
config: data,
};
}
exports.getNetworksData = getNetworksData;
async function getNetworksData(nwids) {
const prefix = "/controller/network/";
const links = nwids.map((nwid) => prefix + nwid);
const multipleRes = await axios
.all(links.map((l) => api.get(l)))
.then(
axios.spread(function (...res) {
return res;
})
)
.catch(function () {
return [];
});
let data = Promise.all(
multipleRes.map((el) => {
return getNetworkAdditionalData(el.data);
})
);
return data;
}
exports.createNetworkAdditionalData = createNetworkAdditionalData;
async function createNetworkAdditionalData(data) {
const saveData = {
id: data.id,
additionalConfig: {
description: "",
rulesSource: constants.defaultRulesSource,
},
};
db.get("networks").push(saveData).write();
}
exports.updateNetworkAdditionalData = updateNetworkAdditionalData;
async function updateNetworkAdditionalData(nwid, data) {
let additionalData = {};
if (data.hasOwnProperty("description")) {
additionalData.description = data.description;
}
if (data.hasOwnProperty("rulesSource")) {
additionalData.rulesSource = data.rulesSource;
}
if (additionalData) {
db.get("networks")
.filter({ id: nwid })
.map("additionalConfig")
.map((additionalConfig) => _.assign(additionalConfig, additionalData))
.write();
}
}
exports.deleteNetworkAdditionalData = deleteNetworkAdditionalData;
async function deleteNetworkAdditionalData(nwid) {
db.get("networks").remove({ id: nwid }).write();
}

View file

@ -0,0 +1,56 @@
exports.defaultRulesSource = `
# This is a default rule set that allows IPv4 and IPv6 traffic but otherwise
# behaves like a standard Ethernet switch.
#
# Allow only IPv4, IPv4 ARP, and IPv6 Ethernet frames.
#
drop
not ethertype ipv4
and not ethertype arp
and not ethertype ipv6
;
#
# Uncomment to drop non-ZeroTier issued and managed IP addresses.
#
# This prevents IP spoofing but also blocks manual IP management at the OS level and
# bridging unless special rules to exempt certain hosts or traffic are added before
# this rule.
#
#drop
# not chr ipauth
#;
# Accept anything else. This is required since default is 'drop'.
accept;
`;
exports.defaultRules = `
[
{
"type": "MATCH_ETHERTYPE",
"not": true,
"or": false,
"etherType": 2048
},
{
"type": "MATCH_ETHERTYPE",
"not": true,
"or": false,
"etherType": 2054
},
{
"type": "MATCH_ETHERTYPE",
"not": true,
"or": false,
"etherType": 34525
},
{
"type": "ACTION_DROP"
},
{
"type": "ACTION_ACCEPT"
}
]
`;

View file

@ -0,0 +1,19 @@
const axios = require("axios");
const fs = require("fs");
const baseURL = process.env.ZU_CONTROLLER_ENDPOINT || "http://localhost:9993/";
var token;
if (process.env.ZU_CONTROLLER_TOKEN) {
token = process.env.ZU_CONTROLLER_TOKEN;
} else {
token = fs.readFileSync("/var/lib/zerotier-one/authtoken.secret", "utf8");
}
module.exports = axios.create({
baseURL: baseURL,
responseType: "json",
headers: {
"X-ZT1-Auth": token,
},
});

8
backend/utils/db.js Normal file
View file

@ -0,0 +1,8 @@
const low = require("lowdb");
const FileSync = require("lowdb/adapters/FileSync");
const adapter = new FileSync(process.env.ZU_DATAPATH || "data/db.json");
const db = low(adapter);
module.exports = db;

View file

@ -0,0 +1,16 @@
const crypto = require("crypto");
const hashPassword = require("pbkdf2-wrapper/hashText");
module.exports = async function () {
if (!process.env.ZU_DEFAULT_PASSWORD || !process.env.ZU_DEFAULT_USERNAME) {
console.error("ZU_DEFAULT_PASSWORD or ZU_DEFAULT_USERNAME not found!");
process.exit(1);
}
const username = process.env.ZU_DEFAULT_USERNAME;
const hash = await hashPassword(process.env.ZU_DEFAULT_PASSWORD);
return {
username: username,
password_hash: hash,
token: crypto.randomBytes(16).toString("hex"),
};
};

View file

@ -0,0 +1,6 @@
const api = require("../utils/controller-api");
module.exports = async function () {
const res = await api.get("status");
return res.data.address;
};

554
backend/yarn.lock Normal file
View file

@ -0,0 +1,554 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
abbott@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/abbott/-/abbott-1.1.3.tgz"
integrity sha1-JvOtm7vb/+LFa1sDdU5ZgasOXlw=
accepts@~1.3.5, accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz"
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"
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
basic-auth@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz"
integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
dependencies:
safe-buffer "5.1.2"
body-parser@1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz"
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.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
compressible@~2.0.16:
version "2.0.18"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
dependencies:
mime-db ">= 1.43.0 < 2"
compression@^1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
dependencies:
accepts "~1.3.5"
bytes "3.0.0"
compressible "~2.0.16"
debug "2.6.9"
on-headers "~1.0.2"
safe-buffer "5.1.2"
vary "~1.1.2"
content-disposition@0.5.3:
version "0.5.3"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz"
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"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
cookie-parser@^1.4.4:
version "1.4.5"
resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.5.tgz"
integrity sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==
dependencies:
cookie "0.4.0"
cookie-signature "1.0.6"
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz"
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
cookie@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz"
integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@~4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz"
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"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
depd@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
dotenv@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz"
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz"
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"
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
express-bearer-token@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/express-bearer-token/-/express-bearer-token-2.4.0.tgz"
integrity sha512-2+kRZT2xo+pmmvSY7Ma5FzxTJpO3kGaPCEXPbAm3GaoZ/z6FE4K6L7cvs1AUZwY2xkk15PcQw7t4dWjsl5rdJw==
dependencies:
cookie "^0.3.1"
cookie-parser "^1.4.4"
express@~4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz"
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"
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"
follow-redirects@^1.10.0:
version "1.13.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz"
integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz"
integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
graceful-fs@^4.1.3:
version "4.2.6"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
helmet@^4.4.1:
version "4.4.1"
resolved "https://registry.yarnpkg.com/helmet/-/helmet-4.4.1.tgz#a17e1444d81d7a83ddc6e6f9bc6e2055b994efe7"
integrity sha512-G8tp0wUMI7i8wkMk2xLcEvESg5PiCitFMYgGRc/PwULB0RVhTP5GFdxOwvJwp9XVha8CuS8mnhmE8I/8dx/pbw==
http-errors@1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz"
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"
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"
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"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
inherits@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz"
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"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
is-promise@^2.1.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz"
integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
lodash@4:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lowdb@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-1.0.0.tgz"
integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==
dependencies:
graceful-fs "^4.1.3"
is-promise "^2.1.0"
lodash "4"
pify "^3.0.0"
steno "^0.4.1"
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz"
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"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
mime-db@1.45.0:
version "1.45.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz"
integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==
"mime-db@>= 1.43.0 < 2":
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.28"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz"
integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==
dependencies:
mime-db "1.45.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
morgan@~1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz"
integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==
dependencies:
basic-auth "~2.0.1"
debug "2.6.9"
depd "~2.0.0"
on-finished "~2.3.0"
on-headers "~1.0.2"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz"
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"
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"
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"
integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
dependencies:
ee-first "1.1.1"
on-headers@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz"
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
p-debounce@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/p-debounce/-/p-debounce-3.0.1.tgz#51c38b03aa09f319ec507f1d8aba831949c8bbf2"
integrity sha512-7n7FWY/f4gmVkd6BwC2EZRbTnAmZbL/Zdrc3qbJRnwkb3OUp4HbPlEN1XybpQk0MML6RDDdePMFIr4dOXXfPNw==
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz"
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"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
pbkdf2-wrapper@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/pbkdf2-wrapper/-/pbkdf2-wrapper-1.3.2.tgz"
integrity sha512-McL8NfgXcIsLewiKd8MS4vQO+Q0JuQ7fxEAOIIKs/FJt49fnuJDRG6nkSp0TpXVjRydTllmhALFfPckc3zcA8w==
dependencies:
righto "^6.1.3"
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
proxy-addr@~2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz"
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"
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"
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"
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"
righto@^6.1.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/righto/-/righto-6.1.3.tgz"
integrity sha512-tfnK3e10FjBCKSfVI69vJCzSCsHNaxCK7pdEhnxGM89KxHm4ykxT5B1jq6Xoj12+vK1atUvcKwAIFG84IBrPLw==
dependencies:
abbott "^1.1.3"
setimmediate "^1.0.5"
safe-buffer@5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz"
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"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
send@0.17.1:
version "0.17.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz"
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"
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"
setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
setprototypeof@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz"
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"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
steno@^0.4.1:
version "0.4.4"
resolved "https://registry.yarnpkg.com/steno/-/steno-0.4.4.tgz"
integrity sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=
dependencies:
graceful-fs "^4.1.3"
toidentifier@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz"
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"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz"
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"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=