{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,QAAQ,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAEhD,MAAM,WAAW,MAAM;IACtB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1D,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1D,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3D;AAID,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAE1D;AASD,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKzD;AAuBD,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAYzD;AA0BD,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAOnD","sourcesContent":["/**\n * Minimal structured logger shared by pi-ai and its consumers. The library\n * never writes files: entries go to an injectable sink (setLogSink). Without\n * a sink, warn/error fall back to console.error and debug/info are dropped.\n * Logging must never throw into the caller.\n */\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport interface LogEntry {\n\tts: string;\n\tlevel: LogLevel;\n\tcomponent: string;\n\tmsg: string;\n\t[field: string]: unknown;\n}\n\nexport type LogSink = (entry: LogEntry) => void;\n\nexport interface Logger {\n\tdebug(msg: string, fields?: Record<string, unknown>): void;\n\tinfo(msg: string, fields?: Record<string, unknown>): void;\n\twarn(msg: string, fields?: Record<string, unknown>): void;\n\terror(msg: string, fields?: Record<string, unknown>): void;\n}\n\nlet sink: LogSink | undefined;\n\n/** Install the process-wide log sink. Pass undefined to restore the default. */\nexport function setLogSink(next: LogSink | undefined): void {\n\tsink = next;\n}\n\nconst REDACTED = \"[REDACTED]\";\nconst SENSITIVE_KEY = /(?:^|[_-])(token|api[_-]?key|secret|password|credential|authorization|cookie)(?:$|[_-])/i;\nconst ENV_ASSIGNMENT =\n\t/\\b[A-Z][A-Z0-9_]*(?:TOKEN|API_KEY|SECRET|PASSWORD|CREDENTIAL|AUTHORIZATION|COOKIE)\\s*=\\s*[^\\s\"']+/g;\nconst AUTHORIZATION_VALUE = /\\b(?:bearer|basic)\\s+[A-Za-z0-9._~+/=:-]+/gi;\nconst KNOWN_TOKEN = /\\b(?:sk-[A-Za-z0-9_-]+|AIza[A-Za-z0-9_-]+|gh[pousr]_[A-Za-z0-9_-]+)\\b/g;\n\n/** Redact common credential formats before diagnostics leave the process. */\nexport function redactSensitiveText(value: string): string {\n\treturn value\n\t\t.replace(ENV_ASSIGNMENT, (assignment) => `${assignment.slice(0, assignment.indexOf(\"=\") + 1)}${REDACTED}`)\n\t\t.replace(AUTHORIZATION_VALUE, REDACTED)\n\t\t.replace(KNOWN_TOKEN, REDACTED);\n}\n\nfunction jsonReplacer(): (key: string, value: unknown) => unknown {\n\tconst seen = new WeakSet<object>();\n\treturn (key, value) => {\n\t\tif (SENSITIVE_KEY.test(key)) return REDACTED;\n\t\tif (typeof value === \"string\") return redactSensitiveText(value);\n\t\tif (typeof value === \"bigint\") return value.toString();\n\t\tif (value instanceof Error) {\n\t\t\treturn {\n\t\t\t\tname: value.name,\n\t\t\t\tmessage: redactSensitiveText(value.message),\n\t\t\t\tstack: redactSensitiveText(value.stack ?? \"\"),\n\t\t\t};\n\t\t}\n\t\tif (typeof value === \"object\" && value !== null) {\n\t\t\tif (seen.has(value)) return \"[Circular]\";\n\t\t\tseen.add(value);\n\t\t}\n\t\treturn value;\n\t};\n}\n\n/** JSON.stringify that never throws and redacts credentials from logs and diagnostics. */\nexport function stringifyLogEntry(entry: LogEntry): string {\n\ttry {\n\t\treturn JSON.stringify(entry, jsonReplacer());\n\t} catch {\n\t\treturn JSON.stringify({\n\t\t\tts: entry.ts,\n\t\t\tlevel: entry.level,\n\t\t\tcomponent: entry.component,\n\t\t\tmsg: redactSensitiveText(String(entry.msg)),\n\t\t\tfieldsError: \"unserializable fields dropped\",\n\t\t});\n\t}\n}\n\nfunction redactLogEntry(entry: LogEntry): LogEntry {\n\treturn JSON.parse(stringifyLogEntry(entry)) as LogEntry;\n}\n\nfunction emit(level: LogLevel, component: string, msg: string, fields?: Record<string, unknown>): void {\n\ttry {\n\t\t// Reserved keys win over caller fields so entries can't be misclassified.\n\t\tconst entry: LogEntry = { ...fields, ts: new Date().toISOString(), level, component, msg };\n\t\ttry {\n\t\t\tif (sink) {\n\t\t\t\tsink(redactLogEntry(entry));\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Broken sink: fall through to the console fallback.\n\t\t}\n\t\tif (level === \"warn\" || level === \"error\") {\n\t\t\tconsole.error(stringifyLogEntry(entry));\n\t\t}\n\t} catch {\n\t\t// Diagnostics must never break the operation being logged.\n\t}\n}\n\nexport function getLogger(component: string): Logger {\n\treturn {\n\t\tdebug: (msg, fields) => emit(\"debug\", component, msg, fields),\n\t\tinfo: (msg, fields) => emit(\"info\", component, msg, fields),\n\t\twarn: (msg, fields) => emit(\"warn\", component, msg, fields),\n\t\terror: (msg, fields) => emit(\"error\", component, msg, fields),\n\t};\n}\n"]}