diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 8e3570d95..a8f99e8da 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -14,7 +14,7 @@ jobs: run: | cp webapp/.env.template webapp/.env cp frontend/.env.dist frontend/.env - cp backend/.env.template backend/.env + cp backend/.env.test_e2e backend/.env - name: Build docker images run: | @@ -77,7 +77,7 @@ jobs: docker load < /tmp/images/neo4j.tar docker load < /tmp/images/backend.tar docker load < /tmp/images/webapp.tar - docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend --build + docker compose -f docker-compose.yml -f docker-compose.test.yml up --build --detach --no-deps webapp neo4j backend mailserver sleep 90s - name: Full stack tests | run tests diff --git a/backend/.env.test_e2e b/backend/.env.test_e2e new file mode 100644 index 000000000..e21ce3057 --- /dev/null +++ b/backend/.env.test_e2e @@ -0,0 +1,41 @@ +DEBUG=true + +NEO4J_URI=bolt://localhost:7687 +NEO4J_USERNAME=neo4j +NEO4J_PASSWORD=letmein +GRAPHQL_URI=http://localhost:4000 +CLIENT_URI=http://localhost:3000 + +# E-Mail default settings +EMAIL_SUPPORT="devops@ocelot.social" +EMAIL_DEFAULT_SENDER="devops@ocelot.social" +SMTP_HOST=mailserver +SMTP_PORT=1025 +SMTP_IGNORE_TLS=true +SMTP_MAX_CONNECTIONS=5 +SMTP_MAX_MESSAGES=Infinity +SMTP_USERNAME= +SMTP_PASSWORD= +SMTP_SECURE="false" # true for 465, false for other ports +SMTP_DKIM_DOMAINNAME= +SMTP_DKIM_KEYSELECTOR= +SMTP_DKIM_PRIVATKEY= + +JWT_SECRET="b/&&7b78BF&fv/Vd" +JWT_EXPIRES="2y" +MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g" + +PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78" + +SENTRY_DSN_BACKEND= +COMMIT= +PUBLIC_REGISTRATION=false +INVITE_REGISTRATION=true + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_ENDPOINT= +AWS_REGION= +AWS_BUCKET= + +CATEGORIES_ACTIVE=false diff --git a/backend/package.json b/backend/package.json index 645bfd83b..76b0a30b6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -36,6 +36,7 @@ "cheerio": "~1.0.0", "cross-env": "~7.0.3", "dotenv": "~16.5.0", + "email-templates": "^12.0.2", "express": "^5.1.0", "graphql": "^14.6.0", "graphql-middleware": "~4.0.2", @@ -77,6 +78,8 @@ "node-fetch": "^2.7.0", "nodemailer": "^6.10.1", "nodemailer-html-to-text": "^3.2.0", + "preview-email": "^3.1.0", + "pug": "^3.0.3", "request": "~2.88.2", "sanitize-html": "~2.16.0", "slug": "~9.1.0", @@ -88,6 +91,7 @@ "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "^4.5.0", "@faker-js/faker": "9.7.0", + "@types/email-templates": "^10.0.4", "@types/jest": "^29.5.14", "@types/lodash": "^4.17.16", "@types/node": "^22.15.3", @@ -119,7 +123,10 @@ }, "resolutions": { "**/**/fs-capacitor": "^6.2.0", - "**/graphql-upload": "^11.0.0" + "**/graphql-upload": "^11.0.0", + "**/strip-ansi": "6.0.1", + "**/string-width": "4.2.0", + "**/wrap-ansi": "7.0.0" }, "engines": { "node": ">=20.12.1" diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 9b82299ae..e50f96dd2 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -25,6 +25,7 @@ const environment = { DISABLED_MIDDLEWARES: ['test', 'development'].includes(env.NODE_ENV as string) ? (env.DISABLED_MIDDLEWARES?.split(',') ?? []) : [], + SEND_MAIL: env.NODE_ENV !== 'test', } const required = { diff --git a/backend/src/db/seed.ts b/backend/src/db/seed.ts index 0e2c2c61d..ed1fff1a4 100644 --- a/backend/src/db/seed.ts +++ b/backend/src/db/seed.ts @@ -26,6 +26,8 @@ if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { throw new Error(`You cannot seed the database in a non-staging and real production environment!`) } +CONFIG.SEND_MAIL = true + const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl'] ;(async function () { diff --git a/backend/src/db/types/User.ts b/backend/src/db/types/User.ts new file mode 100644 index 000000000..5f621868b --- /dev/null +++ b/backend/src/db/types/User.ts @@ -0,0 +1,32 @@ +import { Integer, Node } from 'neo4j-driver' + +export interface UserDbProperties { + allowEmbedIframes: boolean + awaySince?: string + createdAt: string + deleted: boolean + disabled: boolean + emailNotificationsChatMessage?: boolean + emailNotificationsCommentOnObservedPost?: boolean + emailNotificationsFollowingUsers?: boolean + emailNotificationsGroupMemberJoined?: boolean + emailNotificationsGroupMemberLeft?: boolean + emailNotificationsGroupMemberRemoved?: boolean + emailNotificationsGroupMemberRoleChanged?: boolean + emailNotificationsMention?: boolean + emailNotificationsPostInGroup?: boolean + encryptedPassword: string + id: string + lastActiveAt?: string + lastOnlineStatus?: string + locale: string + name: string + role: string + showShoutsPublicly: boolean + slug: string + termsAndConditionsAgreedAt: string + termsAndConditionsAgreedVersion: string + updatedAt: string +} + +export type User = Node diff --git a/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap new file mode 100644 index 000000000..fd7b90395 --- /dev/null +++ b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap @@ -0,0 +1,247 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sendChatMessageMail English chat_message template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello chatReceiver,

+
+
+

you have received a new chat message from chatSender. +

