{"version":3,"sources":["../src/fastify.ts","../src/utils.ts"],"sourcesContent":["import type { SnapWyrConfig, LogEntry } from '@snapwyr/core';\nimport { generateRequestId } from '@snapwyr/core';\nimport { logRequest } from './utils.js';\n\nconst requestTimingMap = new WeakMap<\n  object,\n  { id: string; startTime: number }\n>();\n\n/**\n * @example\n * ```ts\n * await fastify.register(snapwyr, { logBody: true });\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction snapwyrPlugin(fastify: any, config: SnapWyrConfig, done: () => void) {\n  if (config.enabled === false) {\n    done();\n    return;\n  }\n\n  fastify.addHook(\n    'onRequest',\n    (request: any, reply: any, hookDone: () => void) => {\n      const id = generateRequestId();\n      requestTimingMap.set(request, { id, startTime: Date.now() });\n\n      if (config.requestId) {\n        try {\n          reply.header('X-Request-ID', id);\n        } catch {}\n      }\n      hookDone();\n    }\n  );\n\n  fastify.addHook(\n    'onResponse',\n    (request: any, reply: any, hookDone: () => void) => {\n      const timing = requestTimingMap.get(request) || {\n        id: generateRequestId(),\n        startTime: Date.now(),\n      };\n      const duration = Date.now() - timing.startTime;\n      const status = reply.statusCode;\n\n      requestTimingMap.delete(request);\n\n      if (\n        config.statusCodes &&\n        config.statusCodes.length > 0 &&\n        !config.statusCodes.includes(status)\n      ) {\n        hookDone();\n        return;\n      }\n\n      if (config.errorsOnly && status < 400) {\n        hookDone();\n        return;\n      }\n\n      let requestBody: string | undefined;\n      if (config.logBody && request.body) {\n        try {\n          const body =\n            typeof request.body === 'string'\n              ? request.body\n              : JSON.stringify(request.body);\n          requestBody = config.bodySizeLimit\n            ? body.slice(0, config.bodySizeLimit)\n            : body;\n        } catch {}\n      }\n\n      logRequest({\n        id: timing.id,\n        method: request.method,\n        status,\n        duration,\n        url: request.url,\n        startTime: timing.startTime,\n        config,\n        requestBody: config.logBody ? requestBody : undefined,\n      });\n\n      hookDone();\n    }\n  );\n\n  done();\n}\n\nObject.defineProperty(snapwyrPlugin, Symbol.for('skip-override'), {\n  value: true,\n  writable: false,\n});\n\nexport const snapwyr = snapwyrPlugin;\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":"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","names":[]}