UNPKG

try-catch-cloud

Version:

Advanced error logging for your app with low code integration

267 lines (262 loc) 7.2 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/helpers.ts function isAxiosError(e) { return e !== null && typeof e === "object" && e.isAxiosError === true; } __name(isAxiosError, "isAxiosError"); // src/core/types.ts var WrappedError = class extends Error { static { __name(this, "WrappedError"); } value; constructor(message, value) { super(message); this.value = value; } }; // src/core/baseUtility.ts var BaseErrorUtility = class { constructor(projectName, apiKey, debug) { this.projectName = projectName; this.apiKey = apiKey; this.debug = debug; if (!projectName.length) { throw new Error("Invalid project name"); } if (!apiKey.length) { throw new Error("Invalid API key"); } } static { __name(this, "BaseErrorUtility"); } async sendError(error, userContext) { try { const payload = await this.mapErrorRequestPayload( error, void 0, userContext ); return this.send(payload); } catch (err) { if (this.debug) { console.log(err); } } } /** * Sends error and request details to try-catch-cloud service. * You can manually map details from your route to request object. * * ``` * errorTrack.sendErrorFromEndpoint( * error, * { * url: req.originalUrl.split("?")[0], * method: req.method, * headers: req.headers, * query: req.query, * body, * }, * { ...context } * ); * ``` */ async sendErrorFromEndPoint(error, request, userContext) { try { const payload = await this.mapErrorRequestPayload( error, request, userContext ); return this.send(payload); } catch (err) { if (this.debug) { console.log(err); } } } async send(payload) { const result = await fetch("https://oleksiikilevoi.site/api/err-log/new", { method: "POST", body: JSON.stringify(payload), headers: { "Content-Type": "application/json" } }); if (this.debug) { console.info("Send result", await result.text()); } const data = await result.json(); return data; } /** * It has to return Promise to be compatible with different implementations * e.g. Hono returns Promise when accessing req.json() */ async mapErrorRequestPayload(error, request, context) { if (!(error instanceof Error)) { return this.mapErrorRequestPayload( new WrappedError("An unexpected value was thrown", error), request, context ); } if (isAxiosError(error)) { return this.mapAxiosError(error, request, context); } const { stack, ...restErrorDetails } = error; return { ...request ? await this.mapRequest(request) : {}, errorName: error.name, errorDetails: this.sanitizeValue({ ...restErrorDetails, name: error.name, message: error.message }), stack: error.stack ?? null, context: this.sanitizeValue(context ?? null), user: this.apiKey, projectName: this.projectName }; } mapAxiosRequestBody(error) { try { const config = error.config; const headers = config?.headers ?? {}; const contentType = headers["content-type"] ?? headers["Content-Type"] ?? ""; if (!contentType.length) { return null; } if (contentType.startsWith("multipart/form-data")) { return "#form-data"; } if (contentType === "application/json") { return JSON.parse(config?.data); } if (contentType.match(/^text\/.+$/)) { return config?.data; } if (contentType === "application/x-www-form-urlencoded") { return new URLSearchParams(config?.data).toString(); } if (contentType === "application/xml") { return decodeURIComponent(config?.data); } if (contentType === "application/octet-stream") { return "#binary-data"; } return "#tcc/mapping-failed"; } catch (e) { if (this.debug) { console.error(e); } return "#tcc/mapping-failed"; } } async mapAxiosError(error, request, context) { const { config, response } = error; const requestBody = this.mapAxiosRequestBody(error); const responseBody = response?.data; const rawUrl = config?.url ?? ""; const query = Object.fromEntries([ ...URL.canParse(rawUrl) ? new URL(rawUrl).searchParams.entries() : [] ]); const axiosDetails = { url: config?.url ?? null, query: Object.keys(query).length ? query : null, params: config?.params ?? null, method: config?.method?.toUpperCase() || null, statusCode: response?.status ?? 0, requestBody, responseBody: responseBody ?? null, headers: config?.headers ?? {} }; return { ...request ? await this.mapRequest(request) : {}, errorName: error.constructor.name ?? error.name, errorDetails: this.sanitizeValue({ ...axiosDetails, name: error.name, message: error.message }), stack: error.stack ?? null, context: context ?? null, user: this.apiKey, projectName: this.projectName }; } sanitizeValue(value, level = 1) { if (typeof value === "object" && value !== null && !Array.isArray(value)) { const result = {}; for (const [key, objValue] of Object.entries(value)) { result[key] = this.sanitizeValue(objValue, level + 1); } return result; } if (typeof value === "object" && Array.isArray(value)) { return value.map((item) => this.sanitizeValue(item, level + 1)); } if (typeof value === "function") { return void 0; } return value; } }; // src/lambda/utility.ts var LambdaErrorUtility = class extends BaseErrorUtility { static { __name(this, "LambdaErrorUtility"); } mapRequest(request) { const method = request.httpMethod ?? null; const url = request.path ?? null; const query = Object.keys(request.queryStringParameters ?? {}).length ? request.queryStringParameters : null; return { method, url, requestBody: request.body, query, headers: request.headers ?? null }; } sendErrorFromEndPoint = async (error, request, userContext) => { return super.sendErrorFromEndPoint(error, request, userContext); }; }; // src/lambda/index.ts var errorContext = {}; function tryCatch({ projectName, apiKey }) { const tryCatchInstance = new LambdaErrorUtility(projectName, apiKey); return { before: () => { errorContext = {}; }, onError: async (req) => { await tryCatchInstance.sendErrorFromEndPoint( req.error, req.event, errorContext ); } }; } __name(tryCatch, "tryCatch"); function addContext(ctx) { errorContext = { ...errorContext, ...ctx }; } __name(addContext, "addContext"); export { LambdaErrorUtility, addContext, tryCatch }; //# sourceMappingURL=index.mjs.map