{"version":3,"sources":["../../src/adonisjs/middleware.ts"],"sourcesContent":["import { HttpContext } from \"@adonisjs/core/http\";\nimport { NextFn } from \"@adonisjs/core/types/http\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { OutgoingHttpHeaders } from \"node:http\";\nimport { performance } from \"node:perf_hooks\";\n\nimport type { 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 type { ApitallyConsumer } from \"../common/types.js\";\n\ndeclare module \"@adonisjs/core/http\" {\n  interface HttpContext {\n    apitallyConsumer?: ApitallyConsumer | string;\n    apitallyError?: Error;\n  }\n}\n\nexport default class ApitallyMiddleware {\n  async handle(ctx: HttpContext, next: NextFn) {\n    const client: ApitallyClient =\n      await ctx.containerResolver.make(\"apitallyClient\");\n    const logsContext: AsyncLocalStorage<LogRecord[]> =\n      await ctx.containerResolver.make(\"apitallyLogsContext\");\n\n    if (\n      !client.isEnabled() ||\n      ctx.request.method().toUpperCase() === \"OPTIONS\"\n    ) {\n      await next();\n      return;\n    }\n\n    return logsContext.run([], async () => {\n      const path = ctx.route?.pattern;\n      const timestamp = Date.now() / 1000;\n      const startTime = performance.now();\n\n      const spanHandle = client.spanCollector.startSpan();\n      await spanHandle.runInContext(next);\n\n      const responseTime = performance.now() - startTime;\n      spanHandle.setName(`${ctx.request.method()} ${path}`);\n      const spans = spanHandle.end();\n      const traceId = spanHandle.traceId;\n\n      const requestSize = parseContentLength(\n        ctx.request.header(\"content-length\"),\n      );\n      const requestContentType = ctx.request.header(\"content-type\")?.toString();\n      let responseStatus = ctx.response.getStatus();\n      let responseHeaders = ctx.response.getHeaders();\n      let responseSize: number | undefined;\n      let responseContentType: string | undefined;\n\n      const consumer = ctx.apitallyConsumer\n        ? consumerFromStringOrObject(ctx.apitallyConsumer)\n        : null;\n      client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n      const onWriteHead = (\n        statusCode: number,\n        headers: OutgoingHttpHeaders,\n      ) => {\n        responseStatus = statusCode;\n        responseHeaders = headers;\n        responseSize = parseContentLength(headers[\"content-length\"]);\n        responseContentType = headers[\"content-type\"]?.toString();\n\n        if (path) {\n          client.requestCounter.addRequest({\n            consumer: consumer?.identifier,\n            method: ctx.request.method(),\n            path,\n            statusCode: responseStatus,\n            responseTime,\n            requestSize,\n            responseSize,\n          });\n\n          if (\n            responseStatus === 422 &&\n            ctx.apitallyError &&\n            \"code\" in ctx.apitallyError &&\n            \"messages\" in ctx.apitallyError &&\n            ctx.apitallyError.code === \"E_VALIDATION_ERROR\" &&\n            Array.isArray(ctx.apitallyError.messages)\n          ) {\n            ctx.apitallyError.messages.forEach((message) => {\n              client.validationErrorCounter.addValidationError({\n                consumer: consumer?.identifier,\n                method: ctx.request.method(),\n                path,\n                loc: message.field,\n                msg: message.message,\n                type: message.rule,\n              });\n            });\n          }\n\n          if (responseStatus === 500 && ctx.apitallyError) {\n            client.serverErrorCounter.addServerError({\n              consumer: consumer?.identifier,\n              method: ctx.request.method(),\n              path,\n              type: ctx.apitallyError.name,\n              msg: ctx.apitallyError.message,\n              traceback: ctx.apitallyError.stack || \"\",\n            });\n          }\n        }\n      };\n\n      // Capture the final status code and response headers just before they are sent\n      const originalWriteHead = ctx.response.response.writeHead;\n      ctx.response.response.writeHead = (...args: any) => {\n        originalWriteHead.apply(ctx.response.response, args);\n        onWriteHead(args[0], typeof args[1] === \"string\" ? args[2] : args[1]);\n        return ctx.response.response;\n      };\n\n      if (client.requestLogger.enabled) {\n        const logs = logsContext.getStore();\n\n        const onEnd = (chunk: any) => {\n          const requestBody =\n            client.requestLogger.config.logRequestBody &&\n            client.requestLogger.isSupportedContentType(requestContentType)\n              ? ctx.request.raw()\n              : undefined;\n          const responseBody =\n            client.requestLogger.config.logResponseBody &&\n            client.requestLogger.isSupportedContentType(responseContentType)\n              ? chunk\n              : undefined;\n\n          client.requestLogger.logRequest(\n            {\n              timestamp,\n              method: ctx.request.method(),\n              path,\n              url: ctx.request.completeUrl(true),\n              headers: convertHeaders(ctx.request.headers()),\n              size: requestSize,\n              consumer: consumer?.identifier,\n              body: requestBody ? Buffer.from(requestBody) : undefined,\n            },\n            {\n              statusCode: responseStatus,\n              responseTime: responseTime / 1000,\n              headers: convertHeaders(responseHeaders),\n              size: responseSize,\n              body: responseBody ? Buffer.from(responseBody) : undefined,\n            },\n            ctx.apitallyError,\n            logs,\n            spans,\n            traceId,\n          );\n        };\n\n        // Capture the final response body just before it is sent\n        const originalEnd = ctx.response.response.end;\n        ctx.response.response.end = (...args: any) => {\n          originalEnd.apply(ctx.response.response, args);\n          onEnd(typeof args[0] !== \"function\" ? args[0] : undefined);\n          return ctx.response.response;\n        };\n      }\n    });\n  }\n}\n"],"mappings":"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","names":["ApitallyMiddleware","handle","ctx","next","client","containerResolver","make","logsContext","isEnabled","request","method","toUpperCase","run","path","route","pattern","timestamp","Date","now","startTime","performance","spanHandle","spanCollector","startSpan","runInContext","responseTime","setName","spans","end","traceId","requestSize","parseContentLength","header","requestContentType","toString","responseStatus","response","getStatus","responseHeaders","getHeaders","responseSize","responseContentType","consumer","apitallyConsumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","onWriteHead","statusCode","headers","_a","requestCounter","addRequest","identifier","apitallyError","code","Array","isArray","messages","forEach","message","validationErrorCounter","addValidationError","loc","field","msg","type","rule","serverErrorCounter","addServerError","name","traceback","stack","originalWriteHead","writeHead","args","apply","requestLogger","enabled","logs","getStore","onEnd","chunk","requestBody","config","logRequestBody","isSupportedContentType","raw","undefined","responseBody","logResponseBody","logRequest","url","completeUrl","convertHeaders","size","body","Buffer","from","originalEnd"]}