feat: move transaction emails to zeptomail

This commit is contained in:
Rotimi Best
2024-08-10 09:58:33 +01:00
parent f9fdd72ed9
commit a742b1af43
14 changed files with 199 additions and 153 deletions
+1
View File
@@ -16,3 +16,4 @@ SMTP_PASSWORD=
SMTP_USER_NOTIFY=
SMTP_PASSWORD_NOTIFY=
SMTP_PORT=
ZOHO_TOKEN=
+1
View File
@@ -37,6 +37,7 @@
"nodemailer": "^6.9.5",
"nodemon": "^3.0.1",
"puppeteer": "^20.9.0",
"zeptomail": "^6.0.0",
"zod": "^3.21.4"
}
}
+67 -18
View File
@@ -1,6 +1,6 @@
const express = require('express');
const zod = require('zod');
const { getTransporter } = require('../utils/getTransporter');
const { nodemailerTransporter, zohoClient } = require('../utils/email');
const { withEmailTemplate } = require('../utils/withEmailTemplate');
const router = express.Router();
@@ -9,13 +9,70 @@ let personalTransporter;
let defaultTransporter;
// Initialize transporters
getTransporter(true).then((t) => {
nodemailerTransporter(true).then((t) => {
personalTransporter = t;
});
getTransporter(false).then((t) => {
nodemailerTransporter(false).then((t) => {
defaultTransporter = t;
});
async function sendWithNodemailer(emailData) {
const { from, to, subject, content, isPersonalEmail, replyTo } = emailData;
const transporter = isPersonalEmail ? personalTransporter : defaultTransporter;
if (!transporter) {
return;
}
return await transporter.sendMail({
from: from || '"Best from ClassroomIO" <best@classroomio.com>',
to,
subject,
replyTo,
html: withEmailTemplate(content)
});
}
// format: "ClassroomIO Developers (via ClassroomIO.com)" <notify@mail.classroomio.com>
function extractNameAndEmail(str) {
// Use regular expressions to match the name and email
const regex = /"(.*?)"\s+<\s*(.*?)@(.*?)\s*>/;
const match = str.match(regex);
if (match) {
// Extract the name and email from the match groups
const name = match[1];
const email = match[2] + '@' + match[3];
return { name, email };
} else {
// Return undefined if the format doesn't match
return { name: from, email: from };
}
}
async function sendWithZoho(emailData) {
const { from, to, subject, content } = emailData;
const fromData = extractNameAndEmail(from);
return zohoClient.sendMail({
from: {
address: fromData.email,
name: fromData.name
},
to: [
{
email_address: {
address: to
}
}
],
subject,
htmlbody: content
});
}
router.post('/', async (req, res) => {
try {
const mySchema = zod.array(
@@ -35,26 +92,18 @@ router.post('/', async (req, res) => {
Promise.all(
emailDataArray.map(async (emailData) => {
let res;
try {
const { from, to, subject, content, isPersonalEmail, replyTo } = emailData;
const transporter = isPersonalEmail ? personalTransporter : defaultTransporter;
if (!transporter) {
return;
if (emailData.isPersonalEmail) {
res = await sendWithNodemailer(emailData);
} else {
res = await sendWithZoho(emailData);
}
const res = await transporter.sendMail({
from: from || '"Best from ClassroomIO" <best@classroomio.com>',
to,
subject,
replyTo,
html: withEmailTemplate(content)
});
console.log('Email status', res);
} catch (error) {
console.error('Error sending email', error);
console.error('Error sending email', error, error?.error?.details?.[0]);
}
})
);
+49
View File
@@ -0,0 +1,49 @@
const { SendMailClient } = require('zeptomail');
const nodemailer = require('nodemailer');
const {
ZOHO_TOKEN,
SMTP_HOST,
SMTP_PORT,
SMTP_USER,
SMTP_PASSWORD,
SMTP_USER_NOTIFY,
SMTP_PASSWORD_NOTIFY
} = process.env;
console.log({ ZOHO_TOKEN });
const zohoClient = new SendMailClient({ url: 'api.zeptomail.eu/', token: ZOHO_TOKEN });
const nodemailerTransporter = async (isPersonal = false) => {
if (
!SMTP_HOST ||
!SMTP_PORT ||
!SMTP_USER ||
!SMTP_PASSWORD ||
!SMTP_USER_NOTIFY ||
!SMTP_PASSWORD_NOTIFY
) {
console.error('SMTP configuration missing');
return null;
}
try {
const transporter = nodemailer.createTransport({
host: SMTP_HOST,
port: parseInt(SMTP_PORT || '465', 10),
secure: true,
auth: {
user: isPersonal ? SMTP_USER : SMTP_USER_NOTIFY,
pass: isPersonal ? SMTP_PASSWORD : SMTP_PASSWORD_NOTIFY
}
});
await transporter.verify();
return transporter;
} catch (error) {
console.error('Transporter error:', error);
return null;
}
};
module.exports = { nodemailerTransporter, zohoClient };
@@ -26,7 +26,7 @@ export async function POST({ request }) {
const emailData = [
{
from: `"${orgName} (via ClassroomIO.com)" <notify@classroomio.com>`,
from: `"${orgName} (via ClassroomIO.com)" <notify@mail.classroomio.com>`,
to,
subject: `[Submitted]: ${exerciseTitle}`,
content
@@ -14,7 +14,7 @@ const sendEmailNotification = async (feedId: string, authorId: string, comment?:
if (comment) {
const emailData = [
{
from: `"${feed.org.name} - ClassroomIO" <notify@classroomio.com>`,
from: `"${feed.org.name} - ClassroomIO" <notify@mail.classroomio.com>`,
to: feed.teacherEmail,
subject: `[${feed.courseTitle}] - News feed comment`,
content: `
@@ -42,7 +42,7 @@ const sendEmailNotification = async (feedId: string, authorId: string, comment?:
// else send to everyone except the author of the post
const emailsData = feed.courseMembers.map((member) => ({
from: `"${feed.org.name} - ClassroomIO" <notify@classroomio.com>`,
from: `"${feed.org.name} - ClassroomIO" <notify@mail.classroomio.com>`,
to: member.email,
replyTo: feed.teacherEmail,
subject: `[${feed.courseTitle}] - New post in course`,
@@ -34,7 +34,7 @@ export async function POST({ request }) {
const emailData = [
{
from: `"${orgName} - ClassroomIO" <notify@classroomio.com>`,
from: `"${orgName} - ClassroomIO" <notify@mail.classroomio.com>`,
to,
replyTo: teacherEmail,
subject: `[${courseName}] One more step left`,
@@ -28,7 +28,7 @@ export async function POST({ request }) {
const emailData = [
{
from: `"${orgName} (via ClassroomIO.com)" <notify@classroomio.com>`,
from: `"${orgName} (via ClassroomIO.com)" <notify@mail.classroomio.com>`,
to,
subject: `${orgName} - Welcome to Class 🎉`,
content: `
@@ -26,7 +26,7 @@ export async function POST({ request }) {
const emailData = [
{
from: `"${orgName} (via ClassroomIO.com)" <notify@classroomio.com>`,
from: `"${orgName} (via ClassroomIO.com)" <notify@mail.classroomio.com>`,
to,
subject: 'Submission Update',
content
@@ -33,7 +33,7 @@ export async function POST({ request }) {
const emailData = [
{
from: `ClassroomIO" <notify@classroomio.com>`,
from: `"ClassroomIO" <notify@mail.classroomio.com>`,
to,
subject: `[${courseName}] Request to Join Course!`,
content: `
@@ -27,7 +27,7 @@ export async function POST({ request }) {
const emailData = [
{
from: `"ClassroomIO" <notify@classroomio.com>`,
from: `"ClassroomIO" <notify@mail.classroomio.com>`,
to,
subject: `[${courseName}] You've got a new student 🎉!`,
content: `
@@ -30,7 +30,7 @@ export async function POST({ request }) {
const emailData = [
{
from: `"${orgName} (via ClassroomIO.com)" <notify@classroomio.com>`,
from: `"${orgName} (via ClassroomIO.com)" <notify@mail.classroomio.com>`,
to,
subject: `You have been invited to a course in ${orgName}!`,
content: `
@@ -51,6 +51,7 @@ export async function POST({ request }) {
isPersonalEmail: true
}
];
await sendEmail(emailData);
return json({
+71 -126
View File
@@ -98,6 +98,9 @@ importers:
puppeteer:
specifier: ^20.9.0
version: 20.9.0(typescript@5.3.2)
zeptomail:
specifier: ^6.0.0
version: 6.0.0
zod:
specifier: ^3.21.4
version: 3.22.4
@@ -158,7 +161,7 @@ importers:
version: 5.1.6
vite:
specifier: ^4.4.2
version: 4.4.8(@types/node@22.2.0)(sass@1.64.2)
version: 4.4.8(@types/node@18.11.10)(sass@1.64.2)
apps/dashboard:
dependencies:
@@ -357,7 +360,7 @@ importers:
version: 3.1.1
jest:
specifier: ^29.7.0
version: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2)
version: 29.7.0(@types/node@18.11.10)(ts-node@10.9.2)
postcss:
specifier: ^8.4.24
version: 8.4.24
@@ -396,7 +399,7 @@ importers:
version: 29.1.1(@babel/core@7.22.9)(babel-jest@29.7.0)(jest@29.7.0)(typescript@5.1.6)
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@22.2.0)(typescript@5.1.6)
version: 10.9.2(@types/node@18.11.10)(typescript@5.1.6)
tsconfig:
specifier: workspace:*
version: link:../../packages/tsconfig
@@ -408,7 +411,7 @@ importers:
version: 5.1.6
vite:
specifier: ^4.4.8
version: 4.4.8(@types/node@22.2.0)(sass@1.64.2)
version: 4.4.8(@types/node@18.11.10)(sass@1.64.2)
apps/docs:
dependencies:
@@ -1637,7 +1640,7 @@ packages:
'@babel/traverse': 7.23.4
'@babel/types': 7.23.4
convert-source-map: 1.9.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -1660,7 +1663,7 @@ packages:
'@babel/traverse': 7.23.4
'@babel/types': 7.23.4
convert-source-map: 2.0.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -1683,7 +1686,7 @@ packages:
'@babel/traverse': 7.23.9
'@babel/types': 7.23.9
convert-source-map: 2.0.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -1817,7 +1820,7 @@ packages:
'@babel/core': 7.22.9
'@babel/helper-compilation-targets': 7.22.15
'@babel/helper-plugin-utils': 7.22.5
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
lodash.debounce: 4.0.8
resolve: 1.22.8
transitivePeerDependencies:
@@ -3137,7 +3140,7 @@ packages:
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.23.4
'@babel/types': 7.23.4
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -3155,7 +3158,7 @@ packages:
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.23.9
'@babel/types': 7.23.9
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -3514,7 +3517,7 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
espree: 9.6.1
globals: 13.23.0
ignore: 5.3.0
@@ -3584,7 +3587,7 @@ packages:
engines: {node: '>=10.10.0'}
dependencies:
'@humanwhocodes/object-schema': 2.0.1
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -4346,8 +4349,8 @@ packages:
typescript:
optional: true
dependencies:
debug: 4.3.4(supports-color@5.5.0)
extract-zip: 2.0.1
debug: 4.3.4(supports-color@8.1.1)
extract-zip: 2.0.1(supports-color@8.1.1)
progress: 2.0.3
proxy-agent: 6.3.0
tar-fs: 3.0.4
@@ -5819,7 +5822,7 @@ packages:
sirv: 2.0.3
svelte: 4.1.2
undici: 5.22.1
vite: 4.4.8(@types/node@22.2.0)(sass@1.64.2)
vite: 4.4.8(@types/node@18.11.10)(sass@1.64.2)
transitivePeerDependencies:
- supports-color
dev: true
@@ -5847,7 +5850,7 @@ packages:
svelte: 4.1.2
tiny-glob: 0.2.9
undici: 5.26.5
vite: 4.4.8(@types/node@22.2.0)(sass@1.64.2)
vite: 4.4.8(@types/node@18.11.10)(sass@1.64.2)
transitivePeerDependencies:
- supports-color
@@ -5860,9 +5863,9 @@ packages:
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte': 2.5.3(svelte@4.1.2)(vite@4.4.8)
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
svelte: 4.1.2
vite: 4.4.8(@types/node@22.2.0)(sass@1.64.2)
vite: 4.4.8(@types/node@18.11.10)(sass@1.64.2)
transitivePeerDependencies:
- supports-color
@@ -5874,13 +5877,13 @@ packages:
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 1.0.4(@sveltejs/vite-plugin-svelte@2.5.3)(svelte@4.1.2)(vite@4.4.8)
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.5
svelte: 4.1.2
svelte-hmr: 0.15.3(svelte@4.1.2)
vite: 4.4.8(@types/node@22.2.0)(sass@1.64.2)
vite: 4.4.8(@types/node@18.11.10)(sass@1.64.2)
vitefu: 0.2.5(vite@4.4.8)
transitivePeerDependencies:
- supports-color
@@ -5963,7 +5966,7 @@ packages:
chalk: 3.0.0
css.escape: 1.5.1
dom-accessibility-api: 0.5.16
jest: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2)
jest: 29.7.0(@types/node@18.11.10)(ts-node@10.9.2)
lodash: 4.17.21
redent: 3.0.0
dev: true
@@ -6176,11 +6179,6 @@ packages:
/@types/node@18.11.10:
resolution: {integrity: sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==}
/@types/node@22.2.0:
resolution: {integrity: sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==}
dependencies:
undici-types: 6.13.0
/@types/nodemailer@6.4.11:
resolution: {integrity: sha512-Ld2c0frwpGT4VseuoeboCXQ7UJIkK3X7Lx/4YsZEiUHtHsthWAOCYtf6PAiLhMtfwV0cWJRabLBS3+LD8x6Nrw==}
dependencies:
@@ -6298,7 +6296,7 @@ packages:
'@typescript-eslint/type-utils': 6.12.0(eslint@8.57.0)(typescript@5.3.2)
'@typescript-eslint/utils': 6.12.0(eslint@8.57.0)(typescript@5.3.2)
'@typescript-eslint/visitor-keys': 6.12.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.57.0
graphemer: 1.4.0
ignore: 5.3.0
@@ -6324,7 +6322,7 @@ packages:
'@typescript-eslint/types': 6.12.0
'@typescript-eslint/typescript-estree': 6.12.0(typescript@5.3.2)
'@typescript-eslint/visitor-keys': 6.12.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.57.0
typescript: 5.3.2
transitivePeerDependencies:
@@ -6359,7 +6357,7 @@ packages:
dependencies:
'@typescript-eslint/typescript-estree': 6.12.0(typescript@5.3.2)
'@typescript-eslint/utils': 6.12.0(eslint@8.57.0)(typescript@5.3.2)
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.57.0
ts-api-utils: 1.0.3(typescript@5.3.2)
typescript: 5.3.2
@@ -6388,7 +6386,7 @@ packages:
dependencies:
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/visitor-keys': 5.62.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
@@ -6409,7 +6407,7 @@ packages:
dependencies:
'@typescript-eslint/types': 6.12.0
'@typescript-eslint/visitor-keys': 6.12.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
@@ -6684,7 +6682,7 @@ packages:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
dependencies:
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
dev: false
@@ -6693,7 +6691,7 @@ packages:
resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
engines: {node: '>= 14'}
dependencies:
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
@@ -8219,7 +8217,7 @@ packages:
path-type: 4.0.0
dev: false
/create-jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2):
/create-jest@29.7.0(@types/node@18.11.10)(ts-node@10.9.2):
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
@@ -8228,7 +8226,7 @@ packages:
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
jest-config: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2)
jest-config: 29.7.0(@types/node@18.11.10)(ts-node@10.9.2)
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@@ -8754,6 +8752,7 @@ packages:
dependencies:
ms: 2.1.2
supports-color: 5.5.0
dev: false
/debug@4.3.4(supports-color@8.1.1):
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
@@ -8766,7 +8765,6 @@ packages:
dependencies:
ms: 2.1.2
supports-color: 8.1.1
dev: true
/debug@4.3.6:
resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==}
@@ -9440,7 +9438,7 @@ packages:
eslint: '*'
eslint-plugin-import: '*'
dependencies:
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
enhanced-resolve: 5.15.0
eslint: 8.57.0
eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.12.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
@@ -9637,7 +9635,7 @@ packages:
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
'@jridgewell/sourcemap-codec': 1.4.15
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.57.0
eslint-compat-utils: 0.1.2(eslint@8.57.0)
esutils: 2.0.3
@@ -9749,7 +9747,7 @@ packages:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
@@ -10091,20 +10089,6 @@ packages:
/extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
/extract-zip@2.0.1:
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
engines: {node: '>= 10.17.0'}
hasBin: true
dependencies:
debug: 4.3.4(supports-color@5.5.0)
get-stream: 5.2.0
yauzl: 2.10.0
optionalDependencies:
'@types/yauzl': 2.10.3
transitivePeerDependencies:
- supports-color
dev: false
/extract-zip@2.0.1(supports-color@8.1.1):
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
engines: {node: '>= 10.17.0'}
@@ -10117,7 +10101,6 @@ packages:
'@types/yauzl': 2.10.3
transitivePeerDependencies:
- supports-color
dev: true
/extsprintf@1.3.0:
resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==}
@@ -10494,7 +10477,7 @@ packages:
dependencies:
basic-ftp: 5.0.4
data-uri-to-buffer: 6.0.2
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
fs-extra: 11.2.0
transitivePeerDependencies:
- supports-color
@@ -10739,7 +10722,6 @@ packages:
/has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
dev: true
/has-property-descriptors@1.0.1:
resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==}
@@ -11009,7 +10991,7 @@ packages:
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
dev: false
@@ -11028,7 +11010,7 @@ packages:
engines: {node: '>= 6'}
dependencies:
agent-base: 6.0.2
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
dev: false
@@ -11038,7 +11020,7 @@ packages:
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
@@ -11173,7 +11155,7 @@ packages:
dependencies:
'@ioredis/commands': 1.2.0
cluster-key-slot: 1.1.2
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
denque: 2.1.0
lodash.defaults: 4.2.0
lodash.isarguments: 3.1.0
@@ -11587,7 +11569,7 @@ packages:
resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
engines: {node: '>=10'}
dependencies:
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
istanbul-lib-coverage: 3.2.2
source-map: 0.6.1
transitivePeerDependencies:
@@ -11664,7 +11646,7 @@ packages:
- supports-color
dev: true
/jest-cli@29.7.0(@types/node@22.2.0)(ts-node@10.9.2):
/jest-cli@29.7.0(@types/node@18.11.10)(ts-node@10.9.2):
resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
@@ -11678,10 +11660,10 @@ packages:
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
create-jest: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2)
create-jest: 29.7.0(@types/node@18.11.10)(ts-node@10.9.2)
exit: 0.1.2
import-local: 3.1.0
jest-config: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2)
jest-config: 29.7.0(@types/node@18.11.10)(ts-node@10.9.2)
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@@ -11727,48 +11709,7 @@ packages:
pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.9.2(@types/node@22.2.0)(typescript@5.1.6)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
dev: true
/jest-config@29.7.0(@types/node@22.2.0)(ts-node@10.9.2):
resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@types/node': '*'
ts-node: '>=9.0.0'
peerDependenciesMeta:
'@types/node':
optional: true
ts-node:
optional: true
dependencies:
'@babel/core': 7.23.3
'@jest/test-sequencer': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.2.0
babel-jest: 29.7.0(@babel/core@7.23.3)
chalk: 4.1.2
ci-info: 3.9.0
deepmerge: 4.3.1
glob: 7.2.3
graceful-fs: 4.2.11
jest-circus: 29.7.0
jest-environment-node: 29.7.0
jest-get-type: 29.6.3
jest-regex-util: 29.6.3
jest-resolve: 29.7.0
jest-runner: 29.7.0
jest-util: 29.7.0
jest-validate: 29.7.0
micromatch: 4.0.5
parse-json: 5.2.0
pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.9.2(@types/node@22.2.0)(typescript@5.1.6)
ts-node: 10.9.2(@types/node@18.11.10)(typescript@5.1.6)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@@ -12057,7 +11998,7 @@ packages:
supports-color: 8.1.1
dev: true
/jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2):
/jest@29.7.0(@types/node@18.11.10)(ts-node@10.9.2):
resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
@@ -12070,7 +12011,7 @@ packages:
'@jest/core': 29.7.0(ts-node@10.9.2)
'@jest/types': 29.6.3
import-local: 3.1.0
jest-cli: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2)
jest-cli: 29.7.0(@types/node@18.11.10)(ts-node@10.9.2)
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@@ -13253,7 +13194,7 @@ packages:
resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==}
dependencies:
'@types/debug': 4.1.12
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
decode-named-character-reference: 1.0.2
micromark-core-commonmark: 1.1.0
micromark-factory-space: 1.1.0
@@ -14064,7 +14005,7 @@ packages:
dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0
agent-base: 7.1.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
get-uri: 6.0.3
http-proxy-agent: 7.0.1
https-proxy-agent: 7.0.4
@@ -14321,7 +14262,7 @@ packages:
dependencies:
lilconfig: 2.1.0
postcss: 8.4.24
ts-node: 10.9.2(@types/node@22.2.0)(typescript@5.1.6)
ts-node: 10.9.2(@types/node@18.11.10)(typescript@5.1.6)
yaml: 2.3.4
dev: true
@@ -14339,7 +14280,7 @@ packages:
dependencies:
lilconfig: 2.1.0
postcss: 8.4.32
ts-node: 10.9.2(@types/node@22.2.0)(typescript@5.1.6)
ts-node: 10.9.2(@types/node@18.11.10)(typescript@5.1.6)
yaml: 2.3.4
/postcss-nested@6.0.1(postcss@8.4.32):
@@ -14638,7 +14579,7 @@ packages:
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
http-proxy-agent: 7.0.1
https-proxy-agent: 7.0.4
lru-cache: 7.18.3
@@ -14692,7 +14633,7 @@ packages:
'@puppeteer/browsers': 1.4.6(typescript@5.3.2)
chromium-bidi: 0.4.16(devtools-protocol@0.0.1147663)
cross-fetch: 4.0.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
devtools-protocol: 0.0.1147663
typescript: 5.3.2
ws: 8.13.0
@@ -15564,7 +15505,7 @@ packages:
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4(supports-color@8.1.1)
socks: 2.7.3
transitivePeerDependencies:
- supports-color
@@ -16044,7 +15985,6 @@ packages:
engines: {node: '>=10'}
dependencies:
has-flag: 4.0.0
dev: true
/supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
@@ -16132,7 +16072,7 @@ packages:
jest: '>= 27'
svelte: '>= 3'
dependencies:
jest: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2)
jest: 29.7.0(@types/node@18.11.10)(ts-node@10.9.2)
svelte: 4.1.2
dev: true
@@ -16684,7 +16624,7 @@ packages:
babel-jest: 29.7.0(@babel/core@7.22.9)
bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0
jest: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2)
jest: 29.7.0(@types/node@18.11.10)(ts-node@10.9.2)
jest-util: 29.7.0
json5: 2.2.3
lodash.memoize: 4.1.2
@@ -16694,7 +16634,7 @@ packages:
yargs-parser: 21.1.1
dev: true
/ts-node@10.9.2(@types/node@22.2.0)(typescript@5.1.6):
/ts-node@10.9.2(@types/node@18.11.10)(typescript@5.1.6):
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
hasBin: true
peerDependencies:
@@ -16713,7 +16653,7 @@ packages:
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
'@types/node': 22.2.0
'@types/node': 18.11.10
acorn: 8.11.2
acorn-walk: 8.3.1
arg: 4.1.3
@@ -16972,9 +16912,6 @@ packages:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
dev: false
/undici-types@6.13.0:
resolution: {integrity: sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==}
/undici@5.22.1:
resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==}
engines: {node: '>=14.0'}
@@ -17372,7 +17309,7 @@ packages:
vfile-message: 4.0.2
dev: false
/vite@4.4.8(@types/node@22.2.0)(sass@1.64.2):
/vite@4.4.8(@types/node@18.11.10)(sass@1.64.2):
resolution: {integrity: sha512-LONawOUUjxQridNWGQlNizfKH89qPigK36XhMI7COMGztz8KNY0JHim7/xDd71CZwGT4HtSRgI7Hy+RlhG0Gvg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
@@ -17400,7 +17337,7 @@ packages:
terser:
optional: true
dependencies:
'@types/node': 22.2.0
'@types/node': 18.11.10
esbuild: 0.18.20
postcss: 8.4.32
rollup: 3.29.4
@@ -17416,7 +17353,7 @@ packages:
vite:
optional: true
dependencies:
vite: 4.4.8(@types/node@22.2.0)(sass@1.64.2)
vite: 4.4.8(@types/node@18.11.10)(sass@1.64.2)
/vscode-oniguruma@1.7.0:
resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
@@ -17798,6 +17735,14 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
/zeptomail@6.0.0:
resolution: {integrity: sha512-RO7xOBGVGthxMK4IsOVWdkNUAxn1VGFer8Awo+t7KMaO/8xMgQtSGqFcjr48SeYbzDuw6U1FKfpRmeyXt7edOA==}
dependencies:
node-fetch: 2.7.0
transitivePeerDependencies:
- encoding
dev: false
/zod@3.21.4:
resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
dev: false