pika-shared 1.0.2
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.
- package/LICENSE +9 -0
- package/dist/index.d.mts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +9 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types/chatbot/bedrock-lambda-error.d.mts +12 -0
- package/dist/types/chatbot/bedrock-lambda-error.d.ts +12 -0
- package/dist/types/chatbot/bedrock-lambda-error.js +12 -0
- package/dist/types/chatbot/bedrock-lambda-error.js.map +1 -0
- package/dist/types/chatbot/bedrock-lambda-error.mjs +10 -0
- package/dist/types/chatbot/bedrock-lambda-error.mjs.map +1 -0
- package/dist/types/chatbot/bedrock.d.mts +489 -0
- package/dist/types/chatbot/bedrock.d.ts +489 -0
- package/dist/types/chatbot/bedrock.js +4 -0
- package/dist/types/chatbot/bedrock.js.map +1 -0
- package/dist/types/chatbot/bedrock.mjs +3 -0
- package/dist/types/chatbot/bedrock.mjs.map +1 -0
- package/dist/types/chatbot/chatbot-types.d.mts +2240 -0
- package/dist/types/chatbot/chatbot-types.d.ts +2240 -0
- package/dist/types/chatbot/chatbot-types.js +358 -0
- package/dist/types/chatbot/chatbot-types.js.map +1 -0
- package/dist/types/chatbot/chatbot-types.mjs +301 -0
- package/dist/types/chatbot/chatbot-types.mjs.map +1 -0
- package/dist/types/upload-types.d.mts +29 -0
- package/dist/types/upload-types.d.ts +29 -0
- package/dist/types/upload-types.js +4 -0
- package/dist/types/upload-types.js.map +1 -0
- package/dist/types/upload-types.mjs +3 -0
- package/dist/types/upload-types.mjs.map +1 -0
- package/dist/util/api-gateway-utils.d.mts +133 -0
- package/dist/util/api-gateway-utils.d.ts +133 -0
- package/dist/util/api-gateway-utils.js +77 -0
- package/dist/util/api-gateway-utils.js.map +1 -0
- package/dist/util/api-gateway-utils.mjs +73 -0
- package/dist/util/api-gateway-utils.mjs.map +1 -0
- package/dist/util/bedrock.d.mts +53 -0
- package/dist/util/bedrock.d.ts +53 -0
- package/dist/util/bedrock.js +80 -0
- package/dist/util/bedrock.js.map +1 -0
- package/dist/util/bedrock.mjs +74 -0
- package/dist/util/bedrock.mjs.map +1 -0
- package/dist/util/chatbot-shared-utils.d.mts +42 -0
- package/dist/util/chatbot-shared-utils.d.ts +42 -0
- package/dist/util/chatbot-shared-utils.js +44 -0
- package/dist/util/chatbot-shared-utils.js.map +1 -0
- package/dist/util/chatbot-shared-utils.mjs +33 -0
- package/dist/util/chatbot-shared-utils.mjs.map +1 -0
- package/dist/util/http-status-error.d.mts +9 -0
- package/dist/util/http-status-error.d.ts +9 -0
- package/dist/util/http-status-error.js +13 -0
- package/dist/util/http-status-error.js.map +1 -0
- package/dist/util/http-status-error.mjs +11 -0
- package/dist/util/http-status-error.mjs.map +1 -0
- package/dist/util/jwt.d.mts +24 -0
- package/dist/util/jwt.d.ts +24 -0
- package/dist/util/jwt.js +54 -0
- package/dist/util/jwt.js.map +1 -0
- package/dist/util/jwt.mjs +31 -0
- package/dist/util/jwt.mjs.map +1 -0
- package/dist/util/server-client-utils.d.mts +15 -0
- package/dist/util/server-client-utils.d.ts +15 -0
- package/dist/util/server-client-utils.js +37 -0
- package/dist/util/server-client-utils.js.map +1 -0
- package/dist/util/server-client-utils.mjs +34 -0
- package/dist/util/server-client-utils.mjs.map +1 -0
- package/dist/util/server-utils.d.mts +4 -0
- package/dist/util/server-utils.d.ts +4 -0
- package/dist/util/server-utils.js +20 -0
- package/dist/util/server-utils.js.map +1 -0
- package/dist/util/server-utils.mjs +17 -0
- package/dist/util/server-utils.mjs.map +1 -0
- package/package.json +70 -0
- package/readme.md +3 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { APIGatewayProxyEventV2WithRequestContext, APIGatewayEventRequestContextV2, Context, APIGatewayProxyResultV2, APIGatewayProxyHandlerV2, APIGatewayProxyStructuredResultV2 } from 'aws-lambda';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Utility functions and types for AWS API Gateway Lambda integration.
|
|
5
|
+
* This module provides type-safe wrappers and helpers for handling
|
|
6
|
+
* API Gateway events and responses in AWS Lambda functions.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extends the standard Error interface to include an optional HTTP status code.
|
|
11
|
+
* This allows for standardized error handling with appropriate HTTP responses.
|
|
12
|
+
*/
|
|
13
|
+
interface HttpError extends Error {
|
|
14
|
+
statusCode?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A type that enhances the standard API Gateway event by replacing the body property
|
|
18
|
+
* with a strongly-typed version rather than the default string representation.
|
|
19
|
+
*
|
|
20
|
+
* @template TRequestBody - The type of the request body after parsing
|
|
21
|
+
* @template TRequestContext - The type of the request context
|
|
22
|
+
*/
|
|
23
|
+
interface APIGatewayProxyEventPikaWithRequestContext<TRequestBody, TRequestContext> extends Omit<APIGatewayProxyEventV2WithRequestContext<TRequestContext>, 'body'> {
|
|
24
|
+
body: TRequestBody;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A specialized API Gateway event type that includes:
|
|
28
|
+
* 1. A strongly-typed request body
|
|
29
|
+
* 2. The standard API Gateway request context
|
|
30
|
+
* 3. Additional path and httpMethod properties for routing
|
|
31
|
+
*
|
|
32
|
+
* @template TRequestBody - The type of the parsed request body (defaults to never if not provided)
|
|
33
|
+
*/
|
|
34
|
+
type APIGatewayProxyEventPika<TRequestBody = never> = APIGatewayProxyEventPikaWithRequestContext<TRequestBody, APIGatewayEventRequestContextV2> & {
|
|
35
|
+
path: string;
|
|
36
|
+
httpMethod: string;
|
|
37
|
+
resource: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* A generic handler type for AWS Lambda functions.
|
|
41
|
+
*
|
|
42
|
+
* @template TEvent - The event type (input) for the handler
|
|
43
|
+
* @template TResult - The result type (output) returned by the handler
|
|
44
|
+
* @returns Either void or a Promise containing the result
|
|
45
|
+
*/
|
|
46
|
+
type HandlerPika<TEvent = unknown, TResult = unknown> = (event: TEvent, context: Context) => void | Promise<TResult>;
|
|
47
|
+
/**
|
|
48
|
+
* A specialized handler type for API Gateway Lambda integrations with typed request and response.
|
|
49
|
+
*
|
|
50
|
+
* @template B - The request body type (defaults to never if not provided)
|
|
51
|
+
* @template T - The response type (defaults to never if not provided)
|
|
52
|
+
*/
|
|
53
|
+
type APIGatewayProxyHandlerPika<B = never, T = never> = HandlerPika<APIGatewayProxyEventPika<B>, APIGatewayProxyResultV2<T>>;
|
|
54
|
+
/**
|
|
55
|
+
* A higher-order function that wraps a typed Lambda handler to provide:
|
|
56
|
+
* 1. Automatic JSON parsing of the request body
|
|
57
|
+
* 2. Error handling with appropriate status codes
|
|
58
|
+
* 3. Consistent response formatting
|
|
59
|
+
*
|
|
60
|
+
* @template TRequestBody - The expected type of the request body after parsing
|
|
61
|
+
* @template TResponse - The expected type of the response
|
|
62
|
+
* @param fn - The Lambda handler function to wrap
|
|
63
|
+
* @returns A standard API Gateway Lambda handler with error handling and type safety
|
|
64
|
+
*/
|
|
65
|
+
declare function apiGatewayFunctionDecorator<TRequestBody, TResponse>(fn: APIGatewayProxyHandlerPika<TRequestBody, TResponse>): APIGatewayProxyHandlerV2<TResponse>;
|
|
66
|
+
/**
|
|
67
|
+
* Creates a standardized API Gateway response object.
|
|
68
|
+
* Handles both string and non-string body types by automatically stringifying as needed.
|
|
69
|
+
*
|
|
70
|
+
* @template T - The type of the response body
|
|
71
|
+
* @param body - The response body to send
|
|
72
|
+
* @param statusCode - The HTTP status code (defaults to 200 OK)
|
|
73
|
+
* @returns A properly formatted API Gateway response object
|
|
74
|
+
*/
|
|
75
|
+
declare function toResponse<T>(body: T, statusCode?: number): APIGatewayProxyStructuredResultV2;
|
|
76
|
+
/**
|
|
77
|
+
* Type definition for Lambda Function URL events with additional IAM authorization details.
|
|
78
|
+
* Extends the standard API Gateway event with specific requestContext properties for
|
|
79
|
+
* Function URL integration patterns.
|
|
80
|
+
*
|
|
81
|
+
* @template T - The request body type
|
|
82
|
+
*/
|
|
83
|
+
type LambdaFunctionUrlProxyEventPika<T = never> = APIGatewayProxyEventPika<T> & {
|
|
84
|
+
requestContext: {
|
|
85
|
+
authorizer?: {
|
|
86
|
+
iam: {
|
|
87
|
+
cognitoIdentity?: string;
|
|
88
|
+
cognitoIdentityId?: string;
|
|
89
|
+
userId?: string;
|
|
90
|
+
user?: string;
|
|
91
|
+
callerId?: string;
|
|
92
|
+
caller?: string;
|
|
93
|
+
accessKey?: string;
|
|
94
|
+
principalOrgId?: string;
|
|
95
|
+
accountId?: string;
|
|
96
|
+
userArn?: string;
|
|
97
|
+
[key: string]: any;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
identity?: {
|
|
101
|
+
cognitoIdentity?: string;
|
|
102
|
+
cognitoIdentityId?: string;
|
|
103
|
+
user?: string;
|
|
104
|
+
userId?: string;
|
|
105
|
+
caller?: string;
|
|
106
|
+
callerId?: string;
|
|
107
|
+
accessKey?: string;
|
|
108
|
+
principalOrgId?: string;
|
|
109
|
+
accountId?: string;
|
|
110
|
+
userArn?: string;
|
|
111
|
+
[key: string]: any;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Converts a Lambda Function URL event into a standardized API Gateway event.
|
|
117
|
+
* This function ensures that IAM authorization details are properly mapped
|
|
118
|
+
* from the authorizer to the identity property for consistent access patterns.
|
|
119
|
+
*
|
|
120
|
+
* Also, parses the body string into the generic type T if it exists and is a string
|
|
121
|
+
*
|
|
122
|
+
* This exists because the Lambda Function URL event has a different structure
|
|
123
|
+
* than the API Gateway event and we almost never use Lambdas that are Function URL based
|
|
124
|
+
* and so it's easier to just convert the event to a standard API Gateway event than
|
|
125
|
+
* to handle the different event types.
|
|
126
|
+
*
|
|
127
|
+
* @template T - The request body type
|
|
128
|
+
* @param event - The Lambda Function URL event to convert
|
|
129
|
+
* @returns A standardized API Gateway event
|
|
130
|
+
*/
|
|
131
|
+
declare function convertFunctionUrlEventToStandardApiGatewayEvent<T>(event: LambdaFunctionUrlProxyEventPika<T>): APIGatewayProxyEventPika<T>;
|
|
132
|
+
|
|
133
|
+
export { type APIGatewayProxyEventPika, type APIGatewayProxyHandlerPika, type HandlerPika, type HttpError, type LambdaFunctionUrlProxyEventPika, apiGatewayFunctionDecorator, convertFunctionUrlEventToStandardApiGatewayEvent, toResponse };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/util/api-gateway-utils.ts
|
|
4
|
+
function isObjectResponse(data) {
|
|
5
|
+
return data !== null && data !== void 0 && typeof data === "object" && ("statusCode" in data || "body" in data);
|
|
6
|
+
}
|
|
7
|
+
function apiGatewayFunctionDecorator(fn) {
|
|
8
|
+
return async (event, context) => {
|
|
9
|
+
let error;
|
|
10
|
+
let response;
|
|
11
|
+
Object.keys(event.headers).forEach((k) => {
|
|
12
|
+
let lowerK = k.toLowerCase();
|
|
13
|
+
if (!event.headers[lowerK]) {
|
|
14
|
+
event.headers[lowerK] = event.headers[k];
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
try {
|
|
18
|
+
const eventWithType = {
|
|
19
|
+
...event,
|
|
20
|
+
body: event.body ? JSON.parse(event.body) : void 0
|
|
21
|
+
};
|
|
22
|
+
response = await fn(eventWithType, context);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
error = err;
|
|
25
|
+
}
|
|
26
|
+
if (error) {
|
|
27
|
+
console.error(error);
|
|
28
|
+
return toResponse(typeof error === "string" ? error : error.message, error.statusCode ?? 500);
|
|
29
|
+
}
|
|
30
|
+
if (response === void 0) {
|
|
31
|
+
return toResponse(
|
|
32
|
+
{
|
|
33
|
+
message: "Resource not found",
|
|
34
|
+
code: "RESOURCE_NOT_FOUND"
|
|
35
|
+
},
|
|
36
|
+
404
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return isObjectResponse(response) ? response : toResponse(response, 200);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function toResponse(body, statusCode = 200) {
|
|
43
|
+
return {
|
|
44
|
+
body: typeof body === "string" ? body : JSON.stringify(body),
|
|
45
|
+
statusCode
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function convertFunctionUrlEventToStandardApiGatewayEvent(event) {
|
|
49
|
+
if (event.requestContext.identity == null && event.requestContext.authorizer?.iam != null) {
|
|
50
|
+
event.requestContext.identity = event.requestContext.authorizer.iam;
|
|
51
|
+
event.requestContext.authorizer.iam.cognitoIdentityId = event.headers["cognito-identity-id"];
|
|
52
|
+
event.requestContext.authorizer.iam.user = event.requestContext.authorizer.iam.userId;
|
|
53
|
+
event.requestContext.authorizer.iam.caller = event.requestContext.authorizer.iam.callerId;
|
|
54
|
+
}
|
|
55
|
+
if (event.body && typeof event.body === "string") {
|
|
56
|
+
try {
|
|
57
|
+
const contentType = event.headers["content-type"] || event.headers["Content-Type"];
|
|
58
|
+
if (contentType && contentType.includes("application/json")) {
|
|
59
|
+
event.body = JSON.parse(event.body);
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error("Error parsing request body as JSON:", {
|
|
63
|
+
body: event.body,
|
|
64
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
65
|
+
errorStack: error instanceof Error ? error.stack : void 0
|
|
66
|
+
});
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return event;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
exports.apiGatewayFunctionDecorator = apiGatewayFunctionDecorator;
|
|
74
|
+
exports.convertFunctionUrlEventToStandardApiGatewayEvent = convertFunctionUrlEventToStandardApiGatewayEvent;
|
|
75
|
+
exports.toResponse = toResponse;
|
|
76
|
+
//# sourceMappingURL=api-gateway-utils.js.map
|
|
77
|
+
//# sourceMappingURL=api-gateway-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/api-gateway-utils.ts"],"names":[],"mappings":"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","file":"api-gateway-utils.js","sourcesContent":["/**\n * Utility functions and types for AWS API Gateway Lambda integration.\n * This module provides type-safe wrappers and helpers for handling\n * API Gateway events and responses in AWS Lambda functions.\n */\nimport type {\n APIGatewayEventRequestContextV2,\n APIGatewayProxyEventV2WithRequestContext,\n APIGatewayProxyHandlerV2,\n APIGatewayProxyResultV2,\n APIGatewayProxyStructuredResultV2,\n Context\n} from 'aws-lambda';\n\n/**\n * Extends the standard Error interface to include an optional HTTP status code.\n * This allows for standardized error handling with appropriate HTTP responses.\n */\nexport interface HttpError extends Error {\n statusCode?: number;\n}\n\n/**\n * A type that enhances the standard API Gateway event by replacing the body property\n * with a strongly-typed version rather than the default string representation.\n *\n * @template TRequestBody - The type of the request body after parsing\n * @template TRequestContext - The type of the request context\n */\ninterface APIGatewayProxyEventPikaWithRequestContext<TRequestBody, TRequestContext> extends Omit<APIGatewayProxyEventV2WithRequestContext<TRequestContext>, 'body'> {\n body: TRequestBody;\n}\n\n/**\n * A specialized API Gateway event type that includes:\n * 1. A strongly-typed request body\n * 2. The standard API Gateway request context\n * 3. Additional path and httpMethod properties for routing\n *\n * @template TRequestBody - The type of the parsed request body (defaults to never if not provided)\n */\nexport type APIGatewayProxyEventPika<TRequestBody = never> = APIGatewayProxyEventPikaWithRequestContext<TRequestBody, APIGatewayEventRequestContextV2> & {\n path: string;\n httpMethod: string;\n resource: string;\n};\n\n/**\n * A generic handler type for AWS Lambda functions.\n *\n * @template TEvent - The event type (input) for the handler\n * @template TResult - The result type (output) returned by the handler\n * @returns Either void or a Promise containing the result\n */\nexport type HandlerPika<TEvent = unknown, TResult = unknown> = (event: TEvent, context: Context) => void | Promise<TResult>;\n\n/**\n * A specialized handler type for API Gateway Lambda integrations with typed request and response.\n *\n * @template B - The request body type (defaults to never if not provided)\n * @template T - The response type (defaults to never if not provided)\n */\nexport type APIGatewayProxyHandlerPika<B = never, T = never> = HandlerPika<APIGatewayProxyEventPika<B>, APIGatewayProxyResultV2<T>>;\n\n/**\n * Type guard function that determines if the provided data is a structured API Gateway response.\n * A structured response must be an object containing either 'statusCode' or 'body' properties.\n *\n * @template TResponse - The expected response type\n * @param data - The data to check\n * @returns A type predicate indicating if the data is a structured API Gateway response\n */\nfunction isObjectResponse<TResponse>(data: undefined | void | APIGatewayProxyResultV2<TResponse>): data is APIGatewayProxyStructuredResultV2 {\n return data !== null && data !== undefined && typeof data === 'object' && ('statusCode' in data || 'body' in data);\n}\n\n/**\n * A higher-order function that wraps a typed Lambda handler to provide:\n * 1. Automatic JSON parsing of the request body\n * 2. Error handling with appropriate status codes\n * 3. Consistent response formatting\n *\n * @template TRequestBody - The expected type of the request body after parsing\n * @template TResponse - The expected type of the response\n * @param fn - The Lambda handler function to wrap\n * @returns A standard API Gateway Lambda handler with error handling and type safety\n */\nexport function apiGatewayFunctionDecorator<TRequestBody, TResponse>(fn: APIGatewayProxyHandlerPika<TRequestBody, TResponse>): APIGatewayProxyHandlerV2<TResponse> {\n return async (event, context) => {\n let error: HttpError | undefined;\n let response: undefined | void | APIGatewayProxyResultV2<TResponse>;\n\n // Just in case, add another version of the headers with all lowercase keys;\n // helps when running locally. Seems APIGateway automatically converts to lowercase\n Object.keys(event.headers).forEach((k) => {\n let lowerK = k.toLowerCase();\n if (!event.headers[lowerK]) {\n event.headers[lowerK] = event.headers[k];\n }\n });\n try {\n // Parse the request body if present and create a typed event object\n const eventWithType: APIGatewayProxyEventPika<TRequestBody> = {\n ...(event as any),\n body: event.body ? JSON.parse(event.body) : undefined\n };\n\n // Call the handler with the typed event\n response = await fn(eventWithType, context);\n } catch (err) {\n // Capture any errors that occur during processing\n error = err as HttpError;\n }\n\n // Handle errors by returning an appropriate error response\n if (error) {\n console.error(error);\n return toResponse(typeof error === 'string' ? error : error.message, error.statusCode ?? 500);\n }\n\n // Return resource not found if the response is undefined\n if (response === undefined) {\n return toResponse(\n {\n message: 'Resource not found',\n code: 'RESOURCE_NOT_FOUND'\n },\n 404\n );\n }\n\n // Either return the structured response directly or wrap the result in a standard response\n return isObjectResponse(response) ? response : toResponse(response, 200);\n };\n}\n\n/**\n * Creates a standardized API Gateway response object.\n * Handles both string and non-string body types by automatically stringifying as needed.\n *\n * @template T - The type of the response body\n * @param body - The response body to send\n * @param statusCode - The HTTP status code (defaults to 200 OK)\n * @returns A properly formatted API Gateway response object\n */\nexport function toResponse<T>(body: T, statusCode = 200): APIGatewayProxyStructuredResultV2 {\n return {\n body: typeof body === 'string' ? body : JSON.stringify(body),\n statusCode\n };\n}\n\n/**\n * Type definition for Lambda Function URL events with additional IAM authorization details.\n * Extends the standard API Gateway event with specific requestContext properties for\n * Function URL integration patterns.\n *\n * @template T - The request body type\n */\nexport type LambdaFunctionUrlProxyEventPika<T = never> = APIGatewayProxyEventPika<T> & {\n requestContext: {\n authorizer?: {\n iam: {\n cognitoIdentity?: string;\n cognitoIdentityId?: string;\n userId?: string;\n user?: string;\n callerId?: string;\n caller?: string;\n accessKey?: string;\n principalOrgId?: string;\n accountId?: string;\n userArn?: string;\n [key: string]: any;\n };\n };\n identity?: {\n cognitoIdentity?: string;\n cognitoIdentityId?: string;\n user?: string;\n userId?: string;\n caller?: string;\n callerId?: string;\n accessKey?: string;\n principalOrgId?: string;\n accountId?: string;\n userArn?: string;\n [key: string]: any;\n };\n };\n};\n\n/**\n * Converts a Lambda Function URL event into a standardized API Gateway event.\n * This function ensures that IAM authorization details are properly mapped\n * from the authorizer to the identity property for consistent access patterns.\n *\n * Also, parses the body string into the generic type T if it exists and is a string\n *\n * This exists because the Lambda Function URL event has a different structure\n * than the API Gateway event and we almost never use Lambdas that are Function URL based\n * and so it's easier to just convert the event to a standard API Gateway event than\n * to handle the different event types.\n *\n * @template T - The request body type\n * @param event - The Lambda Function URL event to convert\n * @returns A standardized API Gateway event\n */\nexport function convertFunctionUrlEventToStandardApiGatewayEvent<T>(event: LambdaFunctionUrlProxyEventPika<T>): APIGatewayProxyEventPika<T> {\n if (event.requestContext.identity == null && event.requestContext.authorizer?.iam != null) {\n event.requestContext.identity = event.requestContext.authorizer.iam;\n event.requestContext.authorizer.iam.cognitoIdentityId = event.headers['cognito-identity-id'];\n event.requestContext.authorizer.iam.user = event.requestContext.authorizer.iam.userId;\n event.requestContext.authorizer.iam.caller = event.requestContext.authorizer.iam.callerId;\n }\n\n // Parse the body string into the generic type T if it exists and is a string\n if (event.body && typeof event.body === 'string') {\n try {\n // If content-type is not application/json, don't attempt to parse\n const contentType = event.headers['content-type'] || event.headers['Content-Type'];\n if (contentType && contentType.includes('application/json')) {\n event.body = JSON.parse(event.body) as T;\n }\n } catch (error) {\n console.error('Error parsing request body as JSON:', {\n body: event.body,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : undefined\n });\n throw error;\n }\n }\n\n return event;\n}\n"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/util/api-gateway-utils.ts
|
|
2
|
+
function isObjectResponse(data) {
|
|
3
|
+
return data !== null && data !== void 0 && typeof data === "object" && ("statusCode" in data || "body" in data);
|
|
4
|
+
}
|
|
5
|
+
function apiGatewayFunctionDecorator(fn) {
|
|
6
|
+
return async (event, context) => {
|
|
7
|
+
let error;
|
|
8
|
+
let response;
|
|
9
|
+
Object.keys(event.headers).forEach((k) => {
|
|
10
|
+
let lowerK = k.toLowerCase();
|
|
11
|
+
if (!event.headers[lowerK]) {
|
|
12
|
+
event.headers[lowerK] = event.headers[k];
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
try {
|
|
16
|
+
const eventWithType = {
|
|
17
|
+
...event,
|
|
18
|
+
body: event.body ? JSON.parse(event.body) : void 0
|
|
19
|
+
};
|
|
20
|
+
response = await fn(eventWithType, context);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
error = err;
|
|
23
|
+
}
|
|
24
|
+
if (error) {
|
|
25
|
+
console.error(error);
|
|
26
|
+
return toResponse(typeof error === "string" ? error : error.message, error.statusCode ?? 500);
|
|
27
|
+
}
|
|
28
|
+
if (response === void 0) {
|
|
29
|
+
return toResponse(
|
|
30
|
+
{
|
|
31
|
+
message: "Resource not found",
|
|
32
|
+
code: "RESOURCE_NOT_FOUND"
|
|
33
|
+
},
|
|
34
|
+
404
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return isObjectResponse(response) ? response : toResponse(response, 200);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function toResponse(body, statusCode = 200) {
|
|
41
|
+
return {
|
|
42
|
+
body: typeof body === "string" ? body : JSON.stringify(body),
|
|
43
|
+
statusCode
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function convertFunctionUrlEventToStandardApiGatewayEvent(event) {
|
|
47
|
+
if (event.requestContext.identity == null && event.requestContext.authorizer?.iam != null) {
|
|
48
|
+
event.requestContext.identity = event.requestContext.authorizer.iam;
|
|
49
|
+
event.requestContext.authorizer.iam.cognitoIdentityId = event.headers["cognito-identity-id"];
|
|
50
|
+
event.requestContext.authorizer.iam.user = event.requestContext.authorizer.iam.userId;
|
|
51
|
+
event.requestContext.authorizer.iam.caller = event.requestContext.authorizer.iam.callerId;
|
|
52
|
+
}
|
|
53
|
+
if (event.body && typeof event.body === "string") {
|
|
54
|
+
try {
|
|
55
|
+
const contentType = event.headers["content-type"] || event.headers["Content-Type"];
|
|
56
|
+
if (contentType && contentType.includes("application/json")) {
|
|
57
|
+
event.body = JSON.parse(event.body);
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("Error parsing request body as JSON:", {
|
|
61
|
+
body: event.body,
|
|
62
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
63
|
+
errorStack: error instanceof Error ? error.stack : void 0
|
|
64
|
+
});
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return event;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { apiGatewayFunctionDecorator, convertFunctionUrlEventToStandardApiGatewayEvent, toResponse };
|
|
72
|
+
//# sourceMappingURL=api-gateway-utils.mjs.map
|
|
73
|
+
//# sourceMappingURL=api-gateway-utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/util/api-gateway-utils.ts"],"names":[],"mappings":"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","file":"api-gateway-utils.mjs","sourcesContent":["/**\n * Utility functions and types for AWS API Gateway Lambda integration.\n * This module provides type-safe wrappers and helpers for handling\n * API Gateway events and responses in AWS Lambda functions.\n */\nimport type {\n APIGatewayEventRequestContextV2,\n APIGatewayProxyEventV2WithRequestContext,\n APIGatewayProxyHandlerV2,\n APIGatewayProxyResultV2,\n APIGatewayProxyStructuredResultV2,\n Context\n} from 'aws-lambda';\n\n/**\n * Extends the standard Error interface to include an optional HTTP status code.\n * This allows for standardized error handling with appropriate HTTP responses.\n */\nexport interface HttpError extends Error {\n statusCode?: number;\n}\n\n/**\n * A type that enhances the standard API Gateway event by replacing the body property\n * with a strongly-typed version rather than the default string representation.\n *\n * @template TRequestBody - The type of the request body after parsing\n * @template TRequestContext - The type of the request context\n */\ninterface APIGatewayProxyEventPikaWithRequestContext<TRequestBody, TRequestContext> extends Omit<APIGatewayProxyEventV2WithRequestContext<TRequestContext>, 'body'> {\n body: TRequestBody;\n}\n\n/**\n * A specialized API Gateway event type that includes:\n * 1. A strongly-typed request body\n * 2. The standard API Gateway request context\n * 3. Additional path and httpMethod properties for routing\n *\n * @template TRequestBody - The type of the parsed request body (defaults to never if not provided)\n */\nexport type APIGatewayProxyEventPika<TRequestBody = never> = APIGatewayProxyEventPikaWithRequestContext<TRequestBody, APIGatewayEventRequestContextV2> & {\n path: string;\n httpMethod: string;\n resource: string;\n};\n\n/**\n * A generic handler type for AWS Lambda functions.\n *\n * @template TEvent - The event type (input) for the handler\n * @template TResult - The result type (output) returned by the handler\n * @returns Either void or a Promise containing the result\n */\nexport type HandlerPika<TEvent = unknown, TResult = unknown> = (event: TEvent, context: Context) => void | Promise<TResult>;\n\n/**\n * A specialized handler type for API Gateway Lambda integrations with typed request and response.\n *\n * @template B - The request body type (defaults to never if not provided)\n * @template T - The response type (defaults to never if not provided)\n */\nexport type APIGatewayProxyHandlerPika<B = never, T = never> = HandlerPika<APIGatewayProxyEventPika<B>, APIGatewayProxyResultV2<T>>;\n\n/**\n * Type guard function that determines if the provided data is a structured API Gateway response.\n * A structured response must be an object containing either 'statusCode' or 'body' properties.\n *\n * @template TResponse - The expected response type\n * @param data - The data to check\n * @returns A type predicate indicating if the data is a structured API Gateway response\n */\nfunction isObjectResponse<TResponse>(data: undefined | void | APIGatewayProxyResultV2<TResponse>): data is APIGatewayProxyStructuredResultV2 {\n return data !== null && data !== undefined && typeof data === 'object' && ('statusCode' in data || 'body' in data);\n}\n\n/**\n * A higher-order function that wraps a typed Lambda handler to provide:\n * 1. Automatic JSON parsing of the request body\n * 2. Error handling with appropriate status codes\n * 3. Consistent response formatting\n *\n * @template TRequestBody - The expected type of the request body after parsing\n * @template TResponse - The expected type of the response\n * @param fn - The Lambda handler function to wrap\n * @returns A standard API Gateway Lambda handler with error handling and type safety\n */\nexport function apiGatewayFunctionDecorator<TRequestBody, TResponse>(fn: APIGatewayProxyHandlerPika<TRequestBody, TResponse>): APIGatewayProxyHandlerV2<TResponse> {\n return async (event, context) => {\n let error: HttpError | undefined;\n let response: undefined | void | APIGatewayProxyResultV2<TResponse>;\n\n // Just in case, add another version of the headers with all lowercase keys;\n // helps when running locally. Seems APIGateway automatically converts to lowercase\n Object.keys(event.headers).forEach((k) => {\n let lowerK = k.toLowerCase();\n if (!event.headers[lowerK]) {\n event.headers[lowerK] = event.headers[k];\n }\n });\n try {\n // Parse the request body if present and create a typed event object\n const eventWithType: APIGatewayProxyEventPika<TRequestBody> = {\n ...(event as any),\n body: event.body ? JSON.parse(event.body) : undefined\n };\n\n // Call the handler with the typed event\n response = await fn(eventWithType, context);\n } catch (err) {\n // Capture any errors that occur during processing\n error = err as HttpError;\n }\n\n // Handle errors by returning an appropriate error response\n if (error) {\n console.error(error);\n return toResponse(typeof error === 'string' ? error : error.message, error.statusCode ?? 500);\n }\n\n // Return resource not found if the response is undefined\n if (response === undefined) {\n return toResponse(\n {\n message: 'Resource not found',\n code: 'RESOURCE_NOT_FOUND'\n },\n 404\n );\n }\n\n // Either return the structured response directly or wrap the result in a standard response\n return isObjectResponse(response) ? response : toResponse(response, 200);\n };\n}\n\n/**\n * Creates a standardized API Gateway response object.\n * Handles both string and non-string body types by automatically stringifying as needed.\n *\n * @template T - The type of the response body\n * @param body - The response body to send\n * @param statusCode - The HTTP status code (defaults to 200 OK)\n * @returns A properly formatted API Gateway response object\n */\nexport function toResponse<T>(body: T, statusCode = 200): APIGatewayProxyStructuredResultV2 {\n return {\n body: typeof body === 'string' ? body : JSON.stringify(body),\n statusCode\n };\n}\n\n/**\n * Type definition for Lambda Function URL events with additional IAM authorization details.\n * Extends the standard API Gateway event with specific requestContext properties for\n * Function URL integration patterns.\n *\n * @template T - The request body type\n */\nexport type LambdaFunctionUrlProxyEventPika<T = never> = APIGatewayProxyEventPika<T> & {\n requestContext: {\n authorizer?: {\n iam: {\n cognitoIdentity?: string;\n cognitoIdentityId?: string;\n userId?: string;\n user?: string;\n callerId?: string;\n caller?: string;\n accessKey?: string;\n principalOrgId?: string;\n accountId?: string;\n userArn?: string;\n [key: string]: any;\n };\n };\n identity?: {\n cognitoIdentity?: string;\n cognitoIdentityId?: string;\n user?: string;\n userId?: string;\n caller?: string;\n callerId?: string;\n accessKey?: string;\n principalOrgId?: string;\n accountId?: string;\n userArn?: string;\n [key: string]: any;\n };\n };\n};\n\n/**\n * Converts a Lambda Function URL event into a standardized API Gateway event.\n * This function ensures that IAM authorization details are properly mapped\n * from the authorizer to the identity property for consistent access patterns.\n *\n * Also, parses the body string into the generic type T if it exists and is a string\n *\n * This exists because the Lambda Function URL event has a different structure\n * than the API Gateway event and we almost never use Lambdas that are Function URL based\n * and so it's easier to just convert the event to a standard API Gateway event than\n * to handle the different event types.\n *\n * @template T - The request body type\n * @param event - The Lambda Function URL event to convert\n * @returns A standardized API Gateway event\n */\nexport function convertFunctionUrlEventToStandardApiGatewayEvent<T>(event: LambdaFunctionUrlProxyEventPika<T>): APIGatewayProxyEventPika<T> {\n if (event.requestContext.identity == null && event.requestContext.authorizer?.iam != null) {\n event.requestContext.identity = event.requestContext.authorizer.iam;\n event.requestContext.authorizer.iam.cognitoIdentityId = event.headers['cognito-identity-id'];\n event.requestContext.authorizer.iam.user = event.requestContext.authorizer.iam.userId;\n event.requestContext.authorizer.iam.caller = event.requestContext.authorizer.iam.callerId;\n }\n\n // Parse the body string into the generic type T if it exists and is a string\n if (event.body && typeof event.body === 'string') {\n try {\n // If content-type is not application/json, don't attempt to parse\n const contentType = event.headers['content-type'] || event.headers['Content-Type'];\n if (contentType && contentType.includes('application/json')) {\n event.body = JSON.parse(event.body) as T;\n }\n } catch (error) {\n console.error('Error parsing request body as JSON:', {\n body: event.body,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : undefined\n });\n throw error;\n }\n }\n\n return event;\n}\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { BedrockActionGroupLambdaEvent, BedrockActionGroupLambdaResponse, Parameter } from '../types/chatbot/bedrock.mjs';
|
|
2
|
+
import { SessionDataWithChatUserCustomDataSpreadIn, RecordOrUndef } from '../types/chatbot/chatbot-types.mjs';
|
|
3
|
+
import '@aws-sdk/client-bedrock-agent-runtime';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handles an error and returns a BedrockLambdaResponse object.
|
|
7
|
+
*
|
|
8
|
+
* If the error is an instance of BedrockLambdaError, it will be returned as is.
|
|
9
|
+
* Otherwise, it will be converted to a string and returned as a BedrockLambdaError.
|
|
10
|
+
*
|
|
11
|
+
* @param error The error to handle.
|
|
12
|
+
* @returns A BedrockLambdaResponse object.
|
|
13
|
+
*/
|
|
14
|
+
declare function handleBedrockError(error: unknown, event: BedrockActionGroupLambdaEvent, sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>): BedrockActionGroupLambdaResponse;
|
|
15
|
+
/**
|
|
16
|
+
* Converter functions for Bedrock parameters.
|
|
17
|
+
*/
|
|
18
|
+
declare const converters: Record<string, (v: string) => any>;
|
|
19
|
+
/**
|
|
20
|
+
* Converts Bedrock parameters to the correct type.
|
|
21
|
+
* @param params - The parameters to convert.
|
|
22
|
+
* @returns A record of parameter names and their converted values.
|
|
23
|
+
*/
|
|
24
|
+
declare function convertBedrockParamsToCorrectType(params?: Parameter[]): Record<string, any>;
|
|
25
|
+
/**
|
|
26
|
+
* Normalizes the session attributes, converting the type to T. An empty sessionAttributes object is the
|
|
27
|
+
* same as undefined.
|
|
28
|
+
*
|
|
29
|
+
* Type T is the type of the session attributes.
|
|
30
|
+
*
|
|
31
|
+
* @param sessionAttributes The session attributes.
|
|
32
|
+
* @returns The normalized session attributes.
|
|
33
|
+
*/
|
|
34
|
+
declare function normalizeSessionAttributes(sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>): SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef> | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a BedrockLambdaResponse object.
|
|
37
|
+
*
|
|
38
|
+
* Type T is the type of the session attributes. By including the sessionAttributes
|
|
39
|
+
* parameter, we are telling Bedrock to persist the session attributes for the
|
|
40
|
+
* duration of the session and they will be passed in on all subsequent calls
|
|
41
|
+
* using the event.sessionAttributes parameter.
|
|
42
|
+
*
|
|
43
|
+
* @param body The body of the response.
|
|
44
|
+
* @param actionGroup The action group.
|
|
45
|
+
* @param messageVersion The message version.
|
|
46
|
+
* @param functionName The function name.
|
|
47
|
+
* @param sessionAttributes The session attributes.
|
|
48
|
+
* @param failed Whether the response is a failure.
|
|
49
|
+
* @returns A BedrockLambdaResponse object.
|
|
50
|
+
*/
|
|
51
|
+
declare function createBedrockLambdaResponse(body: string | Record<string, any>, actionGroup: string, messageVersion: string, functionName: string, sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>, failed?: boolean): BedrockActionGroupLambdaResponse;
|
|
52
|
+
|
|
53
|
+
export { convertBedrockParamsToCorrectType, converters, createBedrockLambdaResponse, handleBedrockError, normalizeSessionAttributes };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { BedrockActionGroupLambdaEvent, BedrockActionGroupLambdaResponse, Parameter } from '../types/chatbot/bedrock.js';
|
|
2
|
+
import { SessionDataWithChatUserCustomDataSpreadIn, RecordOrUndef } from '../types/chatbot/chatbot-types.js';
|
|
3
|
+
import '@aws-sdk/client-bedrock-agent-runtime';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handles an error and returns a BedrockLambdaResponse object.
|
|
7
|
+
*
|
|
8
|
+
* If the error is an instance of BedrockLambdaError, it will be returned as is.
|
|
9
|
+
* Otherwise, it will be converted to a string and returned as a BedrockLambdaError.
|
|
10
|
+
*
|
|
11
|
+
* @param error The error to handle.
|
|
12
|
+
* @returns A BedrockLambdaResponse object.
|
|
13
|
+
*/
|
|
14
|
+
declare function handleBedrockError(error: unknown, event: BedrockActionGroupLambdaEvent, sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>): BedrockActionGroupLambdaResponse;
|
|
15
|
+
/**
|
|
16
|
+
* Converter functions for Bedrock parameters.
|
|
17
|
+
*/
|
|
18
|
+
declare const converters: Record<string, (v: string) => any>;
|
|
19
|
+
/**
|
|
20
|
+
* Converts Bedrock parameters to the correct type.
|
|
21
|
+
* @param params - The parameters to convert.
|
|
22
|
+
* @returns A record of parameter names and their converted values.
|
|
23
|
+
*/
|
|
24
|
+
declare function convertBedrockParamsToCorrectType(params?: Parameter[]): Record<string, any>;
|
|
25
|
+
/**
|
|
26
|
+
* Normalizes the session attributes, converting the type to T. An empty sessionAttributes object is the
|
|
27
|
+
* same as undefined.
|
|
28
|
+
*
|
|
29
|
+
* Type T is the type of the session attributes.
|
|
30
|
+
*
|
|
31
|
+
* @param sessionAttributes The session attributes.
|
|
32
|
+
* @returns The normalized session attributes.
|
|
33
|
+
*/
|
|
34
|
+
declare function normalizeSessionAttributes(sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>): SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef> | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a BedrockLambdaResponse object.
|
|
37
|
+
*
|
|
38
|
+
* Type T is the type of the session attributes. By including the sessionAttributes
|
|
39
|
+
* parameter, we are telling Bedrock to persist the session attributes for the
|
|
40
|
+
* duration of the session and they will be passed in on all subsequent calls
|
|
41
|
+
* using the event.sessionAttributes parameter.
|
|
42
|
+
*
|
|
43
|
+
* @param body The body of the response.
|
|
44
|
+
* @param actionGroup The action group.
|
|
45
|
+
* @param messageVersion The message version.
|
|
46
|
+
* @param functionName The function name.
|
|
47
|
+
* @param sessionAttributes The session attributes.
|
|
48
|
+
* @param failed Whether the response is a failure.
|
|
49
|
+
* @returns A BedrockLambdaResponse object.
|
|
50
|
+
*/
|
|
51
|
+
declare function createBedrockLambdaResponse(body: string | Record<string, any>, actionGroup: string, messageVersion: string, functionName: string, sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>, failed?: boolean): BedrockActionGroupLambdaResponse;
|
|
52
|
+
|
|
53
|
+
export { convertBedrockParamsToCorrectType, converters, createBedrockLambdaResponse, handleBedrockError, normalizeSessionAttributes };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/types/chatbot/bedrock-lambda-error.ts
|
|
4
|
+
var BedrockLambdaError = class extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// src/util/bedrock.ts
|
|
11
|
+
function handleBedrockError(error, event, sessionAttributes) {
|
|
12
|
+
if (error instanceof BedrockLambdaError) {
|
|
13
|
+
return createBedrockLambdaResponse(error.message, event.actionGroup, event.messageVersion, event.function ?? "NoFunctionNameSpecified", sessionAttributes);
|
|
14
|
+
} else {
|
|
15
|
+
console.error("Unexpected error:", error);
|
|
16
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
17
|
+
return createBedrockLambdaResponse(errorMessage, event.actionGroup, event.messageVersion, event.function ?? "NoFunctionNameSpecified", sessionAttributes, true);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
var converters = {
|
|
21
|
+
defaultPassThrough: (v) => v,
|
|
22
|
+
number: (v) => parseFloat(v),
|
|
23
|
+
boolean: (v) => v === "true",
|
|
24
|
+
integer: (v) => parseInt(v),
|
|
25
|
+
array: (v) => {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(v);
|
|
28
|
+
} catch {
|
|
29
|
+
if (v.startsWith("[") && v.endsWith("]")) {
|
|
30
|
+
const content = v.slice(1, -1).trim();
|
|
31
|
+
if (content === "") {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
return content.split(",").map((item) => item.trim());
|
|
35
|
+
}
|
|
36
|
+
return [v];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
function convertBedrockParamsToCorrectType(params) {
|
|
41
|
+
return params?.reduce(
|
|
42
|
+
(acc, param) => {
|
|
43
|
+
acc[param.name] = converters[param.type] ? converters[param.type](param.value) : converters.defaultPassThrough(param.value);
|
|
44
|
+
return acc;
|
|
45
|
+
},
|
|
46
|
+
{}
|
|
47
|
+
) ?? {};
|
|
48
|
+
}
|
|
49
|
+
function normalizeSessionAttributes(sessionAttributes) {
|
|
50
|
+
if (!sessionAttributes || Object.keys(sessionAttributes).length === 0) {
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
return sessionAttributes;
|
|
54
|
+
}
|
|
55
|
+
function createBedrockLambdaResponse(body, actionGroup, messageVersion, functionName, sessionAttributes, failed) {
|
|
56
|
+
return {
|
|
57
|
+
messageVersion,
|
|
58
|
+
response: {
|
|
59
|
+
actionGroup,
|
|
60
|
+
function: functionName,
|
|
61
|
+
functionResponse: {
|
|
62
|
+
...failed ? { responseState: "FAILURE" } : {},
|
|
63
|
+
responseBody: {
|
|
64
|
+
TEXT: {
|
|
65
|
+
body: typeof body === "string" ? body : JSON.stringify(body)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
sessionAttributes
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
exports.convertBedrockParamsToCorrectType = convertBedrockParamsToCorrectType;
|
|
75
|
+
exports.converters = converters;
|
|
76
|
+
exports.createBedrockLambdaResponse = createBedrockLambdaResponse;
|
|
77
|
+
exports.handleBedrockError = handleBedrockError;
|
|
78
|
+
exports.normalizeSessionAttributes = normalizeSessionAttributes;
|
|
79
|
+
//# sourceMappingURL=bedrock.js.map
|
|
80
|
+
//# sourceMappingURL=bedrock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/types/chatbot/bedrock-lambda-error.ts","../../src/util/bedrock.ts"],"names":[],"mappings":"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","file":"bedrock.js","sourcesContent":["/**\n * This error is used to allow us to distinguish between errors that are\n * expected to be returned to the user and errors that are unexpected.\n *\n * These errors are caught in the Bedrock Lambda and converted into a\n * BedrockLambdaResponse object using the message as the body.\n */\nexport class BedrockLambdaError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n","import { BedrockActionGroupLambdaEvent, BedrockActionGroupLambdaResponse, Parameter } from '../types/chatbot/bedrock';\nimport { BedrockLambdaError } from '../types/chatbot/bedrock-lambda-error';\nimport { RecordOrUndef, SessionDataWithChatUserCustomDataSpreadIn } from '../types/chatbot/chatbot-types';\n\n/**\n * Handles an error and returns a BedrockLambdaResponse object.\n *\n * If the error is an instance of BedrockLambdaError, it will be returned as is.\n * Otherwise, it will be converted to a string and returned as a BedrockLambdaError.\n *\n * @param error The error to handle.\n * @returns A BedrockLambdaResponse object.\n */\nexport function handleBedrockError(\n error: unknown,\n event: BedrockActionGroupLambdaEvent,\n sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>\n): BedrockActionGroupLambdaResponse {\n if (error instanceof BedrockLambdaError) {\n return createBedrockLambdaResponse(error.message, event.actionGroup, event.messageVersion, event.function ?? 'NoFunctionNameSpecified', sessionAttributes);\n } else {\n console.error('Unexpected error:', error);\n const errorMessage = error instanceof Error ? error.message : String(error);\n return createBedrockLambdaResponse(errorMessage, event.actionGroup, event.messageVersion, event.function ?? 'NoFunctionNameSpecified', sessionAttributes, true);\n }\n}\n\n/**\n * Converter functions for Bedrock parameters.\n */\nexport const converters: Record<string, (v: string) => any> = {\n defaultPassThrough: (v: string) => v,\n number: (v: string) => parseFloat(v),\n boolean: (v: string) => v === 'true',\n integer: (v: string) => parseInt(v),\n array: (v: string) => {\n try {\n // First try to parse as valid JSON\n return JSON.parse(v);\n } catch {\n /* \n TODO: is this a good idea? Why did Bedrock give me a param that was invalid JSON for the array?\n\n {\n \"name\": \"s3Keys\",\n \"type\": \"array\",\n \"value\": \"[uploads/2fbbf97a-070d-48ca-9d5e-fc9bcc88e570/019735c5-b0f0-7419-94d5-093b4202977a_sample-data.csv]\"\n }\n */\n // If JSON.parse fails, try to handle array with unquoted strings\n // Remove brackets and split by comma, then trim whitespace\n if (v.startsWith('[') && v.endsWith(']')) {\n const content = v.slice(1, -1).trim();\n if (content === '') {\n return [];\n }\n return content.split(',').map((item) => item.trim());\n }\n // If it doesn't look like an array format, return as single-item array\n return [v];\n }\n }\n};\n\n/**\n * Converts Bedrock parameters to the correct type.\n * @param params - The parameters to convert.\n * @returns A record of parameter names and their converted values.\n */\nexport function convertBedrockParamsToCorrectType(params?: Parameter[]): Record<string, any> {\n return (\n params?.reduce(\n (acc, param) => {\n acc[param.name] = converters[param.type] ? converters[param.type](param.value) : converters.defaultPassThrough(param.value);\n return acc;\n },\n {} as Record<string, any>\n ) ?? {}\n );\n}\n\n/**\n * Normalizes the session attributes, converting the type to T. An empty sessionAttributes object is the\n * same as undefined.\n *\n * Type T is the type of the session attributes.\n *\n * @param sessionAttributes The session attributes.\n * @returns The normalized session attributes.\n */\nexport function normalizeSessionAttributes(\n sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>\n): SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef> | undefined {\n if (!sessionAttributes || Object.keys(sessionAttributes).length === 0) {\n return undefined;\n }\n\n return sessionAttributes;\n}\n\n/**\n * Creates a BedrockLambdaResponse object.\n *\n * Type T is the type of the session attributes. By including the sessionAttributes\n * parameter, we are telling Bedrock to persist the session attributes for the\n * duration of the session and they will be passed in on all subsequent calls\n * using the event.sessionAttributes parameter.\n *\n * @param body The body of the response.\n * @param actionGroup The action group.\n * @param messageVersion The message version.\n * @param functionName The function name.\n * @param sessionAttributes The session attributes.\n * @param failed Whether the response is a failure.\n * @returns A BedrockLambdaResponse object.\n */\nexport function createBedrockLambdaResponse(\n body: string | Record<string, any>,\n actionGroup: string,\n messageVersion: string,\n functionName: string,\n sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>,\n failed?: boolean\n): BedrockActionGroupLambdaResponse {\n return {\n messageVersion,\n response: {\n actionGroup,\n function: functionName,\n functionResponse: {\n ...(failed ? { responseState: 'FAILURE' } : {}),\n responseBody: {\n TEXT: {\n body: typeof body === 'string' ? body : JSON.stringify(body)\n }\n }\n }\n },\n sessionAttributes\n };\n}\n"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// src/types/chatbot/bedrock-lambda-error.ts
|
|
2
|
+
var BedrockLambdaError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
}
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// src/util/bedrock.ts
|
|
9
|
+
function handleBedrockError(error, event, sessionAttributes) {
|
|
10
|
+
if (error instanceof BedrockLambdaError) {
|
|
11
|
+
return createBedrockLambdaResponse(error.message, event.actionGroup, event.messageVersion, event.function ?? "NoFunctionNameSpecified", sessionAttributes);
|
|
12
|
+
} else {
|
|
13
|
+
console.error("Unexpected error:", error);
|
|
14
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15
|
+
return createBedrockLambdaResponse(errorMessage, event.actionGroup, event.messageVersion, event.function ?? "NoFunctionNameSpecified", sessionAttributes, true);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
var converters = {
|
|
19
|
+
defaultPassThrough: (v) => v,
|
|
20
|
+
number: (v) => parseFloat(v),
|
|
21
|
+
boolean: (v) => v === "true",
|
|
22
|
+
integer: (v) => parseInt(v),
|
|
23
|
+
array: (v) => {
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(v);
|
|
26
|
+
} catch {
|
|
27
|
+
if (v.startsWith("[") && v.endsWith("]")) {
|
|
28
|
+
const content = v.slice(1, -1).trim();
|
|
29
|
+
if (content === "") {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return content.split(",").map((item) => item.trim());
|
|
33
|
+
}
|
|
34
|
+
return [v];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
function convertBedrockParamsToCorrectType(params) {
|
|
39
|
+
return params?.reduce(
|
|
40
|
+
(acc, param) => {
|
|
41
|
+
acc[param.name] = converters[param.type] ? converters[param.type](param.value) : converters.defaultPassThrough(param.value);
|
|
42
|
+
return acc;
|
|
43
|
+
},
|
|
44
|
+
{}
|
|
45
|
+
) ?? {};
|
|
46
|
+
}
|
|
47
|
+
function normalizeSessionAttributes(sessionAttributes) {
|
|
48
|
+
if (!sessionAttributes || Object.keys(sessionAttributes).length === 0) {
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
return sessionAttributes;
|
|
52
|
+
}
|
|
53
|
+
function createBedrockLambdaResponse(body, actionGroup, messageVersion, functionName, sessionAttributes, failed) {
|
|
54
|
+
return {
|
|
55
|
+
messageVersion,
|
|
56
|
+
response: {
|
|
57
|
+
actionGroup,
|
|
58
|
+
function: functionName,
|
|
59
|
+
functionResponse: {
|
|
60
|
+
...failed ? { responseState: "FAILURE" } : {},
|
|
61
|
+
responseBody: {
|
|
62
|
+
TEXT: {
|
|
63
|
+
body: typeof body === "string" ? body : JSON.stringify(body)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
sessionAttributes
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { convertBedrockParamsToCorrectType, converters, createBedrockLambdaResponse, handleBedrockError, normalizeSessionAttributes };
|
|
73
|
+
//# sourceMappingURL=bedrock.mjs.map
|
|
74
|
+
//# sourceMappingURL=bedrock.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/types/chatbot/bedrock-lambda-error.ts","../../src/util/bedrock.ts"],"names":[],"mappings":"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","file":"bedrock.mjs","sourcesContent":["/**\n * This error is used to allow us to distinguish between errors that are\n * expected to be returned to the user and errors that are unexpected.\n *\n * These errors are caught in the Bedrock Lambda and converted into a\n * BedrockLambdaResponse object using the message as the body.\n */\nexport class BedrockLambdaError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n","import { BedrockActionGroupLambdaEvent, BedrockActionGroupLambdaResponse, Parameter } from '../types/chatbot/bedrock';\nimport { BedrockLambdaError } from '../types/chatbot/bedrock-lambda-error';\nimport { RecordOrUndef, SessionDataWithChatUserCustomDataSpreadIn } from '../types/chatbot/chatbot-types';\n\n/**\n * Handles an error and returns a BedrockLambdaResponse object.\n *\n * If the error is an instance of BedrockLambdaError, it will be returned as is.\n * Otherwise, it will be converted to a string and returned as a BedrockLambdaError.\n *\n * @param error The error to handle.\n * @returns A BedrockLambdaResponse object.\n */\nexport function handleBedrockError(\n error: unknown,\n event: BedrockActionGroupLambdaEvent,\n sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>\n): BedrockActionGroupLambdaResponse {\n if (error instanceof BedrockLambdaError) {\n return createBedrockLambdaResponse(error.message, event.actionGroup, event.messageVersion, event.function ?? 'NoFunctionNameSpecified', sessionAttributes);\n } else {\n console.error('Unexpected error:', error);\n const errorMessage = error instanceof Error ? error.message : String(error);\n return createBedrockLambdaResponse(errorMessage, event.actionGroup, event.messageVersion, event.function ?? 'NoFunctionNameSpecified', sessionAttributes, true);\n }\n}\n\n/**\n * Converter functions for Bedrock parameters.\n */\nexport const converters: Record<string, (v: string) => any> = {\n defaultPassThrough: (v: string) => v,\n number: (v: string) => parseFloat(v),\n boolean: (v: string) => v === 'true',\n integer: (v: string) => parseInt(v),\n array: (v: string) => {\n try {\n // First try to parse as valid JSON\n return JSON.parse(v);\n } catch {\n /* \n TODO: is this a good idea? Why did Bedrock give me a param that was invalid JSON for the array?\n\n {\n \"name\": \"s3Keys\",\n \"type\": \"array\",\n \"value\": \"[uploads/2fbbf97a-070d-48ca-9d5e-fc9bcc88e570/019735c5-b0f0-7419-94d5-093b4202977a_sample-data.csv]\"\n }\n */\n // If JSON.parse fails, try to handle array with unquoted strings\n // Remove brackets and split by comma, then trim whitespace\n if (v.startsWith('[') && v.endsWith(']')) {\n const content = v.slice(1, -1).trim();\n if (content === '') {\n return [];\n }\n return content.split(',').map((item) => item.trim());\n }\n // If it doesn't look like an array format, return as single-item array\n return [v];\n }\n }\n};\n\n/**\n * Converts Bedrock parameters to the correct type.\n * @param params - The parameters to convert.\n * @returns A record of parameter names and their converted values.\n */\nexport function convertBedrockParamsToCorrectType(params?: Parameter[]): Record<string, any> {\n return (\n params?.reduce(\n (acc, param) => {\n acc[param.name] = converters[param.type] ? converters[param.type](param.value) : converters.defaultPassThrough(param.value);\n return acc;\n },\n {} as Record<string, any>\n ) ?? {}\n );\n}\n\n/**\n * Normalizes the session attributes, converting the type to T. An empty sessionAttributes object is the\n * same as undefined.\n *\n * Type T is the type of the session attributes.\n *\n * @param sessionAttributes The session attributes.\n * @returns The normalized session attributes.\n */\nexport function normalizeSessionAttributes(\n sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>\n): SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef> | undefined {\n if (!sessionAttributes || Object.keys(sessionAttributes).length === 0) {\n return undefined;\n }\n\n return sessionAttributes;\n}\n\n/**\n * Creates a BedrockLambdaResponse object.\n *\n * Type T is the type of the session attributes. By including the sessionAttributes\n * parameter, we are telling Bedrock to persist the session attributes for the\n * duration of the session and they will be passed in on all subsequent calls\n * using the event.sessionAttributes parameter.\n *\n * @param body The body of the response.\n * @param actionGroup The action group.\n * @param messageVersion The message version.\n * @param functionName The function name.\n * @param sessionAttributes The session attributes.\n * @param failed Whether the response is a failure.\n * @returns A BedrockLambdaResponse object.\n */\nexport function createBedrockLambdaResponse(\n body: string | Record<string, any>,\n actionGroup: string,\n messageVersion: string,\n functionName: string,\n sessionAttributes?: SessionDataWithChatUserCustomDataSpreadIn<RecordOrUndef>,\n failed?: boolean\n): BedrockActionGroupLambdaResponse {\n return {\n messageVersion,\n response: {\n actionGroup,\n function: functionName,\n functionResponse: {\n ...(failed ? { responseState: 'FAILURE' } : {}),\n responseBody: {\n TEXT: {\n body: typeof body === 'string' ? body : JSON.stringify(body)\n }\n }\n }\n },\n sessionAttributes\n };\n}\n"]}
|