loggily 0.10.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/context.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as _setContextHooks, t as _clearContextHooks } from "./core-DKwMMOZw.mjs";
1
+ import { n as _setContextHooks, t as _clearContextHooks } from "./core-CM_9Xqz-.mjs";
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
3
  //#region src/context.ts
4
4
  /**
@@ -58,7 +58,8 @@ function createFileWriter(filePath, options = {}) {
58
58
  let fd = null;
59
59
  let timer = null;
60
60
  let closed = false;
61
- fd = fs.openSync(filePath, "a");
61
+ const stdFd = stdStreamFd(filePath);
62
+ fd = stdFd ?? fs.openSync(filePath, "a");
62
63
  /** Flush buffer contents to disk synchronously */
63
64
  function flush() {
64
65
  if (buffer.length === 0 || fd === null) return;
@@ -88,7 +89,7 @@ function createFileWriter(filePath, options = {}) {
88
89
  flush();
89
90
  } catch {} finally {
90
91
  if (fd !== null) {
91
- fs.closeSync(fd);
92
+ if (stdFd === null) fs.closeSync(fd);
92
93
  fd = null;
93
94
  }
94
95
  process.removeListener("exit", exitHandler);
@@ -96,6 +97,17 @@ function createFileWriter(filePath, options = {}) {
96
97
  }
97
98
  };
98
99
  }
100
+ /**
101
+ * Map a std-stream pseudo-path to its file descriptor, or null for regular
102
+ * paths. Covers the portable spellings: /dev/stdout, /dev/stderr, /dev/fd/N
103
+ * (macOS + Linux), /proc/self/fd/N (Linux).
104
+ */
105
+ function stdStreamFd(filePath) {
106
+ if (filePath === "/dev/stdout") return 1;
107
+ if (filePath === "/dev/stderr") return 2;
108
+ const m = /^(?:\/dev\/fd|\/proc\/self\/fd)\/(\d+)$/.exec(filePath);
109
+ return m ? Number(m[1]) : null;
110
+ }
99
111
  //#endregion
100
112
  //#region src/tracing.ts
101
113
  let currentIdFormat = "simple";
