{"version":3,"sources":["../src/nestjs.ts","../src/utils.ts"],"sourcesContent":["import type { SnapWyrConfig, LogEntry } from '@snapwyr/core';\nimport { generateRequestId } from '@snapwyr/core';\nimport { logRequest } from './utils.js';\nimport { tap, Observable } from 'rxjs';\n\ninterface ExecutionContext {\n  switchToHttp(): {\n    getRequest<T = unknown>(): T;\n    getResponse<T = unknown>(): T;\n  };\n}\n\ninterface CallHandler<T = unknown> {\n  handle(): Observable<T>;\n}\n\ninterface NestInterceptor<T = unknown, R = unknown> {\n  intercept(context: ExecutionContext, next: CallHandler<T>): Observable<R>;\n}\ninterface HttpRequest {\n  method: string;\n  url: string;\n  originalUrl?: string;\n  body?: unknown;\n}\n\ninterface HttpResponse {\n  statusCode: number;\n  setHeader?: (name: string, value: string) => void;\n}\n\nexport function SnapwyrInterceptor(\n  config: SnapWyrConfig = {}\n): NestInterceptor {\n  return {\n    intercept(context, next) {\n      if (config.enabled === false) return next.handle();\n\n      const id = generateRequestId();\n      const request = context.switchToHttp().getRequest<HttpRequest>();\n      const response = context.switchToHttp().getResponse<HttpResponse>();\n      const startTime = Date.now();\n      const method = request.method;\n      const url = request.url || request.originalUrl || '';\n\n      if (config.requestId && response.setHeader)\n        try {\n          response.setHeader('X-Request-ID', id);\n        } catch {}\n\n      return next.handle().pipe(\n        tap({\n          next: (data: unknown) => {\n            const duration = Date.now() - startTime;\n            const status = response.statusCode || 200;\n            if (\n              config.statusCodes &&\n              config.statusCodes.length > 0 &&\n              !config.statusCodes.includes(status)\n            )\n              return;\n\n            let requestBody: string | undefined,\n              responseBody: string | undefined;\n            if (config.logBody) {\n              try {\n                if (request.body) {\n                  requestBody =\n                    typeof request.body === 'string'\n                      ? request.body\n                      : JSON.stringify(request.body);\n                  if (config.bodySizeLimit)\n                    requestBody = requestBody.slice(0, config.bodySizeLimit);\n                }\n                if (data) {\n                  responseBody =\n                    typeof data === 'string' ? data : JSON.stringify(data);\n                  if (config.bodySizeLimit)\n                    responseBody = responseBody.slice(0, config.bodySizeLimit);\n                }\n              } catch {}\n            }\n            logRequest({\n              id,\n              method,\n              status,\n              duration,\n              url,\n              startTime,\n              config,\n              requestBody: config.logBody ? requestBody : undefined,\n              responseBody: config.logBody ? responseBody : undefined,\n            });\n          },\n          error: (error: unknown) => {\n            const duration = Date.now() - startTime;\n            const err = error as { status?: number; message?: string };\n            const status = err.status || 500;\n            if (\n              config.statusCodes &&\n              config.statusCodes.length > 0 &&\n              !config.statusCodes.includes(status)\n            )\n              return;\n            logRequest({\n              id,\n              method,\n              status,\n              duration,\n              url,\n              startTime,\n              config,\n              error: err.message || String(error),\n            });\n          },\n        })\n      );\n    },\n  };\n}\n\nexport type { SnapWyrConfig, LogEntry };\n","import type { SnapWyrConfig, LogEntry } from '@snapwyr/core';\nimport {\n  snapwyr as coreEmitter,\n  getByteSize,\n  formatBytes,\n  redactSensitiveData,\n} from '@snapwyr/core';\n\nexport interface LogParams {\n  id: string;\n  method: string;\n  status: number;\n  duration: number;\n  url: string;\n  startTime: number;\n  config: SnapWyrConfig;\n  requestBody?: string;\n  responseBody?: string;\n  error?: string;\n}\n\nexport function logRequest(params: LogParams): void {\n  const { id, method, status, duration, url, startTime, config, error } =\n    params;\n  let { requestBody, responseBody } = params;\n\n  if (config.silent) return;\n\n  const showTimestamp = config.showTimestamp !== false;\n  const format = config.format || 'pretty';\n  const slowThreshold = config.slowThreshold ?? 1000;\n  const isSlow = duration >= slowThreshold;\n\n  const requestSize = requestBody ? getByteSize(requestBody) : 0;\n  const responseSize = responseBody ? getByteSize(responseBody) : 0;\n  const totalSize = requestSize + responseSize;\n\n  if (config.redact && config.redact.length > 0) {\n    if (requestBody)\n      requestBody = redactSensitiveData(requestBody, config.redact);\n    if (responseBody)\n      responseBody = redactSensitiveData(responseBody, config.redact);\n  }\n\n  const logEntry: LogEntry = {\n    id,\n    timestamp: new Date(startTime).toISOString(),\n    method: method.toUpperCase(),\n    url,\n    status,\n    duration,\n    slow: isSlow,\n  };\n\n  if (config.prefix) logEntry.prefix = config.prefix;\n  if (error) logEntry.error = error;\n  if (requestBody) logEntry.requestBody = requestBody;\n  if (responseBody) logEntry.responseBody = responseBody;\n  if (config.sizeTracking) {\n    logEntry.requestSize = requestSize;\n    logEntry.responseSize = responseSize;\n    logEntry.totalSize = totalSize;\n  }\n\n  try {\n    coreEmitter.emit('request', {\n      id,\n      method: method.toUpperCase(),\n      url,\n      status,\n      duration,\n      timestamp: startTime,\n      requestBody,\n      responseBody,\n      error,\n      requestSize: config.sizeTracking ? requestSize : undefined,\n      responseSize: config.sizeTracking ? responseSize : undefined,\n      direction: 'incoming',\n    });\n  } catch {}\n\n  if (config.transport) {\n    config.transport(logEntry);\n  }\n\n  if (format === 'json') {\n    console.log(JSON.stringify(logEntry));\n    return;\n  }\n\n  const useEmoji = config.emoji === true;\n  let statusEmoji = '';\n  if (useEmoji) {\n    if (error || status >= 500) statusEmoji = '✗ ';\n    else if (status >= 400) statusEmoji = '⚠ ';\n    else if (status >= 300) statusEmoji = '↪ ';\n    else statusEmoji = '✓ ';\n  }\n\n  const statusColor =\n    status >= 500\n      ? '\\x1b[31m'\n      : status >= 400\n        ? '\\x1b[33m'\n        : status >= 300\n          ? '\\x1b[36m'\n          : '\\x1b[32m';\n  const durationColor = isSlow\n    ? '\\x1b[31m'\n    : duration < 100\n      ? '\\x1b[32m'\n      : '\\x1b[33m';\n  const methodColors: Record<string, string> = {\n    GET: '\\x1b[34m',\n    POST: '\\x1b[32m',\n    PUT: '\\x1b[33m',\n    PATCH: '\\x1b[35m',\n    DELETE: '\\x1b[31m',\n  };\n  const methodColor = methodColors[method] || '';\n  const reset = '\\x1b[0m';\n  const dim = '\\x1b[2m';\n  const bold = '\\x1b[1m';\n  const timestamp = showTimestamp\n    ? new Date(startTime).toISOString().slice(11, 23) + ' '\n    : '';\n  const slowIndicator = isSlow ? ` ${bold}[SLOW]${reset}` : '';\n  const requestIdDisplay = config.requestId ? `${dim}[${id}]${reset} ` : '';\n  const sizeDisplay = config.sizeTracking\n    ? `${dim}${formatBytes(totalSize)}${reset} `\n    : '';\n\n  const parts = [\n    config.prefix ? `${dim}${config.prefix} ${reset}` : '',\n    requestIdDisplay,\n    `${dim}${timestamp}${reset}`,\n    `${methodColor}${method.padEnd(6)}${reset}`,\n    `${statusColor}${statusEmoji}${status}${reset}`,\n    `${durationColor}${duration}ms${reset}${slowIndicator}`,\n    sizeDisplay,\n    `${dim}${url}${reset}`,\n  ].filter(Boolean);\n\n  if (error) parts.push(`\\n  ${dim}Error: ${error}${reset}`);\n  if (requestBody) parts.push(`\\n  ${dim}Request: ${requestBody}${reset}`);\n  if (responseBody) parts.push(`\\n  ${dim}Response: ${responseBody}${reset}`);\n\n  console.log(parts.join(' '));\n}\n"],"mappings":";AACA,SAAS,yBAAyB;;;ACAlC;AAAA,EACE,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAeA,SAAS,WAAW,QAAyB;AAClD,QAAM,EAAE,IAAI,QAAQ,QAAQ,UAAU,KAAK,WAAW,QAAQ,MAAM,IAClE;AACF,MAAI,EAAE,aAAa,aAAa,IAAI;AAEpC,MAAI,OAAO,OAAQ;AAEnB,QAAM,gBAAgB,OAAO,kBAAkB;AAC/C,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,gBAAgB,OAAO,iBAAiB;AAC9C,QAAM,SAAS,YAAY;AAE3B,QAAM,cAAc,cAAc,YAAY,WAAW,IAAI;AAC7D,QAAM,eAAe,eAAe,YAAY,YAAY,IAAI;AAChE,QAAM,YAAY,cAAc;AAEhC,MAAI,OAAO,UAAU,OAAO,OAAO,SAAS,GAAG;AAC7C,QAAI;AACF,oBAAc,oBAAoB,aAAa,OAAO,MAAM;AAC9D,QAAI;AACF,qBAAe,oBAAoB,cAAc,OAAO,MAAM;AAAA,EAClE;AAEA,QAAM,WAAqB;AAAA,IACzB;AAAA,IACA,WAAW,IAAI,KAAK,SAAS,EAAE,YAAY;AAAA,IAC3C,QAAQ,OAAO,YAAY;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AAEA,MAAI,OAAO,OAAQ,UAAS,SAAS,OAAO;AAC5C,MAAI,MAAO,UAAS,QAAQ;AAC5B,MAAI,YAAa,UAAS,cAAc;AACxC,MAAI,aAAc,UAAS,eAAe;AAC1C,MAAI,OAAO,cAAc;AACvB,aAAS,cAAc;AACvB,aAAS,eAAe;AACxB,aAAS,YAAY;AAAA,EACvB;AAEA,MAAI;AACF,gBAAY,KAAK,WAAW;AAAA,MAC1B;AAAA,MACA,QAAQ,OAAO,YAAY;AAAA,MAC3B;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,OAAO,eAAe,cAAc;AAAA,MACjD,cAAc,OAAO,eAAe,eAAe;AAAA,MACnD,WAAW;AAAA,IACb,CAAC;AAAA,EACH,QAAQ;AAAA,EAAC;AAET,MAAI,OAAO,WAAW;AACpB,WAAO,UAAU,QAAQ;AAAA,EAC3B;AAEA,MAAI,WAAW,QAAQ;AACrB,YAAQ,IAAI,KAAK,UAAU,QAAQ,CAAC;AACpC;AAAA,EACF;AAEA,QAAM,WAAW,OAAO,UAAU;AAClC,MAAI,cAAc;AAClB,MAAI,UAAU;AACZ,QAAI,SAAS,UAAU,IAAK,eAAc;AAAA,aACjC,UAAU,IAAK,eAAc;AAAA,aAC7B,UAAU,IAAK,eAAc;AAAA,QACjC,eAAc;AAAA,EACrB;AAEA,QAAM,cACJ,UAAU,MACN,aACA,UAAU,MACR,aACA,UAAU,MACR,aACA;AACV,QAAM,gBAAgB,SAClB,aACA,WAAW,MACT,aACA;AACN,QAAM,eAAuC;AAAA,IAC3C,KAAK;AAAA,IACL,MAAM;AAAA,IACN,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AACA,QAAM,cAAc,aAAa,MAAM,KAAK;AAC5C,QAAM,QAAQ;AACd,QAAM,MAAM;AACZ,QAAM,OAAO;AACb,QAAM,YAAY,gBACd,IAAI,KAAK,SAAS,EAAE,YAAY,EAAE,MAAM,IAAI,EAAE,IAAI,MAClD;AACJ,QAAM,gBAAgB,SAAS,IAAI,IAAI,SAAS,KAAK,KAAK;AAC1D,QAAM,mBAAmB,OAAO,YAAY,GAAG,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM;AACvE,QAAM,cAAc,OAAO,eACvB,GAAG,GAAG,GAAG,YAAY,SAAS,CAAC,GAAG,KAAK,MACvC;AAEJ,QAAM,QAAQ;AAAA,IACZ,OAAO,SAAS,GAAG,GAAG,GAAG,OAAO,MAAM,IAAI,KAAK,KAAK;AAAA,IACpD;AAAA,IACA,GAAG,GAAG,GAAG,SAAS,GAAG,KAAK;AAAA,IAC1B,GAAG,WAAW,GAAG,OAAO,OAAO,CAAC,CAAC,GAAG,KAAK;AAAA,IACzC,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,KAAK;AAAA,IAC7C,GAAG,aAAa,GAAG,QAAQ,KAAK,KAAK,GAAG,aAAa;AAAA,IACrD;AAAA,IACA,GAAG,GAAG,GAAG,GAAG,GAAG,KAAK;AAAA,EACtB,EAAE,OAAO,OAAO;AAEhB,MAAI,MAAO,OAAM,KAAK;AAAA,IAAO,GAAG,UAAU,KAAK,GAAG,KAAK,EAAE;AACzD,MAAI,YAAa,OAAM,KAAK;AAAA,IAAO,GAAG,YAAY,WAAW,GAAG,KAAK,EAAE;AACvE,MAAI,aAAc,OAAM,KAAK;AAAA,IAAO,GAAG,aAAa,YAAY,GAAG,KAAK,EAAE;AAE1E,UAAQ,IAAI,MAAM,KAAK,GAAG,CAAC;AAC7B;;;ADjJA,SAAS,WAAuB;AA4BzB,SAAS,mBACd,SAAwB,CAAC,GACR;AACjB,SAAO;AAAA,IACL,UAAU,SAAS,MAAM;AACvB,UAAI,OAAO,YAAY,MAAO,QAAO,KAAK,OAAO;AAEjD,YAAM,KAAK,kBAAkB;AAC7B,YAAM,UAAU,QAAQ,aAAa,EAAE,WAAwB;AAC/D,YAAM,WAAW,QAAQ,aAAa,EAAE,YAA0B;AAClE,YAAM,YAAY,KAAK,IAAI;AAC3B,YAAM,SAAS,QAAQ;AACvB,YAAM,MAAM,QAAQ,OAAO,QAAQ,eAAe;AAElD,UAAI,OAAO,aAAa,SAAS;AAC/B,YAAI;AACF,mBAAS,UAAU,gBAAgB,EAAE;AAAA,QACvC,QAAQ;AAAA,QAAC;AAEX,aAAO,KAAK,OAAO,EAAE;AAAA,QACnB,IAAI;AAAA,UACF,MAAM,CAAC,SAAkB;AACvB,kBAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,kBAAM,SAAS,SAAS,cAAc;AACtC,gBACE,OAAO,eACP,OAAO,YAAY,SAAS,KAC5B,CAAC,OAAO,YAAY,SAAS,MAAM;AAEnC;AAEF,gBAAI,aACF;AACF,gBAAI,OAAO,SAAS;AAClB,kBAAI;AACF,oBAAI,QAAQ,MAAM;AAChB,gCACE,OAAO,QAAQ,SAAS,WACpB,QAAQ,OACR,KAAK,UAAU,QAAQ,IAAI;AACjC,sBAAI,OAAO;AACT,kCAAc,YAAY,MAAM,GAAG,OAAO,aAAa;AAAA,gBAC3D;AACA,oBAAI,MAAM;AACR,iCACE,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AACvD,sBAAI,OAAO;AACT,mCAAe,aAAa,MAAM,GAAG,OAAO,aAAa;AAAA,gBAC7D;AAAA,cACF,QAAQ;AAAA,cAAC;AAAA,YACX;AACA,uBAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,aAAa,OAAO,UAAU,cAAc;AAAA,cAC5C,cAAc,OAAO,UAAU,eAAe;AAAA,YAChD,CAAC;AAAA,UACH;AAAA,UACA,OAAO,CAAC,UAAmB;AACzB,kBAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,kBAAM,MAAM;AACZ,kBAAM,SAAS,IAAI,UAAU;AAC7B,gBACE,OAAO,eACP,OAAO,YAAY,SAAS,KAC5B,CAAC,OAAO,YAAY,SAAS,MAAM;AAEnC;AACF,uBAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,OAAO,IAAI,WAAW,OAAO,KAAK;AAAA,YACpC,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;","names":[]}