diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 311afbe706..ed2d94aacd 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -108,7 +108,7 @@ You can install them following any of the steps described below: - Installation steps: https://asdf-vm.com/guide/getting-started.html - asdf plugin add nodejs - asdf plugin add python - - npm install -g yarn + - asdf plugin add yarn ### Using NVM and pyenv diff --git a/lerna.json b/lerna.json index b391c8e05c..8972a00565 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.4.20", + "version": "3.4.21", "npmClient": "yarn", "concurrency": 20, "command": { diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 310bf2172c..2b3dde91f8 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -81,11 +81,11 @@ export const screenComponentErrorList = derived( const errors: UIComponentError[] = [] function checkComponentErrors(component: Component, ancestors: string[]) { + errors.push(...getMissingAncestors(component, definitions, ancestors)) errors.push( ...getInvalidDatasources(screen, component, datasources, definitions) ) errors.push(...getMissingRequiredSettings(component, definitions)) - errors.push(...getMissingAncestors(component, definitions, ancestors)) for (const child of component._children || []) { checkComponentErrors(child, [...ancestors, component._component]) @@ -239,7 +239,10 @@ function getMissingAncestors( ancestors: string[] ): UIComponentError[] { const definition = definitions[component._component] - + if (ancestors.some(a => !a.startsWith(BudibasePrefix))) { + // We don't have a way to know what components are used within a plugin component + return [] + } if (!definition?.requiredAncestors?.length) { return [] } diff --git a/packages/client/src/index.js b/packages/client/src/index.js deleted file mode 100644 index f19c46a452..0000000000 --- a/packages/client/src/index.js +++ /dev/null @@ -1,142 +0,0 @@ -import ClientApp from "./components/ClientApp.svelte" -import UpdatingApp from "./components/UpdatingApp.svelte" -import { - builderStore, - appStore, - blockStore, - componentStore, - environmentStore, - dndStore, - eventStore, - hoverStore, - stateStore, - routeStore, -} from "./stores" -import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-vite.js" -import { get } from "svelte/store" -import { initWebsocket } from "./websocket.js" - -// Provide svelte and svelte/internal as globals for custom components -import * as svelte from "svelte" -import * as internal from "svelte/internal" - -window.svelte_internal = internal -window.svelte = svelte - -// Initialise spectrum icons -loadSpectrumIcons() - -let app - -const loadBudibase = async () => { - // Update builder store with any builder flags - builderStore.set({ - ...get(builderStore), - inBuilder: !!window["##BUDIBASE_IN_BUILDER##"], - layout: window["##BUDIBASE_PREVIEW_LAYOUT##"], - screen: window["##BUDIBASE_PREVIEW_SCREEN##"], - selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"], - previewId: window["##BUDIBASE_PREVIEW_ID##"], - theme: window["##BUDIBASE_PREVIEW_THEME##"], - customTheme: window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"], - previewDevice: window["##BUDIBASE_PREVIEW_DEVICE##"], - navigation: window["##BUDIBASE_PREVIEW_NAVIGATION##"], - hiddenComponentIds: window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"], - usedPlugins: window["##BUDIBASE_USED_PLUGINS##"], - location: window["##BUDIBASE_LOCATION##"], - snippets: window["##BUDIBASE_SNIPPETS##"], - componentErrors: window["##BUDIBASE_COMPONENT_ERRORS##"], - }) - - // Set app ID - this window flag is set by both the preview and the real - // server rendered app HTML - appStore.actions.setAppId(window["##BUDIBASE_APP_ID##"]) - - // Set the flag used to determine if the app is being loaded via an iframe - appStore.actions.setAppEmbedded( - window["##BUDIBASE_APP_EMBEDDED##"] === "true" - ) - - if (window.MIGRATING_APP) { - new UpdatingApp({ - target: window.document.body, - }) - return - } - - // Fetch environment info - if (!get(environmentStore)?.loaded) { - await environmentStore.actions.fetchEnvironment() - } - - // Register handler for runtime events from the builder - window.handleBuilderRuntimeEvent = (type, data) => { - if (!window["##BUDIBASE_IN_BUILDER##"]) { - return - } - if (type === "event-completed") { - eventStore.actions.resolveEvent(data) - } else if (type === "eject-block") { - const block = blockStore.actions.getBlock(data) - block?.eject() - } else if (type === "dragging-new-component") { - const { dragging, component } = data - if (dragging) { - const definition = - componentStore.actions.getComponentDefinition(component) - dndStore.actions.startDraggingNewComponent({ component, definition }) - } else { - dndStore.actions.reset() - } - } else if (type === "request-context") { - const { selectedComponentInstance, screenslotInstance } = - get(componentStore) - const instance = selectedComponentInstance || screenslotInstance - const context = instance?.getDataContext() - let stringifiedContext = null - try { - stringifiedContext = JSON.stringify(context) - } catch (error) { - // Ignore - invalid context - } - eventStore.actions.dispatchEvent("provide-context", { - context: stringifiedContext, - }) - } else if (type === "hover-component") { - hoverStore.actions.hoverComponent(data, false) - } else if (type === "builder-meta") { - builderStore.actions.setMetadata(data) - } else if (type === "builder-state") { - const [[key, value]] = Object.entries(data) - stateStore.actions.setValue(key, value) - } else if (type === "builder-url-test-data") { - const { route, testValue } = data - routeStore.actions.setTestUrlParams(route, testValue) - } - } - - // Register any custom components - if (window["##BUDIBASE_CUSTOM_COMPONENTS##"]) { - window["##BUDIBASE_CUSTOM_COMPONENTS##"].forEach(component => { - componentStore.actions.registerCustomComponent(component) - }) - } - - // Make a callback available for custom component bundles to register - // themselves at runtime - window.registerCustomComponent = - componentStore.actions.registerCustomComponent - - // Initialise websocket - initWebsocket() - - // Create app if one hasn't been created yet - if (!app) { - app = new ClientApp({ - target: window.document.body, - }) - } -} - -// Attach to window so the HTML template can call this when it loads -window.loadBudibase = loadBudibase diff --git a/packages/server/src/automations/tests/steps/sendSmtpEmail.spec.ts b/packages/server/src/automations/tests/steps/sendSmtpEmail.spec.ts index 7452239dfa..7aff612a97 100644 --- a/packages/server/src/automations/tests/steps/sendSmtpEmail.spec.ts +++ b/packages/server/src/automations/tests/steps/sendSmtpEmail.spec.ts @@ -1,3 +1,4 @@ +import { SendEmailResponse } from "@budibase/types" import TestConfiguration from "../../../tests/utilities/TestConfiguration" import * as workerRequests from "../../../utilities/workerRequests" @@ -5,17 +6,18 @@ jest.mock("../../../utilities/workerRequests", () => ({ sendSmtpEmail: jest.fn(), })) -function generateResponse(to: string, from: string) { +function generateResponse(to: string, from: string): SendEmailResponse { return { - success: true, - response: { - accepted: [to], - envelope: { - from: from, - to: [to], - }, - message: `Email sent to ${to}.`, + message: `Email sent to ${to}.`, + accepted: [to], + envelope: { + from: from, + to: [to], }, + messageId: "messageId", + pending: [], + rejected: [], + response: "response", } } diff --git a/packages/server/src/utilities/workerRequests.ts b/packages/server/src/utilities/workerRequests.ts index 0f487d9f31..dd1493b82f 100644 --- a/packages/server/src/utilities/workerRequests.ts +++ b/packages/server/src/utilities/workerRequests.ts @@ -8,7 +8,15 @@ import { logging, env as coreEnv, } from "@budibase/backend-core" -import { Ctx, User, EmailInvite, EmailAttachment } from "@budibase/types" +import { + Ctx, + User, + EmailInvite, + EmailAttachment, + SendEmailResponse, + SendEmailRequest, + EmailTemplatePurpose, +} from "@budibase/types" interface Request { ctx?: Ctx @@ -110,25 +118,23 @@ export async function sendSmtpEmail({ invite?: EmailInvite }) { // tenant ID will be set in header + const request: SendEmailRequest = { + email: to, + from, + contents, + subject, + cc, + bcc, + purpose: EmailTemplatePurpose.CUSTOM, + automation, + invite, + attachments, + } const response = await fetch( checkSlashesInUrl(env.WORKER_URL + `/api/global/email/send`), - createRequest({ - method: "POST", - body: { - email: to, - from, - contents, - subject, - cc, - bcc, - purpose: "custom", - automation, - invite, - attachments, - }, - }) + createRequest({ method: "POST", body: request }) ) - return checkResponse(response, "send email") + return (await checkResponse(response, "send email")) as SendEmailResponse } export async function removeAppFromUserRoles(ctx: Ctx, appId: string) { diff --git a/packages/types/package.json b/packages/types/package.json index ee3c059bc9..a6e08ab84c 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -17,6 +17,7 @@ "@budibase/nano": "10.1.5", "@types/json-schema": "^7.0.15", "@types/koa": "2.13.4", + "@types/nodemailer": "^6.4.17", "@types/redlock": "4.0.7", "koa-useragent": "^4.1.0", "rimraf": "3.0.2", diff --git a/packages/types/src/api/web/global/email.ts b/packages/types/src/api/web/global/email.ts index a0ca0e8485..9b3713f91c 100644 --- a/packages/types/src/api/web/global/email.ts +++ b/packages/types/src/api/web/global/email.ts @@ -1,4 +1,5 @@ import { EmailAttachment, EmailInvite } from "../../../documents" +import SMTPTransport from "nodemailer/lib/smtp-transport" export enum EmailTemplatePurpose { CORE = "core", @@ -10,19 +11,18 @@ export enum EmailTemplatePurpose { } export interface SendEmailRequest { - workspaceId?: string email: string - userId: string + userId?: string purpose: EmailTemplatePurpose contents?: string from?: string subject: string - cc?: boolean - bcc?: boolean + cc?: string + bcc?: string automation?: boolean invite?: EmailInvite attachments?: EmailAttachment[] } -export interface SendEmailResponse extends Record { +export interface SendEmailResponse extends SMTPTransport.SentMessageInfo { message: string } diff --git a/packages/types/src/documents/app/automation/automation.ts b/packages/types/src/documents/app/automation/automation.ts index d5ef35d059..dd85e1f3aa 100644 --- a/packages/types/src/documents/app/automation/automation.ts +++ b/packages/types/src/documents/app/automation/automation.ts @@ -1,10 +1,10 @@ import { Document } from "../../document" import { User } from "../../global" -import { ReadStream } from "fs" import { Row } from "../row" import { Table } from "../table" import { AutomationStep, AutomationTrigger } from "./schema" import { ContextEmitter } from "../../../sdk" +import { Readable } from "stream" export enum AutomationIOType { OBJECT = "object", @@ -99,7 +99,7 @@ export interface SendEmailOpts { // workspaceId If finer grain controls being used then this will lookup config for workspace. workspaceId?: string // user If sending to an existing user the object can be provided, this is used in the context. - user: User + user?: User // from If sending from an address that is not what is configured in the SMTP config. from?: string // contents If sending a custom email then can supply contents which will be added to it. @@ -108,8 +108,8 @@ export interface SendEmailOpts { subject: string // info Pass in a structure of information to be stored alongside the invitation. info?: any - cc?: boolean - bcc?: boolean + cc?: string + bcc?: string automation?: boolean invite?: EmailInvite attachments?: EmailAttachment[] @@ -269,7 +269,7 @@ export type AutomationAttachment = { export type AutomationAttachmentContent = { filename: string - content: ReadStream | NodeJS.ReadableStream + content: Readable } export type BucketedContent = AutomationAttachmentContent & { diff --git a/packages/worker/package.json b/packages/worker/package.json index 28728272ca..b67e8d4911 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -85,11 +85,14 @@ "@types/jsonwebtoken": "9.0.3", "@types/koa__router": "12.0.4", "@types/lodash": "4.14.200", + "@types/maildev": "^0.0.7", "@types/node-fetch": "2.6.4", + "@types/nodemailer": "^6.4.17", "@types/server-destroy": "1.0.1", "@types/supertest": "2.0.14", "@types/uuid": "8.3.4", "jest": "29.7.0", + "maildev": "^2.2.1", "nock": "^13.5.4", "nodemon": "2.0.15", "rimraf": "3.0.2", diff --git a/packages/worker/src/api/controllers/global/email.ts b/packages/worker/src/api/controllers/global/email.ts index ad0fc3fa32..801bb0d349 100644 --- a/packages/worker/src/api/controllers/global/email.ts +++ b/packages/worker/src/api/controllers/global/email.ts @@ -11,7 +11,6 @@ export async function sendEmail( ctx: UserCtx ) { let { - workspaceId, email, userId, purpose, @@ -24,13 +23,15 @@ export async function sendEmail( invite, attachments, } = ctx.request.body - let user: any + let user: User | undefined = undefined if (userId) { const db = tenancy.getGlobalDB() - user = await db.get(userId) + user = await db.tryGet(userId) + if (!user) { + ctx.throw(404, "User not found.") + } } const response = await sendEmailFn(email, purpose, { - workspaceId, user, contents, from, diff --git a/packages/worker/src/api/routes/global/tests/email.spec.ts b/packages/worker/src/api/routes/global/tests/email.spec.ts index eb46c501d9..bbc6000b58 100644 --- a/packages/worker/src/api/routes/global/tests/email.spec.ts +++ b/packages/worker/src/api/routes/global/tests/email.spec.ts @@ -1,33 +1,269 @@ -jest.mock("nodemailer") -import { EmailTemplatePurpose } from "@budibase/types" -import { TestConfiguration, mocks } from "../../../../tests" - -const sendMailMock = mocks.email.mock() +import { EmailTemplatePurpose, SendEmailRequest } from "@budibase/types" +import { TestConfiguration } from "../../../../tests" +import { + captureEmail, + deleteAllEmail, + getAttachments, + Mailserver, + startMailserver, + stopMailserver, +} from "../../../../tests/mocks/email" +import { objectStore } from "@budibase/backend-core" describe("/api/global/email", () => { const config = new TestConfiguration() + let mailserver: Mailserver beforeAll(async () => { await config.beforeAll() + mailserver = await startMailserver(config) }) afterAll(async () => { + await stopMailserver(mailserver) await config.afterAll() }) - it("should be able to send an email (with mocking)", async () => { - // initially configure settings - await config.saveSmtpConfig() - await config.saveSettingsConfig() + beforeEach(async () => { + await deleteAllEmail(mailserver) + }) - const res = await config.api.emails.sendEmail( - EmailTemplatePurpose.INVITATION + interface TestCase { + req: Partial + expectedStatus?: number + expectedContents?: string + } + + const testCases: TestCase[] = [ + { + req: { + purpose: EmailTemplatePurpose.WELCOME, + }, + expectedContents: `Thanks for getting started with Budibase's Budibase platform.`, + }, + { + req: { + purpose: EmailTemplatePurpose.INVITATION, + }, + expectedContents: `Use the button below to set up your account and get started:`, + }, + { + req: { + purpose: EmailTemplatePurpose.PASSWORD_RECOVERY, + }, + expectedContents: `You recently requested to reset your password for your Budibase account in your Budibase platform`, + }, + { + req: { + purpose: EmailTemplatePurpose.CUSTOM, + contents: "Hello, world!", + }, + expectedContents: "Hello, world!", + }, + ] + + it.each(testCases)( + "can send $req.purpose emails", + async ({ req, expectedContents, expectedStatus }) => { + const email = await captureEmail(mailserver, async () => { + const res = await config.api.emails.sendEmail( + { + email: "to@example.com", + subject: "Test", + userId: config.user!._id, + purpose: EmailTemplatePurpose.WELCOME, + ...req, + }, + { + status: expectedStatus || 200, + } + ) + expect(res.message).toBeDefined() + }) + + expect(email.html).toContain(expectedContents) + expect(email.html).not.toContain("Invalid binding") + } + ) + + it("should be able to send an email with an attachment", async () => { + let bucket = "testbucket" + let filename = "test.txt" + await objectStore.upload({ + bucket, + filename, + body: Buffer.from("test data"), + }) + let presignedUrl = await objectStore.getPresignedUrl( + bucket, + filename, + 60000 ) - expect(res.body.message).toBeDefined() - expect(sendMailMock).toHaveBeenCalled() - const emailCall = sendMailMock.mock.calls[0][0] - expect(emailCall.subject).toBe("Hello!") - expect(emailCall.html).not.toContain("Invalid binding") + let attachmentObject = { + url: presignedUrl, + filename, + } + + const email = await captureEmail(mailserver, async () => { + const res = await config.api.emails.sendEmail({ + email: "to@example.com", + subject: "Test", + userId: config.user!._id, + purpose: EmailTemplatePurpose.WELCOME, + attachments: [attachmentObject], + }) + expect(res.message).toBeDefined() + }) + + expect(email.html).toContain( + "Thanks for getting started with Budibase's Budibase platform." + ) + expect(email.html).not.toContain("Invalid binding") + + const attachments = await getAttachments(mailserver, email) + expect(attachments).toEqual(["test data"]) + }) + + it("should be able to send email without a userId", async () => { + const res = await config.api.emails.sendEmail({ + email: "to@example.com", + subject: "Test", + purpose: EmailTemplatePurpose.WELCOME, + }) + expect(res.message).toBeDefined() + }) + + it("should fail to send a password reset email without a userId", async () => { + const res = await config.api.emails.sendEmail( + { + email: "to@example.com", + subject: "Test", + purpose: EmailTemplatePurpose.PASSWORD_RECOVERY, + }, + { + status: 400, + } + ) + expect(res.message).toBeDefined() + }) + + it("can cc people", async () => { + const email = await captureEmail(mailserver, async () => { + await config.api.emails.sendEmail({ + email: "to@example.com", + cc: "cc@example.com", + subject: "Test", + purpose: EmailTemplatePurpose.CUSTOM, + contents: "Hello, world!", + }) + }) + + expect(email.cc).toEqual([{ address: "cc@example.com", name: "" }]) + }) + + it("can bcc people", async () => { + const email = await captureEmail(mailserver, async () => { + await config.api.emails.sendEmail({ + email: "to@example.com", + bcc: "bcc@example.com", + subject: "Test", + purpose: EmailTemplatePurpose.CUSTOM, + contents: "Hello, world!", + }) + }) + + expect(email.calculatedBcc).toEqual([ + { address: "bcc@example.com", name: "" }, + ]) + }) + + it("can change the from address", async () => { + const email = await captureEmail(mailserver, async () => { + const res = await config.api.emails.sendEmail({ + email: "to@example.com", + from: "from@example.com", + subject: "Test", + purpose: EmailTemplatePurpose.CUSTOM, + contents: "Hello, world!", + }) + expect(res.message).toBeDefined() + }) + + expect(email.to).toEqual([{ address: "to@example.com", name: "" }]) + expect(email.from).toEqual([{ address: "from@example.com", name: "" }]) + }) + + it("can send a calendar invite", async () => { + const startTime = new Date() + const endTime = new Date() + + const email = await captureEmail(mailserver, async () => { + await config.api.emails.sendEmail({ + email: "to@example.com", + subject: "Test", + purpose: EmailTemplatePurpose.CUSTOM, + contents: "Hello, world!", + invite: { + startTime, + endTime, + summary: "Summary", + location: "Location", + url: "http://example.com", + }, + }) + }) + + expect(email.alternatives).toEqual([ + { + charset: "utf-8", + contentType: "text/calendar", + method: "REQUEST", + transferEncoding: "7bit", + content: expect.any(String), + }, + ]) + + // Reference iCal invite: + // BEGIN:VCALENDAR + // VERSION:2.0 + // PRODID:-//sebbo.net//ical-generator//EN + // NAME:Invite + // X-WR-CALNAME:Invite + // BEGIN:VEVENT + // UID:2b5947b7-ec5a-4341-8d70-8d8130183f2a + // SEQUENCE:0 + // DTSTAMP:20200101T000000Z + // DTSTART:20200101T000000Z + // DTEND:20200101T000000Z + // SUMMARY:Summary + // LOCATION:Location + // URL;VALUE=URI:http://example.com + // END:VEVENT + // END:VCALENDAR + expect(email.alternatives[0].content).toContain("BEGIN:VCALENDAR") + expect(email.alternatives[0].content).toContain("BEGIN:VEVENT") + expect(email.alternatives[0].content).toContain("UID:") + expect(email.alternatives[0].content).toContain("SEQUENCE:0") + expect(email.alternatives[0].content).toContain("SUMMARY:Summary") + expect(email.alternatives[0].content).toContain("LOCATION:Location") + expect(email.alternatives[0].content).toContain( + "URL;VALUE=URI:http://example.com" + ) + expect(email.alternatives[0].content).toContain("END:VEVENT") + expect(email.alternatives[0].content).toContain("END:VCALENDAR") + + const formatDate = (date: Date) => + date.toISOString().replace(/[-:]/g, "").split(".")[0] + "Z" + + expect(email.alternatives[0].content).toContain( + `DTSTAMP:${formatDate(startTime)}` + ) + expect(email.alternatives[0].content).toContain( + `DTSTART:${formatDate(startTime)}` + ) + expect(email.alternatives[0].content).toContain( + `DTEND:${formatDate(endTime)}` + ) }) }) diff --git a/packages/worker/src/api/routes/global/tests/realEmail.spec.ts b/packages/worker/src/api/routes/global/tests/realEmail.spec.ts deleted file mode 100644 index 479a1f0476..0000000000 --- a/packages/worker/src/api/routes/global/tests/realEmail.spec.ts +++ /dev/null @@ -1,108 +0,0 @@ -jest.unmock("node-fetch") -import { TestConfiguration } from "../../../../tests" -import { objectStore } from "@budibase/backend-core" -import { helpers } from "@budibase/shared-core" - -import tk from "timekeeper" -import { EmailAttachment, EmailTemplatePurpose } from "@budibase/types" - -const fetch = require("node-fetch") - -const nodemailer = require("nodemailer") - -// for the real email tests give them a long time to try complete/fail -jest.setTimeout(30000) - -describe("/api/global/email", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - tk.reset() - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - async function sendRealEmail( - purpose: string, - attachments?: EmailAttachment[] - ) { - let response, text - try { - await helpers.withTimeout(20000, () => config.saveEtherealSmtpConfig()) - await helpers.withTimeout(20000, () => config.saveSettingsConfig()) - let res - if (attachments) { - res = await config.api.emails - .sendEmail(purpose, attachments) - .timeout(20000) - } else { - res = await config.api.emails.sendEmail(purpose).timeout(20000) - } - // ethereal hiccup, can't test right now - if (res.status >= 300) { - return - } - expect(res.body.message).toBeDefined() - const testUrl = nodemailer.getTestMessageUrl(res.body) - expect(testUrl).toBeDefined() - response = await fetch(testUrl) - text = await response.text() - } catch (err: any) { - // ethereal hiccup, can't test right now - if (parseInt(err.status) >= 300 || (err && err.errno === "ETIME")) { - return - } else { - throw err - } - } - let toCheckFor - switch (purpose) { - case EmailTemplatePurpose.WELCOME: - toCheckFor = `Thanks for getting started with Budibase's Budibase platform.` - break - case EmailTemplatePurpose.INVITATION: - toCheckFor = `Use the button below to set up your account and get started:` - break - case EmailTemplatePurpose.PASSWORD_RECOVERY: - toCheckFor = `You recently requested to reset your password for your Budibase account in your Budibase platform` - break - } - expect(text).toContain(toCheckFor) - } - - it("should be able to send a welcome email", async () => { - await sendRealEmail(EmailTemplatePurpose.WELCOME) - }) - - it("should be able to send a invitation email", async () => { - await sendRealEmail(EmailTemplatePurpose.INVITATION) - }) - - it("should be able to send a password recovery email", async () => { - await sendRealEmail(EmailTemplatePurpose.PASSWORD_RECOVERY) - }) - - it("should be able to send an email with attachments", async () => { - let bucket = "testbucket" - let filename = "test.txt" - await objectStore.upload({ - bucket, - filename, - body: Buffer.from("test data"), - }) - let presignedUrl = await objectStore.getPresignedUrl( - bucket, - filename, - 60000 - ) - - let attachmentObject = { - url: presignedUrl, - filename, - } - await sendRealEmail(EmailTemplatePurpose.WELCOME, [attachmentObject]) - }) -}) diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index b4d5061db7..b49d4119df 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -32,6 +32,8 @@ import { AuthToken, SCIMConfig, ConfigType, + SMTPConfig, + SMTPInnerConfig, } from "@budibase/types" import API from "./api" import jwt, { Secret } from "jsonwebtoken" @@ -348,9 +350,15 @@ class TestConfiguration { // CONFIGS - SMTP - async saveSmtpConfig() { + async saveSmtpConfig(config?: SMTPInnerConfig) { await this.deleteConfig(Config.SMTP) - await this._req(structures.configs.smtp(), null, controllers.config.save) + + let smtpConfig: SMTPConfig = structures.configs.smtp() + if (config) { + smtpConfig = { type: ConfigType.SMTP, config } + } + + await this._req(smtpConfig, null, controllers.config.save) } async saveEtherealSmtpConfig() { diff --git a/packages/worker/src/tests/api/email.ts b/packages/worker/src/tests/api/email.ts index a8f5967d58..8624f924a5 100644 --- a/packages/worker/src/tests/api/email.ts +++ b/packages/worker/src/tests/api/email.ts @@ -1,19 +1,18 @@ -import { EmailAttachment } from "@budibase/types" +import { SendEmailRequest, SendEmailResponse } from "@budibase/types" import { TestAPI } from "./base" export class EmailAPI extends TestAPI { - sendEmail = (purpose: string, attachments?: EmailAttachment[]) => { - return this.request + sendEmail = async ( + req: SendEmailRequest, + expectations?: { status?: number } + ): Promise => { + const res = await this.request .post(`/api/global/email/send`) - .send({ - email: "test@example.com", - attachments, - purpose, - tenantId: this.config.getTenantId(), - userId: this.config.user!._id!, - }) + .send(req) .set(this.config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(200) + .expect(expectations?.status || 200) + + return res.body as SendEmailResponse } } diff --git a/packages/worker/src/tests/mocks/email.ts b/packages/worker/src/tests/mocks/email.ts index c16c58ddb5..3fd2962fdd 100644 --- a/packages/worker/src/tests/mocks/email.ts +++ b/packages/worker/src/tests/mocks/email.ts @@ -1,3 +1,10 @@ +import MailDev from "maildev" +import { promisify } from "util" +import TestConfiguration from "../TestConfiguration" + +/** + * @deprecated please use the `MailDev` email server instead of this mock. + */ export function mock() { // mock the email system const sendMailMock = jest.fn() @@ -8,3 +15,170 @@ export function mock() { }) return sendMailMock } + +export type Mailserver = InstanceType +export type MailserverConfig = ConstructorParameters[0] + +export interface Attachment { + checksum: string + contentId: string + contentType: string + fileName: string + generatedFileName: string + length: number + transferEncoding: string + transformed: boolean +} + +export interface Address { + address: string + args?: boolean + name?: string +} + +export interface Alternative { + contentType: string + content: string + charset: string + method: string + transferEncoding: string +} + +export interface Envelope { + from: Address + to: Address[] + host: string + remoteAddress: string +} + +export interface Email { + attachments: Attachment[] + alternatives: Alternative[] + calculatedBcc: Address[] + cc: Address[] + date: string + envelope: Envelope + from: Address[] + headers: Record + html: string + id: string + messageId: string + priority: string + read: boolean + size: number + sizeHuman: string + source: string + time: Date + to: Address[] +} + +export function getUnusedPort(): Promise { + return new Promise((resolve, reject) => { + const server = require("net").createServer() + server.unref() + server.on("error", reject) + server.listen(0, () => { + const port = server.address().port + server.close(() => { + resolve(port) + }) + }) + }) +} + +export async function captureEmail( + mailserver: Mailserver, + f: () => Promise +): Promise { + const timeoutMs = 5000 + let timeout: ReturnType | undefined = undefined + const cancel = () => { + if (timeout) { + clearTimeout(timeout) + timeout = undefined + } + } + const timeoutPromise = new Promise((_, reject) => { + timeout = setTimeout(() => { + reject(new Error("Timed out waiting for email")) + }, timeoutMs) + }) + const mailPromise = new Promise(resolve => { + // @ts-expect-error - types are wrong + mailserver.once("new", email => { + resolve(email as Email) + cancel() + }) + }) + const emailPromise = Promise.race([mailPromise, timeoutPromise]) + try { + await f() + } finally { + cancel() + } + return await emailPromise +} + +export async function startMailserver( + config: TestConfiguration, + opts?: MailserverConfig +): Promise { + if (!opts) { + opts = {} + } + if (!opts.smtp) { + opts.smtp = await getUnusedPort() + } + const mailserver = new MailDev(opts || {}) + await new Promise((resolve, reject) => { + mailserver.listen(err => { + if (err) { + return reject(err) + } + resolve(mailserver) + }) + }) + await config.saveSmtpConfig({ + host: "localhost", + port: opts.smtp, + secure: false, + from: "test@example.com", + }) + return mailserver +} + +export function deleteAllEmail(mailserver: Mailserver) { + return promisify(mailserver.deleteAllEmail).bind(mailserver)() +} + +export function stopMailserver(mailserver: Mailserver) { + return promisify(mailserver.close).bind(mailserver)() +} + +export function getAttachment( + mailserver: Mailserver, + email: Email, + attachment: Attachment +) { + return new Promise(resolve => { + // @ts-expect-error - types are wrong + mailserver.getEmailAttachment( + email.id, + attachment.generatedFileName, + (err: any, _contentType: string, stream: ReadableStream) => { + if (err) { + throw err + } + resolve(new Response(stream).text()) + } + ) + }) +} + +export function getAttachments(mailserver: Mailserver, email: Email) { + return Promise.all( + email.attachments.map(attachment => + getAttachment(mailserver, email, attachment) + ) + ) +} diff --git a/packages/worker/src/utilities/email.ts b/packages/worker/src/utilities/email.ts index fa9dd7a6fa..411f6b1e4a 100644 --- a/packages/worker/src/utilities/email.ts +++ b/packages/worker/src/utilities/email.ts @@ -4,16 +4,17 @@ import { getTemplateByPurpose, EmailTemplates } from "../constants/templates" import { getSettingsTemplateContext } from "./templates" import { processString } from "@budibase/string-templates" import { - User, SendEmailOpts, SMTPInnerConfig, EmailTemplatePurpose, + User, } from "@budibase/types" -import { configs, cache, objectStore } from "@budibase/backend-core" +import { configs, cache, objectStore, HTTPError } from "@budibase/backend-core" import ical from "ical-generator" import _ from "lodash" -const nodemailer = require("nodemailer") +import nodemailer from "nodemailer" +import SMTPTransport from "nodemailer/lib/smtp-transport" const TEST_MODE = env.ENABLE_EMAIL_TEST_MODE && env.isDev() const TYPE = TemplateType.EMAIL @@ -26,7 +27,7 @@ const FULL_EMAIL_PURPOSES = [ ] function createSMTPTransport(config?: SMTPInnerConfig) { - let options: any + let options: SMTPTransport.Options let secure = config?.secure // default it if not specified if (secure == null) { @@ -59,22 +60,6 @@ function createSMTPTransport(config?: SMTPInnerConfig) { return nodemailer.createTransport(options) } -async function getLinkCode( - purpose: EmailTemplatePurpose, - email: string, - user: User, - info: any = null -) { - switch (purpose) { - case EmailTemplatePurpose.PASSWORD_RECOVERY: - return cache.passwordReset.createCode(user._id!, info) - case EmailTemplatePurpose.INVITATION: - return cache.invite.createCode(email, info) - default: - return null - } -} - /** * Builds an email using handlebars and the templates found in the system (default or otherwise). * @param purpose the purpose of the email being built, e.g. invitation, password reset. @@ -87,8 +72,8 @@ async function getLinkCode( async function buildEmail( purpose: EmailTemplatePurpose, email: string, - context: any, - { user, contents }: any = {} + context: Record, + { user, contents }: { user?: User; contents?: string } = {} ) { // this isn't a full email if (FULL_EMAIL_PURPOSES.indexOf(purpose) === -1) { @@ -106,8 +91,8 @@ async function buildEmail( throw "Unable to build email, missing base components" } - let name = user ? user.name : undefined - if (user && !name && user.firstName) { + let name: string | undefined + if (user && user.firstName) { name = user.lastName ? `${user.firstName} ${user.lastName}` : user.firstName } context = { @@ -158,10 +143,21 @@ export async function sendEmail( } const transport = createSMTPTransport(config) // if there is a link code needed this will retrieve it - const code = await getLinkCode(purpose, email, opts.user, opts?.info) + let code: string | null = null + switch (purpose) { + case EmailTemplatePurpose.PASSWORD_RECOVERY: + if (!opts.user || !opts.user._id) { + throw new HTTPError("User must be provided for password recovery.", 400) + } + code = await cache.passwordReset.createCode(opts.user._id, opts.info) + break + case EmailTemplatePurpose.INVITATION: + code = await cache.invite.createCode(email, opts.info) + break + } let context = await getSettingsTemplateContext(purpose, code) - let message: any = { + let message: Parameters[0] = { from: opts?.from || config?.from, html: await buildEmail(purpose, email, context, { user: opts?.user, diff --git a/yarn.lock b/yarn.lock index 48eaaf4ecd..279c4067a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -82,6 +82,17 @@ call-me-maybe "^1.0.1" z-schema "^5.0.1" +"@asamuzakjp/css-color@^2.8.2": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-2.8.3.tgz#665f0f5e8edb95d8f543847529e30fe5cc437ef7" + integrity sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw== + dependencies: + "@csstools/css-calc" "^2.1.1" + "@csstools/css-color-parser" "^3.0.7" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + lru-cache "^10.4.3" + "@aws-crypto/crc32@5.2.0": version "5.2.0" resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-5.2.0.tgz#cfcc22570949c98c6689cfcbd2d693d36cdae2e1" @@ -2785,9 +2796,9 @@ through2 "^2.0.0" "@budibase/pro@npm:@budibase/pro@latest": - version "3.4.12" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.4.12.tgz#60e630944de4e2de970a04179d8f0f57d48ce75e" - integrity sha512-msUBmcWxRDg+ugjZvd27XudERQqtQRdiARsO8MaDVTcp5ejIXgshEIVVshHOCj3hcbRblw9pXvBIMI53iTMUsA== + version "3.4.20" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.4.20.tgz#0d855d6ed8fe92fd178c74a8963d879cc124b034" + integrity sha512-hUteGvhMOKjBo0fluxcqNs7d4x8OU5W8Oqqrm7eIS9Ohe7ala2iWNCcrj+x+S9CavIm6s7JZZnAewa2Maiz2zQ== dependencies: "@anthropic-ai/sdk" "^0.27.3" "@budibase/backend-core" "*" @@ -2957,6 +2968,34 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@csstools/color-helpers@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" + integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== + +"@csstools/css-calc@^2.1.1", "@csstools/css-calc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.2.tgz#bffd55f002dab119b76d4023f95cd943e6c8c11e" + integrity sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw== + +"@csstools/css-color-parser@^3.0.7": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz#5fe9322920851450bf5e065c2b0e731b9e165394" + integrity sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ== + dependencies: + "@csstools/color-helpers" "^5.0.2" + "@csstools/css-calc" "^2.1.2" + +"@csstools/css-parser-algorithms@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz#74426e93bd1c4dcab3e441f5cc7ba4fb35d94356" + integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== + +"@csstools/css-tokenizer@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz#a5502c8539265fecbd873c1e395a890339f119c2" + integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== + "@curlconverter/yargs-parser@^0.0.1": version "0.0.1" resolved "https://registry.yarnpkg.com/@curlconverter/yargs-parser/-/yargs-parser-0.0.1.tgz#62a360cea3d62b9b5805e61e8110cea98da8d140" @@ -6689,6 +6728,13 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== +"@types/maildev@^0.0.7": + version "0.0.7" + resolved "https://registry.yarnpkg.com/@types/maildev/-/maildev-0.0.7.tgz#025e646776a170e63cc8631d09391ba7612ba6cf" + integrity sha512-8Xs2PFd7SA2tAojYuAISTcE+IrrZysfuDQcwiE/cDMbErw2OyAHbO1yiJyYODnmdb/7dUIH7uaTrqK8H+kPB5w== + dependencies: + "@types/node" "*" + "@types/marked@^4.0.7": version "4.0.8" resolved "https://registry.yarnpkg.com/@types/marked/-/marked-4.0.8.tgz#b316887ab3499d0a8f4c70b7bd8508f92d477955" @@ -6775,6 +6821,13 @@ dependencies: undici-types "~6.19.2" +"@types/nodemailer@^6.4.17": + 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/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -7141,6 +7194,11 @@ resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== +"@types/trusted-types@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + "@types/tunnel@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.3.tgz#f109e730b072b3136347561fc558c9358bb8c6e9" @@ -7556,7 +7614,7 @@ abstract-leveldown@~6.2.1, abstract-leveldown@~6.2.3: level-supports "~1.0.0" xtend "~4.0.0" -accepts@^1.3.5, accepts@^1.3.7, accepts@~1.3.4: +accepts@^1.3.5, accepts@^1.3.7, accepts@~1.3.4, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -7627,6 +7685,11 @@ add-stream@^1.0.0: resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ== +addressparser@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" + integrity sha512-aQX7AISOMM7HFE0iZ3+YnD07oIeJqWGVnJ+ZIKaBZAk03ftmVYVqsGas/rbXKR21n4D/hKCSHypvcyOkds/xzg== + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -7641,6 +7704,11 @@ agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" +agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== + agentkeepalive@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" @@ -7907,6 +7975,11 @@ array-differ@^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" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" @@ -8312,6 +8385,11 @@ bare-path@^2.0.0, bare-path@^2.1.0: dependencies: bare-os "^2.1.0" +base32.js@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/base32.js/-/base32.js-0.1.0.tgz#b582dec693c2f11e893cf064ee6ac5b6131a2202" + integrity sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ== + base62@^1.1.0: version "1.2.8" resolved "https://registry.yarnpkg.com/base62/-/base62-1.2.8.tgz#1264cb0fb848d875792877479dbe8bae6bae3428" @@ -8424,6 +8502,24 @@ bn.js@^5.2.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + boolean@^3.0.1: version "3.2.0" resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" @@ -9181,6 +9277,11 @@ commander@^11.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== +commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + commander@^2.16.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@^2.5.0, commander@^2.7.1, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -9245,13 +9346,26 @@ compress-commons@^6.0.2: normalize-path "^3.0.0" readable-stream "^4.0.0" -compressible@^2.0.0: +compressible@^2.0.0, compressible@~2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" +compression@^1.7.4: + version "1.8.0" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.8.0.tgz#09420efc96e11a0f44f3a558de59e321364180f7" + integrity sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA== + dependencies: + bytes "3.1.2" + compressible "~2.0.18" + debug "2.6.9" + negotiator "~0.6.4" + on-headers "~1.0.2" + safe-buffer "5.2.1" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -9325,14 +9439,14 @@ consolidate@^0.16.0: dependencies: bluebird "^3.7.2" -content-disposition@^0.5.2, content-disposition@^0.5.3, content-disposition@^0.5.4, content-disposition@~0.5.2: +content-disposition@0.5.4, content-disposition@^0.5.2, content-disposition@^0.5.3, content-disposition@^0.5.4, content-disposition@~0.5.2: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" -content-type@^1.0.4: +content-type@^1.0.4, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -9420,6 +9534,16 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== + cookie@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" @@ -9488,7 +9612,7 @@ correlation-id@4.0.0: dependencies: uuid "^8.3.1" -cors@~2.8.5: +cors@^2.8.5, cors@~2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== @@ -9643,6 +9767,14 @@ cssstyle@^3.0.0: dependencies: rrweb-cssom "^0.6.0" +cssstyle@^4.0.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.2.1.tgz#5142782410fea95db66fb68147714a652a7c2381" + integrity sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw== + dependencies: + "@asamuzakjp/css-color" "^2.8.2" + rrweb-cssom "^0.8.0" + csvtojson@2.0.10: version "2.0.10" resolved "https://registry.yarnpkg.com/csvtojson/-/csvtojson-2.0.10.tgz#11e7242cc630da54efce7958a45f443210357574" @@ -9757,6 +9889,14 @@ data-urls@^4.0.0: whatwg-mimetype "^3.0.0" whatwg-url "^12.0.0" +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== + dependencies: + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + data-view-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" @@ -9842,6 +9982,13 @@ dd-trace@5.26.0: shell-quote "^1.8.1" tlhunter-sorted-set "^0.1.0" +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@4, debug@^4.0.0, debug@^4.0.1, 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: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" @@ -10164,7 +10311,7 @@ dequal@^2.0.3: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== -destroy@^1.0.4: +destroy@1.2.0, destroy@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -10481,6 +10628,13 @@ domhandler@^5.0.1, domhandler@^5.0.2: dependencies: domelementtype "^2.3.0" +dompurify@^3.1.6: + version "3.2.4" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.4.tgz#af5a5a11407524431456cf18836c55d13441cd8e" + integrity sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + domutils@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" @@ -10662,11 +10816,16 @@ enabled@2.0.x: resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== -encodeurl@^1.0.2: +encodeurl@^1.0.2, encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encoding-down@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" @@ -10950,12 +11109,12 @@ escape-goat@^2.0.0: resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== -escape-html@^1.0.3: +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 sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5, 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" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -11284,6 +11443,11 @@ esutils@^2.0.2, esutils@^2.0.3: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + event-lite@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/event-lite/-/event-lite-0.1.3.tgz#3dfe01144e808ac46448f0c19b4ab68e403a901d" @@ -11377,6 +11541,43 @@ express-useragent@^1.0.15: resolved "https://registry.yarnpkg.com/express-useragent/-/express-useragent-1.0.15.tgz#cefda5fa4904345d51d3368b117a8dd4124985d9" integrity sha512-eq5xMiYCYwFPoekffMjvEIk+NWdlQY9Y38OsTyl13IvA728vKT+q/CSERYWzcw93HGBJcIqMIsZC5CZGARPVdg== +express@^4.21.2: + version "4.21.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" + integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.3" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.7.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.3.1" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.3" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.12" + proxy-addr "~2.0.7" + qs "6.13.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.19.0" + serve-static "1.16.2" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + ext-list@^2.0.0: version "2.2.2" resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" @@ -11399,7 +11600,7 @@ extend-shallow@^2.0.1: dependencies: is-extendable "^0.1.0" -extend@^3.0.2, extend@~3.0.2: +extend@^3.0.2, extend@~3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -11691,6 +11892,19 @@ filter-obj@^1.1.0: resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== + dependencies: + debug "2.6.9" + encodeurl "~2.0.0" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + find-free-port@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-free-port/-/find-free-port-2.0.0.tgz#4b22e5f6579eb1a38c41ac6bcb3efed1b6da9b1b" @@ -11875,7 +12089,12 @@ formidable@^3.5.2: hexoid "^2.0.0" once "^1.4.0" -fresh@^0.5.2, fresh@~0.5.2: +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2, fresh@^0.5.2, fresh@~0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== @@ -12764,6 +12983,13 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" +html-encoding-sniffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== + dependencies: + whatwg-encoding "^3.1.1" + html-entities@^2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" @@ -12867,7 +13093,7 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -http-proxy-agent@^7.0.0: +http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== @@ -12900,6 +13126,14 @@ https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.2: agent-base "^7.0.2" debug "4" +https-proxy-agent@^7.0.5: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -12931,6 +13165,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.5: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.0.tgz#59cdde0a2a297cc2aeb0c6445a195ee89f127550" + integrity sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw== + dependencies: + safer-buffer ">= 2.1.2 < 3" + iconv-lite@0.6.3, iconv-lite@^0.6.2, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" @@ -13213,6 +13454,16 @@ ip@^2.0.0: resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipv6-normalize@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ipv6-normalize/-/ipv6-normalize-1.0.1.tgz#1b3258290d365fa83239e89907dde4592e7620a8" + integrity sha512-Bm6H79i01DjgGTCWjUuCjJ6QDo1HB96PT/xCYuyJUP9WFbVDrLSbG4EZCvOCun2rNswZb0c3e4Jt/ws795esHA== + is-arguments@^1.0.4, is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -14374,6 +14625,33 @@ jsdom@^21.1.1: ws "^8.13.0" xml-name-validator "^4.0.0" +jsdom@^24.1.1: + version "24.1.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.1.3.tgz#88e4a07cb9dd21067514a619e9f17b090a394a9f" + integrity sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ== + dependencies: + cssstyle "^4.0.1" + data-urls "^5.0.0" + decimal.js "^10.4.3" + form-data "^4.0.0" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.5" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.12" + parse5 "^7.1.2" + rrweb-cssom "^0.7.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.4" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + ws "^8.18.0" + xml-name-validator "^5.0.0" + jsesc@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" @@ -15347,6 +15625,11 @@ lru-cache@^10.2.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== +lru-cache@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -15444,6 +15727,27 @@ magic-string@^0.30.11, magic-string@^0.30.3, magic-string@^0.30.4: dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" +maildev@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/maildev/-/maildev-2.2.1.tgz#0a7ea8188bc6d9fea6bd08dbc63ecf4872acc67c" + integrity sha512-uNSJ4LpiNfCMw5KpWAM5x1UgOyG3ngeNwzBqw4/Wl18ECkJDyXBYpTH44HCG8LqAOFLkUiLl/1Ah5lrQhv3GzQ== + dependencies: + addressparser "1.0.1" + async "^3.2.3" + commander "^12.1.0" + compression "^1.7.4" + cors "^2.8.5" + dompurify "^3.1.6" + express "^4.21.2" + iconv-lite "0.5.0" + jsdom "^24.1.1" + mime "2.4.4" + nodemailer "^6.9.14" + smtp-server "^3.13.4" + socket.io "^4.7.5" + uue "3.1.2" + wildstring "1.0.9" + make-dir@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" @@ -15599,6 +15903,11 @@ meow@^8.1.2: type-fest "^0.18.0" yargs-parser "^20.2.3" +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + merge-descriptors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -15614,7 +15923,7 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -methods@^1.1.2: +methods@^1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== @@ -15647,6 +15956,16 @@ mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.24, mime-types@^2.1.29, dependencies: mime-db "1.52.0" +mime@1.6.0, mime@^1.3.4: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + mime@2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" @@ -15657,11 +15976,6 @@ mime@3, mime@^3.0.0: resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== -mime@^1.3.4: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -15943,12 +16257,17 @@ mri@^1.1.0: resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -16115,6 +16434,11 @@ negotiator@0.6.3, negotiator@^0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -16263,11 +16587,21 @@ node-source-walk@^5.0.0: dependencies: "@babel/parser" "^7.0.0" +nodemailer@6.9.15: + version "6.9.15" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.15.tgz#57b79dc522be27e0e47ac16cc860aa0673e62e04" + integrity sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ== + nodemailer@6.9.9: version "6.9.9" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.9.tgz#4549bfbf710cc6addec5064dd0f19874d24248d9" integrity sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA== +nodemailer@^6.9.14: + version "6.10.0" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.10.0.tgz#1f24c9de94ad79c6206f66d132776b6503003912" + integrity sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA== + nodemon@2.0.15: version "2.0.15" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.15.tgz#504516ce3b43d9dc9a955ccd9ec57550a31a8d4e" @@ -16498,6 +16832,11 @@ nwsapi@^2.2.0, nwsapi@^2.2.4: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.12.tgz#fb6af5c0ec35b27b4581eb3bbad34ec9e5c696f8" integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w== +nwsapi@^2.2.12: + version "2.2.16" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.16.tgz#177760bba02c351df1d2644e220c31dfec8cdb43" + integrity sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ== + nx-cloud@16.0.5: version "16.0.5" resolved "https://registry.yarnpkg.com/nx-cloud/-/nx-cloud-16.0.5.tgz#fa0b0185d254405ec47fcbcdbbd8b12ff1add096" @@ -16684,13 +17023,18 @@ on-exit-leak-free@^2.1.0: resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz#5c703c968f7e7f851885f6459bf8a8a57edc9cc4" integrity sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w== -on-finished@^2.3.0: +on-finished@2.4.1, on-finished@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -17141,7 +17485,7 @@ parse5@^7.1.2: dependencies: entities "^4.4.0" -parseurl@^1.3.2, parseurl@^1.3.3: +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" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -17255,7 +17599,7 @@ path-scurry@^1.11.1, path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@^0.1.10: +path-to-regexp@0.1.12, path-to-regexp@^0.1.10: version "0.1.12" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== @@ -18175,6 +18519,14 @@ protocols@^2.0.0, protocols@^2.0.1: resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -18208,6 +18560,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== + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -18223,6 +18580,11 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -18247,7 +18609,7 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@^6.10.3, qs@^6.11.0, qs@^6.4.0: +qs@6.13.0, qs@^6.10.3, qs@^6.11.0, qs@^6.4.0: version "6.13.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== @@ -18327,12 +18689,12 @@ randomstring@1.1.5: dependencies: array-uniq "1.0.2" -range-parser@^1.2.0: +range-parser@^1.2.0, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@^2.2.0: +raw-body@2.5.2, raw-body@^2.2.0: version "2.5.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== @@ -18948,6 +19310,16 @@ rrweb-cssom@^0.6.0: resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== +rrweb-cssom@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b" + integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg== + +rrweb-cssom@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" + integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -19133,6 +19505,25 @@ semver-diff@^3.1.1: dependencies: lru-cache "^6.0.0" +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" @@ -19159,6 +19550,16 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.19.0" + server-destroy@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" @@ -19343,6 +19744,16 @@ smob@^1.0.0: resolved "https://registry.yarnpkg.com/smob/-/smob-1.5.0.tgz#85d79a1403abf128d24d3ebc1cdc5e1a9548d3ab" integrity sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig== +smtp-server@^3.13.4: + version "3.13.6" + resolved "https://registry.yarnpkg.com/smtp-server/-/smtp-server-3.13.6.tgz#779aa22fd3746bb8b94d25ab78447b6e4b7d5b78" + integrity sha512-dqbSPKn3PCq3Gp5hxBM99u7PET7cQSAWrauhtArJbc+zrf5xNEOjm9+Ob3lySySrRoIEvNE0dz+w2H/xWFJNRw== + dependencies: + base32.js "0.1.0" + ipv6-normalize "1.0.1" + nodemailer "6.9.15" + punycode.js "2.3.1" + snowflake-sdk@^1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/snowflake-sdk/-/snowflake-sdk-1.15.0.tgz#cc32fa0f2869d9e5a026e293b50d387ddbd67aca" @@ -19405,7 +19816,7 @@ socket.io-parser@~4.2.4: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" -socket.io@4.8.1: +socket.io@4.8.1, socket.io@^4.7.5: version "4.8.1" resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.8.1.tgz#fa0eaff965cc97fdf4245e8d4794618459f7558a" integrity sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg== @@ -20614,7 +21025,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@4.1.3, "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2, tough-cookie@~2.5.0: +tough-cookie@4.1.3, "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@^4.0.0, tough-cookie@^4.1.2, tough-cookie@^4.1.4, tough-cookie@~2.5.0: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== @@ -20638,6 +21049,13 @@ tr46@^4.1.1: dependencies: punycode "^2.3.0" +tr46@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec" + integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g== + dependencies: + punycode "^2.3.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -20838,7 +21256,7 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@^1.6.14, type-is@^1.6.16, type-is@^1.6.18: +type-is@^1.6.14, type-is@^1.6.16, type-is@^1.6.18, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -21086,7 +21504,7 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -21207,11 +21625,19 @@ util@^0.12.4: is-typed-array "^1.1.3" which-typed-array "^1.1.2" -utils-merge@1.x.x, utils-merge@^1.0.1: +utils-merge@1.0.1, utils-merge@1.x.x, utils-merge@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uue@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/uue/-/uue-3.1.2.tgz#e99368414e87200012eb37de4dbaebaa1c742ad2" + integrity sha512-axKLXVqwtdI/czrjG0X8hyV1KLgeWx8F4KvSbvVCnS+RUvsQMGRjx0kfuZDXXqj0LYvVJmx3B9kWlKtEdRrJLg== + dependencies: + escape-string-regexp "~1.0.5" + extend "~3.0.0" + uuid-random@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/uuid-random/-/uuid-random-1.3.2.tgz#96715edbaef4e84b1dcf5024b00d16f30220e2d0" @@ -21293,7 +21719,7 @@ validator@^13.6.0, validator@^13.7.0: resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== -vary@^1, vary@^1.1.2: +vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== @@ -21411,6 +21837,13 @@ w3c-xmlserializer@^4.0.0: dependencies: xml-name-validator "^4.0.0" +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== + dependencies: + xml-name-validator "^5.0.0" + walkdir@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" @@ -21482,6 +21915,13 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" @@ -21492,6 +21932,11 @@ whatwg-mimetype@^3.0.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + whatwg-url@^12.0.0, whatwg-url@^12.0.1: version "12.0.1" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" @@ -21508,6 +21953,14 @@ whatwg-url@^13.0.0: tr46 "^4.1.1" webidl-conversions "^7.0.0" +whatwg-url@^14.0.0: + version "14.1.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.1.1.tgz#ce71e240c61541315833b5cdafd139a479e47058" + integrity sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ== + dependencies: + tr46 "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -21605,6 +22058,11 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" +wildstring@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/wildstring/-/wildstring-1.0.9.tgz#82a696d5653c7d4ec9ba716859b6b53aba2761c5" + integrity sha512-XBNxKIMLO6uVHf1Xvo++HGWAZZoiVCHmEMCmZJzJ82vQsuUJCLw13Gzq0mRCATk7a3+ZcgeOKSDioavuYqtlfA== + winston-transport@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.9.0.tgz#3bba345de10297654ea6f33519424560003b3bf9" @@ -21762,6 +22220,11 @@ ws@^8.13.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== +ws@^8.18.0: + version "8.18.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== + ws@~8.17.1: version "8.17.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" @@ -21797,6 +22260,11 @@ xml-name-validator@^4.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== + xml-parse-from-string@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28"