{"version":3,"file":"logger.mjs","names":[],"sources":["../../src/extension/logger.ts"],"sourcesContent":["import { defineInterceptor } from '#src/core/interceptors.ts';\nimport { thenMaybe } from '#src/core/results.ts';\nimport type { ResolvedPadroneRuntime } from '#src/core/runtime.ts';\nimport type { AnyPadroneBuilder, CommandTypesBase } from '#src/types/index.ts';\nimport type { WithInterceptor } from '#src/util/type-utils.ts';\nimport type { PadroneTracer } from './tracing.ts';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Log level values ordered by severity. */\nexport type PadroneLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\n/** Logger instance injected into the command context. */\nexport type PadroneLogger = {\n  trace: (...args: unknown[]) => void;\n  debug: (...args: unknown[]) => void;\n  info: (...args: unknown[]) => void;\n  warn: (...args: unknown[]) => void;\n  error: (...args: unknown[]) => void;\n  /** The current effective log level. */\n  level: PadroneLogLevel;\n  /** Create a child logger with a prefix label. */\n  child: (label: string) => PadroneLogger;\n};\n\n/** Configuration for the logger extension. */\nexport type PadroneLoggerConfig = {\n  /** Minimum log level to output. Defaults to `'info'`. */\n  level?: PadroneLogLevel;\n  /** Prefix prepended to every log message. */\n  prefix?: string;\n  /** Include timestamps in log output. Defaults to `false`. */\n  timestamps?: boolean;\n};\n\n/** Builder/program type after applying `padroneLogger()`. Adds `{ logger: PadroneLogger }` to the command context. */\nexport type WithLogger<T> = WithInterceptor<T, { logger: PadroneLogger }>;\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst LEVEL_ORDER: Record<PadroneLogLevel, number> = { trace: 0, debug: 1, info: 2, warn: 3, error: 4, silent: 5 };\nconst LEVEL_LABELS: Record<Exclude<PadroneLogLevel, 'silent'>, string> = {\n  trace: 'TRACE',\n  debug: 'DEBUG',\n  info: 'INFO',\n  warn: 'WARN',\n  error: 'ERROR',\n};\nconst VALID_LEVELS = new Set<string>(Object.keys(LEVEL_ORDER));\n\n/** Format specifier pattern: matches %s, %d, %i, %f, %o, %O, %j, %% */\nconst FORMAT_PATTERN = /%%|%[sdifjoO]/g;\n\n/**\n * Applies printf-style format specifiers to args, following the WHATWG Console Standard\n * and Node.js `util.format` conventions. Remaining args are appended space-separated.\n */\nfunction formatArgs(args: unknown[]): string {\n  if (args.length === 0) return '';\n  if (typeof args[0] !== 'string' || !FORMAT_PATTERN.test(args[0])) {\n    return args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ');\n  }\n\n  const template = args[0];\n  let argIndex = 1;\n  const result = template.replace(FORMAT_PATTERN, (token) => {\n    if (token === '%%') return '%';\n    if (argIndex >= args.length) return token;\n    const val = args[argIndex++];\n    switch (token) {\n      case '%s':\n        return String(val);\n      case '%d':\n      case '%i':\n        return String(Math.trunc(Number(val)));\n      case '%f':\n        return String(Number(val));\n      case '%j':\n        try {\n          return JSON.stringify(val);\n        } catch {\n          return '[Circular]';\n        }\n      case '%o':\n      case '%O':\n        return typeof val === 'string' ? val : JSON.stringify(val);\n      default:\n        return token;\n    }\n  });\n\n  // Append remaining args that weren't consumed by specifiers\n  const remaining = args.slice(argIndex);\n  if (remaining.length === 0) return result;\n  const tail = remaining.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ');\n  return `${result} ${tail}`;\n}\n\nfunction resolveCliLevel(rawArgs: Record<string, unknown>): PadroneLogLevel | undefined {\n  // --trace → trace level\n  if ('trace' in rawArgs) {\n    const t = rawArgs.trace;\n    delete rawArgs.trace;\n    if (t !== false) return 'trace';\n  }\n  // --verbose / --debug → debug level\n  if ('verbose' in rawArgs) {\n    const v = rawArgs.verbose;\n    delete rawArgs.verbose;\n    if (v !== false) return 'debug';\n  }\n  if ('debug' in rawArgs) {\n    const d = rawArgs.debug;\n    delete rawArgs.debug;\n    if (d !== false) return 'debug';\n  }\n  // --silent / --quiet → suppress all output\n  if ('silent' in rawArgs) {\n    const s = rawArgs.silent;\n    delete rawArgs.silent;\n    if (s !== false) return 'silent';\n  }\n  if ('quiet' in rawArgs) {\n    const q = rawArgs.quiet;\n    delete rawArgs.quiet;\n    if (q !== false) return 'silent';\n  }\n  // --log-level=<level> → explicit level (parser keeps kebab-case)\n  if ('log-level' in rawArgs) {\n    const val = rawArgs['log-level'];\n    delete rawArgs['log-level'];\n    if (typeof val === 'string' && VALID_LEVELS.has(val)) return val as PadroneLogLevel;\n  }\n  return undefined;\n}\n\nfunction createLogger(\n  runtime: ResolvedPadroneRuntime,\n  level: PadroneLogLevel,\n  config: ResolvedLoggerConfig,\n  tracing?: PadroneTracer,\n): PadroneLogger {\n  const threshold = LEVEL_ORDER[level];\n\n  function format(lvl: Exclude<PadroneLogLevel, 'silent'>, prefix: string, args: unknown[]): string {\n    const parts: string[] = [];\n    if (config.timestamps) parts.push(new Date().toISOString());\n    parts.push(`[${LEVEL_LABELS[lvl]}]`);\n    if (prefix) parts.push(prefix);\n    parts.push(formatArgs(args));\n    return parts.join(' ');\n  }\n\n  function makeLogger(prefix: string): PadroneLogger {\n    const emit = (lvl: Exclude<PadroneLogLevel, 'silent'>, args: unknown[]) => {\n      if (LEVEL_ORDER[lvl] < threshold) return;\n      const message = format(lvl, prefix, args);\n      tracing?.rootSpan.addEvent('log', {\n        'log.level': lvl,\n        'log.message': args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' '),\n      });\n      if (lvl === 'error' || lvl === 'warn') runtime.error(message);\n      else runtime.output(message);\n    };\n\n    return {\n      trace: (...args) => emit('trace', args),\n      debug: (...args) => emit('debug', args),\n      info: (...args) => emit('info', args),\n      warn: (...args) => emit('warn', args),\n      error: (...args) => emit('error', args),\n      level,\n      child: (label) => makeLogger(prefix ? `${prefix} [${label}]` : `[${label}]`),\n    };\n  }\n\n  return makeLogger(config.prefix);\n}\n\n// ---------------------------------------------------------------------------\n// Interceptor\n// ---------------------------------------------------------------------------\n\ntype ResolvedLoggerConfig = { level: PadroneLogLevel; prefix: string; timestamps: boolean };\n\nfunction loggerInterceptor(rawConfig?: PadroneLoggerConfig) {\n  return defineInterceptor({ id: 'padrone:logger', name: 'padrone:logger' })\n    .requires<{ tracing?: PadroneTracer; loggerConfig?: PadroneLoggerConfig }>()\n    .factory(() => {\n      let cliLevel: PadroneLogLevel | undefined;\n\n      return {\n        parse(_ctx, next) {\n          return thenMaybe(next(), (res) => {\n            cliLevel = resolveCliLevel(res.rawArgs);\n            return res;\n          });\n        },\n\n        execute(ctx, next) {\n          const ctxCfg = (ctx.context as Record<string, unknown> | undefined)?.loggerConfig as PadroneLoggerConfig | undefined;\n          const resolved: ResolvedLoggerConfig = {\n            level: cliLevel ?? rawConfig?.level ?? ctxCfg?.level ?? 'info',\n            prefix: rawConfig?.prefix ?? '',\n            timestamps: rawConfig?.timestamps ?? ctxCfg?.timestamps ?? false,\n          };\n          const logger = createLogger(ctx.runtime, resolved.level, resolved, ctx.context?.tracing);\n          return next({ context: { logger } });\n        },\n      };\n    });\n}\n\n// ---------------------------------------------------------------------------\n// Extension\n// ---------------------------------------------------------------------------\n\n/**\n * Extension that injects a structured logger into the command context.\n *\n * The logger respects a configurable log level threshold, supports prefixed\n * child loggers, and routes output through the runtime's `output`/`error`\n * functions so it works in any environment (terminal, test, web).\n *\n * Supports CLI flags for runtime level overrides:\n * - `--trace` → sets level to `trace`\n * - `--verbose` or `--debug` → sets level to `debug`\n * - `--silent` or `--quiet` → sets level to `silent`\n * - `--log-level=<level>` → sets an explicit level (`trace`, `debug`, `info`, `warn`, `error`, `silent`)\n *\n * CLI flags take precedence over the programmatic config.\n *\n * Provides `{ logger: PadroneLogger }` on the command context.\n * Access it in action handlers as `ctx.context.logger`.\n *\n * Usage:\n * ```ts\n * createPadrone('my-cli')\n *   .extend(padroneLogger({ level: 'info' }))\n *   .command('sync', (c) =>\n *     c.action((_args, ctx) => {\n *       ctx.context.logger.info('starting sync');\n *       const db = ctx.context.logger.child('db');\n *       db.debug('connecting...');\n *     })\n *   )\n * ```\n *\n * Then run:\n * ```sh\n * my-cli sync --verbose      # debug level\n * my-cli sync --quiet        # silent\n * my-cli sync --log-level=warn\n * ```\n */\nexport function padroneLogger<T extends CommandTypesBase>(config?: PadroneLoggerConfig): (builder: T) => WithLogger<T> {\n  return ((builder: AnyPadroneBuilder) => builder.intercept(loggerInterceptor(config))) as any;\n}\n"],"mappings":"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"}