diff --git a/.env.example b/.env.example
index cc0f59f..dff7edf 100644
--- a/.env.example
+++ b/.env.example
@@ -37,4 +37,5 @@ VERCEL_GIT_REPO_OWNER=""
VERCEL_GIT_REPO_SLUG=""
VERCEL_URL=""
ANALYZE="false"
-ANTHROPIC_API_KEY=""
\ No newline at end of file
+ANTHROPIC_API_KEY=""
+NEWS_TO_USE=10
\ No newline at end of file
diff --git a/app/api/confirmation/route.ts b/app/api/confirmation/route.ts
index a8e1aed..e7dab55 100644
--- a/app/api/confirmation/route.ts
+++ b/app/api/confirmation/route.ts
@@ -16,7 +16,7 @@ export const dynamic = 'force-dynamic'; // defaults to force-static
export async function POST(request: NextRequest) {
try {
if (!process.env.RESEND_KEY || !process.env.RESEND_AUDIENCE) {
- throw new Error('RESEND_AUDIENCE is not set');
+ throw new Error('Resend variables not set');
}
const body = await request.json();
const validation = ConfirmationSchema.safeParse(body);
diff --git a/app/api/mailing/route.ts b/app/api/mailing/route.ts
index 4a75413..19a292d 100644
--- a/app/api/mailing/route.ts
+++ b/app/api/mailing/route.ts
@@ -21,6 +21,10 @@ export async function GET(request: NextRequest) {
return ApiResponse(STATUS_UNAUTHORIZED, 'Unauthorized');
}
+ if (!process.env.NEWS_TO_USE) {
+ throw new Error('NEWS_TO_USE is not set');
+ }
+
try {
// send newsletter to users who didn't get it in the last 23h 50m, assuming a cron job every 10 minutes
// this is to avoid sending the newsletter to the same users multiple times
@@ -61,7 +65,7 @@ export async function GET(request: NextRequest) {
orderBy: {
score: 'desc'
},
- take: 25
+ take: Number(process.env.NEWS_TO_USE)
});
console.info(`Found ${news.length} news to include in the newsletter.`);
diff --git a/components/email/Newsletter.tsx b/components/email/Newsletter.tsx
index 931fbcd..2b6d2cc 100644
--- a/components/email/Newsletter.tsx
+++ b/components/email/Newsletter.tsx
@@ -2,10 +2,14 @@ import { getSayings } from '@utils/getSayings';
import { summirize } from '@utils/summarize';
import { textTruncate } from '@utils/textTruncate';
import { NewsType } from '@utils/validationSchemas';
+import createDOMPurify from 'isomorphic-dompurify';
import Template from './Template';
export default async function NewsletterTemplate(stories: NewsType[]) {
const summary = await summirize(stories);
+ const sanitizedSummary = createDOMPurify.sanitize(summary, {
+ USE_PROFILES: { html: true }
+ });
return {
subject: `What's new from the Hackernews forum?`,
@@ -15,21 +19,20 @@ export default async function NewsletterTemplate(stories: NewsType[]) {
${getSayings[Math.floor(Math.random() * getSayings.length)]}!`}
body={
<>
- {summary && (
+ {sanitizedSummary && (
- {summary}
-
+ dangerouslySetInnerHTML={{ __html: sanitizedSummary }}
+ />
)}
{stories.map(story => {
diff --git a/package.json b/package.json
index 8822677..15f829b 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"@vercel/analytics": "^1.1.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
+ "isomorphic-dompurify": "^2.15.0",
"next": "^14.1.0",
"postcss-nesting": "^12.0.2",
"react": "^18",
diff --git a/utils/summarize.ts b/utils/summarize.ts
index 6120d4e..58ca2db 100644
--- a/utils/summarize.ts
+++ b/utils/summarize.ts
@@ -7,7 +7,7 @@ export async function summirize(news: NewsType[]) {
.join('\n\n');
const promptSetup =
- "You are a tech journalist with a technology degree and background. Summarize the following list of posts from an online forum as a TL;DR (Too Long; Didn't Read) summary. Your summary should:\n\n1. Be 200-500 words long.\n\n2. Consist multiple phrases in one single paragraph, combining related news items where possible.\n\n3. Highlight the 2-3 most significant or impactful news items.\n\n4. Provide context within broader tech trends or developments.\n\n5. Use a tone that is informative and slightly enthusiastic, aimed at tech-savvy general readers.\n\n6. Group news items by themes or technology areas if applicable.\n\n7. Be formatted for HTML use, with links incorporated as follows:
[linked text]\n\nFocus on conveying the key points and their potential impact on the tech landscape. Your response should consist of the summary only.\n\nThe news items are structured as follows:\n\nTITLE:
\n\nCONTENT: \n\nLINK: \n\nPlease summarize the following news:";
+ "You are a tech journalist with a technology degree and background. Summarize the following list of posts from an online forum as a TL;DR (Too Long; Didn't Read) summary. Your summary should:\n\n1. Be 150-300 words long (not counting the urls).\n\n2. Consist multiple phrases in one single paragraph, combining related news items where possible.\n\n3. Highlight the 2-3 most significant or impactful news items.\n\n4. Provide context within broader tech trends or developments.\n\n5. Use a tone that is informative and slightly enthusiastic, aimed at tech-savvy general readers.\n\n6. Group news items by themes or technology areas if applicable.\n\n7. Be formatted for HTML use, with links incorporated as follows and including at most 3-4 words: [linked text]\n\nFocus on conveying the key points and their potential impact on the tech landscape. Your response should consist of the summary only.\n\nThe news items are structured as follows:\n\nTITLE: \n\nCONTENT: \n\nLINK: \n\nPlease summarize the following news:";
try {
const response = await message(promptSetup + newsInput);
@@ -16,6 +16,6 @@ export async function summirize(news: NewsType[]) {
return summary.text;
} catch (error) {
console.error(error);
- return;
+ return '';
}
}
diff --git a/yarn.lock b/yarn.lock
index 82085c4..78b0e37 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -736,6 +736,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/dompurify@npm:^3.0.5":
+ version: 3.0.5
+ resolution: "@types/dompurify@npm:3.0.5"
+ dependencies:
+ "@types/trusted-types": "npm:*"
+ checksum: e544b3ce53c41215cabff3d89256ff707c7ee8e0c9a1b5034b22014725d288b16e6942cdcdeeb4221c578c3421a6a4721aa0676431f55d7abd18c07368855c5e
+ languageName: node
+ linkType: hard
+
"@types/json-schema@npm:^7.0.12":
version: 7.0.15
resolution: "@types/json-schema@npm:7.0.15"
@@ -834,6 +843,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/trusted-types@npm:*":
+ version: 2.0.7
+ resolution: "@types/trusted-types@npm:2.0.7"
+ checksum: 8e4202766a65877efcf5d5a41b7dd458480b36195e580a3b1085ad21e948bc417d55d6f8af1fd2a7ad008015d4117d5fdfe432731157da3c68678487174e4ba3
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/eslint-plugin@npm:^6.12.0":
version: 6.21.0
resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0"
@@ -1818,6 +1834,15 @@ __metadata:
languageName: node
linkType: hard
+"cssstyle@npm:^4.0.1":
+ version: 4.1.0
+ resolution: "cssstyle@npm:4.1.0"
+ dependencies:
+ rrweb-cssom: "npm:^0.7.1"
+ checksum: 8ca9e2d1f1b24f93bb5f3f20a7a1e271e58060957880e985ee55614e196a798ffab309ec6bac105af8a439a6764546761813835ebb7f929d60823637ee838a8f
+ languageName: node
+ linkType: hard
+
"csstype@npm:^3.0.2":
version: 3.1.3
resolution: "csstype@npm:3.1.3"
@@ -1839,6 +1864,16 @@ __metadata:
languageName: node
linkType: hard
+"data-urls@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "data-urls@npm:5.0.0"
+ dependencies:
+ whatwg-mimetype: "npm:^4.0.0"
+ whatwg-url: "npm:^14.0.0"
+ checksum: 5c40568c31b02641a70204ff233bc4e42d33717485d074244a98661e5f2a1e80e38fe05a5755dfaf2ee549f2ab509d6a3af2a85f4b2ad2c984e5d176695eaf46
+ languageName: node
+ linkType: hard
+
"data-view-buffer@npm:^1.0.1":
version: 1.0.1
resolution: "data-view-buffer@npm:1.0.1"
@@ -1917,6 +1952,13 @@ __metadata:
languageName: node
linkType: hard
+"decimal.js@npm:^10.4.3":
+ version: 10.4.3
+ resolution: "decimal.js@npm:10.4.3"
+ checksum: de663a7bc4d368e3877db95fcd5c87b965569b58d16cdc4258c063d231ca7118748738df17cd638f7e9dd0be8e34cec08d7234b20f1f2a756a52fc5a38b188d0
+ languageName: node
+ linkType: hard
+
"deep-equal@npm:^2.0.5":
version: 2.2.3
resolution: "deep-equal@npm:2.2.3"
@@ -2054,6 +2096,13 @@ __metadata:
languageName: node
linkType: hard
+"dompurify@npm:^3.1.6":
+ version: 3.1.6
+ resolution: "dompurify@npm:3.1.6"
+ checksum: 036844bc9b717b172ba27f5863b56f950289a05d8eebfb702d6953bbf80bd021e480ce4217bd084567186f2d0ada13358ce5556963492cfe402d774e8667f120
+ languageName: node
+ linkType: hard
+
"domutils@npm:^3.0.1":
version: 3.1.0
resolution: "domutils@npm:3.1.0"
@@ -3233,6 +3282,15 @@ __metadata:
languageName: node
linkType: hard
+"html-encoding-sniffer@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "html-encoding-sniffer@npm:4.0.0"
+ dependencies:
+ whatwg-encoding: "npm:^3.1.1"
+ checksum: e86efd493293a5671b8239bd099d42128433bb3c7b0fdc7819282ef8e118a21f5dead0ad6f358e024a4e5c84f17ebb7a9b36075220fac0a6222b207248bede6f
+ languageName: node
+ linkType: hard
+
"html-escaper@npm:^2.0.2":
version: 2.0.2
resolution: "html-escaper@npm:2.0.2"
@@ -3272,7 +3330,7 @@ __metadata:
languageName: node
linkType: hard
-"http-proxy-agent@npm:^7.0.0":
+"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.2":
version: 7.0.2
resolution: "http-proxy-agent@npm:7.0.2"
dependencies:
@@ -3282,7 +3340,7 @@ __metadata:
languageName: node
linkType: hard
-"https-proxy-agent@npm:^7.0.1":
+"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5":
version: 7.0.5
resolution: "https-proxy-agent@npm:7.0.5"
dependencies:
@@ -3324,7 +3382,7 @@ __metadata:
languageName: node
linkType: hard
-"iconv-lite@npm:^0.6.2":
+"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2":
version: 0.6.3
resolution: "iconv-lite@npm:0.6.3"
dependencies:
@@ -3629,6 +3687,13 @@ __metadata:
languageName: node
linkType: hard
+"is-potential-custom-element-name@npm:^1.0.1":
+ version: 1.0.1
+ resolution: "is-potential-custom-element-name@npm:1.0.1"
+ checksum: ced7bbbb6433a5b684af581872afe0e1767e2d1146b2207ca0068a648fb5cab9d898495d1ac0583524faaf24ca98176a7d9876363097c2d14fee6dd324f3a1ab
+ languageName: node
+ linkType: hard
+
"is-regex@npm:^1.1.4":
version: 1.1.4
resolution: "is-regex@npm:1.1.4"
@@ -3752,6 +3817,17 @@ __metadata:
languageName: node
linkType: hard
+"isomorphic-dompurify@npm:^2.15.0":
+ version: 2.15.0
+ resolution: "isomorphic-dompurify@npm:2.15.0"
+ dependencies:
+ "@types/dompurify": "npm:^3.0.5"
+ dompurify: "npm:^3.1.6"
+ jsdom: "npm:^25.0.0"
+ checksum: a8cbf6e7680be9d8c795d4fca427d673c6fc78bacec736331897795566a6bde9d61f9cbee0d22f89cab0be0ba4f62a8d61484e0639a115e4aed0501e80c583f9
+ languageName: node
+ linkType: hard
+
"iterator.prototype@npm:^1.1.2":
version: 1.1.2
resolution: "iterator.prototype@npm:1.1.2"
@@ -3843,6 +3919,40 @@ __metadata:
languageName: node
linkType: hard
+"jsdom@npm:^25.0.0":
+ version: 25.0.0
+ resolution: "jsdom@npm:25.0.0"
+ dependencies:
+ cssstyle: "npm:^4.0.1"
+ data-urls: "npm:^5.0.0"
+ decimal.js: "npm:^10.4.3"
+ form-data: "npm:^4.0.0"
+ html-encoding-sniffer: "npm:^4.0.0"
+ http-proxy-agent: "npm:^7.0.2"
+ https-proxy-agent: "npm:^7.0.5"
+ is-potential-custom-element-name: "npm:^1.0.1"
+ nwsapi: "npm:^2.2.12"
+ parse5: "npm:^7.1.2"
+ rrweb-cssom: "npm:^0.7.1"
+ saxes: "npm:^6.0.0"
+ symbol-tree: "npm:^3.2.4"
+ tough-cookie: "npm:^4.1.4"
+ w3c-xmlserializer: "npm:^5.0.0"
+ webidl-conversions: "npm:^7.0.0"
+ whatwg-encoding: "npm:^3.1.1"
+ whatwg-mimetype: "npm:^4.0.0"
+ whatwg-url: "npm:^14.0.0"
+ ws: "npm:^8.18.0"
+ xml-name-validator: "npm:^5.0.0"
+ peerDependencies:
+ canvas: ^2.11.2
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+ checksum: f07271fe3922458885540b4dd9fb6170a11059f3128ec3cec2688c5c10fde5ca2e8ddcc207fff540e21dfaf592fae6a791c3359038ba4d116250dd3258fbd8cb
+ languageName: node
+ linkType: hard
+
"json-buffer@npm:3.0.1":
version: 3.0.1
resolution: "json-buffer@npm:3.0.1"
@@ -4561,6 +4671,7 @@ __metadata:
eslint-config-next: "npm:14.0.3"
eslint-config-prettier: "npm:^9.0.0"
husky: "npm:^8.0.3"
+ isomorphic-dompurify: "npm:^2.15.0"
lint-staged: "npm:^15.1.0"
next: "npm:^14.1.0"
postcss: "npm:^8"
@@ -4695,6 +4806,13 @@ __metadata:
languageName: node
linkType: hard
+"nwsapi@npm:^2.2.12":
+ version: 2.2.12
+ resolution: "nwsapi@npm:2.2.12"
+ checksum: 172119e9ef492467ebfb337f9b5fd12a94d2b519377cde3f6ec2f74a86f6d5c00ef3873539bed7142f908ffca4e35383179be2319d04a563071d146bfa3f1673
+ languageName: node
+ linkType: hard
+
"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1":
version: 4.1.1
resolution: "object-assign@npm:4.1.1"
@@ -4920,6 +5038,15 @@ __metadata:
languageName: node
linkType: hard
+"parse5@npm:^7.1.2":
+ version: 7.1.2
+ resolution: "parse5@npm:7.1.2"
+ dependencies:
+ entities: "npm:^4.4.0"
+ checksum: 3c86806bb0fb1e9a999ff3a4c883b1ca243d99f45a619a0898dbf021a95a0189ed955c31b07fe49d342b54e814f33f2c9d7489198e8630dacd5477d413ec5782
+ languageName: node
+ linkType: hard
+
"parseley@npm:^0.12.0":
version: 0.12.1
resolution: "parseley@npm:0.12.1"
@@ -5264,13 +5391,27 @@ __metadata:
languageName: node
linkType: hard
-"punycode@npm:^2.1.0":
+"psl@npm:^1.1.33":
+ version: 1.9.0
+ resolution: "psl@npm:1.9.0"
+ checksum: d07879d4bfd0ac74796306a8e5a36a93cfb9c4f4e8ee8e63fbb909066c192fe1008cd8f12abd8ba2f62ca28247949a20c8fb32e1d18831d9e71285a1569720f9
+ languageName: node
+ linkType: hard
+
+"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.1":
version: 2.3.1
resolution: "punycode@npm:2.3.1"
checksum: febdc4362bead22f9e2608ff0171713230b57aff9dddc1c273aa2a651fbd366f94b7d6a71d78342a7c0819906750351ca7f2edd26ea41b626d87d6a13d1bd059
languageName: node
linkType: hard
+"querystringify@npm:^2.1.1":
+ version: 2.2.0
+ resolution: "querystringify@npm:2.2.0"
+ checksum: 46ab16f252fd892fc29d6af60966d338cdfeea68a231e9457631ffd22d67cec1e00141e0a5236a2eb16c0d7d74175d9ec1d6f963660c6f2b1c2fc85b194c5680
+ languageName: node
+ linkType: hard
+
"queue-microtask@npm:^1.2.2":
version: 1.2.3
resolution: "queue-microtask@npm:1.2.3"
@@ -5441,6 +5582,13 @@ __metadata:
languageName: node
linkType: hard
+"requires-port@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "requires-port@npm:1.0.0"
+ checksum: 878880ee78ccdce372784f62f52a272048e2d0827c29ae31e7f99da18b62a2b9463ea03a75f277352f4697c100183debb0532371ad515a2d49d4bfe596dd4c20
+ languageName: node
+ linkType: hard
+
"resend@npm:^3.1.0":
version: 3.4.0
resolution: "resend@npm:3.4.0"
@@ -5574,6 +5722,13 @@ __metadata:
languageName: node
linkType: hard
+"rrweb-cssom@npm:^0.7.1":
+ version: 0.7.1
+ resolution: "rrweb-cssom@npm:0.7.1"
+ checksum: e80cf25c223a823921d7ab57c0ce78f5b7ebceab857b400cce99dd4913420ce679834bc5707e8ada47d062e21ad368108a9534c314dc8d72c20aa4a4fa0ed16a
+ languageName: node
+ linkType: hard
+
"run-parallel@npm:^1.1.9":
version: 1.2.0
resolution: "run-parallel@npm:1.2.0"
@@ -5620,6 +5775,15 @@ __metadata:
languageName: node
linkType: hard
+"saxes@npm:^6.0.0":
+ version: 6.0.0
+ resolution: "saxes@npm:6.0.0"
+ dependencies:
+ xmlchars: "npm:^2.2.0"
+ checksum: 97b50daf6ca3a153e89842efa18a862e446248296622b7473c169c84c823ee8a16e4a43bac2f73f11fc8cb9168c73fbb0d73340f26552bac17970e9052367aa9
+ languageName: node
+ linkType: hard
+
"scheduler@npm:^0.23.2":
version: 0.23.2
resolution: "scheduler@npm:0.23.2"
@@ -6162,6 +6326,13 @@ __metadata:
languageName: node
linkType: hard
+"symbol-tree@npm:^3.2.4":
+ version: 3.2.4
+ resolution: "symbol-tree@npm:3.2.4"
+ checksum: c09a00aadf279d47d0c5c46ca3b6b2fbaeb45f0a184976d599637d412d3a70bbdc043ff33effe1206dea0e36e0ad226cb957112e7ce9a4bf2daedf7fa4f85c53
+ languageName: node
+ linkType: hard
+
"tailwind-merge@npm:^2.1.0":
version: 2.4.0
resolution: "tailwind-merge@npm:2.4.0"
@@ -6296,6 +6467,27 @@ __metadata:
languageName: node
linkType: hard
+"tough-cookie@npm:^4.1.4":
+ version: 4.1.4
+ resolution: "tough-cookie@npm:4.1.4"
+ dependencies:
+ psl: "npm:^1.1.33"
+ punycode: "npm:^2.1.1"
+ universalify: "npm:^0.2.0"
+ url-parse: "npm:^1.5.3"
+ checksum: 75663f4e2cd085f16af0b217e4218772adf0617fb3227171102618a54ce0187a164e505d61f773ed7d65988f8ff8a8f935d381f87da981752c1171b076b4afac
+ languageName: node
+ linkType: hard
+
+"tr46@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "tr46@npm:5.0.0"
+ dependencies:
+ punycode: "npm:^2.3.1"
+ checksum: 29155adb167d048d3c95d181f7cb5ac71948b4e8f3070ec455986e1f34634acae50ae02a3c8d448121c3afe35b76951cd46ed4c128fd80264280ca9502237a3e
+ languageName: node
+ linkType: hard
+
"tr46@npm:~0.0.3":
version: 0.0.3
resolution: "tr46@npm:0.0.3"
@@ -6498,6 +6690,13 @@ __metadata:
languageName: node
linkType: hard
+"universalify@npm:^0.2.0":
+ version: 0.2.0
+ resolution: "universalify@npm:0.2.0"
+ checksum: e86134cb12919d177c2353196a4cc09981524ee87abf621f7bc8d249dbbbebaec5e7d1314b96061497981350df786e4c5128dbf442eba104d6e765bc260678b5
+ languageName: node
+ linkType: hard
+
"update-browserslist-db@npm:^1.1.0":
version: 1.1.0
resolution: "update-browserslist-db@npm:1.1.0"
@@ -6521,6 +6720,16 @@ __metadata:
languageName: node
linkType: hard
+"url-parse@npm:^1.5.3":
+ version: 1.5.10
+ resolution: "url-parse@npm:1.5.10"
+ dependencies:
+ querystringify: "npm:^2.1.1"
+ requires-port: "npm:^1.0.0"
+ checksum: c9e96bc8c5b34e9f05ddfeffc12f6aadecbb0d971b3cc26015b58d5b44676a99f50d5aeb1e5c9e61fa4d49961ae3ab1ae997369ed44da51b2f5ac010d188e6ad
+ languageName: node
+ linkType: hard
+
"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"
@@ -6538,6 +6747,15 @@ __metadata:
languageName: node
linkType: hard
+"w3c-xmlserializer@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "w3c-xmlserializer@npm:5.0.0"
+ dependencies:
+ xml-name-validator: "npm:^5.0.0"
+ checksum: d78f59e6b4f924aa53b6dfc56949959229cae7fe05ea9374eb38d11edcec01398b7f5d7a12576bd5acc57ff446abb5c9115cd83b9d882555015437cf858d42f0
+ languageName: node
+ linkType: hard
+
"web-streams-polyfill@npm:4.0.0-beta.3":
version: 4.0.0-beta.3
resolution: "web-streams-polyfill@npm:4.0.0-beta.3"
@@ -6552,6 +6770,13 @@ __metadata:
languageName: node
linkType: hard
+"webidl-conversions@npm:^7.0.0":
+ version: 7.0.0
+ resolution: "webidl-conversions@npm:7.0.0"
+ checksum: 4c4f65472c010eddbe648c11b977d048dd96956a625f7f8b9d64e1b30c3c1f23ea1acfd654648426ce5c743c2108a5a757c0592f02902cf7367adb7d14e67721
+ languageName: node
+ linkType: hard
+
"webpack-bundle-analyzer@npm:4.10.1":
version: 4.10.1
resolution: "webpack-bundle-analyzer@npm:4.10.1"
@@ -6575,6 +6800,32 @@ __metadata:
languageName: node
linkType: hard
+"whatwg-encoding@npm:^3.1.1":
+ version: 3.1.1
+ resolution: "whatwg-encoding@npm:3.1.1"
+ dependencies:
+ iconv-lite: "npm:0.6.3"
+ checksum: bbef815eb67f91487c7f2ef96329743f5fd8357d7d62b1119237d25d41c7e452dff8197235b2d3c031365a17f61d3bb73ca49d0ed1582475aa4a670815e79534
+ languageName: node
+ linkType: hard
+
+"whatwg-mimetype@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "whatwg-mimetype@npm:4.0.0"
+ checksum: 894a618e2d90bf444b6f309f3ceb6e58cf21b2beaa00c8b333696958c4076f0c7b30b9d33413c9ffff7c5832a0a0c8569e5bb347ef44beded72aeefd0acd62e8
+ languageName: node
+ linkType: hard
+
+"whatwg-url@npm:^14.0.0":
+ version: 14.0.0
+ resolution: "whatwg-url@npm:14.0.0"
+ dependencies:
+ tr46: "npm:^5.0.0"
+ webidl-conversions: "npm:^7.0.0"
+ checksum: 67ea7a359a90663b28c816d76379b4be62d13446e9a4c0ae0b5ae0294b1c22577750fcdceb40827bb35a61777b7093056953c856604a28b37d6a209ba59ad062
+ languageName: node
+ linkType: hard
+
"whatwg-url@npm:^5.0.0":
version: 5.0.0
resolution: "whatwg-url@npm:5.0.0"
@@ -6727,6 +6978,35 @@ __metadata:
languageName: node
linkType: hard
+"ws@npm:^8.18.0":
+ version: 8.18.0
+ resolution: "ws@npm:8.18.0"
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ">=5.0.2"
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ checksum: 70dfe53f23ff4368d46e4c0b1d4ca734db2c4149c6f68bc62cb16fc21f753c47b35fcc6e582f3bdfba0eaeb1c488cddab3c2255755a5c3eecb251431e42b3ff6
+ languageName: node
+ linkType: hard
+
+"xml-name-validator@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "xml-name-validator@npm:5.0.0"
+ checksum: 43f30f3f6786e406dd665acf08cd742d5f8a46486bd72517edb04b27d1bcd1599664c2a4a99fc3f1e56a3194bff588b12f178b7972bc45c8047bdc4c3ac8d4a1
+ languageName: node
+ linkType: hard
+
+"xmlchars@npm:^2.2.0":
+ version: 2.2.0
+ resolution: "xmlchars@npm:2.2.0"
+ checksum: 4ad5924974efd004a47cce6acf5c0269aee0e62f9a805a426db3337af7bcbd331099df174b024ace4fb18971b8a56de386d2e73a1c4b020e3abd63a4a9b917f1
+ languageName: node
+ linkType: hard
+
"y18n@npm:^5.0.5":
version: 5.0.8
resolution: "y18n@npm:5.0.8"