apitally 0.23.0 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,6 +38,7 @@ const RESPONSE_SYMBOL = Symbol("apitally.response");
38
38
  const RESPONSE_PROMISE_SYMBOL = Symbol("apitally.responsePromise");
39
39
  const ERROR_SYMBOL = Symbol("apitally.error");
40
40
  const CLIENT_SYMBOL = Symbol("apitally.client");
41
+ const REQUEST_SYMBOL = Symbol("apitally.request");
41
42
  function apitallyPlugin(config) {
42
43
  const client = new import_client.ApitallyClient(config);
43
44
  const logsContext = new import_node_async_hooks.AsyncLocalStorage();
@@ -51,7 +52,8 @@ function apitallyPlugin(config) {
51
52
  const originalMapResponse = handler.mapResponse;
52
53
  const originalMapCompactResponse = handler.mapCompactResponse;
53
54
  const originalMapEarlyResponse = handler.mapEarlyResponse;
54
- const captureMappedResponse = /* @__PURE__ */ __name((originalResponse, mappedResponse, request) => {
55
+ const captureMappedResponse = /* @__PURE__ */ __name((originalResponse, mappedResponse, set) => {
56
+ const request = set == null ? void 0 : set[REQUEST_SYMBOL];
55
57
  if (request instanceof Request && mappedResponse instanceof Response && !(RESPONSE_SYMBOL in request) && CLIENT_SYMBOL in request) {
56
58
  if (typeof originalResponse === "string") {
57
59
  const responseBody = Buffer.from(originalResponse);
@@ -74,19 +76,19 @@ function apitallyPlugin(config) {
74
76
  }
75
77
  return mappedResponse;
76
78
  }, "captureMappedResponse");
77
- handler.mapResponse = /* @__PURE__ */ __name(function wrappedMapResponse(response, set, request) {
78
- const mappedResponse = originalMapResponse(response, set, request);
79
- const newResponse = captureMappedResponse(response, mappedResponse, request);
79
+ handler.mapResponse = /* @__PURE__ */ __name(function wrappedMapResponse(response, set) {
80
+ const mappedResponse = originalMapResponse(response, set);
81
+ const newResponse = captureMappedResponse(response, mappedResponse, set);
80
82
  return newResponse;
81
83
  }, "wrappedMapResponse");
82
- handler.mapCompactResponse = /* @__PURE__ */ __name(function wrappedMapCompactResponse(response, request) {
83
- const mappedResponse = originalMapCompactResponse(response, request);
84
- const newResponse = captureMappedResponse(response, mappedResponse, request);
84
+ handler.mapCompactResponse = /* @__PURE__ */ __name(function wrappedMapCompactResponse(response) {
85
+ const mappedResponse = originalMapCompactResponse(response);
86
+ const newResponse = captureMappedResponse(response, mappedResponse, void 0);
85
87
  return newResponse;
86
88
  }, "wrappedMapCompactResponse");
87
- handler.mapEarlyResponse = /* @__PURE__ */ __name(function wrappedMapEarlyResponse(response, set, request) {
88
- const mappedResponse = originalMapEarlyResponse(response, set, request);
89
- const newResponse = captureMappedResponse(response, mappedResponse, request);
89
+ handler.mapEarlyResponse = /* @__PURE__ */ __name(function wrappedMapEarlyResponse(response, set) {
90
+ const mappedResponse = originalMapEarlyResponse(response, set);
91
+ const newResponse = captureMappedResponse(response, mappedResponse, set);
90
92
  return newResponse;
91
93
  }, "wrappedMapEarlyResponse");
92
94
  }
@@ -96,12 +98,13 @@ function apitallyPlugin(config) {
96
98
  client.startSync();
97
99
  }).onStop(async () => {
98
100
  await client.handleShutdown();
99
- }).onRequest(async ({ request }) => {
101
+ }).onRequest(async ({ request, set }) => {
100
102
  if (!client.isEnabled() || request.method.toUpperCase() === "OPTIONS") {
101
103
  return;
102
104
  }
103
105
  request[CLIENT_SYMBOL] = client;
104
106
  request[START_TIME_SYMBOL] = import_node_perf_hooks.performance.now();
107
+ set[REQUEST_SYMBOL] = request;
105
108
  logsContext.enterWith([]);
106
109
  if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody) {
107
110
  const contentType = request.headers.get("content-type");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n request?: Request,\n ) => {\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: Context[\"set\"],\n request?: Request,\n ) {\n const mappedResponse = originalMapResponse(response, set, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n request?: Request,\n ) {\n const mappedResponse = originalMapCompactResponse(response, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: Context[\"set\"],\n request?: Request,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","config","client","ApitallyClient","logsContext","AsyncLocalStorage","requestLogger","enabled","captureLogs","patchConsole","patchWinston","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureResponse","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","set","wrappedMapCompactResponse","wrappedMapEarlyResponse","decorate","onStart","appInfo","getAppInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","performance","now","enterWith","logRequestBody","contentType","headers","get","requestSize","parseContentLength","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","undefined","consumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","convertHeaders","Object","fromEntries","entries","ValidationError","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin","StatusMap"]}
1
+ {"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\nconst REQUEST_SYMBOL = Symbol(\"apitally.request\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ntype ContextSet = Context[\"set\"] & {\n [REQUEST_SYMBOL]?: Request;\n};\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n set?: ContextSet,\n ) => {\n const request = set?.[REQUEST_SYMBOL];\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n ) {\n const mappedResponse = originalMapCompactResponse(response);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n undefined,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request, set }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n (set as ContextSet)[REQUEST_SYMBOL] = request;\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","REQUEST_SYMBOL","config","client","ApitallyClient","logsContext","AsyncLocalStorage","requestLogger","enabled","captureLogs","patchConsole","patchWinston","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","set","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureResponse","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","wrappedMapCompactResponse","undefined","wrappedMapEarlyResponse","decorate","onStart","appInfo","getAppInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","performance","now","enterWith","logRequestBody","contentType","headers","get","requestSize","parseContentLength","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","consumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","convertHeaders","Object","fromEntries","entries","ValidationError","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin","StatusMap"]}
@@ -16,6 +16,7 @@ const RESPONSE_SYMBOL = Symbol("apitally.response");
16
16
  const RESPONSE_PROMISE_SYMBOL = Symbol("apitally.responsePromise");
17
17
  const ERROR_SYMBOL = Symbol("apitally.error");
18
18
  const CLIENT_SYMBOL = Symbol("apitally.client");
19
+ const REQUEST_SYMBOL = Symbol("apitally.request");
19
20
  function apitallyPlugin(config) {
20
21
  const client = new ApitallyClient(config);
21
22
  const logsContext = new AsyncLocalStorage();
@@ -29,7 +30,8 @@ function apitallyPlugin(config) {
29
30
  const originalMapResponse = handler.mapResponse;
30
31
  const originalMapCompactResponse = handler.mapCompactResponse;
31
32
  const originalMapEarlyResponse = handler.mapEarlyResponse;
32
- const captureMappedResponse = /* @__PURE__ */ __name((originalResponse, mappedResponse, request) => {
33
+ const captureMappedResponse = /* @__PURE__ */ __name((originalResponse, mappedResponse, set) => {
34
+ const request = set == null ? void 0 : set[REQUEST_SYMBOL];
33
35
  if (request instanceof Request && mappedResponse instanceof Response && !(RESPONSE_SYMBOL in request) && CLIENT_SYMBOL in request) {
34
36
  if (typeof originalResponse === "string") {
35
37
  const responseBody = Buffer.from(originalResponse);
@@ -52,19 +54,19 @@ function apitallyPlugin(config) {
52
54
  }
53
55
  return mappedResponse;
54
56
  }, "captureMappedResponse");
55
- handler.mapResponse = /* @__PURE__ */ __name(function wrappedMapResponse(response, set, request) {
56
- const mappedResponse = originalMapResponse(response, set, request);
57
- const newResponse = captureMappedResponse(response, mappedResponse, request);
57
+ handler.mapResponse = /* @__PURE__ */ __name(function wrappedMapResponse(response, set) {
58
+ const mappedResponse = originalMapResponse(response, set);
59
+ const newResponse = captureMappedResponse(response, mappedResponse, set);
58
60
  return newResponse;
59
61
  }, "wrappedMapResponse");
60
- handler.mapCompactResponse = /* @__PURE__ */ __name(function wrappedMapCompactResponse(response, request) {
61
- const mappedResponse = originalMapCompactResponse(response, request);
62
- const newResponse = captureMappedResponse(response, mappedResponse, request);
62
+ handler.mapCompactResponse = /* @__PURE__ */ __name(function wrappedMapCompactResponse(response) {
63
+ const mappedResponse = originalMapCompactResponse(response);
64
+ const newResponse = captureMappedResponse(response, mappedResponse, void 0);
63
65
  return newResponse;
64
66
  }, "wrappedMapCompactResponse");
65
- handler.mapEarlyResponse = /* @__PURE__ */ __name(function wrappedMapEarlyResponse(response, set, request) {
66
- const mappedResponse = originalMapEarlyResponse(response, set, request);
67
- const newResponse = captureMappedResponse(response, mappedResponse, request);
67
+ handler.mapEarlyResponse = /* @__PURE__ */ __name(function wrappedMapEarlyResponse(response, set) {
68
+ const mappedResponse = originalMapEarlyResponse(response, set);
69
+ const newResponse = captureMappedResponse(response, mappedResponse, set);
68
70
  return newResponse;
69
71
  }, "wrappedMapEarlyResponse");
70
72
  }
@@ -74,12 +76,13 @@ function apitallyPlugin(config) {
74
76
  client.startSync();
75
77
  }).onStop(async () => {
76
78
  await client.handleShutdown();
77
- }).onRequest(async ({ request }) => {
79
+ }).onRequest(async ({ request, set }) => {
78
80
  if (!client.isEnabled() || request.method.toUpperCase() === "OPTIONS") {
79
81
  return;
80
82
  }
81
83
  request[CLIENT_SYMBOL] = client;
82
84
  request[START_TIME_SYMBOL] = performance.now();
85
+ set[REQUEST_SYMBOL] = request;
83
86
  logsContext.enterWith([]);
84
87
  if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody) {
85
88
  const contentType = request.headers.get("content-type");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n request?: Request,\n ) => {\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: Context[\"set\"],\n request?: Request,\n ) {\n const mappedResponse = originalMapResponse(response, set, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n request?: Request,\n ) {\n const mappedResponse = originalMapCompactResponse(response, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: Context[\"set\"],\n request?: Request,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set, request);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n request,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["StatusMap","ValidationError","AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","convertHeaders","captureResponse","patchConsole","patchWinston","getAppInfo","START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","config","client","logsContext","requestLogger","enabled","captureLogs","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","set","wrappedMapCompactResponse","wrappedMapEarlyResponse","decorate","onStart","appInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","now","enterWith","logRequestBody","contentType","headers","get","requestSize","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","undefined","consumer","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","Object","fromEntries","entries","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin"]}
1
+ {"version":3,"sources":["../../src/elysia/plugin.ts"],"sourcesContent":["import { Context, Elysia, StatusMap, ValidationError } from \"elysia\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { performance } from \"node:perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport { CapturedResponse, captureResponse } from \"../common/response.js\";\nimport { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nimport { patchConsole, patchWinston } from \"../loggers/index.js\";\nimport { getAppInfo } from \"./utils.js\";\n\nconst START_TIME_SYMBOL = Symbol(\"apitally.startTime\");\nconst REQUEST_BODY_SYMBOL = Symbol(\"apitally.requestBody\");\nconst RESPONSE_SYMBOL = Symbol(\"apitally.response\");\nconst RESPONSE_PROMISE_SYMBOL = Symbol(\"apitally.responsePromise\");\nconst ERROR_SYMBOL = Symbol(\"apitally.error\");\nconst CLIENT_SYMBOL = Symbol(\"apitally.client\");\nconst REQUEST_SYMBOL = Symbol(\"apitally.request\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;\n [ERROR_SYMBOL]?: Readonly<Error>;\n [CLIENT_SYMBOL]?: ApitallyClient;\n }\n}\n\ntype ContextSet = Context[\"set\"] & {\n [REQUEST_SYMBOL]?: Request;\n};\n\ninterface ApitallyContext {\n consumer?: ApitallyConsumer | string;\n}\n\nexport default function apitallyPlugin(config: ApitallyConfig) {\n const client = new ApitallyClient(config);\n const logsContext = new AsyncLocalStorage<LogRecord[]>();\n\n if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) {\n patchConsole(logsContext);\n patchWinston(logsContext);\n }\n\n return (app: Elysia) => {\n const handler = app[\"~adapter\"].handler;\n\n if (!handler.mapResponse.name.startsWith(\"wrapped\")) {\n const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureMappedResponse = (\n originalResponse: unknown,\n mappedResponse: unknown,\n set?: ContextSet,\n ) => {\n const request = set?.[REQUEST_SYMBOL];\n if (\n request instanceof Request &&\n mappedResponse instanceof Response &&\n !(RESPONSE_SYMBOL in request) &&\n CLIENT_SYMBOL in request\n ) {\n if (typeof originalResponse === \"string\") {\n // Preserve the response body value as Blob if the original response is a string,\n // so that Bun adds a Content-Type header.\n const responseBody = Buffer.from(originalResponse as string);\n request[RESPONSE_SYMBOL] = mappedResponse;\n request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({\n body: responseBody,\n size: responseBody.length,\n completed: true,\n });\n } else {\n // Otherwise capture the response using streaming\n const client = request[CLIENT_SYMBOL]!;\n const [newResponse, responsePromise] = captureResponse(\n mappedResponse,\n {\n captureBody:\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody,\n maxBodySize: client.requestLogger.maxBodySize,\n },\n );\n request[RESPONSE_SYMBOL] = newResponse;\n request[RESPONSE_PROMISE_SYMBOL] = responsePromise;\n return newResponse;\n }\n }\n return mappedResponse;\n };\n\n handler.mapResponse = function wrappedMapResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n handler.mapCompactResponse = function wrappedMapCompactResponse(\n response: unknown,\n ) {\n const mappedResponse = originalMapCompactResponse(response);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n undefined,\n );\n return newResponse;\n };\n handler.mapEarlyResponse = function wrappedMapEarlyResponse(\n response: unknown,\n set: ContextSet,\n ) {\n const mappedResponse = originalMapEarlyResponse(response, set);\n const newResponse = captureMappedResponse(\n response,\n mappedResponse,\n set,\n );\n return newResponse;\n };\n }\n\n return app\n .decorate(\"apitally\", {} as ApitallyContext)\n .onStart(() => {\n const appInfo = getAppInfo(app, config.appVersion);\n client.setStartupData(appInfo);\n client.startSync();\n })\n .onStop(async () => {\n await client.handleShutdown();\n })\n .onRequest(async ({ request, set }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n request[CLIENT_SYMBOL] = client;\n request[START_TIME_SYMBOL] = performance.now();\n (set as ContextSet)[REQUEST_SYMBOL] = request;\n logsContext.enterWith([]);\n\n // Capture request body\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logRequestBody\n ) {\n const contentType = request.headers.get(\"content-type\");\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ?? 0;\n\n if (\n client.requestLogger.isSupportedContentType(contentType) &&\n requestSize <= client.requestLogger.maxBodySize\n ) {\n try {\n request[REQUEST_BODY_SYMBOL] = Buffer.from(\n await request.clone().arrayBuffer(),\n );\n } catch (error) {\n // ignore\n }\n }\n }\n })\n .onAfterResponse(async ({ request, set, route, apitally }) => {\n if (!client.isEnabled() || request.method.toUpperCase() === \"OPTIONS\") {\n return;\n }\n\n const startTime = request[START_TIME_SYMBOL];\n const responseTime = startTime ? performance.now() - startTime : 0;\n\n const requestBody = request[REQUEST_BODY_SYMBOL];\n const requestSize =\n parseContentLength(request.headers.get(\"content-length\")) ??\n requestBody?.length;\n\n let responsePromise = request[RESPONSE_PROMISE_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n const error = request[ERROR_SYMBOL];\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n // Convert error to response\n try {\n response = error.toResponse() as Response;\n const errorResponseBody = Buffer.from(await response.arrayBuffer());\n responsePromise = Promise.resolve({\n body: errorResponseBody,\n size: errorResponseBody.length,\n completed: true,\n });\n } catch (error) {\n // ignore\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n\n if (!response) {\n // Create empty fake response for errors without the toResponse method\n response = new Response(null, {\n status: statusCode,\n statusText: \"\",\n headers: new Headers(),\n });\n responsePromise = Promise.resolve({\n body: undefined,\n size: 0,\n completed: true,\n });\n }\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n // Log request when response has been fully captured\n responsePromise?.then(async (capturedResponse) => {\n const responseHeaders = response?.headers ?? set.headers;\n const responseSize = capturedResponse.completed\n ? capturedResponse.size\n : undefined;\n\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n statusCode,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n client.requestLogger.logRequest(\n {\n timestamp: (Date.now() - responseTime) / 1000,\n method: request.method,\n path: route,\n url: request.url,\n headers: convertHeaders(\n Object.fromEntries(request.headers.entries()),\n ),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody,\n },\n {\n statusCode,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: capturedResponse.body,\n },\n error,\n logs,\n );\n }\n });\n\n // Handle validation errors\n if (\n (statusCode === 400 || statusCode === 422) &&\n error instanceof ValidationError\n ) {\n try {\n const parsedMessage = JSON.parse(error.message);\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n loc:\n (parsedMessage.on ?? \"\") + \".\" + (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n\n // Handle server errors\n if (statusCode === 500 && error) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: request.method,\n path: route,\n type: error.name,\n msg: error.message,\n traceback: error.stack || \"\",\n });\n }\n })\n .onError(({ request, error }) => {\n if (client.isEnabled() && error instanceof Error) {\n request[ERROR_SYMBOL] = error;\n }\n });\n };\n}\n\nfunction getStatusCode(set: Context[\"set\"]) {\n if (typeof set.status === \"number\") {\n return set.status;\n } else if (typeof set.status === \"string\") {\n return StatusMap[set.status];\n }\n}\n"],"mappings":"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","names":["StatusMap","ValidationError","AsyncLocalStorage","performance","ApitallyClient","consumerFromStringOrObject","parseContentLength","convertHeaders","captureResponse","patchConsole","patchWinston","getAppInfo","START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","RESPONSE_PROMISE_SYMBOL","ERROR_SYMBOL","CLIENT_SYMBOL","REQUEST_SYMBOL","config","client","logsContext","requestLogger","enabled","captureLogs","app","handler","mapResponse","name","startsWith","originalMapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureMappedResponse","originalResponse","mappedResponse","set","request","Request","Response","responseBody","Buffer","from","Promise","resolve","body","size","length","completed","newResponse","responsePromise","captureBody","logResponseBody","maxBodySize","wrappedMapResponse","response","wrappedMapCompactResponse","undefined","wrappedMapEarlyResponse","decorate","onStart","appInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","now","enterWith","logRequestBody","contentType","headers","get","requestSize","isSupportedContentType","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","toResponse","errorResponseBody","statusCode","status","getStatusCode","statusText","Headers","consumer","consumerRegistry","addOrUpdateConsumer","then","capturedResponse","responseHeaders","responseSize","requestCounter","addRequest","identifier","path","logs","getStore","logRequest","timestamp","Date","url","Object","fromEntries","entries","parsedMessage","JSON","parse","message","validationErrorCounter","addValidationError","loc","on","property","msg","type","serverErrorCounter","addServerError","traceback","stack","onError","Error","apitallyPlugin"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apitally",
3
- "version": "0.23.0",
3
+ "version": "0.23.1",
4
4
  "description": "Simple API monitoring & analytics for REST APIs built with Express, Fastify, NestJS, AdonisJS, Hono, H3, Elysia, Hapi, and Koa.",
5
5
  "author": "Apitally <hello@apitally.io>",
6
6
  "license": "MIT",
@@ -291,7 +291,7 @@
291
291
  "@types/koa__router": "^12.0.4",
292
292
  "@types/koa-bodyparser": "^4.3.12",
293
293
  "@types/koa-route": "^3.2.8",
294
- "@types/node": "^22.0.0",
294
+ "@types/node": "^24.0.0",
295
295
  "@types/supertest": "^6.0.0",
296
296
  "@vinejs/vine": "^2.1.0 || ^3.0.1",
297
297
  "@vitest/coverage-v8": "^3.0.0",
@@ -312,7 +312,7 @@
312
312
  "koa-bodyparser": "^4.4.1",
313
313
  "koa-route": "^4.0.0",
314
314
  "nock": "^14.0.0",
315
- "pino-http": "^10.5.0",
315
+ "pino-http": "^11.0.0",
316
316
  "prettier": "^3.1.0",
317
317
  "supertest": "^7.0.0",
318
318
  "tsup": "^8.1.0",