├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin └── chat-message-streaming-examples.ts ├── cdk.json ├── images └── full-arch.png ├── jest.config.js ├── lib └── chat-message-streaming-examples-stack.ts ├── package-lock.json ├── package.json ├── src ├── common-util │ ├── index.js │ ├── lib │ │ └── log.js │ └── package.json └── lambda │ ├── digitalChannelHealthCheck │ ├── index.js │ ├── lib │ │ ├── facebook.js │ │ └── whatsapp.js │ ├── package-lock.json │ └── package.json │ ├── inboundMessageHandler │ ├── index.js │ ├── lib │ │ ├── handlers │ │ │ ├── facebook.js │ │ │ ├── sms.js │ │ │ └── whatsapp.js │ │ ├── inboundHelper.js │ │ └── redact.js │ ├── package-lock.json │ └── package.json │ └── outboundMessageHandler │ ├── index.js │ ├── lib │ ├── handlers │ │ ├── facebook.js │ │ ├── sms.js │ │ └── whatsapp.js │ └── outboundHelper.js │ ├── package-lock.json │ └── package.json ├── test └── chat-message-streaming-examples.test.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # *.js 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | /.idea/ 6 | 7 | # CDK asset staging directory 8 | .cdk.staging 9 | cdk.out 10 | 11 | .DS_Store 12 | packaged.yml 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.1.2] - 2023-05-19 8 | -update dependencies 9 | 10 | ## [1.1.1] - 2023-04-18 11 | -update dependencies 12 | 13 | ## [1.1] - 2023-02-09 14 | 15 | Implementation of Whatsapp Channel 16 | 17 | ### Added 18 | - src/lambda/digitalChannelHealthCheck/lib/whatsapp.js to process WhatsApp webhook health check 19 | - src/lambda/inboundMessageHandler/lib/handlers/whatsapp.js to process incoming WhatsApp messages 20 | - src/lambda/outboundMessageHandler/lib/handlers/whatsapp.js to process outgoing WhatsApp messages 21 | ### Changed 22 | - lib/chat-message-streaming-examples-stack.ts to deploy infrastructure with WhatsApp support 23 | - src/lambda/digitalChannelHealthCheck/index.js to provide WhatsApp webhook health check response 24 | - src/lambda/inboundMessageHandler/index.js to add WhatsApp webhook for incoming messages 25 | - src/lambda/inboundMessageHandler/lib/inboundHelper.js to add WhatsApp channel SNS mapping 26 | - src/lambda/outboundMessageHandler/index.js to add WhatsApp channel support for outbound messaging 27 | 28 | ## [1.0.0] - 2022-09-09 29 | ### Added 30 | - redact.js to add in PII redaction functionality 31 | 32 | ### Changed 33 | - inboundHelper.js to add in logic for turning on redaction capabilities 34 | - chat-message-streaming-examples-stack.ts to give permissions to Amazon Comprehend 35 | - full-arch.png to include call to Amazon Comprehend 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Message streaming API starter projects 2 | 3 | ## Prerequisite 4 | 5 | ### General 6 | 7 | - Create a simple contact flow which routes the contact to the appropriate queue. 8 | - (You can use the sample contact flow for this example.) 9 | - [An Amazon Connect Instance](https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html) 10 | - [An Amazon Connect Contact Flow setup for Amazon Connect (including Disconnect flow) ](https://docs.aws.amazon.com/connect/latest/adminguide/chat.html#example-chat-scenario) 11 | - [AWS CLI setup in your local environment](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) 12 | 13 | ### SMS 14 | 15 | For detailed steps please visit the [blog](https://aws.amazon.com/blogs/contact-center/building-personalized-customer-experiences-over-sms-through-amazon-connect/) 16 | 17 | ### FB 18 | 19 | Please refer to [this blog](https://aws.amazon.com/blogs/contact-center/adding-digital-messaging-channels-to-your-amazon-connect-contact-center/) on the steps to create a Facebook messenger channel. 20 | 21 | ## Install AWS CDK 22 | 23 | `npm -g install typescript` 24 | `npm install -g aws-cdk` 25 | 26 | `cdk bootstrap aws://ACCOUNT_ID/AWS_REGION` 27 | 28 | ## Deployment commands 29 | 30 | - `npm install` 31 | 32 | - `cd src/lambda/inboundMessageHandler` 33 | 34 | - `npm install` 35 | 36 | - `cd ../../..` 37 | 38 | - `cd src/lambda/outboundMessageHandler` 39 | 40 | - `npm install` 41 | 42 | - `cd ../../..` 43 | 44 | - `cd src/lambda/digitalChannelHealthCheck` 45 | 46 | - `npm install` 47 | 48 | - `cd ../../..` 49 | 50 | - CDK deploy using your CLI profile (if you use default profile, don't need to specify the profile flag). Pass in the context required for the cdk stack. 51 | - If you are deploying SMS channel you need to supply the `pinpointAppId` and `smsNumber` variables. 52 | - If you are deploying the Facebook channel you need to supply the `fbSecretArn` variable. 53 | - If you are deploying the WhatsApp channel you need to supply the `waSecretArn` variable. 54 | - If you are deploying PII redaction with any channel you need to supply the `piiRedactionTypes` variable. 55 | 56 | ### Deploy SMS channel only 57 | 58 | ```bash 59 | cdk deploy \ 60 | --context amazonConnectArn= \ 61 | --context contactFlowId= \ 62 | --context smsNumber= \ 63 | --context pinpointAppId= 64 | ``` 65 | 66 | ### Deploy FB Channel only 67 | 68 | ```bash 69 | cdk deploy \ 70 | --context amazonConnectArn= \ 71 | --context contactFlowId= \ 72 | --context fbSecretArn= 73 | ``` 74 | 75 | ### Deploy WhatsApp Channel only 76 | 77 | ```bash 78 | cdk deploy \ 79 | --context amazonConnectArn= \ 80 | --context contactFlowId= \ 81 | --context waSecretArn= 82 | ``` 83 | 84 | ### Deploy BOTH SMS and FB channel 85 | 86 | ```bash 87 | cdk deploy \ 88 | --context amazonConnectArn= \ 89 | --context contactFlowId= \ 90 | --context smsNumber= \ 91 | --context pinpointAppId= \ 92 | --context fbSecretArn= 93 | ``` 94 | 95 | ### Deploy FB channel only with PII redaction 96 | ```bash 97 | cdk deploy \ 98 | --context amazonConnectArn= \ 99 | --context contactFlowId= \ 100 | --context fbSecretArn= \ 101 | --context piiRedactionTypes="" 102 | ``` 103 | 104 | ## Architecture 105 | 106 | ![](images/full-arch.png) 107 | 108 | ### Customer chat path 109 | 110 | 1. Customer starts chat/sends message 111 | 2. Chat message is delivered through Amazon Pinpoint (SMS message) or Amazon API Gateway (digital channels such as Facebook messenger). 112 | 3. AWS Lambda records the chat session context in Amazon DynamoDB 113 | 4. If PII redaction is enabled, AWS Lambda sends the chat message to Amazon Comprehend to detect PII data, then uses the response to filter the message before sending it to Amazon Connect 114 | 5. AWS Lambda sends the chat message to Amazon Connect 115 | 116 | ### Agent chat path 117 | 118 | 1. Agent sends reply message 119 | 2. Chat message is delivered through Amazon Simple Notification Service to AWS Lambda 120 | 3. AWS Lambda looks up the chat session context in Amazon DynamoDB 121 | 4. AWS Lambda sends the chat message the source application directly through APIs (Facebook messenger) or Amazon Pinpoint (SMS message) 122 | 123 | ## Repository structure 124 | 125 | ### CDK 126 | 127 | - `lib/chat-message-streaming-examples-stack.ts` is where your CDK application’s main stack is defined. The stack defines all the AWS infrastructure resources such as API Gateway, Dynamo DB, SNS and Lambda. The code for Lambda is defined in the `src/lambda` directory. 128 | - `bin/chat-message-streaming-examples.ts` is the entrypoint of the CDK application. It will load the stack defined in `lib/chat-message-streaming-examples-stack.ts`. 129 | - `package.json` is your npm module manifest. It includes information like the name of your app, version, dependencies and build scripts like “watch” and “build” (package-lock.json is maintained by npm) 130 | - `cdk.json` tells the toolkit how to run your app. 131 | - `tsconfig.json` your project’s typescript configuration 132 | - `.gitignore` and `.npmignore` tell git and npm which files to include/exclude from source control and when publishing this module to the package manager. 133 | - `node_modules` is maintained by npm and includes all your project’s dependencies. 134 | 135 | ### Lambda 136 | 137 | The Lambda function code is defined in the `src/lambda` function. We have three Lambda functions 138 | 139 | ####`src/lambda/inboundMessageHandler` 140 | This Lambda function is responsible for the intake of messages from a third party. The entry point will take messages from SNS/Pinpoint for the SMS channel and from API Gateway for Digital channels. Each integrated channel will have a channel handler in which the conversion from the 3rd party message format to a format that Amazon Connect expects will be performed. This Lambda is also responsible for starting new chat contacts in Amazon Connect and the orchistration of mapping a 3rd party chat to a chat in Amazon Connect. DynamoDB is used to store ContactID of the Amazon Connect chat, participant information from Amazon Connect, and the vendor's ID given to us by the third party. Any subsequent messages received from the 3rd party with the same vendor ID will be sent to the respective Amazon Connect chat as indicated in the DynamoDB table. 141 | If PII redaction is enabled, the Lmabda function sends all customer chat messages to Amazon Comprehend for PII detection, then uses the response to filter the message by replacing selected PII data with ``. 142 | 143 | ####`src/lambda/outboundMessageHandler` 144 | This Lambda function is responsible for messages originating from Amazon Connect with the destination of a third party channel. The ContactID from the message will be used to look up the VendorID and the appropriate channel in DynamoDB. The message is then passed on to the appropriate channel handler for message transformation and invocation of the respective 3rd party API. 145 | 146 | ####`src/lambda/digitalChannelHealthCheck` 147 | This Lambda function is used for digital channels that require a health check endpoint. Health checks come in from the API Gateway endpoint and are routed to the appropriate channel handler to respond to the third party with their expected payload. 148 | 149 | We have defined a common file structure for lambda functions: 150 | 151 | ``` 152 | ├── index.js // Lambda entry point 153 | ├── lib 154 | │   ├── handlers 155 | │   │   ├── .js messages // Channel handler(s) 156 | │   │   └── ... 157 | │   │   └── ... 158 | │   │   └── ... 159 | │   └── Helper.js // Helper module 160 | ├── package-lock.json 161 | └── package.json 162 | 163 | ``` 164 | 165 | ## Video walkthrough 166 | 167 | Please feel free to watch our webinar where we do a video walkthrough of the project. 168 | 169 | [![Webinar](https://img.youtube.com/vi/2_81YdVJbEw/0.jpg)](https://www.youtube.com/watch?v=2_81YdVJbEw) 170 | 171 | 172 | ## Troubleshooting 173 | 174 | ### SDK errors 175 | 176 | If the method does not exist or you are getting other SDK errors, please check you are using the latest SDK which includes the [new APIs](https://docs.aws.amazon.com/connect/latest/adminguide/chat-message-streaming.html). 177 | 178 | ### Message not getting published to SNS 179 | 180 | - Check the SNS resource based policy to check Amazon Connect service has permissions ( `sns:publish` ) to publish messages to it. 181 | 182 | ```json 183 | { 184 | "Version": "2008-10-17", 185 | "Id": "__default_policy_ID", 186 | "Statement": [ 187 | { 188 | "Sid": "__default_statement_ID", 189 | "Effect": "Allow", 190 | "Principal": { 191 | "Service": "connect.amazonaws.com" 192 | }, 193 | "Action": ["SNS:Publish"], 194 | "Resource": "arn:aws:sns:us-west-2:xxxxxxxxxxxx:yyyyyyyy" 195 | } 196 | ] 197 | } 198 | ``` 199 | 200 | - Check that your SNS topic is a standard topic not FIFO topic (CDK by default deploys with the standard topic) 201 | 202 | ### Contact flow execution not starting 203 | 204 | When a user is using streaming API in place of websockets, they will need to send a connection acknowledgment event (synonymous to connecting to websocket) and only after that contact flow execution will begin. It is expected that customers will call CreateParticipantConnection after they have called StartContactStreaming API to mark “Customer” as connected. This will ensure messages are sent when customer has confirmed that they are ready to receive. 205 | 206 | ### Throttling Exception (HTTP Error Code : 429/TooManyRequests) 207 | 208 | Default TPS imits applied – 5,8 and 2,5 for StartContactStreaming and StopContactStreaming respectively. These are soft limits and can be increased. 209 | 210 | ### CreateParticipantConnection API throwing exception when “ConnectParticipant” Boolean is passed as true 211 | 212 | There are two scenario when this would fail. Either - Streaming is not enabled on the Chat or participant calling the CreateParticipantConnection API is not Customer. 213 | 214 | ### StartContact Streaming API throwing exception when providing correct SNS ARN 215 | 216 | The SNS ARN is malformed, or has different region than the region of Connect Instance. Please note: You might also see error as there is hard limit of max 2 streaming endpoints per chat and you are trying to add more than that. 217 | 218 | 219 | ## FAQ 220 | 221 | **1. How will bi-directional SMS via those API's render interactive messaging?** 222 | 223 | Out of the box SMS does not support the interactive messaging as it is plain text. Other digital channels such as FB has capability to support this (however, you would need to extend the starter project on github to achieve this). 224 | 225 | **2. How can I structure my chat experience so that I identify that this message is 'SMS' channel?** 226 | 227 | In the [InboundContactHandler Lambda function on line 164](https://github.com/amazon-connect/amazon-connect-message-streaming-examples/blob/main/src/lambda/inboundMessageHandler/lib/inboundHelper.js#L164), when the StartChatContact is invoked, channel is passed through as a contact attribute. In the contact flow you can check via the contact attribute `chatframework_Channel` 228 | 229 | **3. Which account to use for logging into Facebook developer account?** 230 | 231 | We suggest you to use an existing Facebook account for testing purposes. 232 | 233 | **4. At step 10, what to do with the login review/submit for review?** 234 | 235 | If you are trying this project for testing purpose, you can skip the submit for review step and create the page. 236 | 237 | **5. How to install the npm module?** 238 | 239 | Refer to the [installing and updating] section from the following link : https://github.com/nvm-sh/nvm#intro 240 | 241 | **6. How to fix the permission denied error if you run the following bash command : npm -g install typescript?** 242 | 243 | Use the following command to install the latest version of nvm 244 | nvm install --lts 245 | 246 | **7. How to pass AWS Key ID and Secret Key while executing the following command : cdk bootstrap aws://ACCOUNT_ID/AWS_REGION?** 247 | 248 | Refer to the following link with detailed guidance on how to use aws configure command to pass the AWS Key ID and Secret Key: 249 | https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html 250 | 251 | **8. Where can I find the supported PII Types?** 252 | 253 | Refer to the following link with detailed PII Type information: 254 | https://docs.aws.amazon.com/comprehend/latest/dg/how-pii.html 255 | 256 | 257 | 258 | 259 | 260 | 261 | ## Security 262 | 263 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 264 | 265 | ## License 266 | 267 | This library is licensed under the MIT-0 License. See the LICENSE file. 268 | -------------------------------------------------------------------------------- /bin/chat-message-streaming-examples.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | // SPDX-License-Identifier: MIT-0 5 | import 'source-map-support/register'; 6 | import * as cdk from '@aws-cdk/core'; 7 | import { ChatMessageStreamingExamplesStack } from '../lib/chat-message-streaming-examples-stack'; 8 | 9 | const app = new cdk.App(); 10 | new ChatMessageStreamingExamplesStack(app, 'ChatMessageStreamingExamplesStack', { 11 | /* If you don't specify 'env', this stack will be environment-agnostic. 12 | * Account/Region-dependent features and context lookups will not work, 13 | * but a single synthesized template can be deployed anywhere. */ 14 | 15 | /* Uncomment the next line to specialize this stack for the AWS Account 16 | * and Region that are implied by the current CLI configuration. */ 17 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 18 | 19 | /* Uncomment the next line if you know exactly what Account and Region you 20 | * want to deploy the stack to. */ 21 | // env: { account: '123456789012', region: 'us-east-1' }, 22 | 23 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 24 | }); 25 | -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/chat-message-streaming-examples.ts", 3 | "context": { 4 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 5 | "@aws-cdk/core:enableStackNameDuplicates": "true", 6 | "aws-cdk:enableDiffNoFail": "true", 7 | "@aws-cdk/core:stackRelativeExports": "true", 8 | "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, 9 | "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, 10 | "@aws-cdk/aws-kms:defaultKeyPolicies": true, 11 | "@aws-cdk/aws-s3:grantWriteWithoutAcl": true, 12 | "@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true, 13 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 14 | "@aws-cdk/aws-efs:defaultEncryptionAtRest": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /images/full-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-connect/amazon-connect-message-streaming-examples/fb5d659fee5ee14b438c25599608f4b57a1ced90/images/full-arch.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /lib/chat-message-streaming-examples-stack.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import * as cdk from '@aws-cdk/core'; 4 | import * as lambda from '@aws-cdk/aws-lambda'; 5 | import * as dynamodb from '@aws-cdk/aws-dynamodb'; 6 | import * as sns from '@aws-cdk/aws-sns'; 7 | import * as subscriptions from '@aws-cdk/aws-sns-subscriptions'; 8 | import * as iam from '@aws-cdk/aws-iam'; 9 | import * as path from 'path'; 10 | import * as apigw2 from '@aws-cdk/aws-apigatewayv2'; 11 | import * as apigw2i from '@aws-cdk/aws-apigatewayv2-integrations'; 12 | import { Duration } from '@aws-cdk/core'; 13 | 14 | export class ChatMessageStreamingExamplesStack extends cdk.Stack { 15 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 16 | super(scope, id, props); 17 | 18 | // The code that defines your stack goes here 19 | 20 | // Need deployment mechanism to check if they are deploying SMS or FB or Both demos and validate on that 21 | 22 | // Get environment variables from context 23 | 24 | const amazonConnectArn = this.node.tryGetContext("amazonConnectArn"); 25 | const contactFlowId = this.node.tryGetContext("contactFlowId"); 26 | const pinpointAppId = this.node.tryGetContext("pinpointAppId"); 27 | const smsNumber = this.node.tryGetContext("smsNumber"); 28 | const fbSecretArn = this.node.tryGetContext("fbSecretArn"); 29 | const waSecretArn = this.node.tryGetContext("waSecretArn"); 30 | const piiRedactionTypes = this.node.tryGetContext("piiRedactionTypes"); 31 | let enableFB = false; 32 | let enableWhatsApp = false; 33 | let enableSMS = false; 34 | let enablePII = false; 35 | 36 | // Validating that environment variables are present 37 | if(amazonConnectArn === undefined){ 38 | throw new Error("Missing amazonConnectArn in the context"); 39 | } 40 | 41 | if(contactFlowId === undefined){ 42 | throw new Error("Missing Amazon Connect Contact flow Id in the context"); 43 | } 44 | 45 | if(pinpointAppId === undefined && smsNumber === undefined){ 46 | enableSMS = false; 47 | } else if (pinpointAppId !== undefined && smsNumber === undefined){ 48 | throw new Error("Missing smsNumber in the context"); 49 | } else if (pinpointAppId === undefined && smsNumber !== undefined){ 50 | throw new Error("Missing pinpointAppId in the context"); 51 | } else { 52 | enableSMS = true; 53 | } 54 | 55 | if(fbSecretArn != undefined){ 56 | enableFB = true; 57 | } 58 | 59 | if(waSecretArn != undefined){ 60 | enableWhatsApp = true; 61 | } 62 | 63 | if(piiRedactionTypes != undefined){ 64 | if (piiRedactionTypes) { 65 | enablePII = true; 66 | } else { 67 | throw new Error("piiRedactionTypes cannot be empty, expecting comma separated values of AWS Comprehend PII types"); 68 | } 69 | } 70 | 71 | 72 | if(enableWhatsApp === false && enableFB === false && enableSMS === false){ 73 | throw new Error("Please enable at least one channel, SMS, Facebook or WhatsApp. You can do so by providing fbSecretArn in the context to enable Facebook, waSecretArn in the context to enable WhatsApp or by providing pinpointAppId and smsNumber to enable SMS channel"); 74 | } 75 | 76 | const debugLog = new cdk.CfnParameter(this, 'debugLog', { 77 | allowedValues: ['true', 'false'], 78 | default: 'false', 79 | type: 'String', 80 | description: 81 | 'Setting to enable debug level logging in lambda functions. Recommended to turn this off in production.', 82 | }); 83 | 84 | // pinpoint project will not be in cdk - phone number has to be manually claimed 85 | 86 | // DDB - need GSI 87 | 88 | // Dynamo DB table 89 | 90 | const chatContactDdbTable = new dynamodb.Table(this, 'chatTable', { 91 | partitionKey: { 92 | name: 'contactId', 93 | type: dynamodb.AttributeType.STRING, 94 | }, 95 | timeToLiveAttribute: 'date', 96 | billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, 97 | removalPolicy: cdk.RemovalPolicy.DESTROY, 98 | }); 99 | 100 | // Dynamo DB table GSI 101 | // vendorId is phone number or facebook user id 102 | 103 | const vendorIdChannelIndexName = 'vendorId-index'; 104 | chatContactDdbTable.addGlobalSecondaryIndex({ 105 | indexName: vendorIdChannelIndexName, 106 | partitionKey: { 107 | name: 'vendorId', 108 | type: dynamodb.AttributeType.STRING, 109 | }, 110 | sortKey: { 111 | name: 'channel', 112 | type: dynamodb.AttributeType.STRING, 113 | }, 114 | }); 115 | 116 | let smsOutboundMsgStreamingTopic; 117 | let smsOutboundMsgStreamingTopicStatement; 118 | 119 | if(enableSMS){ 120 | // outbound SNS topic 121 | smsOutboundMsgStreamingTopic = new sns.Topic( 122 | this, 123 | 'smsOutboundMsgStreamingTopic', 124 | {} 125 | ); 126 | 127 | smsOutboundMsgStreamingTopicStatement = new iam.PolicyStatement({ 128 | actions: [ 129 | 'sns:Subscribe', 130 | 'sns:Publish' 131 | ], 132 | principals: [new iam.ServicePrincipal('connect.amazonaws.com')], 133 | resources: [smsOutboundMsgStreamingTopic.topicArn], 134 | }); 135 | 136 | smsOutboundMsgStreamingTopic.addToResourcePolicy(smsOutboundMsgStreamingTopicStatement) 137 | } 138 | 139 | 140 | let digitalOutboundMsgStreamingTopic; 141 | let digitalOutboundMsgStreamingTopicStatement; 142 | 143 | if(enableFB || enableWhatsApp){ 144 | digitalOutboundMsgStreamingTopic = new sns.Topic( 145 | this, 146 | 'digitalOutboundMsgStreamingTopic', 147 | {} 148 | ); 149 | 150 | digitalOutboundMsgStreamingTopicStatement = new iam.PolicyStatement({ 151 | actions: [ 152 | 'sns:Subscribe', 153 | 'sns:Publish' 154 | ], 155 | principals: [new iam.ServicePrincipal('connect.amazonaws.com')], 156 | resources: [digitalOutboundMsgStreamingTopic.topicArn], 157 | }); 158 | 159 | digitalOutboundMsgStreamingTopic.addToResourcePolicy(digitalOutboundMsgStreamingTopicStatement); 160 | } 161 | 162 | 163 | // Inbound Lambda function 164 | const inboundMessageFunction = new lambda.Function( 165 | this, 166 | 'inboundMessageFunction', 167 | { 168 | runtime: lambda.Runtime.NODEJS_14_X, 169 | handler: 'index.handler', 170 | code: lambda.Code.fromAsset( 171 | path.resolve(__dirname, '../src/lambda/inboundMessageHandler') 172 | ), 173 | timeout: Duration.seconds(120), 174 | memorySize: 512, 175 | environment: { 176 | FB_SECRET: fbSecretArn, 177 | WA_SECRET: waSecretArn, 178 | CONTACT_TABLE: chatContactDdbTable.tableName, 179 | AMAZON_CONNECT_ARN: amazonConnectArn, 180 | CONTACT_FLOW_ID: contactFlowId, 181 | DIGITAL_OUTBOUND_SNS_TOPIC: (digitalOutboundMsgStreamingTopic !== undefined ? digitalOutboundMsgStreamingTopic.topicArn : "" ), 182 | SMS_OUTBOUND_SNS_TOPIC: (smsOutboundMsgStreamingTopic !== undefined ? smsOutboundMsgStreamingTopic.topicArn : "" ), 183 | VENDOR_ID_CHANNEL_INDEX_NAME: vendorIdChannelIndexName, 184 | DEBUG_LOG: debugLog.valueAsString, 185 | PII_DETECTION_TYPES: (piiRedactionTypes !== undefined ? piiRedactionTypes : "" ) 186 | }, 187 | } 188 | ); 189 | 190 | // Inbound SNS topic (for SMS) 191 | let inboundSMSTopic: sns.Topic; 192 | 193 | if(enableSMS){ 194 | inboundSMSTopic = new sns.Topic(this, 'InboundSMSTopic', {}); 195 | inboundSMSTopic.addSubscription( 196 | new subscriptions.LambdaSubscription(inboundMessageFunction) 197 | ); 198 | new cdk.CfnOutput(this, 'SmsInboundTopic', { 199 | value: inboundSMSTopic.topicArn.toString(), 200 | }); 201 | } 202 | 203 | if(enablePII){ 204 | inboundMessageFunction.addToRolePolicy( 205 | new iam.PolicyStatement({ 206 | actions: ['comprehend:DetectPiiEntities'], 207 | resources: ['*'], 208 | effect: iam.Effect.ALLOW, 209 | }) 210 | ); 211 | } 212 | 213 | if(enableFB){ 214 | inboundMessageFunction.addToRolePolicy( 215 | new iam.PolicyStatement({ 216 | actions: ['secretsmanager:GetSecretValue'], 217 | resources: [fbSecretArn], 218 | effect: iam.Effect.ALLOW, 219 | }) 220 | ); 221 | } 222 | if(enableWhatsApp){ 223 | inboundMessageFunction.addToRolePolicy( 224 | new iam.PolicyStatement({ 225 | actions: ['secretsmanager:GetSecretValue'], 226 | resources: [waSecretArn], 227 | effect: iam.Effect.ALLOW, 228 | }) 229 | ); 230 | } 231 | 232 | 233 | inboundMessageFunction.addToRolePolicy( 234 | new iam.PolicyStatement({ 235 | actions: ['connect:StartChatContact'], 236 | resources: [ 237 | `${this.node.tryGetContext("amazonConnectArn")}/contact-flow/${this.node.tryGetContext("contactFlowId")}`, 238 | ], 239 | effect: iam.Effect.ALLOW, 240 | }) 241 | ); 242 | 243 | inboundMessageFunction.addToRolePolicy( 244 | new iam.PolicyStatement({ 245 | actions: ['connect:StartContactStreaming'], 246 | resources: [`${this.node.tryGetContext("amazonConnectArn")}/contact/*`], 247 | effect: iam.Effect.ALLOW, 248 | }) 249 | ); 250 | 251 | inboundMessageFunction.addToRolePolicy( 252 | new iam.PolicyStatement({ 253 | actions: [ 254 | 'dynamodb:PutItem', 255 | 'dynamodb:GetItem', 256 | 'dynamodb:Scan', 257 | 'dynamodb:Query', 258 | 'dynamodb:UpdateItem', 259 | ], 260 | resources: [ 261 | chatContactDdbTable.tableArn, 262 | `${chatContactDdbTable.tableArn}/index/${vendorIdChannelIndexName}`, 263 | ], 264 | effect: iam.Effect.ALLOW, 265 | }) 266 | ); 267 | 268 | // SNS topic filter rules (filter by attribute at the topic level) 269 | // outbound Lambda function 270 | const outboundMessageFunction = new lambda.Function( 271 | this, 272 | 'outboundMessageFunction', 273 | { 274 | runtime: lambda.Runtime.NODEJS_14_X, 275 | handler: 'index.handler', 276 | code: lambda.Code.fromAsset( 277 | path.resolve(__dirname, '../src/lambda/outboundMessageHandler') 278 | ), 279 | timeout: Duration.seconds(60), 280 | memorySize: 512, 281 | environment: { 282 | CONTACT_TABLE: chatContactDdbTable.tableName, 283 | PINPOINT_APPLICATION_ID: pinpointAppId, 284 | FB_SECRET: fbSecretArn, 285 | WA_SECRET: waSecretArn, 286 | SMS_NUMBER: smsNumber, 287 | DEBUG_LOG: debugLog.valueAsString, 288 | }, 289 | } 290 | ); 291 | 292 | outboundMessageFunction.addToRolePolicy( 293 | new iam.PolicyStatement({ 294 | actions: ['mobiletargeting:SendMessages'], 295 | effect: iam.Effect.ALLOW, 296 | resources: [ 297 | `arn:aws:mobiletargeting:${this.region}:${this.account}:apps/${this.node.tryGetContext("pinpointAppId")}/messages`, 298 | ], 299 | }) 300 | ); 301 | 302 | if(enableFB){ 303 | outboundMessageFunction.addToRolePolicy( 304 | new iam.PolicyStatement({ 305 | actions: ['secretsmanager:GetSecretValue'], 306 | resources: [fbSecretArn], 307 | effect: iam.Effect.ALLOW, 308 | }) 309 | ); 310 | } 311 | if(enableWhatsApp){ 312 | outboundMessageFunction.addToRolePolicy( 313 | new iam.PolicyStatement({ 314 | actions: ['secretsmanager:GetSecretValue'], 315 | resources: [waSecretArn], 316 | effect: iam.Effect.ALLOW, 317 | }) 318 | ); 319 | } 320 | 321 | outboundMessageFunction.addToRolePolicy( 322 | new iam.PolicyStatement({ 323 | actions: ['dynamodb:GetItem', 'dynamodb:DeleteItem'], 324 | resources: [ 325 | chatContactDdbTable.tableArn, 326 | `${chatContactDdbTable.tableArn}/index/${vendorIdChannelIndexName}`, 327 | ], 328 | effect: iam.Effect.ALLOW, 329 | }) 330 | ); 331 | 332 | // health check Lambda 333 | let healthCheckFunction: lambda.Function; 334 | let digitalChannelMessageIntegration: apigw2i.HttpLambdaIntegration; 335 | let digitalChannelHealthCheckIntegration: apigw2i.HttpLambdaIntegration; 336 | let digitalChannelApi; 337 | 338 | if(enableFB || enableWhatsApp){ 339 | healthCheckFunction = new lambda.Function( 340 | this, 341 | 'healthCheckFunction', 342 | { 343 | runtime: lambda.Runtime.NODEJS_14_X, 344 | handler: 'index.handler', 345 | code: lambda.Code.fromAsset( 346 | path.resolve(__dirname, '../src/lambda/digitalChannelHealthCheck') 347 | ), 348 | environment: { 349 | DEBUG_LOG: debugLog.valueAsString, 350 | FB_SECRET: fbSecretArn, 351 | WA_SECRET: waSecretArn, 352 | }, 353 | } 354 | ); 355 | if(enableFB){ 356 | healthCheckFunction.addToRolePolicy( 357 | new iam.PolicyStatement({ 358 | actions: ['secretsmanager:GetSecretValue'], 359 | resources: [fbSecretArn], 360 | effect: iam.Effect.ALLOW, 361 | }) 362 | ); 363 | } 364 | if(enableWhatsApp){ 365 | healthCheckFunction.addToRolePolicy( 366 | new iam.PolicyStatement({ 367 | actions: ['secretsmanager:GetSecretValue'], 368 | resources: [waSecretArn], 369 | effect: iam.Effect.ALLOW, 370 | }) 371 | ); 372 | } 373 | // inbound API Gateway (digital channel) 374 | digitalChannelMessageIntegration = new apigw2i.HttpLambdaIntegration( 375 | 'inboundMessageFunction', inboundMessageFunction); 376 | 377 | // digitalChannelHealthCheckIntegration = new apigw2i.HttpLambdaIntegration({ 378 | digitalChannelHealthCheckIntegration = new apigw2i.HttpLambdaIntegration( 379 | 'healthCheckFunction', healthCheckFunction); 380 | 381 | digitalChannelApi = new apigw2.HttpApi(this, 'digitalChannelApi', { 382 | corsPreflight: { 383 | allowOrigins: ['*'], 384 | allowMethods: [ 385 | apigw2.CorsHttpMethod.OPTIONS, 386 | apigw2.CorsHttpMethod.POST, 387 | apigw2.CorsHttpMethod.GET, 388 | ], 389 | allowHeaders: ['Content-Type'], 390 | }, 391 | }); 392 | if(enableFB){ 393 | digitalChannelApi.addRoutes({ 394 | path: '/webhook/facebook', 395 | methods: [apigw2.HttpMethod.POST], 396 | integration: digitalChannelMessageIntegration, 397 | }); 398 | digitalChannelApi.addRoutes({ 399 | path: '/webhook/facebook', 400 | methods: [apigw2.HttpMethod.GET], 401 | integration: digitalChannelHealthCheckIntegration, 402 | }); 403 | new cdk.CfnOutput(this, 'FacebookApiGatewayWebhook', { 404 | value: digitalChannelApi.apiEndpoint.toString() + '/webhook/facebook', 405 | }); 406 | } 407 | 408 | if(enableWhatsApp){ 409 | digitalChannelApi.addRoutes({ 410 | path: '/webhook/whatsapp', 411 | methods: [apigw2.HttpMethod.POST], 412 | integration: digitalChannelMessageIntegration, 413 | }); 414 | digitalChannelApi.addRoutes({ 415 | path: '/webhook/whatsapp', 416 | methods: [apigw2.HttpMethod.GET], 417 | integration: digitalChannelHealthCheckIntegration, 418 | }); 419 | new cdk.CfnOutput(this, 'WhatsAppApiGatewayWebhook', { 420 | value: digitalChannelApi.apiEndpoint.toString() + '/webhook/whatsapp', 421 | }); 422 | } 423 | 424 | // Outbound lambda subscribe to streaming topic 425 | if(digitalOutboundMsgStreamingTopic){ 426 | digitalOutboundMsgStreamingTopic.addSubscription( 427 | new subscriptions.LambdaSubscription(outboundMessageFunction, { 428 | filterPolicy: { 429 | MessageVisibility: sns.SubscriptionFilter.stringFilter({ 430 | allowlist: ['CUSTOMER', 'ALL'], 431 | }), 432 | } 433 | }) 434 | ); 435 | } 436 | } 437 | 438 | if(smsOutboundMsgStreamingTopic){ 439 | smsOutboundMsgStreamingTopic.addSubscription( 440 | new subscriptions.LambdaSubscription(outboundMessageFunction, { 441 | filterPolicy: { 442 | MessageVisibility: sns.SubscriptionFilter.stringFilter({ 443 | allowlist: ['CUSTOMER', 'ALL'], 444 | }), 445 | } 446 | }) 447 | ); 448 | } 449 | 450 | } 451 | } 452 | 453 | // pinpoint project in cdk - phone number has to be manually claimed 454 | // inbound SNS topic (for SMS) 455 | // inbound API Gateway (digital channel) 456 | // inbound Lambda 457 | // DDB - 458 | // outbound SNS topic 459 | // outbound lambda 460 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-message-streaming-examples", 3 | "version": "1.0.0", 4 | "bin": { 5 | "chat-message-streaming-examples": "bin/chat-message-streaming-examples.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@aws-cdk/assert": "^1.105.0", 15 | "@types/jest": "^26.0.10", 16 | "@types/node": "^10.17.27", 17 | "aws-cdk": "^1.105.0", 18 | "jest": "^26.4.2", 19 | "ts-jest": "^26.2.0", 20 | "ts-node": "^9.0.0", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "@aws-cdk/aws-apigateway": "^1.135.0", 25 | "@aws-cdk/aws-apigatewayv2": "^1.135.0", 26 | "@aws-cdk/aws-apigatewayv2-integrations": "^1.135.0", 27 | "@aws-cdk/aws-dynamodb": "^1.135.0", 28 | "@aws-cdk/aws-iam": "^1.135.0", 29 | "@aws-cdk/aws-lambda": "^1.135.0", 30 | "@aws-cdk/aws-pinpoint": "^1.135.0", 31 | "@aws-cdk/aws-s3": "^1.135.0", 32 | "@aws-cdk/aws-s3-notifications": "^1.135.0", 33 | "@aws-cdk/aws-secretsmanager": "^1.135.0", 34 | "@aws-cdk/aws-sns": "^1.135.0", 35 | "@aws-cdk/aws-sns-subscriptions": "^1.135.0", 36 | "@aws-cdk/core": "^1.135.0", 37 | "source-map-support": "^0.5.16" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/common-util/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const log = require('./lib/log'); 4 | 5 | module.exports = { 6 | log, 7 | }; 8 | -------------------------------------------------------------------------------- /src/common-util/lib/log.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const isDebugEnabled = () => process.env.DEBUG_LOG === 'true'; 5 | 6 | const log = (level, msg, params) => { 7 | if (level === 'DEBUG' && !isDebugEnabled()) { 8 | return; 9 | } 10 | 11 | const logMsg = {}; 12 | logMsg.level = level; 13 | logMsg.message = msg; 14 | logMsg.params = 15 | params instanceof Error 16 | ? { message: params.toString(), stack: params.stack } 17 | : params; 18 | 19 | try { 20 | const vals = []; 21 | const message = JSON.stringify(logMsg, (key, value) => { 22 | if (typeof value === 'object' && value !== null) { 23 | if (vals.indexOf(value) !== -1) { 24 | try { 25 | return JSON.parse(JSON.stringify(value)); 26 | } catch (error) { 27 | return null; 28 | } 29 | } 30 | vals.push(value); 31 | } 32 | return value; 33 | }); 34 | 35 | console.log(message); 36 | } catch (err) { 37 | log('ERROR', 'Failure stringifing log message', { error: err }); 38 | } 39 | }; 40 | 41 | module.exports.debug = (msg, params) => log('DEBUG', msg, params); 42 | module.exports.info = (msg, params) => log('INFO', msg, params); 43 | module.exports.warn = (msg, params) => log('WARN', msg, params); 44 | module.exports.error = (msg, params) => log('ERROR', msg, params); 45 | -------------------------------------------------------------------------------- /src/common-util/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "common-util", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "MIT-0" 14 | } 15 | -------------------------------------------------------------------------------- /src/lambda/digitalChannelHealthCheck/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const { log } = require('common-util'); 4 | const fb = require('./lib/facebook'); 5 | const wa = require('./lib/whatsapp'); 6 | 7 | exports.handler = async (event) => { 8 | log.debug('Event', event); 9 | 10 | switch (event.rawPath) { 11 | case '/webhook/facebook': 12 | log.debug('Facebook channel detected.'); 13 | return await fb.handler(event); 14 | case '/webhook/whatsapp': 15 | log.debug('WhatsApp channel detected.'); 16 | return await wa.handler(event); 17 | default: 18 | log.warn( 19 | `Request path "${event.rawPath}" does not match any expected paths.` 20 | ); 21 | return { 22 | statusCode: 400, 23 | body: JSON.stringify({}), 24 | }; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/lambda/digitalChannelHealthCheck/lib/facebook.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const AWS = require('aws-sdk'); 4 | const secretManager = new AWS.SecretsManager(); 5 | 6 | const { log } = require('common-util'); 7 | let verifyToken = undefined; 8 | 9 | exports.handler = async (event) => { 10 | log.debug('Event', event); 11 | 12 | if (verifyToken === undefined) { 13 | await getFacebookSecrets(); 14 | } 15 | 16 | var queryParams = event.queryStringParameters; 17 | 18 | var rVerifyToken = queryParams['hub.verify_token']; 19 | 20 | if (rVerifyToken === verifyToken) { 21 | var challenge = queryParams['hub.challenge']; 22 | const response = { 23 | statusCode: 200, 24 | body: parseInt(challenge), 25 | }; 26 | return response; 27 | } else { 28 | const response = { 29 | statusCode: 200, 30 | body: JSON.stringify('Wrong validation token'), 31 | }; 32 | return response; 33 | } 34 | }; 35 | 36 | // Production improvement: add error handling in here 37 | const getFacebookSecrets = async () => { 38 | if (process.env.FB_SECRET) { 39 | const params = { 40 | SecretId: process.env.FB_SECRET, 41 | }; 42 | const response = await secretManager.getSecretValue(params).promise(); 43 | verifyToken = JSON.parse(response.SecretString).VERIFY_TOKEN; 44 | } else { 45 | verifyToken = null; 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /src/lambda/digitalChannelHealthCheck/lib/whatsapp.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const AWS = require('aws-sdk'); 4 | const secretManager = new AWS.SecretsManager(); 5 | 6 | const { log } = require('common-util'); 7 | let waVerifyToken = undefined; 8 | 9 | exports.handler = async (event) => { 10 | log.debug('Event', event); 11 | 12 | if (waVerifyToken === undefined) { 13 | await getWhatsAppSecrets(); 14 | } 15 | 16 | var queryParams = event.queryStringParameters; 17 | 18 | var rVerifyToken = queryParams['hub.verify_token']; 19 | 20 | if (rVerifyToken === waVerifyToken) { 21 | var challenge = queryParams['hub.challenge']; 22 | const response = { 23 | statusCode: 200, 24 | body: parseInt(challenge), 25 | }; 26 | return response; 27 | } else { 28 | const response = { 29 | statusCode: 200, 30 | body: JSON.stringify('Wrong access token for WhatsApp'), 31 | }; 32 | return response; 33 | } 34 | }; 35 | 36 | // Production improvement: add error handling in here 37 | const getWhatsAppSecrets = async () => { 38 | if (process.env.WA_SECRET) { 39 | const params = { 40 | SecretId: process.env.WA_SECRET, 41 | }; 42 | const response = await secretManager.getSecretValue(params).promise(); 43 | waVerifyToken = JSON.parse(response.SecretString).WA_VERIFY_TOKEN; 44 | } else { 45 | waVerifyToken = null; 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /src/lambda/digitalChannelHealthCheck/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "digitalchannelhealthcheck", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "MIT-0", 14 | "dependencies": { 15 | "common-util": "file:./../../common-util", 16 | "aws-sdk": "^2.1020.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lambda/inboundMessageHandler/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const { log } = require('common-util'); 5 | const sms = require('./lib/handlers/sms'); 6 | const fb = require('./lib/handlers/facebook'); 7 | const wa = require('./lib/handlers/whatsapp'); 8 | 9 | exports.handler = async (event) => { 10 | log.debug('Event', event); 11 | 12 | if (event.rawPath === undefined) { 13 | log.debug('SMS Request detected'); 14 | await processSnsRequest(event); 15 | } else { 16 | log.debug('Digital channel Request detected'); 17 | await processDigitalChannelRequest(event); 18 | } 19 | 20 | const response = { 21 | statusCode: 200, 22 | body: JSON.stringify({}), 23 | }; 24 | return response; 25 | }; 26 | 27 | const processDigitalChannelRequest = async (event) => { 28 | switch (event.rawPath) { 29 | case '/webhook/whatsapp': 30 | log.debug('WhatsApp channel detected.'); 31 | validRequest = await wa.validateRequest(event); 32 | if (!validRequest) { 33 | log.warn('Invalid payload signature'); 34 | return { 35 | statusCode: 403, 36 | body: 'Request validation failed', 37 | }; 38 | } 39 | log.debug('Process event body'); 40 | await wa.handler(event.body); 41 | break; 42 | case '/webhook/facebook': 43 | log.debug('Facebook channel detected.'); 44 | validRequest = await fb.validateRequest(event); 45 | if (!validRequest) { 46 | log.warn('Invalid payload signature'); 47 | return { 48 | statusCode: 403, 49 | body: 'Request validation failed', 50 | }; 51 | } 52 | log.debug('Process event body'); 53 | await fb.handler(event.body); 54 | break; 55 | default: 56 | log.warn( 57 | `Request path "${event.rawPath}" does not match any expected paths.` 58 | ); 59 | break; 60 | } 61 | }; 62 | 63 | const processSnsRequest = async (event) => { 64 | for (let i = 0; i < event.Records.length; i++) { 65 | const record = event.Records[i]; 66 | 67 | if (record.EventSource === 'aws:sns') { 68 | await sms.handler(record.Sns.Message); 69 | } 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /src/lambda/inboundMessageHandler/lib/handlers/facebook.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const { log } = require('common-util'); 5 | const crypto = require('crypto'); 6 | const inboundHelper = require('../inboundHelper'); 7 | const CHANNEL_TYPE = 'FACEBOOK'; 8 | const AWS = require('aws-sdk'); 9 | const secretManager = new AWS.SecretsManager(); 10 | const handler = async (messagePayloadString) => { 11 | log.debug('Facebook message handler'); 12 | 13 | const messagePayload = JSON.parse(messagePayloadString); 14 | 15 | await processMessagePayload(messagePayload); 16 | }; 17 | 18 | const processMessagePayload = async (messagePayload) => { 19 | const vendorIdParticipantMap = {}; 20 | const participantGetOrCreatePromises = []; 21 | 22 | // Get all participants for unique vendor ids 23 | // Docs: https://developers.facebook.com/docs/messenger-platform/reference/webhook-events 24 | for (let i = 0; i < messagePayload.entry.length; i++) { 25 | const entry = messagePayload.entry[i]; 26 | 27 | // One webhook event do not have messaging objects. Not supported. 28 | if (entry.messaging === undefined) { 29 | log.warn( 30 | 'Facebook "standby" webhook event is not supported. Unsubscribe from this webhook event to reduce traffic.' 31 | ); 32 | continue; 33 | } 34 | 35 | 36 | for (let j = 0; j < entry.messaging.length; j++) { 37 | const messaging = entry.messaging[j]; 38 | 39 | if (messaging.message === undefined) { 40 | log.warn( 41 | 'Facebook "read" webhook event is not supported. Unsubscribe from this webhook event to reduce traffic.' 42 | ); 43 | continue; 44 | } 45 | const vendorId = await getVendorId(messaging); 46 | await inboundHelper 47 | .getOrCreateParticipant(CHANNEL_TYPE, vendorId) 48 | .then((participant) => { 49 | vendorIdParticipantMap[vendorId] = participant; 50 | }) 51 | 52 | // participantGetOrCreatePromises.push( 53 | // inboundHelper 54 | // .getOrCreateParticipant(CHANNEL_TYPE, vendorId) 55 | // .then((participant) => { 56 | // vendorIdParticipantMap[vendorId] = participant; 57 | // }) 58 | // ); 59 | } 60 | } 61 | 62 | // Await getting all unique participants 63 | // await Promise.all(participantGetOrCreatePromises); 64 | log.debug('vendor id participant map', vendorIdParticipantMap); 65 | 66 | // Process each individual message 67 | for (let i = 0; i < messagePayload.entry.length; i++) { 68 | const entry = messagePayload.entry[i]; 69 | log.debug('Entry', entry); 70 | for (let j = 0; j < entry.messaging.length; j++) { 71 | const messaging = entry.messaging[j]; 72 | 73 | const vendorId = await getVendorId(messaging); 74 | 75 | const participant = vendorIdParticipantMap[vendorId]; 76 | 77 | log.debug('Message', messaging.message); 78 | log.debug('Participant', participant); 79 | await processMessage(messaging.message, participant); 80 | } 81 | } 82 | }; 83 | 84 | const processMessage = async (message, participant) => { 85 | if (message === undefined) { 86 | log.warn('Undefined message'); 87 | return; 88 | } 89 | 90 | // Do not accept message echos 91 | // https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/message-echoes 92 | if (message.is_echo == true) { 93 | log.warn( 94 | 'Facebook "message_echoes" webhook event is not supported. Unsubscribe from this webhook event to reduce traffic.' 95 | ); 96 | return; 97 | } 98 | 99 | // Support regular text based message 100 | if (message.text !== undefined) { 101 | await inboundHelper.sendMessage(participant, message.text); 102 | 103 | // Support gifs, images, thumbs up 104 | } else if (message.attachments !== undefined) { 105 | log.debug('Attachments found', message.attachments); 106 | 107 | for (let i = 0; i < message.attachments.length; i++) { 108 | const attachment = message.attachments[i]; 109 | // Attachment types from https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messages 110 | let attachmentMessage; 111 | switch (attachment.type) { 112 | case 'image': 113 | attachmentMessage = `User sent an image: ${attachment.payload.url}`; 114 | break; 115 | case 'video': 116 | attachmentMessage = `User sent a video: ${attachment.payload.url}`; 117 | break; 118 | case 'audio': 119 | attachmentMessage = `User sent an audio message: ${attachment.payload.url}`; 120 | break; 121 | case 'file': 122 | attachmentMessage = `User sent a file: ${attachment.payload.url}`; 123 | break; 124 | default: 125 | log.warn( 126 | `Facebook attachment type "${message.attachments[i].type}" not supported.` 127 | ); 128 | continue; 129 | } 130 | 131 | await inboundHelper.sendMessage(participant, attachmentMessage); 132 | } 133 | } else { 134 | log.warn('Unsupported message detected.', message); 135 | } 136 | }; 137 | 138 | const getVendorId = async (messaging) => { 139 | return messaging.sender.id; 140 | }; 141 | 142 | let appSecret = undefined; 143 | const validateRequest = async (request) => { 144 | if (appSecret === undefined) { 145 | await getFacebookSecrets(); 146 | } 147 | 148 | if (appSecret === null) { 149 | log.error('FB Secret not found. Cannot process record.'); 150 | return false; 151 | } 152 | 153 | const signature = request.headers['x-hub-signature']; 154 | 155 | if (signature === undefined) { 156 | log.warn('No signature found. Request invalid.'); 157 | return false; 158 | } 159 | const requestHash = signature.split('=')[1]; 160 | 161 | const payloadHash = crypto 162 | .createHmac('sha1', appSecret) 163 | .update(request.body) 164 | .digest('hex'); 165 | 166 | if (requestHash === payloadHash) { 167 | log.debug('Facebook Request Validation - Hash match'); 168 | return true; 169 | } else { 170 | log.debug('Facebook Request Validation - Hash does not match'); 171 | return false; 172 | } 173 | }; 174 | 175 | const getFacebookSecrets = async () => { 176 | if(process.env.FB_SECRET){ 177 | const params = { 178 | SecretId: process.env.FB_SECRET 179 | } 180 | const response = await secretManager.getSecretValue(params).promise(); 181 | appSecret = JSON.parse(response.SecretString).APP_SECRET 182 | } else { 183 | appSecret = null; 184 | } 185 | 186 | }; 187 | 188 | module.exports = { handler, validateRequest }; 189 | -------------------------------------------------------------------------------- /src/lambda/inboundMessageHandler/lib/handlers/sms.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const inboundHelper = require('../inboundHelper'); 5 | const CHANNEL_TYPE = 'SMS'; 6 | 7 | const handler = async (messageString) => { 8 | const message = JSON.parse(messageString); 9 | const participant = await inboundHelper.getOrCreateParticipant( 10 | CHANNEL_TYPE, 11 | getVendorId(message) 12 | ); 13 | 14 | await inboundHelper.sendMessage(participant, message.messageBody); 15 | }; 16 | 17 | const getVendorId = (message) => { 18 | return message.originationNumber; 19 | }; 20 | 21 | module.exports = { handler }; 22 | -------------------------------------------------------------------------------- /src/lambda/inboundMessageHandler/lib/handlers/whatsapp.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const { log } = require('common-util'); 5 | const crypto = require('crypto'); 6 | const inboundHelper = require('../inboundHelper'); 7 | const CHANNEL_TYPE = 'WHATSAPP'; 8 | const AWS = require('aws-sdk'); 9 | const secretManager = new AWS.SecretsManager(); 10 | 11 | const handler = async (messagePayloadString) => { 12 | log.debug('WhatsApp message handler'); 13 | 14 | const messagePayload = JSON.parse(messagePayloadString); 15 | log.debug('messagePayload.object:', messagePayload.object); 16 | 17 | await processWhatsAppMessagePayload(messagePayload); 18 | }; 19 | 20 | const processWhatsAppMessagePayload = async (messagePayload) => { 21 | const vendorIdParticipantMap = {}; 22 | log.debug('processWhatsAppMessagePayload'); 23 | // Get all participants for unique vendor ids 24 | // Docs: https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/components 25 | 26 | // Process each individual message 27 | for (const entry of messagePayload.entry) { 28 | log.debug('Entry', entry); 29 | for (const change of entry.changes) { 30 | if (change.value.messages === undefined) { 31 | log.info( 32 | 'Ignoring WhatsApp event, missing "messages" object' 33 | ); 34 | continue; 35 | } 36 | for (const [message, messageContent] of Object.entries(change.value.messages)) { 37 | const vendorId = await getWhatsAppVendorId(messageContent); 38 | await inboundHelper 39 | .getOrCreateParticipant(CHANNEL_TYPE, vendorId) 40 | .then((participant) => { 41 | vendorIdParticipantMap[vendorId] = participant; 42 | }) 43 | await processWhatsAppMessage(messageContent, vendorIdParticipantMap[vendorId]); 44 | } 45 | } 46 | } 47 | }; 48 | 49 | const processWhatsAppMessage = async (message, participant) => { 50 | log.debug("processMessage message: ", message) 51 | if (message === undefined) { 52 | log.warn('Undefined message'); 53 | return; 54 | } 55 | 56 | switch (message.type) { 57 | // Media types from https://developers.facebook.com/docs/whatsapp/cloud-api/reference/messages#media-object 58 | case 'audio': 59 | messageToSend = `User sent an audio message - unsupported`; 60 | break 61 | case 'button': 62 | messageToSend = `User sent button - unsupported`; 63 | break 64 | case 'context': 65 | messageToSend = `User sent context - unsupported`; 66 | break 67 | case 'document': 68 | messageToSend = `User sent a document - unsupported`; 69 | break 70 | case 'image': 71 | messageToSend = `User sent an image - unsupported`; 72 | break 73 | case 'interactive': 74 | messageToSend = `User sent an interactive message - unsupported `; 75 | break 76 | case 'location': 77 | messageToSend = `User sent their location: ${JSON.stringify(message.location)}`; 78 | break 79 | case 'reaction': 80 | messageToSend = `User reacted with emoji: ${message.reaction.emoji}`; 81 | break 82 | case 'sticker': 83 | messageToSend = `User sent a sticker - unsupported`; 84 | break 85 | case 'template': 86 | messageToSend = `User sent a template - unsupported`; 87 | break 88 | 89 | // Support regular text based message 90 | case 'text': 91 | messageToSend = message.text.body; 92 | break; 93 | default: 94 | log.warn( 95 | `WhatsApp message type "${message.type}" not supported.` 96 | ); 97 | }; 98 | await inboundHelper.sendMessage(participant, messageToSend); 99 | } 100 | 101 | const getWhatsAppVendorId = async (message) => { 102 | return message.from; 103 | }; 104 | 105 | let appSecret = undefined; 106 | const validateRequest = async (request) => { 107 | if (appSecret === undefined) { 108 | await getWhatsAppSecrets(); 109 | } 110 | 111 | if (appSecret === null) { 112 | log.error('App Secret not found. Cannot process record.'); 113 | return false; 114 | } 115 | 116 | const signature = request.headers['x-hub-signature-256']; 117 | 118 | if (signature === undefined) { 119 | log.warn('No signature found. Request invalid.'); 120 | return false; 121 | } 122 | const requestHash = signature.split('=')[1]; 123 | 124 | const payloadHash = crypto 125 | .createHmac('sha256', appSecret) 126 | .update(request.body) 127 | .digest('hex'); 128 | 129 | if (requestHash === payloadHash) { 130 | log.debug('WhatsApp Request Validation - Hash match'); 131 | return true; 132 | } else { 133 | log.debug('WhatsApp Request Validation - Hash does not match'); 134 | return false; 135 | } 136 | }; 137 | 138 | const getWhatsAppSecrets = async () => { 139 | if(process.env.WA_SECRET){ 140 | const params = { 141 | SecretId: process.env.WA_SECRET 142 | } 143 | const response = await secretManager.getSecretValue(params).promise(); 144 | appSecret = JSON.parse(response.SecretString).WA_APP_SECRET 145 | } else { 146 | appSecret = null; 147 | } 148 | 149 | }; 150 | 151 | module.exports = { handler, validateRequest }; 152 | -------------------------------------------------------------------------------- /src/lambda/inboundMessageHandler/lib/inboundHelper.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const AWS = require('aws-sdk'); 5 | const { log } = require('common-util'); 6 | const redact = require('./redact'); 7 | 8 | const connect = new AWS.Connect(); 9 | const connectParticipant = new AWS.ConnectParticipant(); 10 | const ddb = new AWS.DynamoDB.DocumentClient(); 11 | 12 | const ACKNOWLEDGED_EVENT_CONTENT_TYPE = 13 | 'application/vnd.amazonaws.connect.event.connection.acknowledged'; 14 | const TYPING_EVENT_CONTENT_TYPE = 15 | 'application/vnd.amazonaws.connect.event.typing'; 16 | 17 | const { 18 | CONTACT_TABLE, 19 | AMAZON_CONNECT_ARN, 20 | CONTACT_FLOW_ID, 21 | DIGITAL_OUTBOUND_SNS_TOPIC, 22 | SMS_OUTBOUND_SNS_TOPIC, 23 | VENDOR_ID_CHANNEL_INDEX_NAME, 24 | } = process.env; 25 | 26 | const channelSNSTopicMap = { 27 | SMS: SMS_OUTBOUND_SNS_TOPIC, 28 | FB: DIGITAL_OUTBOUND_SNS_TOPIC, 29 | WHATSAPP: DIGITAL_OUTBOUND_SNS_TOPIC, 30 | default: DIGITAL_OUTBOUND_SNS_TOPIC, 31 | }; 32 | 33 | //////////////////// 34 | // Public Methods // 35 | //////////////////// 36 | const getOrCreateParticipant = async (channel, vendorId) => { 37 | const existingParticipant = await checkForExistingParticipant( 38 | channel, 39 | vendorId 40 | ); 41 | 42 | // Condition to check 1/ there is an existing chat AND 2/ The chat has not completed (indicated by no S3 transcript) AND 3/ Chat is less than 24 hours old 43 | if ( 44 | existingParticipant !== null //&& 45 | //getCurrentTime() - existingParticipant.datetime < parseInt(SESSION_DURATION) 46 | ) { 47 | log.debug('Existing participant', existingParticipant); 48 | if (existingParticipant.connectionCredentials === undefined) { 49 | const connectionCreds = await createParticipantConnection( 50 | existingParticipant.ParticipantToken 51 | ); 52 | 53 | existingParticipant.connectionCredentials = connectionCreds; 54 | 55 | // Production improvement: Update database with connection credentials 56 | } 57 | return existingParticipant; 58 | } 59 | log.debug('creating participant', { channel, vendorId }); 60 | 61 | // If any of the above condition is not met, new chat contact is connected but could be linked together through previous/next/current contact Ids 62 | 63 | const participant = await addNewParticipant( 64 | channel, 65 | vendorId, 66 | existingParticipant 67 | ); 68 | log.debug('finished creating participant', { channel, vendorId }); 69 | 70 | 71 | return participant; 72 | }; 73 | 74 | const sendMessage = async (participant, message) => { 75 | let messageToSend = ''; 76 | if (process.env.PII_DETECTION_TYPES) { 77 | messageToSend = await redact.redactPII(message); 78 | log.debug(`redactPII returned: ${messageToSend}`); 79 | } 80 | else { 81 | messageToSend = message; 82 | } 83 | 84 | const params = { 85 | ConnectionToken: participant.connectionCredentials.token, 86 | Content: messageToSend, 87 | ContentType: 'text/plain', 88 | }; 89 | log.debug('Send message params', params); 90 | const result = await connectParticipant.sendMessage(params).promise(); 91 | log.debug('Send message result', result); 92 | }; 93 | 94 | const sendAttachment = async (participant, url) => { 95 | const params = { 96 | ConnectionToken: participant.connectionCredentials.token, 97 | Content: message, 98 | ContentType: 'text/plain', 99 | }; 100 | log.debug('Send message params', params); 101 | const result = await connectParticipant.sendMessage(params).promise(); 102 | log.debug('Send message result', result); 103 | }; 104 | 105 | const sendAcknowledgedEvent = async (participant) => { 106 | await sendEvent(participant, ACKNOWLEDGED_EVENT_CONTENT_TYPE); 107 | }; 108 | 109 | const sendTypingEvent = async (participant) => { 110 | await sendEvent(participant, TYPING_EVENT_CONTENT_TYPE); 111 | }; 112 | 113 | ///////////////////// 114 | // Private Methods // 115 | ///////////////////// 116 | const startContactStreaming = async (channel, participant) => { 117 | const snsTopic = 118 | channelSNSTopicMap[channel] !== undefined 119 | ? channelSNSTopicMap[channel] 120 | : channelSNSTopicMap.default; 121 | 122 | const streamingParams = { 123 | InstanceId: getConnectInstanceId(AMAZON_CONNECT_ARN), 124 | ContactId: participant.contactId, 125 | ChatStreamingConfiguration: { 126 | StreamingEndpointArn: snsTopic 127 | }, 128 | }; 129 | 130 | // Production improvement: error handling 131 | log.debug('Start Contact Streaming params', streamingParams); 132 | const streamingResult = await connect 133 | .startContactStreaming(streamingParams) 134 | .promise(); 135 | log.debug('Start Contact Streaming Result', streamingResult); 136 | 137 | }; 138 | 139 | const checkForExistingParticipant = async (channel, vendorId) => { 140 | const params = { 141 | TableName: CONTACT_TABLE, 142 | IndexName: VENDOR_ID_CHANNEL_INDEX_NAME, 143 | KeyConditionExpression: 'vendorId = :vendorId and channel = :channel', 144 | ExpressionAttributeValues: { 145 | ':vendorId': vendorId, 146 | ':channel': channel, 147 | }, 148 | }; 149 | log.debug('Check existing participant params', params); 150 | const result = await ddb.query(params).promise(); 151 | log.debug('Check existing participant result', result); 152 | 153 | if ( 154 | result.Items === undefined || 155 | result.Items === null || 156 | result.Items.length === 0 157 | ) { 158 | log.debug('Existing participant not found'); 159 | 160 | return null; 161 | } 162 | 163 | log.debug('Existing participant found', result.Items[0]); 164 | return result.Items[0]; 165 | }; 166 | 167 | const addNewParticipant = async (channel, vendorId, existingParticipant) => { 168 | const params = { 169 | ContactFlowId: CONTACT_FLOW_ID, 170 | InstanceId: getConnectInstanceId(AMAZON_CONNECT_ARN), 171 | ParticipantDetails: { 172 | DisplayName: vendorId, 173 | }, 174 | Attributes: { 175 | chatframework_Channel: channel, 176 | chatframework_VendorId: vendorId, 177 | }, 178 | }; 179 | 180 | log.debug('Create new chat params', params); 181 | const result = await connect.startChatContact(params).promise(); 182 | const startStreamingParams = { 183 | contactId: result.ContactId, 184 | participantId: result.ParticipantId 185 | } 186 | const startStreaming = await startContactStreaming(channel, startStreamingParams); 187 | log.debug('Create new chat results', result); 188 | 189 | const connectionCreds = await createParticipantConnection( 190 | result.ParticipantToken 191 | ); 192 | log.debug('Create participant connection results', connectionCreds); 193 | 194 | const updateResults = await updatePreviousConnectionData( 195 | existingParticipant, 196 | result 197 | ); 198 | 199 | const ddbItem = { 200 | vendorId, 201 | channel, 202 | contactId: result.ContactId, 203 | previousContactId: 204 | existingParticipant === null 205 | ? 'INITIAL_CHAT' 206 | : existingParticipant.previousContactId, 207 | s3Key: existingParticipant === null ? 'NONE' : existingParticipant.s3Key, 208 | nextContactId: 'CURRENT_CHAT', 209 | participantId: result.ParticipantId, 210 | participantToken: result.ParticipantToken, 211 | connectionCredentials: { 212 | token: connectionCreds.ConnectionToken, 213 | expiration: connectionCreds.Expiry, 214 | }, 215 | datetime: Date.now(), 216 | }; 217 | const ddbParams = { 218 | TableName: CONTACT_TABLE, 219 | Item: ddbItem, 220 | }; 221 | 222 | log.debug('Put participant info params', ddbParams); 223 | const ddbResult = await ddb.put(ddbParams).promise(); 224 | log.debug('Put participant info result', ddbResult); 225 | 226 | return ddbItem; 227 | }; 228 | 229 | const updatePreviousConnectionData = async ( 230 | existingParticipant, 231 | startChatResult 232 | ) => { 233 | if (existingParticipant === null) { 234 | return; 235 | } else { 236 | const params = { 237 | TableName: CONTACT_TABLE, 238 | Key: { 239 | contactId: existingParticipant.previousContactId, 240 | }, 241 | UpdateExpression: 'set nextContactId = :next', 242 | ExpressionAttributeValues: { 243 | ':next': startChatResult.ContactId, 244 | }, 245 | }; 246 | const ddbResult = await ddb.update(params).promise(); 247 | log.debug('Put participant info result', ddbResult); 248 | return ddbResult; 249 | } 250 | }; 251 | 252 | const createParticipantConnection = async (participantToken) => { 253 | const params = { 254 | ParticipantToken: participantToken, 255 | Type: ['CONNECTION_CREDENTIALS'], 256 | ConnectParticipant: true 257 | }; 258 | 259 | log.debug('Create participant params', params); 260 | const result = await connectParticipant 261 | .createParticipantConnection(params) 262 | .promise(); 263 | log.debug('Create participant result', result); 264 | 265 | return result.ConnectionCredentials; 266 | }; 267 | 268 | const sendEvent = async (participant, eventType) => { 269 | if ( 270 | participant === undefined || 271 | participant === null || 272 | participant.connectionCredentials === null || 273 | participant.connectionCredentials === undefined || 274 | participant.connectionCredentials.token === undefined 275 | ) { 276 | const ex = new Error('Participant credentials do not exist'); 277 | log.error('Error sending Event', ex); 278 | throw ex; 279 | } 280 | 281 | const params = { 282 | ConnectionToken: participant.connectionCredentials.token, 283 | ContentType: eventType, 284 | }; 285 | 286 | log.debug('Send event params', params); 287 | const result = await connectParticipant.sendEvent(params).promise(); 288 | // Production improvement: Code for failure path 289 | log.debug('Send event result', result); 290 | }; 291 | 292 | const getConnectInstanceId = (connectArn) => { 293 | const instanceArnSplit = connectArn.split('/'); 294 | return instanceArnSplit[instanceArnSplit.length - 1]; 295 | }; 296 | 297 | const getCurrentTime = () => { 298 | return Math.floor(Date.now() / 1000); 299 | }; 300 | 301 | module.exports = { 302 | getOrCreateParticipant, 303 | sendMessage, 304 | sendAcknowledgedEvent, 305 | sendTypingEvent, 306 | }; 307 | -------------------------------------------------------------------------------- /src/lambda/inboundMessageHandler/lib/redact.js: -------------------------------------------------------------------------------- 1 | const { log } = require('common-util'); 2 | const AWS = require('aws-sdk'); 3 | let comprehend = new AWS.Comprehend({ apiVersion: '2017-11-27' }); 4 | let piiDetectionTypes = process.env.PII_DETECTION_TYPES; 5 | const piiDetectionTypeArray = piiDetectionTypes.split(/[ ,]+/); 6 | 7 | const redactPII = async (message) => { 8 | 9 | log.debug(`RedactPII called: ${message}`); 10 | 11 | return new Promise(function (resolve, reject) { 12 | let redactedMessage = message; 13 | 14 | const params = { 15 | Text: message, 16 | /* required STRING_VALUE */ 17 | LanguageCode: "en" /* Only English (en) is supported as of May 2022 */ 18 | /* possible country codes: en | es | fr | de | it | pt | ar | hi | ja | ko | zh | zh-TW */ 19 | }; 20 | comprehend.detectPiiEntities(params, function (err, data) { 21 | if (err) { 22 | log.error(err, err.stack); // an error occurred 23 | return reject(err, err.stack); 24 | } 25 | else { 26 | log.debug(data); // log successful response from Comprehend 27 | 28 | // Get a list of PII entities we care about and put them in a dict with BeginOffset as keys 29 | let entitiesToRedact = {}; 30 | for (let entity = 0; entity < data.Entities.length; entity++) { 31 | if (piiDetectionTypeArray.includes(data.Entities[entity].Type)) { 32 | entitiesToRedact[data.Entities[entity].BeginOffset] = data.Entities[entity] 33 | } 34 | } 35 | log.debug ("entitiesToRedact: ", entitiesToRedact); 36 | 37 | // Create an array of BeginOffsets to allow replacing each entity back to front 38 | let beginOffsets = Object.keys(entitiesToRedact).reverse(); 39 | 40 | // Replace each entity in the message string. 41 | beginOffsets.forEach((key) => { 42 | log.debug("offset: ", key ); 43 | let beginOffset = entitiesToRedact[key].BeginOffset 44 | let endOffset = entitiesToRedact[key].EndOffset 45 | let type = entitiesToRedact[key].Type 46 | redactedMessage = redactedMessage.substr(0,beginOffset) + "<" + type + ">" + redactedMessage.substr(endOffset) 47 | }); 48 | 49 | log.debug(`final redacted message in comprehend function ${redactedMessage}`); 50 | resolve(redactedMessage); 51 | } 52 | }); 53 | }) 54 | }; 55 | 56 | module.exports = { 57 | redactPII 58 | }; 59 | -------------------------------------------------------------------------------- /src/lambda/inboundMessageHandler/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inboundmessagehandler", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "inboundmessagehandler", 9 | "version": "1.0.0", 10 | "license": "MIT-0", 11 | "dependencies": { 12 | "aws-sdk": "^2.1020.0", 13 | "common-util": "file:./../../common-util" 14 | } 15 | }, 16 | "../../common-util": { 17 | "version": "1.0.0", 18 | "license": "MIT-0" 19 | }, 20 | "node_modules/available-typed-arrays": { 21 | "version": "1.0.5", 22 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", 23 | "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", 24 | "engines": { 25 | "node": ">= 0.4" 26 | }, 27 | "funding": { 28 | "url": "https://github.com/sponsors/ljharb" 29 | } 30 | }, 31 | "node_modules/aws-sdk": { 32 | "version": "2.1360.0", 33 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1360.0.tgz", 34 | "integrity": "sha512-wW1CviH1s6bl5+wO+KM7aSc3yy6cQPJT85Fd4rQgrn0uwfjg9fx7KJ0FRhv+eU4DabkRjcSMlKo1IGhARmT6Tw==", 35 | "dependencies": { 36 | "buffer": "4.9.2", 37 | "events": "1.1.1", 38 | "ieee754": "1.1.13", 39 | "jmespath": "0.16.0", 40 | "querystring": "0.2.0", 41 | "sax": "1.2.1", 42 | "url": "0.10.3", 43 | "util": "^0.12.4", 44 | "uuid": "8.0.0", 45 | "xml2js": "0.5.0" 46 | }, 47 | "engines": { 48 | "node": ">= 10.0.0" 49 | } 50 | }, 51 | "node_modules/base64-js": { 52 | "version": "1.5.1", 53 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 54 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 55 | "funding": [ 56 | { 57 | "type": "github", 58 | "url": "https://github.com/sponsors/feross" 59 | }, 60 | { 61 | "type": "patreon", 62 | "url": "https://www.patreon.com/feross" 63 | }, 64 | { 65 | "type": "consulting", 66 | "url": "https://feross.org/support" 67 | } 68 | ] 69 | }, 70 | "node_modules/buffer": { 71 | "version": "4.9.2", 72 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 73 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", 74 | "dependencies": { 75 | "base64-js": "^1.0.2", 76 | "ieee754": "^1.1.4", 77 | "isarray": "^1.0.0" 78 | } 79 | }, 80 | "node_modules/call-bind": { 81 | "version": "1.0.2", 82 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 83 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 84 | "dependencies": { 85 | "function-bind": "^1.1.1", 86 | "get-intrinsic": "^1.0.2" 87 | }, 88 | "funding": { 89 | "url": "https://github.com/sponsors/ljharb" 90 | } 91 | }, 92 | "node_modules/common-util": { 93 | "resolved": "../../common-util", 94 | "link": true 95 | }, 96 | "node_modules/define-properties": { 97 | "version": "1.1.4", 98 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", 99 | "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", 100 | "dependencies": { 101 | "has-property-descriptors": "^1.0.0", 102 | "object-keys": "^1.1.1" 103 | }, 104 | "engines": { 105 | "node": ">= 0.4" 106 | }, 107 | "funding": { 108 | "url": "https://github.com/sponsors/ljharb" 109 | } 110 | }, 111 | "node_modules/es-abstract": { 112 | "version": "1.20.3", 113 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", 114 | "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", 115 | "dependencies": { 116 | "call-bind": "^1.0.2", 117 | "es-to-primitive": "^1.2.1", 118 | "function-bind": "^1.1.1", 119 | "function.prototype.name": "^1.1.5", 120 | "get-intrinsic": "^1.1.3", 121 | "get-symbol-description": "^1.0.0", 122 | "has": "^1.0.3", 123 | "has-property-descriptors": "^1.0.0", 124 | "has-symbols": "^1.0.3", 125 | "internal-slot": "^1.0.3", 126 | "is-callable": "^1.2.6", 127 | "is-negative-zero": "^2.0.2", 128 | "is-regex": "^1.1.4", 129 | "is-shared-array-buffer": "^1.0.2", 130 | "is-string": "^1.0.7", 131 | "is-weakref": "^1.0.2", 132 | "object-inspect": "^1.12.2", 133 | "object-keys": "^1.1.1", 134 | "object.assign": "^4.1.4", 135 | "regexp.prototype.flags": "^1.4.3", 136 | "safe-regex-test": "^1.0.0", 137 | "string.prototype.trimend": "^1.0.5", 138 | "string.prototype.trimstart": "^1.0.5", 139 | "unbox-primitive": "^1.0.2" 140 | }, 141 | "engines": { 142 | "node": ">= 0.4" 143 | }, 144 | "funding": { 145 | "url": "https://github.com/sponsors/ljharb" 146 | } 147 | }, 148 | "node_modules/es-to-primitive": { 149 | "version": "1.2.1", 150 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 151 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 152 | "dependencies": { 153 | "is-callable": "^1.1.4", 154 | "is-date-object": "^1.0.1", 155 | "is-symbol": "^1.0.2" 156 | }, 157 | "engines": { 158 | "node": ">= 0.4" 159 | }, 160 | "funding": { 161 | "url": "https://github.com/sponsors/ljharb" 162 | } 163 | }, 164 | "node_modules/events": { 165 | "version": "1.1.1", 166 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 167 | "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", 168 | "engines": { 169 | "node": ">=0.4.x" 170 | } 171 | }, 172 | "node_modules/for-each": { 173 | "version": "0.3.3", 174 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 175 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 176 | "dependencies": { 177 | "is-callable": "^1.1.3" 178 | } 179 | }, 180 | "node_modules/function-bind": { 181 | "version": "1.1.1", 182 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 183 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 184 | }, 185 | "node_modules/function.prototype.name": { 186 | "version": "1.1.5", 187 | "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", 188 | "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", 189 | "dependencies": { 190 | "call-bind": "^1.0.2", 191 | "define-properties": "^1.1.3", 192 | "es-abstract": "^1.19.0", 193 | "functions-have-names": "^1.2.2" 194 | }, 195 | "engines": { 196 | "node": ">= 0.4" 197 | }, 198 | "funding": { 199 | "url": "https://github.com/sponsors/ljharb" 200 | } 201 | }, 202 | "node_modules/functions-have-names": { 203 | "version": "1.2.3", 204 | "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 205 | "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", 206 | "funding": { 207 | "url": "https://github.com/sponsors/ljharb" 208 | } 209 | }, 210 | "node_modules/get-intrinsic": { 211 | "version": "1.1.3", 212 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 213 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 214 | "dependencies": { 215 | "function-bind": "^1.1.1", 216 | "has": "^1.0.3", 217 | "has-symbols": "^1.0.3" 218 | }, 219 | "funding": { 220 | "url": "https://github.com/sponsors/ljharb" 221 | } 222 | }, 223 | "node_modules/get-symbol-description": { 224 | "version": "1.0.0", 225 | "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", 226 | "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", 227 | "dependencies": { 228 | "call-bind": "^1.0.2", 229 | "get-intrinsic": "^1.1.1" 230 | }, 231 | "engines": { 232 | "node": ">= 0.4" 233 | }, 234 | "funding": { 235 | "url": "https://github.com/sponsors/ljharb" 236 | } 237 | }, 238 | "node_modules/has": { 239 | "version": "1.0.3", 240 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 241 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 242 | "dependencies": { 243 | "function-bind": "^1.1.1" 244 | }, 245 | "engines": { 246 | "node": ">= 0.4.0" 247 | } 248 | }, 249 | "node_modules/has-bigints": { 250 | "version": "1.0.2", 251 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", 252 | "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", 253 | "funding": { 254 | "url": "https://github.com/sponsors/ljharb" 255 | } 256 | }, 257 | "node_modules/has-property-descriptors": { 258 | "version": "1.0.0", 259 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", 260 | "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", 261 | "dependencies": { 262 | "get-intrinsic": "^1.1.1" 263 | }, 264 | "funding": { 265 | "url": "https://github.com/sponsors/ljharb" 266 | } 267 | }, 268 | "node_modules/has-symbols": { 269 | "version": "1.0.3", 270 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 271 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 272 | "engines": { 273 | "node": ">= 0.4" 274 | }, 275 | "funding": { 276 | "url": "https://github.com/sponsors/ljharb" 277 | } 278 | }, 279 | "node_modules/has-tostringtag": { 280 | "version": "1.0.0", 281 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 282 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 283 | "dependencies": { 284 | "has-symbols": "^1.0.2" 285 | }, 286 | "engines": { 287 | "node": ">= 0.4" 288 | }, 289 | "funding": { 290 | "url": "https://github.com/sponsors/ljharb" 291 | } 292 | }, 293 | "node_modules/ieee754": { 294 | "version": "1.1.13", 295 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 296 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 297 | }, 298 | "node_modules/inherits": { 299 | "version": "2.0.4", 300 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 301 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 302 | }, 303 | "node_modules/internal-slot": { 304 | "version": "1.0.3", 305 | "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", 306 | "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", 307 | "dependencies": { 308 | "get-intrinsic": "^1.1.0", 309 | "has": "^1.0.3", 310 | "side-channel": "^1.0.4" 311 | }, 312 | "engines": { 313 | "node": ">= 0.4" 314 | } 315 | }, 316 | "node_modules/is-arguments": { 317 | "version": "1.1.1", 318 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 319 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 320 | "dependencies": { 321 | "call-bind": "^1.0.2", 322 | "has-tostringtag": "^1.0.0" 323 | }, 324 | "engines": { 325 | "node": ">= 0.4" 326 | }, 327 | "funding": { 328 | "url": "https://github.com/sponsors/ljharb" 329 | } 330 | }, 331 | "node_modules/is-bigint": { 332 | "version": "1.0.4", 333 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", 334 | "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", 335 | "dependencies": { 336 | "has-bigints": "^1.0.1" 337 | }, 338 | "funding": { 339 | "url": "https://github.com/sponsors/ljharb" 340 | } 341 | }, 342 | "node_modules/is-boolean-object": { 343 | "version": "1.1.2", 344 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", 345 | "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", 346 | "dependencies": { 347 | "call-bind": "^1.0.2", 348 | "has-tostringtag": "^1.0.0" 349 | }, 350 | "engines": { 351 | "node": ">= 0.4" 352 | }, 353 | "funding": { 354 | "url": "https://github.com/sponsors/ljharb" 355 | } 356 | }, 357 | "node_modules/is-callable": { 358 | "version": "1.2.7", 359 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", 360 | "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", 361 | "engines": { 362 | "node": ">= 0.4" 363 | }, 364 | "funding": { 365 | "url": "https://github.com/sponsors/ljharb" 366 | } 367 | }, 368 | "node_modules/is-date-object": { 369 | "version": "1.0.5", 370 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 371 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 372 | "dependencies": { 373 | "has-tostringtag": "^1.0.0" 374 | }, 375 | "engines": { 376 | "node": ">= 0.4" 377 | }, 378 | "funding": { 379 | "url": "https://github.com/sponsors/ljharb" 380 | } 381 | }, 382 | "node_modules/is-generator-function": { 383 | "version": "1.0.10", 384 | "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", 385 | "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", 386 | "dependencies": { 387 | "has-tostringtag": "^1.0.0" 388 | }, 389 | "engines": { 390 | "node": ">= 0.4" 391 | }, 392 | "funding": { 393 | "url": "https://github.com/sponsors/ljharb" 394 | } 395 | }, 396 | "node_modules/is-negative-zero": { 397 | "version": "2.0.2", 398 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", 399 | "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", 400 | "engines": { 401 | "node": ">= 0.4" 402 | }, 403 | "funding": { 404 | "url": "https://github.com/sponsors/ljharb" 405 | } 406 | }, 407 | "node_modules/is-number-object": { 408 | "version": "1.0.7", 409 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", 410 | "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", 411 | "dependencies": { 412 | "has-tostringtag": "^1.0.0" 413 | }, 414 | "engines": { 415 | "node": ">= 0.4" 416 | }, 417 | "funding": { 418 | "url": "https://github.com/sponsors/ljharb" 419 | } 420 | }, 421 | "node_modules/is-regex": { 422 | "version": "1.1.4", 423 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 424 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 425 | "dependencies": { 426 | "call-bind": "^1.0.2", 427 | "has-tostringtag": "^1.0.0" 428 | }, 429 | "engines": { 430 | "node": ">= 0.4" 431 | }, 432 | "funding": { 433 | "url": "https://github.com/sponsors/ljharb" 434 | } 435 | }, 436 | "node_modules/is-shared-array-buffer": { 437 | "version": "1.0.2", 438 | "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", 439 | "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", 440 | "dependencies": { 441 | "call-bind": "^1.0.2" 442 | }, 443 | "funding": { 444 | "url": "https://github.com/sponsors/ljharb" 445 | } 446 | }, 447 | "node_modules/is-string": { 448 | "version": "1.0.7", 449 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 450 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 451 | "dependencies": { 452 | "has-tostringtag": "^1.0.0" 453 | }, 454 | "engines": { 455 | "node": ">= 0.4" 456 | }, 457 | "funding": { 458 | "url": "https://github.com/sponsors/ljharb" 459 | } 460 | }, 461 | "node_modules/is-symbol": { 462 | "version": "1.0.4", 463 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", 464 | "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", 465 | "dependencies": { 466 | "has-symbols": "^1.0.2" 467 | }, 468 | "engines": { 469 | "node": ">= 0.4" 470 | }, 471 | "funding": { 472 | "url": "https://github.com/sponsors/ljharb" 473 | } 474 | }, 475 | "node_modules/is-typed-array": { 476 | "version": "1.1.9", 477 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", 478 | "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", 479 | "dependencies": { 480 | "available-typed-arrays": "^1.0.5", 481 | "call-bind": "^1.0.2", 482 | "es-abstract": "^1.20.0", 483 | "for-each": "^0.3.3", 484 | "has-tostringtag": "^1.0.0" 485 | }, 486 | "engines": { 487 | "node": ">= 0.4" 488 | }, 489 | "funding": { 490 | "url": "https://github.com/sponsors/ljharb" 491 | } 492 | }, 493 | "node_modules/is-weakref": { 494 | "version": "1.0.2", 495 | "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", 496 | "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", 497 | "dependencies": { 498 | "call-bind": "^1.0.2" 499 | }, 500 | "funding": { 501 | "url": "https://github.com/sponsors/ljharb" 502 | } 503 | }, 504 | "node_modules/isarray": { 505 | "version": "1.0.0", 506 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 507 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" 508 | }, 509 | "node_modules/jmespath": { 510 | "version": "0.16.0", 511 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", 512 | "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", 513 | "engines": { 514 | "node": ">= 0.6.0" 515 | } 516 | }, 517 | "node_modules/object-inspect": { 518 | "version": "1.12.2", 519 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", 520 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", 521 | "funding": { 522 | "url": "https://github.com/sponsors/ljharb" 523 | } 524 | }, 525 | "node_modules/object-keys": { 526 | "version": "1.1.1", 527 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 528 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 529 | "engines": { 530 | "node": ">= 0.4" 531 | } 532 | }, 533 | "node_modules/object.assign": { 534 | "version": "4.1.4", 535 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", 536 | "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", 537 | "dependencies": { 538 | "call-bind": "^1.0.2", 539 | "define-properties": "^1.1.4", 540 | "has-symbols": "^1.0.3", 541 | "object-keys": "^1.1.1" 542 | }, 543 | "engines": { 544 | "node": ">= 0.4" 545 | }, 546 | "funding": { 547 | "url": "https://github.com/sponsors/ljharb" 548 | } 549 | }, 550 | "node_modules/punycode": { 551 | "version": "1.3.2", 552 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 553 | "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" 554 | }, 555 | "node_modules/querystring": { 556 | "version": "0.2.0", 557 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 558 | "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", 559 | "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", 560 | "engines": { 561 | "node": ">=0.4.x" 562 | } 563 | }, 564 | "node_modules/regexp.prototype.flags": { 565 | "version": "1.4.3", 566 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", 567 | "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", 568 | "dependencies": { 569 | "call-bind": "^1.0.2", 570 | "define-properties": "^1.1.3", 571 | "functions-have-names": "^1.2.2" 572 | }, 573 | "engines": { 574 | "node": ">= 0.4" 575 | }, 576 | "funding": { 577 | "url": "https://github.com/sponsors/ljharb" 578 | } 579 | }, 580 | "node_modules/safe-buffer": { 581 | "version": "5.2.1", 582 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 583 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 584 | "funding": [ 585 | { 586 | "type": "github", 587 | "url": "https://github.com/sponsors/feross" 588 | }, 589 | { 590 | "type": "patreon", 591 | "url": "https://www.patreon.com/feross" 592 | }, 593 | { 594 | "type": "consulting", 595 | "url": "https://feross.org/support" 596 | } 597 | ] 598 | }, 599 | "node_modules/safe-regex-test": { 600 | "version": "1.0.0", 601 | "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", 602 | "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", 603 | "dependencies": { 604 | "call-bind": "^1.0.2", 605 | "get-intrinsic": "^1.1.3", 606 | "is-regex": "^1.1.4" 607 | }, 608 | "funding": { 609 | "url": "https://github.com/sponsors/ljharb" 610 | } 611 | }, 612 | "node_modules/sax": { 613 | "version": "1.2.1", 614 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 615 | "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" 616 | }, 617 | "node_modules/side-channel": { 618 | "version": "1.0.4", 619 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 620 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 621 | "dependencies": { 622 | "call-bind": "^1.0.0", 623 | "get-intrinsic": "^1.0.2", 624 | "object-inspect": "^1.9.0" 625 | }, 626 | "funding": { 627 | "url": "https://github.com/sponsors/ljharb" 628 | } 629 | }, 630 | "node_modules/string.prototype.trimend": { 631 | "version": "1.0.5", 632 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", 633 | "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", 634 | "dependencies": { 635 | "call-bind": "^1.0.2", 636 | "define-properties": "^1.1.4", 637 | "es-abstract": "^1.19.5" 638 | }, 639 | "funding": { 640 | "url": "https://github.com/sponsors/ljharb" 641 | } 642 | }, 643 | "node_modules/string.prototype.trimstart": { 644 | "version": "1.0.5", 645 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", 646 | "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", 647 | "dependencies": { 648 | "call-bind": "^1.0.2", 649 | "define-properties": "^1.1.4", 650 | "es-abstract": "^1.19.5" 651 | }, 652 | "funding": { 653 | "url": "https://github.com/sponsors/ljharb" 654 | } 655 | }, 656 | "node_modules/unbox-primitive": { 657 | "version": "1.0.2", 658 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", 659 | "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", 660 | "dependencies": { 661 | "call-bind": "^1.0.2", 662 | "has-bigints": "^1.0.2", 663 | "has-symbols": "^1.0.3", 664 | "which-boxed-primitive": "^1.0.2" 665 | }, 666 | "funding": { 667 | "url": "https://github.com/sponsors/ljharb" 668 | } 669 | }, 670 | "node_modules/url": { 671 | "version": "0.10.3", 672 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 673 | "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", 674 | "dependencies": { 675 | "punycode": "1.3.2", 676 | "querystring": "0.2.0" 677 | } 678 | }, 679 | "node_modules/util": { 680 | "version": "0.12.4", 681 | "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", 682 | "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", 683 | "dependencies": { 684 | "inherits": "^2.0.3", 685 | "is-arguments": "^1.0.4", 686 | "is-generator-function": "^1.0.7", 687 | "is-typed-array": "^1.1.3", 688 | "safe-buffer": "^5.1.2", 689 | "which-typed-array": "^1.1.2" 690 | } 691 | }, 692 | "node_modules/uuid": { 693 | "version": "8.0.0", 694 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", 695 | "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", 696 | "bin": { 697 | "uuid": "dist/bin/uuid" 698 | } 699 | }, 700 | "node_modules/which-boxed-primitive": { 701 | "version": "1.0.2", 702 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", 703 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", 704 | "dependencies": { 705 | "is-bigint": "^1.0.1", 706 | "is-boolean-object": "^1.1.0", 707 | "is-number-object": "^1.0.4", 708 | "is-string": "^1.0.5", 709 | "is-symbol": "^1.0.3" 710 | }, 711 | "funding": { 712 | "url": "https://github.com/sponsors/ljharb" 713 | } 714 | }, 715 | "node_modules/which-typed-array": { 716 | "version": "1.1.8", 717 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", 718 | "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", 719 | "dependencies": { 720 | "available-typed-arrays": "^1.0.5", 721 | "call-bind": "^1.0.2", 722 | "es-abstract": "^1.20.0", 723 | "for-each": "^0.3.3", 724 | "has-tostringtag": "^1.0.0", 725 | "is-typed-array": "^1.1.9" 726 | }, 727 | "engines": { 728 | "node": ">= 0.4" 729 | }, 730 | "funding": { 731 | "url": "https://github.com/sponsors/ljharb" 732 | } 733 | }, 734 | "node_modules/xml2js": { 735 | "version": "0.5.0", 736 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", 737 | "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", 738 | "dependencies": { 739 | "sax": ">=0.6.0", 740 | "xmlbuilder": "~11.0.0" 741 | }, 742 | "engines": { 743 | "node": ">=4.0.0" 744 | } 745 | }, 746 | "node_modules/xmlbuilder": { 747 | "version": "11.0.1", 748 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 749 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", 750 | "engines": { 751 | "node": ">=4.0" 752 | } 753 | } 754 | }, 755 | "dependencies": { 756 | "available-typed-arrays": { 757 | "version": "1.0.5", 758 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", 759 | "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" 760 | }, 761 | "aws-sdk": { 762 | "version": "2.1360.0", 763 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1360.0.tgz", 764 | "integrity": "sha512-wW1CviH1s6bl5+wO+KM7aSc3yy6cQPJT85Fd4rQgrn0uwfjg9fx7KJ0FRhv+eU4DabkRjcSMlKo1IGhARmT6Tw==", 765 | "requires": { 766 | "buffer": "4.9.2", 767 | "events": "1.1.1", 768 | "ieee754": "1.1.13", 769 | "jmespath": "0.16.0", 770 | "querystring": "0.2.0", 771 | "sax": "1.2.1", 772 | "url": "0.10.3", 773 | "util": "^0.12.4", 774 | "uuid": "8.0.0", 775 | "xml2js": "0.5.0" 776 | } 777 | }, 778 | "base64-js": { 779 | "version": "1.5.1", 780 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 781 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 782 | }, 783 | "buffer": { 784 | "version": "4.9.2", 785 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 786 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", 787 | "requires": { 788 | "base64-js": "^1.0.2", 789 | "ieee754": "^1.1.4", 790 | "isarray": "^1.0.0" 791 | } 792 | }, 793 | "call-bind": { 794 | "version": "1.0.2", 795 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 796 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 797 | "requires": { 798 | "function-bind": "^1.1.1", 799 | "get-intrinsic": "^1.0.2" 800 | } 801 | }, 802 | "common-util": { 803 | "version": "file:../../common-util" 804 | }, 805 | "define-properties": { 806 | "version": "1.1.4", 807 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", 808 | "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", 809 | "requires": { 810 | "has-property-descriptors": "^1.0.0", 811 | "object-keys": "^1.1.1" 812 | } 813 | }, 814 | "es-abstract": { 815 | "version": "1.20.3", 816 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", 817 | "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", 818 | "requires": { 819 | "call-bind": "^1.0.2", 820 | "es-to-primitive": "^1.2.1", 821 | "function-bind": "^1.1.1", 822 | "function.prototype.name": "^1.1.5", 823 | "get-intrinsic": "^1.1.3", 824 | "get-symbol-description": "^1.0.0", 825 | "has": "^1.0.3", 826 | "has-property-descriptors": "^1.0.0", 827 | "has-symbols": "^1.0.3", 828 | "internal-slot": "^1.0.3", 829 | "is-callable": "^1.2.6", 830 | "is-negative-zero": "^2.0.2", 831 | "is-regex": "^1.1.4", 832 | "is-shared-array-buffer": "^1.0.2", 833 | "is-string": "^1.0.7", 834 | "is-weakref": "^1.0.2", 835 | "object-inspect": "^1.12.2", 836 | "object-keys": "^1.1.1", 837 | "object.assign": "^4.1.4", 838 | "regexp.prototype.flags": "^1.4.3", 839 | "safe-regex-test": "^1.0.0", 840 | "string.prototype.trimend": "^1.0.5", 841 | "string.prototype.trimstart": "^1.0.5", 842 | "unbox-primitive": "^1.0.2" 843 | } 844 | }, 845 | "es-to-primitive": { 846 | "version": "1.2.1", 847 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 848 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 849 | "requires": { 850 | "is-callable": "^1.1.4", 851 | "is-date-object": "^1.0.1", 852 | "is-symbol": "^1.0.2" 853 | } 854 | }, 855 | "events": { 856 | "version": "1.1.1", 857 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 858 | "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" 859 | }, 860 | "for-each": { 861 | "version": "0.3.3", 862 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 863 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 864 | "requires": { 865 | "is-callable": "^1.1.3" 866 | } 867 | }, 868 | "function-bind": { 869 | "version": "1.1.1", 870 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 871 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 872 | }, 873 | "function.prototype.name": { 874 | "version": "1.1.5", 875 | "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", 876 | "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", 877 | "requires": { 878 | "call-bind": "^1.0.2", 879 | "define-properties": "^1.1.3", 880 | "es-abstract": "^1.19.0", 881 | "functions-have-names": "^1.2.2" 882 | } 883 | }, 884 | "functions-have-names": { 885 | "version": "1.2.3", 886 | "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 887 | "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" 888 | }, 889 | "get-intrinsic": { 890 | "version": "1.1.3", 891 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 892 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 893 | "requires": { 894 | "function-bind": "^1.1.1", 895 | "has": "^1.0.3", 896 | "has-symbols": "^1.0.3" 897 | } 898 | }, 899 | "get-symbol-description": { 900 | "version": "1.0.0", 901 | "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", 902 | "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", 903 | "requires": { 904 | "call-bind": "^1.0.2", 905 | "get-intrinsic": "^1.1.1" 906 | } 907 | }, 908 | "has": { 909 | "version": "1.0.3", 910 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 911 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 912 | "requires": { 913 | "function-bind": "^1.1.1" 914 | } 915 | }, 916 | "has-bigints": { 917 | "version": "1.0.2", 918 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", 919 | "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" 920 | }, 921 | "has-property-descriptors": { 922 | "version": "1.0.0", 923 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", 924 | "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", 925 | "requires": { 926 | "get-intrinsic": "^1.1.1" 927 | } 928 | }, 929 | "has-symbols": { 930 | "version": "1.0.3", 931 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 932 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 933 | }, 934 | "has-tostringtag": { 935 | "version": "1.0.0", 936 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 937 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 938 | "requires": { 939 | "has-symbols": "^1.0.2" 940 | } 941 | }, 942 | "ieee754": { 943 | "version": "1.1.13", 944 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 945 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 946 | }, 947 | "inherits": { 948 | "version": "2.0.4", 949 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 950 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 951 | }, 952 | "internal-slot": { 953 | "version": "1.0.3", 954 | "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", 955 | "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", 956 | "requires": { 957 | "get-intrinsic": "^1.1.0", 958 | "has": "^1.0.3", 959 | "side-channel": "^1.0.4" 960 | } 961 | }, 962 | "is-arguments": { 963 | "version": "1.1.1", 964 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 965 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 966 | "requires": { 967 | "call-bind": "^1.0.2", 968 | "has-tostringtag": "^1.0.0" 969 | } 970 | }, 971 | "is-bigint": { 972 | "version": "1.0.4", 973 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", 974 | "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", 975 | "requires": { 976 | "has-bigints": "^1.0.1" 977 | } 978 | }, 979 | "is-boolean-object": { 980 | "version": "1.1.2", 981 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", 982 | "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", 983 | "requires": { 984 | "call-bind": "^1.0.2", 985 | "has-tostringtag": "^1.0.0" 986 | } 987 | }, 988 | "is-callable": { 989 | "version": "1.2.7", 990 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", 991 | "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" 992 | }, 993 | "is-date-object": { 994 | "version": "1.0.5", 995 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 996 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 997 | "requires": { 998 | "has-tostringtag": "^1.0.0" 999 | } 1000 | }, 1001 | "is-generator-function": { 1002 | "version": "1.0.10", 1003 | "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", 1004 | "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", 1005 | "requires": { 1006 | "has-tostringtag": "^1.0.0" 1007 | } 1008 | }, 1009 | "is-negative-zero": { 1010 | "version": "2.0.2", 1011 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", 1012 | "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" 1013 | }, 1014 | "is-number-object": { 1015 | "version": "1.0.7", 1016 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", 1017 | "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", 1018 | "requires": { 1019 | "has-tostringtag": "^1.0.0" 1020 | } 1021 | }, 1022 | "is-regex": { 1023 | "version": "1.1.4", 1024 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 1025 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 1026 | "requires": { 1027 | "call-bind": "^1.0.2", 1028 | "has-tostringtag": "^1.0.0" 1029 | } 1030 | }, 1031 | "is-shared-array-buffer": { 1032 | "version": "1.0.2", 1033 | "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", 1034 | "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", 1035 | "requires": { 1036 | "call-bind": "^1.0.2" 1037 | } 1038 | }, 1039 | "is-string": { 1040 | "version": "1.0.7", 1041 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 1042 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 1043 | "requires": { 1044 | "has-tostringtag": "^1.0.0" 1045 | } 1046 | }, 1047 | "is-symbol": { 1048 | "version": "1.0.4", 1049 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", 1050 | "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", 1051 | "requires": { 1052 | "has-symbols": "^1.0.2" 1053 | } 1054 | }, 1055 | "is-typed-array": { 1056 | "version": "1.1.9", 1057 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", 1058 | "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", 1059 | "requires": { 1060 | "available-typed-arrays": "^1.0.5", 1061 | "call-bind": "^1.0.2", 1062 | "es-abstract": "^1.20.0", 1063 | "for-each": "^0.3.3", 1064 | "has-tostringtag": "^1.0.0" 1065 | } 1066 | }, 1067 | "is-weakref": { 1068 | "version": "1.0.2", 1069 | "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", 1070 | "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", 1071 | "requires": { 1072 | "call-bind": "^1.0.2" 1073 | } 1074 | }, 1075 | "isarray": { 1076 | "version": "1.0.0", 1077 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1078 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" 1079 | }, 1080 | "jmespath": { 1081 | "version": "0.16.0", 1082 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", 1083 | "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" 1084 | }, 1085 | "object-inspect": { 1086 | "version": "1.12.2", 1087 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", 1088 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" 1089 | }, 1090 | "object-keys": { 1091 | "version": "1.1.1", 1092 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 1093 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 1094 | }, 1095 | "object.assign": { 1096 | "version": "4.1.4", 1097 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", 1098 | "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", 1099 | "requires": { 1100 | "call-bind": "^1.0.2", 1101 | "define-properties": "^1.1.4", 1102 | "has-symbols": "^1.0.3", 1103 | "object-keys": "^1.1.1" 1104 | } 1105 | }, 1106 | "punycode": { 1107 | "version": "1.3.2", 1108 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 1109 | "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" 1110 | }, 1111 | "querystring": { 1112 | "version": "0.2.0", 1113 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 1114 | "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" 1115 | }, 1116 | "regexp.prototype.flags": { 1117 | "version": "1.4.3", 1118 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", 1119 | "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", 1120 | "requires": { 1121 | "call-bind": "^1.0.2", 1122 | "define-properties": "^1.1.3", 1123 | "functions-have-names": "^1.2.2" 1124 | } 1125 | }, 1126 | "safe-buffer": { 1127 | "version": "5.2.1", 1128 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1129 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1130 | }, 1131 | "safe-regex-test": { 1132 | "version": "1.0.0", 1133 | "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", 1134 | "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", 1135 | "requires": { 1136 | "call-bind": "^1.0.2", 1137 | "get-intrinsic": "^1.1.3", 1138 | "is-regex": "^1.1.4" 1139 | } 1140 | }, 1141 | "sax": { 1142 | "version": "1.2.1", 1143 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 1144 | "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" 1145 | }, 1146 | "side-channel": { 1147 | "version": "1.0.4", 1148 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1149 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1150 | "requires": { 1151 | "call-bind": "^1.0.0", 1152 | "get-intrinsic": "^1.0.2", 1153 | "object-inspect": "^1.9.0" 1154 | } 1155 | }, 1156 | "string.prototype.trimend": { 1157 | "version": "1.0.5", 1158 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", 1159 | "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", 1160 | "requires": { 1161 | "call-bind": "^1.0.2", 1162 | "define-properties": "^1.1.4", 1163 | "es-abstract": "^1.19.5" 1164 | } 1165 | }, 1166 | "string.prototype.trimstart": { 1167 | "version": "1.0.5", 1168 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", 1169 | "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", 1170 | "requires": { 1171 | "call-bind": "^1.0.2", 1172 | "define-properties": "^1.1.4", 1173 | "es-abstract": "^1.19.5" 1174 | } 1175 | }, 1176 | "unbox-primitive": { 1177 | "version": "1.0.2", 1178 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", 1179 | "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", 1180 | "requires": { 1181 | "call-bind": "^1.0.2", 1182 | "has-bigints": "^1.0.2", 1183 | "has-symbols": "^1.0.3", 1184 | "which-boxed-primitive": "^1.0.2" 1185 | } 1186 | }, 1187 | "url": { 1188 | "version": "0.10.3", 1189 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 1190 | "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", 1191 | "requires": { 1192 | "punycode": "1.3.2", 1193 | "querystring": "0.2.0" 1194 | } 1195 | }, 1196 | "util": { 1197 | "version": "0.12.4", 1198 | "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", 1199 | "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", 1200 | "requires": { 1201 | "inherits": "^2.0.3", 1202 | "is-arguments": "^1.0.4", 1203 | "is-generator-function": "^1.0.7", 1204 | "is-typed-array": "^1.1.3", 1205 | "safe-buffer": "^5.1.2", 1206 | "which-typed-array": "^1.1.2" 1207 | } 1208 | }, 1209 | "uuid": { 1210 | "version": "8.0.0", 1211 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", 1212 | "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" 1213 | }, 1214 | "which-boxed-primitive": { 1215 | "version": "1.0.2", 1216 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", 1217 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", 1218 | "requires": { 1219 | "is-bigint": "^1.0.1", 1220 | "is-boolean-object": "^1.1.0", 1221 | "is-number-object": "^1.0.4", 1222 | "is-string": "^1.0.5", 1223 | "is-symbol": "^1.0.3" 1224 | } 1225 | }, 1226 | "which-typed-array": { 1227 | "version": "1.1.8", 1228 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", 1229 | "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", 1230 | "requires": { 1231 | "available-typed-arrays": "^1.0.5", 1232 | "call-bind": "^1.0.2", 1233 | "es-abstract": "^1.20.0", 1234 | "for-each": "^0.3.3", 1235 | "has-tostringtag": "^1.0.0", 1236 | "is-typed-array": "^1.1.9" 1237 | } 1238 | }, 1239 | "xml2js": { 1240 | "version": "0.5.0", 1241 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", 1242 | "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", 1243 | "requires": { 1244 | "sax": ">=0.6.0", 1245 | "xmlbuilder": "~11.0.0" 1246 | } 1247 | }, 1248 | "xmlbuilder": { 1249 | "version": "11.0.1", 1250 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 1251 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" 1252 | } 1253 | } 1254 | } 1255 | -------------------------------------------------------------------------------- /src/lambda/inboundMessageHandler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inboundmessagehandler", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "MIT-0", 14 | "dependencies": { 15 | "common-util": "file:./../../common-util", 16 | "aws-sdk": "^2.1020.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lambda/outboundMessageHandler/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const AWS = require('aws-sdk'); 5 | const { log } = require('common-util'); 6 | const sms = require('./lib/handlers/sms'); 7 | const fb = require('./lib/handlers/facebook'); 8 | const wa = require('./lib/handlers/whatsapp'); 9 | const { lookupContactId, deleteRecord } = require('./lib/outboundHelper'); 10 | const SMS_CHANNEL_TYPE = 'SMS'; 11 | const FB_CHANNEL_TYPE = 'FACEBOOK'; 12 | const WA_CHANNEL_TYPE = 'WHATSAPP'; 13 | const CUSTOMER_ROLE = 'CUSTOMER'; 14 | const PARTICIPANT_LEFT_CONTENT_TYPE = 15 | 'application/vnd.amazonaws.connect.event.participant.left'; 16 | const CHAT_ENDED_CONTENT_TYPE = 17 | 'application/vnd.amazonaws.connect.event.chat.ended'; 18 | const CUSTOMER = 'CUSTOMER'; 19 | const ALL = 'ALL'; 20 | 21 | exports.handler = async (event) => { 22 | log.debug('Event', event); 23 | 24 | if (event.Records === undefined) { 25 | const errorText = 'Unsupported event type. event.Records not defined.'; 26 | log.error(errorText); 27 | throw new Error(errorText); 28 | } 29 | 30 | log.debug(`Processing ${event.Records.length} records`); 31 | for (let i = 0; i < event.Records.length; i++) { 32 | const record = event.Records[i]; 33 | if (!validateRecord(record)) { 34 | continue; 35 | } 36 | 37 | // Lookup contact info from Dynamo 38 | const recordLookup = await lookupContactId( 39 | record.Sns.MessageAttributes.InitialContactId.Value 40 | ); 41 | 42 | // If record doesn't exist, report error 43 | if (recordLookup === null) { 44 | log.error( 45 | `Record not found for ContactID "${record.Sns.MessageAttributes.InitialContactId.Value}"`, 46 | record.Sns 47 | ); 48 | continue; 49 | } 50 | 51 | if ( 52 | record.Sns.MessageAttributes.ContentType.Value === CHAT_ENDED_CONTENT_TYPE 53 | ) { 54 | await deleteRecord(recordLookup.contactId); 55 | continue; 56 | } 57 | 58 | await handleMessage(record, recordLookup); 59 | } 60 | }; 61 | 62 | const validateRecord = (record) => { 63 | if (record.EventSource !== 'aws:sns') { 64 | log.warn( 65 | 'Unsuported event source for record. Record will not be processed.', 66 | record 67 | ); 68 | return false; 69 | } 70 | 71 | // Ensure we don't send customer messages back to the customer. 72 | if ( 73 | (record.Sns.MessageAttributes.ParticipantRole === undefined || 74 | record.Sns.MessageAttributes.ParticipantRole.Value === CUSTOMER_ROLE) && 75 | record.Sns.MessageAttributes.ContentType.Value !== CHAT_ENDED_CONTENT_TYPE && 76 | ((record.Sns.MessageAttributes.MessageVisibility.Value == CUSTOMER || 77 | record.Sns.MessageAttributes.MessageVisibility.Value == ALL) && 78 | record.Sns.MessageAttributes.MessageVisibility.Value != CUSTOMER) 79 | ) { 80 | log.debug('Customer event. Ignoring.'); 81 | return false; 82 | } 83 | 84 | // Event for the agent leaving the conversation 85 | if ( 86 | record.Sns.MessageAttributes.ContentType === undefined || 87 | record.Sns.MessageAttributes.ContentType.Value === 88 | PARTICIPANT_LEFT_CONTENT_TYPE 89 | ) { 90 | log.debug('Agent leaving conversation event. Ignoring.'); 91 | return false; 92 | } 93 | 94 | return true; 95 | }; 96 | 97 | const handleMessage = async (record, recordLookup) => { 98 | switch (recordLookup.channel) { 99 | case SMS_CHANNEL_TYPE: 100 | await sms.handler(recordLookup.vendorId, JSON.parse(record.Sns.Message)); 101 | break; 102 | case FB_CHANNEL_TYPE: 103 | await fb.handler(recordLookup.vendorId, JSON.parse(record.Sns.Message)); 104 | break; 105 | case WA_CHANNEL_TYPE: 106 | await wa.handler(recordLookup.vendorId, JSON.parse(record.Sns.Message)); 107 | break; 108 | default: 109 | log.error(`Unsupported channel type: ${recordLookup.channel}`); 110 | break; 111 | } 112 | }; 113 | -------------------------------------------------------------------------------- /src/lambda/outboundMessageHandler/lib/handlers/facebook.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const https = require('https'); 5 | const AWS = require('aws-sdk'); 6 | const { log } = require('common-util'); 7 | const crypto = require('crypto'); 8 | 9 | const PATH = '/v2.6/me/messages'; 10 | let pageToken = undefined; 11 | let appSecret = undefined; 12 | 13 | const secretManager = new AWS.SecretsManager(); 14 | 15 | const handler = async (facebookId, message) => { 16 | if (message.Type === 'EVENT') { 17 | log.debug('Ignoring event message', message); 18 | return; 19 | } 20 | 21 | if (pageToken === undefined || appSecret === undefined) { 22 | await getFacebookSecrets(); 23 | } 24 | 25 | if (pageToken === null) { 26 | log.error('Page token not found'); 27 | } 28 | 29 | return await sendMessage(facebookId, message); 30 | }; 31 | 32 | const sendMessage = async (facebookId, message) => { 33 | const body = { 34 | recipient: { id: facebookId }, 35 | message: { text: message.Content }, 36 | }; 37 | log.debug('Send FB Message body', body); 38 | 39 | const appsecret_proof = crypto.createHmac('sha256', appSecret).update(pageToken).digest('hex'); 40 | const options = { 41 | host: 'graph.facebook.com', 42 | path: `${PATH}?access_token=${pageToken}&appsecret_proof=${appsecret_proof}`, 43 | method: 'POST', 44 | headers: { 'Content-Type': 'application/json' }, 45 | }; 46 | 47 | const result = await new Promise((resolve, reject) => { 48 | const req = https.request(options, (res) => { 49 | let responseBody = ''; 50 | res.on('data', (chunk) => { 51 | responseBody += chunk; 52 | }); 53 | 54 | res.on('end', () => { 55 | resolve(responseBody); 56 | }); 57 | }); 58 | 59 | req.on('error', (err) => { 60 | log.error('Error sending FB message', err); 61 | reject(err); 62 | }); 63 | 64 | req.write(JSON.stringify(body)); 65 | req.end(); 66 | }); 67 | 68 | const resultObj = JSON.parse(result); 69 | log.debug('Send FB Message result', result); 70 | 71 | if (resultObj.error !== undefined) { 72 | log.error('Error sending FB message', resultObj); 73 | return false; 74 | } 75 | 76 | return true; 77 | }; 78 | 79 | 80 | const getFacebookSecrets = async () => { 81 | if (process.env.FB_SECRET) { 82 | const params = { 83 | SecretId: process.env.FB_SECRET 84 | } 85 | const response = await secretManager.getSecretValue(params).promise(); 86 | pageToken = JSON.parse(response.SecretString).PAGE_TOKEN 87 | appSecret = JSON.parse(response.SecretString).APP_SECRET 88 | } else { 89 | pageToken = null; 90 | appSecret = null; 91 | } 92 | 93 | }; 94 | 95 | module.exports = { handler }; 96 | -------------------------------------------------------------------------------- /src/lambda/outboundMessageHandler/lib/handlers/sms.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const AWS = require('aws-sdk'); 5 | const { log } = require('common-util'); 6 | 7 | const pinpoint = new AWS.Pinpoint(); 8 | 9 | const { PINPOINT_APPLICATION_ID, SMS_NUMBER } = process.env; 10 | 11 | const handler = async (phoneNumber, message) => { 12 | if (message.Type === 'EVENT') { 13 | log.debug('Ignoring event message', message); 14 | return; 15 | } 16 | 17 | await sendMessage(phoneNumber, message); 18 | }; 19 | 20 | const sendMessage = async (phoneNumber, message) => { 21 | const params = { 22 | ApplicationId: PINPOINT_APPLICATION_ID, 23 | MessageRequest: { 24 | Addresses: { 25 | [phoneNumber]: { 26 | ChannelType: 'SMS', 27 | }, 28 | }, 29 | MessageConfiguration: { 30 | SMSMessage: { 31 | Body: message.Content, 32 | OriginationNumber: SMS_NUMBER, 33 | MessageType: "TRANSACTIONAL" 34 | }, 35 | }, 36 | }, 37 | }; 38 | 39 | log.debug('Send pinpoint message params', params); 40 | const result = await pinpoint.sendMessages(params).promise(); 41 | log.debug('Send pinpoint message result', result); 42 | 43 | if (result.MessageResponse.Result[phoneNumber].StatusCode !== 200) { 44 | log.error('SMS Send failure', { params, result }); 45 | // Production improvement: error handling. 46 | return false; 47 | } 48 | 49 | return true; 50 | }; 51 | 52 | module.exports = { handler }; 53 | -------------------------------------------------------------------------------- /src/lambda/outboundMessageHandler/lib/handlers/whatsapp.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const https = require('https'); 5 | const AWS = require('aws-sdk'); 6 | const { log } = require('common-util'); 7 | 8 | const PATH = '/v15.0'; 9 | let accessToken = undefined; 10 | let phoneNoId = undefined; 11 | 12 | const secretManager = new AWS.SecretsManager(); 13 | 14 | const handler = async (toPhoneNumber, message) => { 15 | if (message.Type === 'EVENT') { 16 | log.debug('Ignoring event message', message); 17 | return; 18 | } 19 | 20 | if (accessToken === undefined || phoneNoId === undefined) { 21 | await getWhatsAppSecrets(); 22 | } 23 | 24 | if (accessToken === null) { 25 | log.error('WA_ACCESS_TOKEN not found in Secrets Manager'); 26 | } 27 | 28 | if (phoneNoId === null) { 29 | log.error('WA_PHONE_NUMBER_ID not found in Secrets Manager'); 30 | } 31 | 32 | return await sendMessage(toPhoneNumber, message); 33 | }; 34 | 35 | const sendMessage = async (toPhoneNumber, message) => { 36 | const body = { 37 | messaging_product: "whatsapp", 38 | recipient_type: "individual", 39 | to: toPhoneNumber, 40 | type: "text", 41 | text: { 42 | preview_url: false, 43 | body: message.Content 44 | }, 45 | }; 46 | 47 | const options = { 48 | host: 'graph.facebook.com', 49 | path: `${PATH}/${phoneNoId}/messages`, 50 | method: 'POST', 51 | headers: { 52 | 'Authorization': `Bearer ${accessToken}`, 53 | 'Content-Type': 'application/json' }, 54 | }; 55 | 56 | const result = await new Promise((resolve, reject) => { 57 | const req = https.request(options, (res) => { 58 | let responseBody = ''; 59 | res.on('data', (chunk) => { 60 | responseBody += chunk; 61 | }); 62 | 63 | res.on('end', () => { 64 | resolve(responseBody); 65 | }); 66 | }); 67 | 68 | req.on('error', (err) => { 69 | log.error('Error sending WA message', err); 70 | reject(err); 71 | }); 72 | 73 | req.write(JSON.stringify(body)); 74 | req.end(); 75 | }); 76 | 77 | const resultObj = JSON.parse(result); 78 | log.debug('Send WA Message result', result); 79 | 80 | if (resultObj.error !== undefined) { 81 | log.error('Error sending WA message', resultObj); 82 | return false; 83 | } 84 | 85 | return true; 86 | }; 87 | 88 | const getWhatsAppSecrets = async () => { 89 | if(process.env.WA_SECRET){ 90 | const params = { 91 | SecretId: process.env.WA_SECRET 92 | } 93 | const response = await secretManager.getSecretValue(params).promise(); 94 | accessToken = JSON.parse(response.SecretString).WA_ACCESS_TOKEN 95 | phoneNoId = JSON.parse(response.SecretString).WA_PHONE_NUMBER_ID 96 | } else { 97 | accessToken = null; 98 | phoneNoId = null; 99 | } 100 | 101 | }; 102 | 103 | module.exports = { handler }; 104 | -------------------------------------------------------------------------------- /src/lambda/outboundMessageHandler/lib/outboundHelper.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const { log } = require('common-util'); 3 | 4 | const ddb = new AWS.DynamoDB.DocumentClient(); 5 | 6 | const PARTICIPANT_LEFT_CONTENT_TYPE = 7 | 'application/vnd.amazonaws.connect.event.participant.left'; 8 | 9 | const { CONTACT_TABLE } = process.env; 10 | 11 | const lookupContactId = async (contactId) => { 12 | const params = { 13 | TableName: CONTACT_TABLE, 14 | Key: { 15 | contactId, 16 | }, 17 | }; 18 | 19 | log.debug('Contact Id Lookup Request', params); 20 | const result = await ddb.get(params).promise(); 21 | log.debug('Contact Id Lookup Result', result); 22 | 23 | if (result.Item === undefined || result.Item === null) { 24 | log.warn(`No Contact Match Found for ${contactId}`); 25 | return null; 26 | } 27 | 28 | return result.Item.contactId !== undefined ? result.Item : null; 29 | }; 30 | 31 | const deleteRecord = async (contactId) => { 32 | const params = { 33 | TableName: CONTACT_TABLE, 34 | Key: { 35 | contactId, 36 | }, 37 | }; 38 | 39 | log.debug('Contact Id Delete Request', params); 40 | const result = await ddb.delete(params).promise(); 41 | log.debug('Contact Id Delete Result', result); 42 | }; 43 | 44 | module.exports = { 45 | lookupContactId, 46 | deleteRecord, 47 | }; 48 | -------------------------------------------------------------------------------- /src/lambda/outboundMessageHandler/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "outboundMessageHandler", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "outboundMessageHandler", 9 | "version": "1.0.0", 10 | "license": "MIT-0", 11 | "dependencies": { 12 | "aws-sdk": "^2.1020.0", 13 | "common-util": "file:./../../common-util" 14 | } 15 | }, 16 | "../../common-util": { 17 | "version": "1.0.0", 18 | "license": "MIT-0" 19 | }, 20 | "node_modules/available-typed-arrays": { 21 | "version": "1.0.5", 22 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", 23 | "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", 24 | "engines": { 25 | "node": ">= 0.4" 26 | }, 27 | "funding": { 28 | "url": "https://github.com/sponsors/ljharb" 29 | } 30 | }, 31 | "node_modules/aws-sdk": { 32 | "version": "2.1360.0", 33 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1360.0.tgz", 34 | "integrity": "sha512-wW1CviH1s6bl5+wO+KM7aSc3yy6cQPJT85Fd4rQgrn0uwfjg9fx7KJ0FRhv+eU4DabkRjcSMlKo1IGhARmT6Tw==", 35 | "dependencies": { 36 | "buffer": "4.9.2", 37 | "events": "1.1.1", 38 | "ieee754": "1.1.13", 39 | "jmespath": "0.16.0", 40 | "querystring": "0.2.0", 41 | "sax": "1.2.1", 42 | "url": "0.10.3", 43 | "util": "^0.12.4", 44 | "uuid": "8.0.0", 45 | "xml2js": "0.5.0" 46 | }, 47 | "engines": { 48 | "node": ">= 10.0.0" 49 | } 50 | }, 51 | "node_modules/base64-js": { 52 | "version": "1.5.1", 53 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 54 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 55 | "funding": [ 56 | { 57 | "type": "github", 58 | "url": "https://github.com/sponsors/feross" 59 | }, 60 | { 61 | "type": "patreon", 62 | "url": "https://www.patreon.com/feross" 63 | }, 64 | { 65 | "type": "consulting", 66 | "url": "https://feross.org/support" 67 | } 68 | ] 69 | }, 70 | "node_modules/buffer": { 71 | "version": "4.9.2", 72 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 73 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", 74 | "dependencies": { 75 | "base64-js": "^1.0.2", 76 | "ieee754": "^1.1.4", 77 | "isarray": "^1.0.0" 78 | } 79 | }, 80 | "node_modules/call-bind": { 81 | "version": "1.0.2", 82 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 83 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 84 | "dependencies": { 85 | "function-bind": "^1.1.1", 86 | "get-intrinsic": "^1.0.2" 87 | }, 88 | "funding": { 89 | "url": "https://github.com/sponsors/ljharb" 90 | } 91 | }, 92 | "node_modules/common-util": { 93 | "resolved": "../../common-util", 94 | "link": true 95 | }, 96 | "node_modules/define-properties": { 97 | "version": "1.1.4", 98 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", 99 | "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", 100 | "dependencies": { 101 | "has-property-descriptors": "^1.0.0", 102 | "object-keys": "^1.1.1" 103 | }, 104 | "engines": { 105 | "node": ">= 0.4" 106 | }, 107 | "funding": { 108 | "url": "https://github.com/sponsors/ljharb" 109 | } 110 | }, 111 | "node_modules/es-abstract": { 112 | "version": "1.20.3", 113 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", 114 | "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", 115 | "dependencies": { 116 | "call-bind": "^1.0.2", 117 | "es-to-primitive": "^1.2.1", 118 | "function-bind": "^1.1.1", 119 | "function.prototype.name": "^1.1.5", 120 | "get-intrinsic": "^1.1.3", 121 | "get-symbol-description": "^1.0.0", 122 | "has": "^1.0.3", 123 | "has-property-descriptors": "^1.0.0", 124 | "has-symbols": "^1.0.3", 125 | "internal-slot": "^1.0.3", 126 | "is-callable": "^1.2.6", 127 | "is-negative-zero": "^2.0.2", 128 | "is-regex": "^1.1.4", 129 | "is-shared-array-buffer": "^1.0.2", 130 | "is-string": "^1.0.7", 131 | "is-weakref": "^1.0.2", 132 | "object-inspect": "^1.12.2", 133 | "object-keys": "^1.1.1", 134 | "object.assign": "^4.1.4", 135 | "regexp.prototype.flags": "^1.4.3", 136 | "safe-regex-test": "^1.0.0", 137 | "string.prototype.trimend": "^1.0.5", 138 | "string.prototype.trimstart": "^1.0.5", 139 | "unbox-primitive": "^1.0.2" 140 | }, 141 | "engines": { 142 | "node": ">= 0.4" 143 | }, 144 | "funding": { 145 | "url": "https://github.com/sponsors/ljharb" 146 | } 147 | }, 148 | "node_modules/es-to-primitive": { 149 | "version": "1.2.1", 150 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 151 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 152 | "dependencies": { 153 | "is-callable": "^1.1.4", 154 | "is-date-object": "^1.0.1", 155 | "is-symbol": "^1.0.2" 156 | }, 157 | "engines": { 158 | "node": ">= 0.4" 159 | }, 160 | "funding": { 161 | "url": "https://github.com/sponsors/ljharb" 162 | } 163 | }, 164 | "node_modules/events": { 165 | "version": "1.1.1", 166 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 167 | "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", 168 | "engines": { 169 | "node": ">=0.4.x" 170 | } 171 | }, 172 | "node_modules/for-each": { 173 | "version": "0.3.3", 174 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 175 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 176 | "dependencies": { 177 | "is-callable": "^1.1.3" 178 | } 179 | }, 180 | "node_modules/function-bind": { 181 | "version": "1.1.1", 182 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 183 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 184 | }, 185 | "node_modules/function.prototype.name": { 186 | "version": "1.1.5", 187 | "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", 188 | "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", 189 | "dependencies": { 190 | "call-bind": "^1.0.2", 191 | "define-properties": "^1.1.3", 192 | "es-abstract": "^1.19.0", 193 | "functions-have-names": "^1.2.2" 194 | }, 195 | "engines": { 196 | "node": ">= 0.4" 197 | }, 198 | "funding": { 199 | "url": "https://github.com/sponsors/ljharb" 200 | } 201 | }, 202 | "node_modules/functions-have-names": { 203 | "version": "1.2.3", 204 | "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 205 | "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", 206 | "funding": { 207 | "url": "https://github.com/sponsors/ljharb" 208 | } 209 | }, 210 | "node_modules/get-intrinsic": { 211 | "version": "1.1.3", 212 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 213 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 214 | "dependencies": { 215 | "function-bind": "^1.1.1", 216 | "has": "^1.0.3", 217 | "has-symbols": "^1.0.3" 218 | }, 219 | "funding": { 220 | "url": "https://github.com/sponsors/ljharb" 221 | } 222 | }, 223 | "node_modules/get-symbol-description": { 224 | "version": "1.0.0", 225 | "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", 226 | "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", 227 | "dependencies": { 228 | "call-bind": "^1.0.2", 229 | "get-intrinsic": "^1.1.1" 230 | }, 231 | "engines": { 232 | "node": ">= 0.4" 233 | }, 234 | "funding": { 235 | "url": "https://github.com/sponsors/ljharb" 236 | } 237 | }, 238 | "node_modules/has": { 239 | "version": "1.0.3", 240 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 241 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 242 | "dependencies": { 243 | "function-bind": "^1.1.1" 244 | }, 245 | "engines": { 246 | "node": ">= 0.4.0" 247 | } 248 | }, 249 | "node_modules/has-bigints": { 250 | "version": "1.0.2", 251 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", 252 | "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", 253 | "funding": { 254 | "url": "https://github.com/sponsors/ljharb" 255 | } 256 | }, 257 | "node_modules/has-property-descriptors": { 258 | "version": "1.0.0", 259 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", 260 | "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", 261 | "dependencies": { 262 | "get-intrinsic": "^1.1.1" 263 | }, 264 | "funding": { 265 | "url": "https://github.com/sponsors/ljharb" 266 | } 267 | }, 268 | "node_modules/has-symbols": { 269 | "version": "1.0.3", 270 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 271 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 272 | "engines": { 273 | "node": ">= 0.4" 274 | }, 275 | "funding": { 276 | "url": "https://github.com/sponsors/ljharb" 277 | } 278 | }, 279 | "node_modules/has-tostringtag": { 280 | "version": "1.0.0", 281 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 282 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 283 | "dependencies": { 284 | "has-symbols": "^1.0.2" 285 | }, 286 | "engines": { 287 | "node": ">= 0.4" 288 | }, 289 | "funding": { 290 | "url": "https://github.com/sponsors/ljharb" 291 | } 292 | }, 293 | "node_modules/ieee754": { 294 | "version": "1.1.13", 295 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 296 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 297 | }, 298 | "node_modules/inherits": { 299 | "version": "2.0.4", 300 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 301 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 302 | }, 303 | "node_modules/internal-slot": { 304 | "version": "1.0.3", 305 | "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", 306 | "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", 307 | "dependencies": { 308 | "get-intrinsic": "^1.1.0", 309 | "has": "^1.0.3", 310 | "side-channel": "^1.0.4" 311 | }, 312 | "engines": { 313 | "node": ">= 0.4" 314 | } 315 | }, 316 | "node_modules/is-arguments": { 317 | "version": "1.1.1", 318 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 319 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 320 | "dependencies": { 321 | "call-bind": "^1.0.2", 322 | "has-tostringtag": "^1.0.0" 323 | }, 324 | "engines": { 325 | "node": ">= 0.4" 326 | }, 327 | "funding": { 328 | "url": "https://github.com/sponsors/ljharb" 329 | } 330 | }, 331 | "node_modules/is-bigint": { 332 | "version": "1.0.4", 333 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", 334 | "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", 335 | "dependencies": { 336 | "has-bigints": "^1.0.1" 337 | }, 338 | "funding": { 339 | "url": "https://github.com/sponsors/ljharb" 340 | } 341 | }, 342 | "node_modules/is-boolean-object": { 343 | "version": "1.1.2", 344 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", 345 | "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", 346 | "dependencies": { 347 | "call-bind": "^1.0.2", 348 | "has-tostringtag": "^1.0.0" 349 | }, 350 | "engines": { 351 | "node": ">= 0.4" 352 | }, 353 | "funding": { 354 | "url": "https://github.com/sponsors/ljharb" 355 | } 356 | }, 357 | "node_modules/is-callable": { 358 | "version": "1.2.7", 359 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", 360 | "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", 361 | "engines": { 362 | "node": ">= 0.4" 363 | }, 364 | "funding": { 365 | "url": "https://github.com/sponsors/ljharb" 366 | } 367 | }, 368 | "node_modules/is-date-object": { 369 | "version": "1.0.5", 370 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 371 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 372 | "dependencies": { 373 | "has-tostringtag": "^1.0.0" 374 | }, 375 | "engines": { 376 | "node": ">= 0.4" 377 | }, 378 | "funding": { 379 | "url": "https://github.com/sponsors/ljharb" 380 | } 381 | }, 382 | "node_modules/is-generator-function": { 383 | "version": "1.0.10", 384 | "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", 385 | "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", 386 | "dependencies": { 387 | "has-tostringtag": "^1.0.0" 388 | }, 389 | "engines": { 390 | "node": ">= 0.4" 391 | }, 392 | "funding": { 393 | "url": "https://github.com/sponsors/ljharb" 394 | } 395 | }, 396 | "node_modules/is-negative-zero": { 397 | "version": "2.0.2", 398 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", 399 | "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", 400 | "engines": { 401 | "node": ">= 0.4" 402 | }, 403 | "funding": { 404 | "url": "https://github.com/sponsors/ljharb" 405 | } 406 | }, 407 | "node_modules/is-number-object": { 408 | "version": "1.0.7", 409 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", 410 | "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", 411 | "dependencies": { 412 | "has-tostringtag": "^1.0.0" 413 | }, 414 | "engines": { 415 | "node": ">= 0.4" 416 | }, 417 | "funding": { 418 | "url": "https://github.com/sponsors/ljharb" 419 | } 420 | }, 421 | "node_modules/is-regex": { 422 | "version": "1.1.4", 423 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 424 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 425 | "dependencies": { 426 | "call-bind": "^1.0.2", 427 | "has-tostringtag": "^1.0.0" 428 | }, 429 | "engines": { 430 | "node": ">= 0.4" 431 | }, 432 | "funding": { 433 | "url": "https://github.com/sponsors/ljharb" 434 | } 435 | }, 436 | "node_modules/is-shared-array-buffer": { 437 | "version": "1.0.2", 438 | "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", 439 | "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", 440 | "dependencies": { 441 | "call-bind": "^1.0.2" 442 | }, 443 | "funding": { 444 | "url": "https://github.com/sponsors/ljharb" 445 | } 446 | }, 447 | "node_modules/is-string": { 448 | "version": "1.0.7", 449 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 450 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 451 | "dependencies": { 452 | "has-tostringtag": "^1.0.0" 453 | }, 454 | "engines": { 455 | "node": ">= 0.4" 456 | }, 457 | "funding": { 458 | "url": "https://github.com/sponsors/ljharb" 459 | } 460 | }, 461 | "node_modules/is-symbol": { 462 | "version": "1.0.4", 463 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", 464 | "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", 465 | "dependencies": { 466 | "has-symbols": "^1.0.2" 467 | }, 468 | "engines": { 469 | "node": ">= 0.4" 470 | }, 471 | "funding": { 472 | "url": "https://github.com/sponsors/ljharb" 473 | } 474 | }, 475 | "node_modules/is-typed-array": { 476 | "version": "1.1.9", 477 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", 478 | "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", 479 | "dependencies": { 480 | "available-typed-arrays": "^1.0.5", 481 | "call-bind": "^1.0.2", 482 | "es-abstract": "^1.20.0", 483 | "for-each": "^0.3.3", 484 | "has-tostringtag": "^1.0.0" 485 | }, 486 | "engines": { 487 | "node": ">= 0.4" 488 | }, 489 | "funding": { 490 | "url": "https://github.com/sponsors/ljharb" 491 | } 492 | }, 493 | "node_modules/is-weakref": { 494 | "version": "1.0.2", 495 | "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", 496 | "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", 497 | "dependencies": { 498 | "call-bind": "^1.0.2" 499 | }, 500 | "funding": { 501 | "url": "https://github.com/sponsors/ljharb" 502 | } 503 | }, 504 | "node_modules/isarray": { 505 | "version": "1.0.0", 506 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 507 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" 508 | }, 509 | "node_modules/jmespath": { 510 | "version": "0.16.0", 511 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", 512 | "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", 513 | "engines": { 514 | "node": ">= 0.6.0" 515 | } 516 | }, 517 | "node_modules/object-inspect": { 518 | "version": "1.12.2", 519 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", 520 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", 521 | "funding": { 522 | "url": "https://github.com/sponsors/ljharb" 523 | } 524 | }, 525 | "node_modules/object-keys": { 526 | "version": "1.1.1", 527 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 528 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 529 | "engines": { 530 | "node": ">= 0.4" 531 | } 532 | }, 533 | "node_modules/object.assign": { 534 | "version": "4.1.4", 535 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", 536 | "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", 537 | "dependencies": { 538 | "call-bind": "^1.0.2", 539 | "define-properties": "^1.1.4", 540 | "has-symbols": "^1.0.3", 541 | "object-keys": "^1.1.1" 542 | }, 543 | "engines": { 544 | "node": ">= 0.4" 545 | }, 546 | "funding": { 547 | "url": "https://github.com/sponsors/ljharb" 548 | } 549 | }, 550 | "node_modules/punycode": { 551 | "version": "1.3.2", 552 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 553 | "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" 554 | }, 555 | "node_modules/querystring": { 556 | "version": "0.2.0", 557 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 558 | "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", 559 | "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", 560 | "engines": { 561 | "node": ">=0.4.x" 562 | } 563 | }, 564 | "node_modules/regexp.prototype.flags": { 565 | "version": "1.4.3", 566 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", 567 | "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", 568 | "dependencies": { 569 | "call-bind": "^1.0.2", 570 | "define-properties": "^1.1.3", 571 | "functions-have-names": "^1.2.2" 572 | }, 573 | "engines": { 574 | "node": ">= 0.4" 575 | }, 576 | "funding": { 577 | "url": "https://github.com/sponsors/ljharb" 578 | } 579 | }, 580 | "node_modules/safe-buffer": { 581 | "version": "5.2.1", 582 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 583 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 584 | "funding": [ 585 | { 586 | "type": "github", 587 | "url": "https://github.com/sponsors/feross" 588 | }, 589 | { 590 | "type": "patreon", 591 | "url": "https://www.patreon.com/feross" 592 | }, 593 | { 594 | "type": "consulting", 595 | "url": "https://feross.org/support" 596 | } 597 | ] 598 | }, 599 | "node_modules/safe-regex-test": { 600 | "version": "1.0.0", 601 | "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", 602 | "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", 603 | "dependencies": { 604 | "call-bind": "^1.0.2", 605 | "get-intrinsic": "^1.1.3", 606 | "is-regex": "^1.1.4" 607 | }, 608 | "funding": { 609 | "url": "https://github.com/sponsors/ljharb" 610 | } 611 | }, 612 | "node_modules/sax": { 613 | "version": "1.2.1", 614 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 615 | "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" 616 | }, 617 | "node_modules/side-channel": { 618 | "version": "1.0.4", 619 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 620 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 621 | "dependencies": { 622 | "call-bind": "^1.0.0", 623 | "get-intrinsic": "^1.0.2", 624 | "object-inspect": "^1.9.0" 625 | }, 626 | "funding": { 627 | "url": "https://github.com/sponsors/ljharb" 628 | } 629 | }, 630 | "node_modules/string.prototype.trimend": { 631 | "version": "1.0.5", 632 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", 633 | "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", 634 | "dependencies": { 635 | "call-bind": "^1.0.2", 636 | "define-properties": "^1.1.4", 637 | "es-abstract": "^1.19.5" 638 | }, 639 | "funding": { 640 | "url": "https://github.com/sponsors/ljharb" 641 | } 642 | }, 643 | "node_modules/string.prototype.trimstart": { 644 | "version": "1.0.5", 645 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", 646 | "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", 647 | "dependencies": { 648 | "call-bind": "^1.0.2", 649 | "define-properties": "^1.1.4", 650 | "es-abstract": "^1.19.5" 651 | }, 652 | "funding": { 653 | "url": "https://github.com/sponsors/ljharb" 654 | } 655 | }, 656 | "node_modules/unbox-primitive": { 657 | "version": "1.0.2", 658 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", 659 | "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", 660 | "dependencies": { 661 | "call-bind": "^1.0.2", 662 | "has-bigints": "^1.0.2", 663 | "has-symbols": "^1.0.3", 664 | "which-boxed-primitive": "^1.0.2" 665 | }, 666 | "funding": { 667 | "url": "https://github.com/sponsors/ljharb" 668 | } 669 | }, 670 | "node_modules/url": { 671 | "version": "0.10.3", 672 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 673 | "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", 674 | "dependencies": { 675 | "punycode": "1.3.2", 676 | "querystring": "0.2.0" 677 | } 678 | }, 679 | "node_modules/util": { 680 | "version": "0.12.4", 681 | "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", 682 | "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", 683 | "dependencies": { 684 | "inherits": "^2.0.3", 685 | "is-arguments": "^1.0.4", 686 | "is-generator-function": "^1.0.7", 687 | "is-typed-array": "^1.1.3", 688 | "safe-buffer": "^5.1.2", 689 | "which-typed-array": "^1.1.2" 690 | } 691 | }, 692 | "node_modules/uuid": { 693 | "version": "8.0.0", 694 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", 695 | "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", 696 | "bin": { 697 | "uuid": "dist/bin/uuid" 698 | } 699 | }, 700 | "node_modules/which-boxed-primitive": { 701 | "version": "1.0.2", 702 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", 703 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", 704 | "dependencies": { 705 | "is-bigint": "^1.0.1", 706 | "is-boolean-object": "^1.1.0", 707 | "is-number-object": "^1.0.4", 708 | "is-string": "^1.0.5", 709 | "is-symbol": "^1.0.3" 710 | }, 711 | "funding": { 712 | "url": "https://github.com/sponsors/ljharb" 713 | } 714 | }, 715 | "node_modules/which-typed-array": { 716 | "version": "1.1.8", 717 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", 718 | "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", 719 | "dependencies": { 720 | "available-typed-arrays": "^1.0.5", 721 | "call-bind": "^1.0.2", 722 | "es-abstract": "^1.20.0", 723 | "for-each": "^0.3.3", 724 | "has-tostringtag": "^1.0.0", 725 | "is-typed-array": "^1.1.9" 726 | }, 727 | "engines": { 728 | "node": ">= 0.4" 729 | }, 730 | "funding": { 731 | "url": "https://github.com/sponsors/ljharb" 732 | } 733 | }, 734 | "node_modules/xml2js": { 735 | "version": "0.5.0", 736 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", 737 | "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", 738 | "dependencies": { 739 | "sax": ">=0.6.0", 740 | "xmlbuilder": "~11.0.0" 741 | }, 742 | "engines": { 743 | "node": ">=4.0.0" 744 | } 745 | }, 746 | "node_modules/xmlbuilder": { 747 | "version": "11.0.1", 748 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 749 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", 750 | "engines": { 751 | "node": ">=4.0" 752 | } 753 | } 754 | }, 755 | "dependencies": { 756 | "available-typed-arrays": { 757 | "version": "1.0.5", 758 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", 759 | "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" 760 | }, 761 | "aws-sdk": { 762 | "version": "2.1360.0", 763 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1360.0.tgz", 764 | "integrity": "sha512-wW1CviH1s6bl5+wO+KM7aSc3yy6cQPJT85Fd4rQgrn0uwfjg9fx7KJ0FRhv+eU4DabkRjcSMlKo1IGhARmT6Tw==", 765 | "requires": { 766 | "buffer": "4.9.2", 767 | "events": "1.1.1", 768 | "ieee754": "1.1.13", 769 | "jmespath": "0.16.0", 770 | "querystring": "0.2.0", 771 | "sax": "1.2.1", 772 | "url": "0.10.3", 773 | "util": "^0.12.4", 774 | "uuid": "8.0.0", 775 | "xml2js": "0.5.0" 776 | } 777 | }, 778 | "base64-js": { 779 | "version": "1.5.1", 780 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 781 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 782 | }, 783 | "buffer": { 784 | "version": "4.9.2", 785 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 786 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", 787 | "requires": { 788 | "base64-js": "^1.0.2", 789 | "ieee754": "^1.1.4", 790 | "isarray": "^1.0.0" 791 | } 792 | }, 793 | "call-bind": { 794 | "version": "1.0.2", 795 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 796 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 797 | "requires": { 798 | "function-bind": "^1.1.1", 799 | "get-intrinsic": "^1.0.2" 800 | } 801 | }, 802 | "common-util": { 803 | "version": "file:../../common-util" 804 | }, 805 | "define-properties": { 806 | "version": "1.1.4", 807 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", 808 | "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", 809 | "requires": { 810 | "has-property-descriptors": "^1.0.0", 811 | "object-keys": "^1.1.1" 812 | } 813 | }, 814 | "es-abstract": { 815 | "version": "1.20.3", 816 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", 817 | "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", 818 | "requires": { 819 | "call-bind": "^1.0.2", 820 | "es-to-primitive": "^1.2.1", 821 | "function-bind": "^1.1.1", 822 | "function.prototype.name": "^1.1.5", 823 | "get-intrinsic": "^1.1.3", 824 | "get-symbol-description": "^1.0.0", 825 | "has": "^1.0.3", 826 | "has-property-descriptors": "^1.0.0", 827 | "has-symbols": "^1.0.3", 828 | "internal-slot": "^1.0.3", 829 | "is-callable": "^1.2.6", 830 | "is-negative-zero": "^2.0.2", 831 | "is-regex": "^1.1.4", 832 | "is-shared-array-buffer": "^1.0.2", 833 | "is-string": "^1.0.7", 834 | "is-weakref": "^1.0.2", 835 | "object-inspect": "^1.12.2", 836 | "object-keys": "^1.1.1", 837 | "object.assign": "^4.1.4", 838 | "regexp.prototype.flags": "^1.4.3", 839 | "safe-regex-test": "^1.0.0", 840 | "string.prototype.trimend": "^1.0.5", 841 | "string.prototype.trimstart": "^1.0.5", 842 | "unbox-primitive": "^1.0.2" 843 | } 844 | }, 845 | "es-to-primitive": { 846 | "version": "1.2.1", 847 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 848 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 849 | "requires": { 850 | "is-callable": "^1.1.4", 851 | "is-date-object": "^1.0.1", 852 | "is-symbol": "^1.0.2" 853 | } 854 | }, 855 | "events": { 856 | "version": "1.1.1", 857 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 858 | "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" 859 | }, 860 | "for-each": { 861 | "version": "0.3.3", 862 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 863 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 864 | "requires": { 865 | "is-callable": "^1.1.3" 866 | } 867 | }, 868 | "function-bind": { 869 | "version": "1.1.1", 870 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 871 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 872 | }, 873 | "function.prototype.name": { 874 | "version": "1.1.5", 875 | "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", 876 | "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", 877 | "requires": { 878 | "call-bind": "^1.0.2", 879 | "define-properties": "^1.1.3", 880 | "es-abstract": "^1.19.0", 881 | "functions-have-names": "^1.2.2" 882 | } 883 | }, 884 | "functions-have-names": { 885 | "version": "1.2.3", 886 | "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 887 | "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" 888 | }, 889 | "get-intrinsic": { 890 | "version": "1.1.3", 891 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 892 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 893 | "requires": { 894 | "function-bind": "^1.1.1", 895 | "has": "^1.0.3", 896 | "has-symbols": "^1.0.3" 897 | } 898 | }, 899 | "get-symbol-description": { 900 | "version": "1.0.0", 901 | "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", 902 | "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", 903 | "requires": { 904 | "call-bind": "^1.0.2", 905 | "get-intrinsic": "^1.1.1" 906 | } 907 | }, 908 | "has": { 909 | "version": "1.0.3", 910 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 911 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 912 | "requires": { 913 | "function-bind": "^1.1.1" 914 | } 915 | }, 916 | "has-bigints": { 917 | "version": "1.0.2", 918 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", 919 | "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" 920 | }, 921 | "has-property-descriptors": { 922 | "version": "1.0.0", 923 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", 924 | "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", 925 | "requires": { 926 | "get-intrinsic": "^1.1.1" 927 | } 928 | }, 929 | "has-symbols": { 930 | "version": "1.0.3", 931 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 932 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 933 | }, 934 | "has-tostringtag": { 935 | "version": "1.0.0", 936 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 937 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 938 | "requires": { 939 | "has-symbols": "^1.0.2" 940 | } 941 | }, 942 | "ieee754": { 943 | "version": "1.1.13", 944 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 945 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 946 | }, 947 | "inherits": { 948 | "version": "2.0.4", 949 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 950 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 951 | }, 952 | "internal-slot": { 953 | "version": "1.0.3", 954 | "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", 955 | "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", 956 | "requires": { 957 | "get-intrinsic": "^1.1.0", 958 | "has": "^1.0.3", 959 | "side-channel": "^1.0.4" 960 | } 961 | }, 962 | "is-arguments": { 963 | "version": "1.1.1", 964 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 965 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 966 | "requires": { 967 | "call-bind": "^1.0.2", 968 | "has-tostringtag": "^1.0.0" 969 | } 970 | }, 971 | "is-bigint": { 972 | "version": "1.0.4", 973 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", 974 | "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", 975 | "requires": { 976 | "has-bigints": "^1.0.1" 977 | } 978 | }, 979 | "is-boolean-object": { 980 | "version": "1.1.2", 981 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", 982 | "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", 983 | "requires": { 984 | "call-bind": "^1.0.2", 985 | "has-tostringtag": "^1.0.0" 986 | } 987 | }, 988 | "is-callable": { 989 | "version": "1.2.7", 990 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", 991 | "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" 992 | }, 993 | "is-date-object": { 994 | "version": "1.0.5", 995 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 996 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 997 | "requires": { 998 | "has-tostringtag": "^1.0.0" 999 | } 1000 | }, 1001 | "is-generator-function": { 1002 | "version": "1.0.10", 1003 | "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", 1004 | "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", 1005 | "requires": { 1006 | "has-tostringtag": "^1.0.0" 1007 | } 1008 | }, 1009 | "is-negative-zero": { 1010 | "version": "2.0.2", 1011 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", 1012 | "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" 1013 | }, 1014 | "is-number-object": { 1015 | "version": "1.0.7", 1016 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", 1017 | "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", 1018 | "requires": { 1019 | "has-tostringtag": "^1.0.0" 1020 | } 1021 | }, 1022 | "is-regex": { 1023 | "version": "1.1.4", 1024 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 1025 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 1026 | "requires": { 1027 | "call-bind": "^1.0.2", 1028 | "has-tostringtag": "^1.0.0" 1029 | } 1030 | }, 1031 | "is-shared-array-buffer": { 1032 | "version": "1.0.2", 1033 | "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", 1034 | "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", 1035 | "requires": { 1036 | "call-bind": "^1.0.2" 1037 | } 1038 | }, 1039 | "is-string": { 1040 | "version": "1.0.7", 1041 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 1042 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 1043 | "requires": { 1044 | "has-tostringtag": "^1.0.0" 1045 | } 1046 | }, 1047 | "is-symbol": { 1048 | "version": "1.0.4", 1049 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", 1050 | "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", 1051 | "requires": { 1052 | "has-symbols": "^1.0.2" 1053 | } 1054 | }, 1055 | "is-typed-array": { 1056 | "version": "1.1.9", 1057 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", 1058 | "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", 1059 | "requires": { 1060 | "available-typed-arrays": "^1.0.5", 1061 | "call-bind": "^1.0.2", 1062 | "es-abstract": "^1.20.0", 1063 | "for-each": "^0.3.3", 1064 | "has-tostringtag": "^1.0.0" 1065 | } 1066 | }, 1067 | "is-weakref": { 1068 | "version": "1.0.2", 1069 | "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", 1070 | "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", 1071 | "requires": { 1072 | "call-bind": "^1.0.2" 1073 | } 1074 | }, 1075 | "isarray": { 1076 | "version": "1.0.0", 1077 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1078 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" 1079 | }, 1080 | "jmespath": { 1081 | "version": "0.16.0", 1082 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", 1083 | "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" 1084 | }, 1085 | "object-inspect": { 1086 | "version": "1.12.2", 1087 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", 1088 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" 1089 | }, 1090 | "object-keys": { 1091 | "version": "1.1.1", 1092 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 1093 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 1094 | }, 1095 | "object.assign": { 1096 | "version": "4.1.4", 1097 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", 1098 | "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", 1099 | "requires": { 1100 | "call-bind": "^1.0.2", 1101 | "define-properties": "^1.1.4", 1102 | "has-symbols": "^1.0.3", 1103 | "object-keys": "^1.1.1" 1104 | } 1105 | }, 1106 | "punycode": { 1107 | "version": "1.3.2", 1108 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 1109 | "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" 1110 | }, 1111 | "querystring": { 1112 | "version": "0.2.0", 1113 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 1114 | "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" 1115 | }, 1116 | "regexp.prototype.flags": { 1117 | "version": "1.4.3", 1118 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", 1119 | "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", 1120 | "requires": { 1121 | "call-bind": "^1.0.2", 1122 | "define-properties": "^1.1.3", 1123 | "functions-have-names": "^1.2.2" 1124 | } 1125 | }, 1126 | "safe-buffer": { 1127 | "version": "5.2.1", 1128 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1129 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1130 | }, 1131 | "safe-regex-test": { 1132 | "version": "1.0.0", 1133 | "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", 1134 | "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", 1135 | "requires": { 1136 | "call-bind": "^1.0.2", 1137 | "get-intrinsic": "^1.1.3", 1138 | "is-regex": "^1.1.4" 1139 | } 1140 | }, 1141 | "sax": { 1142 | "version": "1.2.1", 1143 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 1144 | "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" 1145 | }, 1146 | "side-channel": { 1147 | "version": "1.0.4", 1148 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1149 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1150 | "requires": { 1151 | "call-bind": "^1.0.0", 1152 | "get-intrinsic": "^1.0.2", 1153 | "object-inspect": "^1.9.0" 1154 | } 1155 | }, 1156 | "string.prototype.trimend": { 1157 | "version": "1.0.5", 1158 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", 1159 | "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", 1160 | "requires": { 1161 | "call-bind": "^1.0.2", 1162 | "define-properties": "^1.1.4", 1163 | "es-abstract": "^1.19.5" 1164 | } 1165 | }, 1166 | "string.prototype.trimstart": { 1167 | "version": "1.0.5", 1168 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", 1169 | "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", 1170 | "requires": { 1171 | "call-bind": "^1.0.2", 1172 | "define-properties": "^1.1.4", 1173 | "es-abstract": "^1.19.5" 1174 | } 1175 | }, 1176 | "unbox-primitive": { 1177 | "version": "1.0.2", 1178 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", 1179 | "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", 1180 | "requires": { 1181 | "call-bind": "^1.0.2", 1182 | "has-bigints": "^1.0.2", 1183 | "has-symbols": "^1.0.3", 1184 | "which-boxed-primitive": "^1.0.2" 1185 | } 1186 | }, 1187 | "url": { 1188 | "version": "0.10.3", 1189 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 1190 | "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", 1191 | "requires": { 1192 | "punycode": "1.3.2", 1193 | "querystring": "0.2.0" 1194 | } 1195 | }, 1196 | "util": { 1197 | "version": "0.12.4", 1198 | "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", 1199 | "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", 1200 | "requires": { 1201 | "inherits": "^2.0.3", 1202 | "is-arguments": "^1.0.4", 1203 | "is-generator-function": "^1.0.7", 1204 | "is-typed-array": "^1.1.3", 1205 | "safe-buffer": "^5.1.2", 1206 | "which-typed-array": "^1.1.2" 1207 | } 1208 | }, 1209 | "uuid": { 1210 | "version": "8.0.0", 1211 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", 1212 | "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" 1213 | }, 1214 | "which-boxed-primitive": { 1215 | "version": "1.0.2", 1216 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", 1217 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", 1218 | "requires": { 1219 | "is-bigint": "^1.0.1", 1220 | "is-boolean-object": "^1.1.0", 1221 | "is-number-object": "^1.0.4", 1222 | "is-string": "^1.0.5", 1223 | "is-symbol": "^1.0.3" 1224 | } 1225 | }, 1226 | "which-typed-array": { 1227 | "version": "1.1.8", 1228 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", 1229 | "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", 1230 | "requires": { 1231 | "available-typed-arrays": "^1.0.5", 1232 | "call-bind": "^1.0.2", 1233 | "es-abstract": "^1.20.0", 1234 | "for-each": "^0.3.3", 1235 | "has-tostringtag": "^1.0.0", 1236 | "is-typed-array": "^1.1.9" 1237 | } 1238 | }, 1239 | "xml2js": { 1240 | "version": "0.5.0", 1241 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", 1242 | "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", 1243 | "requires": { 1244 | "sax": ">=0.6.0", 1245 | "xmlbuilder": "~11.0.0" 1246 | } 1247 | }, 1248 | "xmlbuilder": { 1249 | "version": "11.0.1", 1250 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 1251 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" 1252 | } 1253 | } 1254 | } 1255 | -------------------------------------------------------------------------------- /src/lambda/outboundMessageHandler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "outboundMessageHandler", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT-0", 6 | "dependencies": { 7 | "common-util": "file:./../../common-util", 8 | "aws-sdk": "^2.1020.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/chat-message-streaming-examples.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 5 | import * as cdk from '@aws-cdk/core'; 6 | import * as ChatMessageStreamingExamples from '../lib/chat-message-streaming-examples-stack'; 7 | 8 | test('Empty Stack', () => { 9 | const app = new cdk.App(); 10 | // WHEN 11 | const stack = new ChatMessageStreamingExamples.ChatMessageStreamingExamplesStack(app, 'MyTestStack'); 12 | // THEN 13 | expectCDK(stack).to(matchTemplate({ 14 | "Resources": {} 15 | }, MatchStyle.EXACT)) 16 | }); 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | --------------------------------------------------------------------------------