try-catch-cloud
Version:
Advanced error logging for your app with low code integration
267 lines (262 loc) • 7.2 kB
JavaScript
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