Show Chat +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: New chat message", + "text": "HELLO CHATRECEIVER, + +you have received a new chat message from chatSender +[http://webapp:3000/user/chatSender/chatsender]. + +Show Chat [http://webapp:3000/chat] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendChatMessageMail German chat_message template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo chatReceiver,

+
+
+

du hast eine neue Chat-Nachricht von chatSender erhalten. +

Chat anzeigen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Neue Chat Nachricht", + "text": "HALLO CHATRECEIVER, + +du hast eine neue Chat-Nachricht von chatSender +[http://webapp:3000/user/chatSender/chatsender] erhalten. + +Chat anzeigen [http://webapp:3000/chat] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; diff --git a/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap new file mode 100644 index 000000000..698ae9082 --- /dev/null +++ b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap @@ -0,0 +1,2209 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sendNotificationMail English changed_group_member_role template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

your role in the group “The Group” has been changed. Click on the button to view this group:

View group +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: Role in group changed", + "text": "HELLO JENNY ROSTOCK, + +your role in the group “The Group” has been changed. Click on the button to view +this group: + +View group [http://webapp:3000/group/g1/the-group] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English commented_on_post template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig commented on a post that you are observing with the title “New Post”. Click on the button to view this comment: +

View comment +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: New comment on post", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] commented on a post that +you are observing with the title “New Post”. Click on the button to view this +comment: + +View comment [http://webapp:3000/post/p1/new-post#commentId-c1] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English followed_user_posted template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig, a user you are following, wrote a new post with the title “New Post”. Click on the button to view this post: +

View post +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: New post by followd user", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig], a user you are +following, wrote a new post with the title “New Post”. Click on the button to +view this post: + +View post [http://webapp:3000/post/p1/new-post] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English mentioned_in_comment template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig mentioned you in a comment to the post with the title “New Post”. Click on the button to view this comment: +

View comment +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: Mentioned in comment", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] mentioned you in a +comment to the post with the title “New Post”. Click on the button to view this +comment: + +View comment [http://webapp:3000/post/p1/new-post#commentId-c1] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English mentioned_in_post template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig mentioned you in a post with the title “New Post”. Click on the button to view this post: +

View post +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: Mentioned in post", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] mentioned you in a post +with the title “New Post”. Click on the button to view this post: + +View post [http://webapp:3000/post/p1/new-post] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English post_in_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

someone wrote a new post with the title “New Post” in one of your groups. Click on the button to view this post:

View post +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: New post in group", + "text": "HELLO JENNY ROSTOCK, + +someone wrote a new post with the title “New Post” in one of your groups. Click +on the button to view this post: + +View post [http://webapp:3000/post/p1/new-post] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English removed_user_from_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

you have been removed from the group “The Group”.

+
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: Removed from group", + "text": "HELLO JENNY ROSTOCK, + +you have been removed from the group “The Group”. + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English user_joined_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig joined the group “The Group”. Click on the button to view this group: +

View group +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: User joined group", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] joined the group “The +Group”. Click on the button to view this group: + +View group [http://webapp:3000/group/g1/the-group] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English user_left_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig left the group “The Group”. Click on the button to view this group: +

View group +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: User left group", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] left the group “The +Group”. Click on the button to view this group: + +View group [http://webapp:3000/group/g1/the-group] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German changed_group_member_role template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

deine Rolle in der Gruppe „The Group“ wurde geändert. Klicke auf den Knopf, um diese Gruppe zu sehen:

Gruppe ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Rolle in Gruppe geändert", + "text": "HALLO JENNY ROSTOCK, + +deine Rolle in der Gruppe „The Group“ wurde geändert. Klicke auf den Knopf, um +diese Gruppe zu sehen: + +Gruppe ansehen [http://webapp:3000/group/g1/the-group] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German commented_on_post template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig hat einen Beitrag den du beobachtest mit dem Titel „New Post“ kommentiert. Klicke auf den Knopf, um diesen Kommentar zu sehen: +

Kommentar ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Neuer Kommentar zu Beitrag", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] hat einen Beitrag den du +beobachtest mit dem Titel „New Post“ kommentiert. Klicke auf den Knopf, um +diesen Kommentar zu sehen: + +Kommentar ansehen [http://webapp:3000/post/p1/new-post#commentId-c1] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German followed_user_posted template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig, ein Nutzer dem du folgst, hat einen neuen Beitrag mit dem Titel „New Post“ geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen: +

Beitrag ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Neuer Beitrag von gefolgtem Nutzer", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig], ein Nutzer dem du +folgst, hat einen neuen Beitrag mit dem Titel „New Post“ geschrieben. Klicke auf +den Knopf, um diesen Beitrag zu sehen: + +Beitrag ansehen [http://webapp:3000/post/p1/new-post] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German mentioned_in_comment template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig hat dich in einem Kommentar zu dem Beitrag mit dem Titel „New Post“ erwähnt. Klicke auf den Knopf, um den Kommentar zu sehen: +

Kommentar ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Erwähnung in Kommentar", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] hat dich in einem +Kommentar zu dem Beitrag mit dem Titel „New Post“ erwähnt. Klicke auf den Knopf, +um den Kommentar zu sehen: + +Kommentar ansehen [http://webapp:3000/post/p1/new-post#commentId-c1] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German mentioned_in_post template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig hat Dich in einem Beitrag mit dem Titel „New Post“ erwähnt. Klicke auf den Knopf, um den Beitrag zu sehen: +

Beitrag ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Erwähnung in Beitrag", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] hat Dich in einem Beitrag +mit dem Titel „New Post“ erwähnt. Klicke auf den Knopf, um den Beitrag zu sehen: + +Beitrag ansehen [http://webapp:3000/post/p1/new-post] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German post_in_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

jemand hat einen neuen Beitrag mit dem Titel „New Post“ in einer deiner Gruppen geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:

Beitrag ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Neuer Beitrag in Gruppe", + "text": "HALLO JENNY ROSTOCK, + +jemand hat einen neuen Beitrag mit dem Titel „New Post“ in einer deiner Gruppen +geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen: + +Beitrag ansehen [http://webapp:3000/post/p1/new-post] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German removed_user_from_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

du wurdest aus der Gruppe „The Group“ entfernt.

+
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Aus Gruppe entfernt", + "text": "HALLO JENNY ROSTOCK, + +du wurdest aus der Gruppe „The Group“ entfernt. + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German user_joined_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig ist der Gruppe „The Group“ beigetreten. Klicke auf den Knopf, um diese Gruppe zu sehen: +

Gruppe ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Nutzer tritt Gruppe bei", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] ist der Gruppe „The +Group“ beigetreten. Klicke auf den Knopf, um diese Gruppe zu sehen: + +Gruppe ansehen [http://webapp:3000/group/g1/the-group] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German user_left_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig hat die Gruppe „The Group“ verlassen. Klicke auf den Knopf, um diese Gruppe zu sehen: +

Gruppe ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Nutzer verlässt Gruppe", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] hat die Gruppe „The +Group“ verlassen. Klicke auf den Knopf, um diese Gruppe zu sehen: + +Gruppe ansehen [http://webapp:3000/group/g1/the-group] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; diff --git a/backend/src/emails/locales/de.json b/backend/src/emails/locales/de.json new file mode 100644 index 000000000..d09991262 --- /dev/null +++ b/backend/src/emails/locales/de.json @@ -0,0 +1,39 @@ +{ + "notification": "Benachrichtigung", + "subjects": { + "changedGroupMemberRole": "Rolle in Gruppe geändert", + "chatMessage": "Neue Chat Nachricht", + "commentedOnPost": "Neuer Kommentar zu Beitrag", + "followedUserPosted": "Neuer Beitrag von gefolgtem Nutzer", + "mentionedInComment": "Erwähnung in Kommentar", + "mentionedInPost": "Erwähnung in Beitrag", + "removedUserFromGroup": "Aus Gruppe entfernt", + "postInGroup": "Neuer Beitrag in Gruppe", + "userJoinedGroup": "Nutzer tritt Gruppe bei", + "userLeftGroup": "Nutzer verlässt Gruppe" + }, + "buttons": { + "viewChat": "Chat anzeigen", + "viewComment": "Kommentar ansehen", + "viewGroup": "Gruppe ansehen", + "viewPost": "Beitrag ansehen" + }, + "general": { + "greeting": "Hallo", + "seeYou": "Bis bald bei ", + "yourTeam": "– Dein {team} Team", + "settingsHint": "PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine ", + "settingsName": "Benachrichtigungseinstellungen" + }, + "changedGroupMemberRole": "deine Rolle in der Gruppe „{groupName}“ wurde geändert. Klicke auf den Knopf, um diese Gruppe zu sehen:", + "chatMessageStart": "du hast eine neue Chat-Nachricht von ", + "chatMessageEnd": " erhalten.", + "commentedOnPost": " hat einen Beitrag den du beobachtest mit dem Titel „{postTitle}“ kommentiert. Klicke auf den Knopf, um diesen Kommentar zu sehen:", + "followedUserPosted": ", ein Nutzer dem du folgst, hat einen neuen Beitrag mit dem Titel „{postTitle}“ geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:", + "mentionedInComment": " hat dich in einem Kommentar zu dem Beitrag mit dem Titel „{postTitle}“ erwähnt. Klicke auf den Knopf, um den Kommentar zu sehen:", + "mentionedInPost": " hat Dich in einem Beitrag mit dem Titel „{postTitle}“ erwähnt. Klicke auf den Knopf, um den Beitrag zu sehen:", + "postInGroup": "jemand hat einen neuen Beitrag mit dem Titel „{postTitle}“ in einer deiner Gruppen geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:", + "removedUserFromGroup": "du wurdest aus der Gruppe „{groupName}“ entfernt.", + "userJoinedGroup": " ist der Gruppe „{groupName}“ beigetreten. Klicke auf den Knopf, um diese Gruppe zu sehen:", + "userLeftGroup": " hat die Gruppe „{groupName}“ verlassen. Klicke auf den Knopf, um diese Gruppe zu sehen:" +} diff --git a/backend/src/emails/locales/en.json b/backend/src/emails/locales/en.json new file mode 100644 index 000000000..f14f469ae --- /dev/null +++ b/backend/src/emails/locales/en.json @@ -0,0 +1,39 @@ +{ + "notification": "Notification", + "subjects": { + "changedGroupMemberRole": "Role in group changed", + "chatMessage": "New chat message", + "commentedOnPost": "New comment on post", + "followedUserPosted": "New post by followd user", + "mentionedInComment": "Mentioned in comment", + "mentionedInPost": "Mentioned in post", + "removedUserFromGroup": "Removed from group", + "postInGroup": "New post in group", + "userJoinedGroup": "User joined group", + "userLeftGroup": "User left group" + }, + "buttons": { + "viewChat": "Show Chat", + "viewComment": "View comment", + "viewGroup": "View group", + "viewPost": "View post" + }, + "general": { + "greeting": "Hello", + "seeYou": "See you soon on ", + "yourTeam": "– The {team} Team", + "settingsHint": "PS: If you don't want to receive e-mails anymore, change your ", + "settingsName": "notification settings" + }, + "changedGroupMemberRole": "your role in the group “{groupName}” has been changed. Click on the button to view this group:", + "chatMessageStart": "you have received a new chat message from ", + "chatMessageEnd": ".", + "commentedOnPost": " commented on a post that you are observing with the title “{postTitle}”. Click on the button to view this comment:", + "followedUserPosted": ", a user you are following, wrote a new post with the title “{postTitle}”. Click on the button to view this post:", + "mentionedInComment": " mentioned you in a comment to the post with the title “{postTitle}”. Click on the button to view this comment:", + "mentionedInPost": " mentioned you in a post with the title “{postTitle}”. Click on the button to view this post:", + "removedUserFromGroup": "you have been removed from the group “{groupName}”.", + "postInGroup": "someone wrote a new post with the title “{postTitle}” in one of your groups. Click on the button to view this post:", + "userJoinedGroup": " joined the group “{groupName}”. Click on the button to view this group:", + "userLeftGroup": " left the group “{groupName}”. Click on the button to view this group:" +} diff --git a/backend/src/emails/sendChatMessageMail.spec.ts b/backend/src/emails/sendChatMessageMail.spec.ts new file mode 100644 index 000000000..45835bbc3 --- /dev/null +++ b/backend/src/emails/sendChatMessageMail.spec.ts @@ -0,0 +1,87 @@ +import { sendChatMessageMail } from './sendEmail' + +const senderUser = { + allowEmbedIframes: false, + createdAt: '2025-04-30T00:16:49.610Z', + deleted: false, + disabled: false, + emailNotificationsChatMessage: true, + emailNotificationsCommentOnObservedPost: true, + emailNotificationsFollowingUsers: true, + emailNotificationsGroupMemberJoined: true, + emailNotificationsGroupMemberLeft: true, + emailNotificationsGroupMemberRemoved: true, + emailNotificationsGroupMemberRoleChanged: true, + emailNotificationsMention: true, + emailNotificationsPostInGroup: true, + encryptedPassword: '$2b$10$n.WujXapJrvn498lS97MD.gn8QwjWI9xlf8ckEYYtMTOPadMidcbG', + id: 'chatSender', + locale: 'en', + name: 'chatSender', + role: 'user', + showShoutsPublicly: false, + slug: 'chatsender', + termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z', + termsAndConditionsAgreedVersion: '0.0.1', + updatedAt: '2025-04-30T00:16:49.610Z', +} + +const recipientUser = { + allowEmbedIframes: false, + createdAt: '2025-04-30T00:16:49.716Z', + deleted: false, + disabled: false, + emailNotificationsChatMessage: true, + emailNotificationsCommentOnObservedPost: true, + emailNotificationsFollowingUsers: true, + emailNotificationsGroupMemberJoined: true, + emailNotificationsGroupMemberLeft: true, + emailNotificationsGroupMemberRemoved: true, + emailNotificationsGroupMemberRoleChanged: true, + emailNotificationsMention: true, + emailNotificationsPostInGroup: true, + encryptedPassword: '$2b$10$KOrCHvEB5CM7D.P3VcX2z.pSSBZKZhPqHW/QKym6V1S6fiG..xtBq', + id: 'chatReceiver', + locale: 'en', + name: 'chatReceiver', + role: 'user', + showShoutsPublicly: false, + slug: 'chatreceiver', + termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z', + termsAndConditionsAgreedVersion: '0.0.1', + updatedAt: '2025-04-30T00:16:49.716Z', +} + +describe('sendChatMessageMail', () => { + describe('English', () => { + beforeEach(() => { + recipientUser.locale = 'en' + }) + + it('chat_message template', async () => { + await expect( + sendChatMessageMail({ + email: 'user@example.org', + senderUser, + recipientUser, + }), + ).resolves.toMatchSnapshot() + }) + }) + + describe('German', () => { + beforeEach(() => { + recipientUser.locale = 'de' + }) + + it('chat_message template', async () => { + await expect( + sendChatMessageMail({ + email: 'user@example.org', + senderUser, + recipientUser, + }), + ).resolves.toMatchSnapshot() + }) + }) +}) diff --git a/backend/src/emails/sendEmail.ts b/backend/src/emails/sendEmail.ts new file mode 100644 index 000000000..460a3984a --- /dev/null +++ b/backend/src/emails/sendEmail.ts @@ -0,0 +1,204 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ +import path from 'node:path' + +import Email from 'email-templates' +import { createTransport } from 'nodemailer' +// import type Email as EmailType from '@types/email-templates' + +import CONFIG from '@config/index' +import logosWebapp from '@config/logos' +import metadata from '@config/metadata' +import { UserDbProperties } from '@db/types/User' + +const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD +const hasDKIMData = + CONFIG.SMTP_DKIM_DOMAINNAME && CONFIG.SMTP_DKIM_KEYSELECTOR && CONFIG.SMTP_DKIM_PRIVATKEY + +const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI) +const settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI) + +const defaultParams = { + welcomeImageUrl, + APPLICATION_NAME: CONFIG.APPLICATION_NAME, + ORGANIZATION_NAME: metadata.ORGANIZATION_NAME, + ORGANIZATION_URL: CONFIG.ORGANIZATION_URL, + supportUrl: CONFIG.SUPPORT_URL, + settingsUrl, +} + +export const transport = createTransport({ + host: CONFIG.SMTP_HOST, + port: CONFIG.SMTP_PORT, + ignoreTLS: CONFIG.SMTP_IGNORE_TLS, + secure: CONFIG.SMTP_SECURE, // true for 465, false for other ports + pool: true, + maxConnections: CONFIG.SMTP_MAX_CONNECTIONS, + maxMessages: CONFIG.SMTP_MAX_MESSAGES, + auth: hasAuthData && { + user: CONFIG.SMTP_USERNAME, + pass: CONFIG.SMTP_PASSWORD, + }, + dkim: hasDKIMData && { + domainName: CONFIG.SMTP_DKIM_DOMAINNAME, + keySelector: CONFIG.SMTP_DKIM_KEYSELECTOR, + privateKey: CONFIG.SMTP_DKIM_PRIVATKEY, + }, +}) + +const email = new Email({ + message: { + from: `${CONFIG.APPLICATION_NAME}`, + }, + transport, + i18n: { + locales: ['en', 'de'], + defaultLocale: 'en', + retryInDefaultLocale: false, + directory: path.join(__dirname, 'locales'), + updateFiles: false, + objectNotation: true, + mustacheConfig: { + tags: ['{', '}'], + disable: false, + }, + }, + send: CONFIG.SEND_MAIL, + preview: false, + // This is very useful to see the emails sent by the unit tests + /* + preview: { + open: { + app: 'brave-browser', + }, + }, + */ +}) + +interface OriginalMessage { + to: string + from: string + attachments: string[] + subject: string + html: string + text: string +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const sendNotificationMail = async (notification: any): Promise => { + const locale = notification?.to?.locale + const to = notification?.email + const name = notification?.to?.name + const template = notification?.reason + + try { + const { originalMessage } = await email.send({ + template: path.join(__dirname, 'templates', template), + message: { + to, + }, + locals: { + ...defaultParams, + locale, + name, + postTitle: + notification?.from?.__typename === 'Comment' + ? notification?.from?.post?.title + : notification?.from?.title, + postUrl: new URL( + notification?.from?.__typename === 'Comment' + ? `/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}` + : `/post/${notification?.from?.id}/${notification?.from?.slug}`, + CONFIG.CLIENT_URI, + ), + postAuthorName: + notification?.from?.__typename === 'Comment' + ? undefined + : notification?.from?.author?.name, + postAuthorUrl: + notification?.from?.__typename === 'Comment' + ? undefined + : new URL( + `user/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`, + CONFIG.CLIENT_URI, + ), + commenterName: + notification?.from?.__typename === 'Comment' + ? notification?.from?.author?.name + : undefined, + commenterUrl: + notification?.from?.__typename === 'Comment' + ? new URL( + `/user/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`, + CONFIG.CLIENT_URI, + ) + : undefined, + commentUrl: + notification?.from?.__typename === 'Comment' + ? new URL( + `/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}#commentId-${notification?.from?.id}`, + CONFIG.CLIENT_URI, + ) + : undefined, + // chattingUser: 'SR-71', + // chatUrl: new URL('/chat', CONFIG.CLIENT_URI), + groupUrl: + notification?.from?.__typename === 'Group' + ? new URL( + `/group/${notification?.from?.id}/${notification?.from?.slug}`, + CONFIG.CLIENT_URI, + ) + : undefined, + groupName: + notification?.from?.__typename === 'Group' ? notification?.from?.name : undefined, + groupRelatedUserName: + notification?.from?.__typename === 'Group' ? notification?.relatedUser?.name : undefined, + groupRelatedUserUrl: + notification?.from?.__typename === 'Group' + ? new URL( + `/user/${notification?.relatedUser?.id}/${notification?.relatedUser?.slug}`, + CONFIG.CLIENT_URI, + ) + : undefined, + }, + }) + return originalMessage as OriginalMessage + } catch (error) { + throw new Error(error) + } +} + +export interface ChatMessageEmailInput { + senderUser: UserDbProperties + recipientUser: UserDbProperties + email: string +} + +export const sendChatMessageMail = async ( + data: ChatMessageEmailInput, +): Promise => { + const { senderUser, recipientUser } = data + const to = data.email + try { + const { originalMessage } = await email.send({ + template: path.join(__dirname, 'templates', 'chat_message'), + message: { + to, + }, + locals: { + ...defaultParams, + locale: recipientUser.locale, + name: recipientUser.name, + chattingUser: senderUser.name, + chattingUserUrl: new URL(`/user/${senderUser.id}/${senderUser.slug}`, CONFIG.CLIENT_URI), + chatUrl: new URL('/chat', CONFIG.CLIENT_URI), + }, + }) + return originalMessage as OriginalMessage + } catch (error) { + throw new Error(error) + } +} diff --git a/backend/src/emails/sendNotificationMail.spec.ts b/backend/src/emails/sendNotificationMail.spec.ts new file mode 100644 index 000000000..fee641e2e --- /dev/null +++ b/backend/src/emails/sendNotificationMail.spec.ts @@ -0,0 +1,475 @@ +import { sendNotificationMail } from './sendEmail' + +describe('sendNotificationMail', () => { + let locale = 'en' + + describe('English', () => { + beforeEach(() => { + locale = 'en' + }) + + it('followed_user_posted template', async () => { + await expect( + sendNotificationMail({ + reason: 'followed_user_posted', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('post_in_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'post_in_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('mentioned_in_post template', async () => { + await expect( + sendNotificationMail({ + reason: 'mentioned_in_post', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('commented_on_post template', async () => { + await expect( + sendNotificationMail({ + reason: 'commented_on_post', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Comment', + id: 'c1', + slug: 'new-comment', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + post: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('mentioned_in_comment template', async () => { + await expect( + sendNotificationMail({ + reason: 'mentioned_in_comment', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Comment', + id: 'c1', + slug: 'new-comment', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + post: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('changed_group_member_role template', async () => { + await expect( + sendNotificationMail({ + reason: 'changed_group_member_role', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('user_joined_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'user_joined_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + relatedUser: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('user_left_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'user_left_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + relatedUser: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('removed_user_from_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'removed_user_from_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + }), + ).resolves.toMatchSnapshot() + }) + }) + + describe('German', () => { + beforeEach(() => { + locale = 'de' + }) + + it('followed_user_posted template', async () => { + await expect( + sendNotificationMail({ + reason: 'followed_user_posted', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('post_in_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'post_in_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('mentioned_in_post template', async () => { + await expect( + sendNotificationMail({ + reason: 'mentioned_in_post', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('commented_on_post template', async () => { + await expect( + sendNotificationMail({ + reason: 'commented_on_post', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Comment', + id: 'c1', + slug: 'new-comment', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + post: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('mentioned_in_comment template', async () => { + await expect( + sendNotificationMail({ + reason: 'mentioned_in_comment', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Comment', + id: 'c1', + slug: 'new-comment', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + post: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('changed_group_member_role template', async () => { + await expect( + sendNotificationMail({ + reason: 'changed_group_member_role', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('user_joined_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'user_joined_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + relatedUser: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('user_left_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'user_left_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + relatedUser: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('removed_user_from_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'removed_user_from_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + }), + ).resolves.toMatchSnapshot() + }) + }) +}) diff --git a/backend/src/emails/templates/changed_group_member_role/html.pug b/backend/src/emails/templates/changed_group_member_role/html.pug new file mode 100644 index 000000000..acb50546d --- /dev/null +++ b/backend/src/emails/templates/changed_group_member_role/html.pug @@ -0,0 +1,7 @@ +extend ../layout.pug + +block content + .content + - var groupUrl = groupUrl + p= t('changedGroupMemberRole', { groupName }) + a.button(href=groupUrl)= t('buttons.viewGroup') diff --git a/backend/src/emails/templates/changed_group_member_role/subject.pug b/backend/src/emails/templates/changed_group_member_role/subject.pug new file mode 100644 index 000000000..2cd0d345e --- /dev/null +++ b/backend/src/emails/templates/changed_group_member_role/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.changedGroupMemberRole')}` \ No newline at end of file diff --git a/backend/src/emails/templates/chat_message/html.pug b/backend/src/emails/templates/chat_message/html.pug new file mode 100644 index 000000000..d49581d7d --- /dev/null +++ b/backend/src/emails/templates/chat_message/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p= t('chatMessageStart') + a.user(href=chattingUserUrl)= chattingUser + = t('chatMessageEnd') + a.button(href=chatUrl)= t('buttons.viewChat') diff --git a/backend/src/emails/templates/chat_message/subject.pug b/backend/src/emails/templates/chat_message/subject.pug new file mode 100644 index 000000000..73206e2d6 --- /dev/null +++ b/backend/src/emails/templates/chat_message/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.chatMessage')}` \ No newline at end of file diff --git a/backend/src/emails/templates/commented_on_post/html.pug b/backend/src/emails/templates/commented_on_post/html.pug new file mode 100644 index 000000000..b139e0f9d --- /dev/null +++ b/backend/src/emails/templates/commented_on_post/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=commenterUrl)= commenterName + = t('commentedOnPost', { postTitle}) + a.button(href=commentUrl)= t('buttons.viewComment') diff --git a/backend/src/emails/templates/commented_on_post/subject.pug b/backend/src/emails/templates/commented_on_post/subject.pug new file mode 100644 index 000000000..6a3d4da35 --- /dev/null +++ b/backend/src/emails/templates/commented_on_post/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.commentedOnPost')}` \ No newline at end of file diff --git a/backend/src/emails/templates/followed_user_posted/html.pug b/backend/src/emails/templates/followed_user_posted/html.pug new file mode 100644 index 000000000..1b2a0114f --- /dev/null +++ b/backend/src/emails/templates/followed_user_posted/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=postAuthorUrl)= postAuthorName + = t('followedUserPosted', { postTitle }) + a.button(href=postUrl)= t('buttons.viewPost') diff --git a/backend/src/emails/templates/followed_user_posted/subject.pug b/backend/src/emails/templates/followed_user_posted/subject.pug new file mode 100644 index 000000000..0da84b83c --- /dev/null +++ b/backend/src/emails/templates/followed_user_posted/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.followedUserPosted')}` \ No newline at end of file diff --git a/backend/src/emails/templates/includes/footer.pug b/backend/src/emails/templates/includes/footer.pug new file mode 100644 index 000000000..a8deeac84 --- /dev/null +++ b/backend/src/emails/templates/includes/footer.pug @@ -0,0 +1,5 @@ +footer + .footer + - var organizationUrl = ORGANIZATION_URL + - var organizationName = ORGANIZATION_NAME + a(href=organizationUrl)= organizationName \ No newline at end of file diff --git a/backend/src/emails/templates/includes/greeting.pug b/backend/src/emails/templates/includes/greeting.pug new file mode 100644 index 000000000..26ae259c5 --- /dev/null +++ b/backend/src/emails/templates/includes/greeting.pug @@ -0,0 +1,14 @@ +//- This sets the greeting at the end of every e-mail +.text-block + - var organizationUrl = ORGANIZATION_URL + - var team = APPLICATION_NAME + - var settingsUrl = settingsUrl + p= t('general.seeYou') + a.organization(href=organizationUrl)= team + | ! + p= t('general.yourTeam', { team }) + br + p= t('general.settingsHint') + a.settings(href=settingsUrl)= t('general.settingsName') + | ! + diff --git a/backend/src/emails/templates/includes/header.pug b/backend/src/emails/templates/includes/header.pug new file mode 100644 index 000000000..09b2b07b7 --- /dev/null +++ b/backend/src/emails/templates/includes/header.pug @@ -0,0 +1,9 @@ +header + .head + - var img = welcomeImageUrl + img.head-logo( + alt="Welcome Image" + loading="lazy" + src=img + ) + diff --git a/backend/src/emails/templates/includes/salutation.pug b/backend/src/emails/templates/includes/salutation.pug new file mode 100644 index 000000000..faca3bb64 --- /dev/null +++ b/backend/src/emails/templates/includes/salutation.pug @@ -0,0 +1 @@ +h2= `${t('general.greeting')} ${name},` diff --git a/backend/src/emails/templates/includes/webflow.css b/backend/src/emails/templates/includes/webflow.css new file mode 100644 index 000000000..c7ea12921 --- /dev/null +++ b/backend/src/emails/templates/includes/webflow.css @@ -0,0 +1,65 @@ +body{ + display: block; + font-family: Lato, sans-serif; + font-size: 17px; + text-align: left; + text-align: -webkit-left; + justify-content: center; + padding: 15px; + margin: 0px; +} + +h2 { + margin-top: 25px; + font-size: 25px; + font-weight: normal; + line-height: 22px; + color: #333333; +} + +.container { + max-width: 680px; + margin: 0 auto; + display: block; +} + +.head-logo { + width: 60%; + height: auto; + display: block; + margin-left: auto; + margin-right: auto; +} + +a { + color: #17b53e; +} + +a.button { + background: #17b53e; + font-family: Lato, sans-serif; + font-size: 16px; + line-height: 15px; + text-decoration: none; + text-align:center; + padding: 13px 17px; + color: #ffffff; + display: table; + margin-left: auto; + margin-right: auto; + border-radius: 4px; +} + +.text-block { + margin-top: 20px; + color: #000000; +} + +footer { + padding: 20px; + font-family: Lato, sans-serif; + font-size: 12px; + line-height: 15px; + text-align: center; + color: #888888; +} diff --git a/backend/src/emails/templates/layout.pug b/backend/src/emails/templates/layout.pug new file mode 100644 index 000000000..898776323 --- /dev/null +++ b/backend/src/emails/templates/layout.pug @@ -0,0 +1,26 @@ +doctype html +html(lang=locale) + head + meta( + content="multipart/html; charset=UTF-8" + http-equiv="content-type" + ) + meta( + name="viewport" + content="width=device-width, initial-scale=1" + ) + style. + .wf-force-outline-none[tabindex="-1"]:focus{outline:none;} + style + include includes/webflow.css + + body + div.container + include includes/header.pug + include includes/salutation.pug + + .wrapper + block content + include includes/greeting.pug + + include includes/footer.pug diff --git a/backend/src/emails/templates/mentioned_in_comment/html.pug b/backend/src/emails/templates/mentioned_in_comment/html.pug new file mode 100644 index 000000000..a7b9be1de --- /dev/null +++ b/backend/src/emails/templates/mentioned_in_comment/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=commenterUrl)= commenterName + = t('mentionedInComment', { postTitle}) + a.button(href=commentUrl)= t('buttons.viewComment') diff --git a/backend/src/emails/templates/mentioned_in_comment/subject.pug b/backend/src/emails/templates/mentioned_in_comment/subject.pug new file mode 100644 index 000000000..70d094e59 --- /dev/null +++ b/backend/src/emails/templates/mentioned_in_comment/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.mentionedInComment')}` \ No newline at end of file diff --git a/backend/src/emails/templates/mentioned_in_post/html.pug b/backend/src/emails/templates/mentioned_in_post/html.pug new file mode 100644 index 000000000..5a31c7258 --- /dev/null +++ b/backend/src/emails/templates/mentioned_in_post/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=postAuthorUrl)= postAuthorName + = t('mentionedInPost', { postTitle }) + a.button(href=postUrl)= t('buttons.viewPost') diff --git a/backend/src/emails/templates/mentioned_in_post/subject.pug b/backend/src/emails/templates/mentioned_in_post/subject.pug new file mode 100644 index 000000000..c318630a3 --- /dev/null +++ b/backend/src/emails/templates/mentioned_in_post/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.mentionedInPost')}` \ No newline at end of file diff --git a/backend/src/emails/templates/post_in_group/html.pug b/backend/src/emails/templates/post_in_group/html.pug new file mode 100644 index 000000000..bc69ed2e9 --- /dev/null +++ b/backend/src/emails/templates/post_in_group/html.pug @@ -0,0 +1,7 @@ +extend ../layout.pug + +block content + .content + - var postUrl = postUrl + p= t('postInGroup', { postTitle}) + a.button(href=postUrl)= t('buttons.viewPost') diff --git a/backend/src/emails/templates/post_in_group/subject.pug b/backend/src/emails/templates/post_in_group/subject.pug new file mode 100644 index 000000000..1f989190d --- /dev/null +++ b/backend/src/emails/templates/post_in_group/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.postInGroup')}` \ No newline at end of file diff --git a/backend/src/emails/templates/removed_user_from_group/html.pug b/backend/src/emails/templates/removed_user_from_group/html.pug new file mode 100644 index 000000000..cb991540e --- /dev/null +++ b/backend/src/emails/templates/removed_user_from_group/html.pug @@ -0,0 +1,5 @@ +extend ../layout.pug + +block content + .content + p= t('removedUserFromGroup', { groupName }) diff --git a/backend/src/emails/templates/removed_user_from_group/subject.pug b/backend/src/emails/templates/removed_user_from_group/subject.pug new file mode 100644 index 000000000..c70855f62 --- /dev/null +++ b/backend/src/emails/templates/removed_user_from_group/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.removedUserFromGroup')}` \ No newline at end of file diff --git a/backend/src/emails/templates/user_joined_group/html.pug b/backend/src/emails/templates/user_joined_group/html.pug new file mode 100644 index 000000000..00bc116a8 --- /dev/null +++ b/backend/src/emails/templates/user_joined_group/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=groupRelatedUserUrl)= groupRelatedUserName + = t('userJoinedGroup', { groupName }) + a.button(href=groupUrl)= t('buttons.viewGroup') diff --git a/backend/src/emails/templates/user_joined_group/subject.pug b/backend/src/emails/templates/user_joined_group/subject.pug new file mode 100644 index 000000000..4e2cae4a1 --- /dev/null +++ b/backend/src/emails/templates/user_joined_group/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.userJoinedGroup')}` \ No newline at end of file diff --git a/backend/src/emails/templates/user_left_group/html.pug b/backend/src/emails/templates/user_left_group/html.pug new file mode 100644 index 000000000..73374e464 --- /dev/null +++ b/backend/src/emails/templates/user_left_group/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=groupRelatedUserUrl)= groupRelatedUserName + = t('userLeftGroup', { groupName }) + a.button(href=groupUrl)= t('buttons.viewGroup') diff --git a/backend/src/emails/templates/user_left_group/subject.pug b/backend/src/emails/templates/user_left_group/subject.pug new file mode 100644 index 000000000..52aa6f1a6 --- /dev/null +++ b/backend/src/emails/templates/user_left_group/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.userLeftGroup')}` \ No newline at end of file diff --git a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts index 8b41498ab..27aeb8cf4 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts @@ -16,9 +16,9 @@ import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) let query, mutate, authenticatedUser, emaillessMember @@ -208,7 +208,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'mentioned_in_post', + email: 'group.member@example.org', + }), + ) }) it('sends 3 notifications', async () => { @@ -280,7 +286,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'followed_user_posted', + email: 'group.member@example.org', + }), + ) }) it('sends 3 notifications', async () => { @@ -353,7 +365,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'post_in_group', + email: 'group.member@example.org', + }), + ) }) it('sends 3 notifications', async () => { @@ -427,7 +445,7 @@ describe('emails sent for notifications', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) it('sends 3 notifications', async () => { @@ -521,7 +539,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'mentioned_in_comment', + email: 'group.member@example.org', + }), + ) }) it('sends 2 notifications', async () => { @@ -603,7 +627,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'mentioned_in_comment', + email: 'group.member@example.org', + }), + ) }) it('sends 2 notifications', async () => { @@ -686,7 +716,7 @@ describe('emails sent for notifications', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) it('sends 2 notifications', async () => { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts index f595f441e..3bb0d48e3 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts @@ -14,9 +14,9 @@ import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) let query, mutate, authenticatedUser @@ -268,17 +268,17 @@ describe('following users notifications', () => { }) it('sends only two emails, as second follower has emails disabled and email-less follower has no email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(2) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledTimes(2) + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - html: expect.stringContaining('Hello First Follower'), - to: 'first-follower@example.org', + email: 'first-follower@example.org', + reason: 'followed_user_posted', }), ) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - html: expect.stringContaining('Hello Third Follower'), - to: 'third-follower@example.org', + email: 'third-follower@example.org', + reason: 'followed_user_posted', }), ) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts index 539022262..9eb26e57f 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts @@ -17,9 +17,9 @@ import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) let query, mutate, authenticatedUser @@ -394,7 +394,25 @@ describe('mentions in groups', () => { }) it('sends only 3 emails, one for each user with an email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(3) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(3) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_post', + }), + ) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'no.member@example.org', + reason: 'mentioned_in_post', + }), + ) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'pending.member@example.org', + reason: 'mentioned_in_post', + }), + ) }) }) @@ -490,7 +508,13 @@ describe('mentions in groups', () => { }) it('sends only 1 email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_post', + }), + ) }) }) @@ -586,7 +610,13 @@ describe('mentions in groups', () => { }) it('sends only 1 email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_post', + }), + ) }) }) @@ -670,7 +700,19 @@ describe('mentions in groups', () => { }) it('sends 2 emails', () => { - expect(sendMailMock).toHaveBeenCalledTimes(3) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(3) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_comment', + }), + ) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'no.member@example.org', + reason: 'mentioned_in_comment', + }), + ) }) }) @@ -761,7 +803,13 @@ describe('mentions in groups', () => { }) it('sends 1 email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_comment', + }), + ) }) }) @@ -852,7 +900,13 @@ describe('mentions in groups', () => { }) it('sends 1 email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_comment', + }), + ) }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts index 2fff0195d..e8c25a16f 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts @@ -14,9 +14,9 @@ import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) let query, mutate, authenticatedUser @@ -213,10 +213,11 @@ describe('notifications for users that observe a post', () => { }) it('sends one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - to: 'post-author@example.org', + email: 'post-author@example.org', + reason: 'commented_on_post', }), ) }) @@ -303,15 +304,17 @@ describe('notifications for users that observe a post', () => { }) it('sends two emails', () => { - expect(sendMailMock).toHaveBeenCalledTimes(2) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledTimes(2) + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - to: 'post-author@example.org', + email: 'post-author@example.org', + reason: 'commented_on_post', }), ) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - to: 'first-commenter@example.org', + email: 'first-commenter@example.org', + reason: 'commented_on_post', }), ) }) @@ -417,10 +420,11 @@ describe('notifications for users that observe a post', () => { }) it('sends one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - to: 'second-commenter@example.org', + email: 'second-commenter@example.org', + reason: 'commented_on_post', }), ) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts index 8d06396ce..1cbb6a2a1 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts @@ -12,9 +12,9 @@ import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) let isUserOnlineMock = jest.fn().mockReturnValue(false) @@ -109,7 +109,7 @@ describe('online status and sending emails', () => { }) it('sends NO email to the other user', () => { - expect(sendMailMock).not.toBeCalled() + expect(sendNotificationMailMock).not.toBeCalled() }) }) }) @@ -135,7 +135,7 @@ describe('online status and sending emails', () => { }) it('sends email to the other user', () => { - expect(sendMailMock).toBeCalledTimes(1) + expect(sendNotificationMailMock).toBeCalledTimes(1) }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts index 461fa6996..9a7e830ef 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts @@ -17,9 +17,9 @@ import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) let query, mutate, authenticatedUser @@ -137,7 +137,7 @@ describe('notify group members of new posts in group', () => { slug: 'group-member', }, { - email: 'test2@example.org', + email: 'group.member@example.org', password: '1234', }, ) @@ -295,7 +295,13 @@ describe('notify group members of new posts in group', () => { }) it('sends one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'post_in_group', + email: 'group.member@example.org', + }), + ) }) describe('group member mutes group', () => { @@ -337,7 +343,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) describe('group member unmutes group again but disables email', () => { @@ -392,7 +398,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) @@ -433,7 +439,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) @@ -473,7 +479,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index 985a19193..56fddfd74 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -20,16 +20,11 @@ import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' import createServer, { getContext } from '@src/server' -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), -})) - -const chatMessageTemplateMock = jest.fn() -const notificationTemplateMock = jest.fn() -jest.mock('../helpers/email/templateBuilder', () => ({ - chatMessageTemplate: () => chatMessageTemplateMock(), - notificationTemplate: () => notificationTemplateMock(), +const sendChatMessageMailMock: (notification) => void = jest.fn() +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendChatMessageMail: (notification) => sendChatMessageMailMock(notification), + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) let isUserOnlineMock = jest.fn() @@ -240,8 +235,13 @@ describe('notifications', () => { ) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'commented_on_post', + email: 'test@example.org', + }), + ) }) describe('if I have disabled `emailNotificationsCommentOnObservedPost`', () => { @@ -276,8 +276,7 @@ describe('notifications', () => { ) // No Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) @@ -398,8 +397,13 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'mentioned_in_post', + email: 'test@example.org', + }), + ) }) describe('if I have disabled `emailNotificationsMention`', () => { @@ -434,8 +438,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) @@ -941,8 +944,7 @@ describe('notifications', () => { userId: 'chatReceiver', }) - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) @@ -977,8 +979,20 @@ describe('notifications', () => { userId: 'chatReceiver', }) - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(chatMessageTemplateMock).toHaveBeenCalledTimes(1) + expect(sendChatMessageMailMock).toHaveBeenCalledTimes(1) + expect(sendChatMessageMailMock).toHaveBeenCalledWith({ + email: 'user@example.org', + senderUser: expect.objectContaining({ + name: 'chatSender', + slug: 'chatsender', + id: 'chatSender', + }), + recipientUser: expect.objectContaining({ + name: 'chatReceiver', + slug: 'chatreceiver', + id: 'chatReceiver', + }), + }) }) }) @@ -998,8 +1012,7 @@ describe('notifications', () => { expect(pubsubSpy).not.toHaveBeenCalled() expect(pubsubSpy).not.toHaveBeenCalled() - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) @@ -1019,8 +1032,7 @@ describe('notifications', () => { expect(pubsubSpy).not.toHaveBeenCalled() expect(pubsubSpy).not.toHaveBeenCalled() - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) @@ -1056,8 +1068,7 @@ describe('notifications', () => { userId: 'chatReceiver', }) - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) }) @@ -1137,8 +1148,13 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'user_joined_group', + email: 'owner@example.org', + }), + ) }) describe('if the group owner has disabled `emailNotificationsGroupMemberJoined`', () => { @@ -1170,8 +1186,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) @@ -1240,8 +1255,19 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(2) - expect(notificationTemplateMock).toHaveBeenCalledTimes(2) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(2) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'user_joined_group', + email: 'owner@example.org', + }), + ) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'user_left_group', + email: 'owner@example.org', + }), + ) }) describe('if the group owner has disabled `emailNotificationsGroupMemberLeft`', () => { @@ -1285,8 +1311,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) }) }) }) @@ -1345,8 +1370,13 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'changed_group_member_role', + email: 'test@example.org', + }), + ) }) describe('if the group member has disabled `emailNotificationsGroupMemberRoleChanged`', () => { @@ -1378,8 +1408,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) @@ -1437,8 +1466,13 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'removed_user_from_group', + email: 'test@example.org', + }), + ) }) describe('if the previous group member has disabled `emailNotificationsGroupMemberRemoved`', () => { @@ -1470,8 +1504,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 5737a6587..559c72b06 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -10,13 +10,9 @@ import { CHAT_MESSAGE_ADDED, } from '@constants/subscriptions' import { getUnreadRoomsCount } from '@graphql/resolvers/rooms' -import { sendMail } from '@middleware/helpers/email/sendMail' -import { - chatMessageTemplate, - notificationTemplate, -} from '@middleware/helpers/email/templateBuilder' import { isUserOnline } from '@middleware/helpers/isUserOnline' import { validateNotifyUsers } from '@middleware/validation/validationMiddleware' +import { sendNotificationMail, sendChatMessageMail } from '@src/emails/sendEmail' import extractMentionedUsers from './mentions/extractMentionedUsers' @@ -35,12 +31,7 @@ const publishNotifications = async ( !isUserOnline(notificationAdded.to) && !emailsSent.includes(notificationAdded.email) ) { - sendMail( - notificationTemplate({ - email: notificationAdded.email, - variables: { notification: notificationAdded }, - }), - ) + void sendNotificationMail(notificationAdded) emailsSent.push(notificationAdded.email) } }) @@ -496,7 +487,7 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) => // Send EMail if we found a user(not blocked) and he is not considered online if (recipientUser.emailNotificationsChatMessage !== false && !isUserOnline(recipientUser)) { - void sendMail(chatMessageTemplate({ email, variables: { senderUser, recipientUser } })) + void sendChatMessageMail({ email, senderUser, recipientUser }) } } diff --git a/backend/yarn.lock b/backend/yarn.lock index e3e1d9d09..209c482e4 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -166,7 +166,7 @@ "@babel/template" "^7.27.0" "@babel/types" "^7.27.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec" integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== @@ -316,7 +316,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.6.1", "@babel/types@^7.9.6": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== @@ -476,6 +476,13 @@ resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== +"@hapi/boom@^10.0.0": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-10.0.1.tgz#ebb14688275ae150aa6af788dbe482e6a6062685" + integrity sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA== + dependencies: + "@hapi/hoek" "^11.0.2" + "@hapi/bourne@1.x.x": version "1.3.2" resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" @@ -486,6 +493,11 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== +"@hapi/hoek@^11.0.2": + version "11.0.7" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-11.0.7.tgz#56a920793e0a42d10e530da9a64cc0d3919c4002" + integrity sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ== + "@hapi/joi@^15.1.1": version "15.1.1" resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" @@ -821,6 +833,77 @@ resolved "https://registry.yarnpkg.com/@kikobeats/time-span/-/time-span-1.0.5.tgz#9f7c5d48b08da02115dbf3d85ca11a6a6f8bfdeb" integrity sha512-txRAdmi35N1wnsLS1AO5mTlbY5Cv5/61WXqek2y3L9Q7u4mgdUVq819so5xe753hL5gYeLzlWoJ/VJfXg9nx8g== +"@ladjs/consolidate@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@ladjs/consolidate/-/consolidate-1.0.4.tgz#31d9604a0e3de6616aeba062c4390c5aa0e5c04d" + integrity sha512-ErvBg5acSqns86V/xW7gjqqnBBs6thnpMB0gGc3oM7WHsV8PWrnBtKI6dumHDT3UT/zEOfGzp7dmSFqWoCXKWQ== + +"@ladjs/country-language@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@ladjs/country-language/-/country-language-0.2.1.tgz#553f776fa1eb295d0344ed06525a945f94cdafaa" + integrity sha512-e3AmT7jUnfNE6e2mx2+cPYiWdFW3McySDGRhQEYE6SksjZTMj0PTp+R9x1xG89tHRTsyMNJFl9J4HtZPWZzi1Q== + dependencies: + underscore "~1.13.1" + underscore.deep "~0.5.1" + +"@ladjs/country-language@^1.0.1": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@ladjs/country-language/-/country-language-1.0.3.tgz#1131b524c6242567dfc4ce61401ff7a62e91b155" + integrity sha512-FJROu9/hh4eqVAGDyfL8vpv6Vb0qKHX1ozYLRZ+beUzD5xFf+3r0J+SVIWKviEa7W524Qvqou+ta1WrsRgzxGw== + +"@ladjs/i18n@^8.0.3": + version "8.0.3" + resolved "https://registry.yarnpkg.com/@ladjs/i18n/-/i18n-8.0.3.tgz#e2abb0726ff24fd9a8d6e37d5ca351b079974069" + integrity sha512-QYeYGz6uJaH41ZVyNoI2Lt2NyfcpKwpDIBMx3psaE1NBJn8P+jk1m0EIjphfYvnRMnl/QyBpn98FfcTUjTkuBw== + dependencies: + "@hapi/boom" "^10.0.0" + "@ladjs/country-language" "^1.0.1" + boolean "3.2.0" + i18n "^0.15.0" + i18n-locales "^0.0.5" + lodash "^4.17.21" + multimatch "5" + punycode "^2.1.1" + qs "^6.11.0" + titleize "2" + tlds "^1.231.0" + +"@messageformat/core@^3.0.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@messageformat/core/-/core-3.4.0.tgz#2814c23383dec7bddf535d54f2a03e410165ca9f" + integrity sha512-NgCFubFFIdMWJGN5WuQhHCNmzk7QgiVfrViFxcS99j7F5dDS5EP6raR54I+2ydhe4+5/XTn/YIEppFaqqVWHsw== + dependencies: + "@messageformat/date-skeleton" "^1.0.0" + "@messageformat/number-skeleton" "^1.0.0" + "@messageformat/parser" "^5.1.0" + "@messageformat/runtime" "^3.0.1" + make-plural "^7.0.0" + safe-identifier "^0.4.1" + +"@messageformat/date-skeleton@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@messageformat/date-skeleton/-/date-skeleton-1.1.0.tgz#3bad068cbf5873d14592cfc7a73dd4d8615e2739" + integrity sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A== + +"@messageformat/number-skeleton@^1.0.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz#e7c245c41a1b2722bc59dad68f4d454f761bc9b4" + integrity sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg== + +"@messageformat/parser@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.1.tgz#ca7d6c18e9f3f6b6bc984a465dac16da00106055" + integrity sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg== + dependencies: + moo "^0.5.1" + +"@messageformat/runtime@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@messageformat/runtime/-/runtime-3.0.1.tgz#94d1f6c43265c28ef7aed98ecfcc0968c6c849ac" + integrity sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg== + dependencies: + make-plural "^7.0.0" + "@metascraper/helpers@5.46.11", "@metascraper/helpers@^5.34.4": version "5.46.11" resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.46.11.tgz#d55f77623227887a1ee52be3f4ea20174c36ec72" @@ -995,6 +1078,14 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== +"@selderee/plugin-htmlparser2@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz#d5b5e29a7ba6d3958a1972c7be16f4b2c188c517" + integrity sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ== + dependencies: + domhandler "^5.0.3" + selderee "^0.11.0" + "@sentry/apm@5.15.4": version "5.15.4" resolved "https://registry.yarnpkg.com/@sentry/apm/-/apm-5.15.4.tgz#59af766d2bb4c9d98eda5ddba7a32a79ecc807a2" @@ -1219,6 +1310,15 @@ dependencies: "@types/express" "*" +"@types/email-templates@^10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@types/email-templates/-/email-templates-10.0.4.tgz#3181fd540e76e6b90b8b3e0a5a1afbc803ef7797" + integrity sha512-8O2bdGPO6RYgH2DrnFAcuV++s+8KNA5e2Erjl6UxgKRVsBH9zXu2YLrLyOBRMn2VyEYmzgF+6QQUslpVhj0y/g== + dependencies: + "@types/html-to-text" "*" + "@types/nodemailer" "*" + juice "^8.0.0" + "@types/express-serve-static-core@*": version "4.17.7" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.7.tgz#dfe61f870eb549dc6d7e12050901847c7d7e915b" @@ -1272,6 +1372,11 @@ "@types/koa" "*" graphql "^14.5.3" +"@types/html-to-text@*": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@types/html-to-text/-/html-to-text-9.0.4.tgz#4a83dd8ae8bfa91457d0b1ffc26f4d0537eff58c" + integrity sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ== + "@types/http-assert@*": version "1.5.1" resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" @@ -1366,6 +1471,11 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.2.tgz#857a118d8634c84bba7ae14088e4508490cd5da5" integrity sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q== +"@types/minimatch@^3.0.3": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== + "@types/node-fetch@2.5.7": version "2.5.7" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" @@ -1386,6 +1496,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.26.tgz#a8a119960bff16b823be4c617da028570779bcfd" integrity sha512-myMwkO2Cr82kirHY8uknNRHEVtn0wV3DTQfkrjx17jmkstDRZ24gNUdl8AHXVyVclTYI/bNjgTPTAWvWLqXqkw== +"@types/nodemailer@*": + version "6.4.17" + resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.17.tgz#5c82a42aee16a3dd6ea31446a1bd6a447f1ac1a4" + integrity sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww== + dependencies: + "@types/node" "*" + "@types/qs@*": version "6.9.3" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.3.tgz#b755a0934564a200d3efdf88546ec93c369abd03" @@ -1714,6 +1831,11 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + acorn@^8.4.1: version "8.9.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" @@ -1760,6 +1882,19 @@ ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +alce@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/alce/-/alce-1.2.0.tgz#a8be2dacaac42494612f18dc09db691f3dea4aab" + integrity sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w== + dependencies: + esprima "^1.2.0" + estraverse "^1.5.0" + +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -1772,11 +1907,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== - ansi-styles@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1796,11 +1926,6 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -2087,6 +2212,11 @@ array-buffer-byte-length@^1.0.1: call-bind "^1.0.5" is-array-buffer "^3.0.4" +array-differ@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" + integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -2168,6 +2298,16 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + asn1@~0.2.3: version "0.2.6" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" @@ -2175,6 +2315,11 @@ asn1@~0.2.3: dependencies: safer-buffer "~2.1.0" +assert-never@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.4.0.tgz#b0d4988628c87f35eb94716cc54422a63927e175" + integrity sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA== + assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" @@ -2323,6 +2468,13 @@ babel-preset-jest@^29.6.3: babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" +babel-walk@3.0.0-canary-5: + version "3.0.0-canary-5" + resolved "https://registry.yarnpkg.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz#f66ecd7298357aee44955f235a6ef54219104b11" + integrity sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw== + dependencies: + "@babel/types" "^7.9.6" + backo2@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" @@ -2398,6 +2550,11 @@ boolbase@^1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +boolean@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" + integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2605,6 +2762,14 @@ chalk@2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -2618,6 +2783,24 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +character-parser@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0" + integrity sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw== + dependencies: + is-regex "^1.0.3" + +cheerio-select@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.6.0.tgz#489f36604112c722afa147dedd0d4609c09e1696" + integrity sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g== + dependencies: + css-select "^4.3.0" + css-what "^6.0.1" + domelementtype "^2.2.0" + domhandler "^4.3.1" + domutils "^2.8.0" + cheerio-select@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" @@ -2630,6 +2813,32 @@ cheerio-select@^2.1.0: domhandler "^5.0.3" domutils "^3.0.1" +cheerio@1.0.0-rc.10: + version "1.0.0-rc.10" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" + integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== + dependencies: + cheerio-select "^1.5.0" + dom-serializer "^1.3.2" + domhandler "^4.2.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + tslib "^2.2.0" + +cheerio@1.0.0-rc.12: + version "1.0.0-rc.12" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" + integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.0.1" + htmlparser2 "^8.0.1" + parse5 "^7.0.0" + parse5-htmlparser2-tree-adapter "^7.0.0" + cheerio@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0.tgz#1ede4895a82f26e8af71009f961a9b8cb60d6a81" @@ -2686,6 +2895,11 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== +ci-info@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" @@ -2776,6 +2990,11 @@ commander@^2.20.3: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + commander@^9.0.0: version "9.5.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" @@ -2796,6 +3015,14 @@ condense-whitespace@~2.0.0: resolved "https://registry.yarnpkg.com/condense-whitespace/-/condense-whitespace-2.0.0.tgz#94e9644938f66aa7be4b8849f8f0b3cec97d6b3a" integrity sha512-Ath9o58/0rxZXbyoy3zZgrVMoIemi30sukG/btuMKCLyqfQt3dNOWc9N3EHEMa2Q3i0tXQPDJluYFLwy7pJuQw== +constantinople@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-4.0.1.tgz#0def113fa0e4dc8de83331a5cf79c8b325213151" + integrity sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw== + dependencies: + "@babel/parser" "^7.6.0" + "@babel/types" "^7.6.1" + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -2907,6 +3134,17 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^6.0.0: + version "6.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57" + integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -2925,6 +3163,17 @@ cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" +css-select@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + css-select@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" @@ -2936,7 +3185,7 @@ css-select@^5.1.0: domutils "^3.0.1" nth-check "^2.0.1" -css-what@^6.1.0: +css-what@^6.0.1, css-what@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== @@ -3051,7 +3300,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: +debug@4, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -3082,6 +3331,11 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -3097,6 +3351,11 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== +deepmerge@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + defer-to-connect@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" @@ -3174,7 +3433,12 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-newline@^3.0.0: +detect-indent@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + +detect-newline@^3.0.0, detect-newline@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== @@ -3203,6 +3467,14 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +display-notification@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/display-notification/-/display-notification-2.0.0.tgz#49fad2e03289b4f668c296e1855c2cf8ba893d49" + integrity sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw== + dependencies: + escape-string-applescript "^1.0.0" + run-applescript "^3.0.0" + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -3217,6 +3489,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +doctypes@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" + integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== + dom-serializer@^1.0.1: version "1.3.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" @@ -3226,6 +3503,15 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" +dom-serializer@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -3250,6 +3536,13 @@ domelementtype@^2.3.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== +domhandler@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" + integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== + dependencies: + domelementtype "^2.0.1" + domhandler@^4.0.0, domhandler@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" @@ -3257,6 +3550,13 @@ domhandler@^4.0.0, domhandler@^4.2.0: dependencies: domelementtype "^2.2.0" +domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" @@ -3264,6 +3564,15 @@ domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" +domutils@^2.4.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + domutils@^2.5.2: version "2.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" @@ -3301,11 +3610,6 @@ dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -3338,6 +3642,21 @@ electron-to-chromium@^1.5.28: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz#eae1ba6c49a1a61d84cf8263351d3513b2bcc534" integrity sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ== +email-templates@^12.0.2: + version "12.0.2" + resolved "https://registry.yarnpkg.com/email-templates/-/email-templates-12.0.2.tgz#b4a3b45e0da190aa337a24d7619b464ae9d5ac06" + integrity sha512-lCCnOgapf/h5Lqgz9XGlrkfZQW422MoHBylFvBJxq88VlALA6mt018Mp2reZvyimZ411Dyln+JKMN0Z64D6Bew== + dependencies: + "@ladjs/consolidate" "^1.0.4" + "@ladjs/i18n" "^8.0.3" + get-paths "^0.0.7" + html-to-text "^9.0.5" + juice "^10.0.0" + lodash "^4.17.21" + nodemailer "^6.9.14" + optionalDependencies: + preview-email "^3.0.17" + emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -3348,11 +3667,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - encodeurl@^2.0.0, encodeurl@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" @@ -3363,6 +3677,11 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= +encoding-japanese@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/encoding-japanese/-/encoding-japanese-2.2.0.tgz#0ef2d2351250547f432a2dd155453555c16deb59" + integrity sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A== + encoding-sniffer@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz#799569d66d443babe82af18c9f403498365ef1d5" @@ -3673,11 +3992,21 @@ escalade@^3.1.2: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== +escape-goat@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-3.0.0.tgz#e8b5fb658553fe8a3c4959c316c6ebb8c842b19c" + integrity sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw== + escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= +escape-string-applescript@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/escape-string-applescript/-/escape-string-applescript-1.0.0.tgz#6f1c2294245d82c63bc03338dc19a94aa8428892" + integrity sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -3904,6 +4233,11 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" +esprima@^1.2.0: + version "1.2.5" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.5.tgz#0993502feaf668138325756f30f9a51feeec11e9" + integrity sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ== + esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -3923,6 +4257,11 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" +estraverse@^1.5.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + integrity sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA== + estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -3961,6 +4300,19 @@ events@1.1.1: resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= +execa@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== + dependencies: + cross-spawn "^6.0.0" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" @@ -4087,6 +4439,11 @@ ext@^1.7.0: dependencies: type "^2.7.2" +extend-object@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/extend-object/-/extend-object-1.0.0.tgz#42514f84015d1356caf5187969dfb2bc1bda0823" + integrity sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw== + extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -4143,6 +4500,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-printf@^1.6.9: + version "1.6.10" + resolved "https://registry.yarnpkg.com/fast-printf/-/fast-printf-1.6.10.tgz#c44ad871726152159d7a903a5af0d65cf3d75875" + integrity sha512-GwTgG9O4FVIdShhbVF3JxOgSBY2+ePGsu2V/UONgoCPzF9VY6ZdBMKsHKCYQHZwNk3qNouUolRDsgVxcVA5G1w== + fastq@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2" @@ -4229,6 +4591,18 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +fixpack@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fixpack/-/fixpack-4.0.0.tgz#28b9fb8ca04f89aab382021cfa826b36dc381dfd" + integrity sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ== + dependencies: + alce "1.2.0" + chalk "^3.0.0" + detect-indent "^6.0.0" + detect-newline "^3.1.0" + extend-object "^1.0.0" + rc "^1.2.8" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -4438,6 +4812,18 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-paths@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/get-paths/-/get-paths-0.0.7.tgz#15331086752077cf130166ccd233a1cdbeefcf38" + integrity sha512-0wdJt7C1XKQxuCgouqd+ZvLJ56FQixKoki9MrFaO4EriqzXOiH9gbukaDE1ou08S8Ns3/yDzoBAISNPqj6e6tA== + dependencies: + pify "^4.0.1" + +get-port@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + get-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" @@ -4859,7 +5245,7 @@ he@0.5.0: resolved "https://registry.yarnpkg.com/he/-/he-0.5.0.tgz#2c05ffaef90b68e860f3fd2b54ef580989277ee2" integrity sha1-LAX/rvkLaOhg8/0rVO9YCYknfuI= -he@^1.2.0: +he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -4891,6 +5277,27 @@ html-to-text@7.1.1: htmlparser2 "^6.1.0" minimist "^1.2.5" +html-to-text@9.0.5, html-to-text@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-9.0.5.tgz#6149a0f618ae7a0db8085dca9bbf96d32bb8368d" + integrity sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg== + dependencies: + "@selderee/plugin-htmlparser2" "^0.11.0" + deepmerge "^4.3.1" + dom-serializer "^2.0.0" + htmlparser2 "^8.0.2" + selderee "^0.11.0" + +htmlparser2@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-5.0.1.tgz#7daa6fc3e35d6107ac95a4fc08781f091664f6e7" + integrity sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.3.0" + domutils "^2.4.2" + entities "^2.0.0" + htmlparser2@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" @@ -4901,7 +5308,7 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -htmlparser2@^8.0.0: +htmlparser2@^8.0.0, htmlparser2@^8.0.1, htmlparser2@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== @@ -4999,6 +5406,25 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +i18n-locales@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/i18n-locales/-/i18n-locales-0.0.5.tgz#8f587e598ab982511d7c7db910cb45b8d93cd96a" + integrity sha512-Kve1AHy6rqyfJHPy8MIvaKBKhHhHPXV+a/TgMkjp3UBhO3gfWR40ZQn8Xy7LI6g3FhmbvkFtv+GCZy6yvuyeHQ== + dependencies: + "@ladjs/country-language" "^0.2.1" + +i18n@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/i18n/-/i18n-0.15.1.tgz#68fb8993c461cc440bc2485d82f72019f2b92de8" + integrity sha512-yue187t8MqUPMHdKjiZGrX+L+xcUsDClGO0Cz4loaKUOK9WrGw5pgan4bv130utOwX7fHE9w2iUeHFalVQWkXA== + dependencies: + "@messageformat/core" "^3.0.0" + debug "^4.3.3" + fast-printf "^1.6.9" + make-plural "^7.0.0" + math-interval-parser "^2.0.1" + mustache "^4.2.0" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -5087,6 +5513,11 @@ inherits@2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + insane@2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/insane/-/insane-2.6.1.tgz#c7dcae7b51c20346883b71078fad6ce0483c198f" @@ -5242,6 +5673,13 @@ is-core-module@^2.13.0, is-core-module@^2.15.1: dependencies: hasown "^2.0.2" +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-data-view@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" @@ -5254,6 +5692,19 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-expression@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-4.0.0.tgz#c33155962abf21d0afd2552514d67d2ec16fd2ab" + integrity sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A== + dependencies: + acorn "^7.1.1" + object-assign "^4.1.1" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -5325,7 +5776,7 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-promise@^2.2.2: +is-promise@^2.0.0, is-promise@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== @@ -5335,6 +5786,16 @@ is-promise@^4.0.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== +is-regex@^1.0.3: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + is-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" @@ -5462,6 +5923,13 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -5947,6 +6415,11 @@ jmespath@0.16.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== +js-stringify@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" + integrity sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -6077,6 +6550,36 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" +jstransformer@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" + integrity sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A== + dependencies: + is-promise "^2.0.0" + promise "^7.0.1" + +juice@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/juice/-/juice-10.0.1.tgz#a1492091ef739e4771b9f60aad1a608b5a8ea3ba" + integrity sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA== + dependencies: + cheerio "1.0.0-rc.12" + commander "^6.1.0" + mensch "^0.3.4" + slick "^1.12.2" + web-resource-inliner "^6.0.1" + +juice@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/juice/-/juice-8.1.0.tgz#4ea23362522fe06418229943237ee3751a4fca70" + integrity sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA== + dependencies: + cheerio "1.0.0-rc.10" + commander "^6.1.0" + mensch "^0.3.4" + slick "^1.12.2" + web-resource-inliner "^6.0.1" + jwa@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" @@ -6116,6 +6619,11 @@ languagedetect@^2.0.0: resolved "https://registry.yarnpkg.com/languagedetect/-/languagedetect-2.0.0.tgz#4b8fa2b7593b2a3a02fb1100891041c53238936c" integrity sha512-AZb/liiQ+6ZoTj4f1J0aE6OkzhCo8fyH+tuSaPfSo8YHCWLFJrdSixhtO2TYdIkjcDQNaR4RmGaV2A5FJklDMQ== +leac@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/leac/-/leac-0.6.0.tgz#dcf136e382e666bd2475f44a1096061b70dc0912" + integrity sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -6129,6 +6637,26 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +libbase64@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-1.3.0.tgz#053314755a05d2e5f08bbfc48d0290e9322f4406" + integrity sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg== + +libmime@5.3.6: + version "5.3.6" + resolved "https://registry.yarnpkg.com/libmime/-/libmime-5.3.6.tgz#e6dfc655b6b4614bad90e8e65817957903b56580" + integrity sha512-j9mBC7eiqi6fgBPAGvKCXJKJSIASanYF4EeA4iBzSG0HxQxmXnR3KbyWqTn4CwsKSebqCv2f5XZfAO6sKzgvwA== + dependencies: + encoding-japanese "2.2.0" + iconv-lite "0.6.3" + libbase64 "1.3.0" + libqp "2.1.1" + +libqp@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/libqp/-/libqp-2.1.1.tgz#f1be767a58f966f500597997cab72cfc1e17abfa" + integrity sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -6139,6 +6667,13 @@ linkify-html@^4.2.0: resolved "https://registry.yarnpkg.com/linkify-html/-/linkify-html-4.2.0.tgz#06f78780827d90433424e412976d656912b13fb8" integrity sha512-bVXuLiWmGwvlH95hq6q9DFGqTsQeFSGw/nHmvvjGMZv9T3GqkxuW2d2SOgk/a4DV2ajeS4c37EqlF16cjOj7GA== +linkify-it@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + linkifyjs@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.2.0.tgz#9dd30222b9cbabec9c950e725ec00031c7fa3f08" @@ -6228,7 +6763,7 @@ lodash@4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.17.15, lodash@~4.17.11, lodash@~4.17.21: +lodash@^4.17.15, lodash@^4.17.21, lodash@~4.17.11, lodash@~4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6287,6 +6822,31 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= +mailparser@^3.7.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-3.7.2.tgz#00feec656e23c0ae805163581b460c2f72ca75d1" + integrity sha512-iI0p2TCcIodR1qGiRoDBBwboSSff50vQAWytM5JRggLfABa4hHYCf3YVujtuzV454xrOP352VsAPIzviqMTo4Q== + dependencies: + encoding-japanese "2.2.0" + he "1.2.0" + html-to-text "9.0.5" + iconv-lite "0.6.3" + libmime "5.3.6" + linkify-it "5.0.0" + mailsplit "5.4.2" + nodemailer "6.9.16" + punycode.js "2.3.1" + tlds "1.255.0" + +mailsplit@5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/mailsplit/-/mailsplit-5.4.2.tgz#ee2be344bb3511345c0bd6ea72e5657acb8cd83b" + integrity sha512-4cczG/3Iu3pyl8JgQ76dKkisurZTmxMrA4dj/e8d2jKYcFTZ7MxOzg1gTioTDMPuFXwTrVuN/gxhkrO7wLg7qA== + dependencies: + libbase64 "1.3.0" + libmime "5.3.6" + libqp "2.1.1" + make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -6317,6 +6877,11 @@ make-fetch-happen@^13.0.0: promise-retry "^2.0.1" ssri "^10.0.0" +make-plural@^7.0.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-7.4.0.tgz#fa6990dd550dea4de6b20163f74e5ed83d8a8d6d" + integrity sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg== + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -6324,6 +6889,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +math-interval-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/math-interval-parser/-/math-interval-parser-2.0.1.tgz#e22cd6d15a0a7f4c03aec560db76513da615bed4" + integrity sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA== + math-intrinsics@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" @@ -6358,6 +6928,11 @@ memoizee@0.4: next-tick "^1.1.0" timers-ext "^0.1.7" +mensch@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/mensch/-/mensch-0.3.4.tgz#770f91b46cb16ea5b204ee735768c3f0c491fecd" + integrity sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g== + merge-descriptors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" @@ -6568,6 +7143,11 @@ mime@3: resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== +mime@^2.4.6: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -6695,6 +7275,11 @@ moment@2.21.0: resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" integrity sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ== +moo@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" + integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -6705,6 +7290,17 @@ ms@2.1.3, ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multimatch@5: + version "5.0.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" + integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== + dependencies: + "@types/minimatch" "^3.0.3" + array-differ "^3.0.0" + array-union "^2.1.0" + arrify "^2.0.1" + minimatch "^3.0.4" + mustache@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" @@ -6831,6 +7427,11 @@ next-tick@^1.1.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + no-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" @@ -6879,7 +7480,12 @@ nodemailer-html-to-text@^3.2.0: dependencies: html-to-text "7.1.1" -nodemailer@^6.10.1: +nodemailer@6.9.16: + version "6.9.16" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.16.tgz#3ebdf6c6f477c571c0facb0727b33892635e0b8b" + integrity sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ== + +nodemailer@^6.10.1, nodemailer@^6.9.13, nodemailer@^6.9.14: version "6.10.1" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.10.1.tgz#cbc434c54238f83a51c07eabd04e2b3e832da623" integrity sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA== @@ -7094,6 +7700,14 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@7: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -7111,6 +7725,13 @@ p-cancelable@^2.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== +p-event@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" + integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== + dependencies: + p-timeout "^3.1.0" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -7156,11 +7777,25 @@ p-reflect@~2.1.0: resolved "https://registry.yarnpkg.com/p-reflect/-/p-reflect-2.1.0.tgz#5d67c7b3c577c4e780b9451fc9129675bd99fe67" integrity sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg== +p-timeout@^3.0.0, p-timeout@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +p-wait-for@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-wait-for/-/p-wait-for-3.2.0.tgz#640429bcabf3b0dd9f492c31539c5718cb6a3f1f" + integrity sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA== + dependencies: + p-timeout "^3.0.0" + package-json-from-dist@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" @@ -7203,6 +7838,13 @@ parse-uri@~1.0.3: resolved "https://registry.yarnpkg.com/parse-uri/-/parse-uri-1.0.7.tgz#287629a09328a97e398468f21b8a00c4a2d9cc73" integrity sha512-eWuZCMKNlVkXrEoANdXxbmqhu2SQO9jUMCSpdbJDObin0JxISn6e400EWsSRbr/czdKvWKkhZnMKEGUwf/Plmg== +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1" @@ -7218,6 +7860,11 @@ parse5-parser-stream@^7.1.2: dependencies: parse5 "^7.0.0" +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parse5@^7.0.0, parse5@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" @@ -7232,6 +7879,14 @@ parse5@^7.2.1: dependencies: entities "^4.5.0" +parseley@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/parseley/-/parseley-0.12.1.tgz#4afd561d50215ebe259e3e7a853e62f600683aef" + integrity sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw== + dependencies: + leac "^0.6.0" + peberminta "^0.9.0" + parseurl@^1.3.2, parseurl@^1.3.3, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -7255,7 +7910,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^2.0.0: +path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== @@ -7293,6 +7948,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +peberminta@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/peberminta/-/peberminta-0.9.0.tgz#8ec9bc0eb84b7d368126e71ce9033501dca2a352" + integrity sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -7318,6 +7978,11 @@ picomatch@^4.0.2: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pirates@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -7384,6 +8049,23 @@ pretty-ms@~7.0.1: dependencies: parse-ms "^2.1.0" +preview-email@^3.0.17, preview-email@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/preview-email/-/preview-email-3.1.0.tgz#ee8525d878afef4309ae548116e4a4fe8b119a6d" + integrity sha512-ZtV1YrwscEjlrUzYrTSs6Nwo49JM3pXLM4fFOBSC3wSni+bxaWlw9/Qgk75PZO8M7cX2EybmL2iwvaV3vkAttw== + dependencies: + ci-info "^3.8.0" + display-notification "2.0.0" + fixpack "^4.0.0" + get-port "5.1.1" + mailparser "^3.7.1" + nodemailer "^6.9.13" + open "7" + p-event "4.2.0" + p-wait-for "3.2.0" + pug "^3.0.3" + uuid "^9.0.1" + proc-log@^4.1.0, proc-log@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034" @@ -7397,6 +8079,13 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" +promise@^7.0.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -7433,6 +8122,109 @@ pstree.remy@^1.1.8: resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== +pug-attrs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41" + integrity sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA== + dependencies: + constantinople "^4.0.1" + js-stringify "^1.0.2" + pug-runtime "^3.0.0" + +pug-code-gen@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-3.0.3.tgz#58133178cb423fe1716aece1c1da392a75251520" + integrity sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw== + dependencies: + constantinople "^4.0.1" + doctypes "^1.1.0" + js-stringify "^1.0.2" + pug-attrs "^3.0.0" + pug-error "^2.1.0" + pug-runtime "^3.0.1" + void-elements "^3.1.0" + with "^7.0.0" + +pug-error@^2.0.0, pug-error@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-2.1.0.tgz#17ea37b587b6443d4b8f148374ec27b54b406e55" + integrity sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg== + +pug-filters@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-4.0.0.tgz#d3e49af5ba8472e9b7a66d980e707ce9d2cc9b5e" + integrity sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A== + dependencies: + constantinople "^4.0.1" + jstransformer "1.0.0" + pug-error "^2.0.0" + pug-walk "^2.0.0" + resolve "^1.15.1" + +pug-lexer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-5.0.1.tgz#ae44628c5bef9b190b665683b288ca9024b8b0d5" + integrity sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w== + dependencies: + character-parser "^2.2.0" + is-expression "^4.0.0" + pug-error "^2.0.0" + +pug-linker@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-4.0.0.tgz#12cbc0594fc5a3e06b9fc59e6f93c146962a7708" + integrity sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw== + dependencies: + pug-error "^2.0.0" + pug-walk "^2.0.0" + +pug-load@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-3.0.0.tgz#9fd9cda52202b08adb11d25681fb9f34bd41b662" + integrity sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ== + dependencies: + object-assign "^4.1.1" + pug-walk "^2.0.0" + +pug-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-6.0.0.tgz#a8fdc035863a95b2c1dc5ebf4ecf80b4e76a1260" + integrity sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw== + dependencies: + pug-error "^2.0.0" + token-stream "1.0.0" + +pug-runtime@^3.0.0, pug-runtime@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.1.tgz#f636976204723f35a8c5f6fad6acda2a191b83d7" + integrity sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg== + +pug-strip-comments@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz#f94b07fd6b495523330f490a7f554b4ff876303e" + integrity sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ== + dependencies: + pug-error "^2.0.0" + +pug-walk@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-2.0.0.tgz#417aabc29232bb4499b5b5069a2b2d2a24d5f5fe" + integrity sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ== + +pug@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.3.tgz#e18324a314cd022883b1e0372b8af3a1a99f7597" + integrity sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g== + dependencies: + pug-code-gen "^3.0.3" + pug-filters "^4.0.0" + pug-lexer "^5.0.1" + pug-linker "^4.0.0" + pug-load "^3.0.0" + pug-parser "^6.0.0" + pug-runtime "^3.0.1" + pug-strip-comments "^2.0.0" + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -7441,6 +8233,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +punycode.js@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + punycode2@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/punycode2/-/punycode2-1.0.0.tgz#e2b4b9a9a8ff157d0b84438e203181ee7892dfd8" @@ -7473,7 +8270,7 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" -qs@^6.14.0: +qs@^6.11.0, qs@^6.14.0: version "6.14.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== @@ -7525,6 +8322,16 @@ raw-body@^3.0.0: iconv-lite "0.6.3" unpipe "1.0.0" +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + re2@~1.21.4: version "1.21.4" resolved "https://registry.yarnpkg.com/re2/-/re2-1.21.4.tgz#d688edcc40da3cf542ee3a480a8b60e5900dd24d" @@ -7675,6 +8482,15 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== +resolve@^1.15.1: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^1.20.0, resolve@^1.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" @@ -7729,6 +8545,13 @@ rrweb-cssom@^0.8.0: resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== +run-applescript@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-3.2.0.tgz#73fb34ce85d3de8076d511ea767c30d4fdfc918b" + integrity sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg== + dependencies: + execa "^0.10.0" + run-parallel@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" @@ -7766,6 +8589,11 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-identifier@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb" + integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== + safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" @@ -7825,6 +8653,18 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" +selderee@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/selderee/-/selderee-0.11.0.tgz#6af0c7983e073ad3e35787ffe20cefd9daf0ec8a" + integrity sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA== + dependencies: + parseley "^0.12.0" + +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -8060,6 +8900,11 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slick@^1.12.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7" + integrity sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A== + slug@^8.2.2: version "8.2.2" resolved "https://registry.yarnpkg.com/slug/-/slug-8.2.2.tgz#33b019a857a11fc4773c1e9a9f60e3da651a9e5d" @@ -8201,23 +9046,14 @@ string-length@^4.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== +string-width@4.2.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.1.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" + strip-ansi "^6.0.0" string.prototype.trim@^1.2.8: version "1.2.8" @@ -8280,27 +9116,13 @@ string_decoder@^1.3.0: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -8326,6 +9148,11 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + subscriptions-transport-ws@^0.9.11, subscriptions-transport-ws@^0.9.16: version "0.9.19" resolved "https://registry.yarnpkg.com/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.19.tgz#10ca32f7e291d5ee8eb728b9c02e43c52606cdcf" @@ -8467,15 +9294,25 @@ titleize@1.0.0: resolved "https://registry.yarnpkg.com/titleize/-/titleize-1.0.0.tgz#7d350722061830ba6617631e0cfd3ea08398d95a" integrity sha1-fTUHIgYYMLpmF2MeDP0+oIOY2Vo= +titleize@2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-2.1.0.tgz#5530de07c22147a0488887172b5bd94f5b30a48f" + integrity sha512-m+apkYlfiQTKLW+sI4vqUkwMEzfgEUEYSqljx1voUE3Wz/z1ZsxyzSxvH2X8uKVrOp7QkByWt0rA6+gvhCKy6g== + +tlds@1.255.0, tlds@^1.242.0: + version "1.255.0" + resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.255.0.tgz#53c2571766c10da95928c716c48dfcf141341e3f" + integrity sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw== + tlds@^1.187.0: version "1.203.1" resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.203.1.tgz#4dc9b02f53de3315bc98b80665e13de3edfc1dfc" integrity sha512-7MUlYyGJ6rSitEZ3r1Q1QNV8uSIzapS8SmmhSusBuIc7uIxPPwsKllEP0GRp1NS6Ik6F+fRZvnjDWm3ecv2hDw== -tlds@^1.242.0: - version "1.255.0" - resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.255.0.tgz#53c2571766c10da95928c716c48dfcf141341e3f" - integrity sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw== +tlds@^1.231.0: + version "1.256.0" + resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.256.0.tgz#4285b41a7ed4fcc7c5eed8516c3a180e892fad36" + integrity sha512-ZmyVB9DAw+FFTmLElGYJgdZFsKLYd/I59Bg9NHkCGPwAbVZNRilFWDMAdX8UG+bHuv7kfursd5XGqo/9wi26lA== tldts-core@^6.1.78: version "6.1.78" @@ -8518,6 +9355,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +token-stream@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4" + integrity sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg== + toposort@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" @@ -8686,7 +9528,7 @@ tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.4.0, tslib@^2.8.1: +tslib@^2.2.0, tslib@^2.4.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -8847,6 +9689,11 @@ typescript@^5.8.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== +uc.micro@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -8862,6 +9709,16 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== +underscore.deep@~0.5.1: + version "0.5.3" + resolved "https://registry.yarnpkg.com/underscore.deep/-/underscore.deep-0.5.3.tgz#210969d58025339cecabd2a2ad8c3e8925e5c095" + integrity sha512-4OuSOlFNkiVFVc3khkeG112Pdu1gbitMj7t9B9ENb61uFmN70Jq7Iluhi3oflcSgexkKfDdJ5XAJET2gEq6ikA== + +underscore@~1.13.1: + version "1.13.7" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" + integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== + undici-types@~6.21.0: version "6.21.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" @@ -9015,7 +9872,7 @@ uuid@^8.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@~9.0.1: +uuid@^9.0.1, uuid@~9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== @@ -9034,6 +9891,11 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" +valid-data-url@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/valid-data-url/-/valid-data-url-3.0.1.tgz#826c1744e71b5632e847dd15dbd45b9fb38aa34f" + integrity sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA== + validator@^13.15.0: version "13.15.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd" @@ -9063,6 +9925,11 @@ video-extensions@~1.1.0: resolved "https://registry.yarnpkg.com/video-extensions/-/video-extensions-1.1.0.tgz#eaa86b45f29a853c2b873e9d8e23b513712997d6" integrity sha1-6qhrRfKahTwrhz6djiO1E3Epl9Y= +void-elements@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + w3c-xmlserializer@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" @@ -9077,6 +9944,18 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" +web-resource-inliner@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz#df0822f0a12028805fe80719ed52ab6526886e02" + integrity sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A== + dependencies: + ansi-colors "^4.1.1" + escape-goat "^3.0.0" + htmlparser2 "^5.0.0" + mime "^2.4.6" + node-fetch "^2.6.0" + valid-data-url "^3.0.0" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -9193,7 +10072,17 @@ whoops@~5.0.1: resolved "https://registry.yarnpkg.com/whoops/-/whoops-5.0.1.tgz#ce2fd6f255aca09b6fd8ec00c99f9761420296d4" integrity sha512-H2sKu1asxnFE2mNUeRzNY0CQuvl+n6iyE6phvzOaBfZblItNKpC1EzKWy2Zx+woZ3qUFK/wbmmNiLeqXwzk+FA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +with@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/with/-/with-7.0.2.tgz#ccee3ad542d25538a7a7a80aad212b9828495bac" + integrity sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w== + dependencies: + "@babel/parser" "^7.9.6" + "@babel/types" "^7.9.6" + assert-never "^1.2.1" + babel-walk "3.0.0-canary-5" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@7.0.0, wrap-ansi@^7.0.0, wrap-ansi@^8.1.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -9202,24 +10091,6 @@ whoops@~5.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"