/** * Aviation logging module with zero-cost disabled path. * * Loggers are instance-scoped: `createAviation({ logLevel, logger })` builds * one via `createAviationLogger()` and injects it into the player, store, * and wiring modules. There is no module-level logger state. * * When logLevel is 'none' (the default), all log methods are no-op functions. * No conditional checks, no argument evaluation beyond normal JS function call * overhead. To avoid template literal evaluation at call sites, prefer: * log.debug('tag', 'loaded item', { uri }) * over: * log.debug('tag', `loaded ${uri}`) */ export type LogLevel = 'none' | 'info' | 'debug' | 'verbose'; export interface AviationLogger { info(tag: string, message: string, data?: Record): void; debug(tag: string, message: string, data?: Record): void; verbose(tag: string, message: string, data?: Record): void; warn(tag: string, message: string, data?: Record): void; error(tag: string, message: string, data?: Record): void; } const consoleLogger: AviationLogger = { info: (tag, msg, data) => console.info(`[Aviation:${tag}] ${msg}`, data ?? ''), debug: (tag, msg, data) => console.debug(`[Aviation:${tag}] ${msg}`, data ?? ''), verbose: (tag, msg, data) => console.debug(`[Aviation:${tag}] ${msg}`, data ?? ''), warn: (tag, msg, data) => console.warn(`[Aviation:${tag}] ${msg}`, data ?? ''), error: (tag, msg, data) => console.error(`[Aviation:${tag}] ${msg}`, data ?? ''), }; const noop = () => {}; /** * Shared no-op logger. Used as the default when a module is created without * an injected logger (e.g. a store constructed directly in tests). */ export const noopLogger: AviationLogger = { info: noop as AviationLogger['info'], debug: noop as AviationLogger['debug'], verbose: noop as AviationLogger['verbose'], warn: noop as AviationLogger['warn'], error: noop as AviationLogger['error'], }; /** Numeric priority for level comparison. */ const LEVEL_PRIORITY: Record = { none: 0, info: 1, debug: 2, verbose: 3, }; /** * Build a level-filtered logger. * * - level='none': all methods are no-ops (zero cost) * - level='info': info + warn + error active * - level='debug': info + debug + warn + error active * - level='verbose': all methods active * * warn and error are always active unless level='none'. */ export function createAviationLogger(options: { level: LogLevel; logger?: AviationLogger; }): AviationLogger { const { level, logger } = options; const activeLogger = logger ?? consoleLogger; const priority = LEVEL_PRIORITY[level]; if (priority === 0) { return noopLogger; } return { info: priority >= LEVEL_PRIORITY.info ? (tag, msg, data) => activeLogger.info(tag, msg, data) : (noop as AviationLogger['info']), debug: priority >= LEVEL_PRIORITY.debug ? (tag, msg, data) => activeLogger.debug(tag, msg, data) : (noop as AviationLogger['debug']), verbose: priority >= LEVEL_PRIORITY.verbose ? (tag, msg, data) => activeLogger.verbose(tag, msg, data) : (noop as AviationLogger['verbose']), warn: (tag, msg, data) => activeLogger.warn(tag, msg, data), error: (tag, msg, data) => activeLogger.error(tag, msg, data), }; } /** * Emit a misuse warning that is always visible in development builds, * regardless of the configured logLevel. * * Use this for "you're using the API wrong" situations — not for * diagnostic output. The warning fires unconditionally in __DEV__ and * is completely stripped in production (dead-code eliminated by Metro/Hermes). * * Unlike `log.warn`, this is NOT silenced by logLevel='none'. */ export function devWarn( tag: string, message: string, data?: Record ): void { if (__DEV__) { console.warn(`[Aviation:${tag}] ${message}`, data ?? ''); } }