try-catch-cloud
Version:
Advanced error logging for your app with low code integration
327 lines (320 loc) • 9.28 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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 });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/hono/index.ts
var hono_exports = {};
__export(hono_exports, {
HonoErrorUtility: () => HonoErrorUtility,
tryCatch: () => tryCatch
});
module.exports = __toCommonJS(hono_exports);
// src/hono/factory.ts
var tryCatchFactory = /* @__PURE__ */ __name((factoryOptions) => {
return (options) => {
const context = factoryOptions.createContext(options);
return factoryOptions.handler(context);
};
}, "tryCatchFactory");
// 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/hono/utility.ts
var import_node_url = require("url");
var HonoErrorUtility = class extends BaseErrorUtility {
static {
__name(this, "HonoErrorUtility");
}
async mapRequest(req) {
const parsedUrl = new import_node_url.URL(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 errorContext = {};
ctx.set("tryCatch", {
addContext: (errCtx) => {
errorContext = {
...errorContext,
...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, errorContext).then(() => {
}).catch((e) => {
});
}
};
}
});
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
HonoErrorUtility,
tryCatch
});
//# sourceMappingURL=index.js.map