apitally 0.21.4 → 0.22.0
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.
- package/dist/common/requestLogger.cjs +23 -12
- package/dist/common/requestLogger.cjs.map +1 -1
- package/dist/common/requestLogger.d.cts +1 -1
- package/dist/common/requestLogger.d.ts +1 -1
- package/dist/common/requestLogger.js +23 -12
- package/dist/common/requestLogger.js.map +1 -1
- package/dist/common/response.cjs +63 -88
- package/dist/common/response.cjs.map +1 -1
- package/dist/common/response.d.cts +14 -6
- package/dist/common/response.d.ts +14 -6
- package/dist/common/response.js +61 -83
- package/dist/common/response.js.map +1 -1
- package/dist/elysia/index.d.cts +6 -0
- package/dist/elysia/index.d.ts +6 -0
- package/dist/elysia/plugin.cjs +104 -76
- package/dist/elysia/plugin.cjs.map +1 -1
- package/dist/elysia/plugin.d.cts +10 -0
- package/dist/elysia/plugin.d.ts +10 -0
- package/dist/elysia/plugin.js +105 -77
- package/dist/elysia/plugin.js.map +1 -1
- package/dist/h3/plugin.cjs +59 -59
- package/dist/h3/plugin.cjs.map +1 -1
- package/dist/h3/plugin.js +60 -60
- package/dist/h3/plugin.js.map +1 -1
- package/dist/h3/utils.cjs +1 -1
- package/dist/h3/utils.cjs.map +1 -1
- package/dist/h3/utils.js +1 -1
- package/dist/h3/utils.js.map +1 -1
- package/dist/hono/middleware.cjs +63 -64
- package/dist/hono/middleware.cjs.map +1 -1
- package/dist/hono/middleware.js +64 -65
- package/dist/hono/middleware.js.map +1 -1
- package/package.json +2 -2
|
@@ -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 {\n getResponseBody,\n measureResponseSize,\n teeResponse,\n teeResponseBlob,\n} 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 ERROR_SYMBOL = Symbol(\"apitally.error\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [ERROR_SYMBOL]?: Readonly<Error>;\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 const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureResponse = (\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 ) {\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 [newResponse1, newResponse2] =\n originalResponse?.constructor?.name === \"String\"\n ? teeResponseBlob(mappedResponse, originalResponse as string)\n : teeResponse(mappedResponse);\n request[RESPONSE_SYMBOL] = newResponse2;\n return newResponse1;\n } else {\n return mappedResponse;\n }\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 = captureResponse(response, mappedResponse, request);\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 = captureResponse(response, mappedResponse, request);\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 = captureResponse(response, mappedResponse, request);\n return newResponse;\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 logsContext.enterWith([]);\n request[START_TIME_SYMBOL] = performance.now();\n\n // Capture request body for logging if enabled\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 const error = request[ERROR_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n let responseBody: Buffer | undefined;\n let responseSize: number | undefined;\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n try {\n response = error.toResponse();\n } catch (error) {\n // ignore\n }\n }\n\n if (response instanceof Response) {\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(\n response.headers.get(\"content-type\"),\n )\n ) {\n responseBody = (await getResponseBody(response, false))[0];\n responseSize = responseBody.length;\n } else {\n responseSize = (await measureResponseSize(response, false))[0];\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n const responseHeaders = response?.headers ?? set.headers;\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n if (route) {\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 // 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 // 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 ?? \"\") +\n \".\" +\n (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n }\n\n // Request logging\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: responseBody,\n },\n error,\n logs,\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":";;;;;;;;;;;;;;;;;;;AAAA;;;;;AAAA,oBAA4D;AAC5D,8BAAkC;AAClC,6BAA4B;AAE5B,oBAA+B;AAC/B,8BAA2C;AAC3C,qBAAmC;AAEnC,2BAA+B;AAC/B,sBAKO;AAEP,qBAA2C;AAC3C,mBAA2B;AAE3B,MAAMA,oBAAoBC,OAAO,oBAAA;AACjC,MAAMC,sBAAsBD,OAAO,sBAAA;AACnC,MAAME,kBAAkBF,OAAO,mBAAA;AAC/B,MAAMG,eAAeH,OAAO,gBAAA;AAeb,SAAf,eAAuCI,QAAsB;AAC3D,QAAMC,SAAS,IAAIC,6BAAeF,MAAAA;AAClC,QAAMG,cAAc,IAAIC,0CAAAA;AAExB,MAAIH,OAAOI,cAAcC,WAAWL,OAAOI,cAAcL,OAAOO,aAAa;AAC3EC,qCAAaL,WAAAA;AACbM,qCAAaN,WAAAA;EACf;AAEA,SAAO,CAACO,QAAAA;AACN,UAAMC,UAAUD,IAAI,UAAA,EAAYC;AAChC,UAAMC,sBAAsBD,QAAQE;AACpC,UAAMC,6BAA6BH,QAAQI;AAC3C,UAAMC,2BAA2BL,QAAQM;AAEzC,UAAMC,kBAAkB,wBACtBC,kBACAC,gBACAC,YAAAA;AAvDN;AAyDM,UACEA,mBAAmBC,WACnBF,0BAA0BG,YAC1B,EAAEzB,mBAAmBuB,UACrB;AAGA,cAAM,CAACG,cAAcC,YAAAA,MACnBN,0DAAkB,gBAAlBA,mBAA+BO,UAAS,eACpCC,iCAAgBP,gBAAgBD,gBAAAA,QAChCS,6BAAYR,cAAAA;AAClBC,gBAAQvB,eAAAA,IAAmB2B;AAC3B,eAAOD;MACT,OAAO;AACL,eAAOJ;MACT;IACF,GArBwB;AAuBxBT,YAAQE,cAAc,gCAASgB,mBAC7BC,UACAC,KACAV,SAAiB;AAEjB,YAAMD,iBAAiBR,oBAAoBkB,UAAUC,KAAKV,OAAAA;AAC1D,YAAMW,cAAcd,gBAAgBY,UAAUV,gBAAgBC,OAAAA;AAC9D,aAAOW;IACT,GARsB;AAStBrB,YAAQI,qBAAqB,gCAASkB,0BACpCH,UACAT,SAAiB;AAEjB,YAAMD,iBAAiBN,2BAA2BgB,UAAUT,OAAAA;AAC5D,YAAMW,cAAcd,gBAAgBY,UAAUV,gBAAgBC,OAAAA;AAC9D,aAAOW;IACT,GAP6B;AAQ7BrB,YAAQM,mBAAmB,gCAASiB,wBAClCJ,UACAC,KACAV,SAAiB;AAEjB,YAAMD,iBAAiBJ,yBAAyBc,UAAUC,KAAKV,OAAAA;AAC/D,YAAMW,cAAcd,gBAAgBY,UAAUV,gBAAgBC,OAAAA;AAC9D,aAAOW;IACT,GAR2B;AAU3B,WAAOtB,IACJyB,SAAS,YAAY,CAAC,CAAA,EACtBC,QAAQ,MAAA;AACP,YAAMC,cAAUC,yBAAW5B,KAAKV,OAAOuC,UAAU;AACjDtC,aAAOuC,eAAeH,OAAAA;AACtBpC,aAAOwC,UAAS;IAClB,CAAA,EACCC,OAAO,YAAA;AACN,YAAMzC,OAAO0C,eAAc;IAC7B,CAAA,EACCC,UAAU,OAAO,EAAEvB,QAAO,MAAE;AAC3B,UAAI,CAACpB,OAAO4C,UAAS,KAAMxB,QAAQyB,OAAOC,YAAW,MAAO,WAAW;AACrE;MACF;AAEA5C,kBAAY6C,UAAU,CAAA,CAAE;AACxB3B,cAAQ1B,iBAAAA,IAAqBsD,mCAAYC,IAAG;AAG5C,UACEjD,OAAOI,cAAcC,WACrBL,OAAOI,cAAcL,OAAOmD,gBAC5B;AACA,cAAMC,cAAc/B,QAAQgC,QAAQC,IAAI,cAAA;AACxC,cAAMC,kBACJC,mCAAmBnC,QAAQgC,QAAQC,IAAI,gBAAA,CAAA,KAAsB;AAE/D,YACErD,OAAOI,cAAcoD,uBAAuBL,WAAAA,KAC5CG,eAAetD,OAAOI,cAAcqD,aACpC;AACA,cAAI;AACFrC,oBAAQxB,mBAAAA,IAAuB8D,OAAOC,KACpC,MAAMvC,QAAQwC,MAAK,EAAGC,YAAW,CAAA;UAErC,SAASC,OAAO;UAEhB;QACF;MACF;IACF,CAAA,EACCC,gBAAgB,OAAO,EAAE3C,SAASU,KAAKkC,OAAOC,SAAQ,MAAE;AACvD,UAAI,CAACjE,OAAO4C,UAAS,KAAMxB,QAAQyB,OAAOC,YAAW,MAAO,WAAW;AACrE;MACF;AAEA,YAAMoB,YAAY9C,QAAQ1B,iBAAAA;AAC1B,YAAMyE,eAAeD,YAAYlB,mCAAYC,IAAG,IAAKiB,YAAY;AAEjE,YAAME,cAAchD,QAAQxB,mBAAAA;AAC5B,YAAM0D,kBACJC,mCAAmBnC,QAAQgC,QAAQC,IAAI,gBAAA,CAAA,MACvCe,2CAAaC;AAEf,YAAMP,QAAQ1C,QAAQtB,YAAAA;AACtB,UAAI+B,WAAWT,QAAQvB,eAAAA;AACvB,UAAIyE;AACJ,UAAIC;AAEJ,UACE,CAAC1C,YACDiC,SACA,gBAAgBA,SAChB,OAAOA,MAAMU,eAAe,YAC5B;AACA,YAAI;AACF3C,qBAAWiC,MAAMU,WAAU;QAC7B,SAASV,QAAO;QAEhB;MACF;AAEA,UAAIjC,oBAAoBP,UAAU;AAChC,YACEtB,OAAOI,cAAcC,WACrBL,OAAOI,cAAcL,OAAO0E,mBAC5BzE,OAAOI,cAAcoD,uBACnB3B,SAASuB,QAAQC,IAAI,cAAA,CAAA,GAEvB;AACAiB,0BAAgB,UAAMI,iCAAgB7C,UAAU,KAAA,GAAQ,CAAA;AACxD0C,yBAAeD,aAAaD;QAC9B,OAAO;AACLE,0BAAgB,UAAMI,qCAAoB9C,UAAU,KAAA,GAAQ,CAAA;QAC9D;MACF;AAEA,YAAM+C,cAAa/C,qCAAUgD,WAAUC,cAAchD,GAAAA,KAAQ;AAC7D,YAAMiD,mBAAkBlD,qCAAUuB,YAAWtB,IAAIsB;AAEjD,YAAM4B,WAAWf,SAASe,eACtBC,oDAA2BhB,SAASe,QAAQ,IAC5C;AACJhF,aAAOkF,iBAAiBC,oBAAoBH,QAAAA;AAE5C,UAAIhB,OAAO;AACThE,eAAOoF,eAAeC,WAAW;UAC/BL,UAAUA,qCAAUM;UACpBzC,QAAQzB,QAAQyB;UAChB0C,MAAMvB;UACNY;UACAT;UACAb;UACAiB;QACF,CAAA;AAGA,YAAIK,eAAe,OAAOd,OAAO;AAC/B9D,iBAAOwF,mBAAmBC,eAAe;YACvCT,UAAUA,qCAAUM;YACpBzC,QAAQzB,QAAQyB;YAChB0C,MAAMvB;YACN0B,MAAM5B,MAAMrC;YACZkE,KAAK7B,MAAM8B;YACXC,WAAW/B,MAAMgC,SAAS;UAC5B,CAAA;QACF;AAGA,aACGlB,eAAe,OAAOA,eAAe,QACtCd,iBAAiBiC,+BACjB;AACA,cAAI;AACF,kBAAMC,gBAAgBC,KAAKC,MAAMpC,MAAM8B,OAAO;AAC9C5F,mBAAOmG,uBAAuBC,mBAAmB;cAC/CpB,UAAUA,qCAAUM;cACpBzC,QAAQzB,QAAQyB;cAChB0C,MAAMvB;cACNqC,MACGL,cAAcM,MAAM,MACrB,OACCN,cAAcO,YAAY;cAC7BZ,KAAKK,cAAcJ;cACnBF,MAAM;YACR,CAAA;UACF,SAAS5B,QAAO;UAEhB;QACF;MACF;AAGA,UAAI9D,OAAOI,cAAcC,SAAS;AAChC,cAAMmG,OAAOtG,YAAYuG,SAAQ;AACjCzG,eAAOI,cAAcsG,WACnB;UACEC,YAAYC,KAAK3D,IAAG,IAAKkB,gBAAgB;UACzCtB,QAAQzB,QAAQyB;UAChB0C,MAAMvB;UACN6C,KAAKzF,QAAQyF;UACbzD,aAAS0D,qCACPC,OAAOC,YAAY5F,QAAQgC,QAAQ6D,QAAO,CAAA,CAAA;UAE5CC,MAAM5D;UACN0B,UAAUA,qCAAUM;UACpB6B,MAAM/C;QACR,GACA;UACEQ;UACAT,cAAcA,eAAe;UAC7Bf,aAAS0D,qCAAe/B,eAAAA;UACxBmC,MAAM3C;UACN4C,MAAM7C;QACR,GACAR,OACA0C,IAAAA;MAEJ;IACF,CAAA,EACCY,QAAQ,CAAC,EAAEhG,SAAS0C,MAAK,MAAE;AAC1B,UAAI9D,OAAO4C,UAAS,KAAMkB,iBAAiBuD,OAAO;AAChDjG,gBAAQtB,YAAAA,IAAgBgE;MAC1B;IACF,CAAA;EACJ;AACF;AAjPwBwD;AAmPxB,SAASxC,cAAchD,KAAmB;AACxC,MAAI,OAAOA,IAAI+C,WAAW,UAAU;AAClC,WAAO/C,IAAI+C;EACb,WAAW,OAAO/C,IAAI+C,WAAW,UAAU;AACzC,WAAO0C,wBAAUzF,IAAI+C,MAAM;EAC7B;AACF;AANSC;","names":["START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","ERROR_SYMBOL","config","client","ApitallyClient","logsContext","AsyncLocalStorage","requestLogger","enabled","captureLogs","patchConsole","patchWinston","app","handler","originalMapResponse","mapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureResponse","originalResponse","mappedResponse","request","Request","Response","newResponse1","newResponse2","name","teeResponseBlob","teeResponse","wrappedMapResponse","response","set","newResponse","wrappedMapCompactResponse","wrappedMapEarlyResponse","decorate","onStart","appInfo","getAppInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","enterWith","performance","now","logRequestBody","contentType","headers","get","requestSize","parseContentLength","isSupportedContentType","maxBodySize","Buffer","from","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","length","responseBody","responseSize","toResponse","logResponseBody","getResponseBody","measureResponseSize","statusCode","status","getStatusCode","responseHeaders","consumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","path","serverErrorCounter","addServerError","type","msg","message","traceback","stack","ValidationError","parsedMessage","JSON","parse","validationErrorCounter","addValidationError","loc","on","property","logs","getStore","logRequest","timestamp","Date","url","convertHeaders","Object","fromEntries","entries","size","body","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\");\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"]}
|
package/dist/elysia/plugin.d.cts
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import { ApitallyConfig, ApitallyConsumer } from '../common/types.cjs';
|
|
2
2
|
import { Elysia } from 'elysia';
|
|
3
|
+
import { ApitallyClient } from '../common/client.cjs';
|
|
4
|
+
import { CapturedResponse } from '../common/response.cjs';
|
|
3
5
|
import '../common/logging.cjs';
|
|
4
6
|
import 'winston';
|
|
5
7
|
import '../common/requestLogger.cjs';
|
|
6
8
|
import 'node:buffer';
|
|
7
9
|
import 'node:http';
|
|
8
10
|
import '../common/tempGzipFile.cjs';
|
|
11
|
+
import '../common/consumerRegistry.cjs';
|
|
12
|
+
import '../common/requestCounter.cjs';
|
|
13
|
+
import '../common/serverErrorCounter.cjs';
|
|
14
|
+
import '../common/validationErrorCounter.cjs';
|
|
9
15
|
|
|
10
16
|
declare const START_TIME_SYMBOL: unique symbol;
|
|
11
17
|
declare const REQUEST_BODY_SYMBOL: unique symbol;
|
|
12
18
|
declare const RESPONSE_SYMBOL: unique symbol;
|
|
19
|
+
declare const RESPONSE_PROMISE_SYMBOL: unique symbol;
|
|
13
20
|
declare const ERROR_SYMBOL: unique symbol;
|
|
21
|
+
declare const CLIENT_SYMBOL: unique symbol;
|
|
14
22
|
declare global {
|
|
15
23
|
interface Request {
|
|
16
24
|
[START_TIME_SYMBOL]?: number;
|
|
17
25
|
[REQUEST_BODY_SYMBOL]?: Buffer;
|
|
18
26
|
[RESPONSE_SYMBOL]?: Response;
|
|
27
|
+
[RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;
|
|
19
28
|
[ERROR_SYMBOL]?: Readonly<Error>;
|
|
29
|
+
[CLIENT_SYMBOL]?: ApitallyClient;
|
|
20
30
|
}
|
|
21
31
|
}
|
|
22
32
|
interface ApitallyContext {
|
package/dist/elysia/plugin.d.ts
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import { ApitallyConfig, ApitallyConsumer } from '../common/types.js';
|
|
2
2
|
import { Elysia } from 'elysia';
|
|
3
|
+
import { ApitallyClient } from '../common/client.js';
|
|
4
|
+
import { CapturedResponse } from '../common/response.js';
|
|
3
5
|
import '../common/logging.js';
|
|
4
6
|
import 'winston';
|
|
5
7
|
import '../common/requestLogger.js';
|
|
6
8
|
import 'node:buffer';
|
|
7
9
|
import 'node:http';
|
|
8
10
|
import '../common/tempGzipFile.js';
|
|
11
|
+
import '../common/consumerRegistry.js';
|
|
12
|
+
import '../common/requestCounter.js';
|
|
13
|
+
import '../common/serverErrorCounter.js';
|
|
14
|
+
import '../common/validationErrorCounter.js';
|
|
9
15
|
|
|
10
16
|
declare const START_TIME_SYMBOL: unique symbol;
|
|
11
17
|
declare const REQUEST_BODY_SYMBOL: unique symbol;
|
|
12
18
|
declare const RESPONSE_SYMBOL: unique symbol;
|
|
19
|
+
declare const RESPONSE_PROMISE_SYMBOL: unique symbol;
|
|
13
20
|
declare const ERROR_SYMBOL: unique symbol;
|
|
21
|
+
declare const CLIENT_SYMBOL: unique symbol;
|
|
14
22
|
declare global {
|
|
15
23
|
interface Request {
|
|
16
24
|
[START_TIME_SYMBOL]?: number;
|
|
17
25
|
[REQUEST_BODY_SYMBOL]?: Buffer;
|
|
18
26
|
[RESPONSE_SYMBOL]?: Response;
|
|
27
|
+
[RESPONSE_PROMISE_SYMBOL]?: Promise<CapturedResponse>;
|
|
19
28
|
[ERROR_SYMBOL]?: Readonly<Error>;
|
|
29
|
+
[CLIENT_SYMBOL]?: ApitallyClient;
|
|
20
30
|
}
|
|
21
31
|
}
|
|
22
32
|
interface ApitallyContext {
|
package/dist/elysia/plugin.js
CHANGED
|
@@ -7,13 +7,15 @@ import { ApitallyClient } from "../common/client.js";
|
|
|
7
7
|
import { consumerFromStringOrObject } from "../common/consumerRegistry.js";
|
|
8
8
|
import { parseContentLength } from "../common/headers.js";
|
|
9
9
|
import { convertHeaders } from "../common/requestLogger.js";
|
|
10
|
-
import {
|
|
10
|
+
import { captureResponse } from "../common/response.js";
|
|
11
11
|
import { patchConsole, patchWinston } from "../loggers/index.js";
|
|
12
12
|
import { getAppInfo } from "./utils.js";
|
|
13
13
|
const START_TIME_SYMBOL = Symbol("apitally.startTime");
|
|
14
14
|
const REQUEST_BODY_SYMBOL = Symbol("apitally.requestBody");
|
|
15
15
|
const RESPONSE_SYMBOL = Symbol("apitally.response");
|
|
16
|
+
const RESPONSE_PROMISE_SYMBOL = Symbol("apitally.responsePromise");
|
|
16
17
|
const ERROR_SYMBOL = Symbol("apitally.error");
|
|
18
|
+
const CLIENT_SYMBOL = Symbol("apitally.client");
|
|
17
19
|
function apitallyPlugin(config) {
|
|
18
20
|
const client = new ApitallyClient(config);
|
|
19
21
|
const logsContext = new AsyncLocalStorage();
|
|
@@ -23,34 +25,49 @@ function apitallyPlugin(config) {
|
|
|
23
25
|
}
|
|
24
26
|
return (app) => {
|
|
25
27
|
const handler = app["~adapter"].handler;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
if (!handler.mapResponse.name.startsWith("wrapped")) {
|
|
29
|
+
const originalMapResponse = handler.mapResponse;
|
|
30
|
+
const originalMapCompactResponse = handler.mapCompactResponse;
|
|
31
|
+
const originalMapEarlyResponse = handler.mapEarlyResponse;
|
|
32
|
+
const captureMappedResponse = /* @__PURE__ */ __name((originalResponse, mappedResponse, request) => {
|
|
33
|
+
if (request instanceof Request && mappedResponse instanceof Response && !(RESPONSE_SYMBOL in request) && CLIENT_SYMBOL in request) {
|
|
34
|
+
if (typeof originalResponse === "string") {
|
|
35
|
+
const responseBody = Buffer.from(originalResponse);
|
|
36
|
+
request[RESPONSE_SYMBOL] = mappedResponse;
|
|
37
|
+
request[RESPONSE_PROMISE_SYMBOL] = Promise.resolve({
|
|
38
|
+
body: responseBody,
|
|
39
|
+
size: responseBody.length,
|
|
40
|
+
completed: true
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
const client2 = request[CLIENT_SYMBOL];
|
|
44
|
+
const [newResponse, responsePromise] = captureResponse(mappedResponse, {
|
|
45
|
+
captureBody: client2.requestLogger.enabled && client2.requestLogger.config.logResponseBody,
|
|
46
|
+
maxBodySize: client2.requestLogger.maxBodySize
|
|
47
|
+
});
|
|
48
|
+
request[RESPONSE_SYMBOL] = newResponse;
|
|
49
|
+
request[RESPONSE_PROMISE_SYMBOL] = responsePromise;
|
|
50
|
+
return newResponse;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
36
53
|
return mappedResponse;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
+
}, "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);
|
|
58
|
+
return newResponse;
|
|
59
|
+
}, "wrappedMapResponse");
|
|
60
|
+
handler.mapCompactResponse = /* @__PURE__ */ __name(function wrappedMapCompactResponse(response, request) {
|
|
61
|
+
const mappedResponse = originalMapCompactResponse(response, request);
|
|
62
|
+
const newResponse = captureMappedResponse(response, mappedResponse, request);
|
|
63
|
+
return newResponse;
|
|
64
|
+
}, "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);
|
|
68
|
+
return newResponse;
|
|
69
|
+
}, "wrappedMapEarlyResponse");
|
|
70
|
+
}
|
|
54
71
|
return app.decorate("apitally", {}).onStart(() => {
|
|
55
72
|
const appInfo = getAppInfo(app, config.appVersion);
|
|
56
73
|
client.setStartupData(appInfo);
|
|
@@ -61,8 +78,9 @@ function apitallyPlugin(config) {
|
|
|
61
78
|
if (!client.isEnabled() || request.method.toUpperCase() === "OPTIONS") {
|
|
62
79
|
return;
|
|
63
80
|
}
|
|
64
|
-
|
|
81
|
+
request[CLIENT_SYMBOL] = client;
|
|
65
82
|
request[START_TIME_SYMBOL] = performance.now();
|
|
83
|
+
logsContext.enterWith([]);
|
|
66
84
|
if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody) {
|
|
67
85
|
const contentType = request.headers.get("content-type");
|
|
68
86
|
const requestSize = parseContentLength(request.headers.get("content-length")) ?? 0;
|
|
@@ -81,29 +99,39 @@ function apitallyPlugin(config) {
|
|
|
81
99
|
const responseTime = startTime ? performance.now() - startTime : 0;
|
|
82
100
|
const requestBody = request[REQUEST_BODY_SYMBOL];
|
|
83
101
|
const requestSize = parseContentLength(request.headers.get("content-length")) ?? (requestBody == null ? void 0 : requestBody.length);
|
|
84
|
-
|
|
102
|
+
let responsePromise = request[RESPONSE_PROMISE_SYMBOL];
|
|
85
103
|
let response = request[RESPONSE_SYMBOL];
|
|
86
|
-
|
|
87
|
-
let responseSize;
|
|
104
|
+
const error = request[ERROR_SYMBOL];
|
|
88
105
|
if (!response && error && "toResponse" in error && typeof error.toResponse === "function") {
|
|
89
106
|
try {
|
|
90
107
|
response = error.toResponse();
|
|
108
|
+
const errorResponseBody = Buffer.from(await response.arrayBuffer());
|
|
109
|
+
responsePromise = Promise.resolve({
|
|
110
|
+
body: errorResponseBody,
|
|
111
|
+
size: errorResponseBody.length,
|
|
112
|
+
completed: true
|
|
113
|
+
});
|
|
91
114
|
} catch (error2) {
|
|
92
115
|
}
|
|
93
116
|
}
|
|
94
|
-
if (response instanceof Response) {
|
|
95
|
-
if (client.requestLogger.enabled && client.requestLogger.config.logResponseBody && client.requestLogger.isSupportedContentType(response.headers.get("content-type"))) {
|
|
96
|
-
responseBody = (await getResponseBody(response, false))[0];
|
|
97
|
-
responseSize = responseBody.length;
|
|
98
|
-
} else {
|
|
99
|
-
responseSize = (await measureResponseSize(response, false))[0];
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
117
|
const statusCode = (response == null ? void 0 : response.status) ?? getStatusCode(set) ?? 200;
|
|
103
|
-
|
|
118
|
+
if (!response) {
|
|
119
|
+
response = new Response(null, {
|
|
120
|
+
status: statusCode,
|
|
121
|
+
statusText: "",
|
|
122
|
+
headers: new Headers()
|
|
123
|
+
});
|
|
124
|
+
responsePromise = Promise.resolve({
|
|
125
|
+
body: void 0,
|
|
126
|
+
size: 0,
|
|
127
|
+
completed: true
|
|
128
|
+
});
|
|
129
|
+
}
|
|
104
130
|
const consumer = apitally.consumer ? consumerFromStringOrObject(apitally.consumer) : null;
|
|
105
131
|
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
106
|
-
|
|
132
|
+
responsePromise == null ? void 0 : responsePromise.then(async (capturedResponse) => {
|
|
133
|
+
const responseHeaders = (response == null ? void 0 : response.headers) ?? set.headers;
|
|
134
|
+
const responseSize = capturedResponse.completed ? capturedResponse.size : void 0;
|
|
107
135
|
client.requestCounter.addRequest({
|
|
108
136
|
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
109
137
|
method: request.method,
|
|
@@ -113,49 +141,49 @@ function apitallyPlugin(config) {
|
|
|
113
141
|
requestSize,
|
|
114
142
|
responseSize
|
|
115
143
|
});
|
|
116
|
-
if (
|
|
117
|
-
|
|
144
|
+
if (client.requestLogger.enabled) {
|
|
145
|
+
const logs = logsContext.getStore();
|
|
146
|
+
client.requestLogger.logRequest({
|
|
147
|
+
timestamp: (Date.now() - responseTime) / 1e3,
|
|
148
|
+
method: request.method,
|
|
149
|
+
path: route,
|
|
150
|
+
url: request.url,
|
|
151
|
+
headers: convertHeaders(Object.fromEntries(request.headers.entries())),
|
|
152
|
+
size: requestSize,
|
|
153
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
154
|
+
body: requestBody
|
|
155
|
+
}, {
|
|
156
|
+
statusCode,
|
|
157
|
+
responseTime: responseTime / 1e3,
|
|
158
|
+
headers: convertHeaders(responseHeaders),
|
|
159
|
+
size: responseSize,
|
|
160
|
+
body: capturedResponse.body
|
|
161
|
+
}, error, logs);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
if ((statusCode === 400 || statusCode === 422) && error instanceof ValidationError) {
|
|
165
|
+
try {
|
|
166
|
+
const parsedMessage = JSON.parse(error.message);
|
|
167
|
+
client.validationErrorCounter.addValidationError({
|
|
118
168
|
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
119
169
|
method: request.method,
|
|
120
170
|
path: route,
|
|
121
|
-
|
|
122
|
-
msg:
|
|
123
|
-
|
|
171
|
+
loc: (parsedMessage.on ?? "") + "." + (parsedMessage.property ?? ""),
|
|
172
|
+
msg: parsedMessage.message,
|
|
173
|
+
type: ""
|
|
124
174
|
});
|
|
125
|
-
}
|
|
126
|
-
if ((statusCode === 400 || statusCode === 422) && error instanceof ValidationError) {
|
|
127
|
-
try {
|
|
128
|
-
const parsedMessage = JSON.parse(error.message);
|
|
129
|
-
client.validationErrorCounter.addValidationError({
|
|
130
|
-
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
131
|
-
method: request.method,
|
|
132
|
-
path: route,
|
|
133
|
-
loc: (parsedMessage.on ?? "") + "." + (parsedMessage.property ?? ""),
|
|
134
|
-
msg: parsedMessage.message,
|
|
135
|
-
type: ""
|
|
136
|
-
});
|
|
137
|
-
} catch (error2) {
|
|
138
|
-
}
|
|
175
|
+
} catch (error2) {
|
|
139
176
|
}
|
|
140
177
|
}
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
timestamp: (Date.now() - responseTime) / 1e3,
|
|
178
|
+
if (statusCode === 500 && error) {
|
|
179
|
+
client.serverErrorCounter.addServerError({
|
|
180
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
145
181
|
method: request.method,
|
|
146
182
|
path: route,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
body: requestBody
|
|
152
|
-
}, {
|
|
153
|
-
statusCode,
|
|
154
|
-
responseTime: responseTime / 1e3,
|
|
155
|
-
headers: convertHeaders(responseHeaders),
|
|
156
|
-
size: responseSize,
|
|
157
|
-
body: responseBody
|
|
158
|
-
}, error, logs);
|
|
183
|
+
type: error.name,
|
|
184
|
+
msg: error.message,
|
|
185
|
+
traceback: error.stack || ""
|
|
186
|
+
});
|
|
159
187
|
}
|
|
160
188
|
}).onError(({ request, error }) => {
|
|
161
189
|
if (client.isEnabled() && error instanceof Error) {
|
|
@@ -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 {\n getResponseBody,\n measureResponseSize,\n teeResponse,\n teeResponseBlob,\n} 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 ERROR_SYMBOL = Symbol(\"apitally.error\");\n\ndeclare global {\n interface Request {\n [START_TIME_SYMBOL]?: number;\n [REQUEST_BODY_SYMBOL]?: Buffer;\n [RESPONSE_SYMBOL]?: Response;\n [ERROR_SYMBOL]?: Readonly<Error>;\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 const originalMapResponse = handler.mapResponse;\n const originalMapCompactResponse = handler.mapCompactResponse;\n const originalMapEarlyResponse = handler.mapEarlyResponse;\n\n const captureResponse = (\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 ) {\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 [newResponse1, newResponse2] =\n originalResponse?.constructor?.name === \"String\"\n ? teeResponseBlob(mappedResponse, originalResponse as string)\n : teeResponse(mappedResponse);\n request[RESPONSE_SYMBOL] = newResponse2;\n return newResponse1;\n } else {\n return mappedResponse;\n }\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 = captureResponse(response, mappedResponse, request);\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 = captureResponse(response, mappedResponse, request);\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 = captureResponse(response, mappedResponse, request);\n return newResponse;\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 logsContext.enterWith([]);\n request[START_TIME_SYMBOL] = performance.now();\n\n // Capture request body for logging if enabled\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 const error = request[ERROR_SYMBOL];\n let response = request[RESPONSE_SYMBOL];\n let responseBody: Buffer | undefined;\n let responseSize: number | undefined;\n\n if (\n !response &&\n error &&\n \"toResponse\" in error &&\n typeof error.toResponse === \"function\"\n ) {\n try {\n response = error.toResponse();\n } catch (error) {\n // ignore\n }\n }\n\n if (response instanceof Response) {\n if (\n client.requestLogger.enabled &&\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(\n response.headers.get(\"content-type\"),\n )\n ) {\n responseBody = (await getResponseBody(response, false))[0];\n responseSize = responseBody.length;\n } else {\n responseSize = (await measureResponseSize(response, false))[0];\n }\n }\n\n const statusCode = response?.status ?? getStatusCode(set) ?? 200;\n const responseHeaders = response?.headers ?? set.headers;\n\n const consumer = apitally.consumer\n ? consumerFromStringOrObject(apitally.consumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n if (route) {\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 // 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 // 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 ?? \"\") +\n \".\" +\n (parsedMessage.property ?? \"\"),\n msg: parsedMessage.message,\n type: \"\",\n });\n } catch (error) {\n // ignore\n }\n }\n }\n\n // Request logging\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: responseBody,\n },\n error,\n logs,\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","getResponseBody","measureResponseSize","teeResponse","teeResponseBlob","patchConsole","patchWinston","getAppInfo","START_TIME_SYMBOL","Symbol","REQUEST_BODY_SYMBOL","RESPONSE_SYMBOL","ERROR_SYMBOL","config","client","logsContext","requestLogger","enabled","captureLogs","app","handler","originalMapResponse","mapResponse","originalMapCompactResponse","mapCompactResponse","originalMapEarlyResponse","mapEarlyResponse","captureResponse","originalResponse","mappedResponse","request","Request","Response","newResponse1","newResponse2","name","wrappedMapResponse","response","set","newResponse","wrappedMapCompactResponse","wrappedMapEarlyResponse","decorate","onStart","appInfo","appVersion","setStartupData","startSync","onStop","handleShutdown","onRequest","isEnabled","method","toUpperCase","enterWith","now","logRequestBody","contentType","headers","get","requestSize","isSupportedContentType","maxBodySize","Buffer","from","clone","arrayBuffer","error","onAfterResponse","route","apitally","startTime","responseTime","requestBody","length","responseBody","responseSize","toResponse","logResponseBody","statusCode","status","getStatusCode","responseHeaders","consumer","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","path","serverErrorCounter","addServerError","type","msg","message","traceback","stack","parsedMessage","JSON","parse","validationErrorCounter","addValidationError","loc","on","property","logs","getStore","logRequest","timestamp","Date","url","Object","fromEntries","entries","size","body","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\");\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"]}
|