@allma/core-sdk 1.0.1
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/dist/apiResponseBuilder.d.ts +26 -0
- package/dist/apiResponseBuilder.d.ts.map +1 -0
- package/dist/apiResponseBuilder.js +78 -0
- package/dist/apiResponseBuilder.js.map +1 -0
- package/dist/authUtils.d.ts +55 -0
- package/dist/authUtils.d.ts.map +1 -0
- package/dist/authUtils.js +122 -0
- package/dist/authUtils.js.map +1 -0
- package/dist/cdk-utils.d.ts +42 -0
- package/dist/cdk-utils.d.ts.map +1 -0
- package/dist/cdk-utils.js +62 -0
- package/dist/cdk-utils.js.map +1 -0
- package/dist/cloudformation-utils.d.ts +20 -0
- package/dist/cloudformation-utils.d.ts.map +1 -0
- package/dist/cloudformation-utils.js +27 -0
- package/dist/cloudformation-utils.js.map +1 -0
- package/dist/hydrationUtils.d.ts +5 -0
- package/dist/hydrationUtils.d.ts.map +1 -0
- package/dist/hydrationUtils.js +24 -0
- package/dist/hydrationUtils.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/jsonUtils.d.ts +19 -0
- package/dist/jsonUtils.d.ts.map +1 -0
- package/dist/jsonUtils.js +117 -0
- package/dist/jsonUtils.js.map +1 -0
- package/dist/logger.d.ts +6 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +31 -0
- package/dist/logger.js.map +1 -0
- package/dist/objectUtils.d.ts +13 -0
- package/dist/objectUtils.d.ts.map +1 -0
- package/dist/objectUtils.js +38 -0
- package/dist/objectUtils.js.map +1 -0
- package/dist/s3Utils.d.ts +18 -0
- package/dist/s3Utils.d.ts.map +1 -0
- package/dist/s3Utils.js +69 -0
- package/dist/s3Utils.js.map +1 -0
- package/dist/storageUtils.d.ts +22 -0
- package/dist/storageUtils.d.ts.map +1 -0
- package/dist/storageUtils.js +32 -0
- package/dist/storageUtils.js.map +1 -0
- package/dist/tokenEstimator.d.ts +2 -0
- package/dist/tokenEstimator.d.ts.map +1 -0
- package/dist/tokenEstimator.js +45 -0
- package/dist/tokenEstimator.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AdminApiErrorResponse, AdminApiSuccessResponse } from '@allma/core-types';
|
|
2
|
+
import type { APIGatewayProxyResultV2 } from 'aws-lambda';
|
|
3
|
+
/**
|
|
4
|
+
* Builds a standardized success response object.
|
|
5
|
+
* @param data The data payload for the successful response.
|
|
6
|
+
* @returns AdminApiSuccessResponse<T>
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildSuccessResponse<T>(data: T): AdminApiSuccessResponse<T>;
|
|
9
|
+
/**
|
|
10
|
+
* Builds a standardized error response object.
|
|
11
|
+
* @param message The error message.
|
|
12
|
+
* @param code An optional error code (e.g., 'NOT_FOUND', 'VALIDATION_ERROR').
|
|
13
|
+
* @param details Optional additional error details.
|
|
14
|
+
* @returns AdminApiErrorResponse
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildErrorResponse(message: string, code: string, details?: any): AdminApiErrorResponse;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a complete APIGatewayProxyResultV2 object for Lambda responses.
|
|
19
|
+
* It determines the HTTP status code based on the success flag and error code.
|
|
20
|
+
* @param defaultStatusCode The HTTP status code to use if success is true, or if no specific error code maps to a status.
|
|
21
|
+
* @param bodyObject The AdminApiSuccessResponse or AdminApiErrorResponse to be stringified in the body.
|
|
22
|
+
* @param correlationId Optional correlation ID for logging.
|
|
23
|
+
* @returns APIGatewayProxyResultV2
|
|
24
|
+
*/
|
|
25
|
+
export declare function createApiGatewayResponse(defaultStatusCode: number, bodyObject: AdminApiSuccessResponse<any> | AdminApiErrorResponse, correlationId?: string): APIGatewayProxyResultV2;
|
|
26
|
+
//# sourceMappingURL=apiResponseBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiResponseBuilder.d.ts","sourceRoot":"","sources":["../src/apiResponseBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACxF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,uBAAuB,CAAC,CAAC,CAAC,CAE3E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,GAAG,GACd,qBAAqB,CAEvB;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACpC,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,uBAAuB,CAAC,GAAG,CAAC,GAAG,qBAAqB,EAChE,aAAa,CAAC,EAAE,MAAM,GACvB,uBAAuB,CA8CzB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { log_debug } from './logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a standardized success response object.
|
|
4
|
+
* @param data The data payload for the successful response.
|
|
5
|
+
* @returns AdminApiSuccessResponse<T>
|
|
6
|
+
*/
|
|
7
|
+
export function buildSuccessResponse(data) {
|
|
8
|
+
return { success: true, data };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Builds a standardized error response object.
|
|
12
|
+
* @param message The error message.
|
|
13
|
+
* @param code An optional error code (e.g., 'NOT_FOUND', 'VALIDATION_ERROR').
|
|
14
|
+
* @param details Optional additional error details.
|
|
15
|
+
* @returns AdminApiErrorResponse
|
|
16
|
+
*/
|
|
17
|
+
export function buildErrorResponse(message, code, details) {
|
|
18
|
+
return { success: false, error: { message, code, details } };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a complete APIGatewayProxyResultV2 object for Lambda responses.
|
|
22
|
+
* It determines the HTTP status code based on the success flag and error code.
|
|
23
|
+
* @param defaultStatusCode The HTTP status code to use if success is true, or if no specific error code maps to a status.
|
|
24
|
+
* @param bodyObject The AdminApiSuccessResponse or AdminApiErrorResponse to be stringified in the body.
|
|
25
|
+
* @param correlationId Optional correlation ID for logging.
|
|
26
|
+
* @returns APIGatewayProxyResultV2
|
|
27
|
+
*/
|
|
28
|
+
export function createApiGatewayResponse(defaultStatusCode, bodyObject, correlationId) {
|
|
29
|
+
let effectiveStatusCode = defaultStatusCode;
|
|
30
|
+
if (!bodyObject.success) {
|
|
31
|
+
const errorCode = bodyObject.error.code;
|
|
32
|
+
if (errorCode === 'NOT_FOUND') {
|
|
33
|
+
effectiveStatusCode = 404;
|
|
34
|
+
}
|
|
35
|
+
else if (errorCode === 'VALIDATION_ERROR' || errorCode === 'INVALID_INPUT') {
|
|
36
|
+
effectiveStatusCode = 400;
|
|
37
|
+
}
|
|
38
|
+
else if (errorCode === 'UNAUTHORIZED') {
|
|
39
|
+
effectiveStatusCode = 401;
|
|
40
|
+
}
|
|
41
|
+
else if (errorCode === 'FORBIDDEN') {
|
|
42
|
+
effectiveStatusCode = 403;
|
|
43
|
+
}
|
|
44
|
+
else if (errorCode === 'CONFLICT') {
|
|
45
|
+
effectiveStatusCode = 409;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
effectiveStatusCode = defaultStatusCode >= 400 ? defaultStatusCode : 500; // Default to 500 for errors if not specified
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (defaultStatusCode === 204 && bodyObject.data === null) { // Handle 204 No Content specifically
|
|
52
|
+
// For 204, body should be empty.
|
|
53
|
+
log_debug(`Responding with statusCode: ${effectiveStatusCode} (No Content)`, {}, correlationId);
|
|
54
|
+
return {
|
|
55
|
+
statusCode: effectiveStatusCode,
|
|
56
|
+
headers: {
|
|
57
|
+
'Access-Control-Allow-Origin': '*',
|
|
58
|
+
'Access-Control-Allow-Credentials': 'true',
|
|
59
|
+
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
|
|
60
|
+
'Access-Control-Allow-Methods': 'OPTIONS,GET,POST,PUT,DELETE,PATCH',
|
|
61
|
+
},
|
|
62
|
+
body: '', // Empty body for 204
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
log_debug(`Responding with statusCode: ${effectiveStatusCode}`, { bodyPreview: JSON.stringify(bodyObject).substring(0, 2000) }, correlationId);
|
|
66
|
+
return {
|
|
67
|
+
statusCode: effectiveStatusCode,
|
|
68
|
+
headers: {
|
|
69
|
+
'Content-Type': 'application/json',
|
|
70
|
+
'Access-Control-Allow-Origin': '*',
|
|
71
|
+
'Access-Control-Allow-Credentials': 'true',
|
|
72
|
+
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
|
|
73
|
+
'Access-Control-Allow-Methods': 'OPTIONS,GET,POST,PUT,DELETE,PATCH',
|
|
74
|
+
},
|
|
75
|
+
body: JSON.stringify(bodyObject),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=apiResponseBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiResponseBuilder.js","sourceRoot":"","sources":["../src/apiResponseBuilder.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAI,IAAO;IAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAC9B,OAAe,EACf,IAAY,EACZ,OAAa;IAEb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;AACjE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACpC,iBAAyB,EACzB,UAAgE,EAChE,aAAsB;IAEtB,IAAI,mBAAmB,GAAG,iBAAiB,CAAC;IAE5C,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;QACxC,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;YAC5B,mBAAmB,GAAG,GAAG,CAAC;QAC9B,CAAC;aAAM,IAAI,SAAS,KAAK,kBAAkB,IAAI,SAAS,KAAK,eAAe,EAAE,CAAC;YAC3E,mBAAmB,GAAG,GAAG,CAAC;QAC9B,CAAC;aAAM,IAAI,SAAS,KAAK,cAAc,EAAE,CAAC;YACtC,mBAAmB,GAAG,GAAG,CAAC;QAC9B,CAAC;aAAM,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;YACnC,mBAAmB,GAAG,GAAG,CAAC;QAC9B,CAAC;aAAM,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;YAClC,mBAAmB,GAAG,GAAG,CAAC;QAC9B,CAAC;aAAM,CAAC;YACJ,mBAAmB,GAAG,iBAAiB,IAAI,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,6CAA6C;QAC3H,CAAC;IACL,CAAC;SAAM,IAAI,iBAAiB,KAAK,GAAG,IAAI,UAAU,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC,CAAC,qCAAqC;QACvG,iCAAiC;QACjC,SAAS,CAAC,+BAA+B,mBAAmB,eAAe,EAAE,EAAE,EAAE,aAAa,CAAC,CAAC;QAChG,OAAO;YACL,UAAU,EAAE,mBAAmB;YAC/B,OAAO,EAAE;gBACL,6BAA6B,EAAE,GAAG;gBAClC,kCAAkC,EAAE,MAAM;gBAC1C,8BAA8B,EAAE,sEAAsE;gBACtG,8BAA8B,EAAE,mCAAmC;aACtE;YACD,IAAI,EAAE,EAAE,EAAE,qBAAqB;SAChC,CAAC;IACJ,CAAC;IAGD,SAAS,CAAC,+BAA+B,mBAAmB,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAC/I,OAAO;QACH,UAAU,EAAE,mBAAmB;QAC/B,OAAO,EAAE;YACL,cAAc,EAAE,kBAAkB;YAClC,6BAA6B,EAAE,GAAG;YAClC,kCAAkC,EAAE,MAAM;YAC1C,8BAA8B,EAAE,sEAAsE;YACtG,8BAA8B,EAAE,mCAAmC;SACtE;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;KACnC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { APIGatewayProxyEventV2WithJWTAuthorizer } from 'aws-lambda';
|
|
2
|
+
import { AdminPermission, AdminRole } from '@allma/core-types';
|
|
3
|
+
export declare const ADMIN_COGNITO_GROUP_NAME = "Admins";
|
|
4
|
+
export declare const CUSTOM_ADMIN_ROLES_ATTRIBUTE = "custom:admin_roles";
|
|
5
|
+
export interface AuthContext {
|
|
6
|
+
userId: string;
|
|
7
|
+
username: string;
|
|
8
|
+
cognitoGroups: string[];
|
|
9
|
+
adminPermissions: {
|
|
10
|
+
roles: AdminRole[];
|
|
11
|
+
permissions: AdminPermission[];
|
|
12
|
+
};
|
|
13
|
+
isAuthenticated: boolean;
|
|
14
|
+
isSuperAdmin: boolean;
|
|
15
|
+
hasPermission: (permission: AdminPermission) => boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Options for configuring authentication behavior.
|
|
19
|
+
*/
|
|
20
|
+
export interface AuthOptions {
|
|
21
|
+
/**
|
|
22
|
+
* The name of the Cognito group that a user must be a member of.
|
|
23
|
+
* @default 'Admins'
|
|
24
|
+
*/
|
|
25
|
+
requiredGroup?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The name of the custom attribute in the JWT that contains user roles and permissions.
|
|
28
|
+
* @default 'custom:admin_roles'
|
|
29
|
+
*/
|
|
30
|
+
customRolesAttribute?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extracts and validates authentication and authorization information from an API Gateway event.
|
|
34
|
+
* This function is now a pure context extractor and does not enforce group membership.
|
|
35
|
+
*
|
|
36
|
+
* @param event The APIGatewayProxyEventV2.
|
|
37
|
+
* @param correlationId Optional correlation ID for logging.
|
|
38
|
+
* @param options Optional configuration for the custom roles attribute.
|
|
39
|
+
* @returns An AuthContext object.
|
|
40
|
+
* @throws Error if unauthorized (e.g., missing claims).
|
|
41
|
+
*/
|
|
42
|
+
export declare function getAuthContext(event: APIGatewayProxyEventV2WithJWTAuthorizer, correlationId?: string, options?: {
|
|
43
|
+
customRolesAttribute?: string;
|
|
44
|
+
}): AuthContext;
|
|
45
|
+
/**
|
|
46
|
+
* Higher-order function to wrap Lambda handlers with authentication and authorization.
|
|
47
|
+
* This middleware enforces membership in a specified Cognito group and provides an
|
|
48
|
+
* AuthContext for granular permission checks within the handler.
|
|
49
|
+
*
|
|
50
|
+
* @param handler The original Lambda handler function, now receiving an `AuthContext` object.
|
|
51
|
+
* @param options Optional configuration to specify the required group and custom roles attribute.
|
|
52
|
+
* @returns A new Lambda handler function compatible with API Gateway.
|
|
53
|
+
*/
|
|
54
|
+
export declare function withAdminAuth(handler: (event: APIGatewayProxyEventV2WithJWTAuthorizer, authContext: AuthContext) => Promise<any>, options?: AuthOptions): (event: APIGatewayProxyEventV2WithJWTAuthorizer) => Promise<any>;
|
|
55
|
+
//# sourceMappingURL=authUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authUtils.d.ts","sourceRoot":"","sources":["../src/authUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,SAAS,EAA8E,MAAM,mBAAmB,CAAC;AAI3I,eAAO,MAAM,wBAAwB,WAAW,CAAC;AACjD,eAAO,MAAM,4BAA4B,uBAAuB,CAAC;AAEjE,MAAM,WAAW,WAAW;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE;QACd,KAAK,EAAE,SAAS,EAAE,CAAC;QACnB,WAAW,EAAE,eAAe,EAAE,CAAC;KAClC,CAAC;IACF,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,OAAO,CAAC;CAC3D;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAGD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,uCAAuC,EAC9C,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,WAAW,CAqEb;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CACzB,OAAO,EAAE,CAAC,KAAK,EAAE,uCAAuC,EAAE,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,GAAG,CAAC,EACnG,OAAO,CAAC,EAAE,WAAW,IAEP,OAAO,uCAAuC,kBAoD/D"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { buildErrorResponse, createApiGatewayResponse } from './apiResponseBuilder.js';
|
|
2
|
+
import { AdminRole, AdminUserPermissionsSchema, getDefaultPermissionsForRole, isInCognitoGroup } from '@allma/core-types';
|
|
3
|
+
import { log_debug, log_error, log_info, log_warn } from './logger.js';
|
|
4
|
+
// Constants for Cognito group and custom attribute names
|
|
5
|
+
export const ADMIN_COGNITO_GROUP_NAME = 'Admins';
|
|
6
|
+
export const CUSTOM_ADMIN_ROLES_ATTRIBUTE = 'custom:admin_roles';
|
|
7
|
+
/**
|
|
8
|
+
* Extracts and validates authentication and authorization information from an API Gateway event.
|
|
9
|
+
* This function is now a pure context extractor and does not enforce group membership.
|
|
10
|
+
*
|
|
11
|
+
* @param event The APIGatewayProxyEventV2.
|
|
12
|
+
* @param correlationId Optional correlation ID for logging.
|
|
13
|
+
* @param options Optional configuration for the custom roles attribute.
|
|
14
|
+
* @returns An AuthContext object.
|
|
15
|
+
* @throws Error if unauthorized (e.g., missing claims).
|
|
16
|
+
*/
|
|
17
|
+
export function getAuthContext(event, correlationId, options) {
|
|
18
|
+
const claims = event.requestContext.authorizer?.jwt?.claims;
|
|
19
|
+
if (!claims) {
|
|
20
|
+
log_warn('Unauthorized: No JWT claims found in event.requestContext.authorizer.', {}, correlationId);
|
|
21
|
+
throw new Error('Unauthorized');
|
|
22
|
+
}
|
|
23
|
+
const userId = claims.sub;
|
|
24
|
+
const username = claims.email;
|
|
25
|
+
const cognitoGroupsClaimValue = claims['cognito:groups'];
|
|
26
|
+
let cognitoGroups = [];
|
|
27
|
+
if (Array.isArray(cognitoGroupsClaimValue)) {
|
|
28
|
+
cognitoGroups = cognitoGroupsClaimValue.filter(group => typeof group === 'string');
|
|
29
|
+
log_debug('Parsed cognito:groups from JWT array.', { rawGroups: cognitoGroupsClaimValue, parsed: cognitoGroups }, correlationId);
|
|
30
|
+
}
|
|
31
|
+
else if (typeof cognitoGroupsClaimValue === 'string') {
|
|
32
|
+
let processableString = cognitoGroupsClaimValue.trim();
|
|
33
|
+
if (processableString.startsWith('[') && processableString.endsWith(']')) {
|
|
34
|
+
processableString = processableString.substring(1, processableString.length - 1);
|
|
35
|
+
}
|
|
36
|
+
cognitoGroups = processableString.split(',').map(group => group.trim()).filter(group => group.length > 0);
|
|
37
|
+
log_debug('Parsed cognito:groups from JWT string.', { rawString: cognitoGroupsClaimValue, processedString: processableString, parsed: cognitoGroups }, correlationId);
|
|
38
|
+
}
|
|
39
|
+
else if (cognitoGroupsClaimValue !== undefined && cognitoGroupsClaimValue !== null) {
|
|
40
|
+
log_warn('cognito:groups claim is present but is not an array or string.', { type: typeof cognitoGroupsClaimValue, value: cognitoGroupsClaimValue }, correlationId);
|
|
41
|
+
}
|
|
42
|
+
const customRolesAttr = options?.customRolesAttribute || CUSTOM_ADMIN_ROLES_ATTRIBUTE;
|
|
43
|
+
const customAdminRolesString = claims[customRolesAttr];
|
|
44
|
+
let adminUserPermissions = { roles: [], permissions: [] };
|
|
45
|
+
if (typeof customAdminRolesString === 'string') {
|
|
46
|
+
try {
|
|
47
|
+
const parsedPermissions = JSON.parse(customAdminRolesString);
|
|
48
|
+
const validationResult = AdminUserPermissionsSchema.safeParse(parsedPermissions);
|
|
49
|
+
if (validationResult.success) {
|
|
50
|
+
adminUserPermissions = validationResult.data;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
log_warn(`Invalid ${customRolesAttr} attribute format for user.`, { userId, errors: validationResult.error.flatten().fieldErrors }, correlationId);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
log_warn(`Failed to parse ${customRolesAttr} attribute for user.`, { userId, customAdminRolesString, error: e.message }, correlationId);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const isSuperAdmin = adminUserPermissions.roles.includes(AdminRole.SUPER_ADMIN);
|
|
61
|
+
const effectivePermissions = new Set();
|
|
62
|
+
adminUserPermissions.roles.forEach(role => {
|
|
63
|
+
getDefaultPermissionsForRole(role).forEach(p => effectivePermissions.add(p));
|
|
64
|
+
});
|
|
65
|
+
adminUserPermissions.permissions.forEach(p => effectivePermissions.add(p));
|
|
66
|
+
const authContext = {
|
|
67
|
+
userId,
|
|
68
|
+
username,
|
|
69
|
+
cognitoGroups,
|
|
70
|
+
adminPermissions: {
|
|
71
|
+
roles: adminUserPermissions.roles,
|
|
72
|
+
permissions: Array.from(effectivePermissions),
|
|
73
|
+
},
|
|
74
|
+
isAuthenticated: true,
|
|
75
|
+
isSuperAdmin,
|
|
76
|
+
hasPermission: (permission) => (isSuperAdmin || effectivePermissions.has(permission)),
|
|
77
|
+
};
|
|
78
|
+
log_info(`Admin API request auth context established for user: ${username} (ID: ${userId})`, { groups: cognitoGroups, roles: authContext.adminPermissions.roles, permissions: authContext.adminPermissions.permissions.join(',') }, correlationId);
|
|
79
|
+
return authContext;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Higher-order function to wrap Lambda handlers with authentication and authorization.
|
|
83
|
+
* This middleware enforces membership in a specified Cognito group and provides an
|
|
84
|
+
* AuthContext for granular permission checks within the handler.
|
|
85
|
+
*
|
|
86
|
+
* @param handler The original Lambda handler function, now receiving an `AuthContext` object.
|
|
87
|
+
* @param options Optional configuration to specify the required group and custom roles attribute.
|
|
88
|
+
* @returns A new Lambda handler function compatible with API Gateway.
|
|
89
|
+
*/
|
|
90
|
+
export function withAdminAuth(handler, options) {
|
|
91
|
+
return async (event) => {
|
|
92
|
+
const correlationId = event.requestContext.requestId || event.headers['X-Correlation-Id'] || 'UNKNOWN';
|
|
93
|
+
try {
|
|
94
|
+
// Step 1: Extract context. This will throw 'Unauthorized' if claims are missing.
|
|
95
|
+
const authContext = getAuthContext(event, correlationId, options?.customRolesAttribute
|
|
96
|
+
? { customRolesAttribute: options.customRolesAttribute }
|
|
97
|
+
: undefined);
|
|
98
|
+
// Step 2: Enforce group membership.
|
|
99
|
+
const requiredGroup = options?.requiredGroup || ADMIN_COGNITO_GROUP_NAME;
|
|
100
|
+
if (!isInCognitoGroup(authContext.cognitoGroups, requiredGroup)) {
|
|
101
|
+
log_warn(`Forbidden: User '${authContext.username}' (ID: ${authContext.userId}) is not a member of the required '${requiredGroup}' group. User groups: ${authContext.cognitoGroups.join(', ')}`, {}, correlationId);
|
|
102
|
+
throw new Error('Forbidden');
|
|
103
|
+
}
|
|
104
|
+
// Step 3: Call the original handler if authorization succeeds.
|
|
105
|
+
return await handler(event, authContext);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
log_error(`Authorization error in Lambda middleware: ${error.message}`, { errorName: error.name, stack: error.stack }, correlationId);
|
|
109
|
+
log_error(`Authorization error | event is : ${JSON.stringify(event)}`, {}, correlationId);
|
|
110
|
+
if (error.message === 'Unauthorized') {
|
|
111
|
+
return createApiGatewayResponse(401, buildErrorResponse('Unauthorized: Authentication token is missing or invalid.', 'UNAUTHORIZED'), correlationId);
|
|
112
|
+
}
|
|
113
|
+
else if (error.message === 'Forbidden') {
|
|
114
|
+
return createApiGatewayResponse(403, buildErrorResponse('Forbidden: You do not have access to this resource.', 'FORBIDDEN'), correlationId);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
return createApiGatewayResponse(500, buildErrorResponse('Internal Server Error: Failed to process authentication.', 'SERVER_ERROR'), correlationId);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=authUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authUtils.js","sourceRoot":"","sources":["../src/authUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import { StepType } from '@allma/core-types';
|
|
3
|
+
export interface ExternalStepRegistrationProps {
|
|
4
|
+
/**
|
|
5
|
+
The name of the central Allma configuration table.
|
|
6
|
+
*/
|
|
7
|
+
configTableName: string;
|
|
8
|
+
/**
|
|
9
|
+
The unique identifier for the step (e.g., 'my-module/my-step').
|
|
10
|
+
*/
|
|
11
|
+
moduleIdentifier: string;
|
|
12
|
+
/**
|
|
13
|
+
The user-friendly name for the step in the UI.
|
|
14
|
+
*/
|
|
15
|
+
displayName: string;
|
|
16
|
+
/**
|
|
17
|
+
A short description of what the step does.
|
|
18
|
+
*/
|
|
19
|
+
description: string;
|
|
20
|
+
/**
|
|
21
|
+
The step's functional type (e.g., DATA_LOAD).
|
|
22
|
+
*/
|
|
23
|
+
stepType: StepType;
|
|
24
|
+
/**
|
|
25
|
+
The ARN of the Lambda function that executes the step's logic.
|
|
26
|
+
*/
|
|
27
|
+
lambdaArn: string;
|
|
28
|
+
/**
|
|
29
|
+
A default configuration object for the step instance when dragged onto the canvas.
|
|
30
|
+
It will be automatically merged with the moduleIdentifier.
|
|
31
|
+
*/
|
|
32
|
+
defaultConfig: Record<string, any>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
A reusable CDK utility to register an external step in the Allma DynamoDB registry.
|
|
36
|
+
Creates an AwsCustomResource that handles putting the item on deploy/update and deleting it on destroy.
|
|
37
|
+
@param scope The CDK Construct scope.
|
|
38
|
+
@param id The unique ID for this construct.
|
|
39
|
+
@param props The properties for the step registration.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createExternalStepRegistration(scope: Construct, id: string, props: ExternalStepRegistrationProps): void;
|
|
42
|
+
//# sourceMappingURL=cdk-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdk-utils.d.ts","sourceRoot":"","sources":["../src/cdk-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAKvC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,MAAM,WAAW,6BAA6B;IAC1C;;MAEE;IACF,eAAe,EAAE,MAAM,CAAC;IACxB;;MAEE;IACF,gBAAgB,EAAE,MAAM,CAAC;IACzB;;MAEE;IACF,WAAW,EAAE,MAAM,CAAC;IACpB;;MAEE;IACF,WAAW,EAAE,MAAM,CAAC;IACpB;;MAEE;IACF,QAAQ,EAAE,QAAQ,CAAC;IACnB;;MAEE;IACF,SAAS,EAAE,MAAM,CAAC;IAClB;;;MAGE;IACF,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACtC;AACD;;;;;;EAME;AACF,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,6BAA6B,QAiDhH"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as cdk from 'aws-cdk-lib';
|
|
2
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
3
|
+
import { AwsCustomResource, PhysicalResourceId } from 'aws-cdk-lib/custom-resources';
|
|
4
|
+
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
5
|
+
/**
|
|
6
|
+
A reusable CDK utility to register an external step in the Allma DynamoDB registry.
|
|
7
|
+
Creates an AwsCustomResource that handles putting the item on deploy/update and deleting it on destroy.
|
|
8
|
+
@param scope The CDK Construct scope.
|
|
9
|
+
@param id The unique ID for this construct.
|
|
10
|
+
@param props The properties for the step registration.
|
|
11
|
+
*/
|
|
12
|
+
export function createExternalStepRegistration(scope, id, props) {
|
|
13
|
+
const { configTableName, moduleIdentifier, displayName, description, stepType, lambdaArn, defaultConfig } = props;
|
|
14
|
+
const registrationId = `EXTERNAL_STEP#${moduleIdentifier}`;
|
|
15
|
+
const stack = cdk.Stack.of(scope);
|
|
16
|
+
const sdkCall = {
|
|
17
|
+
service: 'DynamoDB',
|
|
18
|
+
action: 'putItem',
|
|
19
|
+
parameters: {
|
|
20
|
+
TableName: configTableName,
|
|
21
|
+
Item: {
|
|
22
|
+
PK: { S: registrationId },
|
|
23
|
+
SK: { S: 'METADATA' },
|
|
24
|
+
itemType: { S: 'ALLMA_EXTERNAL_STEP_REGISTRY' },
|
|
25
|
+
moduleIdentifier: { S: moduleIdentifier },
|
|
26
|
+
lambdaArn: { S: lambdaArn },
|
|
27
|
+
displayName: { S: displayName },
|
|
28
|
+
description: { S: description },
|
|
29
|
+
stepType: { S: stepType },
|
|
30
|
+
defaultConfig: { S: JSON.stringify({ moduleIdentifier, ...defaultConfig }) },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
physicalResourceId: PhysicalResourceId.of(registrationId),
|
|
34
|
+
};
|
|
35
|
+
const deleteCall = {
|
|
36
|
+
service: 'DynamoDB',
|
|
37
|
+
action: 'deleteItem',
|
|
38
|
+
parameters: {
|
|
39
|
+
TableName: configTableName,
|
|
40
|
+
Key: {
|
|
41
|
+
PK: { S: registrationId },
|
|
42
|
+
SK: { S: 'METADATA' },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
new AwsCustomResource(scope, id, {
|
|
47
|
+
onCreate: sdkCall,
|
|
48
|
+
onUpdate: sdkCall,
|
|
49
|
+
onDelete: deleteCall,
|
|
50
|
+
logRetention: RetentionDays.ONE_WEEK,
|
|
51
|
+
installLatestAwsSdk: true, // Ensures SDK is up-to-date for DynamoDB calls
|
|
52
|
+
policy: {
|
|
53
|
+
statements: [
|
|
54
|
+
new iam.PolicyStatement({
|
|
55
|
+
actions: ['dynamodb:PutItem', 'dynamodb:DeleteItem'],
|
|
56
|
+
resources: [`arn:aws:dynamodb:${stack.region}:${stack.account}:table/${configTableName}`],
|
|
57
|
+
}),
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=cdk-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdk-utils.js","sourceRoot":"","sources":["../src/cdk-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAc,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACjG,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAiCrD;;;;;;EAME;AACF,MAAM,UAAU,8BAA8B,CAAC,KAAgB,EAAE,EAAU,EAAE,KAAoC;IAC7G,MAAM,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC;IAClH,MAAM,cAAc,GAAG,iBAAkB,gBAAiB,EAAE,CAAC;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,OAAO,GAAe;QACxB,OAAO,EAAE,UAAU;QACnB,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE;YACR,SAAS,EAAE,eAAe;YAC1B,IAAI,EAAE;gBACF,EAAE,EAAE,EAAE,CAAC,EAAE,cAAc,EAAE;gBACzB,EAAE,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE;gBACrB,QAAQ,EAAE,EAAE,CAAC,EAAE,8BAA8B,EAAE;gBAC/C,gBAAgB,EAAE,EAAE,CAAC,EAAE,gBAAgB,EAAE;gBACzC,SAAS,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE;gBAC3B,WAAW,EAAE,EAAE,CAAC,EAAE,WAAW,EAAE;gBAC/B,WAAW,EAAE,EAAE,CAAC,EAAE,WAAW,EAAE;gBAC/B,QAAQ,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE;gBACzB,aAAa,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAE,GAAG,aAAa,EAAE,CAAC,EAAE;aAC/E;SACJ;QACD,kBAAkB,EAAE,kBAAkB,CAAC,EAAE,CAAC,cAAc,CAAC;KAC5D,CAAC;IACF,MAAM,UAAU,GAAe;QAC3B,OAAO,EAAE,UAAU;QACnB,MAAM,EAAE,YAAY;QACpB,UAAU,EAAE;YACR,SAAS,EAAE,eAAe;YAC1B,GAAG,EAAE;gBACD,EAAE,EAAE,EAAE,CAAC,EAAE,cAAc,EAAE;gBACzB,EAAE,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE;aACxB;SACJ;KACJ,CAAC;IACF,IAAI,iBAAiB,CAAC,KAAK,EAAE,EAAE,EAAE;QAC7B,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,UAAU;QACpB,YAAY,EAAE,aAAa,CAAC,QAAQ;QACpC,mBAAmB,EAAE,IAAI,EAAE,+CAA+C;QAC1E,MAAM,EAAE;YACJ,UAAU,EAAE;gBACR,IAAI,GAAG,CAAC,eAAe,CAAC;oBACpB,OAAO,EAAE,CAAC,kBAAkB,EAAE,qBAAqB,CAAC;oBACpD,SAAS,EAAE,CAAC,oBAAoB,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,UAAU,eAAe,EAAE,CAAC;iBAC5F,CAAC;aACL;SACJ;KACJ,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface CloudFormationEvent {
|
|
2
|
+
RequestType: 'Create' | 'Update' | 'Delete';
|
|
3
|
+
ServiceToken: string;
|
|
4
|
+
ResponseURL: string;
|
|
5
|
+
StackId: string;
|
|
6
|
+
RequestId: string;
|
|
7
|
+
LogicalResourceId: string;
|
|
8
|
+
PhysicalResourceId?: string;
|
|
9
|
+
ResourceType: string;
|
|
10
|
+
ResourceProperties: {
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
};
|
|
13
|
+
OldResourceProperties?: {
|
|
14
|
+
[key: string]: any;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function sendCloudFormationResponse(event: CloudFormationEvent, status: 'SUCCESS' | 'FAILED', data?: {
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=cloudformation-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudformation-utils.d.ts","sourceRoot":"","sources":["../src/cloudformation-utils.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IAC3C,qBAAqB,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAChD;AAED,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,mBAAmB,EAC1B,MAAM,EAAE,SAAS,GAAG,QAAQ,EAC5B,IAAI,CAAC,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,GAC5B,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export async function sendCloudFormationResponse(event, status, data) {
|
|
2
|
+
const responseBody = JSON.stringify({
|
|
3
|
+
Status: status,
|
|
4
|
+
Reason: `See the details in CloudWatch Log Stream: ${process.env.AWS_LAMBDA_LOG_STREAM_NAME}`,
|
|
5
|
+
PhysicalResourceId: event.PhysicalResourceId || `custom-resource-${Date.now()}`,
|
|
6
|
+
StackId: event.StackId,
|
|
7
|
+
RequestId: event.RequestId,
|
|
8
|
+
LogicalResourceId: event.LogicalResourceId,
|
|
9
|
+
Data: data,
|
|
10
|
+
});
|
|
11
|
+
const responseOptions = {
|
|
12
|
+
method: 'PUT',
|
|
13
|
+
body: responseBody,
|
|
14
|
+
headers: {
|
|
15
|
+
'content-type': '',
|
|
16
|
+
'content-length': responseBody.length.toString(),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(event.ResponseURL, responseOptions);
|
|
21
|
+
console.log('CloudFormation response status:', response.status);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error('Failed to send CloudFormation response:', error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=cloudformation-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudformation-utils.js","sourceRoot":"","sources":["../src/cloudformation-utils.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAA0B,EAC1B,MAA4B,EAC5B,IAA6B;IAE7B,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC;QAClC,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,6CAA6C,OAAO,CAAC,GAAG,CAAC,0BAA0B,EAAE;QAC7F,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,IAAI,mBAAmB,IAAI,CAAC,GAAG,EAAE,EAAE;QAC/E,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,IAAI,EAAE,IAAI;KACX,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG;QACtB,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE;YACP,cAAc,EAAE,EAAE;YAClB,gBAAgB,EAAE,YAAY,CAAC,MAAM,CAAC,QAAQ,EAAE;SACjD;KACF,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QACjE,OAAO,CAAC,GAAG,CAAC,iCAAiC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAC;IAClE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrationUtils.d.ts","sourceRoot":"","sources":["../src/hydrationUtils.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,GAAG,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAiBhG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { resolveS3Pointer } from './s3Utils.js';
|
|
2
|
+
import { isS3OutputPointerWrapper } from '@allma/core-types';
|
|
3
|
+
/**
|
|
4
|
+
* Recursively traverses an object or array and resolves any S3 pointers it finds.
|
|
5
|
+
*/
|
|
6
|
+
export async function hydrateInputFromS3Pointers(data, correlationId) {
|
|
7
|
+
if (Array.isArray(data)) {
|
|
8
|
+
return Promise.all(data.map(item => hydrateInputFromS3Pointers(item, correlationId)));
|
|
9
|
+
}
|
|
10
|
+
if (isS3OutputPointerWrapper(data)) {
|
|
11
|
+
return resolveS3Pointer(data._s3_output_pointer, correlationId);
|
|
12
|
+
}
|
|
13
|
+
if (data && typeof data === 'object') {
|
|
14
|
+
const hydratedObject = {};
|
|
15
|
+
for (const key in data) {
|
|
16
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
17
|
+
hydratedObject[key] = await hydrateInputFromS3Pointers(data[key], correlationId);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return hydratedObject;
|
|
21
|
+
}
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=hydrationUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrationUtils.js","sourceRoot":"","sources":["../src/hydrationUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,IAAS,EAAE,aAAsB;IAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,0BAA0B,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACxF,CAAC;IACD,IAAI,wBAAwB,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,OAAO,gBAAgB,CAAC,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACrC,MAAM,cAAc,GAAwB,EAAE,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACpD,cAAc,CAAC,GAAG,CAAC,GAAG,MAAM,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;QACD,OAAO,cAAc,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './apiResponseBuilder.js';
|
|
2
|
+
export * from './authUtils.js';
|
|
3
|
+
export * from './cloudformation-utils.js';
|
|
4
|
+
export * from './hydrationUtils.js';
|
|
5
|
+
export * from './jsonUtils.js';
|
|
6
|
+
export * from './logger.js';
|
|
7
|
+
export * from './s3Utils.js';
|
|
8
|
+
export * from './tokenEstimator.js';
|
|
9
|
+
export * from './storageUtils.js';
|
|
10
|
+
export * from './objectUtils.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './apiResponseBuilder.js';
|
|
2
|
+
export * from './authUtils.js';
|
|
3
|
+
export * from './cloudformation-utils.js';
|
|
4
|
+
export * from './hydrationUtils.js';
|
|
5
|
+
export * from './jsonUtils.js';
|
|
6
|
+
export * from './logger.js';
|
|
7
|
+
export * from './s3Utils.js';
|
|
8
|
+
export * from './tokenEstimator.js';
|
|
9
|
+
export * from './storageUtils.js';
|
|
10
|
+
export * from './objectUtils.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts a potential JSON string from a raw LLM response,
|
|
3
|
+
* attempts to repair it, and then parses it.
|
|
4
|
+
* Handles common issues like markdown code fences and leading/trailing text.
|
|
5
|
+
*
|
|
6
|
+
* @param inputText The raw string response (for example from the LLM).
|
|
7
|
+
* @param correlationId Optional correlation ID for logging.
|
|
8
|
+
* @returns The parsed JSON object.
|
|
9
|
+
* @throws JsonParseError if parsing fails after repair attempts.
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractAndParseJson(inputText: string, correlationId?: string): any;
|
|
12
|
+
/**
|
|
13
|
+
* A safer version of extractAndParseJson that returns null on failure instead of throwing.
|
|
14
|
+
* @param rawLlmResponse The raw string response from the LLM.
|
|
15
|
+
* @param correlationId Optional correlation ID for logging.
|
|
16
|
+
* @returns The parsed JSON object or null if parsing fails.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractAndParseJsonSafe(rawLlmResponse: string, correlationId?: string): any | null;
|
|
19
|
+
//# sourceMappingURL=jsonUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonUtils.d.ts","sourceRoot":"","sources":["../src/jsonUtils.ts"],"names":[],"mappings":"AAMA;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,GAAG,CAwFlF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAOlG"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as jsonrepairModule from 'jsonrepair';
|
|
2
|
+
import { log_debug, log_warn } from './logger.js';
|
|
3
|
+
import { JsonParseError } from '@allma/core-types';
|
|
4
|
+
const repairFunc = jsonrepairModule.jsonrepair || jsonrepairModule;
|
|
5
|
+
/**
|
|
6
|
+
* Extracts a potential JSON string from a raw LLM response,
|
|
7
|
+
* attempts to repair it, and then parses it.
|
|
8
|
+
* Handles common issues like markdown code fences and leading/trailing text.
|
|
9
|
+
*
|
|
10
|
+
* @param inputText The raw string response (for example from the LLM).
|
|
11
|
+
* @param correlationId Optional correlation ID for logging.
|
|
12
|
+
* @returns The parsed JSON object.
|
|
13
|
+
* @throws JsonParseError if parsing fails after repair attempts.
|
|
14
|
+
*/
|
|
15
|
+
export function extractAndParseJson(inputText, correlationId) {
|
|
16
|
+
if (typeof repairFunc !== 'function') {
|
|
17
|
+
// This will give a very clear error in logs if the import fails.
|
|
18
|
+
throw new JsonParseError('Critical error: jsonrepair function could not be imported correctly.');
|
|
19
|
+
}
|
|
20
|
+
if (!inputText || inputText.trim() === "") {
|
|
21
|
+
log_warn('Cannot parse empty or whitespace-only LLM response.', { correlationId });
|
|
22
|
+
throw new JsonParseError("Raw LLM response is empty or whitespace.", { rawResponse: inputText });
|
|
23
|
+
}
|
|
24
|
+
let textToParse = inputText.trim();
|
|
25
|
+
// Attempt to remove markdown fences (```json ... ``` or ``` ... ```)
|
|
26
|
+
// Regex to capture content within ```json ... ``` or ``` ... ```
|
|
27
|
+
const markdownJsonRegex = /^```(?:json)?\s*([\s\S]*?)\s*```$/m;
|
|
28
|
+
const markdownMatch = textToParse.match(markdownJsonRegex);
|
|
29
|
+
if (markdownMatch && markdownMatch[1]) {
|
|
30
|
+
textToParse = markdownMatch[1].trim();
|
|
31
|
+
log_debug('Removed markdown fences from LLM response.', { correlationId });
|
|
32
|
+
}
|
|
33
|
+
// Fallback: if no markdown, try to find the outermost JSON structure.
|
|
34
|
+
// This helps if there's leading/trailing text without markdown.
|
|
35
|
+
if (!(markdownMatch && markdownMatch[1])) {
|
|
36
|
+
const firstBrace = textToParse.indexOf('{');
|
|
37
|
+
const firstBracket = textToParse.indexOf('[');
|
|
38
|
+
let startIndex = -1;
|
|
39
|
+
if (firstBrace !== -1 && firstBracket !== -1) {
|
|
40
|
+
startIndex = Math.min(firstBrace, firstBracket);
|
|
41
|
+
}
|
|
42
|
+
else if (firstBrace !== -1) {
|
|
43
|
+
startIndex = firstBrace;
|
|
44
|
+
}
|
|
45
|
+
else if (firstBracket !== -1) {
|
|
46
|
+
startIndex = firstBracket;
|
|
47
|
+
}
|
|
48
|
+
if (startIndex !== -1) {
|
|
49
|
+
const lastBrace = textToParse.lastIndexOf('}');
|
|
50
|
+
const lastBracket = textToParse.lastIndexOf(']');
|
|
51
|
+
let endIndex = -1;
|
|
52
|
+
if (lastBrace !== -1 && lastBracket !== -1) {
|
|
53
|
+
endIndex = Math.max(lastBrace, lastBracket);
|
|
54
|
+
}
|
|
55
|
+
else if (lastBrace !== -1) {
|
|
56
|
+
endIndex = lastBrace;
|
|
57
|
+
}
|
|
58
|
+
else if (lastBracket !== -1) {
|
|
59
|
+
endIndex = lastBracket;
|
|
60
|
+
}
|
|
61
|
+
if (endIndex > startIndex) {
|
|
62
|
+
const extractedSubstring = textToParse.substring(startIndex, endIndex + 1);
|
|
63
|
+
// Basic sanity check: does the substring look plausible?
|
|
64
|
+
// (e.g. starts with { and ends with } or starts with [ and ends with ])
|
|
65
|
+
const firstChar = extractedSubstring.charAt(0);
|
|
66
|
+
const lastChar = extractedSubstring.charAt(extractedSubstring.length - 1);
|
|
67
|
+
if ((firstChar === '{' && lastChar === '}') || (firstChar === '[' && lastChar === ']')) {
|
|
68
|
+
textToParse = extractedSubstring;
|
|
69
|
+
log_debug('Extracted potential JSON substring from surrounding text.', { correlationId });
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
log_debug('Found start/end braces/brackets, but they did not form a plausible outer pair. Will attempt repair on mostly original text.', { correlationId });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
log_debug('Could not determine a clear JSON substring from braces/brackets. Attempting repair on (markdown-stripped) text.', { correlationId });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
log_debug('No braces or brackets found. Attempting repair on (markdown-stripped) text.', { correlationId });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const repairedJsonString = repairFunc(textToParse);
|
|
85
|
+
//const repairedJsonString = JSON.parse(textToParse);
|
|
86
|
+
log_debug('LLM response string repaired, attempting JSON.parse.', { correlationId });
|
|
87
|
+
return JSON.parse(repairedJsonString);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
log_warn('Failed to parse JSON even after attempting extraction and repair.', {
|
|
91
|
+
originalTextPreview: inputText.substring(0, 2000),
|
|
92
|
+
processedTextPreview: textToParse.substring(0, 2000),
|
|
93
|
+
error: error.message,
|
|
94
|
+
correlationId
|
|
95
|
+
});
|
|
96
|
+
throw new JsonParseError(`Failed to parse JSON object from LLM response: ${error.message}`, {
|
|
97
|
+
rawResponseSnippet: inputText.substring(0, 2000),
|
|
98
|
+
attemptedToParseSnippet: textToParse.substring(0, 2000)
|
|
99
|
+
}, error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A safer version of extractAndParseJson that returns null on failure instead of throwing.
|
|
104
|
+
* @param rawLlmResponse The raw string response from the LLM.
|
|
105
|
+
* @param correlationId Optional correlation ID for logging.
|
|
106
|
+
* @returns The parsed JSON object or null if parsing fails.
|
|
107
|
+
*/
|
|
108
|
+
export function extractAndParseJsonSafe(rawLlmResponse, correlationId) {
|
|
109
|
+
try {
|
|
110
|
+
return extractAndParseJson(rawLlmResponse, correlationId);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
// Error is already logged by extractAndParseJson
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=jsonUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonUtils.js","sourceRoot":"","sources":["../src/jsonUtils.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const log_debug: (message: string, details?: object, correlationId?: string) => void;
|
|
2
|
+
export declare const log_info: (message: string, details?: object, correlationId?: string) => void;
|
|
3
|
+
export declare const log_warn: (message: string, details?: object, correlationId?: string) => void;
|
|
4
|
+
export declare const log_error: (message: string, details?: object, correlationId?: string) => void;
|
|
5
|
+
export declare const log_critical: (message: string, details?: object, correlationId?: string) => void;
|
|
6
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AA+BA,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,EAAE,UAAS,MAAW,EAAE,gBAAgB,MAAM,SAAyD,CAAC;AACjJ,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,EAAE,UAAS,MAAW,EAAE,gBAAgB,MAAM,SAAwD,CAAC;AAC/I,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,EAAE,UAAS,MAAW,EAAE,gBAAgB,MAAM,SAAwD,CAAC;AAC/I,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,EAAE,UAAS,MAAW,EAAE,gBAAgB,MAAM,SAAyD,CAAC;AACjJ,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,EAAE,UAAS,MAAW,EAAE,gBAAgB,MAAM,SAA4D,CAAC"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { LogLevel } from '@allma/core-types';
|
|
2
|
+
const LOG_LEVELS = Object.values(LogLevel);
|
|
3
|
+
// Default to 'INFO' if LOG_LEVEL is not set or is invalid
|
|
4
|
+
let currentLogLevel = process.env.LOG_LEVEL?.toUpperCase() || LogLevel.INFO;
|
|
5
|
+
if (!LOG_LEVELS.includes(currentLogLevel)) {
|
|
6
|
+
console.log(`Invalid LOG_LEVEL: "${process.env.LOG_LEVEL}". Defaulting to INFO.`);
|
|
7
|
+
currentLogLevel = LogLevel.INFO;
|
|
8
|
+
}
|
|
9
|
+
const LOG_LEVEL_NUMERIC = LOG_LEVELS.indexOf(currentLogLevel);
|
|
10
|
+
const log = (level, message, details = {}, correlationId) => {
|
|
11
|
+
const messageLevelNumeric = LOG_LEVELS.indexOf(level);
|
|
12
|
+
// Only log if the message's level is at or above the current log level
|
|
13
|
+
if (messageLevelNumeric >= LOG_LEVEL_NUMERIC) {
|
|
14
|
+
const timestamp = new Date().toISOString();
|
|
15
|
+
const logEntry = {
|
|
16
|
+
level: level,
|
|
17
|
+
message,
|
|
18
|
+
correlationId: correlationId || 'N/A',
|
|
19
|
+
...details,
|
|
20
|
+
timestamp,
|
|
21
|
+
};
|
|
22
|
+
// Using console.log for all levels; CloudWatch will handle them as log events.
|
|
23
|
+
console.log(JSON.stringify(logEntry));
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export const log_debug = (message, details = {}, correlationId) => log(LogLevel.DEBUG, message, details, correlationId);
|
|
27
|
+
export const log_info = (message, details = {}, correlationId) => log(LogLevel.INFO, message, details, correlationId);
|
|
28
|
+
export const log_warn = (message, details = {}, correlationId) => log(LogLevel.WARN, message, details, correlationId);
|
|
29
|
+
export const log_error = (message, details = {}, correlationId) => log(LogLevel.ERROR, message, details, correlationId);
|
|
30
|
+
export const log_critical = (message, details = {}, correlationId) => log(LogLevel.CRITICAL, message, details, correlationId);
|
|
31
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAE3C,0DAA0D;AAC1D,IAAI,eAAe,GAAc,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,EAAe,IAAI,QAAQ,CAAC,IAAI,CAAC;AACpG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,uBAAuB,OAAO,CAAC,GAAG,CAAC,SAAS,wBAAwB,CAAC,CAAC;IAClF,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC;AACpC,CAAC;AACD,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;AAG9D,MAAM,GAAG,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,UAAkB,EAAE,EAAE,aAAsB,EAAE,EAAE;IAC7F,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAEtD,uEAAuE;IACvE,IAAI,mBAAmB,IAAI,iBAAiB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG;YACf,KAAK,EAAE,KAAK;YACZ,OAAO;YACP,aAAa,EAAE,aAAa,IAAI,KAAK;YACrC,GAAG,OAAO;YACV,SAAS;SACV,CAAC;QACF,+EAA+E;QAC/E,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxC,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAe,EAAE,UAAkB,EAAE,EAAE,aAAsB,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;AACjJ,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAE,UAAkB,EAAE,EAAE,aAAsB,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;AAC/I,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAE,UAAkB,EAAE,EAAE,aAAsB,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;AAC/I,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAe,EAAE,UAAkB,EAAE,EAAE,aAAsB,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;AACjJ,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAe,EAAE,UAAkB,EAAE,EAAE,aAAsB,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A utility to check if an item is a non-array, non-null object.
|
|
3
|
+
*/
|
|
4
|
+
export declare function isObject(item: any): item is Record<string, any>;
|
|
5
|
+
/**
|
|
6
|
+
* Deeply merges two objects. The source object's properties overwrite the target's.
|
|
7
|
+
* Properties with `undefined` values in the source are ignored to prevent accidental overwrites.
|
|
8
|
+
* @param target The target object.
|
|
9
|
+
* @param source The source object.
|
|
10
|
+
* @returns A new object with the merged properties.
|
|
11
|
+
*/
|
|
12
|
+
export declare function deepMerge<T extends Record<string, any>>(target: T, source: Record<string, any>): T;
|
|
13
|
+
//# sourceMappingURL=objectUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objectUtils.d.ts","sourceRoot":"","sources":["../src/objectUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAE/D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAyBlG"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A utility to check if an item is a non-array, non-null object.
|
|
3
|
+
*/
|
|
4
|
+
export function isObject(item) {
|
|
5
|
+
return (item && typeof item === 'object' && !Array.isArray(item));
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Deeply merges two objects. The source object's properties overwrite the target's.
|
|
9
|
+
* Properties with `undefined` values in the source are ignored to prevent accidental overwrites.
|
|
10
|
+
* @param target The target object.
|
|
11
|
+
* @param source The source object.
|
|
12
|
+
* @returns A new object with the merged properties.
|
|
13
|
+
*/
|
|
14
|
+
export function deepMerge(target, source) {
|
|
15
|
+
const output = { ...target };
|
|
16
|
+
if (isObject(target) && isObject(source)) {
|
|
17
|
+
for (const key of Object.keys(source)) {
|
|
18
|
+
const sourceValue = source[key];
|
|
19
|
+
// Do not merge if the source value is undefined. This prevents
|
|
20
|
+
// optional properties in override objects (like from branch definitions)
|
|
21
|
+
// from overwriting existing defined values with 'undefined'.
|
|
22
|
+
if (sourceValue === undefined) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const targetValue = target[key];
|
|
26
|
+
if (isObject(targetValue) && isObject(sourceValue)) {
|
|
27
|
+
// If both values are objects, recurse
|
|
28
|
+
output[key] = deepMerge(targetValue, sourceValue);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Otherwise, the source value (which is not undefined) overwrites the target value
|
|
32
|
+
output[key] = sourceValue;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return output;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=objectUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objectUtils.js","sourceRoot":"","sources":["../src/objectUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAS;IAC9B,OAAO,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAgC,MAAS,EAAE,MAA2B;IAC3F,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;IAC7B,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACvC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YAEhC,+DAA+D;YAC/D,yEAAyE;YACzE,6DAA6D;YAC7D,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC5B,SAAS;YACb,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YAEhC,IAAI,QAAQ,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBACjD,sCAAsC;gBACrC,MAAc,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YAC/D,CAAC;iBAAM,CAAC;gBACJ,mFAAmF;gBAClF,MAAc,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;YACvC,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { S3OutputPointerWrapper, S3Pointer } from '@allma/core-types';
|
|
2
|
+
/**
|
|
3
|
+
* Fetches the actual data from an S3 pointer.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveS3Pointer(s3Pointer: S3Pointer, correlationId?: string): Promise<Record<string, any>>;
|
|
6
|
+
/**
|
|
7
|
+
* Checks the size of a payload and offloads it to S3 if it exceeds the defined threshold.
|
|
8
|
+
* This is a generic utility for any Lambda to use for its return payload.
|
|
9
|
+
*
|
|
10
|
+
* @param payload The object to potentially offload.
|
|
11
|
+
* @param bucketName The S3 bucket to upload to.
|
|
12
|
+
* @param keyPrefix A prefix for the S3 key (e.g., 'step_outputs/flow-id/step-id').
|
|
13
|
+
* @param correlationId For logging.
|
|
14
|
+
* @param thresholdBytes The size threshold to trigger offloading. Defaults to the configured environment variable.
|
|
15
|
+
* @returns The original payload if it's small, or an S3OutputPointerWrapper if it was offloaded.
|
|
16
|
+
*/
|
|
17
|
+
export declare function offloadIfLarge(payload: Record<string, any> | undefined, bucketName: string, keyPrefix: string, correlationId: string, thresholdBytes?: number): Promise<Record<string, any> | S3OutputPointerWrapper | undefined>;
|
|
18
|
+
//# sourceMappingURL=s3Utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3Utils.d.ts","sourceRoot":"","sources":["../src/s3Utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAY3E;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAiBjH;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,EACxC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,cAAc,GAAE,MAAwC,GACzD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,sBAAsB,GAAG,SAAS,CAAC,CAgCnE"}
|
package/dist/s3Utils.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
|
|
2
|
+
import { ENV_VAR_NAMES } from '@allma/core-types';
|
|
3
|
+
import { log_info, log_error, log_warn, log_debug } from "./logger.js";
|
|
4
|
+
const s3Client = new S3Client({});
|
|
5
|
+
const MAX_CONTEXT_DATA_SIZE_BYTES_DEFAULT = 10 * 1024; // 10KB default
|
|
6
|
+
const PAYLOAD_OFFLOAD_THRESHOLD_BYTES = process.env[ENV_VAR_NAMES.MAX_CONTEXT_DATA_SIZE_BYTES]
|
|
7
|
+
? parseInt(process.env[ENV_VAR_NAMES.MAX_CONTEXT_DATA_SIZE_BYTES] || '', 10)
|
|
8
|
+
: MAX_CONTEXT_DATA_SIZE_BYTES_DEFAULT;
|
|
9
|
+
/**
|
|
10
|
+
* Fetches the actual data from an S3 pointer.
|
|
11
|
+
*/
|
|
12
|
+
export async function resolveS3Pointer(s3Pointer, correlationId) {
|
|
13
|
+
log_info('Resolving S3 data pointer', { s3Pointer }, correlationId);
|
|
14
|
+
try {
|
|
15
|
+
const command = new GetObjectCommand({
|
|
16
|
+
Bucket: s3Pointer.bucket,
|
|
17
|
+
Key: s3Pointer.key,
|
|
18
|
+
});
|
|
19
|
+
const { Body } = await s3Client.send(command);
|
|
20
|
+
if (Body) {
|
|
21
|
+
const content = await Body.transformToString();
|
|
22
|
+
return JSON.parse(content);
|
|
23
|
+
}
|
|
24
|
+
throw new Error('S3 object body for data pointer is empty.');
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
log_error('Failed to fetch or parse data from S3 pointer', { s3Pointer, error: e.message }, correlationId);
|
|
28
|
+
throw new Error(`Failed to resolve S3 data pointer: ${e.message}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Checks the size of a payload and offloads it to S3 if it exceeds the defined threshold.
|
|
33
|
+
* This is a generic utility for any Lambda to use for its return payload.
|
|
34
|
+
*
|
|
35
|
+
* @param payload The object to potentially offload.
|
|
36
|
+
* @param bucketName The S3 bucket to upload to.
|
|
37
|
+
* @param keyPrefix A prefix for the S3 key (e.g., 'step_outputs/flow-id/step-id').
|
|
38
|
+
* @param correlationId For logging.
|
|
39
|
+
* @param thresholdBytes The size threshold to trigger offloading. Defaults to the configured environment variable.
|
|
40
|
+
* @returns The original payload if it's small, or an S3OutputPointerWrapper if it was offloaded.
|
|
41
|
+
*/
|
|
42
|
+
export async function offloadIfLarge(payload, bucketName, keyPrefix, correlationId, thresholdBytes = PAYLOAD_OFFLOAD_THRESHOLD_BYTES) {
|
|
43
|
+
if (!payload)
|
|
44
|
+
return undefined;
|
|
45
|
+
try {
|
|
46
|
+
const payloadString = JSON.stringify(payload);
|
|
47
|
+
const payloadSize = Buffer.byteLength(payloadString, 'utf-8');
|
|
48
|
+
log_debug(`offloadIfLarge for ${bucketName} ${keyPrefix}...`, { thresholdBytes }, correlationId);
|
|
49
|
+
if (payloadSize > thresholdBytes) {
|
|
50
|
+
const s3Key = `${keyPrefix}_${new Date().toISOString()}.json`;
|
|
51
|
+
log_warn(`Payload is large (${payloadSize} bytes). Offloading to S3.`, { s3Key, thresholdBytes }, correlationId);
|
|
52
|
+
await s3Client.send(new PutObjectCommand({
|
|
53
|
+
Bucket: bucketName,
|
|
54
|
+
Key: s3Key,
|
|
55
|
+
Body: payloadString,
|
|
56
|
+
ContentType: 'application/json',
|
|
57
|
+
}));
|
|
58
|
+
const s3Pointer = { bucket: bucketName, key: s3Key };
|
|
59
|
+
return { _s3_output_pointer: s3Pointer }; // Return the wrapper
|
|
60
|
+
}
|
|
61
|
+
// Payload is small enough, return as is
|
|
62
|
+
return payload;
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
log_error(`Failed to offload payload to S3 for key prefix '${keyPrefix}'`, { error: e.message }, correlationId);
|
|
66
|
+
throw new Error(`Failed during S3 offload attempt: ${e.message}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=s3Utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3Utils.js","sourceRoot":"","sources":["../src/s3Utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PromptTemplateSchema, PromptTemplateVersionStorageItem } from '@allma/core-types';
|
|
3
|
+
/**
|
|
4
|
+
* Converts a DynamoDB storage item to its corresponding API type by stripping storage-specific keys.
|
|
5
|
+
* This is a generic helper for the application's data access layer.
|
|
6
|
+
* @param item The storage item from DynamoDB.
|
|
7
|
+
* @returns The item formatted as its API type.
|
|
8
|
+
*/
|
|
9
|
+
export declare function fromStorageItem<T extends {
|
|
10
|
+
PK: string;
|
|
11
|
+
SK: string;
|
|
12
|
+
itemType: string;
|
|
13
|
+
}, U>(item: T): U;
|
|
14
|
+
/**
|
|
15
|
+
* Converts a PromptTemplate API object to its DynamoDB storage representation.
|
|
16
|
+
* This helper is specific to the application's data access layer for prompt templates.
|
|
17
|
+
* @param apiItem The PromptTemplate object.
|
|
18
|
+
* @returns The validated storage item for a prompt version.
|
|
19
|
+
* @throws An error if the constructed item fails validation.
|
|
20
|
+
*/
|
|
21
|
+
export declare function toPromptTemplateVersionStorageItem(apiItem: z.infer<typeof PromptTemplateSchema>): PromptTemplateVersionStorageItem;
|
|
22
|
+
//# sourceMappingURL=storageUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageUtils.d.ts","sourceRoot":"","sources":["../src/storageUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACH,oBAAoB,EAEpB,gCAAgC,EAEnC,MAAM,mBAAmB,CAAC;AAE3B;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAGrG;AAED;;;;;;GAMG;AACH,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,GAAG,gCAAgC,CAYlI"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PromptTemplateVersionStorageItemSchema, ITEM_TYPE_ALLMA_PROMPT_TEMPLATE, } from '@allma/core-types';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a DynamoDB storage item to its corresponding API type by stripping storage-specific keys.
|
|
4
|
+
* This is a generic helper for the application's data access layer.
|
|
5
|
+
* @param item The storage item from DynamoDB.
|
|
6
|
+
* @returns The item formatted as its API type.
|
|
7
|
+
*/
|
|
8
|
+
export function fromStorageItem(item) {
|
|
9
|
+
const { PK: _, SK: __, itemType: ___, ...apiItem } = item;
|
|
10
|
+
return apiItem;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Converts a PromptTemplate API object to its DynamoDB storage representation.
|
|
14
|
+
* This helper is specific to the application's data access layer for prompt templates.
|
|
15
|
+
* @param apiItem The PromptTemplate object.
|
|
16
|
+
* @returns The validated storage item for a prompt version.
|
|
17
|
+
* @throws An error if the constructed item fails validation.
|
|
18
|
+
*/
|
|
19
|
+
export function toPromptTemplateVersionStorageItem(apiItem) {
|
|
20
|
+
const storageItem = {
|
|
21
|
+
...apiItem,
|
|
22
|
+
PK: `PROMPT_TEMPLATE#${apiItem.id}`,
|
|
23
|
+
SK: `VERSION#${apiItem.version}`,
|
|
24
|
+
itemType: ITEM_TYPE_ALLMA_PROMPT_TEMPLATE,
|
|
25
|
+
};
|
|
26
|
+
const validationResult = PromptTemplateVersionStorageItemSchema.safeParse(storageItem);
|
|
27
|
+
if (!validationResult.success) {
|
|
28
|
+
throw new Error(`Internal data integrity error: Constructed PromptTemplate version storage item is invalid: ${validationResult.error.message}`);
|
|
29
|
+
}
|
|
30
|
+
return validationResult.data;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=storageUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageUtils.js","sourceRoot":"","sources":["../src/storageUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,sCAAsC,EAEtC,+BAA+B,GAClC,MAAM,mBAAmB,CAAC;AAE3B;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAA4D,IAAO;IAC9F,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC;IAC1D,OAAO,OAAY,CAAC;AACxB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kCAAkC,CAAC,OAA6C;IAC5F,MAAM,WAAW,GAAqC;QAClD,GAAG,OAAO;QACV,EAAE,EAAE,mBAAmB,OAAO,CAAC,EAAE,EAAE;QACnC,EAAE,EAAE,WAAW,OAAO,CAAC,OAAO,EAAE;QAChC,QAAQ,EAAE,+BAA+B;KAC5C,CAAC;IACF,MAAM,gBAAgB,GAAG,sCAAsC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACvF,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,8FAA8F,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACpJ,CAAC;IACD,OAAO,gBAAgB,CAAC,IAAI,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenEstimator.d.ts","sourceRoot":"","sources":["../src/tokenEstimator.ts"],"names":[],"mappings":"AA6BA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAc9F"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { log_error, log_warn, log_info } from './logger.js';
|
|
2
|
+
import { Tiktoken } from "js-tiktoken";
|
|
3
|
+
import o200k_base from "js-tiktoken/ranks/o200k_base";
|
|
4
|
+
// Cache the Tiktoken instance globally within this module
|
|
5
|
+
let tokenizerInstance = null;
|
|
6
|
+
let tokenizerInitialized = false;
|
|
7
|
+
function getTokenizer(correlationId) {
|
|
8
|
+
if (!tokenizerInstance) {
|
|
9
|
+
if (tokenizerInitialized) { // It failed before, don't retry endlessly in one invocation
|
|
10
|
+
log_error('Tokenizer previously failed to initialize. Using fallback.', {}, correlationId);
|
|
11
|
+
throw new Error('Tokenizer previously failed to initialize.'); // Forces fallback in estimateTokens
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
tokenizerInstance = new Tiktoken(o200k_base);
|
|
15
|
+
tokenizerInitialized = true; // Mark as initialized (or attempted)
|
|
16
|
+
log_info('Tiktoken (o200k_base) tokenizer initialized successfully.', {}, correlationId || 'TOKENIZER_INIT');
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
tokenizerInitialized = true; // Mark as attempted to prevent re-init loops
|
|
20
|
+
log_error('Failed to initialize tiktoken tokenizer (o200k_base). Subsequent calls will use fallback.', {
|
|
21
|
+
error: e.message,
|
|
22
|
+
}, correlationId || 'TOKENIZER_INIT_ERROR');
|
|
23
|
+
throw new Error(`Critical: Failed to initialize tokenizer: ${e.message}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return tokenizerInstance;
|
|
27
|
+
}
|
|
28
|
+
export function estimateTokens(text, correlationId) {
|
|
29
|
+
if (!text)
|
|
30
|
+
return 0;
|
|
31
|
+
try {
|
|
32
|
+
const enc = getTokenizer(correlationId);
|
|
33
|
+
return enc.encode(text).length;
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
log_warn('Failed to estimate tokens using tiktoken. Falling back to character-based estimation. This is highly inaccurate.', {
|
|
37
|
+
error: e.message,
|
|
38
|
+
textSnippet: text.substring(0, 100),
|
|
39
|
+
correlationId
|
|
40
|
+
});
|
|
41
|
+
// Fallback: very rough estimate (char count / 4)
|
|
42
|
+
return Math.ceil(text.length / 4);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=tokenEstimator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenEstimator.js","sourceRoot":"","sources":["../src/tokenEstimator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,UAAU,MAAM,8BAA8B,CAAC;AAEtD,0DAA0D;AAC1D,IAAI,iBAAiB,GAAoB,IAAI,CAAC;AAC9C,IAAI,oBAAoB,GAAG,KAAK,CAAC;AAEjC,SAAS,YAAY,CAAC,aAAsB;IACxC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACrB,IAAI,oBAAoB,EAAE,CAAC,CAAC,4DAA4D;YACpF,SAAS,CAAC,4DAA4D,EAAE,EAAE,EAAE,aAAa,CAAC,CAAC;YAC3F,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC,CAAC,oCAAoC;QACvG,CAAC;QACD,IAAI,CAAC;YACD,iBAAiB,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC7C,oBAAoB,GAAG,IAAI,CAAC,CAAC,qCAAqC;YAClE,QAAQ,CAAC,2DAA2D,EAAE,EAAE,EAAE,aAAa,IAAI,gBAAgB,CAAC,CAAC;QACjH,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YACd,oBAAoB,GAAG,IAAI,CAAC,CAAC,6CAA6C;YAC1E,SAAS,CAAC,2FAA2F,EAAE;gBACnG,KAAK,EAAE,CAAC,CAAC,OAAO;aACnB,EAAE,aAAa,IAAI,sBAAsB,CAAC,CAAC;YAC5C,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9E,CAAC;IACL,CAAC;IACD,OAAO,iBAAiB,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAA+B,EAAE,aAAsB;IAClF,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;QACxC,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IACnC,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QACd,QAAQ,CAAC,kHAAkH,EAAE;YACzH,KAAK,EAAE,CAAC,CAAC,OAAO;YAChB,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;YACnC,aAAa;SAChB,CAAC,CAAC;QACH,iDAAiD;QACjD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACtC,CAAC;AACL,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@allma/core-sdk",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Core SDK with shared utilities (logging, auth, S3 utils) for building on the Allma serverless AI orchestration platform.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"lint": "eslint ."
|
|
14
|
+
},
|
|
15
|
+
"author": "@webjema",
|
|
16
|
+
"license": "Apache-2.0",
|
|
17
|
+
"homepage": "https://docs.allma.dev",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/ALLMA-dev/allma-core.git",
|
|
21
|
+
"directory": "packages/core-sdk"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"allma",
|
|
25
|
+
"sdk",
|
|
26
|
+
"aws",
|
|
27
|
+
"lambda",
|
|
28
|
+
"utilities",
|
|
29
|
+
"serverless",
|
|
30
|
+
"ai",
|
|
31
|
+
"orchestration"
|
|
32
|
+
],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@allma/core-types": "^1.0.1",
|
|
38
|
+
"@aws-sdk/client-s3": "^3.500.0",
|
|
39
|
+
"@aws-sdk/client-secrets-manager": "^3.500.0",
|
|
40
|
+
"@aws-sdk/client-sns": "^3.500.0",
|
|
41
|
+
"@aws-sdk/client-sqs": "^3.500.0",
|
|
42
|
+
"@aws-sdk/client-ssm": "^3.500.0",
|
|
43
|
+
"@aws-sdk/client-sfn": "^3.500.0",
|
|
44
|
+
"@aws-sdk/lib-dynamodb": "^3.500.0",
|
|
45
|
+
"zod": "^3.22.4"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"eslint": "^9.8.0",
|
|
50
|
+
"typescript-eslint": "^8.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|