UNPKG

try-catch-cloud

Version:

Advanced error logging for your app with low code integration

1 lines 15.5 kB
{"version":3,"sources":["../../src/helpers.ts","../../src/core/types.ts","../../src/core/baseUtility.ts","../../src/lambda/utility.ts","../../src/lambda/index.ts"],"sourcesContent":["import type { AxiosError } from 'axios';\n\nexport function isAxiosError(e: unknown): e is AxiosError {\n return (\n e !== null &&\n typeof e === \"object\" &&\n (e as Pick<AxiosError, \"isAxiosError\">).isAxiosError === true\n );\n}\n","export type SendErrorResponse =\n\t| {\n\t\t\tid: string;\n\t }\n\t| undefined;\n\nexport interface ErrorUtilityMethods<TRequest> {\n\tsendError(\n\t\terror: Error | unknown,\n\t\tuserContext?: Record<string, unknown>,\n\t): Promise<SendErrorResponse>;\n\tsendErrorFromEndPoint(\n\t\terror: Error | unknown,\n\t\trequest: TRequest,\n\t\tuserContext?: Record<string, unknown>,\n\t): Promise<SendErrorResponse>;\n}\n\nexport class WrappedError extends Error {\n\treadonly value: unknown;\n\tconstructor(message: string, value: unknown) {\n\t\tsuper(message);\n\n\t\tthis.value = value;\n\t}\n}\n\nexport interface HttpRequest {\n\turl?: string;\n\tmethod?: string;\n\tpath?: string;\n\tbody?: unknown;\n\tquery?: Record<string, unknown>;\n\theaders?: Record<string, unknown>;\n}\n\nexport interface MappedHttpRequest {\n\tmethod: string | null;\n\turl: string | null;\n\trequestBody: unknown | null;\n\tquery: Record<string, unknown> | null;\n\theaders: Record<string, unknown> | null;\n}\n\nexport type ErrorDetails<\n\tT extends Record<string, unknown> = Record<string, unknown>,\n> = {\n\tname: string;\n\tmessage: string;\n} & T;\n\nexport type ErrorRequestContext = Record<string, unknown> | null;\n\nexport interface ErrorRequest<\n\tTDetails extends Record<string, unknown> = Record<string, unknown>,\n> {\n\terrorName: string;\n\terrorDetails: ErrorDetails<TDetails>;\n\tstack: string | null;\n\tcontext: ErrorRequestContext;\n\tuser: string;\n\tprojectName: string;\n}\n\nexport type AxiosErrorRequest = ErrorRequest<{\n\turl: string | null;\n\tquery: Record<string, string | string[] | undefined> | null;\n\tparams: unknown | null;\n\tmethod: string | null;\n\tstatusCode: number;\n\trequestBody: unknown | null;\n\tresponseBody: unknown | null;\n}>;\n","import { isAxiosError } from \"../helpers\";\nimport type {\n\tMappedHttpRequest,\n\tErrorRequest,\n\tAxiosErrorRequest,\n\tErrorUtilityMethods,\n\tErrorDetails,\n\tErrorRequestContext,\n} from \"./types\";\nimport { WrappedError } from \"./types\";\nimport type { AxiosError } from \"axios\";\n\nexport abstract class BaseErrorUtility<TRequest>\n\timplements ErrorUtilityMethods<TRequest>\n{\n\tconstructor(\n\t\tprotected readonly projectName: string,\n\t\tprotected readonly apiKey: string,\n\t\tprotected readonly debug?: boolean,\n\t) {\n\t\tif (!projectName.length) {\n\t\t\tthrow new Error(\"Invalid project name\");\n\t\t}\n\n\t\tif (!apiKey.length) {\n\t\t\tthrow new Error(\"Invalid API key\");\n\t\t}\n\t}\n\n\tprotected abstract mapRequest(\n\t\trequest: TRequest,\n\t): Promise<MappedHttpRequest> | MappedHttpRequest;\n\n\tasync sendError(\n\t\terror: Error | unknown,\n\t\tuserContext?: Record<string, unknown>,\n\t) {\n\t\ttry {\n\t\t\tconst payload = await this.mapErrorRequestPayload(\n\t\t\t\terror,\n\t\t\t\tundefined,\n\t\t\t\tuserContext,\n\t\t\t);\n\n\t\t\treturn this.send(payload);\n\t\t} catch (err) {\n\t\t\tif (this.debug) {\n\t\t\t\tconsole.log(err);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Sends error and request details to try-catch-cloud service.\n\t * You can manually map details from your route to request object.\n\t *\n\t * ```\n\t * errorTrack.sendErrorFromEndpoint(\n\t * error,\n\t * {\n\t * url: req.originalUrl.split(\"?\")[0],\n\t * method: req.method,\n\t * headers: req.headers,\n\t * query: req.query,\n\t * body,\n\t * },\n\t * { ...context }\n\t * );\n\t * ```\n\t */\n\tasync sendErrorFromEndPoint(\n\t\terror: Error | unknown,\n\t\trequest: TRequest,\n\t\tuserContext?: Record<string, unknown>,\n\t) {\n\t\ttry {\n\t\t\tconst payload = await this.mapErrorRequestPayload(\n\t\t\t\terror,\n\t\t\t\trequest,\n\t\t\t\tuserContext,\n\t\t\t);\n\n\t\t\treturn this.send(payload);\n\t\t} catch (err) {\n\t\t\tif (this.debug) {\n\t\t\t\tconsole.log(err);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected async send(payload: ErrorRequest): Promise<{ id: string }> {\n\t\tconst result = await fetch(\"https://oleksiikilevoi.site/api/err-log/new\", {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: JSON.stringify(payload),\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t});\n\n\t\tif (this.debug) {\n\t\t\tconsole.info(\"Send result\", await result.text());\n\t\t}\n\n const data: { id: string } = await result.json();\n\n return data;\n\t}\n\n\t/**\n\t * It has to return Promise to be compatible with different implementations\n\t * e.g. Hono returns Promise when accessing req.json()\n\t */\n\tprotected async mapErrorRequestPayload(\n\t\terror: Error | unknown,\n\t\trequest?: TRequest,\n\t\tcontext?: Record<string, unknown>,\n\t): Promise<ErrorRequest> {\n\t\tif (!(error instanceof Error)) {\n\t\t\treturn this.mapErrorRequestPayload(\n\t\t\t\tnew WrappedError(\"An unexpected value was thrown\", error),\n\t\t\t\trequest,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t}\n\n\t\tif (isAxiosError(error)) {\n\t\t\treturn this.mapAxiosError(error, request, context);\n\t\t}\n\n\t\tconst { stack, ...restErrorDetails } = error;\n\n\t\treturn {\n\t\t\t...(request ? await this.mapRequest(request) : {}),\n\t\t\terrorName: error.name,\n\t\t\terrorDetails: this.sanitizeValue({\n\t\t\t\t...restErrorDetails,\n\t\t\t\tname: error.name,\n\t\t\t\tmessage: error.message,\n\t\t\t}) as ErrorDetails,\n\t\t\tstack: error.stack ?? null,\n\t\t\tcontext: this.sanitizeValue(context ?? null) as ErrorRequestContext,\n\t\t\tuser: this.apiKey,\n\t\t\tprojectName: this.projectName,\n\t\t};\n\t}\n\n\tprotected mapAxiosRequestBody(error: AxiosError) {\n\t\ttry {\n\t\t\tconst config = error.config;\n\t\t\tconst headers: Record<string, string> = config?.headers ?? {};\n\t\t\tconst contentType: string =\n\t\t\t\theaders[\"content-type\"] ?? headers[\"Content-Type\"] ?? \"\";\n\n\t\t\tif (!contentType.length) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (contentType.startsWith(\"multipart/form-data\")) {\n\t\t\t\treturn \"#form-data\";\n\t\t\t}\n\n\t\t\tif (contentType === \"application/json\") {\n\t\t\t\treturn JSON.parse(config?.data);\n\t\t\t}\n\n\t\t\tif (contentType.match(/^text\\/.+$/)) {\n\t\t\t\treturn config?.data as string;\n\t\t\t}\n\n\t\t\tif (contentType === \"application/x-www-form-urlencoded\") {\n\t\t\t\treturn new URLSearchParams(config?.data).toString();\n\t\t\t}\n\n\t\t\tif (contentType === \"application/xml\") {\n\t\t\t\treturn decodeURIComponent(config?.data); // Assuming data is already a string\n\t\t\t}\n\n\t\t\tif (contentType === \"application/octet-stream\") {\n\t\t\t\treturn \"#binary-data\";\n\t\t\t}\n\n\t\t\treturn \"#tcc/mapping-failed\";\n\t\t} catch (e) {\n\t\t\tif (this.debug) {\n\t\t\t\tconsole.error(e);\n\t\t\t}\n\n\t\t\treturn \"#tcc/mapping-failed\";\n\t\t}\n\t}\n\n\tprotected async mapAxiosError(\n\t\terror: AxiosError,\n\t\trequest?: TRequest,\n\t\tcontext?: ErrorRequestContext,\n\t): Promise<AxiosErrorRequest> {\n\t\tconst { config, response } = error;\n\t\tconst requestBody = this.mapAxiosRequestBody(error);\n\t\tconst responseBody = response?.data;\n\n\t\tconst rawUrl = config?.url ?? \"\";\n\t\tconst query = Object.fromEntries([\n\t\t\t...(URL.canParse(rawUrl) ? new URL(rawUrl).searchParams.entries() : []),\n\t\t]);\n\n\t\tconst axiosDetails = {\n\t\t\turl: config?.url ?? null,\n\t\t\tquery: Object.keys(query).length ? query : null,\n\t\t\tparams: config?.params ?? null,\n\t\t\tmethod: config?.method?.toUpperCase() || null,\n\t\t\tstatusCode: response?.status ?? 0,\n\t\t\trequestBody,\n\t\t\tresponseBody: responseBody ?? null,\n\t\t\theaders: config?.headers ?? {},\n\t\t};\n\n\t\treturn {\n\t\t\t...(request ? await this.mapRequest(request) : {}),\n\t\t\terrorName: error.constructor.name ?? error.name,\n\t\t\terrorDetails: this.sanitizeValue({\n\t\t\t\t...axiosDetails,\n\t\t\t\tname: error.name,\n\t\t\t\tmessage: error.message,\n\t\t\t}) as ErrorDetails<typeof axiosDetails>,\n\t\t\tstack: error.stack ?? null,\n\t\t\tcontext: context ?? null,\n\t\t\tuser: this.apiKey,\n\t\t\tprojectName: this.projectName,\n\t\t};\n\t}\n\n\tprotected sanitizeValue(value: unknown, level = 1): unknown {\n\t\tif (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n\t\t\tconst result: Record<string, unknown> = {};\n\n\t\t\tfor (const [key, objValue] of Object.entries(value)) {\n\t\t\t\tresult[key] = this.sanitizeValue(objValue, level + 1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tif (typeof value === \"object\" && Array.isArray(value)) {\n\t\t\treturn value.map((item) => this.sanitizeValue(item, level + 1));\n\t\t}\n\n\t\tif (typeof value === \"function\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn value;\n\t}\n}\n","import { BaseErrorUtility } from \"../core/baseUtility\";\nimport type { MappedHttpRequest, SendErrorResponse } from \"../core/types\";\nimport type { APIGatewayEventCommon } from \"./types\";\n\nexport class LambdaErrorUtility extends BaseErrorUtility<APIGatewayEventCommon> {\n\tprotected mapRequest(\n\t\trequest: APIGatewayEventCommon,\n\t): MappedHttpRequest | Promise<MappedHttpRequest> {\n\t\tconst method = request.httpMethod ?? null;\n\t\tconst url = request.path ?? null;\n\t\tconst query = Object.keys(request.queryStringParameters ?? {}).length\n\t\t\t? request.queryStringParameters!\n\t\t\t: null;\n\n\t\treturn {\n\t\t\tmethod,\n\t\t\turl,\n\t\t\trequestBody: request.body,\n\t\t\tquery,\n\t\t\theaders: request.headers ?? null,\n\t\t};\n\t}\n\n\tsendErrorFromEndPoint = async (\n\t\terror: unknown,\n\t\trequest: APIGatewayEventCommon,\n\t\tuserContext?: Record<string, unknown> | undefined,\n\t): Promise<SendErrorResponse> => {\n\t\treturn super.sendErrorFromEndPoint(error, request, userContext);\n\t};\n}\n","import type { MiddlewareObj } from \"@middy/core\";\nimport type {\n\tAPIGatewayEventCommon,\n\tErrorContext,\n\tMiddlewareOptions,\n} from \"./types\";\nimport { LambdaErrorUtility } from \"./utility\";\n\nexport { LambdaErrorUtility } from \"./utility\";\n\nlet errorContext: ErrorContext = {};\n\nexport function tryCatch({\n\tprojectName,\n\tapiKey,\n}: MiddlewareOptions): MiddlewareObj<APIGatewayEventCommon> {\n\tconst tryCatchInstance = new LambdaErrorUtility(projectName, apiKey);\n\n\treturn {\n\t\tbefore: () => {\n\t\t\terrorContext = {};\n\t\t},\n\t\tonError: async (req) => {\n\t\t\tawait tryCatchInstance.sendErrorFromEndPoint(\n\t\t\t\treq.error,\n\t\t\t\treq.event,\n\t\t\t\terrorContext,\n\t\t\t);\n\t\t},\n\t};\n}\n\nexport function addContext(ctx: ErrorContext) {\n\terrorContext = {\n\t\t...errorContext,\n\t\t...ctx,\n\t};\n}\n"],"mappings":"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","names":[]}