serverless-spy 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.eslintrc.yml +97 -0
  2. package/.gitattributes +24 -0
  3. package/.husky/pre-commit +4 -0
  4. package/.jsii +3199 -0
  5. package/.prettierignore +1 -0
  6. package/.prettierrc.json +5 -0
  7. package/API.md +123 -0
  8. package/LICENSE +202 -0
  9. package/README.md +1 -0
  10. package/cli/cli.ts +97 -0
  11. package/cli/index.html +43 -0
  12. package/cli/package-lock.json +2356 -0
  13. package/cli/package.json +14 -0
  14. package/cli/serverlessSpy.js +73 -0
  15. package/cli/spy_log.json +0 -0
  16. package/cli/style.css +43 -0
  17. package/cli/ws.ts +79 -0
  18. package/common/getWebSocketUrl.ts +68 -0
  19. package/common/package.json +22 -0
  20. package/common/spyEvents/DynamoDBSpyEvent.ts +12 -0
  21. package/common/spyEvents/EventBridgeRuleSpyEvent.ts +13 -0
  22. package/common/spyEvents/EventBridgeSpyEvent.ts +13 -0
  23. package/common/spyEvents/FunctionConsoleSpyEvent.ts +11 -0
  24. package/common/spyEvents/FunctionContext.ts +8 -0
  25. package/common/spyEvents/FunctionErrorSpyEvent.ts +9 -0
  26. package/common/spyEvents/FunctionRequestSpyEvent.ts +8 -0
  27. package/common/spyEvents/FunctionResponseSpyEvent.ts +10 -0
  28. package/common/spyEvents/S3SpyEvent.ts +9 -0
  29. package/common/spyEvents/SnsSubscriptionSpyEvent.ts +12 -0
  30. package/common/spyEvents/SnsTopicSpyEvent.ts +12 -0
  31. package/common/spyEvents/SpyEvent.ts +3 -0
  32. package/common/spyEvents/SpyMessage.ts +7 -0
  33. package/common/spyEvents/SqsSpyEvent.ts +8 -0
  34. package/functions/onConnect.ts +29 -0
  35. package/functions/onDisconnect.ts +28 -0
  36. package/functions/sendMessage.ts +277 -0
  37. package/functions/tsconfig.dev.json +16 -0
  38. package/functions/tsconfig.json +16 -0
  39. package/index.ts +2 -0
  40. package/lambda-extension/aws/Common.ts +88 -0
  41. package/lambda-extension/aws/Errors.ts +140 -0
  42. package/lambda-extension/aws/UserFunction.ts +169 -0
  43. package/lambda-extension/interceptor.ts +146 -0
  44. package/lambda-extension/spy-wrapper +4 -0
  45. package/lambda-extension/tsconfig.json +16 -0
  46. package/lib/ServerlessSpy.d.ts +27 -0
  47. package/lib/ServerlessSpy.js +301 -0
  48. package/lib/index.d.ts +1 -0
  49. package/lib/index.js +14 -0
  50. package/listener/PrettifyForDisplay.ts +5 -0
  51. package/listener/RecursivePartial.ts +9 -0
  52. package/listener/SpyHandlers.ts.ts +190 -0
  53. package/listener/SpyListener.ts +104 -0
  54. package/listener/createServerlessSpyListener.ts +258 -0
  55. package/listener/index.ts +1 -0
  56. package/listener/matchers.ts +53 -0
  57. package/listener/package.json +19 -0
  58. package/listener/setup.ts +21 -0
  59. package/listener/tsconfig.dev.json +16 -0
  60. package/listener/tsconfig.json +16 -0
  61. package/package.json +132 -0
  62. package/scripts/run-task +1836 -0
  63. package/sst-esbuild/esbuild.js +57 -0
  64. package/x_jest.config.ts +203 -0
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "serverless-spy-cli",
3
+ "version": "0.0.0",
4
+ "description": "",
5
+ "scripts": {},
6
+ "author": "",
7
+ "license": "ISC",
8
+ "dependencies": {
9
+ "@aws-sdk/client-sts": "^3.142.0",
10
+ "@aws-sdk/credential-providers": "^3.142.0",
11
+ "esbuild-runner": "^2.2.1",
12
+ "ws": "^8.8.1"
13
+ }
14
+ }
@@ -0,0 +1,73 @@
1
+ function run() {
2
+ const tableBody = document.getElementById("tableBody");
3
+ var modal = document.getElementById("myModal");
4
+ var span = document.getElementsByClassName("close")[0];
5
+ const modalContent = document.getElementById("modalContent");
6
+ span.onclick = function () {
7
+ modal.style.display = "none";
8
+ };
9
+ window.onclick = function (event) {
10
+ if (event.target == modal) {
11
+ modal.style.display = "none";
12
+ }
13
+ };
14
+
15
+ tableBody.addEventListener("click", function (e) {
16
+ const dataJson = JSON.stringify(
17
+ JSON.parse(e.target.parentElement.children[2].textContent),
18
+ null,
19
+ 2
20
+ );
21
+ modalContent.innerText = dataJson;
22
+ modal.style.display = "block";
23
+ //alert();
24
+ });
25
+
26
+ const url = "SERVERLESS_SPY_WS_URL";
27
+ //"wss://m6g3w6ttdh.execute-api.eu-west-1.amazonaws.com/prod?X-Amz-Security-Token=IQoJb3JpZ2luX2VjELL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCWV1LXdlc3QtMSJGMEQCIDk%2BzQd0RC2DmEgCJEPoRFAkmnhCZl%2BSIlEG4TummRkIAiBIgNG8Yw%2FOAew3XNAE3yrNCK0G0q%2BZI8MwjEa8xoEonirrAQgbEAQaDDA3NjU4NTk2NzA3NSIMxVUxKhwdqGjFOsRtKsgB2eotQMFgl4CrD8uy%2Fe1UE6K4HVc6uWo0gdjgHMPb5C9Mq0wFgHTiDFjFxIFkJpmCiTRXKqNFrx5lpo9C5Ml1bpcv%2BTopT6wvpbFksxChhhnaqUDHiBaGuys8zEy8Bjlu4jF0nKccmUYdieD70%2FXz99OU4wKPxXyWbr0w82y1Xd6ag8g4VNx3EXEdULq7vkM6H2shSAH8EUlZoTD%2BqvIQvyKS8nta3ebhprO%2BsLRQv2YC6k1w46jHuOjtI7hSnmfSKnkUqAxS6g4wgZrFlwY6mQFNSXz7jSF3IRtdcpMEIp%2Bnghf2LhvZKeW6GkZs4DMeuhPjoBehfmXLJIJWgcvLoheQvIt28ddgHU60%2BoRKhZTxQKhpvSOaAbI6M%2B%2FyHvkC2hTR4fEBtESAMnzo8WocfxdaQQ0YVYq2m5NKSXs3QrrsYIIirkFYhB%2BjAwG8bqBAuE9SQNeIthHjT27hB7KUeIIoXtwSVGutv70%3D&X-Amz-Date=20220808T175057Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIARDVHBDXR4QCRLL4O%2F20220808%2Feu-west-1%2Fexecute-api%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=d58ab2b792a69f20f8e3bf9bf4fb19c8ec8f271bd1b594f8cd25c02d4c85521d";
28
+ const ws = new WebSocket(url);
29
+
30
+ ws.addEventListener("open", function (event) {
31
+ console.log("connected " + new Date().toISOString());
32
+ });
33
+
34
+ ws.addEventListener("message", function ({ data }) {
35
+ //debugger;
36
+ //console.log(`From server: ${data}`);
37
+
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(data);
41
+ } catch (err) {
42
+ console.error("Can not parse " + data);
43
+ }
44
+
45
+ addLog(parsed);
46
+
47
+ // if (parsed) {
48
+ // console.log(
49
+ // `\x1b[47m\x1b[34m${parsed.timestamp} \x1b[31m🍕 ${
50
+ // parsed.serviceKey
51
+ // }\x1b[0m\x1b[32m\n${JSON.stringify(parsed.data, null, 2)}\x1b[0m`
52
+ // );
53
+ // }
54
+ });
55
+ ws.addEventListener("close", function () {
56
+ console.log("disconnected " + new Date().toISOString());
57
+ });
58
+
59
+ function addLog(data) {
60
+ const newRow = document.createElement("tr");
61
+ newRow.innerHTML = `
62
+ <td>${new Date(data.timestamp).toLocaleTimeString()}</td>
63
+ <td>${data.serviceKey}</td>
64
+ <td>${JSON.stringify(data.data)}</td>
65
+ `;
66
+ tableBody.append(newRow);
67
+ }
68
+ }
69
+
70
+ document.addEventListener("DOMContentLoaded", function () {
71
+ console.log("RUN");
72
+ run();
73
+ });
File without changes
package/cli/style.css ADDED
@@ -0,0 +1,43 @@
1
+ .log {
2
+ white-space: nowrap
3
+ }
4
+
5
+ /* The Modal (background)
6
+ https://www.w3schools.com/howto/howto_css_modals.asp
7
+ */
8
+ .modal {
9
+ display: none; /* Hidden by default */
10
+ position: fixed; /* Stay in place */
11
+ z-index: 1; /* Sit on top */
12
+ left: 0;
13
+ top: 0;
14
+ width: 100%; /* Full width */
15
+ height: 100%; /* Full height */
16
+ overflow: auto; /* Enable scroll if needed */
17
+ background-color: rgb(0,0,0); /* Fallback color */
18
+ background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
19
+ }
20
+
21
+ /* Modal Content/Box */
22
+ .modal-content {
23
+ background-color: #fefefe;
24
+ margin: 15% auto; /* 15% from the top and centered */
25
+ padding: 20px;
26
+ border: 1px solid #888;
27
+ width: 80%; /* Could be more or less, depending on screen size */
28
+ }
29
+
30
+ /* The Close Button */
31
+ .close {
32
+ color: #aaa;
33
+ float: right;
34
+ font-size: 28px;
35
+ font-weight: bold;
36
+ }
37
+
38
+ .close:hover,
39
+ .close:focus {
40
+ color: black;
41
+ text-decoration: none;
42
+ cursor: pointer;
43
+ }
package/cli/ws.ts ADDED
@@ -0,0 +1,79 @@
1
+ import * as fs from 'fs';
2
+ import * as readline from 'readline';
3
+ import WebSocket from 'ws';
4
+ import { getWebSocketUrl } from '../common/getWebSocketUrl';
5
+
6
+ async function run() {
7
+ // var credentialsProvider = awsCp.fromTemporaryCredentials();
8
+ // const credentials = await credentialsProvider();
9
+
10
+ // const client = new STSClient({});
11
+ // client.send()
12
+
13
+ // const command = new GetSessionTokenCommand({});
14
+ // const { Credentials: credentials } = await client.send(command);
15
+ // //const credentials = s.Credentials;
16
+
17
+ // console.log("credentials", credentials);
18
+
19
+ // const url = process.argv[2];
20
+ const url = 'wss://preh1xo1xh.execute-api.eu-west-1.amazonaws.com/prod';
21
+
22
+ // https://medium.com/@o.bredenberg/iam-sign-your-api-gateway-websocket-connection-request-no-custom-auth-from-your-frontend-65451166757d
23
+ const url2 = await getWebSocketUrl(url);
24
+
25
+ const ws = new WebSocket(url2);
26
+
27
+ const fileDescriptor = fs.openSync('spy_log.json', 'w');
28
+ // 📢📁⚡️💾 💽 💾 💿 📀🛢🪣📑🔊
29
+
30
+ ws.on('open', () => console.log(`connected ${new Date().toISOString()}`));
31
+ ws.on('message', (data: any) => {
32
+ // console.log(`From server: ${data}`);
33
+
34
+ let parsed;
35
+ try {
36
+ parsed = JSON.parse(data);
37
+ } catch (err) {
38
+ console.error(`Can not parse ${data}`);
39
+ }
40
+
41
+ if (parsed) {
42
+ console.log(
43
+ `\x1b[47m\x1b[34m${parsed.timestamp} \x1b[31m🍕 ${
44
+ parsed.serviceKey
45
+ }\x1b[0m\x1b[32m\n${JSON.stringify(parsed.data, null, 2)}\x1b[0m`
46
+ );
47
+ }
48
+
49
+ fs.write(fileDescriptor, data, (error) => {
50
+ if (!error) {
51
+ // fs.close(fileDescriptor);
52
+ } else {
53
+ console.error(error);
54
+ }
55
+ });
56
+ fs.write(fileDescriptor, '\n', (error) => {
57
+ if (!error) {
58
+ // fs.close(fileDescriptor);
59
+ } else {
60
+ console.error(error);
61
+ }
62
+ });
63
+ });
64
+ ws.on('close', () => {
65
+ console.log(`disconnected ${new Date().toISOString()}`);
66
+ fs.close(fileDescriptor);
67
+ process.exit();
68
+ });
69
+
70
+ readline
71
+ .createInterface({
72
+ input: process.stdin,
73
+ output: process.stdout,
74
+ })
75
+ .on('line', (data) => {
76
+ ws.send(data);
77
+ });
78
+ }
79
+ run().catch(console.error);
@@ -0,0 +1,68 @@
1
+ import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
2
+ import { Credentials } from '@aws-sdk/types';
3
+ import * as aws4 from 'aws4';
4
+
5
+ // ""wss://m6g3w6ttdh.execute-api.eu-west-1.amazonaws.com/prod";"
6
+
7
+ export async function getWebSocketUrl(url: string, credentials?: Credentials) {
8
+ const urlParsed = parseUrl(url);
9
+ const hostParts = urlParsed!.host.split('.');
10
+
11
+ console.log('urlParsed', urlParsed);
12
+
13
+ if (!credentials) {
14
+ const credentialsProvider = fromNodeProviderChain();
15
+ credentials = await credentialsProvider();
16
+ }
17
+
18
+ const AWS_REGION = hostParts[2]; // The region of your API-gateway
19
+ const SOCKET_HOST = hostParts[0]; // Your API-gateway ID
20
+ const ENV = urlParsed!.pathname!.replace(/^\//, ''); // The stage of your target deployment
21
+ const WEBSOCKET_URL = `${SOCKET_HOST}.execute-api.${AWS_REGION}.amazonaws.com`; // Don't prepend with wss!
22
+ // const AWS_REGION = "eu-west-1"; // The region of your API-gateway
23
+ // const SOCKET_HOST = "m6g3w6ttdh"; // Your API-gateway ID
24
+ // const ENV = "prod"; // The stage of your target deployment
25
+ // const WEBSOCKET_URL = `${SOCKET_HOST}.execute-api.${AWS_REGION}.amazonaws.com`; // Don't prepend with wss!
26
+
27
+ // Get a signed path
28
+ const { path } = aws4.sign(
29
+ {
30
+ method: 'GET',
31
+ host: WEBSOCKET_URL,
32
+ // path: `/${ENV}?X-Amz-Security-Token=${encodeURIComponent(
33
+ // credentials!.SessionToken!
34
+ // )}`,
35
+ path: `/${ENV}`,
36
+ service: 'execute-api',
37
+ region: AWS_REGION,
38
+ signQuery: true,
39
+ },
40
+ {
41
+ accessKeyId: credentials.accessKeyId,
42
+ secretAccessKey: credentials.secretAccessKey,
43
+ sessionToken: credentials.sessionToken,
44
+ }
45
+ );
46
+
47
+ const url2 = `wss://${WEBSOCKET_URL}${path}`;
48
+ console.log('path', url2);
49
+ return url2;
50
+ }
51
+
52
+ function parseUrl(href: string) {
53
+ const match = href.match(
54
+ /^(wss?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/
55
+ );
56
+ return (
57
+ match && {
58
+ href,
59
+ protocol: match[1],
60
+ host: match[2],
61
+ hostname: match[3],
62
+ port: match[4],
63
+ pathname: match[5],
64
+ search: match[6],
65
+ hash: match[7],
66
+ }
67
+ );
68
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "serverless-spy-common",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "private": true,
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
+ },
10
+ "author": "",
11
+ "license": "ISC",
12
+ "devDependencies": {
13
+ "@types/aws4": "^1.11.2"
14
+ },
15
+ "dependencies": {
16
+ "@aws-sdk/credential-providers": "^3.145.0",
17
+ "@aws-sdk/types": "^3.127.0",
18
+ "@aws-sdk/util-dynamodb": "^3.145.0",
19
+ "aws-lambda": "^1.0.7",
20
+ "aws4": "^1.11.0"
21
+ }
22
+ }
@@ -0,0 +1,12 @@
1
+ import { NativeAttributeValue } from '@aws-sdk/util-dynamodb';
2
+ import { DynamoDBRecord } from 'aws-lambda';
3
+ import { SpyEvent } from './SpyEvent';
4
+
5
+ // asdasd
6
+ export interface DynamoDBSpyEvent<TData = any> extends SpyEvent {
7
+ spyEventType: 'DynamoDB';
8
+ eventName: DynamoDBRecord['eventName'];
9
+ newImage: TData;
10
+ keys: Record<string, NativeAttributeValue>;
11
+ oldImage?: TData;
12
+ }
@@ -0,0 +1,13 @@
1
+ import { SpyEvent } from './SpyEvent';
2
+
3
+ export interface EventBridgeRuleSpyEvent<
4
+ MessageType = any,
5
+ EventBridgeDetailType extends string = string
6
+ > extends SpyEvent {
7
+ spyEventType: 'EventBridgeRule';
8
+ detail: MessageType;
9
+ detailType: EventBridgeDetailType;
10
+ source: string;
11
+ time: string;
12
+ account: string;
13
+ }
@@ -0,0 +1,13 @@
1
+ import { SpyEvent } from './SpyEvent';
2
+
3
+ export interface EventBridgeSpyEvent<
4
+ MessageType = any,
5
+ EventBridgeDetailType extends string = string
6
+ > extends SpyEvent {
7
+ spyEventType: 'EventBridge';
8
+ detail: MessageType;
9
+ detailType: EventBridgeDetailType;
10
+ source: string;
11
+ time: string;
12
+ account: string;
13
+ }
@@ -0,0 +1,11 @@
1
+ import { SpyEvent } from './SpyEvent';
2
+
3
+ export interface FunctionConsoleSpyEvent<TRequest = any> extends SpyEvent {
4
+ spyEventType: 'FunctionConsole';
5
+ request: TRequest;
6
+ console: {
7
+ type: 'log' | 'debug' | 'info' | 'error' | 'warn';
8
+ message?: any;
9
+ optionalParams: any[];
10
+ };
11
+ }
@@ -0,0 +1,8 @@
1
+ import { ClientContext, CognitoIdentity } from 'aws-lambda';
2
+
3
+ export interface FunctionContext {
4
+ functionName: string;
5
+ awsRequestId: string;
6
+ identity?: CognitoIdentity | undefined;
7
+ clientContext?: ClientContext | undefined;
8
+ }
@@ -0,0 +1,9 @@
1
+ import { FunctionContext } from './FunctionContext';
2
+ import { SpyEvent } from './SpyEvent';
3
+
4
+ export interface FunctionErrorSpyEvent<TRequest = any> extends SpyEvent {
5
+ spyEventType: 'FunctionError';
6
+ request: TRequest;
7
+ context: FunctionContext;
8
+ error: any;
9
+ }
@@ -0,0 +1,8 @@
1
+ import { FunctionContext } from './FunctionContext';
2
+ import { SpyEvent } from './SpyEvent';
3
+
4
+ export interface FunctionRequestSpyEvent<TData = any> extends SpyEvent {
5
+ spyEventType: 'FunctionRequest';
6
+ request: TData;
7
+ context: FunctionContext;
8
+ }
@@ -0,0 +1,10 @@
1
+ import { FunctionContext } from './FunctionContext';
2
+ import { SpyEvent } from './SpyEvent';
3
+
4
+ export interface FunctionResponseSpyEvent<TRequest = any, TResponse = any>
5
+ extends SpyEvent {
6
+ spyEventType: 'FunctionResponse';
7
+ request: TRequest;
8
+ context: FunctionContext;
9
+ response: TResponse;
10
+ }
@@ -0,0 +1,9 @@
1
+ import { SpyEvent } from './SpyEvent';
2
+
3
+ export interface S3SpyEvent extends SpyEvent {
4
+ spyEventType: 'S3';
5
+ eventName: string;
6
+ eventTime: string;
7
+ bucket: string;
8
+ key: string;
9
+ }
@@ -0,0 +1,12 @@
1
+ import { SNSMessageAttributes } from 'aws-lambda';
2
+ import { SpyEvent } from './SpyEvent';
3
+
4
+ export interface SnsSubscriptionSpyEvent<MessageType = any> extends SpyEvent {
5
+ spyEventType: 'FunctionSnsSubscription';
6
+ message: MessageType;
7
+ subject: string;
8
+ timestamp: string;
9
+ topicArn: string;
10
+ messageId: string;
11
+ messageAttributes: SNSMessageAttributes;
12
+ }
@@ -0,0 +1,12 @@
1
+ import { SNSMessageAttributes } from 'aws-lambda';
2
+ import { SpyEvent } from './SpyEvent';
3
+
4
+ export interface SnsTopicSpyEvent<MessageType = any> extends SpyEvent {
5
+ spyEventType: 'FunctionSnsTopic';
6
+ message: MessageType;
7
+ subject: string;
8
+ timestamp: string;
9
+ topicArn: string;
10
+ messageId: string;
11
+ messageAttributes: SNSMessageAttributes;
12
+ }
@@ -0,0 +1,3 @@
1
+ export interface SpyEvent {
2
+ spyEventType: string;
3
+ }
@@ -0,0 +1,7 @@
1
+ import { SpyEvent } from './SpyEvent';
2
+
3
+ export type SpyMessage<T extends SpyEvent = SpyEvent> = {
4
+ timestamp: string;
5
+ serviceKey: string;
6
+ data: T;
7
+ };
@@ -0,0 +1,8 @@
1
+ import { SQSMessageAttributes } from 'aws-lambda';
2
+ import { SpyEvent } from './SpyEvent';
3
+
4
+ export interface SqsSpyEvent<MessageType = any> extends SpyEvent {
5
+ spyEventType: 'Sqs';
6
+ body: MessageType;
7
+ messageAttributes: SQSMessageAttributes;
8
+ }
@@ -0,0 +1,29 @@
1
+ import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
2
+ import { APIGatewayEvent } from 'aws-lambda';
3
+
4
+ const ddb = new DynamoDBClient({
5
+ region: process.env.AWS_REGION,
6
+ });
7
+
8
+ exports.handler = async (event: APIGatewayEvent) => {
9
+ console.log('EVENT', JSON.stringify(event));
10
+
11
+ const putParams = new PutItemCommand({
12
+ TableName: process.env.TABLE_NAME as string,
13
+ Item: {
14
+ connectionId: { S: event.requestContext.connectionId! },
15
+ },
16
+ });
17
+
18
+ try {
19
+ await ddb.send(putParams);
20
+ } catch (err) {
21
+ console.error(err);
22
+ return {
23
+ statusCode: 500,
24
+ body: 'Failed to connect: ' + JSON.stringify(err),
25
+ };
26
+ }
27
+
28
+ return { statusCode: 200, body: 'Connected.' };
29
+ };
@@ -0,0 +1,28 @@
1
+ import { DeleteItemCommand, DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
+ import { APIGatewayEvent } from 'aws-lambda';
3
+
4
+ const ddb = new DynamoDBClient({
5
+ region: process.env.AWS_REGION,
6
+ });
7
+ export const handler = async (event: APIGatewayEvent) => {
8
+ console.log('EVENT', JSON.stringify(event));
9
+
10
+ const deleteParams = new DeleteItemCommand({
11
+ TableName: process.env.TABLE_NAME,
12
+ Key: {
13
+ connectionId: { S: event.requestContext.connectionId! },
14
+ },
15
+ });
16
+
17
+ try {
18
+ await ddb.send(deleteParams);
19
+ } catch (err) {
20
+ console.error(err);
21
+ return {
22
+ statusCode: 500,
23
+ body: 'Failed to disconnect: ' + JSON.stringify(err),
24
+ };
25
+ }
26
+
27
+ return { statusCode: 200, body: 'Disconnected.' };
28
+ };