apitally
Version:
Simple API monitoring & analytics for REST APIs built with Express, Fastify, NestJS, AdonisJS, Hono, H3, Elysia, Hapi, and Koa.
175 lines • 7.71 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);
var plugin_exports = {};
__export(plugin_exports, {
apitallyPlugin: () => apitallyPlugin,
setConsumer: () => setConsumer
});
module.exports = __toCommonJS(plugin_exports);
var import_h3 = require("h3");
var import_node_async_hooks = require("node:async_hooks");
var import_node_perf_hooks = require("node:perf_hooks");
var import_client = require("../common/client.js");
var import_consumerRegistry = require("../common/consumerRegistry.js");
var import_headers = require("../common/headers.js");
var import_requestLogger = require("../common/requestLogger.js");
var import_response = require("../common/response.js");
var import_loggers = require("../loggers/index.js");
var import_utils = require("./utils.js");
const REQUEST_TIMESTAMP_SYMBOL = Symbol("apitally.requestTimestamp");
const REQUEST_BODY_SYMBOL = Symbol("apitally.requestBody");
const jsonHeaders = new Headers({
"content-type": "application/json;charset=UTF-8"
});
const apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
const client = new import_client.ApitallyClient(config);
const logsContext = new import_node_async_hooks.AsyncLocalStorage();
const setStartupData = /* @__PURE__ */ __name((attempt = 1) => {
const appInfo = (0, import_utils.getAppInfo)(app, config.appVersion);
if (appInfo.paths.length > 0 || attempt >= 10) {
client.setStartupData(appInfo);
client.startSync();
} else {
setTimeout(() => setStartupData(attempt + 1), 500);
}
}, "setStartupData");
setTimeout(() => setStartupData(), 500);
if (client.requestLogger.config.captureLogs) {
(0, import_loggers.patchConsole)(logsContext);
(0, import_loggers.patchWinston)(logsContext);
}
const handleResponse = /* @__PURE__ */ __name(async (event, response, error) => {
var _a, _b;
if (event.req.method.toUpperCase() === "OPTIONS") {
return response;
}
const startTime = event.context[REQUEST_TIMESTAMP_SYMBOL];
const path = (_a = event.context.matchedRoute) == null ? void 0 : _a.route;
const consumer = getConsumer(event);
client.consumerRegistry.addOrUpdateConsumer(consumer);
if (!response) {
response = new Response(null, {
status: (error == null ? void 0 : error.status) || 500,
statusText: (error == null ? void 0 : error.statusText) || "Internal Server Error",
headers: (error == null ? void 0 : error.headers) ? (0, import_headers.mergeHeaders)(jsonHeaders, error.headers) : jsonHeaders
});
}
const [newResponse, responsePromise] = (0, import_response.captureResponse)(response, {
captureBody: client.requestLogger.enabled && client.requestLogger.config.logResponseBody,
maxBodySize: client.requestLogger.maxBodySize
});
responsePromise.then(async (capturedResponse) => {
const responseTime = startTime ? import_node_perf_hooks.performance.now() - startTime : 0;
const responseSize = capturedResponse.completed ? capturedResponse.size : void 0;
const requestSize = (0, import_headers.parseContentLength)(event.req.headers.get("content-length"));
if (path) {
client.requestCounter.addRequest({
consumer: consumer == null ? void 0 : consumer.identifier,
method: event.req.method,
path,
statusCode: response.status,
responseTime,
requestSize,
responseSize
});
}
if (client.requestLogger.enabled) {
const logs = logsContext.getStore();
client.requestLogger.logRequest({
timestamp: (Date.now() - responseTime) / 1e3,
method: event.req.method,
path,
url: event.req.url,
headers: (0, import_requestLogger.convertHeaders)(Object.fromEntries(event.req.headers.entries())),
size: requestSize,
consumer: consumer == null ? void 0 : consumer.identifier,
body: event.context[REQUEST_BODY_SYMBOL]
}, {
statusCode: response.status,
responseTime: responseTime / 1e3,
headers: (0, import_requestLogger.convertHeaders)(Object.fromEntries(response.headers.entries())),
size: responseSize,
body: capturedResponse.body
}, (error == null ? void 0 : error.cause) instanceof Error ? error.cause : void 0, logs);
}
});
if (path && (error == null ? void 0 : error.status) === 400 && error.data && error.data.name === "ZodError") {
const zodError = error.data;
(_b = zodError.issues) == null ? void 0 : _b.forEach((issue) => {
client.validationErrorCounter.addValidationError({
consumer: consumer == null ? void 0 : consumer.identifier,
method: event.req.method,
path,
loc: issue.path.join("."),
msg: issue.message,
type: issue.code
});
});
}
if (path && (error == null ? void 0 : error.status) === 500 && error.cause instanceof Error) {
client.serverErrorCounter.addServerError({
consumer: consumer == null ? void 0 : consumer.identifier,
method: event.req.method,
path,
type: error.cause.name,
msg: error.cause.message,
traceback: error.cause.stack || ""
});
}
return newResponse;
}, "handleResponse");
app.use((0, import_h3.onRequest)(async (event) => {
logsContext.enterWith([]);
event.context[REQUEST_TIMESTAMP_SYMBOL] = import_node_perf_hooks.performance.now();
const requestContentType = event.req.headers.get("content-type");
const requestSize = (0, import_headers.parseContentLength)(event.req.headers.get("content-length")) ?? 0;
if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody && client.requestLogger.isSupportedContentType(requestContentType) && requestSize <= client.requestLogger.maxBodySize) {
const clonedRequest = event.req.clone();
const requestBody = Buffer.from(await clonedRequest.arrayBuffer());
event.context[REQUEST_BODY_SYMBOL] = requestBody;
}
})).use((0, import_h3.onResponse)((response, event) => {
if (client.isEnabled()) {
return handleResponse(event, response, void 0);
}
})).use((0, import_h3.onError)((error, event) => {
if (client.isEnabled()) {
handleResponse(event, void 0, error);
}
}));
});
function setConsumer(event, consumer) {
event.context.apitallyConsumer = consumer || void 0;
}
__name(setConsumer, "setConsumer");
function getConsumer(event) {
const consumer = event.context.apitallyConsumer;
if (consumer) {
return (0, import_consumerRegistry.consumerFromStringOrObject)(consumer);
}
return null;
}
__name(getConsumer, "getConsumer");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
apitallyPlugin,
setConsumer
});
//# sourceMappingURL=plugin.cjs.map