{"version":3,"sources":["../../src/logger/types.ts","../../src/logger/SmartLogger.ts","../../src/logger/middleware/express.ts"],"names":["AsyncLocalStorage","randomUUID"],"mappings":"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","file":"index.cjs","sourcesContent":["export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\nexport const LOG_LEVELS: Record<LogLevel, number> = {\n  debug: 0,\n  info: 1,\n  warn: 2,\n  error: 3,\n  silent: 4,\n};\n\nexport interface LogContext {\n  requestId?: string;\n  userId?: string;\n  traceId?: string;\n  [key: string]: unknown;\n}\n\nexport interface LogEntry {\n  level: LogLevel;\n  message: string;\n  timestamp: string;\n  context: LogContext;\n  error?: SerializedError;\n  data?: Record<string, unknown>;\n}\n\nexport interface SerializedError {\n  name: string;\n  message: string;\n  stack: string | undefined;\n  code: string | number | undefined;\n}\n\nexport interface Transport {\n  write(entry: LogEntry): void;\n}\n\nexport interface SmartLoggerOptions {\n  /** Minimum log level to output (default: 'info') */\n  level?: LogLevel;\n  /** Service/app name added to every log entry */\n  serviceName?: string;\n  /** Custom transports (default: console JSON transport) */\n  transports?: Transport[];\n  /**\n   * Fields to automatically redact from log output.\n   * Values are replaced with '[REDACTED]'.\n   * Default: ['password', 'token', 'secret', 'authorization', 'cookie', 'apiKey', 'api_key']\n   */\n  redactFields?: string[];\n  /** Pretty-print logs (useful in development, never in production) */\n  prettyPrint?: boolean;\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { randomUUID } from 'node:crypto';\nimport type {\n  LogLevel,\n  LogContext,\n  LogEntry,\n  SerializedError,\n  SmartLoggerOptions,\n  Transport,\n} from './types.js';\nimport { LOG_LEVELS } from './types.js';\n\n// Default fields to redact — prevent secrets from leaking into logs\nconst DEFAULT_REDACT_FIELDS = [\n  'password',\n  'passwd',\n  'token',\n  'accessToken',\n  'access_token',\n  'refreshToken',\n  'refresh_token',\n  'secret',\n  'authorization',\n  'cookie',\n  'apiKey',\n  'api_key',\n  'privateKey',\n  'private_key',\n  'cvv',\n  'cardNumber',\n  'card_number',\n];\n\n/**\n * Console transport — outputs structured JSON (or pretty-printed) logs.\n */\nclass ConsoleTransport implements Transport {\n  constructor(private readonly pretty: boolean) {}\n\n  write(entry: LogEntry): void {\n    const output = this.pretty ? JSON.stringify(entry, null, 2) : JSON.stringify(entry);\n    if (entry.level === 'error' || entry.level === 'warn') {\n      process.stderr.write(output + '\\n');\n    } else {\n      process.stdout.write(output + '\\n');\n    }\n  }\n}\n\n/**\n * SmartLogger — structured, context-aware logging for Node.js backends.\n *\n * Key features:\n * - **Async context propagation**: request ID automatically flows through\n *   all async calls without manual passing — powered by AsyncLocalStorage.\n * - **Automatic redaction**: sensitive fields (passwords, tokens, etc.) are\n *   replaced with '[REDACTED]' before writing to any transport.\n * - **Structured JSON output**: machine-parseable logs, OpenTelemetry-ready.\n * - **Child loggers**: create scoped loggers that inherit parent context.\n *\n * @example\n * const logger = new SmartLogger({ serviceName: 'api', level: 'info' });\n *\n * // In Express middleware:\n * app.use((req, res, next) => {\n *   logger.runWithContext({ requestId: req.headers['x-request-id'] ?? randomUUID() }, next);\n * });\n *\n * // Anywhere in the async call chain:\n * logger.info('User created', { userId: '123' });\n * // → {\"level\":\"info\",\"message\":\"User created\",\"context\":{\"requestId\":\"abc-123\"},\"data\":{\"userId\":\"123\"}}\n */\nexport class SmartLogger {\n  private readonly level: LogLevel;\n  private readonly serviceName: string | undefined;\n  private readonly transports: Transport[];\n  private readonly redactFields: Set<string>;\n  private readonly storage = new AsyncLocalStorage<LogContext>();\n  private readonly baseContext: LogContext;\n\n  constructor(options: SmartLoggerOptions = {}) {\n    this.level = options.level ?? 'info';\n    this.serviceName = options.serviceName;\n    this.transports =\n      options.transports ?? [new ConsoleTransport(options.prettyPrint ?? false)];\n    this.redactFields = new Set([\n      ...DEFAULT_REDACT_FIELDS,\n      ...(options.redactFields ?? []),\n    ]);\n    this.baseContext = this.serviceName ? { service: this.serviceName } : {};\n  }\n\n  // ─── Context management ─────────────────────────────────────────────────────\n\n  /**\n   * Run `fn` with the given context available to all nested async calls.\n   * Use this in middleware to inject requestId, userId, traceId, etc.\n   */\n  runWithContext<T>(context: LogContext, fn: () => T): T {\n    const merged = { ...this.currentContext, ...context };\n    return this.storage.run(merged, fn);\n  }\n\n  /**\n   * Generate a new requestId and run `fn` within that context.\n   * Convenience wrapper for runWithContext.\n   */\n  withRequestId<T>(fn: () => T, extraContext: LogContext = {}): T {\n    return this.runWithContext({ requestId: randomUUID(), ...extraContext }, fn);\n  }\n\n  /** Get the context bound to the current async scope */\n  get currentContext(): LogContext {\n    return this.storage.getStore() ?? this.baseContext;\n  }\n\n  // ─── Logging methods ─────────────────────────────────────────────────────────\n\n  debug(message: string, data?: Record<string, unknown>): void {\n    this.write('debug', message, data);\n  }\n\n  info(message: string, data?: Record<string, unknown>): void {\n    this.write('info', message, data);\n  }\n\n  warn(message: string, data?: Record<string, unknown>): void {\n    this.write('warn', message, data);\n  }\n\n  error(message: string, error?: unknown, data?: Record<string, unknown>): void {\n    const serialized = error ? this.serializeError(error) : undefined;\n    this.write('error', message, data, serialized);\n  }\n\n  // ─── Child logger ────────────────────────────────────────────────────────────\n\n  /**\n   * Create a child logger that merges `context` into every log entry.\n   * The child shares the parent's transports and settings.\n   *\n   * @example\n   * const reqLogger = logger.child({ requestId: req.id, userId: req.user.id });\n   * reqLogger.info('Payment started'); // includes requestId + userId automatically\n   */\n  child(context: LogContext): SmartLogger {\n    const childOpts: SmartLoggerOptions = {\n      level: this.level,\n      transports: this.transports,\n      redactFields: [...this.redactFields],\n    };\n    if (this.serviceName !== undefined) childOpts.serviceName = this.serviceName;\n    const child = new SmartLogger(childOpts);\n    // Bind the extra context into the child by hijacking currentContext\n    Object.assign(child.baseContext, this.currentContext, context);\n    return child;\n  }\n\n  // ─── Private ─────────────────────────────────────────────────────────────────\n\n  private write(\n    level: LogLevel,\n    message: string,\n    data?: Record<string, unknown>,\n    error?: SerializedError,\n  ): void {\n    if (LOG_LEVELS[level] < LOG_LEVELS[this.level]) return;\n\n    const entry: LogEntry = {\n      level,\n      message,\n      timestamp: new Date().toISOString(),\n      context: { ...this.baseContext, ...this.currentContext },\n      ...(error ? { error } : {}),\n      ...(data ? { data: this.redact(data) } : {}),\n    };\n\n    for (const transport of this.transports) {\n      transport.write(entry);\n    }\n  }\n\n  private redact(obj: Record<string, unknown>): Record<string, unknown> {\n    const result: Record<string, unknown> = {};\n    for (const [key, value] of Object.entries(obj)) {\n      if (this.redactFields.has(key)) {\n        result[key] = '[REDACTED]';\n      } else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n        result[key] = this.redact(value as Record<string, unknown>);\n      } else {\n        result[key] = value;\n      }\n    }\n    return result;\n  }\n\n  private serializeError(error: unknown): SerializedError {\n    if (error instanceof Error) {\n      const nodeErr = error as NodeJS.ErrnoException;\n      return {\n        name: error.name,\n        message: error.message,\n        stack: error.stack,\n        code: nodeErr.code,\n      };\n    }\n    return { name: 'UnknownError', message: String(error), stack: undefined, code: undefined };\n  }\n}\n","import { randomUUID } from 'node:crypto';\nimport type { SmartLogger } from '../SmartLogger.js';\n\n/**\n * Minimal Express-compatible types — avoids hard dependency on @types/express.\n */\ninterface Request {\n  headers: Record<string, string | string[] | undefined>;\n  method: string;\n  url: string;\n  [key: string]: unknown;\n}\n\ninterface Response {\n  statusCode: number;\n  on(event: string, listener: () => void): void;\n  setHeader(name: string, value: string): void;\n  [key: string]: unknown;\n}\n\ntype NextFunction = () => void;\n\n/**\n * Express middleware that:\n * 1. Attaches a requestId to every incoming request (from header or generated).\n * 2. Runs the rest of the chain inside a logging context — so every log\n *    call inside a request handler automatically includes the requestId.\n * 3. Logs request start and completion with duration.\n *\n * @example\n * import express from 'express';\n * import { SmartLogger } from 'rezilia/logger';\n * import { requestLogger } from 'rezilia/logger/middleware/express';\n *\n * const app = express();\n * const logger = new SmartLogger({ serviceName: 'api' });\n * app.use(requestLogger(logger));\n */\nexport function requestLogger(logger: SmartLogger) {\n  return (req: Request, res: Response, next: NextFunction): void => {\n    const requestId =\n      (Array.isArray(req.headers['x-request-id'])\n        ? req.headers['x-request-id'][0]\n        : req.headers['x-request-id']) ?? randomUUID();\n\n    const startedAt = Date.now();\n\n    // Expose requestId to the client\n    res.setHeader('x-request-id', requestId);\n\n    logger.runWithContext({ requestId }, () => {\n      logger.info('Request started', { method: req.method, url: req.url });\n\n      res.on('finish', () => {\n        const duration = Date.now() - startedAt;\n        const level = res.statusCode >= 500 ? 'error' : res.statusCode >= 400 ? 'warn' : 'info';\n        logger[level]('Request completed', {\n          method: req.method,\n          url: req.url,\n          status: res.statusCode,\n          durationMs: duration,\n        });\n      });\n\n      next();\n    });\n  };\n}\n"]}