{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Request, RequestHandler } from \"express\";\nimport { XMLParser } from \"fast-xml-parser\";\nimport { randomUUID } from \"node:crypto\";\n\ndeclare global {\n  // eslint-disable-next-line @typescript-eslint/no-namespace\n  namespace Express {\n    interface Request {\n      requestId: string;\n    }\n  }\n}\n\ntype LoggerLevel = \"info\" | \"warn\" | \"error\";\n\ntype LoggerFunction = (\n  context: Record<string, unknown>,\n  message: string\n) => void;\n\ntype Logger = Record<LoggerLevel, LoggerFunction>;\n\ninterface Config {\n  logger?: Logger;\n}\n\nconst parseBody = (body: unknown, contentType?: string) => {\n  if (typeof body === \"object\" || typeof body === \"undefined\") return body;\n\n  const parsedContentType = contentType?.split(\"; \")[0];\n\n  switch (parsedContentType) {\n    case \"image/svg+xml\":\n    case \"application/xml\":\n    case \"application/atom+xml\":\n    case \"text/xml\": {\n      const parser = new XMLParser({\n        ignoreDeclaration: true,\n        parseTagValue: false,\n      });\n      const value = parser.parse(body as string);\n      return value;\n    }\n    case \"application/json\":\n      return JSON.parse(body as string);\n    default:\n      return body;\n  }\n};\n\nconst getLoggerLevel = (statusCode: number): LoggerLevel => {\n  const level =\n    statusCode >= 500 ? \"error\" : statusCode >= 400 ? \"warn\" : \"info\";\n\n  return level;\n};\n\nconst getRequestIp = (req: Request): string =>\n  (req.headers[\"x-envoy-external-address\"] as string) ?? req.ip ?? \"0.0.0.0\";\n\nconst requestLogger =\n  (config?: Config): RequestHandler =>\n  (req, res, next) => {\n    const logger = config?.logger ?? {\n      info: console.log,\n      warn: console.warn,\n      error: console.error,\n    };\n\n    const requestId = randomUUID();\n    req.requestId = requestId;\n\n    let rawResponseBody: unknown;\n    let responseBody: unknown;\n\n    const send = res.send;\n    res.send = (body) => {\n      const contentType = res.getHeader(\"Content-Type\");\n\n      rawResponseBody = body;\n      responseBody = parseBody(\n        body,\n        typeof contentType === \"string\" ? contentType : undefined\n      );\n\n      res.send = send;\n      return res.send(body);\n    };\n\n    res.on(\"finish\", () => {\n      const level = getLoggerLevel(res.statusCode);\n      const ip = getRequestIp(req);\n\n      logger[level](\n        { requestId },\n        `[${ip}] [${req.method}] ${\n          req.originalUrl\n        } ${req.protocol.toUpperCase()} ${req.httpVersion} ${res.statusCode}`\n      );\n\n      const requestContentType = req.header(\"Content-Type\");\n      const requestBody = parseBody(req.body, requestContentType);\n\n      logger[level](\n        { requestId, type: \"REQUEST\" },\n        JSON.stringify({\n          params: req.params,\n          query: req.query,\n          headers: req.headers,\n          cookies: req.cookies,\n          rawBody: req.body,\n          body: requestBody,\n        })\n      );\n\n      logger[level](\n        { requestId, type: \"RESPONSE\" },\n        JSON.stringify({\n          rawBody: rawResponseBody,\n          body: responseBody,\n        })\n      );\n    });\n\n    next();\n  };\n\nexport type { Logger, Config };\nexport default requestLogger;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,6BAA0B;AAC1B,yBAA2B;AAwB3B,IAAM,YAAY,CAAC,MAAe,gBAAyB;AACzD,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,YAAa,QAAO;AAEpE,QAAM,oBAAoB,aAAa,MAAM,IAAI,EAAE,CAAC;AAEpD,UAAQ,mBAAmB;AAAA,IACzB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK,YAAY;AACf,YAAM,SAAS,IAAI,iCAAU;AAAA,QAC3B,mBAAmB;AAAA,QACnB,eAAe;AAAA,MACjB,CAAC;AACD,YAAM,QAAQ,OAAO,MAAM,IAAc;AACzC,aAAO;AAAA,IACT;AAAA,IACA,KAAK;AACH,aAAO,KAAK,MAAM,IAAc;AAAA,IAClC;AACE,aAAO;AAAA,EACX;AACF;AAEA,IAAM,iBAAiB,CAAC,eAAoC;AAC1D,QAAM,QACJ,cAAc,MAAM,UAAU,cAAc,MAAM,SAAS;AAE7D,SAAO;AACT;AAEA,IAAM,eAAe,CAAC,QACnB,IAAI,QAAQ,0BAA0B,KAAgB,IAAI,MAAM;AAEnE,IAAM,gBACJ,CAAC,WACD,CAAC,KAAK,KAAK,SAAS;AAClB,QAAM,SAAS,QAAQ,UAAU;AAAA,IAC/B,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,IACd,OAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,gBAAY,+BAAW;AAC7B,MAAI,YAAY;AAEhB,MAAI;AACJ,MAAI;AAEJ,QAAM,OAAO,IAAI;AACjB,MAAI,OAAO,CAAC,SAAS;AACnB,UAAM,cAAc,IAAI,UAAU,cAAc;AAEhD,sBAAkB;AAClB,mBAAe;AAAA,MACb;AAAA,MACA,OAAO,gBAAgB,WAAW,cAAc;AAAA,IAClD;AAEA,QAAI,OAAO;AACX,WAAO,IAAI,KAAK,IAAI;AAAA,EACtB;AAEA,MAAI,GAAG,UAAU,MAAM;AACrB,UAAM,QAAQ,eAAe,IAAI,UAAU;AAC3C,UAAM,KAAK,aAAa,GAAG;AAE3B,WAAO,KAAK;AAAA,MACV,EAAE,UAAU;AAAA,MACZ,IAAI,EAAE,MAAM,IAAI,MAAM,KACpB,IAAI,WACN,IAAI,IAAI,SAAS,YAAY,CAAC,IAAI,IAAI,WAAW,IAAI,IAAI,UAAU;AAAA,IACrE;AAEA,UAAM,qBAAqB,IAAI,OAAO,cAAc;AACpD,UAAM,cAAc,UAAU,IAAI,MAAM,kBAAkB;AAE1D,WAAO,KAAK;AAAA,MACV,EAAE,WAAW,MAAM,UAAU;AAAA,MAC7B,KAAK,UAAU;AAAA,QACb,QAAQ,IAAI;AAAA,QACZ,OAAO,IAAI;AAAA,QACX,SAAS,IAAI;AAAA,QACb,SAAS,IAAI;AAAA,QACb,SAAS,IAAI;AAAA,QACb,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAEA,WAAO,KAAK;AAAA,MACV,EAAE,WAAW,MAAM,WAAW;AAAA,MAC9B,KAAK,UAAU;AAAA,QACb,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,OAAK;AACP;AAGF,IAAO,gBAAQ;","names":[]}