Add emoji support
continuous-integration/drone/push Build is passing Details

Signed-off-by: Sam Therapy <sam@samtherapy.net>
This commit is contained in:
Sam Therapy 2022-08-03 01:54:55 +02:00
parent 4d4c84981c
commit 133ae0618b
Signed by: sam
GPG Key ID: 4D8B07C18F31ACBD
16 changed files with 167 additions and 243 deletions

127
index.js
View File

@ -6,17 +6,9 @@ import cors from "cors";
import errorPage from "./lib/errorPage.js";
import morgan from "morgan";
import { detector } from "megalodon";
import helmet from "helmet";
const app = Express();
app.use(
helmet({
contentSecurityPolicy: false,
crossOriginEmbedderPolicy: false,
crossOriginResourcePolicy: false,
frameguard: false,
})
);
app.disable("x-powered-by");
const logger = morgan(":method :url :status via :referrer - :response-time ms");
@ -44,13 +36,17 @@ app.get("/api/feed", cors(), logger, function (req, res) {
return;
}
const userUrl = feedUrl.replace(/\.atom.*/i, "");
let userUrl = "";
if (typeof feedUrl === "string") {
userUrl = feedUrl.replace(/\.atom.*/i, "");
}
const redirectUrl = "/api/v1/feed?";
const qs = ["userurl=" + encodeURIComponent(userUrl), "api=v1"];
["size", "theme", "boosts", "replies"].forEach((key) => {
if (typeof req.query[key] != "undefined") {
if (typeof req.query[key] !== "undefined") {
qs.push(key + "=" + encodeURIComponent(req.query[key]));
}
});
@ -64,78 +60,43 @@ app.get("/api/v1/feed", cors(), logger, async function (req, res) {
// userUrl
let type = req.query.instance_type;
let userUrl = req.query.userurl;
if (userUrl === "" || userUrl === undefined) {
if (!userUrl) {
const user = req.query.user;
const instance = req.query.instance;
if (type === "" || type === undefined) {
if (!type) {
type = await detector(instance).catch(() => "");
}
if (type === "mastodon" || type === "pleroma")
userUrl = instance + "/users/" + user;
else if (type === "misskey") userUrl = instance + "/@" + user;
else {
res
.status(400)
.send(errorPage(400, "You need to specify a user URL", null));
return;
switch (type) {
case "mastodon":
case "pleroma":
userUrl = instance + "/users/" + user;
break;
case "misskey":
userUrl = instance + "/@" + user;
break;
default:
res
.status(400)
.send(errorPage(400, "You need to specify a user URL", null));
return;
}
}
const feedUrl = req.query.feedurl;
const opts = {};
if (req.query.size) {
opts.size = req.query.size;
}
if (req.query.theme) {
opts.theme = req.query.theme;
if (opts.theme === "auto-auto") {
switch (type) {
case "mastodon":
opts.theme = "masto-auto";
break;
case "pleroma":
opts.theme = "pleroma";
break;
case "misskey":
opts.theme = "misskey-auto";
break;
default:
break;
}
} else if (opts.theme === "auto-light") {
switch (type) {
case "mastodon":
opts.theme = "masto-light";
break;
case "misskey":
opts.theme = "misskey-light";
break;
case "pleroma":
opts.theme = "pleroma-light";
break;
default:
break;
}
} else if (opts.theme === "auto-dark") {
switch (type) {
case "mastodon":
opts.theme = "masto-dark";
break;
case "misskey":
opts.theme = "misskey-dark";
break;
case "pleroma":
opts.theme = "pleroma-dark";
break;
default:
break;
}
}
}
if (req.query.header) opts.header = req.query.header.toLowerCase() === "true";
if (req.query.boosts) opts.boosts = req.query.boosts.toLowerCase() === "true";
if (req.query.replies)
opts.size = req.query.size;
opts.theme = req.query.theme;
opts.theme = opts.theme.replace("auto-", `${type}-`);
opts.theme ??= "auto-auto";
if (typeof req.query.header === "string")
opts.header = req.query.header.toLowerCase() === "true";
if (typeof req.query.boosts === "string")
opts.boosts = req.query.boosts.toLowerCase() === "true";
if (typeof req.query.replies === "string")
opts.replies = req.query.replies.toLowerCase() === "true";
opts.instance_type = type;
opts.userUrl = userUrl;
@ -152,10 +113,28 @@ app.get("/api/v1/feed", cors(), logger, async function (req, res) {
.status(500)
.send(errorPage(500, null, { theme: opts.theme, size: opts.size }));
// TODO log the error
console.error(er, er.stack);
console.error("error:", er, er.stack);
});
});
// eslint-disable-next-line no-unused-vars
app.use(function (req, res, _next) {
// respond with html page
if (req.accepts("html")) {
res.status(404).send("Not found");
return;
}
// respond with json
if (req.accepts("json")) {
res.status(404).json({ error: "Not found" });
return;
}
// default to plain-text. send()
res.status(404).type("txt").send("Not found");
});
app.listen(process.env.PORT || 8000, "127.0.0.1", function () {
console.log("Server started, listening on " + (process.env.PORT || 8000));
});

View File

@ -38,18 +38,15 @@ async function promiseSome(proms) {
prom // it's already been called
.then(resolve)
.catch(() => {
// console.warn(e);// for debugging
resolve(null);
});
});
}
return await Promise.all(proms.map(noRejectWrap));
return Promise.all(proms.map(noRejectWrap));
}
export default async function (opts) {
// let opts = opts;
let feedUrl = opts.feedUrl;
let userUrl = opts.userUrl;
let isIndex = false;
@ -72,7 +69,7 @@ export default async function (opts) {
let outbox = await apGet(user.outbox);
// outbox.first can be a string for a URL, or an object with stuffs in it
if (typeof outbox.first == "object") {
if (typeof outbox.first === "object") {
feed = outbox.first;
} else {
feed = await apGet(outbox.first);
@ -107,9 +104,9 @@ async function itemsForFeed(opts, user, feed) {
// yes, I have to fetch all the fucking boosts for this whole feed apparently >:/
let boostData = [];
let boostUrls = feed.orderedItems
.filter((i) => i.type == "Announce")
.filter((i) => i.type === "Announce")
.map((i) => i.object);
// console.log(boostUrls);
boostData = await promiseSome(boostUrls.map(apGet));
// now get user data for each of those
@ -126,8 +123,6 @@ async function itemsForFeed(opts, user, feed) {
// some URLs may have failed but IDGAF
// console.log(boostData[0]);
boostData.forEach((boostToot) => {
if (!boostToot) {
// failed request
@ -137,31 +132,31 @@ async function itemsForFeed(opts, user, feed) {
// inject in-place into items
let index = -1;
for (var i = 0; i < items.length; i++) {
if (items[i].object == boostToot.id) {
for (let i = 0; i < items.length; i++) {
if (items[i].object === boostToot.id) {
index = i;
break;
}
}
if (index == -1) {
if (index === -1) {
console.warn(`warning: couldn't match boost to item: ${boostToot}`);
return;
}
boostToot.object = boostToot; // this lets the later stage parser access object without errors :)
items[i] = boostToot;
items[index] = boostToot;
});
}
return items
.filter((item) => {
return typeof item.object == "object"; // handle weird cases
return typeof item.object === "object"; // handle weird cases
})
.map((item) => {
let enclosures = (item.object.attachment || [])
.filter((a) => {
return a.type == "Document";
return a.type === "Document";
})
.map((a) => {
return {
@ -173,6 +168,18 @@ async function itemsForFeed(opts, user, feed) {
let op = item._userdata ? item._userdata : user;
let content =
item.object && item.object.content ? item.object.content : ""; //TODO sanitize then render without entity escapes
item.object.tag.forEach((tag) => {
if (tag.type === "Emoji") {
content = content.replace(
tag.name,
`<img src="${tag.icon.url}" alt="${tag.name}" title="${tag.name}" width="32" height="32"/>`
);
}
});
return {
isBoost: !!item._userdata,
title: item._userdata
@ -183,7 +190,7 @@ async function itemsForFeed(opts, user, feed) {
isReply: !!(item.object && item.object.inReplyTo),
hasCw: item.object.sensitive || false,
cw: item.object.summary,
content: item.object && item.object.content ? item.object.content : "", //TODO sanitize then render without entity escapes
content: content,
atomHref: item.published
? item.published.replace(/\W+/g, "")
: Math.random().toString().replace("./g", ""), // used for IDs
@ -202,7 +209,7 @@ async function itemsForFeed(opts, user, feed) {
});
}
function getNextPage(opts, user, feed) {
function getNextPage(opts, _user, feed) {
//based on feed.next
if (!feed.next) {
return null;
@ -220,7 +227,7 @@ function getNextPage(opts, user, feed) {
// add other params to the end
["theme", "header", "size", "boosts", "replies"].forEach((k) => {
if (typeof opts[k] != "undefined") {
if (typeof opts[k] !== "undefined") {
ret += `&${k}=${opts[k].toString()}`;
}
});
@ -230,7 +237,6 @@ function getNextPage(opts, user, feed) {
// utilities below
function getTimeDisplay(d) {
// let d = d;
if (typeof d !== "object") {
d = new Date(d);
}

View File

@ -6,7 +6,7 @@ export default function (code, message, displayOptions) {
let msg;
// const displayOptions = displayOptions || {};
if (code == 500 && !message) {
if (code === 500 && !message) {
msg =
"<p>Sorry, we are having trouble fetching posts for this user. Please try again later.</p><br><p>If the issue persists, <a href='https://git.froth.zone/Sam/fedifeed/issues'>please open an issue on Gitea</a>, or message sam@froth.zone</p>";
} else {

View File

@ -1,21 +1,22 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>This is an iFrame how did you get here</title>
<meta charset="UTF-8"></meta>
<style type="text/css"></style>
<base target="_top" /><!-- this element is amazing-->
<% if (opts.theme && opts.theme.toLowerCase() == 'masto-light'){ %>
<% if (opts.theme && opts.theme?.toLowerCase() === 'masto-light' || opts.theme?.toLowerCase() === 'mastodon-light'){ %>
<link rel="stylesheet" href="/css/masto-light.css"></link>
<% } else if (opts.theme && opts.theme.toLowerCase() == 'masto-auto'){ %>
<% } else if (opts.theme && opts.theme?.toLowerCase() === 'masto-auto' || opts.theme?.toLowerCase() === 'mastodon-auto'){ %>
<link rel="stylesheet" href="/css/masto-auto.css"></link>
<% } else if (opts.theme && opts.theme.toLowerCase() == 'misskey-dark'){ %>
<% } else if (opts.theme && opts.theme?.toLowerCase() === 'misskey-dark' ){ %>
<link rel="stylesheet" href="/css/misskey-dark.css"></link>
<% } else if (opts.theme && opts.theme.toLowerCase() == 'misskey-light'){ %>
<% } else if (opts.theme && opts.theme?.toLowerCase() === 'misskey-light'){ %>
<link rel="stylesheet" href="/css/misskey-light.css"></link>
<% } else if (opts.theme && opts.theme.toLowerCase() == 'misskey-auto'){ %>
<% } else if (opts.theme && opts.theme?.toLowerCase() === 'misskey-auto'){ %>
<link rel="stylesheet" href="/css/misskey-auto.css"></link>
<% } else if (opts.theme && opts.theme.toLowerCase() == 'pleroma'){ %>
<% } else if (opts.theme && opts.theme?.toLowerCase() === 'pleroma'){ %>
<link rel="stylesheet" href="/css/pleroma.css"></link>
<% } else { %>
<link rel="stylesheet" href="/css/masto-dark.css"></link>
@ -36,7 +37,7 @@
<div class="header" style="<%= meta.headerImage?`background-image:url(${meta.headerImage})`:'' %>">
<a class="header-left" target="_top" href="<%= meta.link %>">
<% if (meta.avatar){ %>
<img class="avatar circular" src="<%= meta.avatar %>"></img>
<img class="avatar circular" src="<%= meta.avatar %>" alt="avatar"/>
<% } %>
</a>
<div class="description header-right">
@ -60,7 +61,7 @@
<% } %>
<div class="author">
<a target="_top" class="avatar" href="<%- item.author.uri %>">
<img class="avatar" src="<%- item.author.avatar %>"/>
<img class="avatar" src="<%- item.author.avatar %>" alt="avatar"/>
</a>
<div class="author-info">
<a target="_top" class="author-displayname" href="<%- item.author.uri %>"> <%= item.author.displayName %> </a>
@ -127,13 +128,13 @@
path: function(){
// need to query this DOM my damn self
let pageLinks = document.querySelectorAll('.hacky_link');
if (!pageLinks || pageLinks.length == 0){
if (!pageLinks || pageLinks.length === 0){
console.log ('next page link could not be found');
return false;
}else{
let finalLink = pageLinks[pageLinks.length-1].href;
// make sure we don't load the same page twice
if (!finalLink || finalLink == window.location.href || finalLink == lastPageLoaded){
if (!finalLink || finalLink === window.location.href || finalLink === lastPageLoaded){
console.log('this was the last page');
return false;
}else{

View File

@ -4,8 +4,6 @@
"cors": "2.8.5",
"ejs": "3.1.8",
"express": "4.18.1",
"feedparser": "2.2.10",
"helmet": "5.1.1",
"megalodon": "4.1.0",
"morgan": "1.10.0",
"serve-static": "1.15.0",

View File

@ -381,3 +381,11 @@ body {
display: none;
}
}
img {
vertical-align: middle;
}
.item-content img {
margin: 0;
}

View File

@ -197,3 +197,15 @@ input[type="checkbox"]:not(:checked) ~ label::after {
input[type="checkbox"]:not(:checked) ~ div {
display: none;
}
img {
vertical-align: middle;
}
img .item-content {
margin: 0;
}
.item-content img {
margin: 0;
}

View File

@ -205,3 +205,11 @@ html,
body {
font-weight: normal;
}
img {
vertical-align: middle;
}
.item-content img {
margin: 0;
}

View File

@ -426,3 +426,11 @@ body {
display: none;
}
}
img {
vertical-align: middle;
}
.item-content img {
margin: 0;
}

View File

@ -207,3 +207,11 @@ input[type="checkbox"]:not(:checked) ~ label::after {
input[type="checkbox"]:not(:checked) ~ div {
display: none;
}
img {
vertical-align: middle;
}
.item-content img {
margin: 0;
}

View File

@ -214,3 +214,11 @@ html,
body {
font-weight: normal;
}
img {
vertical-align: middle;
}
.item-content img {
margin: 0;
}

View File

@ -197,3 +197,11 @@ input[type="checkbox"]:not(:checked) ~ label::after {
input[type="checkbox"]:not(:checked) ~ div {
display: none;
}
img {
vertical-align: middle;
}
.item-content img {
margin: 0;
}

View File

@ -204,3 +204,11 @@ button:hover,
a:hover {
background-color: #394150;
}
img {
vertical-align: middle;
}
.item-content img {
margin: 0;
}

View File

@ -12,7 +12,7 @@
<br />
<div>
<h1>Fedifeed</h1>
<h4>Embedded ActivityPub feeds for blogs, websites, etc.</h4>
<h4>Embedded Mastodon/Pleroma/Misskey feeds for blogs, websites, etc.</h4>
<a href="https://git.froth.zone/sam/fedifeed" class="cta button alt"
>Fork on Gitea
<img class="link-logo after" src="img/gitea-logo.svg" alt="Gitea Logo"
@ -78,7 +78,7 @@
<option value="auto-auto">
auto-auto (depends on instance, automatic)
</option>
<option value="auto-dark">
<option value="auto-dark" selected>
auto-dark (depends on instance, use dark)
</option>
<option value="auto-light">
@ -121,6 +121,7 @@
<span class="iframe-contain">
<iframe
id="frame"
title="Powered by Fedifeed"
allowfullscreen
sandbox="allow-top-navigation allow-scripts"
width="400"

View File

@ -11,7 +11,7 @@ window.genUrl = function genUrl() {
.checked).toString();
let showHeader = document.getElementById("header").checked.toString();
let portStr =
window.location.port && window.location.port != 80
window.location.port && window.location.port !== 80
? ":" + window.location.port
: "";
@ -30,7 +30,7 @@ window.genUrl = function genUrl() {
// Prettier, WHY
document.getElementById(
"result"
).value = `<iframe allowfullscreen sandbox="allow-top-navigation allow-scripts" width="${val(
).value = `<iframe title="Powered by Fedifeed" allowfullscreen sandbox="allow-top-navigation allow-scripts" width="${val(
"width"
)}" height="${val("height")}" src="${iframeUrl}"></iframe>`;

135
yarn.lock
View File

@ -168,13 +168,6 @@ __metadata:
languageName: node
linkType: hard
"addressparser@npm:^1.0.1":
version: 1.0.1
resolution: "addressparser@npm:1.0.1"
checksum: 389051bc6a3a44082a6e7d6256c15e2aba55ae4799f1eed620e34f1c77ddf4dfe9baacc41c6ad25eb5f795195ff8a506dc07ef9e4bc033ee1ab3882edfed396d
languageName: node
linkType: hard
"agent-base@npm:6, agent-base@npm:^6.0.2":
version: 6.0.2
resolution: "agent-base@npm:6.0.2"
@ -274,13 +267,6 @@ __metadata:
languageName: node
linkType: hard
"array-indexofobject@npm:~0.0.1":
version: 0.0.1
resolution: "array-indexofobject@npm:0.0.1"
checksum: 2517000758e1a5975f346c9b88efeab5c42f1817313397761e9e4d27ed8bee7dea1f934e07bd35ff57be9c8ba5e6a27d70707dfb0c101c1fc4aea3bbd922c173
languageName: node
linkType: hard
"array-union@npm:^2.1.0":
version: 2.1.0
resolution: "array-union@npm:2.1.0"
@ -554,13 +540,6 @@ __metadata:
languageName: node
linkType: hard
"core-util-is@npm:~1.0.0":
version: 1.0.3
resolution: "core-util-is@npm:1.0.3"
checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99
languageName: node
linkType: hard
"cors@npm:2.8.5":
version: 2.8.5
resolution: "cors@npm:2.8.5"
@ -988,25 +967,6 @@ __metadata:
languageName: node
linkType: hard
"feedparser@npm:2.2.10":
version: 2.2.10
resolution: "feedparser@npm:2.2.10"
dependencies:
addressparser: ^1.0.1
array-indexofobject: ~0.0.1
lodash.assign: ^4.2.0
lodash.get: ^4.4.2
lodash.has: ^4.5.2
lodash.uniq: ^4.5.0
mri: ^1.1.5
readable-stream: ^2.3.7
sax: ^1.2.4
bin:
feedparser: bin/feedparser.js
checksum: 1b13452824b4711d32e38fbda1a165f40b2de8887bbe8d9dfb88bc9981575ed6bd7f8cca3e53ca22f7d1d564caffa1d632b2559545721433d6ea9a10dcb130be
languageName: node
linkType: hard
"file-entry-cache@npm:^6.0.1":
version: 6.0.1
resolution: "file-entry-cache@npm:6.0.1"
@ -1300,13 +1260,6 @@ __metadata:
languageName: node
linkType: hard
"helmet@npm:5.1.1":
version: 5.1.1
resolution: "helmet@npm:5.1.1"
checksum: b72ba26cc431804ad3b8ecdc18db95409a492cbb7a7e825efc27fc502b9433fec39fc083f2aad4fe7ed1a89a4287560b59f4435f9689eebbae6a2b61a1ec1b7d
languageName: node
linkType: hard
"http-cache-semantics@npm:^4.1.0":
version: 4.1.0
resolution: "http-cache-semantics@npm:4.1.0"
@ -1430,7 +1383,7 @@ __metadata:
languageName: node
linkType: hard
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.3, inherits@npm:~2.0.3":
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.3":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1
@ -1497,13 +1450,6 @@ __metadata:
languageName: node
linkType: hard
"isarray@npm:~1.0.0":
version: 1.0.0
resolution: "isarray@npm:1.0.0"
checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab
languageName: node
linkType: hard
"isexe@npm:^2.0.0":
version: 2.0.0
resolution: "isexe@npm:2.0.0"
@ -1569,27 +1515,6 @@ __metadata:
languageName: node
linkType: hard
"lodash.assign@npm:^4.2.0":
version: 4.2.0
resolution: "lodash.assign@npm:4.2.0"
checksum: 75bbc6733c9f577c448031b4051f990f068802708891f94be9d4c2faffd6a9ec67a2c49671dafc908a068d35687765464853282842b4560b662e6c903d11cc90
languageName: node
linkType: hard
"lodash.get@npm:^4.4.2":
version: 4.4.2
resolution: "lodash.get@npm:4.4.2"
checksum: e403047ddb03181c9d0e92df9556570e2b67e0f0a930fcbbbd779370972368f5568e914f913e93f3b08f6d492abc71e14d4e9b7a18916c31fa04bd2306efe545
languageName: node
linkType: hard
"lodash.has@npm:^4.5.2":
version: 4.5.2
resolution: "lodash.has@npm:4.5.2"
checksum: b3ec829a86852331d48b3730ff06088a283d128a3965aa521ffd942bcf5c82e06bed3164ff7a7751d11e768d88f0d7bab316192091489caf20f452d42f7055d5
languageName: node
linkType: hard
"lodash.merge@npm:^4.6.2":
version: 4.6.2
resolution: "lodash.merge@npm:4.6.2"
@ -1597,13 +1522,6 @@ __metadata:
languageName: node
linkType: hard
"lodash.uniq@npm:^4.5.0":
version: 4.5.0
resolution: "lodash.uniq@npm:4.5.0"
checksum: a4779b57a8d0f3c441af13d9afe7ecff22dd1b8ce1129849f71d9bbc8e8ee4e46dfb4b7c28f7ad3d67481edd6e51126e4e2a6ee276e25906d10f7140187c392d
languageName: node
linkType: hard
"lru-cache@npm:^6.0.0":
version: 6.0.0
resolution: "lru-cache@npm:6.0.0"
@ -1837,13 +1755,6 @@ __metadata:
languageName: node
linkType: hard
"mri@npm:^1.1.5":
version: 1.2.0
resolution: "mri@npm:1.2.0"
checksum: 83f515abbcff60150873e424894a2f65d68037e5a7fcde8a9e2b285ee9c13ac581b63cfc1e6826c4732de3aeb84902f7c1e16b7aff46cd3f897a0f757a894e85
languageName: node
linkType: hard
"ms@npm:2.0.0":
version: 2.0.0
resolution: "ms@npm:2.0.0"
@ -2115,13 +2026,6 @@ __metadata:
languageName: node
linkType: hard
"process-nextick-args@npm:~2.0.0":
version: 2.0.1
resolution: "process-nextick-args@npm:2.0.1"
checksum: 1d38588e520dab7cea67cbbe2efdd86a10cc7a074c09657635e34f035277b59fbb57d09d8638346bf7090f8e8ebc070c96fa5fd183b777fff4f5edff5e9466cf
languageName: node
linkType: hard
"promise-inflight@npm:^1.0.1":
version: 1.0.1
resolution: "promise-inflight@npm:1.0.1"
@ -2191,21 +2095,6 @@ __metadata:
languageName: node
linkType: hard
"readable-stream@npm:^2.3.7":
version: 2.3.7
resolution: "readable-stream@npm:2.3.7"
dependencies:
core-util-is: ~1.0.0
inherits: ~2.0.3
isarray: ~1.0.0
process-nextick-args: ~2.0.0
safe-buffer: ~5.1.1
string_decoder: ~1.1.1
util-deprecate: ~1.0.1
checksum: e4920cf7549a60f8aaf694d483a0e61b2a878b969d224f89b3bc788b8d920075132c4b55a7494ee944c7b6a9a0eada28a7f6220d80b0312ece70bbf08eeca755
languageName: node
linkType: hard
"readable-stream@npm:^3.6.0":
version: 3.6.0
resolution: "readable-stream@npm:3.6.0"
@ -2276,8 +2165,6 @@ __metadata:
eslint-config-prettier: 8.5.0
eslint-plugin-prettier: 4.2.1
express: 4.18.1
feedparser: 2.2.10
helmet: 5.1.1
megalodon: 4.1.0
morgan: 1.10.0
prettier: 2.7.1
@ -2296,7 +2183,7 @@ __metadata:
languageName: node
linkType: hard
"safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1":
"safe-buffer@npm:5.1.2":
version: 5.1.2
resolution: "safe-buffer@npm:5.1.2"
checksum: f2f1f7943ca44a594893a852894055cf619c1fbcb611237fc39e461ae751187e7baf4dc391a72125e0ac4fb2d8c5c0b3c71529622e6a58f46b960211e704903c
@ -2330,13 +2217,6 @@ __metadata:
languageName: node
linkType: hard
"sax@npm:^1.2.4":
version: 1.2.4
resolution: "sax@npm:1.2.4"
checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe
languageName: node
linkType: hard
"semver@npm:^7.3.5":
version: 7.3.7
resolution: "semver@npm:7.3.7"
@ -2518,15 +2398,6 @@ __metadata:
languageName: node
linkType: hard
"string_decoder@npm:~1.1.1":
version: 1.1.1
resolution: "string_decoder@npm:1.1.1"
dependencies:
safe-buffer: ~5.1.0
checksum: 9ab7e56f9d60a28f2be697419917c50cac19f3e8e6c28ef26ed5f4852289fe0de5d6997d29becf59028556f2c62983790c1d9ba1e2a3cc401768ca12d5183a5b
languageName: node
linkType: hard
"strip-ansi@npm:^6.0.1":
version: 6.0.1
resolution: "strip-ansi@npm:6.0.1"
@ -2676,7 +2547,7 @@ __metadata:
languageName: node
linkType: hard
"util-deprecate@npm:^1.0.1, util-deprecate@npm:~1.0.1":
"util-deprecate@npm:^1.0.1":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"
checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2