{"version":3,"file":"log-ymm_cyTo.mjs","names":[],"sources":["../src/log/index.ts"],"sourcesContent":["/**\n * `@pagesmith/core/log` — color-coded server-side logger.\n *\n * Server-only. Browser bundles MUST NOT import this module: it is intended\n * for CLIs, build pipelines, dev/preview HTTP servers, Vite plugins, and\n * other Node-runtime code paths. Browser code should not log to `console`\n * at all (lint-enforced); user-facing messages belong in the UI.\n *\n * Why a custom logger:\n *   - Consistent level filtering across CLIs (`silent | error | warn | info | verbose`).\n *   - ANSI color coding per level so warnings and errors stand out in TTYs\n *     and CI logs. Colors are auto-disabled when:\n *       - `process.env.NO_COLOR` is set (https://no-color.org/)\n *       - `process.env.FORCE_COLOR === '0'`\n *       - the underlying stream is not a TTY (e.g. piped output)\n *   - Optional per-logger `prefix` so subsystems can tag their lines\n *     (e.g. `[pagesmith:ssg]`) without callers repeating the prefix.\n *   - Errors are written to `stderr`; everything else goes to `stdout`. This\n *     keeps protocol channels (e.g. MCP servers reserving `stdout`) clean\n *     when they pin the logger to `stderr` via `error()`.\n */\n\nexport type LogLevel = \"silent\" | \"error\" | \"warn\" | \"info\" | \"verbose\";\n\nexport type LogMethod = (message: string, ...args: unknown[]) => void;\n\nexport interface Logger {\n  readonly level: LogLevel;\n  readonly prefix?: string;\n  shouldLog(minimum: LogLevel): boolean;\n  /** Write to `stderr`. Always shown unless `level === 'silent'`. */\n  error: LogMethod;\n  /** Write to `stdout`. Shown when level is `warn` or higher. */\n  warn: LogMethod;\n  /** Write to `stdout`. Shown when level is `info` or higher. */\n  info: LogMethod;\n  /** Write to `stdout`. Shown only when level is `verbose`. */\n  verbose: LogMethod;\n  /** Return a child logger that prepends `childPrefix` after the parent's prefix. */\n  child(childPrefix: string): Logger;\n}\n\nexport interface CreateLoggerOptions {\n  /** Minimum severity to emit. Defaults to `'info'`. */\n  level?: LogLevel;\n  /** Optional tag rendered before every message (color-stripped on no-color streams). */\n  prefix?: string;\n  /**\n   * Force-enable or force-disable colors. When unset, colors are inferred\n   * from `NO_COLOR`, `FORCE_COLOR`, and stream `isTTY`.\n   */\n  color?: boolean;\n}\n\nconst LOG_RANK: Record<LogLevel, number> = {\n  silent: 0,\n  error: 1,\n  warn: 2,\n  info: 3,\n  verbose: 4,\n};\n\nconst ANSI = {\n  reset: \"\\u001b[0m\",\n  bold: \"\\u001b[1m\",\n  dim: \"\\u001b[2m\",\n  red: \"\\u001b[31m\",\n  yellow: \"\\u001b[33m\",\n  blue: \"\\u001b[34m\",\n  cyan: \"\\u001b[36m\",\n  gray: \"\\u001b[90m\",\n} as const;\n\ntype Stream = \"stdout\" | \"stderr\";\n\nfunction detectColor(stream: Stream): boolean {\n  if (typeof process === \"undefined\") return false;\n  const env = process.env ?? {};\n  if (env.NO_COLOR && env.NO_COLOR !== \"\") return false;\n  if (env.FORCE_COLOR === \"0\" || env.FORCE_COLOR === \"false\") return false;\n  if (env.FORCE_COLOR && env.FORCE_COLOR !== \"\") return true;\n  const target = stream === \"stderr\" ? process.stderr : process.stdout;\n  return Boolean(target && (target as { isTTY?: boolean }).isTTY);\n}\n\nfunction paint(text: string, color: keyof typeof ANSI, enable: boolean): string {\n  return enable ? `${ANSI[color]}${text}${ANSI.reset}` : text;\n}\n\nconst LEVEL_BADGE: Record<\n  Exclude<LogLevel, \"silent\">,\n  { label: string; color: keyof typeof ANSI }\n> = {\n  error: { label: \"error\", color: \"red\" },\n  warn: { label: \"warn\", color: \"yellow\" },\n  info: { label: \"info\", color: \"blue\" },\n  verbose: { label: \"debug\", color: \"gray\" },\n};\n\nfunction format(\n  level: Exclude<LogLevel, \"silent\">,\n  message: string,\n  prefix: string | undefined,\n  color: boolean,\n): string {\n  const badge = LEVEL_BADGE[level];\n  const tag = paint(badge.label.padEnd(5), badge.color, color);\n  if (prefix) {\n    const tinted = paint(prefix, \"cyan\", color);\n    return `${tag} ${tinted} ${message}`;\n  }\n  return `${tag} ${message}`;\n}\n\nfunction write(stream: Stream, line: string, args: unknown[]): void {\n  // The logger module is the single sanctioned wrapper for `console`.\n  // Bypass `no-console` here because we forward to `process.stdout` /\n  // `process.stderr` semantics — `console.info` is locale-formatted and\n  // bound to stdout, but we want unbuffered raw writes to honor the\n  // chosen stream. Using `process.stdout.write` keeps the byte stream\n  // explicit and avoids `console`'s own buffering quirks.\n  const target = stream === \"stderr\" ? process.stderr : process.stdout;\n  const suffix = line.endsWith(\"\\n\") ? \"\" : \"\\n\";\n  if (args.length === 0) {\n    target.write(`${line}${suffix}`);\n    return;\n  }\n  // For structured args, fall back to `console` so it formats objects\n  // (deep inspect) like a developer expects.\n  if (stream === \"stderr\") console.error(line, ...args);\n  else console.info(line, ...args);\n}\n\n/**\n * Create a color-coded logger. Cheap (no I/O) so it can be created once per\n * subsystem and passed around via options bags.\n */\nexport function createLogger(options: CreateLoggerOptions | LogLevel = {}): Logger {\n  const opts: CreateLoggerOptions = typeof options === \"string\" ? { level: options } : options;\n  const level: LogLevel = opts.level ?? \"info\";\n  const prefix = opts.prefix;\n  const colorStdout = opts.color ?? detectColor(\"stdout\");\n  const colorStderr = opts.color ?? detectColor(\"stderr\");\n\n  const rank = LOG_RANK[level];\n  const shouldLog = (minimum: LogLevel): boolean => rank >= LOG_RANK[minimum];\n\n  const logger: Logger = {\n    level,\n    prefix,\n    shouldLog,\n    error(message, ...args) {\n      if (!shouldLog(\"error\")) return;\n      write(\"stderr\", format(\"error\", message, prefix, colorStderr), args);\n    },\n    warn(message, ...args) {\n      if (!shouldLog(\"warn\")) return;\n      write(\"stdout\", format(\"warn\", message, prefix, colorStdout), args);\n    },\n    info(message, ...args) {\n      if (!shouldLog(\"info\")) return;\n      write(\"stdout\", format(\"info\", message, prefix, colorStdout), args);\n    },\n    verbose(message, ...args) {\n      if (!shouldLog(\"verbose\")) return;\n      write(\"stdout\", format(\"verbose\", message, prefix, colorStdout), args);\n    },\n    child(childPrefix) {\n      const merged = prefix ? `${prefix} ${childPrefix}` : childPrefix;\n      return createLogger({ level, prefix: merged, color: opts.color });\n    },\n  };\n\n  return logger;\n}\n\n/**\n * Module-level default logger. Inherits `PAGESMITH_LOG_LEVEL` from the env\n * if set (one of `silent | error | warn | info | verbose`), otherwise `info`.\n *\n * Prefer `createLogger({ prefix })` when a subsystem can pass a logger\n * around; reach for `defaultLogger` only at top-level scripts where\n * threading an instance is impractical.\n */\nexport const defaultLogger: Logger = createLogger({\n  level: resolveEnvLevel() ?? \"info\",\n});\n\nfunction resolveEnvLevel(): LogLevel | undefined {\n  if (typeof process === \"undefined\") return undefined;\n  const raw = process.env?.PAGESMITH_LOG_LEVEL?.trim().toLowerCase();\n  if (!raw) return undefined;\n  if (raw === \"silent\" || raw === \"error\" || raw === \"warn\" || raw === \"info\" || raw === \"verbose\")\n    return raw;\n  return undefined;\n}\n"],"mappings":"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"}