@@ -1368,4 +1380,4 @@ function writeSpan(namespace, duration, attrs) {
1368
1380
  //#endregion
1369
1381
  export { withEnvDefaults as A, setSampleRate as B, setOutputMode as C, startCollecting as D, spansAreEnabled as E, safeStringify as F, createFileWriter as H, serializeCause as I, getIdFormat as L, writeSpan as M, LOG_LEVEL_PRIORITY as N, stopCollecting as O, buildPipeline as P, getSampleRate as R, setLogLevel as S, setTraceFilter as T, traceparent as V, getTraceFilter as _, baseCreateLogger as a, setDebugFilter as b, createSpanDataProxy as c, enableSpans as d, getCollectedSpans as f, getOutputMode as g, getLogLevel as h, addWriter as i, withSpans as j, withConfigMetrics as k, createTestLogger as l, getLogFormat as m, _setContextHooks as n, clearCollectedSpans as o, getDebugFilter as p, _setLogFileWriterFactory as r, createLogger as s, _clearContextHooks as t, disableSpans as u, pipe as v, setSuppressConsole as w, setLogFormat as x, resetIds as y, setIdFormat as z };
1370
1382
 
1371
- //# sourceMappingURL=core-DKwMMOZw.mjs.map
1383
+ //# sourceMappingURL=core-CM_9Xqz-.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"core-DKwMMOZw.mjs","names":["_process","pc","createConsoleSink","pc","_createConsoleSink","_withMetrics","_createMetricsCollector"],"sources":["../src/colors.ts","../src/file-writer.ts","../src/tracing.ts","../src/console-sinks.ts","../src/pipeline.ts","../src/core.ts"],"sourcesContent":["/**\n * Vendored ANSI color functions — replaces picocolors dependency.\n * Supports NO_COLOR, FORCE_COLOR, and TTY detection.\n */\n\nconst _process = typeof process !== \"undefined\" ? process : undefined\n\nconst enabled =\n _process?.env?.[\"FORCE_COLOR\"] !== undefined &&\n _process?.env?.[\"FORCE_COLOR\"] !== \"0\"\n ? true\n : _process?.env?.[\"NO_COLOR\"] !== undefined\n ? false\n : (_process?.stdout?.isTTY ?? false)\n\nfunction wrap(open: string, close: string): (str: string) => string {\n if (!enabled) return (str) => str\n return (str) => open + str + close\n}\n\nexport const colors = {\n dim: wrap(\"\\x1b[2m\", \"\\x1b[22m\"),\n blue: wrap(\"\\x1b[34m\", \"\\x1b[39m\"),\n yellow: wrap(\"\\x1b[33m\", \"\\x1b[39m\"),\n red: wrap(\"\\x1b[31m\", \"\\x1b[39m\"),\n magenta: wrap(\"\\x1b[35m\", \"\\x1b[39m\"),\n cyan: wrap(\"\\x1b[36m\", \"\\x1b[39m\"),\n}\n","/**\n * File writer for loggily — Node.js/Bun only.\n *\n * Separated from core logger to allow tree-shaking in browser bundles.\n * Uses dynamic import(\"node:fs\") to avoid static dependency on Node APIs.\n */\n\nimport { openSync, writeSync, closeSync } from \"node:fs\"\n\n/** Options for creating an async buffered file writer */\nexport interface FileWriterOptions {\n /** Buffer size threshold in bytes before flushing (default: 4096) */\n bufferSize?: number\n /** Flush interval in milliseconds (default: 100) */\n flushInterval?: number\n /** Test-only filesystem injection. Production callers should omit this. */\n __fs?: {\n openSync(path: string, flags: string): number\n writeSync(fd: number, data: string): unknown\n closeSync(fd: number): void\n }\n}\n\n/** An async buffered file writer with automatic flushing */\nexport interface FileWriter {\n /** Write a line to the buffer (appends newline) */\n write(line: string): void\n /** Flush the buffer immediately */\n flush(): void\n /** Close the writer and flush remaining buffer */\n close(): void\n}\n\n/**\n * Create an async buffered file writer for log output.\n * Buffers writes and flushes on size threshold or interval.\n * Registers a process.on('exit') handler to flush remaining buffer.\n *\n * **Node.js/Bun only** — not available in browser environments.\n *\n * @param filePath - Path to the log file (opened in append mode)\n * @param options - Buffer size and flush interval configuration\n * @returns FileWriter with write, flush, and close methods\n *\n * @example\n * const writer = createFileWriter('/tmp/app.log')\n * const unsubscribe = addWriter((formatted) => writer.write(formatted))\n *\n * // On shutdown:\n * unsubscribe()\n * writer.close()\n */\nexport function createFileWriter(\n filePath: string,\n options: FileWriterOptions = {},\n): FileWriter {\n const bufferSize = options.bufferSize ?? 4096\n const flushInterval = options.flushInterval ?? 100\n const fs = options.__fs ?? { openSync, writeSync, closeSync }\n\n let buffer = \"\"\n let fd: number | null = null\n let timer: ReturnType<typeof setInterval> | null = null\n let closed = false\n\n // Open file in append mode\n fd = fs.openSync(filePath, \"a\")\n\n /** Flush buffer contents to disk synchronously */\n function flush(): void {\n if (buffer.length === 0 || fd === null) return\n const data = buffer\n fs.writeSync(fd, data)\n buffer = \"\"\n }\n\n // Set up periodic flush\n timer = setInterval(flush, flushInterval)\n // Don't let the timer keep the process alive\n if (timer && typeof timer === \"object\" && \"unref\" in timer) {\n ;(timer as { unref(): void }).unref()\n }\n\n // Flush on process exit to avoid data loss\n const exitHandler = (): void => flush()\n process.on(\"exit\", exitHandler)\n\n return {\n write(line: string): void {\n if (closed) return\n buffer += line + \"\\n\"\n if (buffer.length >= bufferSize) {\n flush()\n }\n },\n\n flush,\n\n close(): void {\n if (closed) return\n closed = true\n if (timer !== null) {\n clearInterval(timer)\n timer = null\n }\n try {\n flush()\n } catch {\n // Swallow flush errors during close — data loss is unavoidable\n // at this point, but we must still release the fd and exit handler.\n } finally {\n if (fd !== null) {\n fs.closeSync(fd)\n fd = null\n }\n process.removeListener(\"exit\", exitHandler)\n }\n },\n }\n}\n","/**\n * Distributed tracing utilities for loggily.\n *\n * Provides W3C-compatible trace/span ID generation, traceparent header formatting,\n * and head-based sampling. All features are opt-in and don't break the existing API.\n */\n\nimport type { SpanData } from \"./core.js\"\n\n// ============ ID Format ============\n\n/** Supported ID formats */\nexport type IdFormat = \"simple\" | \"w3c\"\n\nlet currentIdFormat: IdFormat = \"simple\"\n\n/**\n * Set the ID format for new spans and traces.\n * - \"simple\": sp_1, sp_2, tr_1, tr_2 (default, lightweight)\n * - \"w3c\": 32-char hex trace ID, 16-char hex span ID (W3C Trace Context compatible)\n *\n * @deprecated Use the `TRACE_ID_FORMAT` env var or `{ idFormat: \"w3c\" }` in the config array instead.\n */\nexport function setIdFormat(format: IdFormat): void {\n currentIdFormat = format\n}\n\n/**\n * Get the current ID format.\n *\n * @deprecated Use the `TRACE_ID_FORMAT` env var or `{ idFormat: \"w3c\" }` in the config array instead.\n */\nexport function getIdFormat(): IdFormat {\n return currentIdFormat\n}\n\n// Simple format counters (used by core.ts via the generator functions)\nlet simpleSpanCounter = 0\nlet simpleTraceCounter = 0\n\n/** Generate a hex string of the given byte length using crypto.randomUUID */\nfunction randomHex(bytes: number): string {\n // crypto.randomUUID() gives us 32 hex chars (128 bits) after removing dashes\n // For 16 bytes (32 hex chars) we use one UUID, for 8 bytes (16 hex chars) we take a slice\n const uuid = crypto.randomUUID().replace(/-/g, \"\")\n return uuid.slice(0, bytes * 2)\n}\n\n/** Generate a span ID according to the current format */\nexport function generateSpanId(): string {\n if (currentIdFormat === \"w3c\") {\n return randomHex(8) // 16-char hex\n }\n return `sp_${(++simpleSpanCounter).toString(36)}`\n}\n\n/** Generate a trace ID according to the current format */\nexport function generateTraceId(): string {\n if (currentIdFormat === \"w3c\") {\n return randomHex(16) // 32-char hex\n }\n return `tr_${(++simpleTraceCounter).toString(36)}`\n}\n\n/** Reset ID counters (for testing) */\nexport function resetIdCounters(): void {\n simpleSpanCounter = 0\n simpleTraceCounter = 0\n}\n\n// ============ W3C Traceparent ============\n\n/** Options for traceparent header formatting */\nexport interface TraceparentOptions {\n /** Whether this span is sampled. Defaults to true for backwards compatibility. */\n sampled?: boolean\n}\n\n/**\n * Format a W3C traceparent header from span data.\n *\n * Format: `{version}-{trace-id}-{span-id}-{trace-flags}`\n * - version: \"00\" (current W3C spec version)\n * - trace-id: 32 hex chars (128 bits)\n * - span-id: 16 hex chars (64 bits)\n * - trace-flags: \"01\" (sampled) or \"00\" (not sampled)\n *\n * Works with both simple and W3C ID formats. Simple IDs are zero-padded to spec length.\n *\n * @param spanData - Span data with id and traceId\n * @param options - Optional settings (sampled flag). Defaults to sampled=true.\n * @returns W3C traceparent header string\n *\n * @example\n * ```typescript\n * const span = log.span?.(\"http-request\")\n * if (!span) return\n * const header = traceparent(span.spanData)\n * // → \"00-a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6-1a2b3c4d5e6f7a8b-01\"\n * fetch(url, { headers: { traceparent: header } })\n * ```\n */\nexport function traceparent(\n spanData: SpanData,\n options?: TraceparentOptions,\n): string {\n const traceId = padHex(spanData.traceId, 32)\n const spanId = padHex(spanData.id, 16)\n const flags = (options?.sampled ?? true) ? \"01\" : \"00\"\n return `00-${traceId}-${spanId}-${flags}`\n}\n\n/** Pad or hash an ID to the specified hex length */\nfunction padHex(id: string, length: number): string {\n // If it's already the right length and looks like hex, use as-is\n if (id.length === length && /^[0-9a-f]+$/.test(id)) {\n return id\n }\n\n // For simple IDs (sp_1, tr_1), create a deterministic hex representation\n // by encoding the string as hex bytes, zero-padded to the target length\n let hex = \"\"\n for (let i = 0; i < id.length; i++) {\n hex += id.charCodeAt(i).toString(16).padStart(2, \"0\")\n }\n // Pad or truncate to target length\n return hex.padStart(length, \"0\").slice(-length)\n}\n\n// ============ Sampling ============\n\nlet sampleRate = 1.0\n\n/**\n * Set the head-based sampling rate for new traces.\n * Applied at trace creation — all spans within a sampled trace are kept.\n *\n * @deprecated Use the `TRACE_SAMPLE_RATE` env var or `{ sampleRate: 0.1 }` in the config array instead.\n * @param rate - Sampling rate from 0.0 (sample nothing) to 1.0 (sample everything, default)\n */\nexport function setSampleRate(rate: number): void {\n if (rate < 0 || rate > 1) {\n throw new Error(`Sample rate must be between 0.0 and 1.0, got ${rate}`)\n }\n sampleRate = rate\n}\n\n/**\n * Get the current sampling rate.\n *\n * @deprecated Use the `TRACE_SAMPLE_RATE` env var or `{ sampleRate: 0.1 }` in the config array instead.\n */\nexport function getSampleRate(): number {\n return sampleRate\n}\n\n/**\n * Determine whether a new trace should be sampled.\n * Called at trace creation time (head-based sampling).\n */\nexport function shouldSample(): boolean {\n if (sampleRate >= 1.0) return true\n if (sampleRate <= 0.0) return false\n return Math.random() < sampleRate\n}\n","/**\n * Console sinks — runtime-aware structured output to console.\n *\n * Two sinks, one contract: `(event: Event) => void`. They do NOT pre-format\n * events into a single ANSI string. Instead they spread structured arguments\n * to `console.*` so the platform can render rich output:\n *\n * Terminal (Node / Bun):\n * console.info(ansiPrefix, message, ...userArgs)\n * — util.format keeps objects inspectable in devtools\n *\n * Browser (Chrome / Firefox / Safari DevTools):\n * console.info(\"%c<level> %c<namespace>\", levelCss, nsCss, message, ...userArgs)\n * — DevTools renders colored prefix and keeps every user arg as an\n * expandable, clickable object.\n *\n * Source-location preservation:\n * We call `console.<level>(...)` from arrow-function sinks. DevTools attribute\n * the log line to the caller's frame, not this file, as long as no wrapper\n * closes over a bound console reference. `Function.prototype.bind.call(console.info, ...)`\n * works too but defeats `vi.spyOn(console, 'info')` because bind captures\n * the function at bind time. Plain arrow functions re-read `console.info`\n * on each call — mockable AND location-preserving.\n *\n * The sinks consume `event.userArgs` (raw user-supplied data/errors) when\n * present, falling back to `event.props` so they still work with code paths\n * that have not yet been migrated.\n */\n\nimport { colors as pc } from \"./colors.js\"\nimport type { Event, LogFormat, OutputLogLevel } from \"./pipeline.js\"\nimport {\n formatConsoleEvent,\n formatJSONEvent,\n safeStringify,\n} from \"./pipeline.js\"\n\nexport type ConsoleSink = (event: Event) => void\n\n// ---------------------------------------------------------------------------\n// Runtime detection\n// ---------------------------------------------------------------------------\n\n/**\n * True when running inside a browser-like environment. We check for the\n * presence of a `window` with `document` — the Node DOM test env (jsdom)\n * also matches, but that is the correct behaviour for DevTools-style output.\n */\nexport function isBrowserRuntime(): boolean {\n return (\n typeof (globalThis as { window?: unknown })?.window !== \"undefined\" &&\n typeof (globalThis as { document?: unknown }).document !== \"undefined\"\n )\n}\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\nfunction timeStr(time: number): string {\n return new Date(time).toISOString().split(\"T\")[1]?.split(\".\")[0] ?? \"\"\n}\n\nfunction levelLabel(level: OutputLogLevel): string {\n switch (level) {\n case \"trace\":\n return \"TRACE\"\n case \"debug\":\n return \"DEBUG\"\n case \"info\":\n return \"INFO\"\n case \"warn\":\n return \"WARN\"\n case \"error\":\n return \"ERROR\"\n }\n}\n\n/**\n * Return the raw user-supplied data attached to the event, in call order.\n * Prefers the explicit `userArgs` field (new path). Falls back to `props`\n * (coerced to a single trailing object) for events emitted by older code or\n * by `writeSpan`. Filters out `undefined` entries.\n */\nfunction userArgsOf(event: Event): unknown[] {\n if (\n \"userArgs\" in event &&\n Array.isArray((event as { userArgs?: unknown[] }).userArgs)\n ) {\n const ua = (event as { userArgs?: unknown[] }).userArgs!\n return ua.filter((v) => v !== undefined)\n }\n if (event.props && Object.keys(event.props).length > 0) {\n return [event.props]\n }\n return []\n}\n\n// ---------------------------------------------------------------------------\n// Terminal sink\n// ---------------------------------------------------------------------------\n\n/**\n * Terminal (ANSI) sink. Emits `[ansiPrefix, message, ...userArgs]` so that:\n * - stdout gets a colored prefix\n * - util.format leaves objects inspectable\n * - vi.spyOn(console, 'info') intercepts calls (arrows re-read console)\n *\n * Spans and JSON format still go through the pre-formatted single-arg path —\n * their consumers are log aggregators, not humans.\n */\nexport function createTerminalConsoleSink(\n format: LogFormat = \"console\",\n): ConsoleSink {\n if (format === \"json\") {\n // JSON format: one line, one arg — downstream log collectors expect that.\n return (event: Event) => routeSingle(event, formatJSONEvent(event))\n }\n\n return (event: Event) => {\n // Spans bypass console and go straight to stderr — that's what Ink's\n // patchConsole relies on to keep span output outside the TUI surface.\n if (event.kind === \"span\") {\n writeStderrLine(formatConsoleEvent(event))\n return\n }\n\n const prefix = `${pc.dim(timeStr(event.time))} ${levelAnsi(event.level)} ${pc.cyan(event.namespace)}`\n const args = userArgsOf(event)\n // Arrow → console.<level>: preserves caller frame + stays mockable.\n invokeForLevel(event.level, prefix, event.message, ...args)\n }\n}\n\n/** Emit one line to process.stderr when available (no-op in browser). */\nfunction writeStderrLine(text: string): void {\n const p = typeof process !== \"undefined\" ? process : undefined\n if (p?.stderr && typeof p.stderr.write === \"function\") {\n p.stderr.write(text + \"\\n\")\n return\n }\n console.info(text)\n}\n\nfunction levelAnsi(level: OutputLogLevel): string {\n switch (level) {\n case \"trace\":\n return pc.dim(\"TRACE\")\n case \"debug\":\n return pc.dim(\"DEBUG\")\n case \"info\":\n return pc.blue(\"INFO\")\n case \"warn\":\n return pc.yellow(\"WARN\")\n case \"error\":\n return pc.red(\"ERROR\")\n }\n}\n\n// ---------------------------------------------------------------------------\n// Browser sink\n// ---------------------------------------------------------------------------\n\n/**\n * Browser (DevTools) sink. Uses `%c` CSS format specifiers so DevTools\n * renders a colored level + namespace prefix, then spreads user args raw so\n * DevTools keeps them as expandable object references (clickable source\n * locations for Error, drill-in for plain objects).\n *\n * For JSON format we still emit a single string — consumers that explicitly\n * asked for JSON want machine-readable output, not DevTools theatrics.\n */\nexport function createBrowserConsoleSink(\n format: LogFormat = \"console\",\n): ConsoleSink {\n if (format === \"json\") {\n return (event: Event) => routeSingle(event, formatJSONEvent(event))\n }\n\n return (event: Event) => {\n if (event.kind === \"span\") {\n const spanTemplate = `%c%s %cSPAN %c%s %c(%sms)`\n const args = userArgsOf(event)\n const spanPropsString =\n args.length > 0\n ? ` ${safeStringify(Object.assign({}, ...args.filter(isPlainRecord)))}`\n : \"\"\n // Spans: compact format; include props inline because DevTools arg\n // ordering would otherwise separate the duration from its context.\n const { durationLabel } = { durationLabel: String(event.duration) }\n invokeForLevel(\n \"info\",\n spanTemplate + (spanPropsString ? \"%s\" : \"\"),\n cssDim(),\n timeStr(event.time),\n cssSpan(),\n cssNamespace(),\n event.namespace,\n cssDim(),\n durationLabel,\n ...(spanPropsString ? [cssDim(), spanPropsString] : []),\n )\n return\n }\n\n // Template: \"<dimTime> <levelBadge> <namespace>\" — 3 %c slots.\n const template = `%c%s %c%s %c%s`\n const args = userArgsOf(event)\n invokeForLevel(\n event.level,\n template,\n cssDim(),\n timeStr(event.time),\n cssLevel(event.level),\n levelLabel(event.level),\n cssNamespace(),\n event.namespace,\n // Message and user args follow — raw, in call order.\n event.message,\n ...args,\n )\n }\n}\n\nfunction isPlainRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v)\n}\n\nfunction cssDim(): string {\n return \"color: #888\"\n}\nfunction cssNamespace(): string {\n return \"color: #0aa; font-weight: bold\"\n}\nfunction cssSpan(): string {\n return \"color: #a0a; font-weight: bold\"\n}\nfunction cssLevel(level: OutputLogLevel): string {\n switch (level) {\n case \"trace\":\n case \"debug\":\n return \"color: #888; font-weight: bold\"\n case \"info\":\n return \"color: #36f; font-weight: bold\"\n case \"warn\":\n return \"color: #b80; font-weight: bold\"\n case \"error\":\n return \"color: #c33; font-weight: bold\"\n }\n}\n\n// ---------------------------------------------------------------------------\n// Console routing\n// ---------------------------------------------------------------------------\n\n/**\n * Dispatch to the right console method for the event's level.\n * We intentionally use arrow-style invocation rather than\n * `Function.prototype.bind.call(console.info, console, ...)` so that:\n * 1. Tests can `vi.spyOn(console, 'info')` AFTER the sink is created.\n * 2. DevTools attribute the log line to the caller's source location\n * (arrows don't appear in the stack between the call and `console.*`).\n */\nfunction invokeForLevel(level: OutputLogLevel, ...args: unknown[]): void {\n switch (level) {\n case \"trace\":\n case \"debug\":\n console.debug(...args)\n return\n case \"info\":\n console.info(...args)\n return\n case \"warn\":\n console.warn(...args)\n return\n case \"error\":\n console.error(...args)\n return\n }\n}\n\n/**\n * Route an already-formatted single string to the correct console level.\n * Used for JSON format and for span events where we preserve the pre-formatted\n * shape.\n */\nfunction routeSingle(event: Event, text: string): void {\n if (event.kind === \"span\") {\n // Spans go to stderr in Node (existing behaviour) but we funnel them\n // through console.info in the sinks so browser DevTools sees them too;\n // the Node path for spans remains writeStderr via the pipeline when the\n // user opts in via `\"stderr\"`.\n console.info(text)\n return\n }\n switch (event.level) {\n case \"trace\":\n case \"debug\":\n console.debug(text)\n return\n case \"info\":\n console.info(text)\n return\n case \"warn\":\n console.warn(text)\n return\n case \"error\":\n console.error(text)\n return\n }\n}\n\n// ---------------------------------------------------------------------------\n// Runtime-selecting factory\n// ---------------------------------------------------------------------------\n\n/**\n * Pick the right sink for the current runtime. Browsers get `%c` CSS, Node\n * gets ANSI. Both emit multi-arg spreads so the platform can render objects\n * as expandable references.\n */\nexport function createConsoleSink(format: LogFormat = \"console\"): ConsoleSink {\n return isBrowserRuntime()\n ? createBrowserConsoleSink(format)\n : createTerminalConsoleSink(format)\n}\n","import { colors as pc } from \"./colors.js\"\nimport { createFileWriter } from \"./file-writer.js\"\nimport { setIdFormat, setSampleRate } from \"./tracing.js\"\nimport type { IdFormat } from \"./tracing.js\"\n\n// ============ Types ============\n\nexport type OutputLogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\"\nexport type LogLevel = OutputLogLevel | \"silent\"\nexport type LogFormat = \"console\" | \"json\"\n\nexport type LogEvent = {\n kind: \"log\"\n time: number\n namespace: string\n level: OutputLogLevel\n message: string\n props?: Record<string, unknown>\n /**\n * Raw user-supplied arguments, in call order (after the message).\n *\n * Populated by the logger façade so that console sinks can spread them to\n * `console.*` and keep objects expandable in Node/browser DevTools. When\n * absent, sinks fall back to `props` (merged context + user data).\n *\n * Example: `log.info(\"greet\", { user: \"a\" })` → userArgs = [{ user: \"a\" }]\n */\n userArgs?: unknown[]\n}\n\nexport type SpanEvent = {\n kind: \"span\"\n time: number\n namespace: string\n name: string\n duration: number\n props?: Record<string, unknown>\n spanId: string\n traceId: string\n parentId: string | null\n /** Raw user-supplied span attributes (mirrors LogEvent.userArgs). */\n userArgs?: unknown[]\n}\n\nexport type Event = LogEvent | SpanEvent\nexport type Stage = (event: Event) => Event | null | void\n\n// ============ Level Priority ============\n\nexport const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n trace: 0,\n debug: 1,\n info: 2,\n warn: 3,\n error: 4,\n silent: 5,\n}\n\n// ============ Runtime Detection ============\n\nconst _process = typeof process !== \"undefined\" ? process : undefined\n\nfunction getEnv(key: string): string | undefined {\n return _process?.env?.[key]\n}\n\nfunction writeStderr(text: string): void {\n if (_process?.stderr?.write) {\n _process.stderr.write(text + \"\\n\")\n } else {\n console.error(text)\n }\n}\n\n// ============ Formatting ============\n\n/** Serialize Error.cause chains up to a max depth */\nexport function serializeCause(cause: unknown, maxDepth: number = 3): unknown {\n if (maxDepth <= 0 || cause === undefined || cause === null) return undefined\n if (cause instanceof Error) {\n const result: Record<string, unknown> = {\n name: cause.name,\n message: cause.message,\n stack: cause.stack,\n }\n if ((cause as { code?: string }).code)\n result.code = (cause as { code?: string }).code\n if (cause.cause !== undefined) {\n result.cause = serializeCause(cause.cause, maxDepth - 1)\n }\n return result\n }\n // Non-Error cause (spec allows any value)\n return cause\n}\n\nexport function safeStringify(value: unknown): string {\n const seen = new WeakSet()\n return JSON.stringify(value, (_key, val) => {\n if (typeof val === \"bigint\") return val.toString()\n if (typeof val === \"symbol\") return val.toString()\n if (val instanceof Error) {\n const result: Record<string, unknown> = {\n message: val.message,\n stack: val.stack,\n name: val.name,\n }\n if ((val as { code?: string }).code)\n result.code = (val as { code?: string }).code\n if (val.cause !== undefined) result.cause = serializeCause(val.cause)\n return result\n }\n if (typeof val === \"object\" && val !== null) {\n if (seen.has(val)) return \"[Circular]\"\n seen.add(val)\n }\n return val\n })\n}\n\nexport function formatConsoleEvent(event: Event): string {\n const time = pc.dim(\n new Date(event.time).toISOString().split(\"T\")[1]?.split(\".\")[0] || \"\",\n )\n const ns = pc.cyan(event.namespace)\n\n if (event.kind === \"span\") {\n const message = `(${event.duration}ms)`\n let output = `${time} ${pc.magenta(\"SPAN\")} ${ns} ${message}`\n if (event.props && Object.keys(event.props).length > 0) {\n output += ` ${pc.dim(safeStringify(event.props))}`\n }\n return output\n }\n\n let levelStr: string\n switch (event.level) {\n case \"trace\":\n levelStr = pc.dim(\"TRACE\")\n break\n case \"debug\":\n levelStr = pc.dim(\"DEBUG\")\n break\n case \"info\":\n levelStr = pc.blue(\"INFO\")\n break\n case \"warn\":\n levelStr = pc.yellow(\"WARN\")\n break\n case \"error\":\n levelStr = pc.red(\"ERROR\")\n break\n }\n\n let output = `${time} ${levelStr} ${ns} ${event.message}`\n if (event.props && Object.keys(event.props).length > 0) {\n output += ` ${pc.dim(safeStringify(event.props))}`\n }\n return output\n}\n\nexport function formatJSONEvent(event: Event): string {\n if (event.kind === \"span\") {\n return safeStringify({\n time: new Date(event.time).toISOString(),\n level: \"span\",\n name: event.namespace,\n msg: `(${event.duration}ms)`,\n duration: event.duration,\n span_id: event.spanId,\n trace_id: event.traceId,\n parent_id: event.parentId,\n ...event.props,\n })\n }\n\n return safeStringify({\n time: new Date(event.time).toISOString(),\n level: event.level,\n name: event.namespace,\n msg: event.message,\n ...event.props,\n })\n}\n\n// ============ Namespace Filter ============\n\nexport type NsFilter = (namespace: string) => boolean\n\nfunction matchesPattern(namespace: string, pattern: string): boolean {\n if (pattern === \"*\") return true\n if (pattern.endsWith(\":*\")) {\n const prefix = pattern.slice(0, -2)\n return namespace === prefix || namespace.startsWith(prefix + \":\")\n }\n return namespace === pattern || namespace.startsWith(pattern + \":\")\n}\n\nexport function parseNsFilter(ns: string | string[]): NsFilter {\n const patterns =\n typeof ns === \"string\" ? ns.split(\",\").map((s) => s.trim()) : ns\n const includes: string[] = []\n const excludes: string[] = []\n\n for (const p of patterns) {\n if (p.startsWith(\"-\")) {\n excludes.push(p.slice(1))\n } else {\n includes.push(p)\n }\n }\n\n return (namespace: string): boolean => {\n for (const exc of excludes) {\n if (matchesPattern(namespace, exc)) return false\n }\n if (includes.length > 0) {\n for (const inc of includes) {\n if (matchesPattern(namespace, inc)) return true\n }\n return false\n }\n return true\n }\n}\n\n// ============ Console Output ============\n\n// Lazy-imported structured console sink (browser %c / terminal multi-arg).\n// Pipeline-level imports avoid a cycle: console-sinks.ts imports format\n// helpers from this file; we want the pipeline to reuse them too.\nimport { createConsoleSink as _createConsoleSink } from \"./console-sinks.js\"\n\n/**\n * Legacy text-based console writer. Retained so older code paths (the\n * env-dynamic pipeline in core.ts, writeSpan) still work, but the modern\n * pipeline now routes through `createConsoleSink` which spreads structured\n * args to `console.*` — that's what preserves expandable objects in browser\n * DevTools and makes `vi.spyOn(console, 'info')` interception work.\n *\n * For spans we still write to stderr in Node because the pipeline-level\n * spanEnabled gate handles human-readable span output; JSON/console sinks\n * route spans through their normal formatters when invoked directly.\n */\nexport function writeToConsole(text: string, event: Event): void {\n if (event.kind === \"span\") {\n writeStderr(text)\n return\n }\n // Arrow dispatch — no .bind() — so vi.spyOn(console, …) installed AFTER\n // this module loaded still intercepts. DevTools frame attribution is\n // determined by the caller of writeToConsole, not by this switch.\n switch (event.level) {\n case \"trace\":\n case \"debug\":\n console.debug(text)\n break\n case \"info\":\n console.info(text)\n break\n case \"warn\":\n console.warn(text)\n break\n case \"error\":\n console.error(text)\n break\n }\n}\n\n// ============ Sinks ============\n\n/**\n * Console sink used inside the pipeline builder. Delegates to the structured\n * console sink (browser %c or terminal multi-arg) so the pipeline preserves\n * expandable user args end-to-end.\n */\nfunction createConsoleSink(format: LogFormat): (event: Event) => void {\n return _createConsoleSink(format)\n}\n\nfunction createFileSink(\n path: string,\n format: LogFormat,\n): { write: (event: Event) => void; dispose: () => void } {\n const writer = createFileWriter(path)\n const formatter = format === \"json\" ? formatJSONEvent : formatConsoleEvent\n return {\n write: (event: Event) => writer.write(formatter(event)),\n dispose: () => writer.close(),\n }\n}\n\nfunction isNodeStream(obj: unknown): boolean {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n (\"_write\" in obj || \"writable\" in obj || \"fd\" in obj)\n )\n}\n\nfunction createWritableSink(\n writable: Writable,\n format: LogFormat,\n): (event: Event) => void {\n // Node.js streams (process.stderr, fs streams) default to string mode\n // Plain { write } objects default to object mode (raw Events)\n const useObjectMode = writable.objectMode ?? !isNodeStream(writable)\n if (!useObjectMode) {\n const formatter = format === \"json\" ? formatJSONEvent : formatConsoleEvent\n return (event: Event) => writable.write(formatter(event) + \"\\n\")\n }\n return (event: Event) => writable.write(event)\n}\n\n// ============ Pipeline ============\n\nexport interface Pipeline {\n dispatch: (event: Event) => void\n spanEnabled: (namespace: string) => boolean\n level: LogLevel\n dispose: () => void\n}\n\ninterface Output {\n levelPriority: number\n nsFilter: NsFilter | null\n write: (event: Event) => void\n dispose?: () => void\n /** Consecutive write failures (guarded dispatch); reset on success. */\n failures?: number\n /** Set after OUTPUT_MAX_STRIKES consecutive failures — sink is skipped. */\n disabled?: boolean\n}\n\n/** Consecutive failures before a throwing output sink is disabled. */\nconst OUTPUT_MAX_STRIKES = 3\n\n/**\n * Report a dispatch-guard failure WITHOUT going through any pipeline —\n * the error channel for the logger itself must not recurse into the logger.\n * Routes through writeStderr (the `_process` guard) for browser safety.\n */\nfunction reportGuard(what: string, err: unknown): void {\n const detail = err instanceof Error ? (err.stack ?? err.message) : String(err)\n try {\n writeStderr(`loggily: ${what}: ${detail}`)\n } catch {\n // The terminal fallback of the error channel itself: if stderr is gone\n // there is nowhere left to report, and throwing here would defeat the\n // guard's entire purpose (logger must never throw into the host).\n }\n}\n\n// ============ Discrimination ============\n\nconst VALID_CONFIG_KEYS = new Set([\n \"level\",\n \"ns\",\n \"format\",\n \"spans\",\n \"metrics\",\n \"idFormat\",\n \"sampleRate\",\n])\nconst SINK_KEYS = new Set([\"file\", \"otel\"])\n\nfunction isPojo(obj: unknown): obj is Record<string, unknown> {\n if (typeof obj !== \"object\" || obj === null) return false\n const proto = Object.getPrototypeOf(obj)\n return proto === Object.prototype || proto === null\n}\n\nfunction isWritable(obj: unknown): obj is Writable {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"write\" in obj &&\n typeof (obj as Record<string, unknown>).write === \"function\"\n )\n}\n\nfunction isValidLogLevel(val: unknown): val is LogLevel {\n return typeof val === \"string\" && val in LOG_LEVEL_PRIORITY\n}\n\n// ============ Config Types ============\n\n/** A writable sink — any object with a write method. Receives raw Event objects by default. */\nexport interface Writable {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n write: (data: any) => any\n /** Set to false to receive formatted strings instead of raw Event objects (default: true) */\n objectMode?: boolean\n}\n\n/** Config keys that set scope for subsequent siblings */\nexport interface ConfigObject {\n level?: LogLevel\n ns?: string | string[]\n format?: LogFormat\n spans?: boolean\n /** Enable per-logger metrics collection. Creates a MetricsCollector accessible via `log.metrics`. */\n metrics?: boolean\n /** ID format for trace/span IDs: \"simple\" (default) or \"w3c\" (W3C Trace Context) */\n idFormat?: \"simple\" | \"w3c\"\n /** Head-based sampling rate for new traces: 0.0 (none) to 1.0 (all, default) */\n sampleRate?: number\n}\n\n/** File output descriptor */\nexport interface FileDescriptor extends ConfigObject {\n file: string\n}\n\n/** OTEL output descriptor (Phase 4) */\nexport interface OtelDescriptor extends ConfigObject {\n otel: Record<string, unknown>\n}\n\n/** A single element in a createLogger config array */\nexport type ConfigElement =\n | ConfigObject\n | FileDescriptor\n | OtelDescriptor\n | Console\n | \"console\"\n | \"stderr\"\n | Stage\n | Writable\n | ConfigElement[]\n\n// ============ Build Pipeline ============\n\ninterface ScopeConfig {\n level: LogLevel\n ns: NsFilter | null\n format: LogFormat\n}\n\nexport function buildPipeline(\n elements: ConfigElement[],\n parentConfig?: Partial<ScopeConfig>,\n): Pipeline {\n const config: ScopeConfig = {\n level: parentConfig?.level ?? readEnvLevel(),\n ns: parentConfig?.ns ?? readEnvNs(),\n format: parentConfig?.format ?? readEnvFormat(),\n }\n // Spans always pass through explicit pipelines. { spans: false } to opt out.\n // The defaultPipeline handles TRACE env var gating separately.\n let spansEnabled = true\n\n const stages: Stage[] = []\n const outputs: Output[] = []\n const branches: Pipeline[] = []\n const disposables: (() => void)[] = []\n\n for (const element of elements) {\n // 1. Array → branch\n if (Array.isArray(element)) {\n const branch = buildPipeline(element as ConfigElement[], { ...config })\n branches.push(branch)\n disposables.push(() => branch.dispose())\n continue\n }\n\n // 2. console (literal or \"console\" string) → console sink (check before function — console is function-like)\n if (element === console || element === \"console\") {\n outputs.push({\n levelPriority: LOG_LEVEL_PRIORITY[config.level],\n nsFilter: config.ns,\n write: createConsoleSink(config.format),\n })\n continue\n }\n\n // 3. Function → stage\n if (typeof element === \"function\") {\n stages.push(element as Stage)\n continue\n }\n\n // 4. Writable ({ write }) → writable sink (checked BEFORE POJO so { write: fn } works)\n if (isWritable(element)) {\n outputs.push({\n levelPriority: LOG_LEVEL_PRIORITY[config.level],\n nsFilter: config.ns,\n write: createWritableSink(element, config.format),\n })\n continue\n }\n\n // 5. POJO → scope config or output descriptor\n if (isPojo(element)) {\n const obj = element\n const keys = Object.keys(obj)\n\n const hasSinkKey = keys.some((k) => SINK_KEYS.has(k))\n const hasUnknownKey = keys.some(\n (k) => !VALID_CONFIG_KEYS.has(k) && !SINK_KEYS.has(k),\n )\n\n if (hasUnknownKey) {\n const unknown = keys.find(\n (k) => !VALID_CONFIG_KEYS.has(k) && !SINK_KEYS.has(k),\n )\n throw new Error(\n `loggily: unknown config key \"${unknown}\" in config object. Valid keys: ${[...VALID_CONFIG_KEYS, ...SINK_KEYS].join(\", \")}`,\n )\n }\n\n if (hasSinkKey) {\n if (typeof obj.file === \"string\") {\n const outputLevel = isValidLogLevel(obj.level)\n ? obj.level\n : config.level\n const outputNs = obj.ns\n ? parseNsFilter(obj.ns as string | string[])\n : config.ns\n const outputFormat = (obj.format as LogFormat) ?? config.format\n const sink = createFileSink(obj.file, outputFormat)\n disposables.push(sink.dispose)\n outputs.push({\n levelPriority: LOG_LEVEL_PRIORITY[outputLevel],\n nsFilter: outputNs,\n write: sink.write,\n dispose: sink.dispose,\n })\n }\n if (obj.otel !== undefined) {\n throw new Error(\n \"loggily: OTEL sink is not yet implemented. See loggily/otel for the planned bridge.\",\n )\n }\n continue\n }\n\n // Scope config — update inherited config\n if (isValidLogLevel(obj.level)) config.level = obj.level\n if (obj.ns !== undefined)\n config.ns = parseNsFilter(obj.ns as string | string[])\n if (obj.format === \"console\" || obj.format === \"json\")\n config.format = obj.format\n if (obj.spans === true) spansEnabled = true\n if (obj.spans === false) spansEnabled = false\n if (obj.idFormat === \"simple\" || obj.idFormat === \"w3c\")\n setIdFormat(obj.idFormat)\n if (typeof obj.sampleRate === \"number\") setSampleRate(obj.sampleRate)\n continue\n }\n\n // 6. String \"stderr\" → stderr sink\n if (element === \"stderr\" && typeof process !== \"undefined\") {\n outputs.push({\n levelPriority: LOG_LEVEL_PRIORITY[config.level],\n nsFilter: config.ns,\n write: createWritableSink(\n process.stderr as unknown as Writable,\n config.format,\n ),\n })\n continue\n }\n\n throw new Error(\n `loggily: unsupported config element of type \"${typeof element}\". ` +\n 'Config arrays accept: objects (config), arrays (branches), functions (stages), console, \"console\", or writables ({ write }).',\n )\n }\n\n // Guarded dispatch: a logger must never throw into its host app. Stage\n // throws drop the event (fail-closed — a broken redaction stage must not\n // leak unprocessed events); output throws are isolated per-sink with a\n // strike counter; branch throws are isolated. All failures are reported\n // once via reportGuard (direct stderr — never through the pipeline).\n const stageReported: boolean[] = []\n const branchReported: boolean[] = []\n const dispatch = (event: Event): void => {\n // Span gate: { spans: false } disables span output for this pipeline\n if (event.kind === \"span\" && !spansEnabled) return\n\n let e: Event = event\n for (let i = 0; i < stages.length; i++) {\n let result: Event | null | void\n try {\n result = stages[i]!(e)\n } catch (err) {\n if (!stageReported[i]) {\n stageReported[i] = true\n reportGuard(\n `stage #${i} threw — dropping event, fail-closed (reported once)`,\n err,\n )\n }\n return\n }\n if (result === null) return\n if (result !== undefined) e = result\n }\n for (const output of outputs) {\n if (output.disabled) continue\n if (\n e.kind === \"log\" &&\n LOG_LEVEL_PRIORITY[e.level] < output.levelPriority\n )\n continue\n if (output.nsFilter && !output.nsFilter(e.namespace)) continue\n try {\n output.write(e)\n if (output.failures) output.failures = 0\n } catch (err) {\n output.failures = (output.failures ?? 0) + 1\n if (output.failures === 1) {\n reportGuard(\"output write threw — event lost for this sink\", err)\n }\n if (output.failures >= OUTPUT_MAX_STRIKES) {\n output.disabled = true\n reportGuard(\n `output disabled after ${output.failures} consecutive failures`,\n err,\n )\n }\n }\n }\n for (let i = 0; i < branches.length; i++) {\n try {\n branches[i]!.dispatch(e)\n } catch (err) {\n // Branch pipelines guard themselves; this catch is the backstop for\n // anything that escapes (e.g. a foreign Pipeline implementation).\n if (!branchReported[i]) {\n branchReported[i] = true\n reportGuard(`branch #${i} dispatch threw (reported once)`, err)\n }\n }\n }\n }\n\n const spanEnabledForNamespace = (namespace: string): boolean => {\n if (!spansEnabled) return false\n if (\n outputs.some((output) => !output.nsFilter || output.nsFilter(namespace))\n )\n return true\n if (branches.some((branch) => branch.spanEnabled(namespace))) return true\n // Stages may consume or forward spans themselves, so keep `.span` available\n // for stage-only explicit pipelines.\n return stages.length > 0\n }\n\n return {\n dispatch,\n spanEnabled: spanEnabledForNamespace,\n level: config.level,\n dispose: () => {\n for (const d of disposables) d()\n },\n }\n}\n\n// ============ Env Var Readers (exported for withEnvDefaults plugin) ============\n\nexport function readEnvLevel(): LogLevel {\n const env = getEnv(\"LOG_LEVEL\")?.toLowerCase()\n let level: LogLevel =\n env === \"trace\" ||\n env === \"debug\" ||\n env === \"info\" ||\n env === \"warn\" ||\n env === \"error\" ||\n env === \"silent\"\n ? env\n : \"info\"\n\n const debugEnv = getEnv(\"DEBUG\")\n if (debugEnv && LOG_LEVEL_PRIORITY[level] > LOG_LEVEL_PRIORITY.debug) {\n level = \"debug\"\n }\n\n return level\n}\n\n/**\n * Namespace-aware level: only bumps to debug if the namespace matches the DEBUG filter.\n * This enables zero-overhead conditional gating — `log.debug?.()` returns undefined\n * for namespaces outside the DEBUG filter, skipping argument evaluation entirely.\n */\nexport function readEnvLevelForNamespace(namespace: string): LogLevel {\n const env = getEnv(\"LOG_LEVEL\")?.toLowerCase()\n const baseLevel: LogLevel =\n env === \"trace\" ||\n env === \"debug\" ||\n env === \"info\" ||\n env === \"warn\" ||\n env === \"error\" ||\n env === \"silent\"\n ? env\n : \"info\"\n\n const debugEnv = getEnv(\"DEBUG\")\n if (debugEnv && LOG_LEVEL_PRIORITY[baseLevel] > LOG_LEVEL_PRIORITY.debug) {\n // DEBUG is set and would bump level — check if this namespace matches\n const nsFilter = readEnvNs()\n if (nsFilter && nsFilter(namespace)) {\n return \"debug\" // namespace matches DEBUG filter — bump to debug\n }\n // Namespace doesn't match — keep the configured level\n return baseLevel\n }\n\n return baseLevel\n}\n\nexport function readEnvNs(): NsFilter | null {\n const debugEnv = getEnv(\"DEBUG\")\n if (!debugEnv) return null\n\n const parts = debugEnv.split(\",\").map((s) => s.trim())\n return parseNsFilter(parts)\n}\n\nexport function readEnvFormat(): LogFormat {\n const envFormat = getEnv(\"LOG_FORMAT\")?.toLowerCase()\n if (envFormat === \"json\") return \"json\"\n if (envFormat === \"console\") return \"console\"\n if (getEnv(\"TRACE_FORMAT\") === \"json\") return \"json\"\n if (getEnv(\"NODE_ENV\") === \"production\") return \"json\"\n return \"console\"\n}\n\nexport function readEnvTrace(): { enabled: boolean; filter: NsFilter | null } {\n const traceEnv = getEnv(\"TRACE\")\n if (!traceEnv) return { enabled: false, filter: null }\n if (traceEnv === \"1\" || traceEnv === \"true\")\n return { enabled: true, filter: null }\n const prefixes = traceEnv.split(\",\").map((s) => s.trim())\n return {\n enabled: true,\n filter: (namespace: string) => {\n for (const prefix of prefixes) {\n if (matchesPattern(namespace, prefix)) return true\n }\n return false\n },\n }\n}\n","/**\n * loggily v2 — Structured logging with spans\n *\n * One import. Objects configure. Arrays branch. Values write.\n *\n * @example\n * const log = createLogger('myapp')\n * log.info?.('starting')\n *\n * @example\n * const log = createLogger('myapp', [\n * { level: 'debug', ns: '-sql' },\n * console,\n * { file: '/tmp/app.log', level: 'info', format: 'json' },\n * ])\n * log.info?.('server started', { port: 3000 })\n */\n\nimport {\n type Event,\n type LogEvent,\n type SpanEvent,\n type Pipeline,\n type Stage,\n type LogLevel,\n type OutputLogLevel,\n type LogFormat,\n type NsFilter,\n type ConfigElement,\n type ConfigObject,\n LOG_LEVEL_PRIORITY,\n buildPipeline,\n safeStringify,\n serializeCause,\n readEnvLevel,\n readEnvLevelForNamespace,\n readEnvNs,\n readEnvFormat,\n readEnvTrace,\n writeToConsole,\n formatConsoleEvent,\n formatJSONEvent,\n parseNsFilter,\n} from \"./pipeline.js\"\nimport { createConsoleSink as createStructuredConsoleSink } from \"./console-sinks.js\"\n\nimport {\n createMetricsCollector as _createMetricsCollector,\n withMetrics as _withMetrics,\n} from \"./metrics.js\"\n\nexport type {\n Event,\n LogEvent,\n SpanEvent,\n Stage,\n LogLevel,\n OutputLogLevel,\n LogFormat,\n ConfigElement,\n}\nexport { LOG_LEVEL_PRIORITY, safeStringify }\n\n// ============ Metrics ============\n\nexport interface SpanRecord {\n readonly name: string\n readonly durationMs: number\n}\n\nexport interface SpanRecorder {\n recordSpan(data: SpanRecord): void\n}\n\n// ============ Types ============\n\nexport type LazyMessage = string | (() => string)\nexport type LazyProps =\n | Record<string, unknown>\n | (() => Record<string, unknown>)\n\nexport interface SpanData {\n readonly id: string\n readonly traceId: string\n readonly parentId: string | null\n readonly startTime: number\n readonly endTime: number | null\n readonly duration: number | null\n [key: string]: unknown\n}\n\nexport interface Logger extends Disposable {\n readonly name: string\n readonly props: Readonly<Record<string, unknown>>\n readonly level: LogLevel\n\n dispatch(event: Event): void\n\n trace(message: LazyMessage, data?: Record<string, unknown>): void\n debug(message: LazyMessage, data?: Record<string, unknown>): void\n info(message: LazyMessage, data?: Record<string, unknown>): void\n warn(message: LazyMessage, data?: Record<string, unknown>): void\n error(message: LazyMessage, data?: Record<string, unknown>): void\n error(error: Error, data?: Record<string, unknown>): void\n error(error: Error, message: string, data?: Record<string, unknown>): void\n\n /** @deprecated Use .child() */\n logger(namespace?: string, props?: Record<string, unknown>): ConditionalLogger\n span(namespace?: string, props?: LazyProps): SpanLogger\n child(namespace: string, props?: Record<string, unknown>): ConditionalLogger\n child(context: Record<string, unknown>): ConditionalLogger\n end(): void\n}\n\nexport interface SpanLogger extends ConditionalLogger, Disposable {\n readonly spanData: SpanData & { [key: string]: unknown }\n // Override optional `span` from ConditionalLogger: a SpanLogger is always\n // produced by `withSpans()`, so nested `.span()` is guaranteed present.\n span(namespace?: string, props?: LazyProps): SpanLogger\n /**\n * Record a stopwatch-style checkpoint within this span. Each `lap(name)`\n * captures the time since the previous lap (or span start for the first\n * lap). On span end, laps are emitted in the span event's props as\n * `laps: { name1: deltaMs, name2: deltaMs, ... }` for compact output, plus\n * stored verbatim on `spanData.laps` for programmatic consumers.\n *\n * Use laps when you want sub-span timing without the noise of nested span\n * lines. Nested spans give per-phase log lines + parent context; laps give\n * one summary line with phase deltas in props.\n *\n * @example\n * using span = log.span?.(\"materialize\")\n * loadInputs()\n * span?.lap(\"inputs\")\n * computeMatches()\n * span?.lap(\"matches\")\n * writeOutputs()\n * span?.lap(\"outputs\")\n * // Span end → SPAN ... laps: { inputs: 12, matches: 340, outputs: 8 }\n */\n lap(name: string): void\n}\n\n/**\n * @deprecated `createLogger()` now returns `ConditionalLogger`; use\n * `log.span?.(\"op\")` because spans are only present when enabled.\n */\nexport type SpannedLogger = ConditionalLogger\n\n// ============ ConditionalLogger ============\n\nexport interface ConditionalLogger extends Disposable {\n readonly name: string\n readonly props: Readonly<Record<string, unknown>>\n readonly level: LogLevel\n\n /** Metrics collector, present when `{ metrics: true }` is in config or `withMetrics()` was applied */\n readonly metrics?: import(\"./metrics.js\").MetricsCollector\n\n dispatch(event: Event): void\n\n trace?: (message: LazyMessage, data?: Record<string, unknown>) => void\n debug?: (message: LazyMessage, data?: Record<string, unknown>) => void\n info?: (message: LazyMessage, data?: Record<string, unknown>) => void\n warn?: (message: LazyMessage, data?: Record<string, unknown>) => void\n error?: {\n (message: LazyMessage, data?: Record<string, unknown>): void\n (error: Error, data?: Record<string, unknown>): void\n (error: Error, message: string, data?: Record<string, unknown>): void\n }\n\n /** @deprecated Use .child() */\n logger(namespace?: string, props?: Record<string, unknown>): ConditionalLogger\n span?(namespace?: string, props?: LazyProps): SpanLogger\n child(namespace: string, props?: Record<string, unknown>): ConditionalLogger\n child(context: Record<string, unknown>): ConditionalLogger\n end(): void\n}\n\nconst SPAN_ENABLED = Symbol(\"loggily.spanEnabled\")\n\ntype SpanEnabledChecker = (namespace: string) => boolean\n\ninterface SpanEnabledCarrier {\n [SPAN_ENABLED]?: SpanEnabledChecker\n}\n\nfunction spanIsEnabled(logger: ConditionalLogger, namespace: string): boolean {\n if (collectSpans) return true\n const checker = (logger as unknown as SpanEnabledCarrier)[SPAN_ENABLED]\n return checker ? checker(namespace) : true\n}\n\n// ============ ID Generation ============\n\nimport {\n generateSpanId,\n generateTraceId,\n resetIdCounters,\n shouldSample,\n setIdFormat,\n setSampleRate,\n} from \"./tracing.js\"\nimport type { IdFormat } from \"./tracing.js\"\n\nexport function resetIds(): void {\n resetIdCounters()\n}\n\n// ============ Context Propagation Hooks ============\n\nlet _getContextTags: (() => Record<string, string>) | null = null\nlet _getContextParent:\n | (() => { spanId: string; traceId: string } | null)\n | null = null\nlet _enterContext:\n | ((spanId: string, traceId: string, parentId: string | null) => void)\n | null = null\nlet _exitContext: ((spanId: string) => void) | null = null\n\n/** @internal */\nexport function _setContextHooks(hooks: {\n getContextTags: () => Record<string, string>\n getContextParent: () => { spanId: string; traceId: string } | null\n enterContext: (\n spanId: string,\n traceId: string,\n parentId: string | null,\n ) => void\n exitContext: (spanId: string) => void\n}): void {\n _getContextTags = hooks.getContextTags\n _getContextParent = hooks.getContextParent\n _enterContext = hooks.enterContext\n _exitContext = hooks.exitContext\n}\n\n/** @internal */\nexport function _clearContextHooks(): void {\n _getContextTags = null\n _getContextParent = null\n _enterContext = null\n _exitContext = null\n}\n\n// ============ SpanData Proxy ============\n\ninterface SpanDataFields {\n id: string\n traceId: string\n parentId: string | null\n startTime: number\n endTime: number | null\n duration: number | null\n}\n\nexport function createSpanDataProxy(\n getFields: () => SpanDataFields,\n attrs: Record<string, unknown>,\n): SpanData {\n const READONLY_KEYS = new Set([\n \"id\",\n \"traceId\",\n \"parentId\",\n \"startTime\",\n \"endTime\",\n \"duration\",\n ])\n return new Proxy(attrs, {\n get(_target, prop) {\n if (READONLY_KEYS.has(prop as string)) {\n return getFields()[prop as keyof SpanDataFields]\n }\n return attrs[prop as string]\n },\n set(_target, prop, value) {\n if (READONLY_KEYS.has(prop as string)) {\n return false\n }\n attrs[prop as string] = value\n return true\n },\n }) as SpanData\n}\n\n// ============ Span Collection ============\n\nconst collectedSpans: SpanData[] = []\nlet collectSpans = false\n\nexport function startCollecting(): void {\n collectSpans = true\n collectedSpans.length = 0\n}\n\nexport function stopCollecting(): SpanData[] {\n collectSpans = false\n return [...collectedSpans]\n}\n\nexport function getCollectedSpans(): SpanData[] {\n return [...collectedSpans]\n}\n\nexport function clearCollectedSpans(): void {\n collectedSpans.length = 0\n}\n\n// ============ Implementation ============\n\nfunction resolveMessage(msg: LazyMessage): string {\n return typeof msg === \"function\" ? msg() : msg\n}\n\ninterface SpanLap {\n /** Lap name. */\n readonly name: string\n /** Wall time (ms since span start) at lap creation. */\n readonly elapsedMs: number\n /** Delta (ms) since previous lap, or since span start for the first lap. */\n readonly deltaMs: number\n}\n\ninterface MutableSpanData {\n id: string\n traceId: string\n parentId: string | null\n startTime: number\n endTime: number | null\n duration: number | null\n attrs: Record<string, unknown>\n laps: SpanLap[]\n}\n\nfunction createLoggerImpl(\n name: string,\n props: Record<string, unknown>,\n pipeline: Pipeline,\n): Logger {\n const emitLog = (\n level: OutputLogLevel,\n msgOrError: LazyMessage | Error,\n dataOrMsg?: Record<string, unknown> | string,\n extraData?: Record<string, unknown>,\n ): void => {\n let message: string\n let data: Record<string, unknown> | undefined\n // Raw user args, in call order (after the message). Console sinks spread\n // these to console.* so DevTools keeps objects expandable. We preserve\n // the original references — no JSON-stringifying, no ANSI embedding.\n //\n // Convention:\n // - `log.error(err, …)` → prepend the Error so DevTools shows its\n // clickable stack.\n // - Any structured data (merged context + props + user data) goes in\n // as a single trailing object so it renders as one expandable node.\n const userArgs: unknown[] = []\n\n if (msgOrError instanceof Error) {\n const err = msgOrError\n const contextTags = _getContextTags?.() ?? {}\n if (typeof dataOrMsg === \"string\") {\n message = dataOrMsg\n data = {\n ...contextTags,\n ...props,\n ...extraData,\n error_type: err.name,\n error_message: err.message,\n error_stack: err.stack,\n error_code: (err as { code?: string }).code,\n error_cause:\n err.cause !== undefined ? serializeCause(err.cause) : undefined,\n }\n } else {\n message = err.message\n data = {\n ...contextTags,\n ...props,\n ...(dataOrMsg as Record<string, unknown>),\n error_type: err.name,\n error_stack: err.stack,\n error_code: (err as { code?: string }).code,\n error_cause:\n err.cause !== undefined ? serializeCause(err.cause) : undefined,\n }\n }\n userArgs.push(err)\n if (data && Object.keys(data).length > 0) userArgs.push(data)\n } else {\n message = resolveMessage(msgOrError)\n const contextTags = _getContextTags?.()\n data =\n contextTags && Object.keys(contextTags).length > 0\n ? {\n ...contextTags,\n ...props,\n ...(dataOrMsg as Record<string, unknown>),\n }\n : Object.keys(props).length > 0 || dataOrMsg\n ? { ...props, ...(dataOrMsg as Record<string, unknown>) }\n : undefined\n if (data && Object.keys(data).length > 0) userArgs.push(data)\n }\n\n const event: LogEvent = {\n kind: \"log\",\n time: Date.now(),\n namespace: name,\n level,\n message,\n props: data,\n userArgs,\n }\n pipeline.dispatch(event)\n }\n\n const logger: Logger & SpanEnabledCarrier = {\n name,\n props: Object.freeze({ ...props }),\n\n get level(): LogLevel {\n return pipeline.level\n },\n\n [SPAN_ENABLED](namespace: string): boolean {\n return pipeline.spanEnabled(namespace)\n },\n\n dispatch(event: Event): void {\n pipeline.dispatch(event)\n },\n\n [Symbol.dispose](): void {\n pipeline.dispose()\n },\n\n trace: (msg, data) => emitLog(\"trace\", msg, data),\n debug: (msg, data) => emitLog(\"debug\", msg, data),\n info: (msg, data) => emitLog(\"info\", msg, data),\n warn: (msg, data) => emitLog(\"warn\", msg, data),\n error: (\n msgOrError: LazyMessage | Error,\n dataOrMsg?: Record<string, unknown> | string,\n extraData?: Record<string, unknown>,\n ) => emitLog(\"error\", msgOrError, dataOrMsg, extraData),\n\n /** @deprecated Use .child() instead */\n logger(\n namespace?: string,\n childProps?: Record<string, unknown>,\n ): ConditionalLogger {\n return this.child(namespace ?? \"\", childProps)\n },\n\n span(_namespace?: string, _childProps?: LazyProps): SpanLogger {\n throw new Error(\n \"loggily: span() requires the withSpans() plugin. Use pipe(baseCreateLogger, withSpans()) or the default createLogger.\",\n )\n },\n\n child(\n namespaceOrContext?: string | Record<string, unknown>,\n childProps?: Record<string, unknown>,\n ): ConditionalLogger {\n if (typeof namespaceOrContext === \"string\") {\n const childName = namespaceOrContext\n ? `${name}:${namespaceOrContext}`\n : name\n const mergedProps = { ...props, ...childProps }\n return wrapConditional(\n createLoggerImpl(childName, mergedProps, pipeline),\n () => pipeline.level,\n )\n }\n // Object -> context fields, same namespace\n return wrapConditional(\n createLoggerImpl(name, { ...props, ...namespaceOrContext }, pipeline),\n () => pipeline.level,\n )\n },\n\n end(): void {\n // no-op for non-span loggers\n },\n }\n\n return logger\n}\n\n// ============ ConditionalLogger Proxy ============\n\nfunction wrapConditional(\n logger: Logger,\n getLevel: () => LogLevel,\n): ConditionalLogger {\n return new Proxy(logger as ConditionalLogger, {\n get(target, prop: string | symbol) {\n if (\n typeof prop === \"string\" &&\n prop in LOG_LEVEL_PRIORITY &&\n prop !== \"silent\"\n ) {\n if (\n LOG_LEVEL_PRIORITY[prop as keyof typeof LOG_LEVEL_PRIORITY] <\n LOG_LEVEL_PRIORITY[getLevel()]\n ) {\n return undefined\n }\n }\n // span is optional on ConditionalLogger: return undefined if base impl is the error-thrower\n if (prop === \"span\") {\n const val = (target as unknown as Record<string | symbol, unknown>)[\n prop\n ]\n // If span is the default error-throwing stub, return undefined (making it optional)\n if (val === baseSpanStub) return undefined\n return val\n }\n return (target as unknown as Record<string | symbol, unknown>)[prop]\n },\n })\n}\n\n// Sentinel reference for detecting the base span stub\nconst baseSpanStub = function baseSpanStub(\n _namespace?: string,\n _childProps?: LazyProps,\n): SpanLogger {\n throw new Error(\n \"loggily: span() requires the withSpans() plugin. Use pipe(baseCreateLogger, withSpans()) or the default createLogger.\",\n )\n}\n\n// ============ withSpans Plugin ============\n\n/**\n * Plugin: adds span creation capability to loggers.\n * Without this plugin, `.span` is undefined on ConditionalLogger.\n * Included by default in `createLogger`.\n */\nexport function withSpans(): LoggerPlugin {\n return (factory, _ctx) => {\n return (name, configOrProps?) => {\n const logger = factory(name, configOrProps)\n return augmentWithSpans(logger, null, null, true)\n }\n }\n}\n\ninterface SpanState {\n parentSpanId: string | null\n traceId: string | null\n traceSampled: boolean\n}\n\nfunction augmentWithSpans(\n logger: ConditionalLogger,\n parentSpanId: string | null,\n traceId: string | null,\n traceSampled: boolean,\n): ConditionalLogger {\n const spanState: SpanState = { parentSpanId, traceId, traceSampled }\n const spanMethod = spanIsEnabled(logger, logger.name)\n ? createSpanMethod(logger, spanState)\n : undefined\n\n return new Proxy(logger, {\n get(target, prop: string | symbol) {\n if (prop === \"span\") {\n return spanMethod\n }\n if (prop === \"child\") {\n return function child(\n namespaceOrContext?: string | Record<string, unknown>,\n childProps?: Record<string, unknown>,\n ): ConditionalLogger {\n const childLogger = target.child(\n namespaceOrContext as string,\n childProps,\n )\n // Child loggers inherit span state (parent/trace context)\n return augmentWithSpans(\n childLogger,\n spanState.parentSpanId,\n spanState.traceId,\n spanState.traceSampled,\n )\n }\n }\n if (prop === \"logger\") {\n return function logger(\n namespace?: string,\n childProps?: Record<string, unknown>,\n ): ConditionalLogger {\n const childLogger = target.logger(namespace, childProps)\n return augmentWithSpans(\n childLogger,\n spanState.parentSpanId,\n spanState.traceId,\n spanState.traceSampled,\n )\n }\n }\n return (target as unknown as Record<string | symbol, unknown>)[prop]\n },\n })\n}\n\nfunction createSpanMethod(\n logger: ConditionalLogger,\n spanState: SpanState,\n): (namespace?: string, childProps?: LazyProps) => SpanLogger {\n return (namespace?: string, childProps?: LazyProps): SpanLogger => {\n const childName = namespace ? `${logger.name}:${namespace}` : logger.name\n const resolvedChildProps =\n typeof childProps === \"function\" ? childProps() : childProps\n const mergedProps = { ...logger.props, ...resolvedChildProps }\n const newSpanId = generateSpanId()\n\n let resolvedParentId = spanState.parentSpanId\n let resolvedTraceId = spanState.traceId\n\n if (!resolvedParentId && _getContextParent) {\n const ctxParent = _getContextParent()\n if (ctxParent) {\n resolvedParentId = ctxParent.spanId\n resolvedTraceId = resolvedTraceId || ctxParent.traceId\n }\n }\n\n const isNewTrace = !resolvedTraceId\n const finalTraceId = resolvedTraceId || generateTraceId()\n const sampled = isNewTrace ? shouldSample() : spanState.traceSampled\n\n const newSpanData: MutableSpanData = {\n id: newSpanId,\n traceId: finalTraceId,\n parentId: resolvedParentId,\n startTime: Date.now(),\n endTime: null,\n duration: null,\n attrs: {},\n laps: [],\n }\n\n // Create a child logger for the span to emit logs through\n const childLogger = logger.child(namespace ?? \"\", resolvedChildProps)\n // Augment the child with span capability, setting this span as parent\n const spanAugmented = augmentWithSpans(\n childLogger,\n newSpanId,\n finalTraceId,\n sampled,\n )\n\n _enterContext?.(newSpanId, finalTraceId, resolvedParentId)\n\n const disposeSpan = () => {\n if (newSpanData.endTime !== null) return\n\n newSpanData.endTime = Date.now()\n newSpanData.duration = newSpanData.endTime - newSpanData.startTime\n\n if (collectSpans) {\n collectedSpans.push(\n createSpanDataProxy(\n () => ({\n id: newSpanData.id,\n traceId: newSpanData.traceId,\n parentId: newSpanData.parentId,\n startTime: newSpanData.startTime,\n endTime: newSpanData.endTime,\n duration: newSpanData.duration,\n }),\n { ...newSpanData.attrs },\n ),\n )\n }\n\n _exitContext?.(newSpanId)\n if (sampled) {\n // Emit laps as a flat { name: deltaMs } object — compact for both\n // console and JSON output. spanData.laps stays available for\n // programmatic consumers that want elapsedMs / per-lap details.\n const lapsProp =\n newSpanData.laps.length > 0\n ? Object.fromEntries(\n newSpanData.laps.map((l) => [l.name, l.deltaMs]),\n )\n : undefined\n const spanEvent: SpanEvent = {\n kind: \"span\",\n time: newSpanData.endTime,\n namespace: childName,\n name: childName,\n duration: newSpanData.duration,\n props: {\n ...mergedProps,\n ...newSpanData.attrs,\n ...(lapsProp ? { laps: lapsProp } : {}),\n },\n spanId: newSpanData.id,\n traceId: newSpanData.traceId,\n parentId: newSpanData.parentId,\n }\n logger.dispatch(spanEvent)\n }\n }\n\n const lapImpl = (name: string): void => {\n const now = Date.now()\n const elapsedMs = now - newSpanData.startTime\n const lastLap = newSpanData.laps[newSpanData.laps.length - 1]\n const deltaMs = lastLap ? elapsedMs - lastLap.elapsedMs : elapsedMs\n newSpanData.laps.push({ name, elapsedMs, deltaMs })\n }\n\n const spanDataProxy = createSpanDataProxy(\n () => ({\n id: newSpanData.id,\n traceId: newSpanData.traceId,\n parentId: newSpanData.parentId,\n startTime: newSpanData.startTime,\n endTime: newSpanData.endTime,\n duration:\n newSpanData.endTime !== null\n ? newSpanData.endTime - newSpanData.startTime\n : Date.now() - newSpanData.startTime,\n }),\n newSpanData.attrs,\n )\n\n // Build the SpanLogger by overlaying span-specific properties onto the augmented child.\n // Allow Symbol.dispose to be overridden (withMetrics wraps it).\n let currentDispose = disposeSpan\n const spanLogger = new Proxy(spanAugmented as unknown as SpanLogger, {\n get(target, prop: string | symbol) {\n if (prop === \"spanData\") return spanDataProxy\n if (prop === Symbol.dispose) return currentDispose\n if (prop === \"end\") {\n return () => {\n if (newSpanData.endTime === null) {\n currentDispose()\n }\n }\n }\n if (prop === \"lap\") return lapImpl\n if (prop === \"name\") return childName\n if (prop === \"props\") return Object.freeze({ ...mergedProps })\n return (target as unknown as Record<string | symbol, unknown>)[prop]\n },\n set(_target, prop: string | symbol, value: unknown) {\n if (prop === Symbol.dispose) {\n currentDispose = value as () => void\n return true\n }\n return false\n },\n })\n\n return spanLogger\n }\n}\n\n// ============ Public API ============\n\n// ============ Base createLogger ============\n\n/**\n * Base createLogger — requires a config array.\n * Use the default `createLogger` export (with `withEnvDefaults`) for zero-config.\n *\n * Note: loggers from baseCreateLogger do NOT have `.span()` capability.\n * Use `pipe(baseCreateLogger, withSpans())` or the default `createLogger` for spans.\n */\nexport function baseCreateLogger(\n name: string,\n configOrProps?: ConfigElement[] | Record<string, unknown>,\n): ConditionalLogger {\n let pipeline: Pipeline\n let props: Record<string, unknown> = {}\n\n if (Array.isArray(configOrProps)) {\n pipeline = buildPipeline(configOrProps)\n } else if (configOrProps && typeof configOrProps === \"object\") {\n props = configOrProps as Record<string, unknown>\n pipeline = buildPipeline([\"console\"])\n } else {\n pipeline = buildPipeline([\"console\"])\n }\n\n const logger = createLoggerImpl(name, props, pipeline)\n // Replace the span method with the sentinel stub so wrapConditional can detect it\n ;(logger as unknown as Record<string, unknown>).span = baseSpanStub\n return wrapConditional(logger, () => pipeline.level)\n}\n\n// ============ Compose ============\n\nexport type LoggerFactory = (\n name: string,\n configOrProps?: ConfigElement[] | Record<string, unknown>,\n) => ConditionalLogger\n\nexport interface PluginCtx {\n [key: string]: unknown\n}\n\nexport type LoggerPlugin = (\n factory: LoggerFactory,\n ctx: PluginCtx,\n) => LoggerFactory\n\nexport function pipe(\n base: LoggerFactory,\n ...plugins: LoggerPlugin[]\n): LoggerFactory {\n const ctx: PluginCtx = {}\n return plugins.reduce((factory, plugin) => plugin(factory, ctx), base)\n}\n\n// ============ withEnvDefaults Plugin ============\n\nconst _process = typeof process !== \"undefined\" ? process : undefined\nconst _env = _process?.env ?? ({} as Record<string, string | undefined>)\n\n// Env config — read fresh each dispatch (process.env access is ~ns, parsing is trivial)\nfunction currentLevel(): LogLevel {\n return readEnvLevel()\n}\nfunction currentNs(): NsFilter | null {\n return readEnvNs()\n}\nfunction currentFormat(): LogFormat {\n return readEnvFormat()\n}\nfunction currentTrace(): { enabled: boolean; filter: NsFilter | null } {\n return readEnvTrace()\n}\n\n/**\n * Writer signature for {@link addWriter}.\n *\n * Receives the pre-formatted text (per the active LOG_FORMAT) plus the\n * structural fields needed to route or filter writes downstream:\n * - `level`: log level (or `\"span\"`) for level-aware sinks\n * - `namespace`: emitting namespace; the `{ ns }` filter on\n * {@link addWriter} routes by this value\n * - `event`: full structured Event so JSONL sinks can re-serialize with\n * custom fields (e.g., {@link createLogger}'s `props` end up here).\n *\n * Two-arg writers (legacy shape) keep working — JS ignores extra arguments.\n */\nexport type WriterFn = (\n formatted: string,\n level: string,\n namespace: string,\n event: Event,\n) => void\n\n// Runtime state for legacy addWriter/setSuppressConsole\nconst _writers: Array<WriterFn> = []\nlet _suppressConsole = false\n\n// File writer factory — set by index.ts (avoids node:fs in core.ts for browser compat)\nlet _logFileWriterFactory:\n | ((path: string) => { write: (s: string) => void; close: () => void })\n | null = null\n/** @internal */\nexport function _setLogFileWriterFactory(\n factory: typeof _logFileWriterFactory,\n): void {\n _logFileWriterFactory = factory\n}\n\n/**\n * Plugin: read defaults from environment variables (LOG_LEVEL, DEBUG, LOG_FORMAT, TRACE, LOG_FILE).\n * Included by default. Omit to disable env-var behavior entirely.\n *\n * When no config array is given, provides console output + env-var-based config.\n * When a config array IS given, env vars are already used as defaults by buildPipeline.\n * Legacy setters (setLogLevel, addWriter, etc.) affect loggers created without explicit config.\n */\nexport function withEnvDefaults(): LoggerPlugin {\n return (factory, _ctx) => (name, configOrProps?) => {\n // Apply tracing env vars (once per logger creation, idempotent)\n const envIdFormat = _env.TRACE_ID_FORMAT?.toLowerCase()\n if (envIdFormat === \"simple\" || envIdFormat === \"w3c\") {\n setIdFormat(envIdFormat as IdFormat)\n }\n const envSampleRate = _env.TRACE_SAMPLE_RATE\n if (envSampleRate !== undefined) {\n const rate = Number.parseFloat(envSampleRate)\n if (!Number.isNaN(rate) && rate >= 0 && rate <= 1) {\n setSampleRate(rate)\n }\n }\n\n // Explicit config array — pass through, buildPipeline reads env defaults\n if (Array.isArray(configOrProps)) return factory(name, configOrProps)\n\n // No config array — use env-dynamic pipeline.\n // Pass a config array through the factory so all upstream plugins get applied,\n // with a stage that delegates to the env pipeline for dynamic dispatch.\n const envPipeline = createEnvPipeline()\n const envStage: ConfigElement = (event: Event) => {\n envPipeline.dispatch(event)\n return null // consume the event (env pipeline handles all output)\n }\n\n // Props are passed as the first config element to include them in log output\n if (configOrProps && typeof configOrProps === \"object\") {\n // Object props — create logger with props + env pipeline\n // We need to pass props AND a config array. But factory only accepts one or the other.\n // Solution: create via factory with config array, then the child() with props.\n const logger = factory(name, [{ level: \"trace\" as LogLevel }, envStage])\n return applyNamespaceGating(\n logger.child(configOrProps as Record<string, unknown>),\n )\n }\n\n return applyNamespaceGating(\n factory(name, [{ level: \"trace\" as LogLevel }, envStage]),\n )\n }\n}\n\n/**\n * Wrap a logger so that conditional method gating is namespace-aware.\n * When DEBUG=myapp:db, only loggers whose namespace matches get debug enabled.\n * Without this, all loggers get debug because readEnvLevel() bumps globally.\n */\nfunction applyNamespaceGating(logger: ConditionalLogger): ConditionalLogger {\n return new Proxy(logger, {\n get(target, prop: string | symbol) {\n if (prop === SPAN_ENABLED) {\n return (namespace: string): boolean => {\n if (collectSpans) return true\n const trace = currentTrace()\n if (!trace.enabled) return false\n if (trace.filter && !trace.filter(namespace)) return false\n const ns = currentNs()\n if (ns && !ns(namespace)) return false\n return true\n }\n }\n if (\n typeof prop === \"string\" &&\n prop in LOG_LEVEL_PRIORITY &&\n prop !== \"silent\"\n ) {\n const nsLevel = readEnvLevelForNamespace(target.name)\n if (\n LOG_LEVEL_PRIORITY[prop as keyof typeof LOG_LEVEL_PRIORITY] <\n LOG_LEVEL_PRIORITY[nsLevel]\n ) {\n return undefined\n }\n }\n return (target as unknown as Record<string | symbol, unknown>)[prop]\n },\n })\n}\n\nfunction createEnvPipeline(): Pipeline {\n const disposables: (() => void)[] = []\n const logFile = _env.LOG_FILE\n let fileSink: ((event: Event) => void) | null = null\n if (logFile && _logFileWriterFactory) {\n const writer = _logFileWriterFactory(logFile)\n fileSink = (event: Event) => {\n const fmt =\n currentFormat() === \"json\" ? formatJSONEvent : formatConsoleEvent\n writer.write(fmt(event))\n }\n disposables.push(() => writer.close())\n }\n\n const dispatch = (event: Event): void => {\n if (\n event.kind === \"log\" &&\n LOG_LEVEL_PRIORITY[event.level] < LOG_LEVEL_PRIORITY[currentLevel()]\n )\n return\n if (event.kind === \"span\") {\n const trace = currentTrace()\n if (!trace.enabled) return\n if (trace.filter && !trace.filter(event.namespace)) return\n }\n const ns = currentNs()\n if (ns && !ns(event.namespace)) return\n\n // Writers receive pre-formatted text + structural fields. Two-arg\n // legacy writers ignore the trailing namespace + event args.\n const format = currentFormat()\n const formatter = format === \"json\" ? formatJSONEvent : formatConsoleEvent\n const text = formatter(event)\n const lvl = event.kind === \"log\" ? event.level : \"span\"\n for (const w of _writers) w(text, lvl, event.namespace, event)\n\n // Structured console output: browser DevTools sees %c-styled prefix +\n // expandable objects; Node sees ANSI prefix + util.format-inspectable\n // objects. The sink is recreated per dispatch so LOG_FORMAT env flips\n // take effect without logger rebuild.\n if (!_suppressConsole) createStructuredConsoleSink(format)(event)\n fileSink?.(event)\n }\n\n return {\n dispatch,\n spanEnabled(namespace: string): boolean {\n const trace = currentTrace()\n if (!trace.enabled) return false\n if (trace.filter && !trace.filter(namespace)) return false\n const ns = currentNs()\n if (ns && !ns(namespace)) return false\n return true\n },\n get level() {\n return currentLevel()\n },\n dispose: () => {\n for (const d of disposables) d()\n },\n }\n}\n\n// ============ withConfigMetrics Plugin ============\n\n/**\n * Plugin: when `{ metrics: true }` appears in the config array, automatically\n * creates a MetricsCollector and applies withMetrics to the logger.\n * The collector is accessible via `logger.metrics`.\n */\nexport function withConfigMetrics(): LoggerPlugin {\n return (factory, _ctx) => {\n return (name, configOrProps?) => {\n const logger = factory(name, configOrProps)\n\n // Only scan config arrays, not props objects\n if (!Array.isArray(configOrProps)) return logger\n\n // Check if any config object has metrics: true (flat scan, not into branches)\n const hasMetrics = configOrProps.some(\n (el) =>\n typeof el === \"object\" &&\n el !== null &&\n !Array.isArray(el) &&\n \"metrics\" in el &&\n (el as Record<string, unknown>).metrics === true,\n )\n\n if (!hasMetrics) return logger\n\n const collector = _createMetricsCollector()\n return _withMetrics(collector)(logger)\n }\n }\n}\n\n/** Default createLogger — includes withEnvDefaults + withSpans + withConfigMetrics. */\nexport const createLogger: (\n name: string,\n configOrProps?: ConfigElement[] | Record<string, unknown>,\n) => ConditionalLogger = pipe(\n baseCreateLogger,\n withEnvDefaults(),\n withSpans(),\n withConfigMetrics(),\n) as (\n name: string,\n configOrProps?: ConfigElement[] | Record<string, unknown>,\n) => ConditionalLogger\n\n/** Test helper — all levels, console output. */\nexport function createTestLogger(name: string): ConditionalLogger {\n return pipe(baseCreateLogger, withSpans())(name, [\n { level: \"trace\" },\n \"console\",\n ])\n}\n\n// ============ Legacy Setters ============\n\n/** @deprecated Use config array */\nexport function setLogLevel(level: LogLevel): void {\n _env.LOG_LEVEL = level\n}\nexport function getLogLevel(): LogLevel {\n return currentLevel()\n}\nexport function enableSpans(): void {\n _env.TRACE = \"1\"\n}\nexport function disableSpans(): void {\n delete _env.TRACE\n}\nexport function spansAreEnabled(): boolean {\n return !!_env.TRACE\n}\nexport function setTraceFilter(namespaces: string[] | null): void {\n if (!namespaces || namespaces.length === 0) delete _env.TRACE\n else _env.TRACE = namespaces.join(\",\")\n}\nexport function getTraceFilter(): string[] | null {\n return _env.TRACE ? _env.TRACE.split(\",\") : null\n}\nexport function setDebugFilter(namespaces: string[] | null): void {\n if (!namespaces || namespaces.length === 0) delete _env.DEBUG\n else _env.DEBUG = namespaces.join(\",\")\n}\nexport function getDebugFilter(): string[] | null {\n return _env.DEBUG ? _env.DEBUG.split(\",\") : null\n}\nexport function setLogFormat(format: LogFormat): void {\n _env.LOG_FORMAT = format\n}\nexport function getLogFormat(): LogFormat {\n return currentFormat()\n}\nexport function setSuppressConsole(value: boolean): void {\n _suppressConsole = value\n}\nexport type OutputMode = \"console\" | \"stderr\" | \"writers-only\"\nexport function setOutputMode(_mode: OutputMode): void {\n throw new Error(\n 'loggily: setOutputMode() is removed in v2. Use config arrays: omit console from array for writers-only, use \"stderr\" for stderr mode.',\n )\n}\nexport function getOutputMode(): OutputMode {\n return \"console\"\n}\n/**\n * Register a writer for log records. A writer is the terminal stage of a\n * sub-pipeline — it receives formatted records that pass any filters\n * declared by the optional config.\n *\n * Three forms:\n *\n * - **catch-all**: `addWriter(writer)` — every record reaches the writer.\n * - **scoped**: `addWriter({ ns, level }, writer)` — only records matching\n * the namespace pattern and at-or-above the level reach the writer.\n * Same `ConfigObject` shape used in `createLogger(\"x\", [config, sink])`.\n *\n * `ConfigObject` filters supported here:\n *\n * - `ns: string | string[]` — DEBUG-style glob (e.g. `\"bg-recall:*\"`,\n * `\"myapp,-myapp:noisy\"`). Substring excludes count.\n * - `level: LogLevel` — record's level must be >= this. `\"trace\"` < `\"debug\"`\n * < `\"info\"` < `\"warn\"` < `\"error\"` < `\"silent\"`.\n *\n * Returns an unsubscribe handle.\n *\n * Per-namespace fan-out replaces hand-rolled `appendFileSync(path, line)`\n * patterns: every subsystem writes through `createLogger(\"ns:thing\")`, and\n * the host registers per-namespace writers via this function instead of\n * each subsystem reinventing its own env-var + writer.\n */\nexport function addWriter(writer: WriterFn): () => void\nexport function addWriter(config: ConfigObject, writer: WriterFn): () => void\nexport function addWriter(\n arg1: ConfigObject | WriterFn,\n arg2?: WriterFn,\n): () => void {\n if (typeof arg1 === \"function\") return _addRawWriter(arg1)\n if (!arg2) {\n throw new Error(\n \"addWriter: writer fn required when first argument is a config object\",\n )\n }\n const config = arg1\n const matches = config.ns ? parseNsFilter(config.ns) : null\n const minPriority = config.level ? LOG_LEVEL_PRIORITY[config.level] : null\n return _addRawWriter((formatted, level, namespace, event) => {\n if (matches && !matches(namespace)) return\n if (minPriority !== null) {\n const recordPriority =\n LOG_LEVEL_PRIORITY[level as LogLevel] ?? LOG_LEVEL_PRIORITY.info\n if (recordPriority < minPriority) return\n }\n arg2(formatted, level, namespace, event)\n })\n}\n\nfunction _addRawWriter(writer: WriterFn): () => void {\n _writers.push(writer)\n return () => {\n const i = _writers.indexOf(writer)\n if (i !== -1) _writers.splice(i, 1)\n }\n}\nexport function writeSpan(\n namespace: string,\n duration: number,\n attrs: Record<string, unknown>,\n): void {\n createEnvPipeline().dispatch({\n kind: \"span\",\n time: Date.now(),\n namespace,\n name: namespace,\n duration,\n props: attrs,\n spanId: (attrs.span_id as string) ?? \"\",\n traceId: (attrs.trace_id as string) ?? \"\",\n parentId: (attrs.parent_id as string | null) ?? null,\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"core-CM_9Xqz-.mjs","names":["_process","pc","createConsoleSink","pc","_createConsoleSink","_withMetrics","_createMetricsCollector"],"sources":["../src/colors.ts","../src/file-writer.ts","../src/tracing.ts","../src/console-sinks.ts","../src/pipeline.ts","../src/core.ts"],"sourcesContent":["/**\n * Vendored ANSI color functions — replaces picocolors dependency.\n * Supports NO_COLOR, FORCE_COLOR, and TTY detection.\n */\n\nconst _process = typeof process !== \"undefined\" ? process : undefined\n\nconst enabled =\n _process?.env?.[\"FORCE_COLOR\"] !== undefined &&\n _process?.env?.[\"FORCE_COLOR\"] !== \"0\"\n ? true\n : _process?.env?.[\"NO_COLOR\"] !== undefined\n ? false\n : (_process?.stdout?.isTTY ?? false)\n\nfunction wrap(open: string, close: string): (str: string) => string {\n if (!enabled) return (str) => str\n return (str) => open + str + close\n}\n\nexport const colors = {\n dim: wrap(\"\\x1b[2m\", \"\\x1b[22m\"),\n blue: wrap(\"\\x1b[34m\", \"\\x1b[39m\"),\n yellow: wrap(\"\\x1b[33m\", \"\\x1b[39m\"),\n red: wrap(\"\\x1b[31m\", \"\\x1b[39m\"),\n magenta: wrap(\"\\x1b[35m\", \"\\x1b[39m\"),\n cyan: wrap(\"\\x1b[36m\", \"\\x1b[39m\"),\n}\n","/**\n * File writer for loggily — Node.js/Bun only.\n *\n * Separated from core logger to allow tree-shaking in browser bundles.\n * Uses dynamic import(\"node:fs\") to avoid static dependency on Node APIs.\n */\n\nimport { openSync, writeSync, closeSync } from \"node:fs\"\n\n/** Options for creating an async buffered file writer */\nexport interface FileWriterOptions {\n /** Buffer size threshold in bytes before flushing (default: 4096) */\n bufferSize?: number\n /** Flush interval in milliseconds (default: 100) */\n flushInterval?: number\n /** Test-only filesystem injection. Production callers should omit this. */\n __fs?: {\n openSync(path: string, flags: string): number\n writeSync(fd: number, data: string): unknown\n closeSync(fd: number): void\n }\n}\n\n/** An async buffered file writer with automatic flushing */\nexport interface FileWriter {\n /** Write a line to the buffer (appends newline) */\n write(line: string): void\n /** Flush the buffer immediately */\n flush(): void\n /** Close the writer and flush remaining buffer */\n close(): void\n}\n\n/**\n * Create an async buffered file writer for log output.\n * Buffers writes and flushes on size threshold or interval.\n * Registers a process.on('exit') handler to flush remaining buffer.\n *\n * **Node.js/Bun only** — not available in browser environments.\n *\n * @param filePath - Path to the log file (opened in append mode)\n * @param options - Buffer size and flush interval configuration\n * @returns FileWriter with write, flush, and close methods\n *\n * @example\n * const writer = createFileWriter('/tmp/app.log')\n * const unsubscribe = addWriter((formatted) => writer.write(formatted))\n *\n * // On shutdown:\n * unsubscribe()\n * writer.close()\n */\nexport function createFileWriter(\n filePath: string,\n options: FileWriterOptions = {},\n): FileWriter {\n const bufferSize = options.bufferSize ?? 4096\n const flushInterval = options.flushInterval ?? 100\n const fs = options.__fs ?? { openSync, writeSync, closeSync }\n\n let buffer = \"\"\n let fd: number | null = null\n let timer: ReturnType<typeof setInterval> | null = null\n let closed = false\n\n // Std-stream paths (DEBUG_LOG=/dev/stderr et al.) bind the already-open\n // descriptor instead of re-opening the device node: openSync(\"/dev/stderr\",\n // \"a\") works on macOS but throws ENXIO on Linux whenever stderr is a pipe\n // (CI runners). The borrowed fd is never closed — it belongs to the process.\n const stdFd = stdStreamFd(filePath)\n\n // Open file in append mode (regular paths only)\n fd = stdFd ?? fs.openSync(filePath, \"a\")\n\n /** Flush buffer contents to disk synchronously */\n function flush(): void {\n if (buffer.length === 0 || fd === null) return\n const data = buffer\n fs.writeSync(fd, data)\n buffer = \"\"\n }\n\n // Set up periodic flush\n timer = setInterval(flush, flushInterval)\n // Don't let the timer keep the process alive\n if (timer && typeof timer === \"object\" && \"unref\" in timer) {\n ;(timer as { unref(): void }).unref()\n }\n\n // Flush on process exit to avoid data loss\n const exitHandler = (): void => flush()\n process.on(\"exit\", exitHandler)\n\n return {\n write(line: string): void {\n if (closed) return\n buffer += line + \"\\n\"\n if (buffer.length >= bufferSize) {\n flush()\n }\n },\n\n flush,\n\n close(): void {\n if (closed) return\n closed = true\n if (timer !== null) {\n clearInterval(timer)\n timer = null\n }\n try {\n flush()\n } catch {\n // Swallow flush errors during close — data loss is unavoidable\n // at this point, but we must still release the fd and exit handler.\n } finally {\n if (fd !== null) {\n // Borrowed std-stream descriptors stay open — closing fd 1/2 would\n // sever the whole process's stdout/stderr.\n if (stdFd === null) fs.closeSync(fd)\n fd = null\n }\n process.removeListener(\"exit\", exitHandler)\n }\n },\n }\n}\n\n/**\n * Map a std-stream pseudo-path to its file descriptor, or null for regular\n * paths. Covers the portable spellings: /dev/stdout, /dev/stderr, /dev/fd/N\n * (macOS + Linux), /proc/self/fd/N (Linux).\n */\nfunction stdStreamFd(filePath: string): number | null {\n if (filePath === \"/dev/stdout\") return 1\n if (filePath === \"/dev/stderr\") return 2\n const m = /^(?:\\/dev\\/fd|\\/proc\\/self\\/fd)\\/(\\d+)$/.exec(filePath)\n return m ? Number(m[1]) : null\n}\n","/**\n * Distributed tracing utilities for loggily.\n *\n * Provides W3C-compatible trace/span ID generation, traceparent header formatting,\n * and head-based sampling. All features are opt-in and don't break the existing API.\n */\n\nimport type { SpanData } from \"./core.js\"\n\n// ============ ID Format ============\n\n/** Supported ID formats */\nexport type IdFormat = \"simple\" | \"w3c\"\n\nlet currentIdFormat: IdFormat = \"simple\"\n\n/**\n * Set the ID format for new spans and traces.\n * - \"simple\": sp_1, sp_2, tr_1, tr_2 (default, lightweight)\n * - \"w3c\": 32-char hex trace ID, 16-char hex span ID (W3C Trace Context compatible)\n *\n * @deprecated Use the `TRACE_ID_FORMAT` env var or `{ idFormat: \"w3c\" }` in the config array instead.\n */\nexport function setIdFormat(format: IdFormat): void {\n currentIdFormat = format\n}\n\n/**\n * Get the current ID format.\n *\n * @deprecated Use the `TRACE_ID_FORMAT` env var or `{ idFormat: \"w3c\" }` in the config array instead.\n */\nexport function getIdFormat(): IdFormat {\n return currentIdFormat\n}\n\n// Simple format counters (used by core.ts via the generator functions)\nlet simpleSpanCounter = 0\nlet simpleTraceCounter = 0\n\n/** Generate a hex string of the given byte length using crypto.randomUUID */\nfunction randomHex(bytes: number): string {\n // crypto.randomUUID() gives us 32 hex chars (128 bits) after removing dashes\n // For 16 bytes (32 hex chars) we use one UUID, for 8 bytes (16 hex chars) we take a slice\n const uuid = crypto.randomUUID().replace(/-/g, \"\")\n return uuid.slice(0, bytes * 2)\n}\n\n/** Generate a span ID according to the current format */\nexport function generateSpanId(): string {\n if (currentIdFormat === \"w3c\") {\n return randomHex(8) // 16-char hex\n }\n return `sp_${(++simpleSpanCounter).toString(36)}`\n}\n\n/** Generate a trace ID according to the current format */\nexport function generateTraceId(): string {\n if (currentIdFormat === \"w3c\") {\n return randomHex(16) // 32-char hex\n }\n return `tr_${(++simpleTraceCounter).toString(36)}`\n}\n\n/** Reset ID counters (for testing) */\nexport function resetIdCounters(): void {\n simpleSpanCounter = 0\n simpleTraceCounter = 0\n}\n\n// ============ W3C Traceparent ============\n\n/** Options for traceparent header formatting */\nexport interface TraceparentOptions {\n /** Whether this span is sampled. Defaults to true for backwards compatibility. */\n sampled?: boolean\n}\n\n/**\n * Format a W3C traceparent header from span data.\n *\n * Format: `{version}-{trace-id}-{span-id}-{trace-flags}`\n * - version: \"00\" (current W3C spec version)\n * - trace-id: 32 hex chars (128 bits)\n * - span-id: 16 hex chars (64 bits)\n * - trace-flags: \"01\" (sampled) or \"00\" (not sampled)\n *\n * Works with both simple and W3C ID formats. Simple IDs are zero-padded to spec length.\n *\n * @param spanData - Span data with id and traceId\n * @param options - Optional settings (sampled flag). Defaults to sampled=true.\n * @returns W3C traceparent header string\n *\n * @example\n * ```typescript\n * const span = log.span?.(\"http-request\")\n * if (!span) return\n * const header = traceparent(span.spanData)\n * // → \"00-a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6-1a2b3c4d5e6f7a8b-01\"\n * fetch(url, { headers: { traceparent: header } })\n * ```\n */\nexport function traceparent(\n spanData: SpanData,\n options?: TraceparentOptions,\n): string {\n const traceId = padHex(spanData.traceId, 32)\n const spanId = padHex(spanData.id, 16)\n const flags = (options?.sampled ?? true) ? \"01\" : \"00\"\n return `00-${traceId}-${spanId}-${flags}`\n}\n\n/** Pad or hash an ID to the specified hex length */\nfunction padHex(id: string, length: number): string {\n // If it's already the right length and looks like hex, use as-is\n if (id.length === length && /^[0-9a-f]+$/.test(id)) {\n return id\n }\n\n // For simple IDs (sp_1, tr_1), create a deterministic hex representation\n // by encoding the string as hex bytes, zero-padded to the target length\n let hex = \"\"\n for (let i = 0; i < id.length; i++) {\n hex += id.charCodeAt(i).toString(16).padStart(2, \"0\")\n }\n // Pad or truncate to target length\n return hex.padStart(length, \"0\").slice(-length)\n}\n\n// ============ Sampling ============\n\nlet sampleRate = 1.0\n\n/**\n * Set the head-based sampling rate for new traces.\n * Applied at trace creation — all spans within a sampled trace are kept.\n *\n * @deprecated Use the `TRACE_SAMPLE_RATE` env var or `{ sampleRate: 0.1 }` in the config array instead.\n * @param rate - Sampling rate from 0.0 (sample nothing) to 1.0 (sample everything, default)\n */\nexport function setSampleRate(rate: number): void {\n if (rate < 0 || rate > 1) {\n throw new Error(`Sample rate must be between 0.0 and 1.0, got ${rate}`)\n }\n sampleRate = rate\n}\n\n/**\n * Get the current sampling rate.\n *\n * @deprecated Use the `TRACE_SAMPLE_RATE` env var or `{ sampleRate: 0.1 }` in the config array instead.\n */\nexport function getSampleRate(): number {\n return sampleRate\n}\n\n/**\n * Determine whether a new trace should be sampled.\n * Called at trace creation time (head-based sampling).\n */\nexport function shouldSample(): boolean {\n if (sampleRate >= 1.0) return true\n if (sampleRate <= 0.0) return false\n return Math.random() < sampleRate\n}\n","/**\n * Console sinks — runtime-aware structured output to console.\n *\n * Two sinks, one contract: `(event: Event) => void`. They do NOT pre-format\n * events into a single ANSI string. Instead they spread structured arguments\n * to `console.*` so the platform can render rich output:\n *\n * Terminal (Node / Bun):\n * console.info(ansiPrefix, message, ...userArgs)\n * — util.format keeps objects inspectable in devtools\n *\n * Browser (Chrome / Firefox / Safari DevTools):\n * console.info(\"%c<level> %c<namespace>\", levelCss, nsCss, message, ...userArgs)\n * — DevTools renders colored prefix and keeps every user arg as an\n * expandable, clickable object.\n *\n * Source-location preservation:\n * We call `console.<level>(...)` from arrow-function sinks. DevTools attribute\n * the log line to the caller's frame, not this file, as long as no wrapper\n * closes over a bound console reference. `Function.prototype.bind.call(console.info, ...)`\n * works too but defeats `vi.spyOn(console, 'info')` because bind captures\n * the function at bind time. Plain arrow functions re-read `console.info`\n * on each call — mockable AND location-preserving.\n *\n * The sinks consume `event.userArgs` (raw user-supplied data/errors) when\n * present, falling back to `event.props` so they still work with code paths\n * that have not yet been migrated.\n */\n\nimport { colors as pc } from \"./colors.js\"\nimport type { Event, LogFormat, OutputLogLevel } from \"./pipeline.js\"\nimport {\n formatConsoleEvent,\n formatJSONEvent,\n safeStringify,\n} from \"./pipeline.js\"\n\nexport type ConsoleSink = (event: Event) => void\n\n// ---------------------------------------------------------------------------\n// Runtime detection\n// ---------------------------------------------------------------------------\n\n/**\n * True when running inside a browser-like environment. We check for the\n * presence of a `window` with `document` — the Node DOM test env (jsdom)\n * also matches, but that is the correct behaviour for DevTools-style output.\n */\nexport function isBrowserRuntime(): boolean {\n return (\n typeof (globalThis as { window?: unknown })?.window !== \"undefined\" &&\n typeof (globalThis as { document?: unknown }).document !== \"undefined\"\n )\n}\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\nfunction timeStr(time: number): string {\n return new Date(time).toISOString().split(\"T\")[1]?.split(\".\")[0] ?? \"\"\n}\n\nfunction levelLabel(level: OutputLogLevel): string {\n switch (level) {\n case \"trace\":\n return \"TRACE\"\n case \"debug\":\n return \"DEBUG\"\n case \"info\":\n return \"INFO\"\n case \"warn\":\n return \"WARN\"\n case \"error\":\n return \"ERROR\"\n }\n}\n\n/**\n * Return the raw user-supplied data attached to the event, in call order.\n * Prefers the explicit `userArgs` field (new path). Falls back to `props`\n * (coerced to a single trailing object) for events emitted by older code or\n * by `writeSpan`. Filters out `undefined` entries.\n */\nfunction userArgsOf(event: Event): unknown[] {\n if (\n \"userArgs\" in event &&\n Array.isArray((event as { userArgs?: unknown[] }).userArgs)\n ) {\n const ua = (event as { userArgs?: unknown[] }).userArgs!\n return ua.filter((v) => v !== undefined)\n }\n if (event.props && Object.keys(event.props).length > 0) {\n return [event.props]\n }\n return []\n}\n\n// ---------------------------------------------------------------------------\n// Terminal sink\n// ---------------------------------------------------------------------------\n\n/**\n * Terminal (ANSI) sink. Emits `[ansiPrefix, message, ...userArgs]` so that:\n * - stdout gets a colored prefix\n * - util.format leaves objects inspectable\n * - vi.spyOn(console, 'info') intercepts calls (arrows re-read console)\n *\n * Spans and JSON format still go through the pre-formatted single-arg path —\n * their consumers are log aggregators, not humans.\n */\nexport function createTerminalConsoleSink(\n format: LogFormat = \"console\",\n): ConsoleSink {\n if (format === \"json\") {\n // JSON format: one line, one arg — downstream log collectors expect that.\n return (event: Event) => routeSingle(event, formatJSONEvent(event))\n }\n\n return (event: Event) => {\n // Spans bypass console and go straight to stderr — that's what Ink's\n // patchConsole relies on to keep span output outside the TUI surface.\n if (event.kind === \"span\") {\n writeStderrLine(formatConsoleEvent(event))\n return\n }\n\n const prefix = `${pc.dim(timeStr(event.time))} ${levelAnsi(event.level)} ${pc.cyan(event.namespace)}`\n const args = userArgsOf(event)\n // Arrow → console.<level>: preserves caller frame + stays mockable.\n invokeForLevel(event.level, prefix, event.message, ...args)\n }\n}\n\n/** Emit one line to process.stderr when available (no-op in browser). */\nfunction writeStderrLine(text: string): void {\n const p = typeof process !== \"undefined\" ? process : undefined\n if (p?.stderr && typeof p.stderr.write === \"function\") {\n p.stderr.write(text + \"\\n\")\n return\n }\n console.info(text)\n}\n\nfunction levelAnsi(level: OutputLogLevel): string {\n switch (level) {\n case \"trace\":\n return pc.dim(\"TRACE\")\n case \"debug\":\n return pc.dim(\"DEBUG\")\n case \"info\":\n return pc.blue(\"INFO\")\n case \"warn\":\n return pc.yellow(\"WARN\")\n case \"error\":\n return pc.red(\"ERROR\")\n }\n}\n\n// ---------------------------------------------------------------------------\n// Browser sink\n// ---------------------------------------------------------------------------\n\n/**\n * Browser (DevTools) sink. Uses `%c` CSS format specifiers so DevTools\n * renders a colored level + namespace prefix, then spreads user args raw so\n * DevTools keeps them as expandable object references (clickable source\n * locations for Error, drill-in for plain objects).\n *\n * For JSON format we still emit a single string — consumers that explicitly\n * asked for JSON want machine-readable output, not DevTools theatrics.\n */\nexport function createBrowserConsoleSink(\n format: LogFormat = \"console\",\n): ConsoleSink {\n if (format === \"json\") {\n return (event: Event) => routeSingle(event, formatJSONEvent(event))\n }\n\n return (event: Event) => {\n if (event.kind === \"span\") {\n const spanTemplate = `%c%s %cSPAN %c%s %c(%sms)`\n const args = userArgsOf(event)\n const spanPropsString =\n args.length > 0\n ? ` ${safeStringify(Object.assign({}, ...args.filter(isPlainRecord)))}`\n : \"\"\n // Spans: compact format; include props inline because DevTools arg\n // ordering would otherwise separate the duration from its context.\n const { durationLabel } = { durationLabel: String(event.duration) }\n invokeForLevel(\n \"info\",\n spanTemplate + (spanPropsString ? \"%s\" : \"\"),\n cssDim(),\n timeStr(event.time),\n cssSpan(),\n cssNamespace(),\n event.namespace,\n cssDim(),\n durationLabel,\n ...(spanPropsString ? [cssDim(), spanPropsString] : []),\n )\n return\n }\n\n // Template: \"<dimTime> <levelBadge> <namespace>\" — 3 %c slots.\n const template = `%c%s %c%s %c%s`\n const args = userArgsOf(event)\n invokeForLevel(\n event.level,\n template,\n cssDim(),\n timeStr(event.time),\n cssLevel(event.level),\n levelLabel(event.level),\n cssNamespace(),\n event.namespace,\n // Message and user args follow — raw, in call order.\n event.message,\n ...args,\n )\n }\n}\n\nfunction isPlainRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v)\n}\n\nfunction cssDim(): string {\n return \"color: #888\"\n}\nfunction cssNamespace(): string {\n return \"color: #0aa; font-weight: bold\"\n}\nfunction cssSpan(): string {\n return \"color: #a0a; font-weight: bold\"\n}\nfunction cssLevel(level: OutputLogLevel): string {\n switch (level) {\n case \"trace\":\n case \"debug\":\n return \"color: #888; font-weight: bold\"\n case \"info\":\n return \"color: #36f; font-weight: bold\"\n case \"warn\":\n return \"color: #b80; font-weight: bold\"\n case \"error\":\n return \"color: #c33; font-weight: bold\"\n }\n}\n\n// ---------------------------------------------------------------------------\n// Console routing\n// ---------------------------------------------------------------------------\n\n/**\n * Dispatch to the right console method for the event's level.\n * We intentionally use arrow-style invocation rather than\n * `Function.prototype.bind.call(console.info, console, ...)` so that:\n * 1. Tests can `vi.spyOn(console, 'info')` AFTER the sink is created.\n * 2. DevTools attribute the log line to the caller's source location\n * (arrows don't appear in the stack between the call and `console.*`).\n */\nfunction invokeForLevel(level: OutputLogLevel, ...args: unknown[]): void {\n switch (level) {\n case \"trace\":\n case \"debug\":\n console.debug(...args)\n return\n case \"info\":\n console.info(...args)\n return\n case \"warn\":\n console.warn(...args)\n return\n case \"error\":\n console.error(...args)\n return\n }\n}\n\n/**\n * Route an already-formatted single string to the correct console level.\n * Used for JSON format and for span events where we preserve the pre-formatted\n * shape.\n */\nfunction routeSingle(event: Event, text: string): void {\n if (event.kind === \"span\") {\n // Spans go to stderr in Node (existing behaviour) but we funnel them\n // through console.info in the sinks so browser DevTools sees them too;\n // the Node path for spans remains writeStderr via the pipeline when the\n // user opts in via `\"stderr\"`.\n console.info(text)\n return\n }\n switch (event.level) {\n case \"trace\":\n case \"debug\":\n console.debug(text)\n return\n case \"info\":\n console.info(text)\n return\n case \"warn\":\n console.warn(text)\n return\n case \"error\":\n console.error(text)\n return\n }\n}\n\n// ---------------------------------------------------------------------------\n// Runtime-selecting factory\n// ---------------------------------------------------------------------------\n\n/**\n * Pick the right sink for the current runtime. Browsers get `%c` CSS, Node\n * gets ANSI. Both emit multi-arg spreads so the platform can render objects\n * as expandable references.\n */\nexport function createConsoleSink(format: LogFormat = \"console\"): ConsoleSink {\n return isBrowserRuntime()\n ? createBrowserConsoleSink(format)\n : createTerminalConsoleSink(format)\n}\n","import { colors as pc } from \"./colors.js\"\nimport { createFileWriter } from \"./file-writer.js\"\nimport { setIdFormat, setSampleRate } from \"./tracing.js\"\nimport type { IdFormat } from \"./tracing.js\"\n\n// ============ Types ============\n\nexport type OutputLogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\"\nexport type LogLevel = OutputLogLevel | \"silent\"\nexport type LogFormat = \"console\" | \"json\"\n\nexport type LogEvent = {\n kind: \"log\"\n time: number\n namespace: string\n level: OutputLogLevel\n message: string\n props?: Record<string, unknown>\n /**\n * Raw user-supplied arguments, in call order (after the message).\n *\n * Populated by the logger façade so that console sinks can spread them to\n * `console.*` and keep objects expandable in Node/browser DevTools. When\n * absent, sinks fall back to `props` (merged context + user data).\n *\n * Example: `log.info(\"greet\", { user: \"a\" })` → userArgs = [{ user: \"a\" }]\n */\n userArgs?: unknown[]\n}\n\nexport type SpanEvent = {\n kind: \"span\"\n time: number\n namespace: string\n name: string\n duration: number\n props?: Record<string, unknown>\n spanId: string\n traceId: string\n parentId: string | null\n /** Raw user-supplied span attributes (mirrors LogEvent.userArgs). */\n userArgs?: unknown[]\n}\n\nexport type Event = LogEvent | SpanEvent\nexport type Stage = (event: Event) => Event | null | void\n\n// ============ Level Priority ============\n\nexport const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n trace: 0,\n debug: 1,\n info: 2,\n warn: 3,\n error: 4,\n silent: 5,\n}\n\n// ============ Runtime Detection ============\n\nconst _process = typeof process !== \"undefined\" ? process : undefined\n\nfunction getEnv(key: string): string | undefined {\n return _process?.env?.[key]\n}\n\nfunction writeStderr(text: string): void {\n if (_process?.stderr?.write) {\n _process.stderr.write(text + \"\\n\")\n } else {\n console.error(text)\n }\n}\n\n// ============ Formatting ============\n\n/** Serialize Error.cause chains up to a max depth */\nexport function serializeCause(cause: unknown, maxDepth: number = 3): unknown {\n if (maxDepth <= 0 || cause === undefined || cause === null) return undefined\n if (cause instanceof Error) {\n const result: Record<string, unknown> = {\n name: cause.name,\n message: cause.message,\n stack: cause.stack,\n }\n if ((cause as { code?: string }).code)\n result.code = (cause as { code?: string }).code\n if (cause.cause !== undefined) {\n result.cause = serializeCause(cause.cause, maxDepth - 1)\n }\n return result\n }\n // Non-Error cause (spec allows any value)\n return cause\n}\n\nexport function safeStringify(value: unknown): string {\n const seen = new WeakSet()\n return JSON.stringify(value, (_key, val) => {\n if (typeof val === \"bigint\") return val.toString()\n if (typeof val === \"symbol\") return val.toString()\n if (val instanceof Error) {\n const result: Record<string, unknown> = {\n message: val.message,\n stack: val.stack,\n name: val.name,\n }\n if ((val as { code?: string }).code)\n result.code = (val as { code?: string }).code\n if (val.cause !== undefined) result.cause = serializeCause(val.cause)\n return result\n }\n if (typeof val === \"object\" && val !== null) {\n if (seen.has(val)) return \"[Circular]\"\n seen.add(val)\n }\n return val\n })\n}\n\nexport function formatConsoleEvent(event: Event): string {\n const time = pc.dim(\n new Date(event.time).toISOString().split(\"T\")[1]?.split(\".\")[0] || \"\",\n )\n const ns = pc.cyan(event.namespace)\n\n if (event.kind === \"span\") {\n const message = `(${event.duration}ms)`\n let output = `${time} ${pc.magenta(\"SPAN\")} ${ns} ${message}`\n if (event.props && Object.keys(event.props).length > 0) {\n output += ` ${pc.dim(safeStringify(event.props))}`\n }\n return output\n }\n\n let levelStr: string\n switch (event.level) {\n case \"trace\":\n levelStr = pc.dim(\"TRACE\")\n break\n case \"debug\":\n levelStr = pc.dim(\"DEBUG\")\n break\n case \"info\":\n levelStr = pc.blue(\"INFO\")\n break\n case \"warn\":\n levelStr = pc.yellow(\"WARN\")\n break\n case \"error\":\n levelStr = pc.red(\"ERROR\")\n break\n }\n\n let output = `${time} ${levelStr} ${ns} ${event.message}`\n if (event.props && Object.keys(event.props).length > 0) {\n output += ` ${pc.dim(safeStringify(event.props))}`\n }\n return output\n}\n\nexport function formatJSONEvent(event: Event): string {\n if (event.kind === \"span\") {\n return safeStringify({\n time: new Date(event.time).toISOString(),\n level: \"span\",\n name: event.namespace,\n msg: `(${event.duration}ms)`,\n duration: event.duration,\n span_id: event.spanId,\n trace_id: event.traceId,\n parent_id: event.parentId,\n ...event.props,\n })\n }\n\n return safeStringify({\n time: new Date(event.time).toISOString(),\n level: event.level,\n name: event.namespace,\n msg: event.message,\n ...event.props,\n })\n}\n\n// ============ Namespace Filter ============\n\nexport type NsFilter = (namespace: string) => boolean\n\nfunction matchesPattern(namespace: string, pattern: string): boolean {\n if (pattern === \"*\") return true\n if (pattern.endsWith(\":*\")) {\n const prefix = pattern.slice(0, -2)\n return namespace === prefix || namespace.startsWith(prefix + \":\")\n }\n return namespace === pattern || namespace.startsWith(pattern + \":\")\n}\n\nexport function parseNsFilter(ns: string | string[]): NsFilter {\n const patterns =\n typeof ns === \"string\" ? ns.split(\",\").map((s) => s.trim()) : ns\n const includes: string[] = []\n const excludes: string[] = []\n\n for (const p of patterns) {\n if (p.startsWith(\"-\")) {\n excludes.push(p.slice(1))\n } else {\n includes.push(p)\n }\n }\n\n return (namespace: string): boolean => {\n for (const exc of excludes) {\n if (matchesPattern(namespace, exc)) return false\n }\n if (includes.length > 0) {\n for (const inc of includes) {\n if (matchesPattern(namespace, inc)) return true\n }\n return false\n }\n return true\n }\n}\n\n// ============ Console Output ============\n\n// Lazy-imported structured console sink (browser %c / terminal multi-arg).\n// Pipeline-level imports avoid a cycle: console-sinks.ts imports format\n// helpers from this file; we want the pipeline to reuse them too.\nimport { createConsoleSink as _createConsoleSink } from \"./console-sinks.js\"\n\n/**\n * Legacy text-based console writer. Retained so older code paths (the\n * env-dynamic pipeline in core.ts, writeSpan) still work, but the modern\n * pipeline now routes through `createConsoleSink` which spreads structured\n * args to `console.*` — that's what preserves expandable objects in browser\n * DevTools and makes `vi.spyOn(console, 'info')` interception work.\n *\n * For spans we still write to stderr in Node because the pipeline-level\n * spanEnabled gate handles human-readable span output; JSON/console sinks\n * route spans through their normal formatters when invoked directly.\n */\nexport function writeToConsole(text: string, event: Event): void {\n if (event.kind === \"span\") {\n writeStderr(text)\n return\n }\n // Arrow dispatch — no .bind() — so vi.spyOn(console, …) installed AFTER\n // this module loaded still intercepts. DevTools frame attribution is\n // determined by the caller of writeToConsole, not by this switch.\n switch (event.level) {\n case \"trace\":\n case \"debug\":\n console.debug(text)\n break\n case \"info\":\n console.info(text)\n break\n case \"warn\":\n console.warn(text)\n break\n case \"error\":\n console.error(text)\n break\n }\n}\n\n// ============ Sinks ============\n\n/**\n * Console sink used inside the pipeline builder. Delegates to the structured\n * console sink (browser %c or terminal multi-arg) so the pipeline preserves\n * expandable user args end-to-end.\n */\nfunction createConsoleSink(format: LogFormat): (event: Event) => void {\n return _createConsoleSink(format)\n}\n\nfunction createFileSink(\n path: string,\n format: LogFormat,\n): { write: (event: Event) => void; dispose: () => void } {\n const writer = createFileWriter(path)\n const formatter = format === \"json\" ? formatJSONEvent : formatConsoleEvent\n return {\n write: (event: Event) => writer.write(formatter(event)),\n dispose: () => writer.close(),\n }\n}\n\nfunction isNodeStream(obj: unknown): boolean {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n (\"_write\" in obj || \"writable\" in obj || \"fd\" in obj)\n )\n}\n\nfunction createWritableSink(\n writable: Writable,\n format: LogFormat,\n): (event: Event) => void {\n // Node.js streams (process.stderr, fs streams) default to string mode\n // Plain { write } objects default to object mode (raw Events)\n const useObjectMode = writable.objectMode ?? !isNodeStream(writable)\n if (!useObjectMode) {\n const formatter = format === \"json\" ? formatJSONEvent : formatConsoleEvent\n return (event: Event) => writable.write(formatter(event) + \"\\n\")\n }\n return (event: Event) => writable.write(event)\n}\n\n// ============ Pipeline ============\n\nexport interface Pipeline {\n dispatch: (event: Event) => void\n spanEnabled: (namespace: string) => boolean\n level: LogLevel\n dispose: () => void\n}\n\ninterface Output {\n levelPriority: number\n nsFilter: NsFilter | null\n write: (event: Event) => void\n dispose?: () => void\n /** Consecutive write failures (guarded dispatch); reset on success. */\n failures?: number\n /** Set after OUTPUT_MAX_STRIKES consecutive failures — sink is skipped. */\n disabled?: boolean\n}\n\n/** Consecutive failures before a throwing output sink is disabled. */\nconst OUTPUT_MAX_STRIKES = 3\n\n/**\n * Report a dispatch-guard failure WITHOUT going through any pipeline —\n * the error channel for the logger itself must not recurse into the logger.\n * Routes through writeStderr (the `_process` guard) for browser safety.\n */\nfunction reportGuard(what: string, err: unknown): void {\n const detail = err instanceof Error ? (err.stack ?? err.message) : String(err)\n try {\n writeStderr(`loggily: ${what}: ${detail}`)\n } catch {\n // The terminal fallback of the error channel itself: if stderr is gone\n // there is nowhere left to report, and throwing here would defeat the\n // guard's entire purpose (logger must never throw into the host).\n }\n}\n\n// ============ Discrimination ============\n\nconst VALID_CONFIG_KEYS = new Set([\n \"level\",\n \"ns\",\n \"format\",\n \"spans\",\n \"metrics\",\n \"idFormat\",\n \"sampleRate\",\n])\nconst SINK_KEYS = new Set([\"file\", \"otel\"])\n\nfunction isPojo(obj: unknown): obj is Record<string, unknown> {\n if (typeof obj !== \"object\" || obj === null) return false\n const proto = Object.getPrototypeOf(obj)\n return proto === Object.prototype || proto === null\n}\n\nfunction isWritable(obj: unknown): obj is Writable {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"write\" in obj &&\n typeof (obj as Record<string, unknown>).write === \"function\"\n )\n}\n\nfunction isValidLogLevel(val: unknown): val is LogLevel {\n return typeof val === \"string\" && val in LOG_LEVEL_PRIORITY\n}\n\n// ============ Config Types ============\n\n/** A writable sink — any object with a write method. Receives raw Event objects by default. */\nexport interface Writable {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n write: (data: any) => any\n /** Set to false to receive formatted strings instead of raw Event objects (default: true) */\n objectMode?: boolean\n}\n\n/** Config keys that set scope for subsequent siblings */\nexport interface ConfigObject {\n level?: LogLevel\n ns?: string | string[]\n format?: LogFormat\n spans?: boolean\n /** Enable per-logger metrics collection. Creates a MetricsCollector accessible via `log.metrics`. */\n metrics?: boolean\n /** ID format for trace/span IDs: \"simple\" (default) or \"w3c\" (W3C Trace Context) */\n idFormat?: \"simple\" | \"w3c\"\n /** Head-based sampling rate for new traces: 0.0 (none) to 1.0 (all, default) */\n sampleRate?: number\n}\n\n/** File output descriptor */\nexport interface FileDescriptor extends ConfigObject {\n file: string\n}\n\n/** OTEL output descriptor (Phase 4) */\nexport interface OtelDescriptor extends ConfigObject {\n otel: Record<string, unknown>\n}\n\n/** A single element in a createLogger config array */\nexport type ConfigElement =\n | ConfigObject\n | FileDescriptor\n | OtelDescriptor\n | Console\n | \"console\"\n | \"stderr\"\n | Stage\n | Writable\n | ConfigElement[]\n\n// ============ Build Pipeline ============\n\ninterface ScopeConfig {\n level: LogLevel\n ns: NsFilter | null\n format: LogFormat\n}\n\nexport function buildPipeline(\n elements: ConfigElement[],\n parentConfig?: Partial<ScopeConfig>,\n): Pipeline {\n const config: ScopeConfig = {\n level: parentConfig?.level ?? readEnvLevel(),\n ns: parentConfig?.ns ?? readEnvNs(),\n format: parentConfig?.format ?? readEnvFormat(),\n }\n // Spans always pass through explicit pipelines. { spans: false } to opt out.\n // The defaultPipeline handles TRACE env var gating separately.\n let spansEnabled = true\n\n const stages: Stage[] = []\n const outputs: Output[] = []\n const branches: Pipeline[] = []\n const disposables: (() => void)[] = []\n\n for (const element of elements) {\n // 1. Array → branch\n if (Array.isArray(element)) {\n const branch = buildPipeline(element as ConfigElement[], { ...config })\n branches.push(branch)\n disposables.push(() => branch.dispose())\n continue\n }\n\n // 2. console (literal or \"console\" string) → console sink (check before function — console is function-like)\n if (element === console || element === \"console\") {\n outputs.push({\n levelPriority: LOG_LEVEL_PRIORITY[config.level],\n nsFilter: config.ns,\n write: createConsoleSink(config.format),\n })\n continue\n }\n\n // 3. Function → stage\n if (typeof element === \"function\") {\n stages.push(element as Stage)\n continue\n }\n\n // 4. Writable ({ write }) → writable sink (checked BEFORE POJO so { write: fn } works)\n if (isWritable(element)) {\n outputs.push({\n levelPriority: LOG_LEVEL_PRIORITY[config.level],\n nsFilter: config.ns,\n write: createWritableSink(element, config.format),\n })\n continue\n }\n\n // 5. POJO → scope config or output descriptor\n if (isPojo(element)) {\n const obj = element\n const keys = Object.keys(obj)\n\n const hasSinkKey = keys.some((k) => SINK_KEYS.has(k))\n const hasUnknownKey = keys.some(\n (k) => !VALID_CONFIG_KEYS.has(k) && !SINK_KEYS.has(k),\n )\n\n if (hasUnknownKey) {\n const unknown = keys.find(\n (k) => !VALID_CONFIG_KEYS.has(k) && !SINK_KEYS.has(k),\n )\n throw new Error(\n `loggily: unknown config key \"${unknown}\" in config object. Valid keys: ${[...VALID_CONFIG_KEYS, ...SINK_KEYS].join(\", \")}`,\n )\n }\n\n if (hasSinkKey) {\n if (typeof obj.file === \"string\") {\n const outputLevel = isValidLogLevel(obj.level)\n ? obj.level\n : config.level\n const outputNs = obj.ns\n ? parseNsFilter(obj.ns as string | string[])\n : config.ns\n const outputFormat = (obj.format as LogFormat) ?? config.format\n const sink = createFileSink(obj.file, outputFormat)\n disposables.push(sink.dispose)\n outputs.push({\n levelPriority: LOG_LEVEL_PRIORITY[outputLevel],\n nsFilter: outputNs,\n write: sink.write,\n dispose: sink.dispose,\n })\n }\n if (obj.otel !== undefined) {\n throw new Error(\n \"loggily: OTEL sink is not yet implemented. See loggily/otel for the planned bridge.\",\n )\n }\n continue\n }\n\n // Scope config — update inherited config\n if (isValidLogLevel(obj.level)) config.level = obj.level\n if (obj.ns !== undefined)\n config.ns = parseNsFilter(obj.ns as string | string[])\n if (obj.format === \"console\" || obj.format === \"json\")\n config.format = obj.format\n if (obj.spans === true) spansEnabled = true\n if (obj.spans === false) spansEnabled = false\n if (obj.idFormat === \"simple\" || obj.idFormat === \"w3c\")\n setIdFormat(obj.idFormat)\n if (typeof obj.sampleRate === \"number\") setSampleRate(obj.sampleRate)\n continue\n }\n\n // 6. String \"stderr\" → stderr sink\n if (element === \"stderr\" && typeof process !== \"undefined\") {\n outputs.push({\n levelPriority: LOG_LEVEL_PRIORITY[config.level],\n nsFilter: config.ns,\n write: createWritableSink(\n process.stderr as unknown as Writable,\n config.format,\n ),\n })\n continue\n }\n\n throw new Error(\n `loggily: unsupported config element of type \"${typeof element}\". ` +\n 'Config arrays accept: objects (config), arrays (branches), functions (stages), console, \"console\", or writables ({ write }).',\n )\n }\n\n // Guarded dispatch: a logger must never throw into its host app. Stage\n // throws drop the event (fail-closed — a broken redaction stage must not\n // leak unprocessed events); output throws are isolated per-sink with a\n // strike counter; branch throws are isolated. All failures are reported\n // once via reportGuard (direct stderr — never through the pipeline).\n const stageReported: boolean[] = []\n const branchReported: boolean[] = []\n const dispatch = (event: Event): void => {\n // Span gate: { spans: false } disables span output for this pipeline\n if (event.kind === \"span\" && !spansEnabled) return\n\n let e: Event = event\n for (let i = 0; i < stages.length; i++) {\n let result: Event | null | void\n try {\n result = stages[i]!(e)\n } catch (err) {\n if (!stageReported[i]) {\n stageReported[i] = true\n reportGuard(\n `stage #${i} threw — dropping event, fail-closed (reported once)`,\n err,\n )\n }\n return\n }\n if (result === null) return\n if (result !== undefined) e = result\n }\n for (const output of outputs) {\n if (output.disabled) continue\n if (\n e.kind === \"log\" &&\n LOG_LEVEL_PRIORITY[e.level] < output.levelPriority\n )\n continue\n if (output.nsFilter && !output.nsFilter(e.namespace)) continue\n try {\n output.write(e)\n if (output.failures) output.failures = 0\n } catch (err) {\n output.failures = (output.failures ?? 0) + 1\n if (output.failures === 1) {\n reportGuard(\"output write threw — event lost for this sink\", err)\n }\n if (output.failures >= OUTPUT_MAX_STRIKES) {\n output.disabled = true\n reportGuard(\n `output disabled after ${output.failures} consecutive failures`,\n err,\n )\n }\n }\n }\n for (let i = 0; i < branches.length; i++) {\n try {\n branches[i]!.dispatch(e)\n } catch (err) {\n // Branch pipelines guard themselves; this catch is the backstop for\n // anything that escapes (e.g. a foreign Pipeline implementation).\n if (!branchReported[i]) {\n branchReported[i] = true\n reportGuard(`branch #${i} dispatch threw (reported once)`, err)\n }\n }\n }\n }\n\n const spanEnabledForNamespace = (namespace: string): boolean => {\n if (!spansEnabled) return false\n if (\n outputs.some((output) => !output.nsFilter || output.nsFilter(namespace))\n )\n return true\n if (branches.some((branch) => branch.spanEnabled(namespace))) return true\n // Stages may consume or forward spans themselves, so keep `.span` available\n // for stage-only explicit pipelines.\n return stages.length > 0\n }\n\n return {\n dispatch,\n spanEnabled: spanEnabledForNamespace,\n level: config.level,\n dispose: () => {\n for (const d of disposables) d()\n },\n }\n}\n\n// ============ Env Var Readers (exported for withEnvDefaults plugin) ============\n\nexport function readEnvLevel(): LogLevel {\n const env = getEnv(\"LOG_LEVEL\")?.toLowerCase()\n let level: LogLevel =\n env === \"trace\" ||\n env === \"debug\" ||\n env === \"info\" ||\n env === \"warn\" ||\n env === \"error\" ||\n env === \"silent\"\n ? env\n : \"info\"\n\n const debugEnv = getEnv(\"DEBUG\")\n if (debugEnv && LOG_LEVEL_PRIORITY[level] > LOG_LEVEL_PRIORITY.debug) {\n level = \"debug\"\n }\n\n return level\n}\n\n/**\n * Namespace-aware level: only bumps to debug if the namespace matches the DEBUG filter.\n * This enables zero-overhead conditional gating — `log.debug?.()` returns undefined\n * for namespaces outside the DEBUG filter, skipping argument evaluation entirely.\n */\nexport function readEnvLevelForNamespace(namespace: string): LogLevel {\n const env = getEnv(\"LOG_LEVEL\")?.toLowerCase()\n const baseLevel: LogLevel =\n env === \"trace\" ||\n env === \"debug\" ||\n env === \"info\" ||\n env === \"warn\" ||\n env === \"error\" ||\n env === \"silent\"\n ? env\n : \"info\"\n\n const debugEnv = getEnv(\"DEBUG\")\n if (debugEnv && LOG_LEVEL_PRIORITY[baseLevel] > LOG_LEVEL_PRIORITY.debug) {\n // DEBUG is set and would bump level — check if this namespace matches\n const nsFilter = readEnvNs()\n if (nsFilter && nsFilter(namespace)) {\n return \"debug\" // namespace matches DEBUG filter — bump to debug\n }\n // Namespace doesn't match — keep the configured level\n return baseLevel\n }\n\n return baseLevel\n}\n\nexport function readEnvNs(): NsFilter | null {\n const debugEnv = getEnv(\"DEBUG\")\n if (!debugEnv) return null\n\n const parts = debugEnv.split(\",\").map((s) => s.trim())\n return parseNsFilter(parts)\n}\n\nexport function readEnvFormat(): LogFormat {\n const envFormat = getEnv(\"LOG_FORMAT\")?.toLowerCase()\n if (envFormat === \"json\") return \"json\"\n if (envFormat === \"console\") return \"console\"\n if (getEnv(\"TRACE_FORMAT\") === \"json\") return \"json\"\n if (getEnv(\"NODE_ENV\") === \"production\") return \"json\"\n return \"console\"\n}\n\nexport function readEnvTrace(): { enabled: boolean; filter: NsFilter | null } {\n const traceEnv = getEnv(\"TRACE\")\n if (!traceEnv) return { enabled: false, filter: null }\n if (traceEnv === \"1\" || traceEnv === \"true\")\n return { enabled: true, filter: null }\n const prefixes = traceEnv.split(\",\").map((s) => s.trim())\n return {\n enabled: true,\n filter: (namespace: string) => {\n for (const prefix of prefixes) {\n if (matchesPattern(namespace, prefix)) return true\n }\n return false\n },\n }\n}\n","/**\n * loggily v2 — Structured logging with spans\n *\n * One import. Objects configure. Arrays branch. Values write.\n *\n * @example\n * const log = createLogger('myapp')\n * log.info?.('starting')\n *\n * @example\n * const log = createLogger('myapp', [\n * { level: 'debug', ns: '-sql' },\n * console,\n * { file: '/tmp/app.log', level: 'info', format: 'json' },\n * ])\n * log.info?.('server started', { port: 3000 })\n */\n\nimport {\n type Event,\n type LogEvent,\n type SpanEvent,\n type Pipeline,\n type Stage,\n type LogLevel,\n type OutputLogLevel,\n type LogFormat,\n type NsFilter,\n type ConfigElement,\n type ConfigObject,\n LOG_LEVEL_PRIORITY,\n buildPipeline,\n safeStringify,\n serializeCause,\n readEnvLevel,\n readEnvLevelForNamespace,\n readEnvNs,\n readEnvFormat,\n readEnvTrace,\n writeToConsole,\n formatConsoleEvent,\n formatJSONEvent,\n parseNsFilter,\n} from \"./pipeline.js\"\nimport { createConsoleSink as createStructuredConsoleSink } from \"./console-sinks.js\"\n\nimport {\n createMetricsCollector as _createMetricsCollector,\n withMetrics as _withMetrics,\n} from \"./metrics.js\"\n\nexport type {\n Event,\n LogEvent,\n SpanEvent,\n Stage,\n LogLevel,\n OutputLogLevel,\n LogFormat,\n ConfigElement,\n}\nexport { LOG_LEVEL_PRIORITY, safeStringify }\n\n// ============ Metrics ============\n\nexport interface SpanRecord {\n readonly name: string\n readonly durationMs: number\n}\n\nexport interface SpanRecorder {\n recordSpan(data: SpanRecord): void\n}\n\n// ============ Types ============\n\nexport type LazyMessage = string | (() => string)\nexport type LazyProps =\n | Record<string, unknown>\n | (() => Record<string, unknown>)\n\nexport interface SpanData {\n readonly id: string\n readonly traceId: string\n readonly parentId: string | null\n readonly startTime: number\n readonly endTime: number | null\n readonly duration: number | null\n [key: string]: unknown\n}\n\nexport interface Logger extends Disposable {\n readonly name: string\n readonly props: Readonly<Record<string, unknown>>\n readonly level: LogLevel\n\n dispatch(event: Event): void\n\n trace(message: LazyMessage, data?: Record<string, unknown>): void\n debug(message: LazyMessage, data?: Record<string, unknown>): void\n info(message: LazyMessage, data?: Record<string, unknown>): void\n warn(message: LazyMessage, data?: Record<string, unknown>): void\n error(message: LazyMessage, data?: Record<string, unknown>): void\n error(error: Error, data?: Record<string, unknown>): void\n error(error: Error, message: string, data?: Record<string, unknown>): void\n\n /** @deprecated Use .child() */\n logger(namespace?: string, props?: Record<string, unknown>): ConditionalLogger\n span(namespace?: string, props?: LazyProps): SpanLogger\n child(namespace: string, props?: Record<string, unknown>): ConditionalLogger\n child(context: Record<string, unknown>): ConditionalLogger\n end(): void\n}\n\nexport interface SpanLogger extends ConditionalLogger, Disposable {\n readonly spanData: SpanData & { [key: string]: unknown }\n // Override optional `span` from ConditionalLogger: a SpanLogger is always\n // produced by `withSpans()`, so nested `.span()` is guaranteed present.\n span(namespace?: string, props?: LazyProps): SpanLogger\n /**\n * Record a stopwatch-style checkpoint within this span. Each `lap(name)`\n * captures the time since the previous lap (or span start for the first\n * lap). On span end, laps are emitted in the span event's props as\n * `laps: { name1: deltaMs, name2: deltaMs, ... }` for compact output, plus\n * stored verbatim on `spanData.laps` for programmatic consumers.\n *\n * Use laps when you want sub-span timing without the noise of nested span\n * lines. Nested spans give per-phase log lines + parent context; laps give\n * one summary line with phase deltas in props.\n *\n * @example\n * using span = log.span?.(\"materialize\")\n * loadInputs()\n * span?.lap(\"inputs\")\n * computeMatches()\n * span?.lap(\"matches\")\n * writeOutputs()\n * span?.lap(\"outputs\")\n * // Span end → SPAN ... laps: { inputs: 12, matches: 340, outputs: 8 }\n */\n lap(name: string): void\n}\n\n/**\n * @deprecated `createLogger()` now returns `ConditionalLogger`; use\n * `log.span?.(\"op\")` because spans are only present when enabled.\n */\nexport type SpannedLogger = ConditionalLogger\n\n// ============ ConditionalLogger ============\n\nexport interface ConditionalLogger extends Disposable {\n readonly name: string\n readonly props: Readonly<Record<string, unknown>>\n readonly level: LogLevel\n\n /** Metrics collector, present when `{ metrics: true }` is in config or `withMetrics()` was applied */\n readonly metrics?: import(\"./metrics.js\").MetricsCollector\n\n dispatch(event: Event): void\n\n trace?: (message: LazyMessage, data?: Record<string, unknown>) => void\n debug?: (message: LazyMessage, data?: Record<string, unknown>) => void\n info?: (message: LazyMessage, data?: Record<string, unknown>) => void\n warn?: (message: LazyMessage, data?: Record<string, unknown>) => void\n error?: {\n (message: LazyMessage, data?: Record<string, unknown>): void\n (error: Error, data?: Record<string, unknown>): void\n (error: Error, message: string, data?: Record<string, unknown>): void\n }\n\n /** @deprecated Use .child() */\n logger(namespace?: string, props?: Record<string, unknown>): ConditionalLogger\n span?(namespace?: string, props?: LazyProps): SpanLogger\n child(namespace: string, props?: Record<string, unknown>): ConditionalLogger\n child(context: Record<string, unknown>): ConditionalLogger\n end(): void\n}\n\nconst SPAN_ENABLED = Symbol(\"loggily.spanEnabled\")\n\ntype SpanEnabledChecker = (namespace: string) => boolean\n\ninterface SpanEnabledCarrier {\n [SPAN_ENABLED]?: SpanEnabledChecker\n}\n\nfunction spanIsEnabled(logger: ConditionalLogger, namespace: string): boolean {\n if (collectSpans) return true\n const checker = (logger as unknown as SpanEnabledCarrier)[SPAN_ENABLED]\n return checker ? checker(namespace) : true\n}\n\n// ============ ID Generation ============\n\nimport {\n generateSpanId,\n generateTraceId,\n resetIdCounters,\n shouldSample,\n setIdFormat,\n setSampleRate,\n} from \"./tracing.js\"\nimport type { IdFormat } from \"./tracing.js\"\n\nexport function resetIds(): void {\n resetIdCounters()\n}\n\n// ============ Context Propagation Hooks ============\n\nlet _getContextTags: (() => Record<string, string>) | null = null\nlet _getContextParent:\n | (() => { spanId: string; traceId: string } | null)\n | null = null\nlet _enterContext:\n | ((spanId: string, traceId: string, parentId: string | null) => void)\n | null = null\nlet _exitContext: ((spanId: string) => void) | null = null\n\n/** @internal */\nexport function _setContextHooks(hooks: {\n getContextTags: () => Record<string, string>\n getContextParent: () => { spanId: string; traceId: string } | null\n enterContext: (\n spanId: string,\n traceId: string,\n parentId: string | null,\n ) => void\n exitContext: (spanId: string) => void\n}): void {\n _getContextTags = hooks.getContextTags\n _getContextParent = hooks.getContextParent\n _enterContext = hooks.enterContext\n _exitContext = hooks.exitContext\n}\n\n/** @internal */\nexport function _clearContextHooks(): void {\n _getContextTags = null\n _getContextParent = null\n _enterContext = null\n _exitContext = null\n}\n\n// ============ SpanData Proxy ============\n\ninterface SpanDataFields {\n id: string\n traceId: string\n parentId: string | null\n startTime: number\n endTime: number | null\n duration: number | null\n}\n\nexport function createSpanDataProxy(\n getFields: () => SpanDataFields,\n attrs: Record<string, unknown>,\n): SpanData {\n const READONLY_KEYS = new Set([\n \"id\",\n \"traceId\",\n \"parentId\",\n \"startTime\",\n \"endTime\",\n \"duration\",\n ])\n return new Proxy(attrs, {\n get(_target, prop) {\n if (READONLY_KEYS.has(prop as string)) {\n return getFields()[prop as keyof SpanDataFields]\n }\n return attrs[prop as string]\n },\n set(_target, prop, value) {\n if (READONLY_KEYS.has(prop as string)) {\n return false\n }\n attrs[prop as string] = value\n return true\n },\n }) as SpanData\n}\n\n// ============ Span Collection ============\n\nconst collectedSpans: SpanData[] = []\nlet collectSpans = false\n\nexport function startCollecting(): void {\n collectSpans = true\n collectedSpans.length = 0\n}\n\nexport function stopCollecting(): SpanData[] {\n collectSpans = false\n return [...collectedSpans]\n}\n\nexport function getCollectedSpans(): SpanData[] {\n return [...collectedSpans]\n}\n\nexport function clearCollectedSpans(): void {\n collectedSpans.length = 0\n}\n\n// ============ Implementation ============\n\nfunction resolveMessage(msg: LazyMessage): string {\n return typeof msg === \"function\" ? msg() : msg\n}\n\ninterface SpanLap {\n /** Lap name. */\n readonly name: string\n /** Wall time (ms since span start) at lap creation. */\n readonly elapsedMs: number\n /** Delta (ms) since previous lap, or since span start for the first lap. */\n readonly deltaMs: number\n}\n\ninterface MutableSpanData {\n id: string\n traceId: string\n parentId: string | null\n startTime: number\n endTime: number | null\n duration: number | null\n attrs: Record<string, unknown>\n laps: SpanLap[]\n}\n\nfunction createLoggerImpl(\n name: string,\n props: Record<string, unknown>,\n pipeline: Pipeline,\n): Logger {\n const emitLog = (\n level: OutputLogLevel,\n msgOrError: LazyMessage | Error,\n dataOrMsg?: Record<string, unknown> | string,\n extraData?: Record<string, unknown>,\n ): void => {\n let message: string\n let data: Record<string, unknown> | undefined\n // Raw user args, in call order (after the message). Console sinks spread\n // these to console.* so DevTools keeps objects expandable. We preserve\n // the original references — no JSON-stringifying, no ANSI embedding.\n //\n // Convention:\n // - `log.error(err, …)` → prepend the Error so DevTools shows its\n // clickable stack.\n // - Any structured data (merged context + props + user data) goes in\n // as a single trailing object so it renders as one expandable node.\n const userArgs: unknown[] = []\n\n if (msgOrError instanceof Error) {\n const err = msgOrError\n const contextTags = _getContextTags?.() ?? {}\n if (typeof dataOrMsg === \"string\") {\n message = dataOrMsg\n data = {\n ...contextTags,\n ...props,\n ...extraData,\n error_type: err.name,\n error_message: err.message,\n error_stack: err.stack,\n error_code: (err as { code?: string }).code,\n error_cause:\n err.cause !== undefined ? serializeCause(err.cause) : undefined,\n }\n } else {\n message = err.message\n data = {\n ...contextTags,\n ...props,\n ...(dataOrMsg as Record<string, unknown>),\n error_type: err.name,\n error_stack: err.stack,\n error_code: (err as { code?: string }).code,\n error_cause:\n err.cause !== undefined ? serializeCause(err.cause) : undefined,\n }\n }\n userArgs.push(err)\n if (data && Object.keys(data).length > 0) userArgs.push(data)\n } else {\n message = resolveMessage(msgOrError)\n const contextTags = _getContextTags?.()\n data =\n contextTags && Object.keys(contextTags).length > 0\n ? {\n ...contextTags,\n ...props,\n ...(dataOrMsg as Record<string, unknown>),\n }\n : Object.keys(props).length > 0 || dataOrMsg\n ? { ...props, ...(dataOrMsg as Record<string, unknown>) }\n : undefined\n if (data && Object.keys(data).length > 0) userArgs.push(data)\n }\n\n const event: LogEvent = {\n kind: \"log\",\n time: Date.now(),\n namespace: name,\n level,\n message,\n props: data,\n userArgs,\n }\n pipeline.dispatch(event)\n }\n\n const logger: Logger & SpanEnabledCarrier = {\n name,\n props: Object.freeze({ ...props }),\n\n get level(): LogLevel {\n return pipeline.level\n },\n\n [SPAN_ENABLED](namespace: string): boolean {\n return pipeline.spanEnabled(namespace)\n },\n\n dispatch(event: Event): void {\n pipeline.dispatch(event)\n },\n\n [Symbol.dispose](): void {\n pipeline.dispose()\n },\n\n trace: (msg, data) => emitLog(\"trace\", msg, data),\n debug: (msg, data) => emitLog(\"debug\", msg, data),\n info: (msg, data) => emitLog(\"info\", msg, data),\n warn: (msg, data) => emitLog(\"warn\", msg, data),\n error: (\n msgOrError: LazyMessage | Error,\n dataOrMsg?: Record<string, unknown> | string,\n extraData?: Record<string, unknown>,\n ) => emitLog(\"error\", msgOrError, dataOrMsg, extraData),\n\n /** @deprecated Use .child() instead */\n logger(\n namespace?: string,\n childProps?: Record<string, unknown>,\n ): ConditionalLogger {\n return this.child(namespace ?? \"\", childProps)\n },\n\n span(_namespace?: string, _childProps?: LazyProps): SpanLogger {\n throw new Error(\n \"loggily: span() requires the withSpans() plugin. Use pipe(baseCreateLogger, withSpans()) or the default createLogger.\",\n )\n },\n\n child(\n namespaceOrContext?: string | Record<string, unknown>,\n childProps?: Record<string, unknown>,\n ): ConditionalLogger {\n if (typeof namespaceOrContext === \"string\") {\n const childName = namespaceOrContext\n ? `${name}:${namespaceOrContext}`\n : name\n const mergedProps = { ...props, ...childProps }\n return wrapConditional(\n createLoggerImpl(childName, mergedProps, pipeline),\n () => pipeline.level,\n )\n }\n // Object -> context fields, same namespace\n return wrapConditional(\n createLoggerImpl(name, { ...props, ...namespaceOrContext }, pipeline),\n () => pipeline.level,\n )\n },\n\n end(): void {\n // no-op for non-span loggers\n },\n }\n\n return logger\n}\n\n// ============ ConditionalLogger Proxy ============\n\nfunction wrapConditional(\n logger: Logger,\n getLevel: () => LogLevel,\n): ConditionalLogger {\n return new Proxy(logger as ConditionalLogger, {\n get(target, prop: string | symbol) {\n if (\n typeof prop === \"string\" &&\n prop in LOG_LEVEL_PRIORITY &&\n prop !== \"silent\"\n ) {\n if (\n LOG_LEVEL_PRIORITY[prop as keyof typeof LOG_LEVEL_PRIORITY] <\n LOG_LEVEL_PRIORITY[getLevel()]\n ) {\n return undefined\n }\n }\n // span is optional on ConditionalLogger: return undefined if base impl is the error-thrower\n if (prop === \"span\") {\n const val = (target as unknown as Record<string | symbol, unknown>)[\n prop\n ]\n // If span is the default error-throwing stub, return undefined (making it optional)\n if (val === baseSpanStub) return undefined\n return val\n }\n return (target as unknown as Record<string | symbol, unknown>)[prop]\n },\n })\n}\n\n// Sentinel reference for detecting the base span stub\nconst baseSpanStub = function baseSpanStub(\n _namespace?: string,\n _childProps?: LazyProps,\n): SpanLogger {\n throw new Error(\n \"loggily: span() requires the withSpans() plugin. Use pipe(baseCreateLogger, withSpans()) or the default createLogger.\",\n )\n}\n\n// ============ withSpans Plugin ============\n\n/**\n * Plugin: adds span creation capability to loggers.\n * Without this plugin, `.span` is undefined on ConditionalLogger.\n * Included by default in `createLogger`.\n */\nexport function withSpans(): LoggerPlugin {\n return (factory, _ctx) => {\n return (name, configOrProps?) => {\n const logger = factory(name, configOrProps)\n return augmentWithSpans(logger, null, null, true)\n }\n }\n}\n\ninterface SpanState {\n parentSpanId: string | null\n traceId: string | null\n traceSampled: boolean\n}\n\nfunction augmentWithSpans(\n logger: ConditionalLogger,\n parentSpanId: string | null,\n traceId: string | null,\n traceSampled: boolean,\n): ConditionalLogger {\n const spanState: SpanState = { parentSpanId, traceId, traceSampled }\n const spanMethod = spanIsEnabled(logger, logger.name)\n ? createSpanMethod(logger, spanState)\n : undefined\n\n return new Proxy(logger, {\n get(target, prop: string | symbol) {\n if (prop === \"span\") {\n return spanMethod\n }\n if (prop === \"child\") {\n return function child(\n namespaceOrContext?: string | Record<string, unknown>,\n childProps?: Record<string, unknown>,\n ): ConditionalLogger {\n const childLogger = target.child(\n namespaceOrContext as string,\n childProps,\n )\n // Child loggers inherit span state (parent/trace context)\n return augmentWithSpans(\n childLogger,\n spanState.parentSpanId,\n spanState.traceId,\n spanState.traceSampled,\n )\n }\n }\n if (prop === \"logger\") {\n return function logger(\n namespace?: string,\n childProps?: Record<string, unknown>,\n ): ConditionalLogger {\n const childLogger = target.logger(namespace, childProps)\n return augmentWithSpans(\n childLogger,\n spanState.parentSpanId,\n spanState.traceId,\n spanState.traceSampled,\n )\n }\n }\n return (target as unknown as Record<string | symbol, unknown>)[prop]\n },\n })\n}\n\nfunction createSpanMethod(\n logger: ConditionalLogger,\n spanState: SpanState,\n): (namespace?: string, childProps?: LazyProps) => SpanLogger {\n return (namespace?: string, childProps?: LazyProps): SpanLogger => {\n const childName = namespace ? `${logger.name}:${namespace}` : logger.name\n const resolvedChildProps =\n typeof childProps === \"function\" ? childProps() : childProps\n const mergedProps = { ...logger.props, ...resolvedChildProps }\n const newSpanId = generateSpanId()\n\n let resolvedParentId = spanState.parentSpanId\n let resolvedTraceId = spanState.traceId\n\n if (!resolvedParentId && _getContextParent) {\n const ctxParent = _getContextParent()\n if (ctxParent) {\n resolvedParentId = ctxParent.spanId\n resolvedTraceId = resolvedTraceId || ctxParent.traceId\n }\n }\n\n const isNewTrace = !resolvedTraceId\n const finalTraceId = resolvedTraceId || generateTraceId()\n const sampled = isNewTrace ? shouldSample() : spanState.traceSampled\n\n const newSpanData: MutableSpanData = {\n id: newSpanId,\n traceId: finalTraceId,\n parentId: resolvedParentId,\n startTime: Date.now(),\n endTime: null,\n duration: null,\n attrs: {},\n laps: [],\n }\n\n // Create a child logger for the span to emit logs through\n const childLogger = logger.child(namespace ?? \"\", resolvedChildProps)\n // Augment the child with span capability, setting this span as parent\n const spanAugmented = augmentWithSpans(\n childLogger,\n newSpanId,\n finalTraceId,\n sampled,\n )\n\n _enterContext?.(newSpanId, finalTraceId, resolvedParentId)\n\n const disposeSpan = () => {\n if (newSpanData.endTime !== null) return\n\n newSpanData.endTime = Date.now()\n newSpanData.duration = newSpanData.endTime - newSpanData.startTime\n\n if (collectSpans) {\n collectedSpans.push(\n createSpanDataProxy(\n () => ({\n id: newSpanData.id,\n traceId: newSpanData.traceId,\n parentId: newSpanData.parentId,\n startTime: newSpanData.startTime,\n endTime: newSpanData.endTime,\n duration: newSpanData.duration,\n }),\n { ...newSpanData.attrs },\n ),\n )\n }\n\n _exitContext?.(newSpanId)\n if (sampled) {\n // Emit laps as a flat { name: deltaMs } object — compact for both\n // console and JSON output. spanData.laps stays available for\n // programmatic consumers that want elapsedMs / per-lap details.\n const lapsProp =\n newSpanData.laps.length > 0\n ? Object.fromEntries(\n newSpanData.laps.map((l) => [l.name, l.deltaMs]),\n )\n : undefined\n const spanEvent: SpanEvent = {\n kind: \"span\",\n time: newSpanData.endTime,\n namespace: childName,\n name: childName,\n duration: newSpanData.duration,\n props: {\n ...mergedProps,\n ...newSpanData.attrs,\n ...(lapsProp ? { laps: lapsProp } : {}),\n },\n spanId: newSpanData.id,\n traceId: newSpanData.traceId,\n parentId: newSpanData.parentId,\n }\n logger.dispatch(spanEvent)\n }\n }\n\n const lapImpl = (name: string): void => {\n const now = Date.now()\n const elapsedMs = now - newSpanData.startTime\n const lastLap = newSpanData.laps[newSpanData.laps.length - 1]\n const deltaMs = lastLap ? elapsedMs - lastLap.elapsedMs : elapsedMs\n newSpanData.laps.push({ name, elapsedMs, deltaMs })\n }\n\n const spanDataProxy = createSpanDataProxy(\n () => ({\n id: newSpanData.id,\n traceId: newSpanData.traceId,\n parentId: newSpanData.parentId,\n startTime: newSpanData.startTime,\n endTime: newSpanData.endTime,\n duration:\n newSpanData.endTime !== null\n ? newSpanData.endTime - newSpanData.startTime\n : Date.now() - newSpanData.startTime,\n }),\n newSpanData.attrs,\n )\n\n // Build the SpanLogger by overlaying span-specific properties onto the augmented child.\n // Allow Symbol.dispose to be overridden (withMetrics wraps it).\n let currentDispose = disposeSpan\n const spanLogger = new Proxy(spanAugmented as unknown as SpanLogger, {\n get(target, prop: string | symbol) {\n if (prop === \"spanData\") return spanDataProxy\n if (prop === Symbol.dispose) return currentDispose\n if (prop === \"end\") {\n return () => {\n if (newSpanData.endTime === null) {\n currentDispose()\n }\n }\n }\n if (prop === \"lap\") return lapImpl\n if (prop === \"name\") return childName\n if (prop === \"props\") return Object.freeze({ ...mergedProps })\n return (target as unknown as Record<string | symbol, unknown>)[prop]\n },\n set(_target, prop: string | symbol, value: unknown) {\n if (prop === Symbol.dispose) {\n currentDispose = value as () => void\n return true\n }\n return false\n },\n })\n\n return spanLogger\n }\n}\n\n// ============ Public API ============\n\n// ============ Base createLogger ============\n\n/**\n * Base createLogger — requires a config array.\n * Use the default `createLogger` export (with `withEnvDefaults`) for zero-config.\n *\n * Note: loggers from baseCreateLogger do NOT have `.span()` capability.\n * Use `pipe(baseCreateLogger, withSpans())` or the default `createLogger` for spans.\n */\nexport function baseCreateLogger(\n name: string,\n configOrProps?: ConfigElement[] | Record<string, unknown>,\n): ConditionalLogger {\n let pipeline: Pipeline\n let props: Record<string, unknown> = {}\n\n if (Array.isArray(configOrProps)) {\n pipeline = buildPipeline(configOrProps)\n } else if (configOrProps && typeof configOrProps === \"object\") {\n props = configOrProps as Record<string, unknown>\n pipeline = buildPipeline([\"console\"])\n } else {\n pipeline = buildPipeline([\"console\"])\n }\n\n const logger = createLoggerImpl(name, props, pipeline)\n // Replace the span method with the sentinel stub so wrapConditional can detect it\n ;(logger as unknown as Record<string, unknown>).span = baseSpanStub\n return wrapConditional(logger, () => pipeline.level)\n}\n\n// ============ Compose ============\n\nexport type LoggerFactory = (\n name: string,\n configOrProps?: ConfigElement[] | Record<string, unknown>,\n) => ConditionalLogger\n\nexport interface PluginCtx {\n [key: string]: unknown\n}\n\nexport type LoggerPlugin = (\n factory: LoggerFactory,\n ctx: PluginCtx,\n) => LoggerFactory\n\nexport function pipe(\n base: LoggerFactory,\n ...plugins: LoggerPlugin[]\n): LoggerFactory {\n const ctx: PluginCtx = {}\n return plugins.reduce((factory, plugin) => plugin(factory, ctx), base)\n}\n\n// ============ withEnvDefaults Plugin ============\n\nconst _process = typeof process !== \"undefined\" ? process : undefined\nconst _env = _process?.env ?? ({} as Record<string, string | undefined>)\n\n// Env config — read fresh each dispatch (process.env access is ~ns, parsing is trivial)\nfunction currentLevel(): LogLevel {\n return readEnvLevel()\n}\nfunction currentNs(): NsFilter | null {\n return readEnvNs()\n}\nfunction currentFormat(): LogFormat {\n return readEnvFormat()\n}\nfunction currentTrace(): { enabled: boolean; filter: NsFilter | null } {\n return readEnvTrace()\n}\n\n/**\n * Writer signature for {@link addWriter}.\n *\n * Receives the pre-formatted text (per the active LOG_FORMAT) plus the\n * structural fields needed to route or filter writes downstream:\n * - `level`: log level (or `\"span\"`) for level-aware sinks\n * - `namespace`: emitting namespace; the `{ ns }` filter on\n * {@link addWriter} routes by this value\n * - `event`: full structured Event so JSONL sinks can re-serialize with\n * custom fields (e.g., {@link createLogger}'s `props` end up here).\n *\n * Two-arg writers (legacy shape) keep working — JS ignores extra arguments.\n */\nexport type WriterFn = (\n formatted: string,\n level: string,\n namespace: string,\n event: Event,\n) => void\n\n// Runtime state for legacy addWriter/setSuppressConsole\nconst _writers: Array<WriterFn> = []\nlet _suppressConsole = false\n\n// File writer factory — set by index.ts (avoids node:fs in core.ts for browser compat)\nlet _logFileWriterFactory:\n | ((path: string) => { write: (s: string) => void; close: () => void })\n | null = null\n/** @internal */\nexport function _setLogFileWriterFactory(\n factory: typeof _logFileWriterFactory,\n): void {\n _logFileWriterFactory = factory\n}\n\n/**\n * Plugin: read defaults from environment variables (LOG_LEVEL, DEBUG, LOG_FORMAT, TRACE, LOG_FILE).\n * Included by default. Omit to disable env-var behavior entirely.\n *\n * When no config array is given, provides console output + env-var-based config.\n * When a config array IS given, env vars are already used as defaults by buildPipeline.\n * Legacy setters (setLogLevel, addWriter, etc.) affect loggers created without explicit config.\n */\nexport function withEnvDefaults(): LoggerPlugin {\n return (factory, _ctx) => (name, configOrProps?) => {\n // Apply tracing env vars (once per logger creation, idempotent)\n const envIdFormat = _env.TRACE_ID_FORMAT?.toLowerCase()\n if (envIdFormat === \"simple\" || envIdFormat === \"w3c\") {\n setIdFormat(envIdFormat as IdFormat)\n }\n const envSampleRate = _env.TRACE_SAMPLE_RATE\n if (envSampleRate !== undefined) {\n const rate = Number.parseFloat(envSampleRate)\n if (!Number.isNaN(rate) && rate >= 0 && rate <= 1) {\n setSampleRate(rate)\n }\n }\n\n // Explicit config array — pass through, buildPipeline reads env defaults\n if (Array.isArray(configOrProps)) return factory(name, configOrProps)\n\n // No config array — use env-dynamic pipeline.\n // Pass a config array through the factory so all upstream plugins get applied,\n // with a stage that delegates to the env pipeline for dynamic dispatch.\n const envPipeline = createEnvPipeline()\n const envStage: ConfigElement = (event: Event) => {\n envPipeline.dispatch(event)\n return null // consume the event (env pipeline handles all output)\n }\n\n // Props are passed as the first config element to include them in log output\n if (configOrProps && typeof configOrProps === \"object\") {\n // Object props — create logger with props + env pipeline\n // We need to pass props AND a config array. But factory only accepts one or the other.\n // Solution: create via factory with config array, then the child() with props.\n const logger = factory(name, [{ level: \"trace\" as LogLevel }, envStage])\n return applyNamespaceGating(\n logger.child(configOrProps as Record<string, unknown>),\n )\n }\n\n return applyNamespaceGating(\n factory(name, [{ level: \"trace\" as LogLevel }, envStage]),\n )\n }\n}\n\n/**\n * Wrap a logger so that conditional method gating is namespace-aware.\n * When DEBUG=myapp:db, only loggers whose namespace matches get debug enabled.\n * Without this, all loggers get debug because readEnvLevel() bumps globally.\n */\nfunction applyNamespaceGating(logger: ConditionalLogger): ConditionalLogger {\n return new Proxy(logger, {\n get(target, prop: string | symbol) {\n if (prop === SPAN_ENABLED) {\n return (namespace: string): boolean => {\n if (collectSpans) return true\n const trace = currentTrace()\n if (!trace.enabled) return false\n if (trace.filter && !trace.filter(namespace)) return false\n const ns = currentNs()\n if (ns && !ns(namespace)) return false\n return true\n }\n }\n if (\n typeof prop === \"string\" &&\n prop in LOG_LEVEL_PRIORITY &&\n prop !== \"silent\"\n ) {\n const nsLevel = readEnvLevelForNamespace(target.name)\n if (\n LOG_LEVEL_PRIORITY[prop as keyof typeof LOG_LEVEL_PRIORITY] <\n LOG_LEVEL_PRIORITY[nsLevel]\n ) {\n return undefined\n }\n }\n return (target as unknown as Record<string | symbol, unknown>)[prop]\n },\n })\n}\n\nfunction createEnvPipeline(): Pipeline {\n const disposables: (() => void)[] = []\n const logFile = _env.LOG_FILE\n let fileSink: ((event: Event) => void) | null = null\n if (logFile && _logFileWriterFactory) {\n const writer = _logFileWriterFactory(logFile)\n fileSink = (event: Event) => {\n const fmt =\n currentFormat() === \"json\" ? formatJSONEvent : formatConsoleEvent\n writer.write(fmt(event))\n }\n disposables.push(() => writer.close())\n }\n\n const dispatch = (event: Event): void => {\n if (\n event.kind === \"log\" &&\n LOG_LEVEL_PRIORITY[event.level] < LOG_LEVEL_PRIORITY[currentLevel()]\n )\n return\n if (event.kind === \"span\") {\n const trace = currentTrace()\n if (!trace.enabled) return\n if (trace.filter && !trace.filter(event.namespace)) return\n }\n const ns = currentNs()\n if (ns && !ns(event.namespace)) return\n\n // Writers receive pre-formatted text + structural fields. Two-arg\n // legacy writers ignore the trailing namespace + event args.\n const format = currentFormat()\n const formatter = format === \"json\" ? formatJSONEvent : formatConsoleEvent\n const text = formatter(event)\n const lvl = event.kind === \"log\" ? event.level : \"span\"\n for (const w of _writers) w(text, lvl, event.namespace, event)\n\n // Structured console output: browser DevTools sees %c-styled prefix +\n // expandable objects; Node sees ANSI prefix + util.format-inspectable\n // objects. The sink is recreated per dispatch so LOG_FORMAT env flips\n // take effect without logger rebuild.\n if (!_suppressConsole) createStructuredConsoleSink(format)(event)\n fileSink?.(event)\n }\n\n return {\n dispatch,\n spanEnabled(namespace: string): boolean {\n const trace = currentTrace()\n if (!trace.enabled) return false\n if (trace.filter && !trace.filter(namespace)) return false\n const ns = currentNs()\n if (ns && !ns(namespace)) return false\n return true\n },\n get level() {\n return currentLevel()\n },\n dispose: () => {\n for (const d of disposables) d()\n },\n }\n}\n\n// ============ withConfigMetrics Plugin ============\n\n/**\n * Plugin: when `{ metrics: true }` appears in the config array, automatically\n * creates a MetricsCollector and applies withMetrics to the logger.\n * The collector is accessible via `logger.metrics`.\n */\nexport function withConfigMetrics(): LoggerPlugin {\n return (factory, _ctx) => {\n return (name, configOrProps?) => {\n const logger = factory(name, configOrProps)\n\n // Only scan config arrays, not props objects\n if (!Array.isArray(configOrProps)) return logger\n\n // Check if any config object has metrics: true (flat scan, not into branches)\n const hasMetrics = configOrProps.some(\n (el) =>\n typeof el === \"object\" &&\n el !== null &&\n !Array.isArray(el) &&\n \"metrics\" in el &&\n (el as Record<string, unknown>).metrics === true,\n )\n\n if (!hasMetrics) return logger\n\n const collector = _createMetricsCollector()\n return _withMetrics(collector)(logger)\n }\n }\n}\n\n/** Default createLogger — includes withEnvDefaults + withSpans + withConfigMetrics. */\nexport const createLogger: (\n name: string,\n configOrProps?: ConfigElement[] | Record<string, unknown>,\n) => ConditionalLogger = pipe(\n baseCreateLogger,\n withEnvDefaults(),\n withSpans(),\n withConfigMetrics(),\n) as (\n name: string,\n configOrProps?: ConfigElement[] | Record<string, unknown>,\n) => ConditionalLogger\n\n/** Test helper — all levels, console output. */\nexport function createTestLogger(name: string): ConditionalLogger {\n return pipe(baseCreateLogger, withSpans())(name, [\n { level: \"trace\" },\n \"console\",\n ])\n}\n\n// ============ Legacy Setters ============\n\n/** @deprecated Use config array */\nexport function setLogLevel(level: LogLevel): void {\n _env.LOG_LEVEL = level\n}\nexport function getLogLevel(): LogLevel {\n return currentLevel()\n}\nexport function enableSpans(): void {\n _env.TRACE = \"1\"\n}\nexport function disableSpans(): void {\n delete _env.TRACE\n}\nexport function spansAreEnabled(): boolean {\n return !!_env.TRACE\n}\nexport function setTraceFilter(namespaces: string[] | null): void {\n if (!namespaces || namespaces.length === 0) delete _env.TRACE\n else _env.TRACE = namespaces.join(\",\")\n}\nexport function getTraceFilter(): string[] | null {\n return _env.TRACE ? _env.TRACE.split(\",\") : null\n}\nexport function setDebugFilter(namespaces: string[] | null): void {\n if (!namespaces || namespaces.length === 0) delete _env.DEBUG\n else _env.DEBUG = namespaces.join(\",\")\n}\nexport function getDebugFilter(): string[] | null {\n return _env.DEBUG ? _env.DEBUG.split(\",\") : null\n}\nexport function setLogFormat(format: LogFormat): void {\n _env.LOG_FORMAT = format\n}\nexport function getLogFormat(): LogFormat {\n return currentFormat()\n}\nexport function setSuppressConsole(value: boolean): void {\n _suppressConsole = value\n}\nexport type OutputMode = \"console\" | \"stderr\" | \"writers-only\"\nexport function setOutputMode(_mode: OutputMode): void {\n throw new Error(\n 'loggily: setOutputMode() is removed in v2. Use config arrays: omit console from array for writers-only, use \"stderr\" for stderr mode.',\n )\n}\nexport function getOutputMode(): OutputMode {\n return \"console\"\n}\n/**\n * Register a writer for log records. A writer is the terminal stage of a\n * sub-pipeline — it receives formatted records that pass any filters\n * declared by the optional config.\n *\n * Three forms:\n *\n * - **catch-all**: `addWriter(writer)` — every record reaches the writer.\n * - **scoped**: `addWriter({ ns, level }, writer)` — only records matching\n * the namespace pattern and at-or-above the level reach the writer.\n * Same `ConfigObject` shape used in `createLogger(\"x\", [config, sink])`.\n *\n * `ConfigObject` filters supported here:\n *\n * - `ns: string | string[]` — DEBUG-style glob (e.g. `\"bg-recall:*\"`,\n * `\"myapp,-myapp:noisy\"`). Substring excludes count.\n * - `level: LogLevel` — record's level must be >= this. `\"trace\"` < `\"debug\"`\n * < `\"info\"` < `\"warn\"` < `\"error\"` < `\"silent\"`.\n *\n * Returns an unsubscribe handle.\n *\n * Per-namespace fan-out replaces hand-rolled `appendFileSync(path, line)`\n * patterns: every subsystem writes through `createLogger(\"ns:thing\")`, and\n * the host registers per-namespace writers via this function instead of\n * each subsystem reinventing its own env-var + writer.\n */\nexport function addWriter(writer: WriterFn): () => void\nexport function addWriter(config: ConfigObject, writer: WriterFn): () => void\nexport function addWriter(\n arg1: ConfigObject | WriterFn,\n arg2?: WriterFn,\n): () => void {\n if (typeof arg1 === \"function\") return _addRawWriter(arg1)\n if (!arg2) {\n throw new Error(\n \"addWriter: writer fn required when first argument is a config object\",\n )\n }\n const config = arg1\n const matches = config.ns ? parseNsFilter(config.ns) : null\n const minPriority = config.level ? LOG_LEVEL_PRIORITY[config.level] : null\n return _addRawWriter((formatted, level, namespace, event) => {\n if (matches && !matches(namespace)) return\n if (minPriority !== null) {\n const recordPriority =\n LOG_LEVEL_PRIORITY[level as LogLevel] ?? LOG_LEVEL_PRIORITY.info\n if (recordPriority < minPriority) return\n }\n arg2(formatted, level, namespace, event)\n })\n}\n\nfunction _addRawWriter(writer: WriterFn): () => void {\n _writers.push(writer)\n return () => {\n const i = _writers.indexOf(writer)\n if (i !== -1) _writers.splice(i, 1)\n }\n}\nexport function writeSpan(\n namespace: string,\n duration: number,\n attrs: Record<string, unknown>,\n): void {\n createEnvPipeline().dispatch({\n kind: \"span\",\n time: Date.now(),\n namespace,\n name: namespace,\n duration,\n props: attrs,\n spanId: (attrs.span_id as string) ?? \"\",\n traceId: (attrs.trace_id as string) ?? \"\",\n parentId: (attrs.parent_id as string | null) ?? null,\n })\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { A as withEnvDefaults, B as setSampleRate, C as setOutputMode, D as startCollecting, E as spansAreEnabled, F as safeStringify, L as getIdFormat, M as writeSpan, N as LOG_LEVEL_PRIORITY, O as stopCollecting, P as buildPipeline, R as getSampleRate, S as setLogLevel, T as setTraceFilter, V as traceparent, _ as getTraceFilter, a as baseCreateLogger, b as setDebugFilter, d as enableSpans, f as getCollectedSpans, g as getOutputMode, h as getLogLevel, i as addWriter, j as withSpans, k as withConfigMetrics, l as createTestLogger, m as getLogFormat, o as clearCollectedSpans, p as getDebugFilter, s as createLogger, u as disableSpans, v as pipe, w as setSuppressConsole, x as setLogFormat, y as resetIds, z as setIdFormat } from "./core-DKwMMOZw.mjs";
1
+ import { A as withEnvDefaults, B as setSampleRate, C as setOutputMode, D as startCollecting, E as spansAreEnabled, F as safeStringify, L as getIdFormat, M as writeSpan, N as LOG_LEVEL_PRIORITY, O as stopCollecting, P as buildPipeline, R as getSampleRate, S as setLogLevel, T as setTraceFilter, V as traceparent, _ as getTraceFilter, a as baseCreateLogger, b as setDebugFilter, d as enableSpans, f as getCollectedSpans, g as getOutputMode, h as getLogLevel, i as addWriter, j as withSpans, k as withConfigMetrics, l as createTestLogger, m as getLogFormat, o as clearCollectedSpans, p as getDebugFilter, s as createLogger, u as disableSpans, v as pipe, w as setSuppressConsole, x as setLogFormat, y as resetIds, z as setIdFormat } from "./core-CM_9Xqz-.mjs";
2
2
  //#region src/index.browser.ts
3
3
  /** @throws Always — createFileWriter is not available in browser environments */
4
4
  function createFileWriter() {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as withEnvDefaults, B as setSampleRate, C as setOutputMode, D as startCollecting, E as spansAreEnabled, F as safeStringify, H as createFileWriter, I as serializeCause, L as getIdFormat, M as writeSpan, N as LOG_LEVEL_PRIORITY, O as stopCollecting, P as buildPipeline, R as getSampleRate, S as setLogLevel, T as setTraceFilter, V as traceparent, _ as getTraceFilter, a as baseCreateLogger, b as setDebugFilter, c as createSpanDataProxy, d as enableSpans, f as getCollectedSpans, g as getOutputMode, h as getLogLevel, i as addWriter, j as withSpans, k as withConfigMetrics, l as createTestLogger, m as getLogFormat, n as _setContextHooks, o as clearCollectedSpans, p as getDebugFilter, r as _setLogFileWriterFactory, s as createLogger, t as _clearContextHooks, u as disableSpans, v as pipe, w as setSuppressConsole, x as setLogFormat, y as resetIds, z as setIdFormat } from "./core-DKwMMOZw.mjs";
1
+ import { A as withEnvDefaults, B as setSampleRate, C as setOutputMode, D as startCollecting, E as spansAreEnabled, F as safeStringify, H as createFileWriter, I as serializeCause, L as getIdFormat, M as writeSpan, N as LOG_LEVEL_PRIORITY, O as stopCollecting, P as buildPipeline, R as getSampleRate, S as setLogLevel, T as setTraceFilter, V as traceparent, _ as getTraceFilter, a as baseCreateLogger, b as setDebugFilter, c as createSpanDataProxy, d as enableSpans, f as getCollectedSpans, g as getOutputMode, h as getLogLevel, i as addWriter, j as withSpans, k as withConfigMetrics, l as createTestLogger, m as getLogFormat, n as _setContextHooks, o as clearCollectedSpans, p as getDebugFilter, r as _setLogFileWriterFactory, s as createLogger, t as _clearContextHooks, u as disableSpans, v as pipe, w as setSuppressConsole, x as setLogFormat, y as resetIds, z as setIdFormat } from "./core-CM_9Xqz-.mjs";
2
2
  //#region src/index.ts
3
3
  _setLogFileWriterFactory(createFileWriter);
4
4
  //#endregion
package/dist/worker.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as baseCreateLogger, j as withSpans, s as createLogger, v as pipe } from "./core-DKwMMOZw.mjs";
1
+ import { a as baseCreateLogger, j as withSpans, s as createLogger, v as pipe } from "./core-CM_9Xqz-.mjs";
2
2
  //#region src/worker.ts
3
3
  /**
4
4
  * Worker Thread Logger/Console Forwarding
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loggily",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "Debugs, logs, and spans — one API. Disabled logs skip argument evaluation entirely via optional chaining. ~3KB, zero dependencies.",
5
5
  "keywords": [
6
6
  "browser",
@@ -67,11 +67,11 @@
67
67
  "mitata": "^1.0.34",
68
68
  "oxfmt": "0.38.0",
69
69
  "tsdown": "^0.21.7",
70
+ "typescript": "^5.9.0",
70
71
  "vitepress": "^1.6.4",
71
72
  "vitepress-enrich": "^0.4.0",
72
73
  "vitepress-plugin-llms": "^1.12.0",
73
- "vitest": "^4.1.1",
74
- "typescript": "^5.9.0"
74
+ "vitest": "^4.1.1"
75
75
  },
76
76
  "peerDependencies": {
77
77
  "@opentelemetry/api": ">=1.0.0"