diff --git a/.env.appStore.example b/.env.appStore.example index dafa17f00e..47c99d5b09 100644 --- a/.env.appStore.example +++ b/.env.appStore.example @@ -14,6 +14,7 @@ # - ZAPIER # - LARK # - WEB3 +# - SALESFORCE # - APP STORE ********************************************************************************************** # ⚠️ ⚠️ ⚠️ THESE WILL BE MIGRATED TO THE DATABASE TO PREVENT AWS's 4KB ENV QUOTA ⚠️ ⚠️ ⚠️ @@ -103,4 +104,9 @@ LARK_OPEN_VERIFICATION_TOKEN="" # @see https://github.com/calcom/cal.com/blob/main/packages/app-store/rainbow/README.md ALCHEMY_API_KEY="" INFURA_API_KEY="" + +# - SALESFORCE +# Used for the Salesforce (Sales Cloud) app +SALESFORCE_CONSUMER_KEY="" +SALESFORCE_CONSUMER_SECRET="" # ********************************************************************************************************* diff --git a/packages/app-store/apps.metadata.generated.ts b/packages/app-store/apps.metadata.generated.ts index df6120d107..be1fcb4323 100644 --- a/packages/app-store/apps.metadata.generated.ts +++ b/packages/app-store/apps.metadata.generated.ts @@ -30,6 +30,7 @@ import { metadata as qr_code_meta } from "./qr_code/_metadata"; import { metadata as rainbow_meta } from "./rainbow/_metadata"; import { metadata as raycast_meta } from "./raycast/_metadata"; import { metadata as riverside_meta } from "./riverside/_metadata"; +import { metadata as salesforce_meta } from "./salesforce/_metadata"; import { metadata as sendgrid_meta } from "./sendgrid/_metadata"; import { metadata as signal_meta } from "./signal/_metadata"; import { metadata as sirius_video_meta } from "./sirius_video/_metadata"; @@ -73,6 +74,7 @@ export const appStoreMetadata = { rainbow: rainbow_meta, raycast: raycast_meta, riverside: riverside_meta, + salesforce: salesforce_meta, sendgrid: sendgrid_meta, signal: signal_meta, sirius_video: sirius_video_meta, diff --git a/packages/app-store/apps.server.generated.ts b/packages/app-store/apps.server.generated.ts index e4f65ae098..eacbf52fdd 100644 --- a/packages/app-store/apps.server.generated.ts +++ b/packages/app-store/apps.server.generated.ts @@ -29,6 +29,7 @@ export const apiHandlers = { rainbow: import("./rainbow/api"), raycast: import("./raycast/api"), riverside: import("./riverside/api"), + salesforce: import("./salesforce/api"), sendgrid: import("./sendgrid/api"), signal: import("./signal/api"), sirius_video: import("./sirius_video/api"), diff --git a/packages/app-store/index.ts b/packages/app-store/index.ts index 3fc35571bd..6c678cd4f9 100644 --- a/packages/app-store/index.ts +++ b/packages/app-store/index.ts @@ -15,6 +15,7 @@ import * as jitsivideo from "./jitsivideo"; import * as larkcalendar from "./larkcalendar"; import * as office365calendar from "./office365calendar"; import * as office365video from "./office365video"; +import * as salesforce from "./salesforce"; import * as sendgrid from "./sendgrid"; import * as stripepayment from "./stripepayment"; import * as tandemvideo from "./tandemvideo"; @@ -37,6 +38,7 @@ const appStore = { larkcalendar, office365calendar, office365video, + salesforce, sendgrid, stripepayment, tandemvideo, diff --git a/packages/app-store/salesforce/README.mdx b/packages/app-store/salesforce/README.mdx new file mode 100644 index 0000000000..5188df3107 --- /dev/null +++ b/packages/app-store/salesforce/README.mdx @@ -0,0 +1,12 @@ +--- +description: Salesforce (Sales Cloud) is a cloud-based application designed to help your salespeople sell smarter and faster by centralizing customer information, logging their interactions with your company, and automating many of the tasks salespeople do every day. +items: + - /api/app-store/salesforce/1.png +--- + +{description} +
+

