UNPKG

try-catch-cloud

Version:

Advanced error logging for your app with low code integration

475 lines (460 loc) 12.8 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: 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/core/utility.ts var ErrorUtility = class extends BaseErrorUtility { static { __name(this, "ErrorUtility"); } mapRequest(req) { return { method: req.method ?? null, url: req.url ?? null, requestBody: req.body ?? null, query: req.query ?? null, headers: req.headers ?? null }; } async sendErrorFromEndPoint(error, request, userContext) { return super.sendErrorFromEndPoint(error, request, userContext); } }; // src/express/index.ts var express_exports = {}; __export(express_exports, { ExpressErrorUtility: () => ExpressErrorUtility, addContext: () => addContext, setupTryCatch: () => setupTryCatch }); // src/express/factory.ts var setupTryCatchFactory = /* @__PURE__ */ __name((factoryOptions) => { return (options) => { const context = factoryOptions.createContext(options); return { initialize: factoryOptions.handlers.initialize(context), onError: factoryOptions.handlers.onError(context) }; }; }, "setupTryCatchFactory"); // src/express/utility.ts var ExpressErrorUtility = class extends BaseErrorUtility { static { __name(this, "ExpressErrorUtility"); } mapRequest(req) { const headers = req.headers; const method = req.method.toUpperCase(); const [url] = req.originalUrl.split("?"); return { method, url, requestBody: req.body && Object.keys(req.body).length ? req.body : null, query: Object.keys(req.query).length ? req.query : null, headers: Object.keys(headers).length ? headers : null }; } /** * Sends error and Express request details to try-catch-cloud service. * ``` * app.use((error, req, res, next) => { * errorUtility.sendErrorFromEndPoint(error, req) * .catch((e) => { ... }); * * res.status(500).json({ message: "Internal server error" }); * }); * ``` */ async sendErrorFromEndPoint(error, request, userContext) { return super.sendErrorFromEndPoint(error, request, userContext); } }; // src/express/index.ts var setupTryCatch = setupTryCatchFactory({ createContext: (opts) => ({ instance: new ExpressErrorUtility(opts.projectName, opts.apiKey) }), handlers: { initialize: (context) => (req, res, next) => { req.tryCatchContext = {}; next(); }, onError: (context) => (error, req, res, next) => { const tccReq = req; const errorContext2 = tccReq.tryCatchContext ?? {}; context.instance.sendErrorFromEndPoint(error, req, errorContext2).catch((e) => { console.warn(e); }); return next(error); } } }); var addContext = /* @__PURE__ */ __name((req, details) => { const tccReq = req; if (!tccReq.tryCatchContext) { req.tryCatchContext = {}; } tccReq.tryCatchContext = { ...tccReq.tryCatchContext, ...details }; }, "addContext"); // src/hono/index.ts var hono_exports = {}; __export(hono_exports, { HonoErrorUtility: () => HonoErrorUtility, tryCatch: () => tryCatch }); // src/hono/factory.ts var tryCatchFactory = /* @__PURE__ */ __name((factoryOptions) => { return (options) => { const context = factoryOptions.createContext(options); return factoryOptions.handler(context); }; }, "tryCatchFactory"); // src/hono/utility.ts import { URL as URL2 } from "url"; var HonoErrorUtility = class extends BaseErrorUtility { static { __name(this, "HonoErrorUtility"); } async mapRequest(req) { const parsedUrl = new URL2(req.url); const [url] = parsedUrl.pathname.split("?"); let requestBody; try { requestBody = await req.json(); } catch (e) { requestBody = null; } const query = req.query(); const result = { method: req.method.toUpperCase(), url, requestBody, headers: req.header(), query: Object.keys(query).length ? query : null }; return result; } /** * Sends error and Hono request details to try-catch-cloud service. * ``` * app.onError(async (err, ctx) => { * errorUtility.sendErrorFromEndPoint(err, ctx.req, { ...errorContext }) * .catch((e) => { ... }); * * return ctx.json({ message: 'Internal server error' }, 500); * }); * ``` */ async sendErrorFromEndPoint(error, request, userContext) { return super.sendErrorFromEndPoint(error, request, userContext); } }; // src/hono/index.ts var tryCatch = tryCatchFactory({ createContext: (options) => ({ instance: new HonoErrorUtility( options.projectName, options.apiKey, options.debug ) }), handler: ({ instance }) => { return async (ctx, next) => { let errorContext2 = {}; ctx.set("tryCatch", { addContext: (errCtx) => { errorContext2 = { ...errorContext2, ...errCtx }; } }); await next(); if (ctx.error) { const error = ctx.error; if (error instanceof Error) { const ctrName = error.constructor.name; if (ctrName === "HTTPException") { return; } } instance.sendErrorFromEndPoint(ctx.error, ctx.req, errorContext2).then(() => { }).catch((e) => { }); } }; } }); // src/lambda/index.ts var lambda_exports = {}; __export(lambda_exports, { LambdaErrorUtility: () => LambdaErrorUtility, addContext: () => addContext2, tryCatch: () => tryCatch2 }); // 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 tryCatch2({ projectName, apiKey }) { const tryCatchInstance = new LambdaErrorUtility(projectName, apiKey); return { before: () => { errorContext = {}; }, onError: async (req) => { await tryCatchInstance.sendErrorFromEndPoint( req.error, req.event, errorContext ); } }; } __name(tryCatch2, "tryCatch"); function addContext2(ctx) { errorContext = { ...errorContext, ...ctx }; } __name(addContext2, "addContext"); export { ErrorUtility, express_exports as express, hono_exports as hono, lambda_exports as lambda }; //# sourceMappingURL=index.mjs.map