diff --git a/components/signaturit/.gitignore b/components/signaturit/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/signaturit/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/signaturit/actions/create-certified-email/create-certified-email.mjs b/components/signaturit/actions/create-certified-email/create-certified-email.mjs new file mode 100644 index 0000000000000..8bedaa6cdf933 --- /dev/null +++ b/components/signaturit/actions/create-certified-email/create-certified-email.mjs @@ -0,0 +1,116 @@ +import FormData from "form-data"; +import fs from "fs"; +import { TYPE_OPTIONS } from "../../common/constants.mjs"; +import { + checkTmp, + parseObject, +} from "../../common/utils.mjs"; +import signaturit from "../../signaturit.app.mjs"; + +export default { + key: "signaturit-create-certified-email", + name: "Create Certified Email", + description: "Initiates the creation of a certified email. [See the documentation](https://docs.signaturit.com/api/latest#emails_create_email)", + version: "0.0.1", + type: "action", + props: { + signaturit, + body: { + propDefinition: [ + signaturit, + "body", + ], + optional: true, + }, + brandingId: { + propDefinition: [ + signaturit, + "brandingId", + ], + optional: true, + }, + eventsUrl: { + propDefinition: [ + signaturit, + "eventsUrl", + ], + optional: true, + }, + data: { + propDefinition: [ + signaturit, + "data", + ], + optional: true, + }, + attachments: { + propDefinition: [ + signaturit, + "attachments", + ], + optional: true, + }, + recipients: { + propDefinition: [ + signaturit, + "recipients", + ], + }, + type: { + type: "string", + label: "Type", + description: "Type of certified email.", + options: TYPE_OPTIONS, + optional: true, + }, + subject: { + propDefinition: [ + signaturit, + "subject", + ], + optional: true, + }, + }, + async run({ $ }) { + const formData = new FormData(); + + let i = 0; + for (const recipient of parseObject(this.recipients)) { + for (const [ + key, + value, + ] of Object.entries(recipient)) { + formData.append(`recipients[${i}][${key}]`, value); + } + i++; + } + if (this.data) { + for (const [ + key, + value, + ] of Object.entries(this.data)) { + formData.append(`data[${key}]`, value); + } + i++; + } + if (this.body) formData.append("body", this.body); + if (this.brandingId) formData.append("branding_id", this.brandingId); + if (this.eventsUrl) formData.append("events_url", this.eventsUrl); + if (this.type) formData.append("type", this.type); + if (this.subject) formData.append("subject", this.subject); + + if (this.attachments) { + let j = 0; + for (const file of parseObject(this.attachments)) { + formData.append(`attachments[${j++}]`, fs.createReadStream(checkTmp(file))); + } + } + const response = await this.signaturit.createCertifiedEmail({ + $, + data: formData, + headers: formData.getHeaders(), + }); + $.export("$summary", `Created certified email with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs b/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs new file mode 100644 index 0000000000000..11198fe5734ea --- /dev/null +++ b/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs @@ -0,0 +1,227 @@ +import FormData from "form-data"; +import fs from "fs"; +import { + DELIVERY_TYPE_OPTIONS, + SIGNATURE_TYPE_OPTIONS, + SIGNING_MODE_OPTIONS, +} from "../../common/constants.mjs"; +import { + checkTmp, + parseObject, +} from "../../common/utils.mjs"; +import signaturit from "../../signaturit.app.mjs"; + +export default { + key: "signaturit-create-signature-request-from-template", + name: "Create Signature Request from Template", + description: "Creates a signature request using a pre-existing template. [See the documentation](https://docs.signaturit.com/api/latest#signatures_create_signature)", + version: "0.0.1", + type: "action", + props: { + signaturit, + body: { + propDefinition: [ + signaturit, + "body", + ], + description: "Email body (html code is allowed) for **email** and **sms** type requests. Note: For **sms** request types it will be truncated to 120 characters Note: For **sms** the body should contain the tag **{{url}}** where we will include the document url.", + optional: true, + }, + brandingId: { + propDefinition: [ + signaturit, + "brandingId", + ], + optional: true, + }, + callbackUrl: { + type: "string", + label: "Callback URL", + description: "Url to redirect the user when finish the signature process.", + optional: true, + }, + data: { + propDefinition: [ + signaturit, + "data", + ], + optional: true, + }, + deliveryType: { + type: "string", + label: "Delivery Type", + description: "Delivery type for signature request.", + options: DELIVERY_TYPE_OPTIONS, + optional: true, + }, + expireTime: { + type: "integer", + label: "Expiration Time (days)", + description: "Expiration time of the document in days (max 365).", + min: 1, + max: 365, + optional: true, + }, + eventsUrl: { + propDefinition: [ + signaturit, + "eventsUrl", + ], + optional: true, + }, + files: { + propDefinition: [ + signaturit, + "attachments", + ], + label: "Files", + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "Name assigned to the signature request.", + }, + recipients: { + propDefinition: [ + signaturit, + "recipients", + ], + description: "List of recipients in JSON format, e.g., '{\"name\": \"John Doe\", \"email\": \"john@example.com\", \"phone\": \"34555667788\"}'. [See the documentation](https://docs.signaturit.com/api/latest#signatures_create_signature) for further information.", + }, + cc: { + type: "string[]", + label: "CC", + description: "List with email recipients containing name and email for people that will receive a copy of the signed document when the process is completed. e.g., '{\"name\": \"John Doe\", \"email\": \"john@example.com\"}'.", + optional: true, + }, + replyTo: { + type: "string", + label: "Reply To", + description: "Email Reply-To address.", + optional: true, + }, + reminders: { + type: "string[]", + label: "Reminders (days)", + description: "Reminders in days to send automatic reminders. You can set it 0 to disable reminders. E.g. [1,3,4]", + optional: true, + }, + signingMode: { + type: "string", + label: "Signing Mode", + description: "The signing mode lets you control the order in which your recipients receive and sign your documents.", + options: SIGNING_MODE_OPTIONS, + optional: true, + }, + subject: { + propDefinition: [ + signaturit, + "subject", + ], + optional: true, + }, + templates: { + propDefinition: [ + signaturit, + "templates", + ], + }, + type: { + type: "string", + label: "Type", + description: "The type of the signature.", + options: SIGNATURE_TYPE_OPTIONS, + optional: true, + }, + }, + methods: { + transformArray({ + arr, prefixBase, formData, + }) { + let result = []; + function processObject(obj, prefix = "") { + for (let key in obj) { + if (Array.isArray(obj[key])) { + obj[key].forEach((item, index) => { + processObject(item, `${prefix}[${key}][${index}]`); + }); + } else if (typeof obj[key] === "object") { + processObject(obj[key], `${prefix}[${key}]`); + } else { + result.push({ + key: `${prefixBase}${prefix}[${key}]`, + value: `${obj[key]}`, + }); + } + } + } + arr.map((item, index) => { + processObject(item, `[${index}]`); + }); + for (const { + key, value, + } of result) { + formData.append(key, value); + } + return result; + }, + }, + async run({ $ }) { + const formData = new FormData(); + this.transformArray({ + arr: parseObject(this.recipients), + prefixBase: "recipients", + formData, + }); + if (this.cc) { + let j = 0; + for (const item of parseObject(this.cc)) { + formData.append(`cc[${j++}]`, item); + } + } + if (this.data) { + for (const [ + key, + value, + ] of Object.entries(parseObject(this.data))) { + formData.append(`data[${key}]`, value); + } + } + if (this.templates) { + let k = 0; + for (const templateId of parseObject(this.templates)) { + formData.append(`templates[${k++}]`, templateId); + } + } + if (this.reminders) { + let m = 0; + for (const reminder of parseObject(this.reminders)) { + formData.append(`reminders[${m++}]`, reminder); + } + } + if (this.body) formData.append("body", this.body); + if (this.brandingId) formData.append("branding_id", this.brandingId); + if (this.callbackUrl) formData.append("callback_url", this.callbackUrl); + if (this.deliveryType) formData.append("delivery_type", this.deliveryType); + if (this.expireTime) formData.append("expire_time", this.expireTime); + if (this.eventsUrl) formData.append("events_url", this.eventsUrl); + if (this.name) formData.append("name", this.name); + if (this.replyTo) formData.append("reply_to", this.replyTo); + if (this.type) formData.append("type", this.type); + + if (this.files) { + let k = 0; + for (const file of parseObject(this.files)) { + formData.append(`files[${k++}]`, fs.createReadStream(checkTmp(file))); + } + } + const response = await this.signaturit.createSignatureRequest({ + $, + data: formData, + headers: formData.getHeaders(), + }); + $.export("$summary", `Created signature request with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs b/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs new file mode 100644 index 0000000000000..7ce371d9838f7 --- /dev/null +++ b/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs @@ -0,0 +1,26 @@ +import signaturit from "../../signaturit.app.mjs"; + +export default { + key: "signaturit-send-signature-request-reminder", + name: "Send Signature Request Reminder", + description: "Sends a reminder for a pending signature request. [See the documentation](https://docs.signaturit.com/api/latest#signatures_send_reminder)", + version: "0.0.1", + type: "action", + props: { + signaturit, + signatureRequestId: { + propDefinition: [ + signaturit, + "signatureRequestId", + ], + }, + }, + async run({ $ }) { + const response = await this.signaturit.sendReminder({ + $, + signatureRequestId: this.signatureRequestId, + }); + $.export("$summary", `Sent reminder for signature request ${this.signatureRequestId}`); + return response; + }, +}; diff --git a/components/signaturit/app/signaturit.app.ts b/components/signaturit/app/signaturit.app.ts deleted file mode 100644 index e2cef98c1f866..0000000000000 --- a/components/signaturit/app/signaturit.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "signaturit", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); \ No newline at end of file diff --git a/components/signaturit/common/constants.mjs b/components/signaturit/common/constants.mjs new file mode 100644 index 0000000000000..4f6a575022b0b --- /dev/null +++ b/components/signaturit/common/constants.mjs @@ -0,0 +1,57 @@ +export const LIMIT = 100; + +export const TYPE_OPTIONS = [ + { + label: "Delivery - Send the email as it is certifying the delivery process.", + value: "delivery", + }, + { + label: "Open Document - Send a modified version of the email with a button that redirects the user to our platform to open the **PDF** attachments. With this method, you can track when the user opens the attached files. Note: This method only supports **PDF** documents to be attached.", + value: "open_document", + }, + { + label: "Open Every Document - This type works like the **Open Document** type but allows to track the opening of every **PDF** file in emails with multiple attachments.", + value: "open_every_document", + }, +]; + +export const DELIVERY_TYPE_OPTIONS = [ + { + label: "Email - The signature request is sent by email. This is the default behavior when no type is specified.", + value: "email", + }, + { + label: "SMS - The signature request is sent by SMS. You must include the **phone** in the **recipients** parameter.", + value: "sms", + }, + { + label: "URL - The signature request is not sent to the signer. Instead of this, the creation request will return a **url** parameter that you can open in the browser to complete the signature.", + value: "url", + }, +]; + +export const SIGNING_MODE_OPTIONS = [ + { + label: "Sequential - Each recipient receives the request once the previous recipient has completed their action.", + value: "sequential", + }, + { + label: "Parallel - All recipients receive the request in parallel.", + value: "parallel", + }, +]; + +export const SIGNATURE_TYPE_OPTIONS = [ + { + label: "Simple - A simple signature request is created.", + value: "simple", + }, + { + label: "Advanced - An advanced signature request is created. We capture the biometric information of the signer with the signature draw.", + value: "advanced", + }, + { + label: "Smart - The system creates different type of signature depending in the user device. A simple signature is created for desktop pcs and advanced signature is created for mobile and tablet devices.", + value: "smart", + }, +]; diff --git a/components/signaturit/common/utils.mjs b/components/signaturit/common/utils.mjs new file mode 100644 index 0000000000000..b75bc9340048a --- /dev/null +++ b/components/signaturit/common/utils.mjs @@ -0,0 +1,39 @@ +export const parseObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return obj.map((item) => { + if (typeof item === "string") { + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +}; + +export const checkTmp = (filename) => { + if (!filename.startsWith("/tmp")) { + return `/tmp/${filename}`; + } + return filename; +}; + +export const getDateFormatted = (dateStr) => { + const date = new Date(dateStr); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; +}; diff --git a/components/signaturit/package.json b/components/signaturit/package.json index 90b869197bb74..81bceb2a914a3 100644 --- a/components/signaturit/package.json +++ b/components/signaturit/package.json @@ -1,16 +1,20 @@ { "name": "@pipedream/signaturit", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Signaturit Components", - "main": "dist/app/signaturit.app.mjs", + "main": "signaturit.app.mjs", "keywords": [ "pipedream", "signaturit" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/signaturit", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3", + "form-data": "^4.0.1", + "fs": "^0.0.1-security" } } diff --git a/components/signaturit/signaturit.app.mjs b/components/signaturit/signaturit.app.mjs new file mode 100644 index 0000000000000..7078264908002 --- /dev/null +++ b/components/signaturit/signaturit.app.mjs @@ -0,0 +1,172 @@ +import { axios } from "@pipedream/platform"; +import { LIMIT } from "./common/constants.mjs"; + +export default { + type: "app", + app: "signaturit", + propDefinitions: { + body: { + type: "string", + label: "Body", + description: "Email body (html code is allowed).", + }, + brandingId: { + type: "string", + label: "Branding ID", + description: "ID of the branding to use.", + async options() { + const data = await this.listBrandings(); + + return data.map(({ id }) => id); + }, + }, + eventsUrl: { + type: "string", + label: "Events URL", + description: "URL to receive real-time events.", + }, + data: { + type: "object", + label: "Custom Data", + description: "Custom key-value data in JSON format.", + }, + attachments: { + type: "string[]", + label: "Attachments", + description: "A list of paths to the files saved to the `/tmp` directory (e.g. `/tmp/example.pdf`). [See the documentation](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory).", + }, + recipients: { + type: "string[]", + label: "Recipients", + description: "List of recipients in JSON format, e.g., '{\"name\": \"John Doe\", \"email\": \"john@example.com\"}'. [See the documentation](https://docs.signaturit.com/api/latest#emails_create_email) for further information.", + }, + subject: { + type: "string", + label: "Subject", + description: "Email subject for **email** type requests.", + }, + templates: { + type: "string[]", + label: "Templates", + description: "Templates to use in the signature request.", + async options({ page }) { + const data = await this.listTemplates({ + params: { + limit: LIMIT, + offset: LIMIT * page, + }, + }); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + signatureRequestId: { + type: "string", + label: "Signature Request ID", + description: "ID of the signature request to send a reminder for.", + async options({ page }) { + const data = await this.listSignatureRequests({ + params: { + limit: LIMIT, + offset: LIMIT * page, + }, + }); + + return data.map(({ id }) => id); + }, + }, + }, + methods: { + _baseUrl() { + return `https://${this.$auth.domain}.signaturit.com/v3`; + }, + _headers(headers = {}) { + return { + ...headers, + "Authorization": `Bearer ${this.$auth.access_token}`, + }; + }, + _makeRequest({ + $ = this, path, headers, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + headers: this._headers(headers), + ...opts, + }); + }, + listBrandings() { + return this._makeRequest({ + path: "/brandings.json", + }); + }, + listTemplates() { + return this._makeRequest({ + path: "/templates.json", + }); + }, + createCertifiedEmail(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/emails.json", + ...opts, + }); + }, + createSignatureRequest(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/signatures.json", + ...opts, + }); + }, + sendReminder({ + signatureRequestId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/signatures/${signatureRequestId}/reminder.json`, + ...opts, + }); + }, + listSignatureRequests(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/signatures.json", + ...opts, + }); + }, + async *paginate({ + fn, params = {}, maxResults = null, ...opts + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + params.limit = LIMIT; + params.offset = LIMIT * page; + page++; + + const data = await fn({ + params, + ...opts, + }); + for (const d of data) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = data.length; + + } while (hasMore); + }, + }, +}; diff --git a/components/signaturit/sources/common/base.mjs b/components/signaturit/sources/common/base.mjs new file mode 100644 index 0000000000000..97ced71101be7 --- /dev/null +++ b/components/signaturit/sources/common/base.mjs @@ -0,0 +1,62 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import { getDateFormatted } from "../../common/utils.mjs"; +import app from "../../signaturit.app.mjs"; + +export default { + props: { + app, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastDate() { + return this.db.get("lastDate") || 0; + }, + _setLastDate(lastDate) { + this.db.set("lastDate", lastDate); + }, + async emitEvent(maxResults = false) { + const lastDate = this._getLastDate(); + + const response = this.app.paginate({ + fn: this.app.listSignatureRequests, + params: { + status: "completed", + since: getDateFormatted(lastDate), + }, + maxResults, + }); + + let responseArray = []; + for await (const item of response) { + if (Date.parse(item.created_at) <= lastDate) break; + responseArray.push(item); + } + + if (responseArray.length) { + this._setLastDate(Date.parse(responseArray[0].created_at)); + } + + for (const item of responseArray.reverse()) { + this.$emit(item, { + id: item.id, + summary: `New signed document: ${item.id}`, + ts: Date.parse(item.created_at), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/signaturit/sources/new-signed-document/new-signed-document.mjs b/components/signaturit/sources/new-signed-document/new-signed-document.mjs new file mode 100644 index 0000000000000..3b0e998e6c2c3 --- /dev/null +++ b/components/signaturit/sources/new-signed-document/new-signed-document.mjs @@ -0,0 +1,13 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "signaturit-new-signed-document", + name: "New Signed Document", + description: "Emit new event when a document has been newly signed.", + version: "0.0.1", + type: "source", + dedupe: "unique", + sampleEmit, +}; diff --git a/components/signaturit/sources/new-signed-document/test-event.mjs b/components/signaturit/sources/new-signed-document/test-event.mjs new file mode 100644 index 0000000000000..cf77f1539cd29 --- /dev/null +++ b/components/signaturit/sources/new-signed-document/test-event.mjs @@ -0,0 +1,24 @@ +export default { + created_at: "2014-08-21T08:53:35+0000", + data: [], + documents: [ + { + email: "john@signaturit.com", + events: [ + { + type: "email_processed", + created_at: "2014-08-21T08:55:14+0000" + } + ], + file: { + name: "document.pdf", + pages: 11, + size: 72218, + }, + id: "9781f42d-2910-11e4-b3d4-0aa7697eb409", + name: "John", + status: "ready", + } + ], + id: "974e6f6c-2910-11e4-b3d4-0aa7697eb409" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2e2a335e3d77..07bfc072b6085 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9383,7 +9383,17 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/signaturit: {} + components/signaturit: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 + form-data: + specifier: ^4.0.1 + version: 4.0.1 + fs: + specifier: ^0.0.1-security + version: 0.0.1-security components/signerx: dependencies: @@ -24489,22 +24499,22 @@ packages: superagent@3.8.1: resolution: {integrity: sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==} engines: {node: '>= 4.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@4.1.0: resolution: {integrity: sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag==} engines: {node: '>= 6.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@5.3.1: resolution: {integrity: sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==} engines: {node: '>= 7.0.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@7.1.6: resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please downgrade to v7.1.5 if you need IE/ActiveXObject support OR upgrade to v8.0.0 as we no longer support IE and published an incorrect patch version (see https://github.com/visionmedia/superagent/issues/1731) + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}