Features:

+ diff --git a/packages/app-store/salesforce/_metadata.ts b/packages/app-store/salesforce/_metadata.ts new file mode 100644 index 0000000000..9c7f2aa320 --- /dev/null +++ b/packages/app-store/salesforce/_metadata.ts @@ -0,0 +1,10 @@ +import type { AppMeta } from "@calcom/types/App"; + +import config from "./config.json"; + +export const metadata = { + category: "other", + ...config, +} as AppMeta; + +export default metadata; diff --git a/packages/app-store/salesforce/api/add.ts b/packages/app-store/salesforce/api/add.ts new file mode 100644 index 0000000000..e56d882e21 --- /dev/null +++ b/packages/app-store/salesforce/api/add.ts @@ -0,0 +1,24 @@ +import jsforce from "jsforce"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { WEBAPP_URL } from "@calcom/lib/constants"; + +import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug"; + +let consumer_key = ""; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== "GET") return res.status(405).json({ message: "Method not allowed" }); + + const appKeys = await getAppKeysFromSlug("salesforce"); + if (typeof appKeys.consumer_key === "string") consumer_key = appKeys.consumer_key; + if (!consumer_key) return res.status(400).json({ message: "Salesforce client id missing." }); + + const salesforceClient = new jsforce.Connection({ + clientId: consumer_key, + redirectUri: `${WEBAPP_URL}/api/integrations/salesforce/callback`, + }); + + const url = salesforceClient.oauth2.getAuthorizationUrl({ scope: "refresh_token full" }); + res.status(200).json({ url }); +} diff --git a/packages/app-store/salesforce/api/callback.ts b/packages/app-store/salesforce/api/callback.ts new file mode 100644 index 0000000000..178447b9b4 --- /dev/null +++ b/packages/app-store/salesforce/api/callback.ts @@ -0,0 +1,55 @@ +import jsforce from "jsforce"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { WEBAPP_URL } from "@calcom/lib/constants"; +import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; +import prisma from "@calcom/prisma"; + +import { decodeOAuthState } from "../../_utils/decodeOAuthState"; +import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug"; +import getInstalledAppPath from "../../_utils/getInstalledAppPath"; + +let consumer_key = ""; +let consumer_secret = ""; +const instance_url = ""; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const { code } = req.query; + + if (code === undefined && typeof code !== "string") { + res.status(400).json({ message: "`code` must be a string" }); + return; + } + + if (!req.session?.user?.id) { + return res.status(401).json({ message: "You must be logged in to do this" }); + } + + const appKeys = await getAppKeysFromSlug("salesforce"); + if (typeof appKeys.consumer_key === "string") consumer_key = appKeys.consumer_key; + if (typeof appKeys.consumer_secret === "string") consumer_secret = appKeys.consumer_secret; + if (!consumer_key) return res.status(400).json({ message: "Salesforce consumer key missing." }); + if (!consumer_secret) return res.status(400).json({ message: "Salesforce consumer secret missing." }); + + const conn = new jsforce.Connection({ + clientId: consumer_key, + clientSecret: consumer_secret, + redirectUri: WEBAPP_URL + "/api/integrations/salesforce/callback", + }); + + const salesforceTokenInfo = await conn.oauth2.requestToken(code as string); + + await prisma.credential.create({ + data: { + type: "salesforce_other_calendar", + key: salesforceTokenInfo as any, + userId: req.session.user.id, + appId: "salesforce", + }, + }); + + const state = decodeOAuthState(req); + res.redirect( + getSafeRedirectUrl(state?.returnTo) ?? getInstalledAppPath({ variant: "other", slug: "salesforce" }) + ); +} diff --git a/packages/app-store/salesforce/api/index.ts b/packages/app-store/salesforce/api/index.ts new file mode 100644 index 0000000000..eb12c1b4ed --- /dev/null +++ b/packages/app-store/salesforce/api/index.ts @@ -0,0 +1,2 @@ +export { default as add } from "./add"; +export { default as callback } from "./callback"; diff --git a/packages/app-store/salesforce/components/.gitkeep b/packages/app-store/salesforce/components/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/app-store/salesforce/config.json b/packages/app-store/salesforce/config.json new file mode 100644 index 0000000000..52e0ad3308 --- /dev/null +++ b/packages/app-store/salesforce/config.json @@ -0,0 +1,16 @@ +{ + "/*": "Don't modify slug - If required, do it using cli edit command", + "name": "Salesforce", + "slug": "salesforce", + "type": "salesforce_other_calendar", + "imageSrc": "/api/app-store/salesforce/icon.png", + "logo": "/api/app-store/salesforce/icon.png", + "url": "https://cal.com/apps/salesforce", + "variant": "other_calendar", + "categories": ["other"], + "publisher": "Cal.com", + "email": "help@cal.com", + "description": "Salesforce (Sales Cloud) is a cloud-based application designed to help your salespeople sell smarter and faster by centralizing customer information, logging their interactions with your company, and automating many of the tasks salespeople do every day.", + "extendsFeature": "User", + "__createdUsingCli": true +} diff --git a/packages/app-store/salesforce/index.ts b/packages/app-store/salesforce/index.ts new file mode 100644 index 0000000000..5373eb04ef --- /dev/null +++ b/packages/app-store/salesforce/index.ts @@ -0,0 +1,3 @@ +export * as api from "./api"; +export * as lib from "./lib"; +export { metadata } from "./_metadata"; diff --git a/packages/app-store/salesforce/lib/CalendarService.ts b/packages/app-store/salesforce/lib/CalendarService.ts new file mode 100644 index 0000000000..75798c1dc8 --- /dev/null +++ b/packages/app-store/salesforce/lib/CalendarService.ts @@ -0,0 +1,275 @@ +import jsforce, { TokenResponse } from "jsforce"; +import { RRule } from "rrule"; + +import { getLocation } from "@calcom/lib/CalEventParser"; +import { WEBAPP_URL } from "@calcom/lib/constants"; +import { HttpError } from "@calcom/lib/http-error"; +import logger from "@calcom/lib/logger"; +import prisma from "@calcom/prisma"; +import type { + Calendar, + CalendarEvent, + IntegrationCalendar, + NewCalendarEventType, + Person, +} from "@calcom/types/Calendar"; +import { CredentialPayload } from "@calcom/types/Credential"; + +import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug"; + +type ExtendedTokenResponse = TokenResponse & { + instance_url: string; +}; + +type ContactSearchResult = { + attributes: { + type: string; + url: string; + }; + Id: string; + Email: string; +}; + +const sfApiErrors = { + INVALID_EVENTWHOIDS: "INVALID_FIELD: No such column 'EventWhoIds' on sobject of type Event", +}; + +export default class SalesforceCalendarService implements Calendar { + private integrationName = ""; + private conn: Promise; + private log: typeof logger; + private calWarnings: string[] = []; + + constructor(credential: CredentialPayload) { + this.integrationName = "salesforce_other_calendar"; + this.conn = this.getClient(credential).then((c) => c); + this.log = logger.getChildLogger({ prefix: [`[[lib] ${this.integrationName}`] }); + } + + private getClient = async (credential: CredentialPayload) => { + let consumer_key = ""; + let consumer_secret = ""; + + const appKeys = await getAppKeysFromSlug("salesforce"); + if (typeof appKeys.consumer_key === "string") consumer_key = appKeys.consumer_key; + if (typeof appKeys.consumer_secret === "string") consumer_secret = appKeys.consumer_secret; + if (!consumer_key) + throw new HttpError({ statusCode: 400, message: "Salesforce consumer key is missing." }); + if (!consumer_secret) + throw new HttpError({ statusCode: 400, message: "Salesforce consumer secret missing." }); + + const credentialKey = credential.key as unknown as ExtendedTokenResponse; + + return new jsforce.Connection({ + clientId: consumer_key, + clientSecret: consumer_secret, + redirectUri: WEBAPP_URL + "/api/integrations/salesforce/callback", + instanceUrl: credentialKey.instance_url, + accessToken: credentialKey.access_token, + refreshToken: credentialKey.refresh_token, + }); + }; + + private salesforceContactCreate = async (attendees: Person[]) => { + const conn = await this.conn; + const createdContacts = await Promise.all( + attendees.map(async (attendee) => { + const [FirstName, LastName] = attendee.name ? attendee.name.split(" ") : [attendee.email, ""]; + return await conn + .sobject("Contact") + .create({ + FirstName, + LastName, + Email: attendee.email, + }) + .then((result) => { + if (result.success) { + return { Id: result.id, Email: attendee.email }; + } + }); + }) + ); + return createdContacts.filter( + (contact): contact is Omit => contact !== undefined + ); + }; + + private salesforceContactSearch = async (event: CalendarEvent) => { + const conn = await this.conn; + const search: ContactSearchResult[] = await conn.sobject("Contact").find( + event.attendees.map((att) => ({ Email: att.email })), + ["Id", "Email"] + ); + return search; + }; + + private getSalesforceEventBody = (event: CalendarEvent): string => { + return `${event.organizer.language.translate("invitee_timezone")}: ${ + event.attendees[0].timeZone + } \r\n\r\n ${event.organizer.language.translate("share_additional_notes")}\r\n${ + event.additionalNotes || "-" + }`; + }; + + private salesforceCreateEventApiCall = async ( + event: CalendarEvent, + options: { [key: string]: unknown } + ) => { + const conn = await this.conn; + return await conn.sobject("Event").create({ + StartDateTime: new Date(event.startTime).toISOString(), + EndDateTime: new Date(event.endTime).toISOString(), + Subject: event.title, + Description: this.getSalesforceEventBody(event), + Location: getLocation(event), + ...options, + ...(event.recurringEvent && { + IsRecurrence2: true, + Recurrence2PatternText: new RRule(event.recurringEvent).toString(), + }), + }); + }; + + private salesforceCreateEvent = async ( + event: CalendarEvent, + contacts: Omit[] + ) => { + const createdEvent = await this.salesforceCreateEventApiCall(event, { + EventWhoIds: contacts.map((contact) => contact.Id), + }).catch(async (reason) => { + if (reason === sfApiErrors.INVALID_EVENTWHOIDS) { + this.calWarnings.push( + `Please enable option "Allow Users to Relate Multiple Contacts to Tasks and Events" under + "Setup > Feature Settings > Sales > Activity Settings" to be able to create events with + multiple contact attendees.` + ); + // User has not configured "Allow Users to Relate Multiple Contacts to Tasks and Events" + // proceeding to create the event using just the first attendee as the primary WhoId + return await this.salesforceCreateEventApiCall(event, { + WhoId: contacts[0], + }); + } else { + return Promise.reject(); + } + }); + return createdEvent; + }; + + private salesforceUpdateEvent = async (uid: string, event: CalendarEvent) => { + const conn = await this.conn; + return await conn.sobject("Event").update({ + Id: uid, + StartDateTime: new Date(event.startTime).toISOString(), + EndDateTime: new Date(event.endTime).toISOString(), + Subject: event.title, + Description: this.getSalesforceEventBody(event), + Location: getLocation(event), + ...(event.recurringEvent && { + IsRecurrence2: true, + Recurrence2PatternText: new RRule(event.recurringEvent).toString(), + }), + }); + }; + + private salesforceDeleteEvent = async (uid: string) => { + const conn = await this.conn; + return await conn.sobject("Event").delete(uid); + }; + + async handleEventCreation(event: CalendarEvent, contacts: Omit[]) { + const sfEvent = await this.salesforceCreateEvent(event, contacts); + if (sfEvent.success) { + this.log.debug("event:creation:ok", { sfEvent }); + return Promise.resolve({ + uid: sfEvent.id, + id: sfEvent.id, + type: "salesforce_other_calendar", + password: "", + url: "", + additionalInfo: { contacts, sfEvent, calWarnings: this.calWarnings }, + }); + } + this.log.debug("event:creation:notOk", { event, sfEvent, contacts }); + return Promise.reject({ + calError: "Something went wrong when creating an event in Salesforce", + }); + } + + async createEvent(event: CalendarEvent): Promise { + debugger; + const contacts = await this.salesforceContactSearch(event); + if (contacts.length) { + if (contacts.length == event.attendees.length) { + // All attendees do exist in Salesforce + this.log.debug("contact:search:all", { event, contacts }); + return await this.handleEventCreation(event, contacts); + } else { + // Some attendees don't exist in Salesforce + // Get the existing contacts' email to filter out + this.log.debug("contact:search:notAll", { event, contacts }); + const existingContacts = contacts.map((contact) => contact.Email); + this.log.debug("contact:filter:existing", { existingContacts }); + // Get non existing contacts filtering out existing from attendees + const nonExistingContacts = event.attendees.filter( + (attendee) => !existingContacts.includes(attendee.email) + ); + this.log.debug("contact:filter:nonExisting", { nonExistingContacts }); + // Only create contacts in Salesforce that were not present in the previous contact search + const createContacts = await this.salesforceContactCreate(nonExistingContacts); + this.log.debug("contact:created", { createContacts }); + // Continue with event creation and association only when all contacts are present in Salesforce + if (createContacts.length) { + this.log.debug("contact:creation:ok"); + return await this.handleEventCreation(event, createContacts.concat(contacts)); + } + return Promise.reject({ + calError: "Something went wrong when creating non-existing attendees in Salesforce", + }); + } + } else { + this.log.debug("contact:search:none", { event, contacts }); + const createContacts = await this.salesforceContactCreate(event.attendees); + this.log.debug("contact:created", { createContacts }); + if (createContacts.length) { + this.log.debug("contact:creation:ok"); + return await this.handleEventCreation(event, createContacts); + } + } + return Promise.reject({ + calError: "Something went wrong when searching/creating the attendees in Salesforce", + }); + } + + async updateEvent(uid: string, event: CalendarEvent): Promise { + const updatedEvent = await this.salesforceUpdateEvent(uid, event); + if (updatedEvent.success) { + return Promise.resolve({ + uid: updatedEvent.id, + id: updatedEvent.id, + type: "salesforce_other_calendar", + password: "", + url: "", + additionalInfo: { calWarnings: this.calWarnings }, + }); + } else { + return Promise.reject({ calError: "Something went wrong when updating the event in Salesforce" }); + } + } + + async deleteEvent(uid: string) { + const deletedEvent = await this.salesforceDeleteEvent(uid); + if (deletedEvent.success) { + Promise.resolve(); + } else { + Promise.reject({ calError: "Something went wrong when deleting the event in Salesforce" }); + } + } + + async getAvailability(dateFrom: string, dateTo: string, selectedCalendars: IntegrationCalendar[]) { + return Promise.resolve([]); + } + + async listCalendars(event?: CalendarEvent) { + return Promise.resolve([]); + } +} diff --git a/packages/app-store/salesforce/lib/index.ts b/packages/app-store/salesforce/lib/index.ts new file mode 100644 index 0000000000..e168c149df --- /dev/null +++ b/packages/app-store/salesforce/lib/index.ts @@ -0,0 +1 @@ +export { default as CalendarService } from "./CalendarService"; diff --git a/packages/app-store/salesforce/package.json b/packages/app-store/salesforce/package.json new file mode 100644 index 0000000000..0922d1570f --- /dev/null +++ b/packages/app-store/salesforce/package.json @@ -0,0 +1,16 @@ +{ + "private": true, + "name": "@calcom/salesforce", + "version": "0.0.0", + "main": "./index.ts", + "description": "Salesforce (Sales Cloud) is a cloud-based application designed to help your salespeople sell smarter and faster by centralizing customer information, logging their interactions with your company, and automating many of the tasks salespeople do every day.", + "dependencies": { + "@calcom/lib": "*", + "@calcom/prisma": "*", + "jsforce": "^1.11.0" + }, + "devDependencies": { + "@calcom/types": "*", + "@types/jsforce": "^1.11.0" + } +} diff --git a/packages/app-store/salesforce/static/1.png b/packages/app-store/salesforce/static/1.png new file mode 100644 index 0000000000..50ff69138f Binary files /dev/null and b/packages/app-store/salesforce/static/1.png differ diff --git a/packages/app-store/salesforce/static/icon.png b/packages/app-store/salesforce/static/icon.png new file mode 100644 index 0000000000..884bc21213 Binary files /dev/null and b/packages/app-store/salesforce/static/icon.png differ diff --git a/packages/prisma/seed-app-store.ts b/packages/prisma/seed-app-store.ts index 155976df0c..1f2c294a52 100644 --- a/packages/prisma/seed-app-store.ts +++ b/packages/prisma/seed-app-store.ts @@ -238,6 +238,12 @@ export default async function main() { client_secret: process.env.HUBSPOT_CLIENT_SECRET, }); } + if (process.env.SALESFORCE_CONSUMER_KEY && process.env.SALESFORCE_CONSUMER_SECRET) { + await createApp("salesforce", "salesforce", ["other"], "salesforce_other_calendar", { + consumer_key: process.env.SALESFORCE_CONSUMER_KEY, + consumer_secret: process.env.SALESFORCE_CONSUMER_SECRET, + }); + } await createApp("wipe-my-cal", "wipemycalother", ["other"], "wipemycal_other"); if (process.env.GIPHY_API_KEY) { await createApp("giphy", "giphy", ["other"], "giphy_other", { diff --git a/turbo.json b/turbo.json index 2a23beb910..c045dc9626 100644 --- a/turbo.json +++ b/turbo.json @@ -230,6 +230,8 @@ "$PLAYWRIGHT_TEST_BASE_URL", "$QUICK", "$RAILWAY_STATIC_URL", + "$SALESFORCE_CONSUMER_KEY", + "$SALESFORCE_CONSUMER_SECRET", "$SAML_ADMINS", "$SAML_DATABASE_URL", "$SEND_FEEDBACK_EMAIL", diff --git a/yarn.lock b/yarn.lock index 005cfc8b6c..4b4c9389cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7098,6 +7098,13 @@ "@types/parse5" "^6.0.3" "@types/tough-cookie" "*" +"@types/jsforce@^1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@types/jsforce/-/jsforce-1.11.0.tgz#ae702453bf3c691dec543b85ff938a539506254d" + integrity sha512-8ofRXHaHk2tgmxbEusRlo+NTItEly+SA3F1c+HAglvJ8LQK7Wzkvxjh3F4C0/sIpxUsowwAEgysPKWVtsT446g== + dependencies: + "@types/node" ">=4.0" + "@types/json-buffer@~3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" @@ -7222,7 +7229,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@16.9.1", "@types/node@>=8.1.0", "@types/node@^12.12.54", "@types/node@^12.12.6", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0": +"@types/node@*", "@types/node@16.9.1", "@types/node@>=4.0", "@types/node@>=8.1.0", "@types/node@^12.12.54", "@types/node@^12.12.6", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0": version "16.9.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708" integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g== @@ -8833,7 +8840,7 @@ arrify@^2.0.0, arrify@^2.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asap@^2.0.0: +asap@*, asap@^2.0.0, asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== @@ -9262,6 +9269,11 @@ base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1, base64-j resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base64-url@^2.2.0: + version "2.3.3" + resolved "https://registry.yarnpkg.com/base64-url/-/base64-url-2.3.3.tgz#645b71455c75109511f27d98450327e455f488ec" + integrity sha512-dLMhIsK7OplcDauDH/tZLvK7JmUZK3A7KiQpjNzsBrM6Etw7hzNI1tLEywqJk9NnwkgWuFKSlx/IUO7vF6Mo8Q== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -10454,6 +10466,13 @@ cluster-key-slot@1.1.0: resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== +co-prompt@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/co-prompt/-/co-prompt-1.0.0.tgz#fb370e9edac48576b27a732fe5d7f21d9fc6e6f6" + integrity sha512-uKmEbjDnL9SJTb+TNfIFsATe1F3IsNsR7KDGUG1hq7ColkMV0MSn7dg3eKVS+3wwtyvVqrgfIwi39NOJiknO7Q== + dependencies: + keypress "~0.2.1" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -10485,6 +10504,11 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== +coffeescript@^1.10.0: + version "1.12.7" + resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-1.12.7.tgz#e57ee4c4867cf7f606bfc4a0f2d550c0981ddd27" + integrity sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA== + collapse-white-space@^1.0.2: version "1.0.6" resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" @@ -11008,6 +11032,13 @@ crypto@^1.0.1: resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== +csprng@*: + version "0.1.2" + resolved "https://registry.yarnpkg.com/csprng/-/csprng-0.1.2.tgz#4bc68f12fa368d252a59841cbaca974b18ab45e2" + integrity sha512-D3WAbvvgUVIqSxUfdvLeGjuotsB32bvfVPd+AaaTWMtyUeC9zgCnw5xs94no89yFLVsafvY9dMZEhTwsY/ZecA== + dependencies: + sequin "*" + css-background-parser@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/css-background-parser/-/css-background-parser-0.1.0.tgz#48a17f7fe6d4d4f1bca3177ddf16c5617950741b" @@ -11125,6 +11156,18 @@ csstype@^3.0.2, csstype@^3.0.7: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== +csv-parse@^4.10.1: + version "4.16.3" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7" + integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== + +csv-stringify@^1.0.4: + version "1.1.2" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-1.1.2.tgz#77a41526581bce3380f12b00d7c5bbac70c82b58" + integrity sha512-3NmNhhd+AkYs5YtM1GEh01VR6PKj6qch2ayfQaltx5xpcAdThjnbbI5eT8CzRVpXfGKAxnmrSYLsNl/4f3eWiw== + dependencies: + lodash.get "~4.4.2" + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -13351,6 +13394,25 @@ fault@^1.0.0: dependencies: format "^0.2.0" +faye-websocket@>=0.9.1: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +faye@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/faye/-/faye-1.4.0.tgz#01d3d26ed5642c1cb203eed358afb1c1444b8669" + integrity sha512-kRrIg4be8VNYhycS2PY//hpBJSzZPr/DBbcy9VWelhZMW3KhyLkQR0HL0k0MNpmVoNFF4EdfMFkNAWjTP65g6w== + dependencies: + asap "*" + csprng "*" + faye-websocket ">=0.9.1" + safe-buffer "*" + tough-cookie "*" + tunnel-agent "*" + fb-watchman@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" @@ -14920,6 +14982,11 @@ http-https@^1.0.0: resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" @@ -16599,6 +16666,27 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +jsforce@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/jsforce/-/jsforce-1.11.0.tgz#7e853b82c534f1fe8e18f432a344bae81881a133" + integrity sha512-vYNXJXXdz9ZQNdfRqq/MCJ/zU7JGA7iEduwafQDzChR9FeqXgTNfHTppLVbw9mIniKkQZemmxSOtl7N04lj/5Q== + dependencies: + base64-url "^2.2.0" + co-prompt "^1.0.0" + coffeescript "^1.10.0" + commander "^2.9.0" + csv-parse "^4.10.1" + csv-stringify "^1.0.4" + faye "^1.4.0" + inherits "^2.0.1" + lodash "^4.17.19" + multistream "^2.0.5" + opn "^5.3.0" + promise "^7.1.1" + readable-stream "^2.1.0" + request "^2.72.0" + xml2js "^0.4.16" + json-bigint@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" @@ -16852,6 +16940,11 @@ keen-slider@^6.8.0: resolved "https://registry.yarnpkg.com/keen-slider/-/keen-slider-6.8.3.tgz#c42b27fb1afca0a2eb722676410cdd3af80f2e0f" integrity sha512-tJFokyUiHAtbGGQHnNMm3MLCpOm+z1JZsaOOMVnzTYAfAqLDT/zI/cE1mf82Bxmp1uHSIeUUkH+EZGzRQrSO/w== +keypress@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/keypress/-/keypress-0.2.1.tgz#1e80454250018dbad4c3fe94497d6e67b6269c77" + integrity sha512-HjorDJFNhnM4SicvaUXac0X77NiskggxJdesG72+O5zBKpSqKFCrqmndKVqpu3pFqkla0St6uGk8Ju0sCurrmg== + keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" @@ -17177,7 +17270,7 @@ lodash.debounce@^4, lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= -lodash.get@^4.4.2: +lodash.get@^4.4.2, lodash.get@~4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= @@ -18674,6 +18767,14 @@ multihashes@^0.4.15, multihashes@~0.4.15: multibase "^0.7.0" varint "^5.0.0" +multistream@^2.0.5: + version "2.1.1" + resolved "https://registry.yarnpkg.com/multistream/-/multistream-2.1.1.tgz#629d3a29bd76623489980d04519a2c365948148c" + integrity sha512-xasv76hl6nr1dEy3lPvy7Ej7K/Lx3O/FCvwge8PeVJpciPPoNCbaANcNiBug3IpdvTveZUcAV0DJzdnUDMesNQ== + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.5" + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -19455,6 +19556,13 @@ openid-client@^5.1.0: object-hash "^2.0.1" oidc-token-hash "^5.0.1" +opn@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -20576,6 +20684,13 @@ promise.prototype.finally@^3.1.0: define-properties "^1.1.3" es-abstract "^1.19.1" +promise@^7.1.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, prompts@^2.2.1, prompts@^2.4.0: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -21479,7 +21594,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.0, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -21879,7 +21994,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.66.0, request@^2.79.0, request@^2.88.0: +request@^2.66.0, request@^2.72.0, request@^2.79.0, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -22181,6 +22296,11 @@ sade@^1.7.3: dependencies: mri "^1.1.0" +safe-buffer@*, safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-buffer@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -22191,11 +22311,6 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-event-emitter@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" @@ -22448,6 +22563,11 @@ seq-queue@^0.0.5: resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" integrity sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4= +sequin@*: + version "0.1.1" + resolved "https://registry.yarnpkg.com/sequin/-/sequin-0.1.1.tgz#5c2d389d66a383734eaafbc45edeb2c1cb1be701" + integrity sha512-hJWMZRwP75ocoBM+1/YaCsvS0j5MTPeBHJkS2/wruehl9xwtX30HlDF1Gt6UZ8HHHY8SJa2/IL+jo+JJCd59rA== + serialize-error@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" @@ -24061,6 +24181,16 @@ totalist@^1.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +tough-cookie@*: + version "4.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" + integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -24279,7 +24409,7 @@ tty-browserify@0.0.0: resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw== -tunnel-agent@^0.6.0: +tunnel-agent@*, tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= @@ -24843,6 +24973,11 @@ universalify@^0.1.0, universalify@^0.1.2: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" @@ -24949,7 +25084,7 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@^1.4.3, url-parse@^1.5.8, url-parse@^1.5.9: +url-parse@^1.4.3, url-parse@^1.5.3, url-parse@^1.5.8, url-parse@^1.5.9: version "1.5.10" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== @@ -25761,6 +25896,20 @@ webpack@^5.9.0: watchpack "^2.4.0" webpack-sources "^3.2.3" +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + websocket@^1.0.32: version "1.0.34" resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" @@ -26066,7 +26215,7 @@ xml-parse-from-string@^1.0.0: resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" integrity sha1-qQKekp09vN7RafPG4oI42VpdWig= -xml2js@0.4.23, xml2js@^0.4.23, xml2js@^0.4.5: +xml2js@0.4.23, xml2js@^0.4.16, xml2js@^0.4.23, xml2js@^0.4.5: version "0.4.23" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==