UNPKG

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
"use strict"; 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