/**
 * Log Sanitizer for Vite Plugin
 *
 * Sanitizes secrets from log messages while preserving debugging information.
 * This is a lightweight version for the vite-plugin, avoiding a dependency
 * on the telemetry package.
 *
 * DESIGN DECISION: Only sanitize actual secrets (API keys, tokens, JWTs).
 * Preserve debugging info (emails, file paths, stack traces) for troubleshooting.
 *
 * @see engineering/projects/o11y-refactor/managed-cloud-telemetry.md#logging-strategy
 */
// ============================================================================
// Secret Patterns - These should NEVER appear in logs
// ============================================================================
const SECRET_PATTERNS = [
    // Bearer/JWT tokens
    {
        pattern: /(\bbearer\s+)[a-zA-Z0-9\-._~+/]+=*/gi,
        replacement: "$1[REDACTED]",
    },
    {
        pattern: /\beyJ[A-Za-z0-9-_]{17,}\.[A-Za-z0-9-_]{20,}\.[A-Za-z0-9-_]{20,}\b/g,
        replacement: "[JWT_REDACTED]",
    },
    // API keys
    {
        pattern: /(\bapi[_\s]?key[:\s=]+)[a-zA-Z0-9\-._~+/]+=*/gi,
        replacement: "$1[REDACTED]",
    },
    // OpenAI/Anthropic keys
    { pattern: /\bsk-[A-Za-z0-9]{48}\b/g, replacement: "[API_KEY_REDACTED]" },
    {
        pattern: /\bsk-ant-[A-Za-z0-9-]{32,}\b/g,
        replacement: "[API_KEY_REDACTED]",
    },
];
/**
 * Fields that contain secrets and should be stripped from log objects.
 * Only includes actual secret field names - NOT debugging info like stack traces.
 */
const SECRET_FIELDS = new Set([
    // Auth/secrets
    "password",
    "apikey",
    "api_key",
    "secret",
    "jwt",
    "access_token",
    "refresh_token",
    "private_key",
    "credentials",
    "auth_token",
    "authorization",
    "cookie",
    "x-api-key",
]);
// ============================================================================
// Public API
// ============================================================================
/**
 * Sanitizes secrets from a log message string.
 * Preserves debugging info (emails, file paths, etc.) for troubleshooting.
 */
export function sanitizeLogMessage(message) {
    if (!message || typeof message !== "string") {
        return message;
    }
    let sanitized = message;
    for (const { pattern, replacement } of SECRET_PATTERNS) {
        pattern.lastIndex = 0;
        sanitized = sanitized.replace(pattern, replacement);
    }
    return sanitized;
}
/**
 * Sanitizes secrets within a stack trace while preserving the full trace.
 * Stack traces are essential for debugging - only redact secrets within them.
 */
export function redactStackTrace(stack) {
    if (!stack || typeof stack !== "string") {
        return stack;
    }
    // Only sanitize secrets, preserve the full stack trace for debugging
    return sanitizeLogMessage(stack);
}
/**
 * Recursively sanitizes secrets from an object.
 * Strips secret fields entirely, sanitizes secrets in string values.
 *
 * NOTE: For object inputs, this returns a plain `Record<string, unknown>` copy.
 * The original prototype chain is not preserved (e.g., Error instances become
 * plain objects). The return type `T` is kept for convenience at call sites
 * but callers should not rely on `instanceof` checks after sanitization.
 */
export function sanitizeLogObject(obj, depth = 0) {
    if (depth > 10) {
        return "[MAX_DEPTH_REACHED]";
    }
    if (obj === null || obj === undefined) {
        return obj;
    }
    if (typeof obj === "string") {
        return sanitizeLogMessage(obj);
    }
    if (typeof obj !== "object") {
        return obj;
    }
    if (Array.isArray(obj)) {
        return obj.map((item) => sanitizeLogObject(item, depth + 1));
    }
    const sanitized = {};
    for (const [key, value] of Object.entries(obj)) {
        const lowerKey = key.toLowerCase();
        // Strip secret fields entirely
        if (SECRET_FIELDS.has(lowerKey)) {
            continue;
        }
        // Recursively sanitize all other fields
        sanitized[key] = sanitizeLogObject(value, depth + 1);
    }
    return sanitized;
}
export function safeJsonParse(str) {
    try {
        return JSON.parse(str);
    }
    catch {
        return undefined;
    }
}
/**
 * Safe JSON stringify with secret sanitization.
 *
 * NOTE: This function strips object keys that match SECRET_FIELDS (e.g.,
 * "password", "api_key", "access_token") from the serialized output. Callers
 * outside of logging contexts should be aware that fields with these exact key
 * names will be omitted from the resulting JSON string.
 */
export function safeJsonStringify(obj, space) {
    if (obj === null || obj === undefined) {
        return "{}";
    }
    if (typeof obj === "string") {
        return sanitizeLogMessage(obj);
    }
    const seen = new WeakSet();
    return JSON.stringify(obj, (key, value) => {
        // Strip secret fields
        if (key && SECRET_FIELDS.has(key.toLowerCase())) {
            return undefined;
        }
        if (value === null || value === undefined) {
            return value;
        }
        if (typeof value === "object") {
            if (seen.has(value)) {
                return "[Circular Reference]";
            }
            seen.add(value);
            if (value instanceof Error) {
                return {
                    name: value.name,
                    message: sanitizeLogMessage(value.message),
                    // Preserve full stack trace for debugging
                    stack: sanitizeLogMessage(value.stack || ""),
                };
            }
        }
        if (typeof value === "string") {
            return sanitizeLogMessage(value);
        }
        if (typeof value === "function") {
            return `[Function: ${value.name || "anonymous"}]`;
        }
        if (typeof value === "symbol") {
            return `[Symbol: ${value.toString()}]`;
        }
        if (typeof value === "bigint") {
            return `[BigInt: ${value.toString()}]`;
        }
        return value;
    }, space);
}
//# sourceMappingURL=log-sanitizer.js.map