/** * @fileoverview Runtime debug configuration. * * Reads process environment at call time of {@link loadDebugConfig} so tests can * mutate `process.env` between cases. Production servers typically load once at boot. * * @module debug/config */ /** * @typedef {Object} DebugConfig * @property {boolean} enabled - True when any debug output should be produced. * @property {Set|null} namespaces - Allowed namespaces; `null` means all. * @property {'debug'|'info'|'warn'|'error'} level - Minimum severity to emit. * @property {string|null} filePath - Optional secondary log file path. */ const LEVEL_ORDER = { debug: 10, info: 20, warn: 30, error: 40 }; /** * Parse `DEBUG_VERAE` into an enabled flag and optional namespace allow-list. * * @param {string|undefined} raw - Raw env value (e.g. `"1"`, `"*"`, `"auth,nats"`). * @returns {{ enabled: boolean, namespaces: Set|null }} * * @example * parseDebugVeraeEnv('auth,jobs'); // { enabled: true, namespaces: Set{'auth','jobs'} } * parseDebugVeraeEnv(undefined); // { enabled: false, namespaces: null } */ export function parseDebugVeraeEnv(raw) { if (raw === undefined || raw === null) { return { enabled: false, namespaces: null }; } const trimmed = String(raw).trim(); if (!trimmed || trimmed === '0' || trimmed.toLowerCase() === 'false' || trimmed.toLowerCase() === 'off') { return { enabled: false, namespaces: null }; } if (trimmed === '1' || trimmed === '*' || trimmed.toLowerCase() === 'true' || trimmed.toLowerCase() === 'all') { return { enabled: true, namespaces: null }; } const parts = trimmed .split(',') .map((p) => p.trim().toLowerCase()) .filter(Boolean); if (parts.length === 0) { return { enabled: false, namespaces: null }; } return { enabled: true, namespaces: new Set(parts) }; } /** * Normalize a level string to a known level. * * @param {string|undefined} raw - Env level value. * @param {'debug'|'info'|'warn'|'error'} [fallback='debug'] - Default level. * @returns {'debug'|'info'|'warn'|'error'} */ export function parseLevel(raw, fallback = 'debug') { const value = String(raw ?? fallback).toLowerCase(); if (value in LEVEL_ORDER) return /** @type {'debug'|'info'|'warn'|'error'} */ (value); return fallback; } /** * Load full debug configuration from `process.env`. * * Environment variables: * - `DEBUG_VERAE` — enable + namespaces (see {@link parseDebugVeraeEnv}) * - `DEBUG_VERAE_LEVEL` — minimum level * - `DEBUG_VERAE_FILE` — optional log file path * * @param {NodeJS.ProcessEnv} [env=process.env] - Environment map (injectable for tests). * @returns {DebugConfig} */ export function loadDebugConfig(env = process.env) { const { enabled, namespaces } = parseDebugVeraeEnv(env.DEBUG_VERAE); return { enabled, namespaces, level: parseLevel(env.DEBUG_VERAE_LEVEL, 'debug'), filePath: env.DEBUG_VERAE_FILE ? String(env.DEBUG_VERAE_FILE) : null, }; } /** * Whether a message at `messageLevel` in `namespace` should be emitted. * * @param {DebugConfig} config - Active config. * @param {string} namespace - Logger namespace (e.g. `auth`). * @param {'debug'|'info'|'warn'|'error'} messageLevel - Message severity. * @returns {boolean} */ export function shouldLog(config, namespace, messageLevel) { if (!config.enabled) return false; if (config.namespaces && !config.namespaces.has(String(namespace).toLowerCase())) { return false; } return LEVEL_ORDER[messageLevel] >= LEVEL_ORDER[config.level]; } export { LEVEL_ORDER };