UNPKG

try-catch-cloud

Version:

Advanced error logging for your app with low code integration

291 lines (285 loc) 8.2 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // 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/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/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 errorContext = tccReq.tryCatchContext ?? {}; context.instance.sendErrorFromEndPoint(error, req, errorContext).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"); export { ExpressErrorUtility, addContext, setupTryCatch }; //# sourceMappingURL=index.mjs.map