try-catch-cloud
Version:
Advanced error logging for your app with low code integration
318 lines (311 loc) • 9.29 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/express/index.ts
var express_exports = {};
__export(express_exports, {
ExpressErrorUtility: () => ExpressErrorUtility,
addContext: () => addContext,
setupTryCatch: () => setupTryCatch
});
module.exports = __toCommonJS(express_exports);
// 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");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ExpressErrorUtility,
addContext,
setupTryCatch
});
//# sourceMappingURL=index.js.map