From 603cb5d6958ee75215792a9bf395e4ded502d46b Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Tue, 14 Mar 2023 14:41:29 +0100 Subject: [PATCH 01/13] Added channel caputre and bot webhook functions --- functions/captureChannelWithBot.protected.ts | 97 ++++++++++++ .../webhooks/chatbotCallback.protected.ts | 145 ++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 functions/captureChannelWithBot.protected.ts create mode 100644 functions/webhooks/chatbotCallback.protected.ts diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts new file mode 100644 index 00000000..5fcc7b83 --- /dev/null +++ b/functions/captureChannelWithBot.protected.ts @@ -0,0 +1,97 @@ +/** + * Copyright (C) 2021-2023 Technology Matters + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +/* eslint-disable global-require */ +/* eslint-disable import/no-dynamic-require */ +import '@twilio-labs/serverless-runtime-types'; +import { Context, ServerlessCallback } from '@twilio-labs/serverless-runtime-types/types'; +import { + responseWithCors, + bindResolve, + error400, + error500, + success, +} from '@tech-matters/serverless-helpers'; + +type EnvVars = { + CHAT_SERVICE_SID: string; +}; + +type Body = { + channelSid: string; +}; + +export const handler = async ( + context: Context, + event: Body, + callback: ServerlessCallback, +) => { + const response = responseWithCors(); + const resolve = bindResolve(callback)(response); + + try { + const { channelSid } = event; + + if (channelSid === undefined) { + resolve(error400('channelSid')); + return; + } + + const channel = await context + .getTwilioClient() + .chat.v2.services(context.CHAT_SERVICE_SID) + .channels(channelSid) + .fetch(); + + const channelAttributes = JSON.parse(channel.attributes); + + /** + * Remove the 'studio' type webhook so further messages does not start a new Studio execution + * NOTE: is extremely important to "cleanup" (with Janitor) the channels where this is done, or they'll stay in a stuck state. + */ + // This is also used in functions/sendMessageAndRunJanitor.protected.ts, maybe factor out + const channelWebhooks = await context + .getTwilioClient() + .chat.services(context.CHAT_SERVICE_SID) + .channels(channelSid) + .webhooks.list(); + + // Remove the studio trigger webhooks to prevent this channel to trigger subsequent Studio flows executions + await Promise.all( + channelWebhooks.map(async (w) => { + if (w.type === 'studio') { + await w.remove(); + } + }), + ); + + await channel.update({ + attributes: JSON.stringify({ + ...channelAttributes, + channelCapturedByBot: { + botId: 'C6HUSTIFBR', // This should be passed as parameter + botAliasId: 'TSTALIASID', // This should be passed as parameter + localeId: 'en_US', // This should be passed as parameter + }, + }), + }); + + resolve(success('Channel caputer by bot =)')); + } catch (err) { + if (err instanceof Error) resolve(error500(err)); + else resolve(error500(new Error(String(err)))); + } +}; diff --git a/functions/webhooks/chatbotCallback.protected.ts b/functions/webhooks/chatbotCallback.protected.ts new file mode 100644 index 00000000..40a6df14 --- /dev/null +++ b/functions/webhooks/chatbotCallback.protected.ts @@ -0,0 +1,145 @@ +/** + * Copyright (C) 2021-2023 Technology Matters + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +/* eslint-disable global-require */ +/* eslint-disable import/no-dynamic-require */ +import '@twilio-labs/serverless-runtime-types'; +import { Context, ServerlessCallback } from '@twilio-labs/serverless-runtime-types/types'; +import { + responseWithCors, + bindResolve, + error400, + error500, + success, +} from '@tech-matters/serverless-helpers'; +import AWS from 'aws-sdk'; +import type { MessageInstance } from 'twilio/lib/rest/chat/v2/service/channel/message'; +import { omit } from 'lodash'; +import type { WebhookEvent } from '../helpers/customChannels/flexToCustomChannel.private'; + +type EnvVars = { + CHAT_SERVICE_SID: string; + ASELO_APP_ACCESS_KEY: string; + ASELO_APP_SECRET_KEY: string; + AWS_REGION: string; +}; + +export type Body = Partial & { + // recipientId?: string; +}; + +export const handler = async ( + context: Context, + event: Body, + callback: ServerlessCallback, +) => { + const response = responseWithCors(); + const resolve = bindResolve(callback)(response); + + try { + const { Body, From, ChannelSid, EventType } = event; + if (Body === undefined) { + resolve(error400('Body')); + return; + } + if (From === undefined) { + resolve(error400('From')); + return; + } + if (ChannelSid === undefined) { + resolve(error400('ChannelSid')); + return; + } + if (EventType === undefined) { + resolve(error400('EventType')); + return; + } + + const client = context.getTwilioClient(); + const channel = await client.chat + .services(context.CHAT_SERVICE_SID) + .channels(ChannelSid) + .fetch(); + + const channelAttributes = JSON.parse(channel.attributes); + + // Send message to bot only if it's from child + if (EventType === 'onMessageSent' && channelAttributes.from === From) { + AWS.config.update({ + credentials: { + accessKeyId: context.ASELO_APP_ACCESS_KEY, + secretAccessKey: context.ASELO_APP_SECRET_KEY, + }, + region: context.AWS_REGION, + }); + + const Lex = new AWS.LexRuntimeV2(); + + const lexResponse = await Lex.recognizeText({ + botId: channelAttributes.channelCapturedByBot.botId, + botAliasId: channelAttributes.channelCapturedByBot.botAliasId, + localeId: channelAttributes.channelCapturedByBot.localeId, + text: Body, + sessionId: From, // We could use some channel/bot info to better scope this + }).promise(); + + // Secuentially wait for the messages to be sent in the correct order + // TODO: probably we want to handle the case where messages is null + /* const messagesSent = */ await lexResponse.messages?.reduce>( + async (accumPromise, message) => { + // TODO: this is unlikely to fail, but maybe we should handle differently? + const resolved = await accumPromise; // wait for previous promise to resolve + const sent = await channel.messages().create({ + body: message.content, + from: 'Bot', + xTwilioWebhookEnabled: 'true', + }); + + return [...resolved, sent]; + }, + Promise.resolve([]), + ); + + // If the session ended, we should unlock the channel to continue the Studio Flow + // TODO: raise the discussion. This could be done from a Lambda that's called when the bot + // finishes the convo. Unfortunately, AWS only allows Lambdas there, so it may require some more work + if (lexResponse.sessionState?.dialogAction?.type === 'Close') { + await Promise.all([ + // This is not really needed as the session will expire, but that depends on the config of Lex. + Lex.deleteSession({ + botId: channelAttributes.channelCapturedByBot.botId, + botAliasId: channelAttributes.channelCapturedByBot.botAliasId, + localeId: channelAttributes.channelCapturedByBot.localeId, + sessionId: From, + }).promise(), + channel.update({ + attributes: JSON.stringify(omit(channelAttributes, 'channelCapturedByBot')), + }), + ]); + + console.log('Channel unblocked and bot session deleted'); + } + + resolve(success('All messages sent :)')); + return; + } + + resolve(success('Event ignored')); + } catch (err) { + if (err instanceof Error) resolve(error500(err)); + else resolve(error500(new Error(String(err)))); + } +}; From a53d7b2bca5271a33d55428ebf6f8d8a05985b61 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Tue, 14 Mar 2023 16:16:22 +0100 Subject: [PATCH 02/13] Added option to first set of messages --- functions/captureChannelWithBot.protected.ts | 57 ++++++++++++++++++- .../webhooks/chatbotCallback.protected.ts | 9 ++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts index 5fcc7b83..0a26d35a 100644 --- a/functions/captureChannelWithBot.protected.ts +++ b/functions/captureChannelWithBot.protected.ts @@ -25,13 +25,19 @@ import { error500, success, } from '@tech-matters/serverless-helpers'; +import AWS from 'aws-sdk'; +import type { MessageInstance } from 'twilio/lib/rest/chat/v2/service/channel/message'; type EnvVars = { CHAT_SERVICE_SID: string; + ASELO_APP_ACCESS_KEY: string; + ASELO_APP_SECRET_KEY: string; + AWS_REGION: string; }; type Body = { channelSid: string; + message: string; }; export const handler = async ( @@ -43,7 +49,7 @@ export const handler = async ( const resolve = bindResolve(callback)(response); try { - const { channelSid } = event; + const { channelSid, message } = event; if (channelSid === undefined) { resolve(error400('channelSid')); @@ -89,6 +95,55 @@ export const handler = async ( }), }); + await channel.webhooks().create({ + type: 'webhook', + configuration: { + filters: ['onMessageSent'], + method: 'POST', + url: `https://${context.DOMAIN_NAME}/webhooks/chatbotCallback`, + }, + }); + + // ============== + /** + * TODO: Factor out shared chunk of code + */ + AWS.config.update({ + credentials: { + accessKeyId: context.ASELO_APP_ACCESS_KEY, + secretAccessKey: context.ASELO_APP_SECRET_KEY, + }, + region: context.AWS_REGION, + }); + + const Lex = new AWS.LexRuntimeV2(); + + const lexResponse = await Lex.recognizeText({ + botId: channelAttributes.channelCapturedByBot.botId, + botAliasId: channelAttributes.channelCapturedByBot.botAliasId, + localeId: channelAttributes.channelCapturedByBot.localeId, + text: message, + sessionId: channel.sid, // We could use some channel/bot info to better scope this + }).promise(); + + // Secuentially wait for the messages to be sent in the correct order + // TODO: probably we want to handle the case where messages is null + /* const messagesSent = */ await lexResponse.messages?.reduce>( + async (accumPromise, message) => { + // TODO: this is unlikely to fail, but maybe we should handle differently? + const resolved = await accumPromise; // wait for previous promise to resolve + const sent = await channel.messages().create({ + body: message.content, + from: 'Bot', + xTwilioWebhookEnabled: 'true', + }); + + return [...resolved, sent]; + }, + Promise.resolve([]), + ); + // ============== + resolve(success('Channel caputer by bot =)')); } catch (err) { if (err instanceof Error) resolve(error500(err)); diff --git a/functions/webhooks/chatbotCallback.protected.ts b/functions/webhooks/chatbotCallback.protected.ts index 40a6df14..efa311bb 100644 --- a/functions/webhooks/chatbotCallback.protected.ts +++ b/functions/webhooks/chatbotCallback.protected.ts @@ -78,6 +78,10 @@ export const handler = async ( // Send message to bot only if it's from child if (EventType === 'onMessageSent' && channelAttributes.from === From) { + // ============== + /** + * TODO: Factor out shared chunk of code + */ AWS.config.update({ credentials: { accessKeyId: context.ASELO_APP_ACCESS_KEY, @@ -93,7 +97,7 @@ export const handler = async ( botAliasId: channelAttributes.channelCapturedByBot.botAliasId, localeId: channelAttributes.channelCapturedByBot.localeId, text: Body, - sessionId: From, // We could use some channel/bot info to better scope this + sessionId: channel.sid, // We could use some channel/bot info to better scope this }).promise(); // Secuentially wait for the messages to be sent in the correct order @@ -112,6 +116,7 @@ export const handler = async ( }, Promise.resolve([]), ); + // ============== // If the session ended, we should unlock the channel to continue the Studio Flow // TODO: raise the discussion. This could be done from a Lambda that's called when the bot @@ -123,7 +128,7 @@ export const handler = async ( botId: channelAttributes.channelCapturedByBot.botId, botAliasId: channelAttributes.channelCapturedByBot.botAliasId, localeId: channelAttributes.channelCapturedByBot.localeId, - sessionId: From, + sessionId: channel.sid, }).promise(), channel.update({ attributes: JSON.stringify(omit(channelAttributes, 'channelCapturedByBot')), From 704e374c9dbd3c8095f79c1f325916b801d9efd4 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Tue, 14 Mar 2023 16:43:36 +0100 Subject: [PATCH 03/13] Use new attributes --- functions/captureChannelWithBot.protected.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts index 0a26d35a..f26e5d61 100644 --- a/functions/captureChannelWithBot.protected.ts +++ b/functions/captureChannelWithBot.protected.ts @@ -84,7 +84,7 @@ export const handler = async ( }), ); - await channel.update({ + const updated = await channel.update({ attributes: JSON.stringify({ ...channelAttributes, channelCapturedByBot: { @@ -95,6 +95,8 @@ export const handler = async ( }), }); + const updatedChannelAttributes = JSON.parse(updated.accountSid); + await channel.webhooks().create({ type: 'webhook', configuration: { @@ -119,9 +121,9 @@ export const handler = async ( const Lex = new AWS.LexRuntimeV2(); const lexResponse = await Lex.recognizeText({ - botId: channelAttributes.channelCapturedByBot.botId, - botAliasId: channelAttributes.channelCapturedByBot.botAliasId, - localeId: channelAttributes.channelCapturedByBot.localeId, + botId: updatedChannelAttributes.channelCapturedByBot.botId, + botAliasId: updatedChannelAttributes.channelCapturedByBot.botAliasId, + localeId: updatedChannelAttributes.channelCapturedByBot.localeId, text: message, sessionId: channel.sid, // We could use some channel/bot info to better scope this }).promise(); From 90bbd09d4c4c3d4e8bafc0164b304aebe3f6d1d9 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Tue, 14 Mar 2023 16:50:28 +0100 Subject: [PATCH 04/13] Fixed silly error =) --- functions/captureChannelWithBot.protected.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts index f26e5d61..e040831e 100644 --- a/functions/captureChannelWithBot.protected.ts +++ b/functions/captureChannelWithBot.protected.ts @@ -95,7 +95,7 @@ export const handler = async ( }), }); - const updatedChannelAttributes = JSON.parse(updated.accountSid); + const updatedChannelAttributes = JSON.parse(updated.attributes); await channel.webhooks().create({ type: 'webhook', From 64dca28399988844806fd848d4c2847a02ac5759 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Wed, 15 Mar 2023 12:06:53 +0100 Subject: [PATCH 05/13] Approach change: trigger flow via API after channel is released --- functions/captureChannelWithBot.protected.ts | 20 ++++++++++++----- .../webhooks/chatbotCallback.protected.ts | 22 ++++++++++++++----- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts index e040831e..cd05d32d 100644 --- a/functions/captureChannelWithBot.protected.ts +++ b/functions/captureChannelWithBot.protected.ts @@ -36,8 +36,9 @@ type EnvVars = { }; type Body = { - channelSid: string; - message: string; + channelSid: string; // (in Studio Flow, flow.channel.address) The channel to capture + message: string; // (in Studio Flow, trigger.message.Body) The triggering message + studioFlowSid: string; // (in Studio Flow, flow.flow_sid) The Studio Flow sid. Needed to trigger an API type execution once the channel is released. }; export const handler = async ( @@ -49,12 +50,20 @@ export const handler = async ( const resolve = bindResolve(callback)(response); try { - const { channelSid, message } = event; + const { channelSid, message, studioFlowSid } = event; if (channelSid === undefined) { resolve(error400('channelSid')); return; } + if (message === undefined) { + resolve(error400('message')); + return; + } + if (studioFlowSid === undefined) { + resolve(error400('studioFlowSid')); + return; + } const channel = await context .getTwilioClient() @@ -91,6 +100,7 @@ export const handler = async ( botId: 'C6HUSTIFBR', // This should be passed as parameter botAliasId: 'TSTALIASID', // This should be passed as parameter localeId: 'en_US', // This should be passed as parameter + studioFlowSid, }, }), }); @@ -125,7 +135,7 @@ export const handler = async ( botAliasId: updatedChannelAttributes.channelCapturedByBot.botAliasId, localeId: updatedChannelAttributes.channelCapturedByBot.localeId, text: message, - sessionId: channel.sid, // We could use some channel/bot info to better scope this + sessionId: channel.sid, }).promise(); // Secuentially wait for the messages to be sent in the correct order @@ -146,7 +156,7 @@ export const handler = async ( ); // ============== - resolve(success('Channel caputer by bot =)')); + resolve(success('Channel captured by bot =)')); } catch (err) { if (err instanceof Error) resolve(error500(err)); else resolve(error500(new Error(String(err)))); diff --git a/functions/webhooks/chatbotCallback.protected.ts b/functions/webhooks/chatbotCallback.protected.ts index efa311bb..2b38d51a 100644 --- a/functions/webhooks/chatbotCallback.protected.ts +++ b/functions/webhooks/chatbotCallback.protected.ts @@ -37,9 +37,7 @@ type EnvVars = { AWS_REGION: string; }; -export type Body = Partial & { - // recipientId?: string; -}; +export type Body = Partial & {}; export const handler = async ( context: Context, @@ -122,16 +120,30 @@ export const handler = async ( // TODO: raise the discussion. This could be done from a Lambda that's called when the bot // finishes the convo. Unfortunately, AWS only allows Lambdas there, so it may require some more work if (lexResponse.sessionState?.dialogAction?.type === 'Close') { + const releasedChannelAttributes = omit(channelAttributes, 'channelCapturedByBot'); + await Promise.all([ - // This is not really needed as the session will expire, but that depends on the config of Lex. + // Delete Lex session. This is not really needed as the session will expire, but that depends on the config of Lex. Lex.deleteSession({ botId: channelAttributes.channelCapturedByBot.botId, botAliasId: channelAttributes.channelCapturedByBot.botAliasId, localeId: channelAttributes.channelCapturedByBot.localeId, sessionId: channel.sid, }).promise(), + // Remove channelCapturedByBot from channel attributes channel.update({ - attributes: JSON.stringify(omit(channelAttributes, 'channelCapturedByBot')), + attributes: JSON.stringify(releasedChannelAttributes), + }), + // Trigger a new API type Studio Flow execution once the channel is released + client.studio.v2.flows('').executions.create({ + from: From, + to: ChannelSid, + parameters: { + ChannelAttributes: { + ...releasedChannelAttributes, + memory: lexResponse.interpretations, + }, + }, }), ]); From 79998017f6d751240d0891fb6d49d8c3d91035ce Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Wed, 15 Mar 2023 12:09:50 +0100 Subject: [PATCH 06/13] Fixed studio flow sid --- .../webhooks/chatbotCallback.protected.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/functions/webhooks/chatbotCallback.protected.ts b/functions/webhooks/chatbotCallback.protected.ts index 2b38d51a..0e48ce1f 100644 --- a/functions/webhooks/chatbotCallback.protected.ts +++ b/functions/webhooks/chatbotCallback.protected.ts @@ -135,16 +135,18 @@ export const handler = async ( attributes: JSON.stringify(releasedChannelAttributes), }), // Trigger a new API type Studio Flow execution once the channel is released - client.studio.v2.flows('').executions.create({ - from: From, - to: ChannelSid, - parameters: { - ChannelAttributes: { - ...releasedChannelAttributes, - memory: lexResponse.interpretations, + client.studio.v2 + .flows(channelAttributes.channelCapturedByBot.studioFlowSid) + .executions.create({ + from: From, + to: ChannelSid, + parameters: { + ChannelAttributes: { + ...releasedChannelAttributes, + memory: lexResponse.interpretations, + }, }, - }, - }), + }), ]); console.log('Channel unblocked and bot session deleted'); From 05f44951d9708a199d74508abecb81bd2b10f939 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Wed, 15 Mar 2023 13:08:26 +0100 Subject: [PATCH 07/13] Added service user to the channel --- functions/captureChannelWithBot.protected.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts index cd05d32d..c56a3941 100644 --- a/functions/captureChannelWithBot.protected.ts +++ b/functions/captureChannelWithBot.protected.ts @@ -38,6 +38,7 @@ type EnvVars = { type Body = { channelSid: string; // (in Studio Flow, flow.channel.address) The channel to capture message: string; // (in Studio Flow, trigger.message.Body) The triggering message + serviceUser: string; // (in Studio Flow, trigger.message.From) The service user unique name studioFlowSid: string; // (in Studio Flow, flow.flow_sid) The Studio Flow sid. Needed to trigger an API type execution once the channel is released. }; @@ -50,7 +51,7 @@ export const handler = async ( const resolve = bindResolve(callback)(response); try { - const { channelSid, message, studioFlowSid } = event; + const { channelSid, message, serviceUser, studioFlowSid } = event; if (channelSid === undefined) { resolve(error400('channelSid')); @@ -60,6 +61,10 @@ export const handler = async ( resolve(error400('message')); return; } + if (serviceUser === undefined) { + resolve(error400('serviceUser')); + return; + } if (studioFlowSid === undefined) { resolve(error400('studioFlowSid')); return; @@ -100,6 +105,7 @@ export const handler = async ( botId: 'C6HUSTIFBR', // This should be passed as parameter botAliasId: 'TSTALIASID', // This should be passed as parameter localeId: 'en_US', // This should be passed as parameter + serviceUser, studioFlowSid, }, }), From 79391abf7fabf34819d7240c9a6852195430b494 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Wed, 15 Mar 2023 13:11:25 +0100 Subject: [PATCH 08/13] Carry around from unique id --- functions/captureChannelWithBot.protected.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts index c56a3941..11eaaad8 100644 --- a/functions/captureChannelWithBot.protected.ts +++ b/functions/captureChannelWithBot.protected.ts @@ -38,7 +38,7 @@ type EnvVars = { type Body = { channelSid: string; // (in Studio Flow, flow.channel.address) The channel to capture message: string; // (in Studio Flow, trigger.message.Body) The triggering message - serviceUser: string; // (in Studio Flow, trigger.message.From) The service user unique name + fromServiceUser: string; // (in Studio Flow, trigger.message.From) The service user unique name studioFlowSid: string; // (in Studio Flow, flow.flow_sid) The Studio Flow sid. Needed to trigger an API type execution once the channel is released. }; @@ -51,7 +51,7 @@ export const handler = async ( const resolve = bindResolve(callback)(response); try { - const { channelSid, message, serviceUser, studioFlowSid } = event; + const { channelSid, message, fromServiceUser, studioFlowSid } = event; if (channelSid === undefined) { resolve(error400('channelSid')); @@ -61,8 +61,8 @@ export const handler = async ( resolve(error400('message')); return; } - if (serviceUser === undefined) { - resolve(error400('serviceUser')); + if (fromServiceUser === undefined) { + resolve(error400('fromServiceUser')); return; } if (studioFlowSid === undefined) { @@ -101,11 +101,11 @@ export const handler = async ( const updated = await channel.update({ attributes: JSON.stringify({ ...channelAttributes, + fromServiceUser, // Save this in the outer scope so it's persisted for later chatbots channelCapturedByBot: { botId: 'C6HUSTIFBR', // This should be passed as parameter botAliasId: 'TSTALIASID', // This should be passed as parameter localeId: 'en_US', // This should be passed as parameter - serviceUser, studioFlowSid, }, }), From 45f0766500a1f73a881a8f5f57e8158ca39984d9 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Wed, 15 Mar 2023 13:12:50 +0100 Subject: [PATCH 09/13] Use fromServiceUser to compare unique sids (breaks for webchat otherwise) --- functions/webhooks/chatbotCallback.protected.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/webhooks/chatbotCallback.protected.ts b/functions/webhooks/chatbotCallback.protected.ts index 0e48ce1f..70d6cb02 100644 --- a/functions/webhooks/chatbotCallback.protected.ts +++ b/functions/webhooks/chatbotCallback.protected.ts @@ -75,7 +75,7 @@ export const handler = async ( const channelAttributes = JSON.parse(channel.attributes); // Send message to bot only if it's from child - if (EventType === 'onMessageSent' && channelAttributes.from === From) { + if (EventType === 'onMessageSent' && channelAttributes.fromServiceUser === From) { // ============== /** * TODO: Factor out shared chunk of code From dc2a3795d23fecccedc2a3228f9dc78d4d7dcce9 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Wed, 15 Mar 2023 14:13:59 +0100 Subject: [PATCH 10/13] Mimic default twilio executions from address (channelSid) --- functions/webhooks/chatbotCallback.protected.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/webhooks/chatbotCallback.protected.ts b/functions/webhooks/chatbotCallback.protected.ts index 70d6cb02..5a76ef77 100644 --- a/functions/webhooks/chatbotCallback.protected.ts +++ b/functions/webhooks/chatbotCallback.protected.ts @@ -138,7 +138,7 @@ export const handler = async ( client.studio.v2 .flows(channelAttributes.channelCapturedByBot.studioFlowSid) .executions.create({ - from: From, + from: ChannelSid, to: ChannelSid, parameters: { ChannelAttributes: { From e22172f743335abf270d263fc35dd51c2a439e6c Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Wed, 15 Mar 2023 16:15:58 +0100 Subject: [PATCH 11/13] Added logic to cleanup the webhook --- functions/captureChannelWithBot.protected.ts | 20 ++++++++++--------- .../webhooks/chatbotCallback.protected.ts | 16 ++++++++++----- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts index 11eaaad8..20b7c9d0 100644 --- a/functions/captureChannelWithBot.protected.ts +++ b/functions/captureChannelWithBot.protected.ts @@ -98,30 +98,32 @@ export const handler = async ( }), ); + const chatbotCallbackWebhook = await channel.webhooks().create({ + type: 'webhook', + configuration: { + filters: ['onMessageSent'], + method: 'POST', + url: `https://${context.DOMAIN_NAME}/webhooks/chatbotCallback`, + }, + }); + const updated = await channel.update({ attributes: JSON.stringify({ ...channelAttributes, fromServiceUser, // Save this in the outer scope so it's persisted for later chatbots + // All of this can be passed as url params to the webhook instead channelCapturedByBot: { botId: 'C6HUSTIFBR', // This should be passed as parameter botAliasId: 'TSTALIASID', // This should be passed as parameter localeId: 'en_US', // This should be passed as parameter studioFlowSid, + chatbotCallbackWebhookSid: chatbotCallbackWebhook.sid, }, }), }); const updatedChannelAttributes = JSON.parse(updated.attributes); - await channel.webhooks().create({ - type: 'webhook', - configuration: { - filters: ['onMessageSent'], - method: 'POST', - url: `https://${context.DOMAIN_NAME}/webhooks/chatbotCallback`, - }, - }); - // ============== /** * TODO: Factor out shared chunk of code diff --git a/functions/webhooks/chatbotCallback.protected.ts b/functions/webhooks/chatbotCallback.protected.ts index 5a76ef77..36bcfc3a 100644 --- a/functions/webhooks/chatbotCallback.protected.ts +++ b/functions/webhooks/chatbotCallback.protected.ts @@ -120,7 +120,11 @@ export const handler = async ( // TODO: raise the discussion. This could be done from a Lambda that's called when the bot // finishes the convo. Unfortunately, AWS only allows Lambdas there, so it may require some more work if (lexResponse.sessionState?.dialogAction?.type === 'Close') { - const releasedChannelAttributes = omit(channelAttributes, 'channelCapturedByBot'); + const releasedChannelAttributes = { + ...omit(channelAttributes, 'channelCapturedByBot'), + memory: lexResponse.interpretations, + }; + // const releasedChannelAttributes = omit(channelAttributes, 'channelCapturedByBot'); await Promise.all([ // Delete Lex session. This is not really needed as the session will expire, but that depends on the config of Lex. @@ -134,6 +138,11 @@ export const handler = async ( channel.update({ attributes: JSON.stringify(releasedChannelAttributes), }), + // Remove this webhook from the channel + channel + .webhooks() + .get(channelAttributes.channelCapturedByBot.chatbotCallbackWebhookSid) + .remove(), // Trigger a new API type Studio Flow execution once the channel is released client.studio.v2 .flows(channelAttributes.channelCapturedByBot.studioFlowSid) @@ -141,10 +150,7 @@ export const handler = async ( from: ChannelSid, to: ChannelSid, parameters: { - ChannelAttributes: { - ...releasedChannelAttributes, - memory: lexResponse.interpretations, - }, + ChannelAttributes: releasedChannelAttributes, }, }), ]); From 16317601b8d9bf193b7c2bc51e17772eaa0b67f5 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Fri, 17 Mar 2023 19:20:23 +0100 Subject: [PATCH 12/13] Reowrked logic of channel capture to use dialoglow --- .gitignore | 1 + functions/captureChannelWithBot.protected.ts | 71 +- .../webhooks/chatbotCallback.protected.ts | 80 +- package-lock.json | 766 ++++++++++++++++-- package.json | 1 + 5 files changed, 762 insertions(+), 157 deletions(-) diff --git a/.gitignore b/.gitignore index 083eff3d..3b5faa2d 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,4 @@ tech-matters-serverless-helpers/lib/ .twiliodeployinfo assets/blocklist.private.json +assets/service-account-key.private.json \ No newline at end of file diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts index 20b7c9d0..56487214 100644 --- a/functions/captureChannelWithBot.protected.ts +++ b/functions/captureChannelWithBot.protected.ts @@ -25,14 +25,11 @@ import { error500, success, } from '@tech-matters/serverless-helpers'; -import AWS from 'aws-sdk'; +import dialogflow from '@google-cloud/dialogflow'; import type { MessageInstance } from 'twilio/lib/rest/chat/v2/service/channel/message'; type EnvVars = { CHAT_SERVICE_SID: string; - ASELO_APP_ACCESS_KEY: string; - ASELO_APP_SECRET_KEY: string; - AWS_REGION: string; }; type Body = { @@ -113,9 +110,8 @@ export const handler = async ( fromServiceUser, // Save this in the outer scope so it's persisted for later chatbots // All of this can be passed as url params to the webhook instead channelCapturedByBot: { - botId: 'C6HUSTIFBR', // This should be passed as parameter - botAliasId: 'TSTALIASID', // This should be passed as parameter - localeId: 'en_US', // This should be passed as parameter + projectId: 'presurveybot-test-pmcl', // This should be passed as parameter + languageCode: 'en-US', // This should be passed as parameter studioFlowSid, chatbotCallbackWebhookSid: chatbotCallbackWebhook.sid, }, @@ -128,40 +124,41 @@ export const handler = async ( /** * TODO: Factor out shared chunk of code */ - AWS.config.update({ - credentials: { - accessKeyId: context.ASELO_APP_ACCESS_KEY, - secretAccessKey: context.ASELO_APP_SECRET_KEY, + // google requires an environment variable called GOOGLE_APPLICATION_CREDENTIALS that points to a file path with the service account key file (json) to authenticate into their API + // to solve for this, we save the key file as a private asset, then use a helper function to find and return the path of the private asset. + // lastly we set the environment variable dynamically at runtime so that it's in place when the sessions client is initialized + process.env.GOOGLE_APPLICATION_CREDENTIALS = + Runtime.getAssets()['/service-account-key.json'].path; + + // Create a new session + const sessionClient = new dialogflow.SessionsClient(); + + const request = { + session: sessionClient.projectAgentSessionPath( + updatedChannelAttributes.channelCapturedByBot.projectId, // projectId + channel.sid, // sessionId + ), + queryInput: { + text: { + // The query to send to the dialogflow agent + text: message, + // The language used by the client (en-US) + languageCode: updatedChannelAttributes.channelCapturedByBot.languageCode, + }, }, - region: context.AWS_REGION, - }); - - const Lex = new AWS.LexRuntimeV2(); + }; - const lexResponse = await Lex.recognizeText({ - botId: updatedChannelAttributes.channelCapturedByBot.botId, - botAliasId: updatedChannelAttributes.channelCapturedByBot.botAliasId, - localeId: updatedChannelAttributes.channelCapturedByBot.localeId, - text: message, - sessionId: channel.sid, - }).promise(); + // Only the first element of the touple seemed relevant so far + const [dialogflowResponse] = await sessionClient.detectIntent(request); - // Secuentially wait for the messages to be sent in the correct order // TODO: probably we want to handle the case where messages is null - /* const messagesSent = */ await lexResponse.messages?.reduce>( - async (accumPromise, message) => { - // TODO: this is unlikely to fail, but maybe we should handle differently? - const resolved = await accumPromise; // wait for previous promise to resolve - const sent = await channel.messages().create({ - body: message.content, - from: 'Bot', - xTwilioWebhookEnabled: 'true', - }); - - return [...resolved, sent]; - }, - Promise.resolve([]), - ); + if (dialogflowResponse.queryResult?.fulfillmentText) { + await channel.messages().create({ + body: dialogflowResponse.queryResult?.fulfillmentText, + from: 'Bot', + xTwilioWebhookEnabled: 'true', + }); + } // ============== resolve(success('Channel captured by bot =)')); diff --git a/functions/webhooks/chatbotCallback.protected.ts b/functions/webhooks/chatbotCallback.protected.ts index 36bcfc3a..149302d7 100644 --- a/functions/webhooks/chatbotCallback.protected.ts +++ b/functions/webhooks/chatbotCallback.protected.ts @@ -25,16 +25,12 @@ import { error500, success, } from '@tech-matters/serverless-helpers'; -import AWS from 'aws-sdk'; -import type { MessageInstance } from 'twilio/lib/rest/chat/v2/service/channel/message'; +import dialogflow from '@google-cloud/dialogflow'; import { omit } from 'lodash'; import type { WebhookEvent } from '../helpers/customChannels/flexToCustomChannel.private'; type EnvVars = { CHAT_SERVICE_SID: string; - ASELO_APP_ACCESS_KEY: string; - ASELO_APP_SECRET_KEY: string; - AWS_REGION: string; }; export type Body = Partial & {}; @@ -80,60 +76,54 @@ export const handler = async ( /** * TODO: Factor out shared chunk of code */ - AWS.config.update({ - credentials: { - accessKeyId: context.ASELO_APP_ACCESS_KEY, - secretAccessKey: context.ASELO_APP_SECRET_KEY, + // google requires an environment variable called GOOGLE_APPLICATION_CREDENTIALS that points to a file path with the service account key file (json) to authenticate into their API + // to solve for this, we save the key file as a private asset, then use a helper function to find and return the path of the private asset. + // lastly we set the environment variable dynamically at runtime so that it's in place when the sessions client is initialized + process.env.GOOGLE_APPLICATION_CREDENTIALS = + Runtime.getAssets()['/service-account-key.json'].path; + + // Create a new session + const sessionClient = new dialogflow.SessionsClient(); + + const request = { + session: sessionClient.projectAgentSessionPath( + channelAttributes.channelCapturedByBot.projectId, // projectId + channel.sid, // sessionId + ), + queryInput: { + text: { + // The query to send to the dialogflow agent + text: Body, + // The language used by the client (en-US) + languageCode: channelAttributes.channelCapturedByBot.languageCode, + }, }, - region: context.AWS_REGION, - }); + }; - const Lex = new AWS.LexRuntimeV2(); + // Only the first element of the touple seemed relevant so far + const [dialogflowResponse] = await sessionClient.detectIntent(request); - const lexResponse = await Lex.recognizeText({ - botId: channelAttributes.channelCapturedByBot.botId, - botAliasId: channelAttributes.channelCapturedByBot.botAliasId, - localeId: channelAttributes.channelCapturedByBot.localeId, - text: Body, - sessionId: channel.sid, // We could use some channel/bot info to better scope this - }).promise(); - - // Secuentially wait for the messages to be sent in the correct order // TODO: probably we want to handle the case where messages is null - /* const messagesSent = */ await lexResponse.messages?.reduce>( - async (accumPromise, message) => { - // TODO: this is unlikely to fail, but maybe we should handle differently? - const resolved = await accumPromise; // wait for previous promise to resolve - const sent = await channel.messages().create({ - body: message.content, - from: 'Bot', - xTwilioWebhookEnabled: 'true', - }); - - return [...resolved, sent]; - }, - Promise.resolve([]), - ); + if (dialogflowResponse.queryResult?.fulfillmentText) { + await channel.messages().create({ + body: dialogflowResponse.queryResult?.fulfillmentText, + from: 'Bot', + xTwilioWebhookEnabled: 'true', + }); + } // ============== // If the session ended, we should unlock the channel to continue the Studio Flow // TODO: raise the discussion. This could be done from a Lambda that's called when the bot // finishes the convo. Unfortunately, AWS only allows Lambdas there, so it may require some more work - if (lexResponse.sessionState?.dialogAction?.type === 'Close') { + if (dialogflowResponse.queryResult?.diagnosticInfo?.fields?.end_conversation.boolValue) { const releasedChannelAttributes = { ...omit(channelAttributes, 'channelCapturedByBot'), - memory: lexResponse.interpretations, + memory: dialogflowResponse.queryResult.parameters?.fields, }; - // const releasedChannelAttributes = omit(channelAttributes, 'channelCapturedByBot'); + // No need to delete the session here, as it's removed once end_conversation state is reached await Promise.all([ - // Delete Lex session. This is not really needed as the session will expire, but that depends on the config of Lex. - Lex.deleteSession({ - botId: channelAttributes.channelCapturedByBot.botId, - botAliasId: channelAttributes.channelCapturedByBot.botAliasId, - localeId: channelAttributes.channelCapturedByBot.localeId, - sessionId: channel.sid, - }).promise(), // Remove channelCapturedByBot from channel attributes channel.update({ attributes: JSON.stringify(releasedChannelAttributes), diff --git a/package-lock.json b/package-lock.json index c1c8e986..0299ae41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -471,6 +471,56 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@google-cloud/dialogflow": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@google-cloud/dialogflow/-/dialogflow-5.7.0.tgz", + "integrity": "sha512-EIuQE9woXfjHLpp+pJ3Zed+dZtyzskP+eCYrV4cpW9h/KyA3STiEb5jf79Z/eJDDExeSjCgNH2O+aGtGl/Uc4Q==", + "requires": { + "google-gax": "^3.5.2" + } + }, + "@grpc/grpc-js": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.12.tgz", + "integrity": "sha512-MbUMvpVvakeKhdYux6gbSIPJaFMLNSY8jw4PqLI+FFztGrQRrYYAnHlR94+ncBQQewkpXQaW449m3tpH/B/ZnQ==", + "requires": { + "@grpc/proto-loader": "^0.7.0", + "@types/node": ">=12.12.47" + } + }, + "@grpc/proto-loader": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.5.tgz", + "integrity": "sha512-mfcTuMbFowq1wh/Rn5KQl6qb95M21Prej3bewD9dUQMurYGVckGO/Pbe2Ocwto6sD05b/mxZLspvqwx60xO2Rg==", + "requires": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^7.0.0", + "yargs": "^16.2.0" + }, + "dependencies": { + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -813,6 +863,14 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@jsdoc/salty": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", + "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "requires": { + "lodash": "^4.17.21" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -839,6 +897,60 @@ "fastq": "^1.6.0" } }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "@sinclair/typebox": { "version": "0.24.28", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.28.tgz", @@ -1163,6 +1275,15 @@ "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==", "dev": true }, + "@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "requires": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -1267,12 +1388,36 @@ "@types/node": "*" } }, + "@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==" + }, "@types/lodash": { "version": "4.14.184", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.184.tgz", "integrity": "sha512-RoZphVtHbxPZizt4IcILciSWiC6dcn+eZ8oX9IWEYfDMcocdd42f7NPI6fQj+6zI8y4E0L7gu2pcZKLGTRaV9Q==", "dev": true }, + "@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "requires": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==" + }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -1284,6 +1429,11 @@ "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==", "dev": true }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" + }, "@types/node": { "version": "13.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz", @@ -1379,6 +1529,15 @@ "@types/node": "*" } }, + "@types/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==", + "requires": { + "@types/glob": "*", + "@types/node": "*" + } + }, "@types/semver": { "version": "7.3.12", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz", @@ -1689,6 +1848,14 @@ "dev": true, "optional": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1707,8 +1874,7 @@ "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" }, "agent-base": { "version": "6.0.2", @@ -1784,8 +1950,7 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "4.3.0", @@ -1862,6 +2027,11 @@ "es-shim-unscopables": "^1.0.0" } }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -2011,8 +2181,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-js": { "version": "1.5.1", @@ -2027,6 +2196,11 @@ "tweetnacl": "^0.14.3" } }, + "bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==" + }, "binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", @@ -2158,7 +2332,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2301,6 +2474,14 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, + "catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "requires": { + "lodash": "^4.17.15" + } + }, "chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", @@ -2512,7 +2693,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -2601,8 +2781,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "conf": { "version": "5.0.0", @@ -2817,8 +2996,7 @@ "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "deepmerge": { "version": "4.2.2", @@ -2915,6 +3093,17 @@ "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==", "dev": true }, + "duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -2952,8 +3141,7 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "encodeurl": { "version": "1.0.2", @@ -2964,7 +3152,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -2978,6 +3165,11 @@ "ansi-colors": "^4.1.1" } }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" + }, "env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -3045,8 +3237,7 @@ "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-html": { "version": "1.0.3", @@ -3059,6 +3250,18 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, "eslint": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", @@ -3593,8 +3796,7 @@ "eslint-visitor-keys": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" }, "espree": { "version": "6.2.1", @@ -3618,8 +3820,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.4.0", @@ -3658,20 +3859,23 @@ "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -3959,14 +4163,18 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "fast-redact": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-1.5.0.tgz", "integrity": "sha512-Afo61CgUjkzdvOKDHn08qnZ0kwck38AOGcMlvSGzvJbIab6soAP5rdoQayecGCDsD69AiF9vJBXyq31eoEO2tQ==" }, + "fast-text-encoding": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", + "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==" + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -4092,8 +4300,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.2", @@ -4129,6 +4336,26 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, + "gaxios": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.0.tgz", + "integrity": "sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A==", + "requires": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.7" + } + }, + "gcp-metadata": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz", + "integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==", + "requires": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + } + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -4138,8 +4365,7 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { "version": "1.1.2", @@ -4190,7 +4416,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4240,6 +4465,73 @@ "slash": "^3.0.0" } }, + "google-auth-library": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.7.0.tgz", + "integrity": "sha512-1M0NG5VDIvJZEnstHbRdckLZESoJwguinwN8Dhae0j2ZKIQFIV63zxm6Fo6nM4xkgqUr2bbMtV5Dgo+Hy6oo0Q==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.0.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "dependencies": { + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + } + } + }, + "google-gax": { + "version": "3.5.8", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-3.5.8.tgz", + "integrity": "sha512-bkqxv7YdYP3FGh+dywvgyctj8XM07toJ/JCWwAkmmE15QTt3ieF/f7Hpz7xG85+dGmZtQR8Y+yMo0ENFmhypNA==", + "requires": { + "@grpc/grpc-js": "~1.8.0", + "@grpc/proto-loader": "^0.7.0", + "@types/long": "^4.0.0", + "@types/rimraf": "^3.0.2", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^8.0.2", + "is-stream-ended": "^0.1.4", + "node-fetch": "^2.6.1", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^1.0.0", + "protobufjs": "7.2.2", + "protobufjs-cli": "1.1.1", + "retry-request": "^5.0.0" + } + }, + "google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "requires": { + "node-forge": "^1.3.1" + } + }, "got": { "version": "11.8.5", "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", @@ -4262,8 +4554,38 @@ "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "requires": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + }, + "dependencies": { + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + } + } }, "har-schema": { "version": "2.0.0", @@ -4449,7 +4771,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -4639,8 +4960,7 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-generator-fn": { "version": "2.1.0", @@ -4716,8 +5036,12 @@ "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-stream-ended": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==" }, "is-string": { "version": "1.0.7", @@ -5328,17 +5652,72 @@ "esprima": "^4.0.0" } }, + "js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "requires": { + "xmlcreate": "^2.0.4" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, + "jsdoc": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "requires": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "dependencies": { + "@babel/parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", + "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==" + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, "json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -5450,6 +5829,14 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "requires": { + "graceful-fs": "^4.1.9" + } + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -5466,7 +5853,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -5478,6 +5864,14 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "requires": { + "uc.micro": "^1.0.1" + } + }, "listr2": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.2.tgz", @@ -5519,8 +5913,7 @@ "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, "lodash.debounce": { "version": "4.0.8", @@ -5684,6 +6077,11 @@ } } }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -5694,7 +6092,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -5731,6 +6128,40 @@ "tmpl": "1.0.5" } }, + "markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "requires": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + } + } + }, + "markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==" + }, + "marked": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==" + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -5802,7 +6233,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5810,8 +6240,7 @@ "minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "mkdirp": { "version": "0.5.6", @@ -5912,6 +6341,19 @@ "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" }, + "node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5965,6 +6407,11 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -6021,7 +6468,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "requires": { "wrappy": "1" } @@ -6039,7 +6485,6 @@ "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", @@ -6172,8 +6617,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "3.1.1", @@ -6438,8 +6882,7 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" }, "prettier": { "version": "2.7.1", @@ -6492,6 +6935,123 @@ "sisteransi": "^1.0.5" } }, + "proto3-json-serializer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.0.tgz", + "integrity": "sha512-SjXwUWe/vANGs/mJJTbw5++7U67nwsymg7qsoPtw6GiXqw3kUy8ByojrlEdVE2efxAdKreX8WkDafxvYW95ZQg==", + "requires": { + "protobufjs": "^7.0.0" + } + }, + "protobufjs": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.2.tgz", + "integrity": "sha512-++PrQIjrom+bFDPpfmqXfAGSQs40116JRrqqyf53dymUMvvb5d/LMRyicRoF1AUKoXVS1/IgJXlEgcpr4gTF3Q==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "dependencies": { + "long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" + } + } + }, + "protobufjs-cli": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.1.tgz", + "integrity": "sha512-VPWMgIcRNyQwWUv8OLPyGQ/0lQY/QTQAVN5fh+XzfDwsVw1FZ2L3DM/bcBf8WPiRz2tNpaov9lPZfNcmNo6LXA==", + "requires": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^4.0.0", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "dependencies": { + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "espree": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", + "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "requires": { + "rimraf": "^3.0.0" + } + } + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -6594,7 +7154,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10001,14 +10560,21 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "requires": { + "lodash": "^4.17.21" + } + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -10074,6 +10640,25 @@ "signal-exit": "^3.0.2" } }, + "retry-request": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", + "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "requires": { + "debug": "^4.1.1", + "extend": "^3.0.2" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + } + } + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -10090,7 +10675,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -10227,8 +10811,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-support": { "version": "0.5.13", @@ -10292,6 +10875,11 @@ "dev": true, "optional": true }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -10306,7 +10894,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -10337,7 +10924,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -10346,7 +10932,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -10372,8 +10957,7 @@ "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, "strtok3": { "version": "7.0.0", @@ -10710,6 +11294,11 @@ } } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", @@ -11053,7 +11642,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, "requires": { "prelude-ls": "~1.1.2" } @@ -11093,6 +11681,16 @@ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, + "uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==" + }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -11104,6 +11702,11 @@ "which-boxed-primitive": "^1.0.2" } }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -11174,8 +11777,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utils-merge": { "version": "1.0.1", @@ -11242,6 +11844,20 @@ "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.8.tgz", "integrity": "sha512-lNR9aAefbGPpHO7AEnY0hCFjz1eTkWCXYvkTRrTHs9qv8zJp+SkVYpzfLIFXQQiG3tVvbNFQgVg2bQS8YGgxyw==" }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -11318,14 +11934,12 @@ "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -11335,8 +11949,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "write": { "version": "1.0.3", @@ -11378,17 +11991,20 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==" }, + "xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index d825537f..397e1aec 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "node": "^14.0" }, "dependencies": { + "@google-cloud/dialogflow": "^5.7.0", "@tech-matters/serverless-helpers": "3.0.5", "@twilio-labs/serverless-runtime-types": "2.2.3", "@twilio/runtime-handler": "1.2.4", From 7968889a9eed7b5bcae0da8edb8955ef68911915 Mon Sep 17 00:00:00 2001 From: Gianfranco Paoloni Date: Fri, 17 Mar 2023 19:27:10 +0100 Subject: [PATCH 13/13] Lint --- functions/captureChannelWithBot.protected.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/functions/captureChannelWithBot.protected.ts b/functions/captureChannelWithBot.protected.ts index 56487214..34e6393d 100644 --- a/functions/captureChannelWithBot.protected.ts +++ b/functions/captureChannelWithBot.protected.ts @@ -26,7 +26,6 @@ import { success, } from '@tech-matters/serverless-helpers'; import dialogflow from '@google-cloud/dialogflow'; -import type { MessageInstance } from 'twilio/lib/rest/chat/v2/service/channel/message'; type EnvVars = { CHAT_SERVICE_SID: string;