voltlog-io 1.0.1 → 1.0.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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/types.ts","../src/core/levels.ts","../src/core/logger.ts","../src/core/pipeline.ts","../src/middleware/ai-enrichment.ts","../src/middleware/alert.ts","../src/middleware/correlation-id.ts","../src/middleware/create-middleware.ts","../src/middleware/deduplication.ts","../src/middleware/heap-usage.ts","../src/middleware/ip.ts","../src/middleware/level-override.ts","../src/middleware/ocpp.ts","../src/middleware/redaction.ts","../src/middleware/sampling.ts","../src/middleware/user-agent.ts","../src/transports/batch.ts","../src/transports/browser-json-stream.ts","../src/transports/console.ts","../src/transports/create-transport.ts","../src/transports/datadog.ts","../src/transports/discord.ts","../src/transports/file.ts","../src/transports/json-stream.ts","../src/transports/loki.ts","../src/transports/pretty.ts","../src/transports/redis.ts","../src/transports/sentry.ts","../src/transports/slack.ts","../src/transports/webhook.ts"],"sourcesContent":["/**\n * @module voltlog-io\n *\n * OCPP-aware structured logger — lightweight, type-safe, framework-agnostic.\n *\n * Works as a standalone package or via `ocpp-ws-io/logger` re-export.\n */\n\n// ─── Level Utilities ─────────────────────────────────────────────\nexport { resolveLevel, shouldIncludeStack, shouldLog } from \"./core/levels.js\";\n// ─── Core ────────────────────────────────────────────────────────\nexport { createLogger } from \"./core/logger.js\";\n// ─── Types ───────────────────────────────────────────────────────\nexport {\n type AlertRule,\n type LogEntry,\n type LogError,\n type Logger,\n type LoggerOptions,\n LogLevel,\n type LogLevelName,\n LogLevelNameMap,\n type LogLevelValue,\n LogLevelValueMap,\n type LogMiddleware,\n type OcppExchangeMeta,\n type Transport,\n} from \"./core/types.js\";\nexport {\n type AiEnrichmentOptions,\n aiEnrichmentMiddleware,\n createOpenAiErrorAnalyzer,\n} from \"./middleware/ai-enrichment.js\";\nexport { alertMiddleware } from \"./middleware/alert.js\";\nexport {\n type CorrelationIdOptions,\n correlationIdMiddleware,\n} from \"./middleware/correlation-id.js\";\nexport { createMiddleware } from \"./middleware/create-middleware.js\";\nexport {\n type DeduplicationOptions,\n deduplicationMiddleware,\n} from \"./middleware/deduplication.js\";\n// Extended Middleware\nexport { heapUsageMiddleware } from \"./middleware/heap-usage.js\";\nexport { ipMiddleware } from \"./middleware/ip.js\";\nexport {\n type LevelOverrideOptions,\n levelOverrideMiddleware,\n} from \"./middleware/level-override.js\";\nexport {\n type OcppMiddlewareOptions,\n ocppMiddleware,\n} from \"./middleware/ocpp.js\";\n// ─── Middleware ──────────────────────────────────────────────────\nexport {\n type RedactionOptions,\n redactionMiddleware,\n} from \"./middleware/redaction.js\";\nexport {\n type SamplingOptions,\n samplingMiddleware,\n} from \"./middleware/sampling.js\";\nexport {\n type UserAgentOptions,\n userAgentMiddleware,\n} from \"./middleware/user-agent.js\";\nexport {\n type BatchTransportOptions,\n batchTransport,\n} from \"./transports/batch.js\";\nexport {\n type BrowserJsonStreamTransportOptions,\n browserJsonStreamTransport,\n} from \"./transports/browser-json-stream.js\";\n// ─── Transports ────────────────────────────────────────────────\nexport {\n type ConsoleTransportOptions,\n consoleTransport,\n} from \"./transports/console.js\";\n// Extended Transports\nexport { createTransport } from \"./transports/create-transport.js\";\nexport {\n type DatadogTransportOptions,\n datadogTransport,\n} from \"./transports/datadog.js\";\nexport {\n type DiscordTransportOptions,\n discordTransport,\n} from \"./transports/discord.js\";\nexport { type FileTransportOptions, fileTransport } from \"./transports/file.js\";\nexport {\n type JsonStreamTransportOptions,\n jsonStreamTransport,\n} from \"./transports/json-stream.js\";\nexport { type LokiTransportOptions, lokiTransport } from \"./transports/loki.js\";\nexport {\n type PrettyTransportOptions,\n prettyTransport,\n} from \"./transports/pretty.js\";\nexport {\n type RedisClient,\n type RedisTransportOptions,\n redisTransport,\n} from \"./transports/redis.js\";\nexport {\n type SentryInstance,\n type SentryTransportOptions,\n sentryTransport,\n} from \"./transports/sentry.js\";\nexport {\n type SlackTransportOptions,\n slackTransport,\n} from \"./transports/slack.js\";\nexport {\n type WebhookTransportOptions,\n webhookTransport,\n} from \"./transports/webhook.js\";\n","/**\n * @module voltlog-io\n * @description Type definitions for the OCPP-aware structured logger.\n */\n\n// ─── Log Levels ──────────────────────────────────────────────────\n\nexport const LogLevel = {\n TRACE: 10,\n DEBUG: 20,\n INFO: 30,\n WARN: 40,\n ERROR: 50,\n FATAL: 60,\n SILENT: Infinity,\n} as const;\n\nexport type LogLevelName = keyof typeof LogLevel;\n\n/** Numeric log level value */\nexport type LogLevelValue = (typeof LogLevel)[LogLevelName];\n\n/** Map from level name (lowercase) to numeric value */\nexport const LogLevelNameMap: Record<string, number> = Object.fromEntries(\n Object.entries(LogLevel).map(([k, v]) => [k.toLowerCase(), v]),\n);\n\n/** Map from numeric value to level name */\nexport const LogLevelValueMap: Record<number, LogLevelName> =\n Object.fromEntries(\n Object.entries(LogLevel)\n .filter(([, v]) => Number.isFinite(v))\n .map(([k, v]) => [v, k as LogLevelName]),\n ) as Record<number, LogLevelName>;\n\n// ─── Log Entry ───────────────────────────────────────────────────\n\nexport interface LogEntry<TMeta = Record<string, unknown>> {\n /** Unique log ID (cuid2) */\n id: string;\n /** Numeric log level */\n level: number;\n /** Human-readable level name */\n levelName: LogLevelName;\n /** Log message */\n message: string;\n /** Unix epoch timestamp (ms) */\n timestamp: number;\n /** User-defined structured metadata (type-safe via generics) */\n meta: TMeta;\n /** Bound context from child logger (e.g. chargePointId, sessionId) */\n context?: Record<string, unknown>;\n /** Correlation ID for tracing across async operations */\n correlationId?: string;\n /** Error information */\n error?: LogError;\n}\n\nexport interface LogError {\n message: string;\n stack?: string;\n code?: string;\n name?: string;\n}\n\n// ─── OCPP Exchange Meta ──────────────────────────────────────────\n\nexport interface OcppExchangeMeta {\n /** Charge point / station identity */\n chargePointId?: string;\n /** OCPP message type */\n messageType?: \"CALL\" | \"CALLRESULT\" | \"CALLERROR\";\n /** OCPP action name (e.g. BootNotification) */\n action?: string;\n /** Message direction */\n direction?: \"IN\" | \"OUT\";\n /** Correlation ID for request/response matching */\n correlationId?: string;\n /** Negotiated OCPP protocol version */\n protocol?: string;\n /** Serialized payload size in bytes */\n payloadSize?: number;\n /** Latency in milliseconds */\n latencyMs?: number;\n /** Response status (e.g. Accepted, Rejected) */\n status?: string;\n}\n\n// ─── Transport ─────────────────────────────────────────────────\n\n/**\n * A Transport receives formatted log entries and delivers them\n * to a destination (console, file, webhook, database, etc.).\n *\n * Transports are async-safe — `write()` can return a Promise.\n */\nexport interface Transport<TMeta = Record<string, unknown>> {\n /** Unique name for this transport */\n name: string;\n /** Optional per-transport level filter */\n level?: LogLevelName;\n /** Process a log entry */\n write(entry: LogEntry<TMeta>): void | Promise<void>;\n /** Flush any buffered entries */\n flush?(): void | Promise<void>;\n /** Graceful shutdown */\n close?(): void | Promise<void>;\n}\n\n// ─── Middleware ───────────────────────────────────────────────────\n\n/**\n * Middleware intercepts log entries before they reach transports.\n * Used for redaction, sampling, enrichment, alerting, etc.\n *\n * Call `next(entry)` to continue the pipeline.\n * Omit `next()` to drop the entry (e.g. sampling).\n */\nexport type LogMiddleware<TMeta = Record<string, unknown>> = (\n entry: LogEntry<TMeta>,\n next: (entry: LogEntry<TMeta>) => void,\n) => void;\n\n// ─── Alert Rule ──────────────────────────────────────────────────\n\n/**\n * Alert rules evaluate log entries and fire callbacks\n * when configurable conditions are met.\n */\nexport interface AlertRule<TMeta = Record<string, unknown>> {\n /** Alert name (for identification) */\n name: string;\n /** Condition — return true if this entry should count toward the alert */\n when: (entry: LogEntry<TMeta>) => boolean;\n /** Number of matching entries required to fire (default: 1) */\n threshold?: number;\n /** Time window in ms for threshold counting */\n windowMs?: number;\n /** Minimum cooldown in ms between alert firings (default: 0) */\n cooldownMs?: number;\n /** Callback fired when alert conditions are met */\n onAlert: (entries: LogEntry<TMeta>[]) => void | Promise<void>;\n}\n\n// ─── Logger Options ──────────────────────────────────────────────\n\nexport interface LoggerOptions<TMeta = Record<string, unknown>> {\n /** Minimum log level (default: INFO) */\n level?: LogLevelName;\n /** Transports for log output */\n transports?: Transport<TMeta>[];\n /** Middleware pipeline */\n middleware?: LogMiddleware<TMeta>[];\n /** Alert rules */\n alerts?: AlertRule<TMeta>[];\n /** Default bound context for all log entries */\n context?: Record<string, unknown>;\n /** Field paths to auto-redact (e.g. ['idToken', 'password']) */\n redact?: string[];\n /**\n * When to include error stack traces:\n * - `true` — always include\n * - `false` — never include\n * - `LogLevelName` — include at this level and above\n * Default: 'ERROR'\n */\n includeStack?: boolean | LogLevelName;\n /**\n * Exchange log mode:\n * - `true` — exchange logs alongside normal logs\n * - `'only'` — only exchange-formatted logs\n * - `false` — disabled (default)\n */\n exchangeLog?: boolean | \"only\";\n /** Custom timestamp function (default: Date.now) */\n timestamp?: () => number;\n}\n\n// ─── Logger Interface ────────────────────────────────────────────\n\nexport interface Logger<TMeta = Record<string, unknown>> {\n trace(message: string, meta?: Partial<TMeta>): void;\n debug(message: string, meta?: Partial<TMeta>): void;\n info(message: string, meta?: Partial<TMeta>): void;\n warn(message: string, meta?: Partial<TMeta>): void;\n error(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void;\n fatal(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void;\n\n /** Create a child logger with additional bound context */\n child(context: Record<string, unknown>): Logger<TMeta>;\n\n /** Add a transport at runtime */\n addTransport(transport: Transport<TMeta>): void;\n /** Remove a transport by name */\n removeTransport(name: string): void;\n /** Add middleware at runtime */\n addMiddleware(middleware: LogMiddleware<TMeta>): void;\n\n /** Flush all transports */\n flush(): Promise<void>;\n /** Close all transports gracefully */\n close(): Promise<void>;\n}\n","/**\n * @module voltlog-io\n * @description Log level utilities — filtering, comparison, resolution.\n */\n\nimport { LogLevel, type LogLevelName, LogLevelNameMap } from \"./types.js\";\n\n/**\n * Resolve a level name string to its numeric value.\n * Case-insensitive. Returns INFO if unrecognized.\n */\nexport function resolveLevel(level: string | LogLevelName): number {\n const n = LogLevelNameMap[level.toLowerCase()];\n return n !== undefined ? n : LogLevel.INFO;\n}\n\n/**\n * Check if a log entry at `entryLevel` passes the `filterLevel`.\n */\nexport function shouldLog(entryLevel: number, filterLevel: number): boolean {\n return entryLevel >= filterLevel;\n}\n\n/**\n * Determine whether to include stack trace for a given entry level.\n */\nexport function shouldIncludeStack(\n entryLevel: number,\n includeStack: boolean | LogLevelName,\n): boolean {\n if (typeof includeStack === \"boolean\") return includeStack;\n return entryLevel >= resolveLevel(includeStack);\n}\n","/**\n * @module voltlog-io\n * @description Core Logger class — zero external dependencies (only cuid2), runtime-agnostic.\n *\n * @example Basic usage\n * ```ts\n * import { createLogger, consoleTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * level: 'INFO',\n * transports: [consoleTransport()],\n * });\n *\n * logger.info('Server started', { port: 9000 });\n * ```\n *\n * @example Child logger with bound context\n * ```ts\n * const cpLogger = logger.child({ chargePointId: 'CP-101' });\n * cpLogger.info('BootNotification received');\n * // → auto-includes context: { chargePointId: 'CP-101' }\n * ```\n *\n * @example Error with stack trace\n * ```ts\n * logger.error('Connection failed', new Error('ETIMEDOUT'));\n * logger.error('Handler crashed', { action: 'BootNotification' }, new Error('null ref'));\n * ```\n *\n * @example With ocpp-ws-io (if user has both packages)\n * ```ts\n * import { createLogger } from 'ocpp-ws-io/logger'; // re-export\n * ```\n */\n\nimport { createId } from \"@paralleldrive/cuid2\";\nimport { resolveLevel, shouldIncludeStack, shouldLog } from \"./levels.js\";\nimport { composeMiddleware, fanOutToTransports } from \"./pipeline.js\";\nimport type {\n LogEntry,\n LogError,\n Logger,\n LoggerOptions,\n LogLevelName,\n LogMiddleware,\n Transport,\n} from \"./types.js\";\n\n// ─── Logger Implementation ──────────────────────────────────────\n\nclass LoggerImpl<TMeta = Record<string, unknown>> implements Logger<TMeta> {\n private _level: number;\n private _transports: Transport<TMeta>[];\n private _middlewareList: LogMiddleware<TMeta>[];\n private _pipeline: (entry: LogEntry<TMeta>) => void;\n private _context: Record<string, unknown>;\n private _includeStack: boolean | LogLevelName;\n private _timestampFn: () => number;\n\n constructor(options: LoggerOptions<TMeta> = {}) {\n this._level = resolveLevel(options.level ?? \"INFO\");\n this._transports = [...(options.transports ?? [])];\n this._middlewareList = [...(options.middleware ?? [])];\n this._context = options.context ? { ...options.context } : {};\n this._includeStack = options.includeStack ?? \"ERROR\";\n this._timestampFn = options.timestamp ?? Date.now;\n this._pipeline = this._buildPipeline();\n }\n\n // ─── Log Methods ────────────────────────────────────────────\n\n trace(message: string, meta?: Partial<TMeta>): void {\n this._log(10, \"TRACE\", message, meta);\n }\n\n debug(message: string, meta?: Partial<TMeta>): void {\n this._log(20, \"DEBUG\", message, meta);\n }\n\n info(message: string, meta?: Partial<TMeta>): void {\n this._log(30, \"INFO\", message, meta);\n }\n\n warn(message: string, meta?: Partial<TMeta>): void {\n this._log(40, \"WARN\", message, meta);\n }\n\n error(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void {\n if (metaOrError instanceof Error) {\n this._log(50, \"ERROR\", message, undefined, metaOrError);\n } else {\n this._log(50, \"ERROR\", message, metaOrError, error);\n }\n }\n\n fatal(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void {\n if (metaOrError instanceof Error) {\n this._log(60, \"FATAL\", message, undefined, metaOrError);\n } else {\n this._log(60, \"FATAL\", message, metaOrError, error);\n }\n }\n\n // ─── Child Logger ───────────────────────────────────────────\n\n child(context: Record<string, unknown>): Logger<TMeta> {\n return new ChildLoggerImpl<TMeta>(this, { ...this._context, ...context });\n }\n\n // ─── Dynamic Configuration ─────────────────────────────────\n\n addTransport(transport: Transport<TMeta>): void {\n this._transports.push(transport);\n }\n\n removeTransport(name: string): void {\n this._transports = this._transports.filter((t) => t.name !== name);\n }\n\n addMiddleware(middleware: LogMiddleware<TMeta>): void {\n this._middlewareList.push(middleware);\n this._pipeline = this._buildPipeline();\n }\n\n // ─── Lifecycle ──────────────────────────────────────────────\n\n async flush(): Promise<void> {\n await Promise.all(this._transports.map((t) => t.flush?.()).filter(Boolean));\n }\n\n async close(): Promise<void> {\n await this.flush();\n await Promise.all(this._transports.map((t) => t.close?.()).filter(Boolean));\n }\n\n // ─── Internal ───────────────────────────────────────────────\n\n /** @internal */\n _log(\n level: number,\n levelName: string,\n message: string,\n meta?: Partial<TMeta>,\n error?: Error,\n ): void {\n this._logWithContext(level, levelName, message, this._context, meta, error);\n }\n\n /** @internal — used by child loggers to inject bound context */\n _logWithContext(\n level: number,\n levelName: string,\n message: string,\n context: Record<string, unknown>,\n meta?: Partial<TMeta>,\n error?: Error,\n ): void {\n if (!shouldLog(level, this._level)) return;\n\n const entry: LogEntry<TMeta> = {\n id: createId(),\n level,\n levelName: levelName as LogLevelName,\n message,\n timestamp: this._timestampFn(),\n meta: (meta ?? {}) as TMeta,\n context: Object.keys(context).length > 0 ? context : undefined,\n };\n\n if (error) {\n const logError: LogError = {\n message: error.message,\n name: error.name,\n code: (error as NodeJS.ErrnoException).code,\n };\n if (shouldIncludeStack(level, this._includeStack)) {\n logError.stack = error.stack;\n }\n entry.error = logError;\n }\n\n this._pipeline(entry);\n }\n\n private _buildPipeline(): (entry: LogEntry<TMeta>) => void {\n return composeMiddleware(this._middlewareList, (entry) => {\n fanOutToTransports(entry, this._transports, this._level);\n });\n }\n}\n\n// ─── Child Logger ────────────────────────────────────────────────\n\nclass ChildLoggerImpl<TMeta = Record<string, unknown>>\n implements Logger<TMeta>\n{\n constructor(\n private _parent: LoggerImpl<TMeta>,\n private _context: Record<string, unknown>,\n ) {}\n\n trace(message: string, meta?: Partial<TMeta>): void {\n this._parent._logWithContext(10, \"TRACE\", message, this._context, meta);\n }\n debug(message: string, meta?: Partial<TMeta>): void {\n this._parent._logWithContext(20, \"DEBUG\", message, this._context, meta);\n }\n info(message: string, meta?: Partial<TMeta>): void {\n this._parent._logWithContext(30, \"INFO\", message, this._context, meta);\n }\n warn(message: string, meta?: Partial<TMeta>): void {\n this._parent._logWithContext(40, \"WARN\", message, this._context, meta);\n }\n error(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void {\n if (metaOrError instanceof Error) {\n this._parent._logWithContext(\n 50,\n \"ERROR\",\n message,\n this._context,\n undefined,\n metaOrError,\n );\n } else {\n this._parent._logWithContext(\n 50,\n \"ERROR\",\n message,\n this._context,\n metaOrError,\n error,\n );\n }\n }\n fatal(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void {\n if (metaOrError instanceof Error) {\n this._parent._logWithContext(\n 60,\n \"FATAL\",\n message,\n this._context,\n undefined,\n metaOrError,\n );\n } else {\n this._parent._logWithContext(\n 60,\n \"FATAL\",\n message,\n this._context,\n metaOrError,\n error,\n );\n }\n }\n\n child(context: Record<string, unknown>): Logger<TMeta> {\n return new ChildLoggerImpl<TMeta>(this._parent, {\n ...this._context,\n ...context,\n });\n }\n\n addTransport(transport: Transport<TMeta>): void {\n this._parent.addTransport(transport);\n }\n removeTransport(name: string): void {\n this._parent.removeTransport(name);\n }\n addMiddleware(middleware: LogMiddleware<TMeta>): void {\n this._parent.addMiddleware(middleware);\n }\n flush(): Promise<void> {\n return this._parent.flush();\n }\n close(): Promise<void> {\n return this._parent.close();\n }\n}\n\n// ─── Factory ─────────────────────────────────────────────────────\n\n/**\n * Create a new logger instance.\n *\n * @example Minimal\n * ```ts\n * const logger = createLogger();\n * logger.info('Hello');\n * ```\n *\n * @example Full options\n * ```ts\n * import { createLogger, consoleTransport, prettyTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * level: 'DEBUG',\n * transports: [prettyTransport()],\n * redact: ['password', 'idToken'],\n * includeStack: 'ERROR',\n * });\n * ```\n *\n * @example OCPP-aware with child loggers\n * ```ts\n * import { createLogger, prettyTransport } from 'voltlog-io';\n * import type { OcppExchangeMeta } from 'voltlog-io';\n *\n * const logger = createLogger<OcppExchangeMeta>({\n * level: 'INFO',\n * transports: [prettyTransport()],\n * });\n *\n * // Per-connection child logger\n * const cpLog = logger.child({ chargePointId: 'CP-101' });\n * cpLog.info('OCPP message', {\n * messageType: 'CALL',\n * action: 'BootNotification',\n * direction: 'IN',\n * });\n * ```\n */\nexport function createLogger<TMeta = Record<string, unknown>>(\n options?: LoggerOptions<TMeta>,\n): Logger<TMeta> {\n return new LoggerImpl<TMeta>(options);\n}\n","/**\n * @module voltlog-io\n * @description Pipeline — middleware composition and transformer fan-out.\n */\n\nimport { resolveLevel, shouldLog } from \"./levels.js\";\nimport type { LogEntry, LogMiddleware, Transport } from \"./types.js\";\n\n/**\n * Build a composed middleware function from an array of middleware.\n * Each middleware calls `next()` to pass the entry forward.\n * Omit `next()` to drop the entry (e.g. sampling).\n */\nexport function composeMiddleware<TMeta = Record<string, unknown>>(\n middleware: LogMiddleware<TMeta>[],\n final: (entry: LogEntry<TMeta>) => void,\n): (entry: LogEntry<TMeta>) => void {\n if (middleware.length === 0) return final;\n\n return (entry: LogEntry<TMeta>) => {\n let index = 0;\n\n const next = (e: LogEntry<TMeta>): void => {\n if (index < middleware.length) {\n // biome-ignore lint/style/noNonNullAssertion: Guarded by length check\n const mw = middleware[index++]!;\n mw(e, next);\n } else {\n final(e);\n }\n };\n\n next(entry);\n };\n}\n\n/**\n * Fan out a log entry to all transports, respecting per-transport level filters.\n * All transports run concurrently and fire-and-forget for non-blocking logging.\n */\nexport function fanOutToTransports<TMeta = Record<string, unknown>>(\n entry: LogEntry<TMeta>,\n transports: Transport<TMeta>[],\n loggerLevel: number,\n): void {\n for (const t of transports) {\n const tLevel = t.level ? resolveLevel(t.level) : loggerLevel;\n\n if (!shouldLog(entry.level, tLevel)) continue;\n\n try {\n const result = t.write(entry);\n if (result && typeof (result as Promise<void>).catch === \"function\") {\n (result as Promise<void>).catch(() => {\n /* Swallow transport errors to avoid crashing the host app */\n });\n }\n } catch {\n /* Sync transport errors swallowed */\n }\n }\n}\n","/**\n * @module voltlog-io\n * @description AI Enrichment middleware — enriches logs with AI analysis (e.g. error explanation).\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Requires an API Key. Using this in the browser will expose your key to the client.\n * > Recommended for server-side use only.\n */\n\nimport { resolveLevel } from \"../core/levels.js\";\nimport type { LogEntry, LogLevelName, LogMiddleware } from \"../core/types.js\";\n\nexport interface AiEnrichmentOptions {\n /**\n * Only trigger for logs at or above this level.\n * Default: ERROR\n */\n level?: LogLevelName;\n\n /**\n * Field name to store the analysis result in `entry.meta`.\n * Default: 'ai_analysis'\n */\n targetField?: string;\n\n /**\n * Custom analyzer function.\n * Return a string (analysis) or object (structured data) to attach to `meta[targetField]`.\n */\n analyzer: (\n entry: LogEntry,\n ) => Promise<string | Record<string, unknown> | null>;\n\n /**\n * Timeout in milliseconds for the AI call.\n * Default: 2000ms (fail fast to avoid blocking for too long)\n */\n timeout?: number;\n\n /**\n * If true, errors in the analyzer are swallowed (logged to console.error but don't crash).\n * Default: true\n */\n swallowErrors?: boolean;\n}\n\n/**\n * Enriches log entries by calling an asynchronous AI analyzer.\n * Appends result to `entry.meta[targetField]`.\n *\n * @example\n * ```ts\n * const ai = aiEnrichmentMiddleware({\n * analyzer: createOpenAiErrorAnalyzer(process.env.OPENAI_API_KEY!),\n * level: \"ERROR\",\n * targetField: \"error_explanation\"\n * });\n * ```\n */\nexport function aiEnrichmentMiddleware<TMeta = Record<string, unknown>>(\n options: AiEnrichmentOptions,\n): LogMiddleware<TMeta> {\n const minLevel = resolveLevel(options.level ?? \"ERROR\");\n const timeoutMs = options.timeout ?? 2000;\n const swallow = options.swallowErrors ?? true;\n const fieldName = options.targetField ?? \"ai_analysis\";\n\n return async (entry, next) => {\n // 1. Check level\n if (entry.level < minLevel) {\n next(entry);\n return;\n }\n\n try {\n // 2. Call analyzer with timeout\n const analysisPromise = options.analyzer(entry as unknown as LogEntry);\n const timeoutPromise = new Promise<null>((_, reject) =>\n setTimeout(() => reject(new Error(\"AI Analysis Timeout\")), timeoutMs),\n );\n\n const result = await Promise.race([analysisPromise, timeoutPromise]);\n\n if (result) {\n entry.meta = {\n ...entry.meta,\n [fieldName]: result,\n };\n }\n } catch (err) {\n if (!swallow) {\n throw err;\n }\n // Otherwise ignore\n }\n\n next(entry);\n };\n}\n\n/**\n * Helper to create an OpenAI-compatible analyzer specifically for Error explanation.\n */\nexport function createOpenAiErrorAnalyzer(\n apiKey: string,\n model = \"gpt-3.5-turbo\",\n systemPrompt = \"You are a log analyzer. Explain this error briefly and suggest a fix in 1 sentence.\",\n): (entry: LogEntry) => Promise<string | null> {\n return async (entry) => {\n try {\n const response = await fetch(\n \"https://api.openai.com/v1/chat/completions\",\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n messages: [\n { role: \"system\", content: systemPrompt },\n {\n role: \"user\",\n content: `Error Message: ${\n entry.message\n }\\nContext: ${JSON.stringify(entry.meta)}`,\n },\n ],\n max_tokens: 150,\n }),\n },\n );\n\n if (!response.ok) return null;\n // biome-ignore lint/suspicious/noExplicitAny: Third-party API response, partial typing is sufficient\n const data = (await response.json()) as any;\n return data.choices?.[0]?.message?.content ?? null;\n } catch {\n return null;\n }\n };\n}\n","/**\n * @module voltlog-io\n * @description Alert middleware — checks logs against rules and triggers alerts.\n * @universal Works in all environments.\n * @example\n * ```ts\n * import { createLogger, consoleTransport, alertMiddleware } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [consoleTransport()],\n * middleware: [\n * alertMiddleware([\n * {\n * name: 'error-spike',\n * when: (entry) => entry.level >= 50, // ERROR+\n * threshold: 10,\n * windowMs: 60_000,\n * cooldownMs: 300_000,\n * onAlert: async (entries) => {\n * await sendEmail({ subject: `${entries.length} errors in 1 min` });\n * },\n * },\n * {\n * name: 'callerror-alert',\n * when: (entry) => entry.meta.messageType === 'CALLERROR',\n * threshold: 5,\n * windowMs: 60_000,\n * onAlert: (entries) => sendSlackNotification(entries),\n * },\n * ]),\n * ],\n * });\n * ```\n */\n\nimport type { AlertRule, LogEntry, LogMiddleware } from \"../core/types.js\";\n\ninterface AlertState<TMeta> {\n entries: LogEntry<TMeta>[];\n lastFired: number;\n}\n\n/**\n * Create an alert middleware that evaluates rules against every log entry.\n *\n * Alerts are non-blocking — `onAlert` failures are silently caught\n * to never interfere with the logging pipeline.\n */\nexport function alertMiddleware<TMeta = Record<string, unknown>>(\n rules: AlertRule<TMeta>[],\n): LogMiddleware<TMeta> {\n const states = new Map<string, AlertState<TMeta>>();\n\n // Initialize state for each rule\n for (const rule of rules) {\n states.set(rule.name, { entries: [], lastFired: -Infinity });\n }\n\n return (entry: LogEntry<TMeta>, next) => {\n const now = entry.timestamp;\n\n for (const rule of rules) {\n if (!rule.when(entry)) continue;\n\n // biome-ignore lint/style/noNonNullAssertion: Initialized in constructor\n const state = states.get(rule.name)!;\n const windowMs = rule.windowMs ?? Infinity;\n const threshold = rule.threshold ?? 1;\n const cooldownMs = rule.cooldownMs ?? 0;\n\n // Trim entries outside the window\n if (Number.isFinite(windowMs)) {\n state.entries = state.entries.filter(\n (e) => now - e.timestamp < windowMs,\n );\n }\n\n state.entries.push(entry);\n\n // Check if threshold is met and cooldown has passed\n if (\n state.entries.length >= threshold &&\n now - state.lastFired >= cooldownMs\n ) {\n const alertEntries = [...state.entries];\n state.entries = [];\n state.lastFired = now;\n\n // Fire alert asynchronously — never block the pipeline\n try {\n const result = rule.onAlert(alertEntries);\n if (result && typeof (result as Promise<void>).catch === \"function\") {\n (result as Promise<void>).catch(() => {\n /* Alert callback errors swallowed */\n });\n }\n } catch {\n /* Sync alert errors swallowed */\n }\n }\n }\n\n // Always pass through — alerts don't drop entries\n next(entry);\n };\n}\n","/**\n * @module voltlog-io\n * @description Correlation ID middleware — adds tracing IDs to logs.\n * @universal Works in all environments.\n * Useful for tracking requests across microservices.\n */\n\nimport { createId } from \"@paralleldrive/cuid2\";\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface CorrelationIdOptions {\n /**\n * Header name or meta key to check for existing ID.\n * Default: 'x-correlation-id' (and checks 'traceId', 'correlationId')\n */\n header?: string;\n /**\n * Function to generate new IDs.\n * Default: cuid2\n */\n generator?: () => string;\n}\n\n/**\n * Middleware that ensures every log entry has a correlation ID.\n * checks:\n * 1. entry.correlationId\n * 2. entry.meta.correlationId\n * 3. entry.meta.traceId\n * 4. entry.meta[header]\n *\n * If none found, generates a new one.\n */\nexport function correlationIdMiddleware<TMeta = Record<string, unknown>>(\n options: CorrelationIdOptions = {},\n): LogMiddleware<TMeta> {\n const header = options.header ?? \"x-correlation-id\";\n const generate = options.generator ?? createId;\n\n return (entry, next) => {\n // 1. Check direct property\n if (entry.correlationId) {\n return next(entry);\n }\n\n // 2. Check meta\n const meta = entry.meta as Record<string, unknown>;\n let id =\n (meta.correlationId as string) ||\n (meta.traceId as string) ||\n (meta[header] as string);\n\n // 3. Generate if missing\n if (!id) {\n id = generate();\n }\n\n // 4. Assign\n entry.correlationId = id;\n\n // Also ensure it's in meta for visibility if desired (optional, but good for transports that only dump meta)\n // We won't overwrite existing keys to avoid side effects, but we can standardize on correlationId\n if (!meta.correlationId) {\n // cast to allows assignment\n (entry.meta as Record<string, unknown>).correlationId = id;\n }\n\n next(entry);\n };\n}\n","import type { LogMiddleware } from \"../core/types.js\";\n\n/**\n * Helper to create a type-safe middleware function.\n *\n * @example\n * const myMiddleware = createMiddleware((entry, next) => {\n * entry.meta.foo = 'bar';\n * next(entry);\n * });\n */\nexport function createMiddleware<TMeta = Record<string, unknown>>(\n fn: LogMiddleware<TMeta>,\n): LogMiddleware<TMeta> {\n return fn;\n}\n","/**\n * @module voltlog-io\n * @description Deduplication middleware — groups identical logs in a time window.\n * @universal Works in all environments.\n */\n\nimport type { LogEntry, LogMiddleware } from \"../core/types.js\";\n\nexport interface DeduplicationOptions<TMeta = Record<string, unknown>> {\n /**\n * Time window in ms to group logs.\n * Logs matching the key within this window will be aggregated.\n * Default: 1000ms\n */\n windowMs?: number;\n /**\n * Function to generate a unique key for deduplication.\n * Default: uses `entry.message` + `entry.level`\n */\n keyFn?: (entry: LogEntry<TMeta>) => string;\n}\n\ninterface DedupState<TMeta> {\n entry: LogEntry<TMeta>;\n count: number;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Deduplication middleware.\n * Buffers logs for `windowMs`. If identical logs arrive, increments a counter.\n * Emits the log once at the end of the window with `meta.duplicateCount`.\n */\nexport function deduplicationMiddleware<TMeta = Record<string, unknown>>(\n options: DeduplicationOptions<TMeta> = {},\n): LogMiddleware<TMeta> {\n const windowMs = options.windowMs ?? 1000;\n const keyFn =\n options.keyFn ??\n ((e) => `${e.level}:${e.message}:${e.error?.message ?? \"\"}`);\n\n const buffer = new Map<string, DedupState<TMeta>>();\n\n return (entry, next) => {\n const key = keyFn(entry);\n\n if (buffer.has(key)) {\n // Duplicate found\n // biome-ignore lint/style/noNonNullAssertion: Guarded by buffer.has check\n const state = buffer.get(key)!;\n state.count++;\n return; // Drop this instance\n }\n\n // New entry\n const timer = setTimeout(() => {\n const state = buffer.get(key);\n if (state) {\n buffer.delete(key);\n if (state.count > 1) {\n state.entry.meta = {\n ...state.entry.meta,\n duplicateCount: state.count,\n };\n }\n next(state.entry);\n }\n }, windowMs);\n\n buffer.set(key, {\n entry,\n count: 1,\n timer,\n });\n };\n}\n","/**\n * @module voltlog-io\n * @description Heap usage middleware — adds memory stats to logs.\n * @universal Works in all environments, but only adds data in Node.js/Bun/Deno.\n * Checks for `process.memoryUsage` presence before execution.\n */\n\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface HeapUsageOptions {\n /**\n * Field name to store memory stats in `entry.meta`.\n * Default: 'memory'\n */\n fieldName?: string;\n}\n\n/**\n * Adds `rss`, `heapTotal`, and `heapUsed` to log metadata.\n * Only works in environments where `process.memoryUsage` is available (Node.js/Bun/Deno).\n */\nexport function heapUsageMiddleware<TMeta = Record<string, unknown>>(\n options: HeapUsageOptions = {},\n): LogMiddleware<TMeta> {\n const fieldName = options.fieldName ?? \"memory\";\n\n return (entry, next) => {\n if (typeof process !== \"undefined\" && process.memoryUsage) {\n const memory = process.memoryUsage();\n entry.meta = {\n ...entry.meta,\n [fieldName]: {\n rss: memory.rss,\n heapTotal: memory.heapTotal,\n heapUsed: memory.heapUsed,\n },\n };\n }\n next(entry);\n };\n}\n","/**\n * @module voltlog-io\n * @description IP middleware — extracts client IP from headers (x-forwarded-for, etc.).\n * @universal Works in any environment where headers are available in metadata.\n */\n\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface IpMiddlewareOptions {\n /**\n * Field name to store the extracted IP in `entry.meta`.\n * Default: 'ip'\n */\n fieldName?: string;\n\n /**\n * Custom list of headers/keys to check for IP address.\n * Default: ['x-forwarded-for', 'x-real-ip', 'req.ip', 'ip']\n */\n headerKeys?: string[];\n}\n\n/**\n * Extracts IP address from commonly used headers in `entry.meta`.\n */\nexport function ipMiddleware<TMeta = Record<string, unknown>>(\n options: IpMiddlewareOptions = {},\n): LogMiddleware<TMeta> {\n const targetField = options.fieldName ?? \"ip\";\n const keysToCheck = options.headerKeys ?? [\n \"x-forwarded-for\",\n \"x-real-ip\",\n \"req.ip\",\n \"ip\",\n \"x-client-ip\",\n ];\n\n return (entry, next) => {\n const meta = entry.meta as Record<string, unknown>;\n const headers = (meta.headers || {}) as Record<string, unknown>;\n const req = (meta.req || {}) as Record<string, unknown>; // Express/Fastify request object often in meta.req\n\n let foundIp: string | undefined;\n\n // Check meta root, meta.headers, meta.req in order for each key\n for (const key of keysToCheck) {\n // 1. Check meta root\n if (typeof meta[key] === \"string\") {\n foundIp = meta[key] as string;\n break;\n }\n // 2. Check headers\n if (typeof headers[key] === \"string\") {\n foundIp = headers[key] as string;\n break;\n }\n // 3. Check req object properties (e.g. req.ip)\n if (typeof req[key] === \"string\") {\n foundIp = req[key] as string;\n break;\n }\n // Special case for 'req.ip' dotted notation if key is 'req.ip'\n if (key === \"req.ip\" && typeof req.ip === \"string\") {\n foundIp = req.ip as string;\n break;\n }\n }\n\n if (foundIp) {\n // Handle comma-separated X-Forwarded-For (take first)\n const firstIp =\n typeof foundIp === \"string\"\n ? foundIp.split(\",\")[0].trim()\n : String(foundIp);\n\n entry.meta = {\n ...entry.meta,\n [targetField]: firstIp,\n };\n }\n\n next(entry);\n };\n}\n","/**\n * @module voltlog-io\n * @description Level Override middleware — allows forcing a log level for specific requests.\n * @universal Works in all environments.\n * Useful for debugging specific users/requests in production without changing global config.\n */\n\nimport type { LogMiddleware } from \"../core/types.js\";\nimport { LogLevel, type LogLevelName } from \"../core/types.js\";\n\nexport interface LevelOverrideOptions {\n /**\n * Header name or meta key to trigger override.\n * Default: 'x-log-level'\n */\n key?: string;\n /**\n * If true, removes the trigger key from metadata before logging.\n * Default: true\n */\n cleanup?: boolean;\n}\n\n/**\n * Dynamically changes the log entry level if a specific key is found in meta/context.\n * E.g. passing `x-log-level: DEBUG` allows a specific request to bypass INFO filters.\n */\nexport function levelOverrideMiddleware<TMeta = Record<string, unknown>>(\n options: LevelOverrideOptions = {},\n): LogMiddleware<TMeta> {\n const key = options.key ?? \"x-log-level\";\n const cleanup = options.cleanup ?? true;\n\n return (entry, next) => {\n const meta = entry.meta as Record<string, unknown>;\n const context = entry.context as Record<string, unknown> | undefined;\n\n // Check meta, then context, then headers in meta\n const levelName =\n (meta[key] as string) ||\n (context?.[key] as string) ||\n ((meta.headers as Record<string, unknown>)?.[key] as string);\n\n if (levelName && typeof levelName === \"string\") {\n const upperName = levelName.toUpperCase() as LogLevelName;\n if (LogLevel[upperName]) {\n // Upgrade the entry level so it passes transport filters\n // NOTE: This modifies the entry itself, which affects all transports\n entry.level = LogLevel[upperName];\n entry.levelName = upperName;\n\n if (cleanup) {\n delete meta[key];\n if (meta.headers) {\n delete (meta.headers as Record<string, unknown>)[key];\n }\n }\n }\n }\n\n next(entry);\n };\n}\n","/**\n * @module voltlog-io\n * @description OCPP middleware — masks sensitive fields and calculates latency for OCPP messages.\n * @universal Works in all environments.\n *\n * @example\n * ```ts\n * import { createLogger, consoleTransport, ocppMiddleware } from 'voltlog-io';\n * import type { OcppExchangeMeta } from 'voltlog-io';\n *\n * const logger = createLogger<OcppExchangeMeta>({\n * transports: [consoleTransport()],\n * middleware: [ocppMiddleware()],\n * });\n *\n * // The middleware auto-computes payloadSize and adds correlationId to the entry\n * logger.info('Message received', {\n * action: 'BootNotification',\n * messageType: 'CALL',\n * direction: 'IN',\n * });\n * ```\n */\n\nimport type {\n LogEntry,\n LogMiddleware,\n OcppExchangeMeta,\n} from \"../core/types.js\";\n\nexport interface OcppMiddlewareOptions {\n /**\n * Automatically compute payload size from meta if not set.\n * Default: true\n */\n autoPayloadSize?: boolean;\n /**\n * Propagate correlationId from meta to entry.correlationId.\n * Default: true\n */\n propagateCorrelationId?: boolean;\n}\n\n/**\n * Create an OCPP enrichment middleware.\n * Enriches log entries with computed OCPP metadata.\n */\nexport function ocppMiddleware(\n options: OcppMiddlewareOptions = {},\n): LogMiddleware<OcppExchangeMeta> {\n const autoPayloadSize = options.autoPayloadSize ?? true;\n const propagateCorrelationId = options.propagateCorrelationId ?? true;\n\n return (entry: LogEntry<OcppExchangeMeta>, next) => {\n const enriched = { ...entry, meta: { ...entry.meta } };\n\n // Auto-compute payload size\n if (\n autoPayloadSize &&\n enriched.meta.payloadSize === undefined &&\n enriched.meta.action\n ) {\n try {\n enriched.meta.payloadSize = JSON.stringify(enriched.meta).length;\n } catch {\n // Failed to compute — leave undefined\n }\n }\n\n // Propagate correlation ID from OCPP meta to entry-level\n if (\n propagateCorrelationId &&\n enriched.meta.correlationId &&\n !enriched.correlationId\n ) {\n enriched.correlationId = enriched.meta.correlationId;\n }\n\n next(enriched);\n };\n}\n","/**\n * @module voltlog-io\n * @description Redaction middleware — masks sensitive data in log entries.\n * @universal Works in all environments.\n *\n * @example\n * ```ts\n * import { createLogger, consoleTransport, redactionMiddleware } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [consoleTransport()],\n * middleware: [redactionMiddleware({ paths: ['password', 'idToken', 'authorization'] })],\n * });\n *\n * logger.info('Auth attempt', { password: 's3cret', user: 'admin' });\n * // → { password: '[REDACTED]', user: 'admin' }\n * ```\n */\n\nimport type { LogEntry, LogMiddleware } from \"../core/types.js\";\n\nconst DEFAULT_REDACT_VALUE = \"[REDACTED]\";\n\nexport interface RedactionOptions {\n /** Field paths to redact (case-insensitive matching) */\n paths: string[];\n /** Replacement value (default: '[REDACTED]') */\n replacement?: string;\n /** Also redact matching keys in nested objects (default: true) */\n deep?: boolean;\n}\n\n/**\n * Create a redaction middleware that replaces sensitive field values.\n */\nexport function redactionMiddleware<TMeta = Record<string, unknown>>(\n options: RedactionOptions,\n): LogMiddleware<TMeta> {\n const paths = new Set(options.paths.map((p) => p.toLowerCase()));\n const replacement = options.replacement ?? DEFAULT_REDACT_VALUE;\n const deep = options.deep ?? true;\n\n function redactObject(obj: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (paths.has(key.toLowerCase())) {\n result[key] = replacement;\n } else if (\n deep &&\n value !== null &&\n typeof value === \"object\" &&\n !Array.isArray(value)\n ) {\n result[key] = redactObject(value as Record<string, unknown>);\n } else {\n result[key] = value;\n }\n }\n return result;\n }\n\n return (entry: LogEntry<TMeta>, next) => {\n const redacted = { ...entry };\n\n if (entry.meta && typeof entry.meta === \"object\") {\n redacted.meta = redactObject(\n entry.meta as Record<string, unknown>,\n ) as TMeta;\n }\n\n if (entry.context && typeof entry.context === \"object\") {\n redacted.context = redactObject(entry.context);\n }\n\n next(redacted);\n };\n}\n","/**\n * @module voltlog-io\n * @description Sampling middleware — rate limits or probabilistically samples logs.\n * @universal Works in all environments.\n * @example\n * ```ts\n * import { createLogger, consoleTransport, samplingMiddleware } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [consoleTransport()],\n * middleware: [\n * samplingMiddleware({\n * keyFn: (entry) => `${entry.meta.action}:${entry.meta.chargePointId}`,\n * maxPerWindow: 10,\n * windowMs: 60_000, // 10 logs per minute per action+CP combo\n * }),\n * ],\n * });\n * ```\n */\n\nimport type { LogEntry, LogMiddleware } from \"../core/types.js\";\n\nexport interface SamplingOptions<TMeta = Record<string, unknown>> {\n /**\n * Function to extract a sampling key from a log entry.\n * Entries with the same key share a rate limit.\n * Default: uses `entry.message`\n */\n keyFn?: (entry: LogEntry<TMeta>) => string;\n /** Maximum entries allowed per key per window (default: 100) */\n maxPerWindow?: number;\n /** Time window in ms (default: 60000 = 1 minute) */\n windowMs?: number;\n /** Probability to keep a log (0 to 1). Default: 1 (keep all) */\n sampleRate?: number;\n /** Logs at this level or higher always pass. Default: 40 (WARN) */\n priorityLevel?: number;\n}\n\ninterface BucketEntry {\n count: number;\n windowStart: number;\n}\n\n/**\n * Create a sampling middleware that drops logs exceeding the rate limit.\n */\nexport function samplingMiddleware<TMeta = Record<string, unknown>>(\n options: SamplingOptions<TMeta> = {},\n): LogMiddleware<TMeta> {\n const keyFn = options.keyFn ?? ((entry: LogEntry<TMeta>) => entry.message);\n const maxPerWindow = options.maxPerWindow ?? 100;\n const windowMs = options.windowMs ?? 60_000;\n const sampleRate = options.sampleRate ?? 1;\n // Default priority to WARN (40) so INFO (30) gets sampled/limited\n const priorityLevel = options.priorityLevel ?? 40;\n\n const buckets = new Map<string, BucketEntry>();\n\n return (entry: LogEntry<TMeta>, next) => {\n // 1. High priority logs always pass\n if (entry.level >= priorityLevel) {\n return next(entry);\n }\n\n // 2. Probabilistic Sampling\n if (sampleRate < 1 && Math.random() > sampleRate) {\n return; // Dropped\n }\n\n // 3. Rate Limiting\n const key = keyFn(entry);\n const now = entry.timestamp;\n\n let bucket = buckets.get(key);\n if (!bucket || now - bucket.windowStart >= windowMs) {\n bucket = { count: 0, windowStart: now };\n buckets.set(key, bucket);\n }\n\n if (bucket.count < maxPerWindow) {\n bucket.count++;\n next(entry);\n }\n // else: Dropped (rate limited)\n\n // Periodic cleanup (simple heuristic)\n if (buckets.size > 2000) {\n // ... cleanup logic ...\n const expireBefore = now - windowMs * 2;\n for (const [k, b] of buckets) {\n if (b.windowStart < expireBefore) {\n buckets.delete(k);\n }\n }\n }\n };\n}\n","/**\n * @module voltlog-io\n * @description User Agent middleware — parses UA strings into browser/os/device info.\n * @universal Works in all environments (Server/Browser).\n * Lightweight regex-based parsing to avoid heavy dependencies.\n */\n\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface UserAgentOptions {\n /**\n * Field to look for user agent string (source).\n * Default checks `entry.meta.userAgent`, `entry.meta['user-agent']`, or context.\n */\n sourceField?: string;\n\n /**\n * Field to store the parsed info (target).\n * Default: 'client'\n */\n targetField?: string;\n}\n\n/**\n * Parses user-agent string into structured data (browser, os).\n */\nexport function userAgentMiddleware<TMeta = Record<string, unknown>>(\n options: UserAgentOptions = {},\n): LogMiddleware<TMeta> {\n const sourceField = options.sourceField;\n const targetField = options.targetField ?? \"client\";\n\n return (entry, next) => {\n const meta = entry.meta as Record<string, unknown>;\n const context = entry.context as Record<string, unknown> | undefined;\n\n // Find UA string\n const ua =\n (sourceField ? (meta[sourceField] as string) : undefined) ||\n (meta.userAgent as string) ||\n (meta[\"user-agent\"] as string) ||\n (context?.userAgent as string) ||\n (context?.[\"user-agent\"] as string);\n\n if (ua) {\n const info = parseUserAgent(ua);\n entry.meta = {\n ...entry.meta,\n [targetField]: info,\n };\n }\n\n next(entry);\n };\n}\n\n// Simple lightweight parser to avoid deps\nfunction parseUserAgent(ua: string) {\n const browser =\n /(opera|chrome|safari|firefox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i.exec(ua) ||\n [];\n let name = browser[1] ? browser[1].toLowerCase() : \"unknown\";\n let version = browser[2] || \"unknown\";\n\n if (/trident/i.test(name)) {\n name = \"ie\";\n } else if (name === \"chrome\") {\n const edge = /edg(e)?\\/(\\d+)/i.exec(ua);\n if (edge) {\n name = \"edge\";\n version = edge[2];\n }\n }\n\n const osResult =\n /(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.exec(\n ua,\n );\n const os = osResult ? osResult[0].toLowerCase() : \"desktop\";\n\n return { browser: name, version, os };\n}\n","/**\n * @module voltlog-io\n * @description Batch transformer — wraps another transformer and buffers logs.\n * @universal Works in all environments.\n *\n * @example\n * ```ts\n * import { createLogger, batchTransport, consoleTransport } from 'voltlog-io';\n *\n * // Batch console output: flush every 100 entries or every 2 seconds\n * const logger = createLogger({\n * transports: [\n * batchTransport(consoleTransport(), { batchSize: 100, flushIntervalMs: 2000 }),\n * ],\n * });\n * ```\n */\n\nimport type { LogEntry, Transport } from \"../core/types.js\";\n\nexport interface BatchTransportOptions {\n /** Number of entries to buffer before flushing (default: 100) */\n batchSize?: number;\n /** Max time in ms to wait before flushing a partial batch (default: 5000) */\n flushIntervalMs?: number;\n}\n\n/**\n * Wrap any transport with batching. Entries are buffered and flushed\n * either when the batch is full or the timer fires.\n */\nexport function batchTransport(\n inner: Transport,\n options: BatchTransportOptions = {},\n): Transport {\n const batchSize = options.batchSize ?? 100;\n const flushIntervalMs = options.flushIntervalMs ?? 5000;\n\n let buffer: LogEntry[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleFlush(): void {\n if (flushTimer) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n doFlush();\n }, flushIntervalMs);\n }\n\n function doFlush(): void {\n if (buffer.length === 0) return;\n const batch = buffer;\n buffer = [];\n for (const entry of batch) {\n try {\n const result = inner.write(entry);\n if (result && typeof (result as Promise<void>).catch === \"function\") {\n (result as Promise<void>).catch(() => {});\n }\n } catch {\n /* Swallowed */\n }\n }\n }\n\n return {\n name: `batch(${inner.name})`,\n level: inner.level,\n write(entry: LogEntry): void {\n buffer.push(entry);\n if (buffer.length >= batchSize) {\n doFlush();\n } else {\n scheduleFlush();\n }\n },\n async flush(): Promise<void> {\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n doFlush();\n await inner.flush?.();\n },\n async close(): Promise<void> {\n await this.flush?.();\n await inner.close?.();\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Browser stream transformer — writes newline-delimited JSON to a WHATWG WritableStream.\n * @browser-only Depends on WHATWG Streams API (typical in Browsers/Edge).\n * Useful for streaming logs in browser environments (e.g. to `fetch` streams or ServiceWorkers).\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface BrowserJsonStreamTransportOptions {\n /**\n * Writable stream to output to.\n * Must be a standard WHATWG WritableStream (available in modern browsers).\n */\n stream: WritableStream<string>;\n /** Per-transport level filter */\n level?: LogLevelName;\n /**\n * Custom serializer. Return the string to write.\n * Default: JSON.stringify(entry) + '\\n'\n */\n serializer?: (entry: LogEntry) => string;\n}\n\n/**\n * Create a JSON stream transport for browsers that writes to a WritableStream.\n */\nexport function browserJsonStreamTransport(\n options: BrowserJsonStreamTransportOptions,\n): Transport {\n const stream = options.stream;\n const writer = stream.getWriter();\n\n const serialize =\n options.serializer ?? ((entry: LogEntry) => `${JSON.stringify(entry)}\\n`);\n\n return {\n name: \"browser-stream\",\n level: options.level,\n async write(entry: LogEntry): Promise<void> {\n try {\n const data = serialize(entry);\n await writer.ready;\n await writer.write(data);\n } catch (err) {\n console.error(\"[voltlog] Failed to write to browser stream\", err);\n }\n },\n async close(): Promise<void> {\n try {\n await writer.close();\n } catch (_err) {\n // Ignore close errors\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Console transformer — writes to `console.log`, `console.error`, etc.\n * @universal Works in all environments. Works in Node.js, browsers, and all runtimes.\n *\n * @example\n * ```ts\n * import { createLogger, consoleTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [consoleTransport()],\n * });\n * ```\n */\n\nimport {\n type LogEntry,\n LogLevel,\n type LogLevelName,\n type Transport,\n} from \"../core/types.js\";\n\nexport interface ConsoleTransportOptions {\n /** Per-transport level filter */\n level?: LogLevelName;\n /**\n * Use appropriate console method per level (console.warn, console.error, etc.).\n * Default: true\n */\n useConsoleLevels?: boolean;\n /**\n * Format the entry before outputting. Return any value that console.log can handle.\n * Default: serializes to JSON string.\n */\n formatter?: (entry: LogEntry) => unknown;\n}\n\n/**\n * Create a console transport. Outputs structured JSON to the console.\n */\nexport function consoleTransport(\n options: ConsoleTransportOptions = {},\n): Transport {\n const useConsoleLevels = options.useConsoleLevels ?? true;\n const formatter =\n options.formatter ?? ((entry: LogEntry) => JSON.stringify(entry));\n\n return {\n name: \"console\",\n level: options.level,\n write(entry: LogEntry): void {\n const output = formatter(entry);\n\n if (!useConsoleLevels) {\n console.log(output);\n return;\n }\n\n if (entry.level >= LogLevel.FATAL) {\n console.error(output);\n } else if (entry.level >= LogLevel.ERROR) {\n console.error(output);\n } else if (entry.level >= LogLevel.WARN) {\n console.warn(output);\n } else if (entry.level >= LogLevel.INFO) {\n console.info(output);\n } else if (entry.level >= LogLevel.DEBUG) {\n console.debug(output);\n } else {\n console.log(output);\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Helper to create custom transports easily.\n * @universal Works in all environments.\n */\n\nimport type { LogEntry, Transport } from \"../core/types.js\";\n\n/**\n * Helper to build a transport without manually defining the object structure.\n *\n * @example\n * const myTransport = createTransport('my-api', async (entry) => {\n * await fetch('https://api.example.com/logs', { body: JSON.stringify(entry) });\n * });\n *\n * @param name - Unique name for the transport\n * @param write - Function to process log entries\n * @param options - Optional overrides (level, flush, close)\n */\nexport function createTransport(\n name: string,\n write: (entry: LogEntry) => void | Promise<void>,\n options: Partial<Omit<Transport, \"name\" | \"write\">> = {},\n): Transport {\n return {\n name,\n write,\n ...options,\n };\n}\n","/**\n * @module voltlog-io\n * @description Datadog transformer — sends logs directly to Datadog Intake API.\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Using this in the browser will expose your API Key.\n * > Recommended for server-side use only.\n */\n\nimport type { LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface DatadogTransportOptions {\n /** Datadog API Key */\n apiKey: string;\n /** Datadog Site (default: datadoghq.com) */\n site?: string; // e.g., datadoghq.eu\n /** Service name tag */\n service?: string;\n /** Source tag (default: nodejs) */\n ddSource?: string;\n /** Hostname override */\n hostname?: string;\n /** Tags (comma separated: env:prod,version:1.0) */\n tags?: string;\n /** Transport level filter */\n level?: LogLevelName;\n}\n\n/**\n * Sends logs to Datadog via HTTP POST.\n * Uses built-in batching (not implemented here for brevity, typically would use batchTransport wrapper).\n * This implementation sends immediately for simplicity or relies on `batchTransport` composition.\n */\nexport function datadogTransport(options: DatadogTransportOptions): Transport {\n const {\n apiKey,\n site = \"datadoghq.com\",\n service,\n ddSource = \"nodejs\",\n hostname,\n tags,\n level,\n } = options;\n const url = `https://http-intake.logs.${site}/api/v2/logs`;\n\n return {\n name: \"datadog\",\n level,\n async write(entry) {\n const payload = {\n ddsource: ddSource,\n ddtags: tags,\n hostname,\n service,\n message: entry.message,\n status: entry.levelName.toLowerCase(), // Datadog uses lowercase status\n ...entry.meta,\n timestamp: entry.timestamp, // Datadog auto-parses this\n };\n\n try {\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"DD-API-KEY\": apiKey,\n },\n body: JSON.stringify(payload),\n });\n } catch (err) {\n console.error(\"[voltlog] Datadog push failed\", err);\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Discord transformer — sends logs to Discord via Webhook.\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Using this in the browser will expose your Webhook URL.\n * > Recommended for server-side use only.\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface DiscordTransportOptions {\n /** Discord Webhook URL */\n webhookUrl: string;\n /** Minimum log level (default: ERROR) */\n level?: LogLevelName;\n /** Username override */\n username?: string;\n /** Avatar URL override */\n avatarUrl?: string;\n}\n\n/**\n * Sends formatted embeds to Discord.\n * Best used for Alerts/Errors.\n */\nexport function discordTransport(options: DiscordTransportOptions): Transport {\n const { webhookUrl, username, avatarUrl, level = \"ERROR\" } = options;\n\n return {\n name: \"discord\",\n level,\n async write(entry) {\n try {\n await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(\n formatDiscordPayload(entry, username, avatarUrl),\n ),\n });\n } catch (_err) {\n // swallow\n }\n },\n };\n}\n\nfunction formatDiscordPayload(\n entry: LogEntry,\n username?: string,\n avatar_url?: string,\n): Record<string, unknown> {\n const color = getLevelColor(entry.level);\n\n return {\n username: username || \"VoltLog\",\n avatar_url,\n embeds: [\n {\n title: `${entry.levelName} - ${entry.message}`,\n color,\n timestamp: new Date(entry.timestamp).toISOString(),\n fields: [\n {\n name: \"Meta\",\n value: `\\`\\`\\`json\\n${JSON.stringify(entry.meta, null, 2).slice(\n 0,\n 1000,\n )}\\n\\`\\`\\``,\n },\n entry.error?.stack\n ? {\n name: \"Stack\",\n value: `\\`\\`\\`js\\n${entry.error.stack.slice(0, 1000)}\\n\\`\\`\\``,\n }\n : null,\n ].filter(Boolean),\n },\n ],\n };\n}\n\nfunction getLevelColor(level: number): number {\n if (level >= 50) return 15158332; // Red (Error)\n if (level >= 40) return 16776960; // Yellow (Warn)\n if (level >= 30) return 3447003; // Blue (Info)\n return 9807270; // Grey/Green\n}\n","/**\n * @module voltlog-io\n * @description File transformer — writes logs to disk with daily rotation.\n * @server-only\n * This transport relies on the `node:fs` module to write files to the local filesystem.\n * Browsers do not have direct access to the filesystem for security reasons.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface FileTransportOptions {\n /** Directory to store logs. Created if missing. */\n dir: string;\n /**\n * Filename pattern. Use `%DATE%` for YYYY-MM-DD.\n * Default: `app-%DATE%.log`\n */\n filename?: string;\n /** Per-transport level filter */\n level?: LogLevelName;\n}\n\n/**\n * Creates a file transport that writes newline-delimited JSON.\n * Rotates files daily based on the `%DATE%` pattern.\n */\nexport function fileTransport(options: FileTransportOptions): Transport {\n const { dir, level } = options;\n const filenamePattern = options.filename ?? \"app-%DATE%.log\";\n\n let currentStream: fs.WriteStream | null = null;\n let currentPath = \"\";\n\n // Ensure directory exists synchronously on startup\n try {\n fs.mkdirSync(dir, { recursive: true });\n } catch (err) {\n console.error(`[voltlog] Failed to create log directory: ${dir}`, err);\n }\n\n function getPath(): string {\n const now = new Date();\n const dateStr = now.toISOString().split(\"T\")[0]; // YYYY-MM-DD\n const filename = filenamePattern.replace(\"%DATE%\", dateStr);\n return path.join(dir, filename);\n }\n\n function rotate(): void {\n const newPath = getPath();\n if (newPath !== currentPath) {\n if (currentStream) {\n currentStream.end();\n }\n currentPath = newPath;\n // 'a' flag for append\n currentStream = fs.createWriteStream(newPath, { flags: \"a\" });\n\n // Handle stream errors to prevent crashing the app\n currentStream.on(\"error\", (err) => {\n console.error(`[voltlog] File write error to ${newPath}:`, err);\n });\n }\n }\n\n // Initialize\n rotate();\n\n return {\n name: \"file\",\n level,\n write(entry: LogEntry): void {\n // Check rotation on every write (low overhead string comparison)\n // For extremely high throughput, this could be optimized to check only every N writes or every few seconds\n rotate();\n\n if (currentStream && !currentStream.writableEnded) {\n const line = `${JSON.stringify(entry)}\\n`;\n currentStream.write(line);\n }\n },\n async flush(): Promise<void> {\n // Streams flush automatically on write usually, but we can try to ensure\n // limited control over fs stream flushing in Node without callbacks\n // No-op for standard fs streams as they handle backpressure internally\n },\n async close(): Promise<void> {\n if (currentStream) {\n return new Promise((resolve) => {\n currentStream?.end(() => resolve());\n });\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description JSON stream transformer — writes newline-delimited JSON to any writable stream.\n * @server-only\n * This transport relies on Node.js-style `Writable` streams (e.g. `process.stdout`, `fs.createWriteStream`).\n * Browser streams (WHATWG Streams API) use a different interface.\n *\n * Useful for file logging, piping to external tools, etc.\n *\n * @example Write to file\n * ```ts\n * import { createWriteStream } from 'node:fs';\n * import { createLogger, jsonStreamTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [\n * jsonStreamTransport({ stream: createWriteStream('./app.log', { flags: 'a' }) }),\n * ],\n * });\n * ```\n *\n * @example Write to stdout\n * ```ts\n * const logger = createLogger({\n * transports: [jsonStreamTransport({ stream: process.stdout })],\n * });\n * ```\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface JsonStreamTransportOptions {\n /** Writable stream to output to (e.g. fs.createWriteStream, process.stdout) */\n stream: NodeJS.WritableStream;\n /** Per-transport level filter */\n level?: LogLevelName;\n /**\n * Custom serializer. Return the string to write.\n * Default: JSON.stringify(entry) + '\\n'\n */\n serializer?: (entry: LogEntry) => string;\n}\n\n/**\n * Create a JSON stream transport that writes newline-delimited JSON.\n */\nexport function jsonStreamTransport(\n options: JsonStreamTransportOptions,\n): Transport {\n const stream = options.stream;\n const serialize =\n options.serializer ?? ((entry: LogEntry) => `${JSON.stringify(entry)}\\n`);\n\n return {\n name: \"json-stream\",\n level: options.level,\n write(entry: LogEntry): void {\n const data = serialize(entry);\n stream.write(data);\n },\n close(): Promise<void> {\n return new Promise((resolve) => {\n if (\"end\" in stream && typeof stream.end === \"function\") {\n stream.end(() => resolve());\n } else {\n resolve();\n }\n });\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Loki transformer — pushes logs to Grafana Loki.\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Using this in the browser exposes auth details and may face CORS issues.\n * > Recommended for server-side use.\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface LokiTransportOptions {\n /** Loki URL (e.g. http://localhost:3100) */\n host: string;\n /** Basic Auth username */\n basicAuthUser?: string;\n /** Basic Auth password/token */\n basicAuthPassword?: string;\n /** Tenant ID (header X-Scope-OrgID) */\n tenantId?: string;\n /** Labels to attach to every stream (e.g. { app: 'volt-server' }) */\n labels?: Record<string, string>;\n /** Transport level filter */\n level?: LogLevelName;\n /** Batch size (default: 10) */\n batchSize?: number;\n /** Batch interval ms (default: 5000) */\n interval?: number;\n}\n\n/**\n * Pushes logs to Grafana Loki via HTTP API.\n * Uses batching to improve performance.\n */\nexport function lokiTransport(options: LokiTransportOptions): Transport {\n const { host, labels = { app: \"voltlog\" }, level } = options;\n const batchSize = options.batchSize ?? 10;\n const interval = options.interval ?? 5000;\n\n const url = `${host.replace(/\\/$/, \"\")}/loki/api/v1/push`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (options.basicAuthUser && options.basicAuthPassword) {\n const creds = btoa(`${options.basicAuthUser}:${options.basicAuthPassword}`);\n headers.Authorization = `Basic ${creds}`;\n }\n\n if (options.tenantId) {\n headers[\"X-Scope-OrgID\"] = options.tenantId;\n }\n\n let buffer: LogEntry[] = [];\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const flush = async () => {\n if (buffer.length === 0) return;\n const batch = buffer;\n buffer = [];\n\n const streams = [\n {\n stream: labels,\n values: batch.map((e) => [\n String(e.timestamp * 1000000), // Loki wants nanoseconds\n JSON.stringify({\n level: e.levelName,\n message: e.message,\n ...e.meta,\n }),\n ]),\n },\n ];\n\n try {\n await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ streams }),\n });\n } catch (err) {\n console.error(\"[voltlog] Loki push failed\", err);\n }\n };\n\n const schedule = () => {\n if (!timer) {\n timer = setTimeout(() => {\n timer = null;\n flush();\n }, interval);\n }\n };\n\n return {\n name: \"loki\",\n level,\n write(entry) {\n buffer.push(entry);\n if (buffer.length >= batchSize) {\n if (timer) clearTimeout(timer);\n timer = null;\n flush();\n } else {\n schedule();\n }\n },\n async flush() {\n if (timer) clearTimeout(timer);\n await flush();\n },\n async close() {\n await this.flush?.();\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Pretty transformer — human-readable colored output with OCPP exchange formatting.\n * @universal Works in all environments (uses ANSI codes, supported by many browser consoles or stripped).\n *\n * @example Dev mode\n * ```ts\n * import { createLogger, prettyTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [prettyTransport()],\n * });\n *\n * logger.info('Server started', { port: 9000 });\n * // → ℹ 2024-01-15T10:30:00.000Z INFO Server started { port: 9000 }\n * ```\n *\n * @example OCPP exchange logs\n * ```ts\n * logger.info('OCPP exchange', {\n * chargePointId: 'CP-101',\n * action: 'BootNotification',\n * messageType: 'CALL',\n * direction: 'IN',\n * latencyMs: 34,\n * status: 'Accepted',\n * });\n * // → ⚡ CP-101 → BootNotification [IN] CALL\n * // → ✔ Accepted (34ms)\n * ```\n */\n\nimport {\n type LogEntry,\n LogLevel,\n type LogLevelName,\n type OcppExchangeMeta,\n type Transport,\n} from \"../core/types.js\";\n\nexport interface PrettyTransportOptions {\n /** Per-transport level filter */\n level?: LogLevelName;\n /** Show timestamps (default: true) */\n timestamps?: boolean;\n /** Use colors in output (default: true) */\n colors?: boolean;\n}\n\n// ─── ANSI Color Codes ────────────────────────────────────────────\n\nconst RESET = \"\\x1b[0m\";\nconst DIM = \"\\x1b[2m\";\nconst BOLD = \"\\x1b[1m\";\n\nconst COLORS: Record<string, string> = {\n TRACE: \"\\x1b[90m\", // gray\n DEBUG: \"\\x1b[36m\", // cyan\n INFO: \"\\x1b[32m\", // green\n WARN: \"\\x1b[33m\", // yellow\n ERROR: \"\\x1b[31m\", // red\n FATAL: \"\\x1b[35;1m\", // bold magenta\n};\n\nconst ICONS: Record<string, string> = {\n TRACE: \"🔍\",\n DEBUG: \"🐛\",\n INFO: \"ℹ\",\n WARN: \"⚠\",\n ERROR: \"✖\",\n FATAL: \"💀\",\n};\n\nconst EXCHANGE_ICONS: Record<string, string> = {\n CALL: \"⚡\",\n CALLRESULT: \"✔\",\n CALLERROR: \"🚨\",\n};\n\nconst DIRECTION_ARROWS: Record<string, string> = {\n IN: \"→\",\n OUT: \"←\",\n};\n\n/**\n * Create a pretty-print transport for dev/debug use.\n * Includes OCPP exchange log prettification.\n */\nexport function prettyTransport(\n options: PrettyTransportOptions = {},\n): Transport {\n const showTimestamps = options.timestamps ?? true;\n const useColors = options.colors ?? true;\n\n function colorize(text: string, color: string): string {\n return useColors ? `${color}${text}${RESET}` : text;\n }\n\n function formatExchange(entry: LogEntry): string | null {\n const meta = entry.meta as unknown as OcppExchangeMeta;\n if (!meta || !meta.action || !meta.messageType) return null;\n\n const icon = EXCHANGE_ICONS[meta.messageType] ?? \"•\";\n const arrow = DIRECTION_ARROWS[meta.direction ?? \"IN\"] ?? \"→\";\n const cpId = meta.chargePointId ?? \"unknown\";\n const action = meta.action;\n const msgType = meta.messageType;\n const dir = meta.direction ?? \"\";\n\n let line = `${icon} ${colorize(cpId, BOLD)} ${arrow} ${colorize(\n action,\n BOLD,\n )} [${dir}] ${colorize(msgType, DIM)}`;\n\n // Second line for response info\n if (meta.status || meta.latencyMs !== undefined) {\n const statusIcon = meta.messageType === \"CALLERROR\" ? \"❌\" : \"✔\";\n const status = meta.status ?? \"\";\n const latency =\n meta.latencyMs !== undefined ? `(${meta.latencyMs}ms)` : \"\";\n line += `\\n${statusIcon} ${status} ${colorize(latency, DIM)}`;\n }\n\n return line;\n }\n\n function formatStandard(entry: LogEntry): string {\n const icon = ICONS[entry.levelName] ?? \"•\";\n const levelColor = COLORS[entry.levelName] ?? \"\";\n const level = colorize(entry.levelName.padEnd(5), levelColor);\n const ts = showTimestamps\n ? `${colorize(new Date(entry.timestamp).toISOString(), DIM)} `\n : \"\";\n\n let line = `${icon} ${ts}${level} ${entry.message}`;\n\n // Add context\n if (entry.context && Object.keys(entry.context).length > 0) {\n line += ` ${colorize(JSON.stringify(entry.context), DIM)}`;\n }\n\n // Add meta (only non-OCPP fields or all if no exchange)\n if (\n entry.meta &&\n Object.keys(entry.meta as Record<string, unknown>).length > 0\n ) {\n line += ` ${colorize(JSON.stringify(entry.meta), DIM)}`;\n }\n\n // Add error\n if (entry.error) {\n line += `\\n ${colorize(\n `${entry.error.name ?? \"Error\"}: ${entry.error.message}`,\n COLORS.ERROR ?? \"\",\n )}`;\n if (entry.error.stack) {\n line += `\\n${colorize(entry.error.stack, DIM)}`;\n }\n }\n\n return line;\n }\n\n return {\n name: \"pretty\",\n level: options.level,\n write(entry: LogEntry): void {\n // Try exchange format first\n const exchangeOutput = formatExchange(entry);\n if (exchangeOutput) {\n console.log(exchangeOutput);\n return;\n }\n\n // Fall back to standard pretty format\n const output = formatStandard(entry);\n\n if (entry.level >= LogLevel.ERROR) {\n console.error(output);\n } else if (entry.level >= LogLevel.WARN) {\n console.warn(output);\n } else {\n console.log(output);\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Redis Streams transformer — publishes log entries to a Redis Stream.\n * @server-only\n * Designed for standard Redis clients (like `ioredis`) which use TCP sockets (unavailable in browsers).\n * For HTTP-based Redis (e.g. Upstash), use a custom transformer or `webhookTransport`.\n *\n * Users can then subscribe (XREAD/XREADGROUP) for real-time dashboards, monitoring, etc.\n *\n * Requires `ioredis` — user brings their own client instance (no hard dep).\n *\n * @example Basic\n * ```ts\n * import Redis from 'ioredis';\n * import { createLogger, redisTransport } from 'voltlog-io';\n *\n * const redis = new Redis();\n * const logger = createLogger({\n * transports: [\n * redisTransport({ client: redis, streamKey: 'logs:ocpp' }),\n * ],\n * });\n *\n * logger.info('CP connected', { chargePointId: 'CP-101' });\n * // → XADD logs:ocpp * level INFO message \"CP connected\" ...\n * ```\n *\n * @example With TTL and max stream length\n * ```ts\n * redisTransport({\n * client: redis,\n * streamKey: 'logs:ocpp',\n * maxLen: 10_000, // keep last 10k entries (approximate trim)\n * })\n * ```\n *\n * @example Subscribing (consumer side)\n * ```ts\n * // In your dashboard / monitoring service:\n * const redis = new Redis();\n *\n * // Read new entries from the stream\n * const entries = await redis.xread('BLOCK', 0, 'STREAMS', 'logs:ocpp', '$');\n *\n * // Or use consumer groups for at-least-once delivery:\n * await redis.xgroup('CREATE', 'logs:ocpp', 'dashboard', '$', 'MKSTREAM');\n * const entries = await redis.xreadgroup(\n * 'GROUP', 'dashboard', 'worker-1',\n * 'BLOCK', 0, 'STREAMS', 'logs:ocpp', '>'\n * );\n * ```\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\n/**\n * Minimal interface for the Redis client methods we need.\n * Compatible with `ioredis` — user provides their own instance.\n */\nexport interface RedisClient {\n xadd(...args: (string | number)[]): Promise<string | null>;\n quit?(): Promise<void>;\n}\n\nexport interface RedisTransportOptions {\n /** Redis client instance (e.g. `new Redis()` from ioredis) */\n client: RedisClient;\n /** Redis Stream key to publish to (default: 'logs') */\n streamKey?: string;\n /**\n * Max approximate stream length — older entries are trimmed.\n * Uses MAXLEN ~ (approximate) to keep the stream bounded.\n * Default: no limit.\n */\n maxLen?: number;\n /** Per-transport level filter */\n level?: LogLevelName;\n /**\n * Custom field mapper — convert LogEntry to flat key-value pairs for Redis.\n * Default: serializes the entire entry as JSON under a single 'data' field.\n */\n fieldMapper?: (entry: LogEntry) => Record<string, string>;\n}\n\n/**\n * Create a Redis Streams transport.\n *\n * Publishes each log entry via XADD to a Redis Stream.\n * Fire-and-forget — errors are silently swallowed to avoid blocking.\n */\nexport function redisTransport(options: RedisTransportOptions): Transport {\n const { client, streamKey = \"logs\", maxLen, level } = options;\n\n const fieldMapper = options.fieldMapper ?? defaultFieldMapper;\n\n function defaultFieldMapper(entry: LogEntry): Record<string, string> {\n return {\n id: entry.id,\n level: String(entry.level),\n levelName: entry.levelName,\n message: entry.message,\n timestamp: String(entry.timestamp),\n data: JSON.stringify({\n meta: entry.meta,\n context: entry.context,\n correlationId: entry.correlationId,\n error: entry.error,\n }),\n };\n }\n\n return {\n name: \"redis\",\n level,\n write(entry: LogEntry): void {\n const fields = fieldMapper(entry);\n const args: (string | number)[] = [streamKey];\n\n // Approximate trimming to bound stream length\n if (maxLen) {\n args.push(\"MAXLEN\", \"~\", maxLen);\n }\n\n args.push(\"*\"); // auto-generate stream entry ID\n\n // Flatten fields into [key, value, key, value, ...]\n for (const [key, value] of Object.entries(fields)) {\n args.push(key, value);\n }\n\n // Fire-and-forget\n client.xadd(...args).catch(() => {\n /* Swallowed — never crash the app */\n });\n },\n async close(): Promise<void> {\n // Don't close the user's Redis client — they own it\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Sentry transformer — pushes errors to Sentry.\n * @universal Works in both Server and Browser (depends on provided Sentry instance).\n */\n\nimport type { LogLevelName, Transport } from \"../core/types.js\";\n\n/**\n * Minimal Sentry client interface to avoid hard dependency on @sentry/* packages.\n * Users pass their Sentry instance (e.g. `import * as Sentry from '@sentry/node'`).\n */\nexport interface SentryInstance {\n captureException(exception: unknown, hint?: unknown): string;\n captureMessage(message: string, level?: unknown): string;\n addBreadcrumb(breadcrumb: unknown): void;\n Severity?: unknown; // To check severity enum if present\n}\n\nexport interface SentryTransportOptions {\n /** Configured Sentry instance/hub */\n sentry: SentryInstance;\n /** Minimum level to trigger captureException (default: ERROR) */\n errorLevel?: LogLevelName;\n /** Minimum level to send breadcrumbs (default: INFO) */\n breadcrumbLevel?: LogLevelName;\n}\n\nimport { resolveLevel } from \"../core/levels.js\";\n\n/**\n * Integrates with an existing Sentry instance.\n * - Logs >= errorLevel -> sent as Exceptions\n * - Logs >= breadcrumbLevel -> sent as Breadcrumbs\n */\nexport function sentryTransport(options: SentryTransportOptions): Transport {\n const { sentry } = options;\n const errorLevelValue = resolveLevel(options.errorLevel ?? \"ERROR\");\n const breadcrumbLevelValue = resolveLevel(options.breadcrumbLevel ?? \"INFO\");\n\n return {\n name: \"sentry\",\n write(entry) {\n // 1. Send as Exception if >= errorLevel\n if (entry.level >= errorLevelValue) {\n if (entry.error) {\n sentry.captureException(entry.error, {\n extra: {\n ...entry.meta,\n context: entry.context,\n },\n level: \"error\",\n });\n } else {\n sentry.captureMessage(entry.message, \"error\");\n }\n }\n\n // 2. Always add breadcrumb for context (if >= breadcrumbLevel)\n if (entry.level >= breadcrumbLevelValue) {\n sentry.addBreadcrumb({\n category: \"log\",\n message: entry.message,\n level: mapLevelToSentry(entry.level),\n data: { ...entry.meta, ...entry.context },\n timestamp: entry.timestamp / 1000,\n });\n }\n },\n };\n}\n\nfunction mapLevelToSentry(level: number): string {\n if (level >= 60) return \"fatal\";\n if (level >= 50) return \"error\";\n if (level >= 40) return \"warning\";\n if (level >= 30) return \"info\";\n return \"debug\";\n}\n","/**\n * @module voltlog-io\n * @description Slack transformer — sends rich notifications to Slack via Incoming Webhook.\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Using this in the browser will expose your Webhook URL.\n * > Recommended for server-side use only.\n * Best used with a filter (e.g. ERROR only) or alert middleware.\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface SlackTransportOptions {\n /** Slack Incoming Webhook URL */\n webhookUrl: string;\n /** Filter level (default: ERROR) */\n level?: LogLevelName;\n /** Custom username (overrides webhook default) */\n username?: string;\n /** Custom icon emoji (overrides webhook default) */\n iconEmoji?: string;\n}\n\n/**\n * Sends logs to Slack with formatting.\n * Best suited for ERROR/FATAL logs or specific alerts.\n */\nexport function slackTransport(options: SlackTransportOptions): Transport {\n const { webhookUrl, username, iconEmoji, level } = options;\n\n return {\n name: \"slack\",\n level: level ?? \"ERROR\", // Default to ERROR to prevent spamming\n async write(entry: LogEntry): Promise<void> {\n try {\n const payload = formatSlackMessage(entry, username, iconEmoji);\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n // We don't want to throw and crash the logger, just console.error\n // console.error(`[voltlog] Slack transport failed: ${response.statusText}`);\n }\n } catch (_err) {\n // console.error(`[voltlog] Slack transport error`, err);\n }\n },\n };\n}\n\nfunction formatSlackMessage(\n entry: LogEntry,\n username?: string,\n icon_emoji?: string,\n): Record<string, unknown> {\n const levelEmoji = getLevelEmoji(entry.level);\n const color = getLevelColor(entry.level);\n\n const blocks = [\n {\n type: \"header\",\n text: {\n type: \"plain_text\",\n text: `${levelEmoji} ${entry.levelName}: ${entry.message}`,\n emoji: true,\n },\n },\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: `*Time:* ${new Date(entry.timestamp).toISOString()}`,\n },\n {\n type: \"mrkdwn\",\n text: `*ID:* \\`${entry.id}\\``,\n },\n ],\n },\n ];\n\n // Add correlation ID if present\n if (entry.correlationId) {\n // biome-ignore lint/suspicious/noExplicitAny: Slack Block Kit types are complex to fully type here\n (blocks[1].elements as any[]).push({\n type: \"mrkdwn\",\n text: `*Trace:* \\`${entry.correlationId}\\``,\n });\n }\n\n // Meta section\n if (Object.keys(entry.meta).length > 0) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*Metadata:*\\n\\`\\`\\`${JSON.stringify(entry.meta, null, 2)}\\`\\`\\``,\n emoji: true,\n },\n });\n }\n\n // Error stack\n if (entry.error?.stack) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*Error Stack:*\\n\\`\\`\\`${entry.error.stack}\\`\\`\\``,\n emoji: true,\n },\n });\n }\n\n return {\n username,\n icon_emoji,\n attachments: [\n {\n color,\n blocks,\n },\n ],\n };\n}\n\nfunction getLevelEmoji(level: number): string {\n if (level >= 60) return \"🔥\"; // FATAL\n if (level >= 50) return \"🚨\"; // ERROR\n if (level >= 40) return \"⚠️\"; // WARN\n if (level >= 30) return \"ℹ️\"; // INFO\n if (level >= 20) return \"🐛\"; // DEBUG\n return \"🔍\"; // TRACE\n}\n\nfunction getLevelColor(level: number): string {\n if (level >= 60) return \"#ff0000\"; // FATAL (Red)\n if (level >= 50) return \"#ff4444\"; // ERROR (Light Red)\n if (level >= 40) return \"#ffbb33\"; // WARN (Orange)\n if (level >= 30) return \"#33b5e5\"; // INFO (Blue)\n if (level >= 20) return \"#99cc00\"; // DEBUG (Green)\n return \"#aa66cc\"; // TRACE (Purple)\n}\n","/**\n * @module voltlog-io\n * @description Webhook transformer — sends logs via HTTP POST.\n * @universal Works in all environments (uses `fetch`).\n * Supports batching for performance.\n *\n * @example\n * ```ts\n * import { createLogger, webhookTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [\n * webhookTransport({\n * url: 'https://my-api.com/logs',\n * headers: { Authorization: 'Bearer token123' },\n * batchSize: 50,\n * flushIntervalMs: 5000,\n * }),\n * ],\n * });\n * ```\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface WebhookTransportOptions {\n /** Target URL to POST log entries to */\n url: string;\n /** HTTP method (default: POST) */\n method?: string;\n /** Additional HTTP headers */\n headers?: Record<string, string>;\n /** Number of entries to batch before sending (default: 1 = no batching) */\n batchSize?: number;\n /** Max time in ms to wait before flushing a partial batch (default: 5000) */\n flushIntervalMs?: number;\n /** Per-transport level filter */\n level?: LogLevelName;\n /** Custom body serializer. Default: JSON.stringify({ entries: [...] }) */\n serializer?: (entries: LogEntry[]) => string;\n /** Retry failed requests (default: false) */\n retry?: boolean;\n /** Max retries (default: 3) */\n maxRetries?: number;\n}\n\n/**\n * Create a webhook transport that POSTs log entries to an HTTP endpoint.\n */\nexport function webhookTransport(options: WebhookTransportOptions): Transport {\n const {\n url,\n method = \"POST\",\n headers = {},\n batchSize = 1,\n flushIntervalMs = 5000,\n retry = false,\n maxRetries = 3,\n } = options;\n\n const serialize =\n options.serializer ??\n ((entries: LogEntry[]) =>\n JSON.stringify({\n entries,\n count: entries.length,\n timestamp: Date.now(),\n }));\n\n let buffer: LogEntry[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n async function sendBatch(entries: LogEntry[], attempt = 0): Promise<void> {\n try {\n const response = await fetch(url, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n body: serialize(entries),\n });\n\n if (!response.ok && retry && attempt < maxRetries) {\n // Exponential backoff retry\n const delay = Math.min(1000 * 2 ** attempt, 30000);\n await new Promise((r) => setTimeout(r, delay));\n return sendBatch(entries, attempt + 1);\n }\n } catch {\n if (retry && attempt < maxRetries) {\n const delay = Math.min(1000 * 2 ** attempt, 30000);\n await new Promise((r) => setTimeout(r, delay));\n return sendBatch(entries, attempt + 1);\n }\n /* Final failure — swallowed to avoid crashing the app */\n }\n }\n\n function scheduleFlush(): void {\n if (flushTimer) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n doFlush();\n }, flushIntervalMs);\n }\n\n function doFlush(): void {\n if (buffer.length === 0) return;\n const batch = buffer;\n buffer = [];\n // Fire-and-forget\n sendBatch(batch).catch(() => {});\n }\n\n return {\n name: \"webhook\",\n level: options.level,\n write(entry: LogEntry): void {\n buffer.push(entry);\n\n if (buffer.length >= batchSize) {\n doFlush();\n } else {\n scheduleFlush();\n }\n },\n async flush(): Promise<void> {\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n if (buffer.length > 0) {\n const batch = buffer;\n buffer = [];\n await sendBatch(batch);\n }\n },\n async close(): Promise<void> {\n await this.flush?.();\n },\n };\n}\n"],"mappings":"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","names":["import_cuid2","fs","path","getLevelColor"]}
1
+ {"version":3,"sources":["/home/runner/work/voltlog-io/voltlog-io/dist/index.js","../src/core/types.ts","../src/core/levels.ts","../src/core/logger.ts","../src/core/pipeline.ts","../src/middleware/ai-enrichment.ts","../src/middleware/alert.ts","../src/middleware/async-context.ts","../src/middleware/correlation-id.ts","../src/middleware/create-middleware.ts","../src/middleware/deduplication.ts","../src/middleware/heap-usage.ts","../src/middleware/http.ts","../src/middleware/ip.ts","../src/middleware/level-override.ts","../src/middleware/ocpp.ts","../src/middleware/otel-trace.ts","../src/middleware/redaction.ts","../src/middleware/sampling.ts","../src/middleware/user-agent.ts","../src/transports/batch.ts","../src/transports/browser-json-stream.ts","../src/transports/console.ts","../src/transports/create-transport.ts","../src/transports/datadog.ts","../src/transports/discord.ts","../src/transports/file.ts","../src/transports/json-stream.ts","../src/transports/loki.ts","../src/transports/otel.ts","../src/transports/pretty.ts","../src/transports/redis.ts","../src/transports/ring-buffer.ts","../src/transports/sentry.ts","../src/transports/slack.ts","../src/transports/webhook.ts"],"names":[],"mappings":"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","file":"/home/runner/work/voltlog-io/voltlog-io/dist/index.js","sourcesContent":[null,"/**\n * @module voltlog-io\n * @description Type definitions for the OCPP-aware structured logger.\n */\n\n// ─── Log Levels ──────────────────────────────────────────────────\n\nexport const LogLevel = {\n TRACE: 10,\n DEBUG: 20,\n INFO: 30,\n WARN: 40,\n ERROR: 50,\n FATAL: 60,\n SILENT: Infinity,\n} as const;\n\nexport type LogLevelName = keyof typeof LogLevel;\n\n/** Numeric log level value */\nexport type LogLevelValue = (typeof LogLevel)[LogLevelName];\n\n/** Map from level name (lowercase) to numeric value */\nexport const LogLevelNameMap: Record<string, number> = Object.fromEntries(\n Object.entries(LogLevel).map(([k, v]) => [k.toLowerCase(), v]),\n);\n\n/** Map from numeric value to level name */\nexport const LogLevelValueMap: Record<number, LogLevelName> =\n Object.fromEntries(\n Object.entries(LogLevel)\n .filter(([, v]) => Number.isFinite(v))\n .map(([k, v]) => [v, k as LogLevelName]),\n ) as Record<number, LogLevelName>;\n\n// ─── Log Entry ───────────────────────────────────────────────────\n\nexport interface LogEntry<TMeta = Record<string, unknown>> {\n /** Unique log ID */\n id: string;\n /** Numeric log level */\n level: number;\n /** Human-readable level name */\n levelName: LogLevelName;\n /** Log message */\n message: string;\n /** Unix epoch timestamp (ms) */\n timestamp: number;\n /** User-defined structured metadata (type-safe via generics) */\n meta: TMeta;\n /** Bound context from child logger (e.g. chargePointId, sessionId) */\n context?: Record<string, unknown>;\n /** Correlation ID for tracing across async operations */\n correlationId?: string;\n /** Error information */\n error?: LogError;\n /** @internal Cached JSON serialization — avoids redundant stringify across transports */\n _json?: string;\n}\n\nexport interface LogError {\n message: string;\n stack?: string;\n code?: string;\n name?: string;\n /** Nested cause chain (ES2022 Error.cause support) */\n cause?: LogError;\n}\n\n// ─── OCPP Exchange Meta ──────────────────────────────────────────\n\nexport interface OcppExchangeMeta {\n /** Charge point / station identity */\n chargePointId?: string;\n /** OCPP message type */\n messageType?: \"CALL\" | \"CALLRESULT\" | \"CALLERROR\";\n /** OCPP action name (e.g. BootNotification) */\n action?: string;\n /** Message direction */\n direction?: \"IN\" | \"OUT\";\n /** Correlation ID for request/response matching */\n correlationId?: string;\n /** Negotiated OCPP protocol version */\n protocol?: string;\n /** Serialized payload size in bytes */\n payloadSize?: number;\n /** Latency in milliseconds */\n latencyMs?: number;\n /** Response status (e.g. Accepted, Rejected) */\n status?: string;\n}\n\n// ─── Transport ─────────────────────────────────────────────────\n\n/**\n * A Transport receives formatted log entries and delivers them\n * to a destination (console, file, webhook, database, etc.).\n *\n * Transports are async-safe — `write()` can return a Promise.\n */\nexport interface Transport<TMeta = Record<string, unknown>> {\n /** Unique name for this transport */\n name: string;\n /** Optional per-transport level filter */\n level?: LogLevelName;\n /** Process a log entry */\n write(entry: LogEntry<TMeta>): void | Promise<void>;\n /** Flush any buffered entries */\n flush?(): void | Promise<void>;\n /** Graceful shutdown */\n close?(): void | Promise<void>;\n}\n\n// ─── Middleware ───────────────────────────────────────────────────\n\n/**\n * Middleware intercepts log entries before they reach transports.\n * Used for redaction, sampling, enrichment, alerting, etc.\n *\n * Call `next(entry)` to continue the pipeline.\n * Omit `next()` to drop the entry (e.g. sampling).\n */\nexport type LogMiddleware<TMeta = Record<string, unknown>> = (\n entry: LogEntry<TMeta>,\n next: (entry: LogEntry<TMeta>) => void,\n) => void;\n\n// ─── Alert Rule ──────────────────────────────────────────────────\n\n/**\n * Alert rules evaluate log entries and fire callbacks\n * when configurable conditions are met.\n */\nexport interface AlertRule<TMeta = Record<string, unknown>> {\n /** Alert name (for identification) */\n name: string;\n /** Condition — return true if this entry should count toward the alert */\n when: (entry: LogEntry<TMeta>) => boolean;\n /** Number of matching entries required to fire (default: 1) */\n threshold?: number;\n /** Time window in ms for threshold counting */\n windowMs?: number;\n /** Minimum cooldown in ms between alert firings (default: 0) */\n cooldownMs?: number;\n /** Callback fired when alert conditions are met */\n onAlert: (entries: LogEntry<TMeta>[]) => void | Promise<void>;\n}\n\n// ─── Timer Result ────────────────────────────────────────────────\n\nexport interface TimerResult<TMeta = Record<string, unknown>> {\n /** End the timer and log the duration */\n done(message: string, meta?: Partial<TMeta>): void;\n /** Get elapsed time in ms without logging */\n elapsed(): number;\n}\n\n// ─── Logger Options ──────────────────────────────────────────────\n\nexport interface LoggerOptions<TMeta = Record<string, unknown>> {\n /** Minimum log level (default: INFO) */\n level?: LogLevelName;\n /** Transports for log output */\n transports?: Transport<TMeta>[];\n /** Middleware pipeline */\n middleware?: LogMiddleware<TMeta>[];\n /** Alert rules */\n alerts?: AlertRule<TMeta>[];\n /** Default bound context for all log entries */\n context?: Record<string, unknown>;\n /** Field paths to auto-redact (e.g. ['idToken', 'password']) */\n redact?: string[];\n /**\n * When to include error stack traces:\n * - `true` — always include\n * - `false` — never include\n * - `LogLevelName` — include at this level and above\n * Default: 'ERROR'\n */\n includeStack?: boolean | LogLevelName;\n /**\n * Exchange log mode:\n * - `true` — exchange logs alongside normal logs\n * - `'only'` — only exchange-formatted logs\n * - `false` — disabled (default)\n */\n exchangeLog?: boolean | \"only\";\n /** Custom timestamp function (default: Date.now) */\n timestamp?: () => number;\n /**\n * Custom ID generator function.\n * Default: `crypto.randomUUID()` (native, fast).\n * Set to `false` to disable ID generation entirely for max performance.\n */\n idGenerator?: (() => string) | false;\n}\n\n// ─── Logger Interface ────────────────────────────────────────────\n\nexport interface Logger<TMeta = Record<string, unknown>> {\n trace(message: string, meta?: Partial<TMeta>): void;\n debug(message: string, meta?: Partial<TMeta>): void;\n info(message: string, meta?: Partial<TMeta>): void;\n warn(message: string, meta?: Partial<TMeta>): void;\n error(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void;\n fatal(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void;\n\n /** Create a child logger with additional bound context */\n child(context: Record<string, unknown>): Logger<TMeta>;\n\n /** Add a transport at runtime */\n addTransport(transport: Transport<TMeta>): void;\n /** Remove a transport by name */\n removeTransport(name: string): void;\n /** Add middleware at runtime */\n addMiddleware(middleware: LogMiddleware<TMeta>): void;\n /** Remove middleware by reference */\n removeMiddleware(middleware: LogMiddleware<TMeta>): void;\n\n /** Change the minimum log level at runtime */\n setLevel(level: LogLevelName): void;\n /** Get the current minimum log level */\n getLevel(): LogLevelName;\n /** Check if a given level would produce output (useful to guard expensive meta computation) */\n isLevelEnabled(level: LogLevelName): boolean;\n\n /** Start a timer — call `.done(msg)` to log elapsed duration */\n startTimer(level?: LogLevelName): TimerResult<TMeta>;\n\n /** Flush all transports */\n flush(): Promise<void>;\n /** Close all transports gracefully */\n close(): Promise<void>;\n}\n","/**\n * @module voltlog-io\n * @description Log level utilities — filtering, comparison, resolution.\n */\n\nimport { LogLevel, type LogLevelName, LogLevelNameMap } from \"./types.js\";\n\n/**\n * Resolve a level name string to its numeric value.\n * Case-insensitive. Returns INFO if unrecognized.\n */\nexport function resolveLevel(level: string | LogLevelName): number {\n const n = LogLevelNameMap[level.toLowerCase()];\n return n !== undefined ? n : LogLevel.INFO;\n}\n\n/**\n * Check if a log entry at `entryLevel` passes the `filterLevel`.\n */\nexport function shouldLog(entryLevel: number, filterLevel: number): boolean {\n return entryLevel >= filterLevel;\n}\n\n/**\n * Determine whether to include stack trace for a given entry level.\n */\nexport function shouldIncludeStack(\n entryLevel: number,\n includeStack: boolean | LogLevelName,\n): boolean {\n if (typeof includeStack === \"boolean\") return includeStack;\n return entryLevel >= resolveLevel(includeStack);\n}\n","/**\n * @module voltlog-io\n * @description Core Logger class — zero external dependencies (only cuid2), runtime-agnostic.\n *\n * @example Basic usage\n * ```ts\n * import { createLogger, consoleTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * level: 'INFO',\n * transports: [consoleTransport()],\n * });\n *\n * logger.info('Server started', { port: 9000 });\n * ```\n *\n * @example Child logger with bound context\n * ```ts\n * const cpLogger = logger.child({ chargePointId: 'CP-101' });\n * cpLogger.info('BootNotification received');\n * // → auto-includes context: { chargePointId: 'CP-101' }\n * ```\n *\n * @example Error with stack trace\n * ```ts\n * logger.error('Connection failed', new Error('ETIMEDOUT'));\n * logger.error('Handler crashed', { action: 'BootNotification' }, new Error('null ref'));\n * ```\n *\n * @example With ocpp-ws-io (if user has both packages)\n * ```ts\n * import { createLogger } from 'ocpp-ws-io/logger'; // re-export\n * ```\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { resolveLevel, shouldIncludeStack, shouldLog } from \"./levels.js\";\nimport { composeMiddleware, fanOutToTransports } from \"./pipeline.js\";\nimport {\n type LogEntry,\n type LogError,\n type Logger,\n type LoggerOptions,\n type LogLevelName,\n LogLevelValueMap,\n type LogMiddleware,\n type TimerResult,\n type Transport,\n} from \"./types.js\";\n\n// ─── Logger Implementation ──────────────────────────────────────\n\nclass LoggerImpl<TMeta = Record<string, unknown>> implements Logger<TMeta> {\n private _level: number;\n private _transports: Transport<TMeta>[];\n private _middlewareList: LogMiddleware<TMeta>[];\n private _pipeline: (entry: LogEntry<TMeta>) => void;\n private _context: Record<string, unknown>;\n private _includeStack: boolean | LogLevelName;\n private _timestampFn: () => number;\n private _idFn: (() => string) | false;\n\n constructor(options: LoggerOptions<TMeta> = {}) {\n this._level = resolveLevel(options.level ?? \"INFO\");\n this._transports = [...(options.transports ?? [])];\n this._middlewareList = [...(options.middleware ?? [])];\n this._context = options.context ? { ...options.context } : {};\n this._includeStack = options.includeStack ?? \"ERROR\";\n this._timestampFn = options.timestamp ?? Date.now;\n this._idFn =\n options.idGenerator !== undefined ? options.idGenerator : randomUUID;\n this._pipeline = this._buildPipeline();\n }\n\n // ─── Log Methods ────────────────────────────────────────────\n\n trace(message: string, meta?: Partial<TMeta>): void {\n if (10 < this._level) return; // fast-fail short-circuit\n this._log(10, \"TRACE\", message, meta);\n }\n\n debug(message: string, meta?: Partial<TMeta>): void {\n if (20 < this._level) return;\n this._log(20, \"DEBUG\", message, meta);\n }\n\n info(message: string, meta?: Partial<TMeta>): void {\n if (30 < this._level) return;\n this._log(30, \"INFO\", message, meta);\n }\n\n warn(message: string, meta?: Partial<TMeta>): void {\n if (40 < this._level) return;\n this._log(40, \"WARN\", message, meta);\n }\n\n error(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void {\n if (50 < this._level) return;\n if (metaOrError instanceof Error) {\n this._log(50, \"ERROR\", message, undefined, metaOrError);\n } else {\n this._log(50, \"ERROR\", message, metaOrError, error);\n }\n }\n\n fatal(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void {\n if (metaOrError instanceof Error) {\n this._log(60, \"FATAL\", message, undefined, metaOrError);\n } else {\n this._log(60, \"FATAL\", message, metaOrError, error);\n }\n }\n\n // ─── Child Logger ───────────────────────────────────────────\n\n child(context: Record<string, unknown>): Logger<TMeta> {\n return new ChildLoggerImpl<TMeta>(this, { ...this._context, ...context });\n }\n\n // ─── Dynamic Configuration ─────────────────────────────────\n\n addTransport(transport: Transport<TMeta>): void {\n this._transports.push(transport);\n }\n\n removeTransport(name: string): void {\n this._transports = this._transports.filter((t) => t.name !== name);\n }\n\n addMiddleware(middleware: LogMiddleware<TMeta>): void {\n this._middlewareList.push(middleware);\n this._pipeline = this._buildPipeline();\n }\n\n removeMiddleware(middleware: LogMiddleware<TMeta>): void {\n this._middlewareList = this._middlewareList.filter((m) => m !== middleware);\n this._pipeline = this._buildPipeline();\n }\n\n // ─── Level Control ─────────────────────────────────────────\n\n setLevel(level: LogLevelName): void {\n this._level = resolveLevel(level);\n }\n\n getLevel(): LogLevelName {\n return (LogLevelValueMap[this._level] ?? \"INFO\") as LogLevelName;\n }\n\n isLevelEnabled(level: LogLevelName): boolean {\n return resolveLevel(level) >= this._level;\n }\n\n // ─── Timer ─────────────────────────────────────────────────\n\n startTimer(level?: LogLevelName): TimerResult<TMeta> {\n const start = performance.now();\n const logLevel = level ?? \"INFO\";\n return {\n done: (message: string, meta?: Partial<TMeta>) => {\n const durationMs = Math.round(performance.now() - start);\n const merged = { ...meta, durationMs } as unknown as Partial<TMeta>;\n const methodKey = logLevel.toLowerCase() as\n | \"trace\"\n | \"debug\"\n | \"info\"\n | \"warn\"\n | \"error\"\n | \"fatal\";\n this[methodKey](message, merged);\n },\n elapsed: () => Math.round(performance.now() - start),\n };\n }\n\n // ─── Lifecycle ──────────────────────────────────────────────\n\n async flush(): Promise<void> {\n await Promise.all(this._transports.map((t) => t.flush?.()).filter(Boolean));\n }\n\n async close(): Promise<void> {\n await this.flush();\n await Promise.all(this._transports.map((t) => t.close?.()).filter(Boolean));\n }\n\n // ─── Internal ───────────────────────────────────────────────\n\n /** @internal */\n _log(\n level: number,\n levelName: string,\n message: string,\n meta?: Partial<TMeta>,\n error?: Error,\n ): void {\n this._logWithContext(level, levelName, message, this._context, meta, error);\n }\n\n /** @internal — used by child loggers to inject bound context */\n _logWithContext(\n level: number,\n levelName: string,\n message: string,\n context: Record<string, unknown>,\n meta?: Partial<TMeta>,\n error?: Error,\n ): void {\n if (!shouldLog(level, this._level)) return;\n\n const entry: LogEntry<TMeta> = {\n id: this._idFn ? this._idFn() : \"\",\n level,\n levelName: levelName as LogLevelName,\n message,\n timestamp: this._timestampFn(),\n meta: (meta ?? {}) as TMeta,\n context: Object.keys(context).length > 0 ? context : undefined,\n };\n\n if (error) {\n entry.error = serializeError(\n error,\n shouldIncludeStack(level, this._includeStack),\n );\n }\n\n this._pipeline(entry);\n }\n\n private _buildPipeline(): (entry: LogEntry<TMeta>) => void {\n return composeMiddleware(this._middlewareList, (entry) => {\n fanOutToTransports(entry, this._transports, this._level);\n });\n }\n}\n\n// ─── Error Serialization ─────────────────────────────────────────\n\n/** Recursively serialize an Error including the ES2022 cause chain */\nfunction serializeError(\n error: Error,\n includeStack: boolean,\n depth = 0,\n): LogError {\n const logError: LogError = {\n message: error.message,\n name: error.name,\n code: (error as NodeJS.ErrnoException).code,\n };\n if (includeStack) {\n logError.stack = error.stack;\n }\n // Recursively serialize cause chain (cap depth at 5 to prevent infinite loops)\n if (error.cause instanceof Error && depth < 5) {\n logError.cause = serializeError(error.cause, includeStack, depth + 1);\n }\n return logError;\n}\n\n// ─── Child Logger ────────────────────────────────────────────────\n\nclass ChildLoggerImpl<TMeta = Record<string, unknown>>\n implements Logger<TMeta>\n{\n constructor(\n private _parent: LoggerImpl<TMeta>,\n private _context: Record<string, unknown>,\n ) {}\n\n trace(message: string, meta?: Partial<TMeta>): void {\n this._parent._logWithContext(10, \"TRACE\", message, this._context, meta);\n }\n debug(message: string, meta?: Partial<TMeta>): void {\n this._parent._logWithContext(20, \"DEBUG\", message, this._context, meta);\n }\n info(message: string, meta?: Partial<TMeta>): void {\n this._parent._logWithContext(30, \"INFO\", message, this._context, meta);\n }\n warn(message: string, meta?: Partial<TMeta>): void {\n this._parent._logWithContext(40, \"WARN\", message, this._context, meta);\n }\n error(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void {\n if (metaOrError instanceof Error) {\n this._parent._logWithContext(\n 50,\n \"ERROR\",\n message,\n this._context,\n undefined,\n metaOrError,\n );\n } else {\n this._parent._logWithContext(\n 50,\n \"ERROR\",\n message,\n this._context,\n metaOrError,\n error,\n );\n }\n }\n fatal(\n message: string,\n metaOrError?: Partial<TMeta> | Error,\n error?: Error,\n ): void {\n if (metaOrError instanceof Error) {\n this._parent._logWithContext(\n 60,\n \"FATAL\",\n message,\n this._context,\n undefined,\n metaOrError,\n );\n } else {\n this._parent._logWithContext(\n 60,\n \"FATAL\",\n message,\n this._context,\n metaOrError,\n error,\n );\n }\n }\n\n child(context: Record<string, unknown>): Logger<TMeta> {\n return new ChildLoggerImpl<TMeta>(this._parent, {\n ...this._context,\n ...context,\n });\n }\n\n addTransport(transport: Transport<TMeta>): void {\n this._parent.addTransport(transport);\n }\n removeTransport(name: string): void {\n this._parent.removeTransport(name);\n }\n addMiddleware(middleware: LogMiddleware<TMeta>): void {\n this._parent.addMiddleware(middleware);\n }\n removeMiddleware(middleware: LogMiddleware<TMeta>): void {\n this._parent.removeMiddleware(middleware);\n }\n setLevel(level: LogLevelName): void {\n this._parent.setLevel(level);\n }\n getLevel(): LogLevelName {\n return this._parent.getLevel();\n }\n isLevelEnabled(level: LogLevelName): boolean {\n return this._parent.isLevelEnabled(level);\n }\n startTimer(level?: LogLevelName): TimerResult<TMeta> {\n return this._parent.startTimer(level);\n }\n flush(): Promise<void> {\n return this._parent.flush();\n }\n close(): Promise<void> {\n return this._parent.close();\n }\n}\n\n// ─── Factory ─────────────────────────────────────────────────────\n\n/**\n * Create a new logger instance.\n *\n * @example Minimal\n * ```ts\n * const logger = createLogger();\n * logger.info('Hello');\n * ```\n *\n * @example Full options\n * ```ts\n * import { createLogger, consoleTransport, prettyTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * level: 'DEBUG',\n * transports: [prettyTransport()],\n * redact: ['password', 'idToken'],\n * includeStack: 'ERROR',\n * });\n * ```\n *\n * @example OCPP-aware with child loggers\n * ```ts\n * import { createLogger, prettyTransport } from 'voltlog-io';\n * import type { OcppExchangeMeta } from 'voltlog-io';\n *\n * const logger = createLogger<OcppExchangeMeta>({\n * level: 'INFO',\n * transports: [prettyTransport()],\n * });\n *\n * // Per-connection child logger\n * const cpLog = logger.child({ chargePointId: 'CP-101' });\n * cpLog.info('OCPP message', {\n * messageType: 'CALL',\n * action: 'BootNotification',\n * direction: 'IN',\n * });\n * ```\n */\nexport function createLogger<TMeta = Record<string, unknown>>(\n options?: LoggerOptions<TMeta>,\n): Logger<TMeta> {\n return new LoggerImpl<TMeta>(options);\n}\n","/**\n * @module voltlog-io\n * @description Pipeline — middleware composition and transformer fan-out.\n */\n\nimport { resolveLevel, shouldLog } from \"./levels.js\";\nimport type { LogEntry, LogMiddleware, Transport } from \"./types.js\";\n\n/**\n * Build a composed middleware function from an array of middleware.\n * Each middleware calls `next()` to pass the entry forward.\n * Omit `next()` to drop the entry (e.g. sampling).\n */\nexport function composeMiddleware<TMeta = Record<string, unknown>>(\n middleware: LogMiddleware<TMeta>[],\n final: (entry: LogEntry<TMeta>) => void,\n): (entry: LogEntry<TMeta>) => void {\n if (middleware.length === 0) return final;\n\n return (entry: LogEntry<TMeta>) => {\n let index = 0;\n\n const next = (e: LogEntry<TMeta>): void => {\n if (index < middleware.length) {\n // biome-ignore lint/style/noNonNullAssertion: Guarded by length check\n const mw = middleware[index++]!;\n mw(e, next);\n } else {\n final(e);\n }\n };\n\n next(entry);\n };\n}\n\n/**\n * Fan out a log entry to all transports, respecting per-transport level filters.\n * All transports run concurrently and fire-and-forget for non-blocking logging.\n */\nexport function fanOutToTransports<TMeta = Record<string, unknown>>(\n entry: LogEntry<TMeta>,\n transports: Transport<TMeta>[],\n loggerLevel: number,\n): void {\n for (const t of transports) {\n const tLevel = t.level ? resolveLevel(t.level) : loggerLevel;\n\n if (!shouldLog(entry.level, tLevel)) continue;\n\n try {\n const result = t.write(entry);\n if (result && typeof (result as Promise<void>).catch === \"function\") {\n (result as Promise<void>).catch(() => {\n /* Swallow transport errors to avoid crashing the host app */\n });\n }\n } catch {\n /* Sync transport errors swallowed */\n }\n }\n}\n","/**\n * @module voltlog-io\n * @description AI Enrichment middleware — enriches logs with AI analysis (e.g. error explanation).\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Requires an API Key. Using this in the browser will expose your key to the client.\n * > Recommended for server-side use only.\n */\n\nimport { resolveLevel } from \"../core/levels.js\";\nimport type { LogEntry, LogLevelName, LogMiddleware } from \"../core/types.js\";\n\nexport interface AiEnrichmentOptions {\n /**\n * Only trigger for logs at or above this level.\n * Default: ERROR\n */\n level?: LogLevelName;\n\n /**\n * Field name to store the analysis result in `entry.meta`.\n * Default: 'ai_analysis'\n */\n targetField?: string;\n\n /**\n * Custom analyzer function.\n * Return a string (analysis) or object (structured data) to attach to `meta[targetField]`.\n */\n analyzer: (\n entry: LogEntry,\n ) => Promise<string | Record<string, unknown> | null>;\n\n /**\n * Timeout in milliseconds for the AI call.\n * Default: 2000ms (fail fast to avoid blocking for too long)\n */\n timeout?: number;\n\n /**\n * If true, errors in the analyzer are swallowed (logged to console.error but don't crash).\n * Default: true\n */\n swallowErrors?: boolean;\n}\n\n/**\n * Enriches log entries by calling an asynchronous AI analyzer.\n * Appends result to `entry.meta[targetField]`.\n *\n * @example\n * ```ts\n * const ai = aiEnrichmentMiddleware({\n * analyzer: createOpenAiErrorAnalyzer(process.env.OPENAI_API_KEY!),\n * level: \"ERROR\",\n * targetField: \"error_explanation\"\n * });\n * ```\n */\nexport function aiEnrichmentMiddleware<TMeta = Record<string, unknown>>(\n options: AiEnrichmentOptions,\n): LogMiddleware<TMeta> {\n const minLevel = resolveLevel(options.level ?? \"ERROR\");\n const timeoutMs = options.timeout ?? 2000;\n const swallow = options.swallowErrors ?? true;\n const fieldName = options.targetField ?? \"ai_analysis\";\n\n return async (entry, next) => {\n // 1. Check level\n if (entry.level < minLevel) {\n next(entry);\n return;\n }\n\n try {\n // 2. Call analyzer with timeout\n const analysisPromise = options.analyzer(entry as unknown as LogEntry);\n const timeoutPromise = new Promise<null>((_, reject) =>\n setTimeout(() => reject(new Error(\"AI Analysis Timeout\")), timeoutMs),\n );\n\n const result = await Promise.race([analysisPromise, timeoutPromise]);\n\n if (result) {\n entry.meta = {\n ...entry.meta,\n [fieldName]: result,\n };\n }\n } catch (err) {\n if (!swallow) {\n throw err;\n }\n // Otherwise ignore\n }\n\n next(entry);\n };\n}\n\n/**\n * Helper to create an OpenAI-compatible analyzer specifically for Error explanation.\n */\nexport function createOpenAiErrorAnalyzer(\n apiKey: string,\n model = \"gpt-3.5-turbo\",\n systemPrompt = \"You are a log analyzer. Explain this error briefly and suggest a fix in 1 sentence.\",\n): (entry: LogEntry) => Promise<string | null> {\n return async (entry) => {\n try {\n const response = await fetch(\n \"https://api.openai.com/v1/chat/completions\",\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n messages: [\n { role: \"system\", content: systemPrompt },\n {\n role: \"user\",\n content: `Error Message: ${\n entry.message\n }\\nContext: ${JSON.stringify(entry.meta)}`,\n },\n ],\n max_tokens: 150,\n }),\n },\n );\n\n if (!response.ok) return null;\n const data = (await response.json()) as any;\n return data.choices?.[0]?.message?.content ?? null;\n } catch {\n return null;\n }\n };\n}\n","/**\n * @module voltlog-io\n * @description Alert middleware — checks logs against rules and triggers alerts.\n * @universal Works in all environments.\n * @example\n * ```ts\n * import { createLogger, consoleTransport, alertMiddleware } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [consoleTransport()],\n * middleware: [\n * alertMiddleware([\n * {\n * name: 'error-spike',\n * when: (entry) => entry.level >= 50, // ERROR+\n * threshold: 10,\n * windowMs: 60_000,\n * cooldownMs: 300_000,\n * onAlert: async (entries) => {\n * await sendEmail({ subject: `${entries.length} errors in 1 min` });\n * },\n * },\n * {\n * name: 'callerror-alert',\n * when: (entry) => entry.meta.messageType === 'CALLERROR',\n * threshold: 5,\n * windowMs: 60_000,\n * onAlert: (entries) => sendSlackNotification(entries),\n * },\n * ]),\n * ],\n * });\n * ```\n */\n\nimport type { AlertRule, LogEntry, LogMiddleware } from \"../core/types.js\";\n\ninterface AlertState<TMeta> {\n entries: LogEntry<TMeta>[];\n lastFired: number;\n}\n\n/**\n * Create an alert middleware that evaluates rules against every log entry.\n *\n * Alerts are non-blocking — `onAlert` failures are silently caught\n * to never interfere with the logging pipeline.\n */\nexport function alertMiddleware<TMeta = Record<string, unknown>>(\n rules: AlertRule<TMeta>[],\n): LogMiddleware<TMeta> {\n const states = new Map<string, AlertState<TMeta>>();\n\n // Initialize state for each rule\n for (const rule of rules) {\n states.set(rule.name, { entries: [], lastFired: -Infinity });\n }\n\n return (entry: LogEntry<TMeta>, next) => {\n const now = entry.timestamp;\n\n for (const rule of rules) {\n if (!rule.when(entry)) continue;\n\n // biome-ignore lint/style/noNonNullAssertion: Initialized in constructor\n const state = states.get(rule.name)!;\n const windowMs = rule.windowMs ?? Infinity;\n const threshold = rule.threshold ?? 1;\n const cooldownMs = rule.cooldownMs ?? 0;\n\n // Trim entries outside the window\n if (Number.isFinite(windowMs)) {\n state.entries = state.entries.filter(\n (e) => now - e.timestamp < windowMs,\n );\n }\n\n state.entries.push(entry);\n\n // Check if threshold is met and cooldown has passed\n if (\n state.entries.length >= threshold &&\n now - state.lastFired >= cooldownMs\n ) {\n const alertEntries = [...state.entries];\n state.entries = [];\n state.lastFired = now;\n\n // Fire alert asynchronously — never block the pipeline\n try {\n const result = rule.onAlert(alertEntries);\n if (result && typeof (result as Promise<void>).catch === \"function\") {\n (result as Promise<void>).catch(() => {\n /* Alert callback errors swallowed */\n });\n }\n } catch {\n /* Sync alert errors swallowed */\n }\n }\n }\n\n // Always pass through — alerts don't drop entries\n next(entry);\n };\n}\n","/**\n * @module voltlog-io\n * @description AsyncLocalStorage context middleware — automatically propagates\n * context across async boundaries without manual child logger threading.\n *\n * @server-only Requires Node.js 16+ (AsyncLocalStorage).\n *\n * @example\n * ```ts\n * import { createLogger, prettyTransport } from 'voltlog-io';\n * import { asyncContextMiddleware } from 'voltlog-io';\n *\n * const asyncCtx = asyncContextMiddleware();\n *\n * const logger = createLogger({\n * middleware: [asyncCtx.middleware],\n * transports: [prettyTransport()],\n * });\n *\n * // In Express middleware — set context once\n * app.use((req, res, next) => {\n * asyncCtx.runInContext({ requestId: req.id, userId: req.user?.id }, next);\n * });\n *\n * // Anywhere downstream — context is automatic\n * async function processOrder(orderId: string) {\n * logger.info('Processing', { orderId });\n * // → auto-includes: { requestId: 'req-123', userId: 'u-42', orderId: '...' }\n *\n * await chargePayment(orderId);\n * logger.info('Payment charged');\n * // → still has requestId and userId, even after await!\n * }\n * ```\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface AsyncContextResult<TMeta = Record<string, unknown>> {\n /** Middleware to add to your logger — injects async context into entries */\n middleware: LogMiddleware<TMeta>;\n\n /**\n * Run a function with the given context.\n * All logs within the function (and any async calls it makes) will\n * automatically include this context in their metadata.\n */\n runInContext: (context: Record<string, unknown>, fn: () => void) => void;\n\n /**\n * Get the current async context, or undefined if not inside a runInContext call.\n */\n getContext: () => Record<string, unknown> | undefined;\n\n /**\n * Update the current async context by merging new values.\n * Only works inside a runInContext call.\n */\n updateContext: (updates: Record<string, unknown>) => void;\n}\n\n/**\n * Creates an AsyncLocalStorage-based context system.\n *\n * Returns a middleware (to add to the logger) and helper functions\n * to manage the async context.\n *\n * How it works:\n * 1. Call `runInContext({ requestId, userId }, handler)` at the start of a request\n * 2. The middleware automatically injects that context into every log entry\n * 3. Context propagates across await, setTimeout, Promise.then, etc.\n * 4. Each request gets its own isolated context (no cross-contamination)\n */\nexport function asyncContextMiddleware<\n TMeta = Record<string, unknown>,\n>(): AsyncContextResult<TMeta> {\n const storage = new AsyncLocalStorage<Record<string, unknown>>();\n\n const middleware: LogMiddleware<TMeta> = (entry, next) => {\n const ctx = storage.getStore();\n if (ctx) {\n // Merge async context into entry's metadata\n const meta = entry.meta as Record<string, unknown>;\n for (const [key, value] of Object.entries(ctx)) {\n // Don't overwrite explicitly provided metadata\n if (!(key in meta)) {\n meta[key] = value;\n }\n }\n\n // Also set correlationId if present in context and not already set\n if (ctx.requestId && !entry.correlationId) {\n entry.correlationId = String(ctx.requestId);\n }\n }\n next(entry);\n };\n\n return {\n middleware,\n\n runInContext(context: Record<string, unknown>, fn: () => void): void {\n // If already inside a context, merge with parent\n const parentCtx = storage.getStore();\n const mergedCtx = parentCtx\n ? { ...parentCtx, ...context }\n : { ...context };\n storage.run(mergedCtx, fn);\n },\n\n getContext(): Record<string, unknown> | undefined {\n return storage.getStore();\n },\n\n updateContext(updates: Record<string, unknown>): void {\n const ctx = storage.getStore();\n if (ctx) {\n Object.assign(ctx, updates);\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Correlation ID middleware — adds tracing IDs to logs.\n * @universal Works in all environments.\n * Useful for tracking requests across microservices.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface CorrelationIdOptions {\n /**\n * Header name or meta key to check for existing ID.\n * Default: 'x-correlation-id' (and checks 'traceId', 'correlationId')\n */\n header?: string;\n /**\n * Function to generate new IDs.\n * Default: cuid2\n */\n generator?: () => string;\n}\n\n/**\n * Middleware that ensures every log entry has a correlation ID.\n * checks:\n * 1. entry.correlationId\n * 2. entry.meta.correlationId\n * 3. entry.meta.traceId\n * 4. entry.meta[header]\n *\n * If none found, generates a new one.\n */\nexport function correlationIdMiddleware<TMeta = Record<string, unknown>>(\n options: CorrelationIdOptions = {},\n): LogMiddleware<TMeta> {\n const header = options.header ?? \"x-correlation-id\";\n const generate = options.generator ?? randomUUID;\n\n return (entry, next) => {\n // 1. Check direct property\n if (entry.correlationId) {\n return next(entry);\n }\n\n // 2. Check meta\n const meta = entry.meta as Record<string, unknown>;\n let id =\n (meta.correlationId as string) ||\n (meta.traceId as string) ||\n (meta[header] as string);\n\n // 3. Generate if missing\n if (!id) {\n id = generate();\n }\n\n // 4. Assign\n entry.correlationId = id;\n\n // Also ensure it's in meta for visibility if desired (optional, but good for transports that only dump meta)\n // We won't overwrite existing keys to avoid side effects, but we can standardize on correlationId\n if (!meta.correlationId) {\n // cast to allows assignment\n (entry.meta as Record<string, unknown>).correlationId = id;\n }\n\n next(entry);\n };\n}\n","import type { LogMiddleware } from \"../core/types.js\";\n\n/**\n * Helper to create a type-safe middleware function.\n *\n * @example\n * const myMiddleware = createMiddleware((entry, next) => {\n * entry.meta.foo = 'bar';\n * next(entry);\n * });\n */\nexport function createMiddleware<TMeta = Record<string, unknown>>(\n fn: LogMiddleware<TMeta>,\n): LogMiddleware<TMeta> {\n return fn;\n}\n","/**\n * @module voltlog-io\n * @description Deduplication middleware — groups identical logs in a time window.\n * @universal Works in all environments.\n */\n\nimport type { LogEntry, LogMiddleware } from \"../core/types.js\";\n\nexport interface DeduplicationOptions<TMeta = Record<string, unknown>> {\n /**\n * Time window in ms to group logs.\n * Logs matching the key within this window will be aggregated.\n * Default: 1000ms\n */\n windowMs?: number;\n /**\n * Function to generate a unique key for deduplication.\n * Default: uses `entry.message` + `entry.level`\n */\n keyFn?: (entry: LogEntry<TMeta>) => string;\n}\n\ninterface DedupState<TMeta> {\n entry: LogEntry<TMeta>;\n count: number;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Deduplication middleware.\n * Buffers logs for `windowMs`. If identical logs arrive, increments a counter.\n * Emits the log once at the end of the window with `meta.duplicateCount`.\n */\nexport function deduplicationMiddleware<TMeta = Record<string, unknown>>(\n options: DeduplicationOptions<TMeta> = {},\n): LogMiddleware<TMeta> {\n const windowMs = options.windowMs ?? 1000;\n const keyFn =\n options.keyFn ??\n ((e) => `${e.level}:${e.message}:${e.error?.message ?? \"\"}`);\n\n const buffer = new Map<string, DedupState<TMeta>>();\n\n return (entry, next) => {\n const key = keyFn(entry);\n\n if (buffer.has(key)) {\n // Duplicate found\n // biome-ignore lint/style/noNonNullAssertion: Guarded by buffer.has check\n const state = buffer.get(key)!;\n state.count++;\n return; // Drop this instance\n }\n\n // New entry\n const timer = setTimeout(() => {\n const state = buffer.get(key);\n if (state) {\n buffer.delete(key);\n if (state.count > 1) {\n state.entry.meta = {\n ...state.entry.meta,\n duplicateCount: state.count,\n };\n }\n next(state.entry);\n }\n }, windowMs);\n\n buffer.set(key, {\n entry,\n count: 1,\n timer,\n });\n };\n}\n","/**\n * @module voltlog-io\n * @description Heap usage middleware — adds memory stats to logs.\n * @universal Works in all environments, but only adds data in Node.js/Bun/Deno.\n * Checks for `process.memoryUsage` presence before execution.\n */\n\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface HeapUsageOptions {\n /**\n * Field name to store memory stats in `entry.meta`.\n * Default: 'memory'\n */\n fieldName?: string;\n}\n\n/**\n * Adds `rss`, `heapTotal`, and `heapUsed` to log metadata.\n * Only works in environments where `process.memoryUsage` is available (Node.js/Bun/Deno).\n */\nexport function heapUsageMiddleware<TMeta = Record<string, unknown>>(\n options: HeapUsageOptions = {},\n): LogMiddleware<TMeta> {\n const fieldName = options.fieldName ?? \"memory\";\n\n return (entry, next) => {\n if (typeof process !== \"undefined\" && process.memoryUsage) {\n const memory = process.memoryUsage();\n entry.meta = {\n ...entry.meta,\n [fieldName]: {\n rss: memory.rss,\n heapTotal: memory.heapTotal,\n heapUsed: memory.heapUsed,\n },\n };\n }\n next(entry);\n };\n}\n","/**\n * @module voltlog-io\n * @description Universal HTTP middleware builder. Allows integration with Express, Fastify, exact node:http, etc.\n * without introducing third-party framework dependencies.\n */\n\nimport type { Logger } from \"../core/types.js\";\n\n/**\n * Extracts essential logic from a given raw HTTP Request object\n * regardless of the underlying framework (Express, Fastify, etc.)\n */\nexport interface HttpRequestMapper<TReq = any> {\n getMethod: (req: TReq) => string;\n getUrl: (req: TReq) => string;\n getIp?: (req: TReq) => string | undefined;\n getUserAgent?: (req: TReq) => string | undefined;\n /** Extract a particular header value */\n getHeader?: (req: TReq, name: string) => string | undefined;\n}\n\n/**\n * Extracts essential logic from a given raw HTTP Response object\n */\nexport interface HttpResponseMapper<TRes = any> {\n getStatusCode: (res: TRes) => number;\n /** Execute callback when response is fully sent to the client */\n onFinish: (res: TRes, callback: () => void) => void;\n}\n\n/**\n * Options for configuring the HTTP access logger\n */\nexport interface HttpLoggerOptions<TReq = any, TRes = any> {\n reqMapper: HttpRequestMapper<TReq>;\n resMapper: HttpResponseMapper<TRes>;\n /** Log level to use for HTTP requests (default: INFO) */\n level?: \"TRACE\" | \"DEBUG\" | \"INFO\" | \"WARN\" | \"ERROR\" | \"FATAL\";\n /** Extract specific dynamic context values from the request to add to meta */\n extractContext?: (req: TReq, res: TRes) => Record<string, unknown>;\n /** Function to skip logging certain requests (e.g., /health) */\n skip?: (req: TReq) => boolean;\n}\n\n/**\n * Common mappers for the raw Node.js `http.IncomingMessage` and `http.ServerResponse`.\n * Since Express and many others extend these objects, they work universally.\n */\nexport const nodeHttpMappers = {\n req: {\n getMethod: (req: any) => req.method || \"UNKNOWN\",\n getUrl: (req: any) => req.originalUrl || req.url || \"/\",\n getIp: (req: any) =>\n req.ip ||\n req.socket?.remoteAddress ||\n req.headers?.[\"x-forwarded-for\"] ||\n undefined,\n getUserAgent: (req: any) => req.headers?.[\"user-agent\"] || undefined,\n getHeader: (req: any, name: string) => req.headers?.[name] || undefined,\n } as HttpRequestMapper,\n res: {\n getStatusCode: (res: any) => res.statusCode || 200,\n onFinish: (res: any, callback: () => void) => {\n // Connect to standard Node events\n if (typeof res.on === \"function\") {\n res.on(\"finish\", callback);\n res.on(\"close\", callback); // Handles client disconnects\n } else {\n // Fallback for weird runtimes\n callback();\n }\n },\n } as HttpResponseMapper,\n};\n\n/**\n * Creates a generic HTTP logging middleware function.\n * Use this inside Express, Fastify, or custom routers.\n *\n * @example\n * ```ts\n * import express from 'express';\n * import { createHttpLogger, nodeHttpMappers, createLogger } from 'voltlog-io';\n *\n * const app = express();\n * const logger = createLogger();\n *\n * const httpLogger = createHttpLogger(logger, {\n * reqMapper: nodeHttpMappers.req,\n * resMapper: nodeHttpMappers.res,\n * });\n *\n * app.use((req, res, next) => {\n * httpLogger(req, res);\n * next();\n * });\n * ```\n */\nexport function createHttpLogger<TReq = any, TRes = any>(\n logger: Logger,\n options: HttpLoggerOptions<TReq, TRes>,\n) {\n const {\n reqMapper,\n resMapper,\n level = \"INFO\",\n skip,\n extractContext,\n } = options;\n\n return (req: TReq, res: TRes) => {\n if (skip?.(req)) {\n return;\n }\n\n const startTime = performance.now();\n let finished = false;\n\n // Attach listener for request completion\n resMapper.onFinish(res, () => {\n if (finished) return;\n finished = true;\n\n const durationMs = Math.round(performance.now() - startTime);\n const statusCode = resMapper.getStatusCode(res);\n const method = reqMapper.getMethod(req);\n const url = reqMapper.getUrl(req);\n\n const meta: Record<string, unknown> = {\n method,\n url,\n statusCode,\n durationMs,\n ip: reqMapper.getIp ? reqMapper.getIp(req) : undefined,\n userAgent: reqMapper.getUserAgent\n ? reqMapper.getUserAgent(req)\n : undefined,\n };\n\n if (extractContext) {\n Object.assign(meta, extractContext(req, res));\n }\n\n // Automatically elevate level for 4xx/5xx responses if default was provided\n let finalLevel = level;\n if (statusCode >= 500) finalLevel = \"ERROR\";\n else if (statusCode >= 400 && level === \"INFO\") finalLevel = \"WARN\";\n\n const methodKey = finalLevel.toLowerCase() as\n | \"info\"\n | \"warn\"\n | \"error\"\n | \"debug\"\n | \"trace\"\n | \"fatal\";\n\n logger[methodKey](\n `${method} ${url} - ${statusCode} (${durationMs}ms)`,\n meta,\n );\n });\n };\n}\n","/**\n * @module voltlog-io\n * @description IP middleware — extracts client IP from headers (x-forwarded-for, etc.).\n * @universal Works in any environment where headers are available in metadata.\n */\n\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface IpMiddlewareOptions {\n /**\n * Field name to store the extracted IP in `entry.meta`.\n * Default: 'ip'\n */\n fieldName?: string;\n\n /**\n * Custom list of headers/keys to check for IP address.\n * Default: ['x-forwarded-for', 'x-real-ip', 'req.ip', 'ip']\n */\n headerKeys?: string[];\n}\n\n/**\n * Extracts IP address from commonly used headers in `entry.meta`.\n */\nexport function ipMiddleware<TMeta = Record<string, unknown>>(\n options: IpMiddlewareOptions = {},\n): LogMiddleware<TMeta> {\n const targetField = options.fieldName ?? \"ip\";\n const keysToCheck = options.headerKeys ?? [\n \"x-forwarded-for\",\n \"x-real-ip\",\n \"req.ip\",\n \"ip\",\n \"x-client-ip\",\n ];\n\n return (entry, next) => {\n const meta = entry.meta as Record<string, unknown>;\n const headers = (meta.headers || {}) as Record<string, unknown>;\n const req = (meta.req || {}) as Record<string, unknown>; // Express/Fastify request object often in meta.req\n\n let foundIp: string | undefined;\n\n // Check meta root, meta.headers, meta.req in order for each key\n for (const key of keysToCheck) {\n // 1. Check meta root\n if (typeof meta[key] === \"string\") {\n foundIp = meta[key] as string;\n break;\n }\n // 2. Check headers\n if (typeof headers[key] === \"string\") {\n foundIp = headers[key] as string;\n break;\n }\n // 3. Check req object properties (e.g. req.ip)\n if (typeof req[key] === \"string\") {\n foundIp = req[key] as string;\n break;\n }\n // Special case for 'req.ip' dotted notation if key is 'req.ip'\n if (key === \"req.ip\" && typeof req.ip === \"string\") {\n foundIp = req.ip as string;\n break;\n }\n }\n\n if (foundIp) {\n // Handle comma-separated X-Forwarded-For (take first)\n const firstIp =\n typeof foundIp === \"string\"\n ? foundIp.split(\",\")[0].trim()\n : String(foundIp);\n\n entry.meta = {\n ...entry.meta,\n [targetField]: firstIp,\n };\n }\n\n next(entry);\n };\n}\n","/**\n * @module voltlog-io\n * @description Level Override middleware — allows forcing a log level for specific requests.\n * @universal Works in all environments.\n * Useful for debugging specific users/requests in production without changing global config.\n */\n\nimport type { LogMiddleware } from \"../core/types.js\";\nimport { LogLevel, type LogLevelName } from \"../core/types.js\";\n\nexport interface LevelOverrideOptions {\n /**\n * Header name or meta key to trigger override.\n * Default: 'x-log-level'\n */\n key?: string;\n /**\n * If true, removes the trigger key from metadata before logging.\n * Default: true\n */\n cleanup?: boolean;\n}\n\n/**\n * Dynamically changes the log entry level if a specific key is found in meta/context.\n * E.g. passing `x-log-level: DEBUG` allows a specific request to bypass INFO filters.\n */\nexport function levelOverrideMiddleware<TMeta = Record<string, unknown>>(\n options: LevelOverrideOptions = {},\n): LogMiddleware<TMeta> {\n const key = options.key ?? \"x-log-level\";\n const cleanup = options.cleanup ?? true;\n\n return (entry, next) => {\n const meta = entry.meta as Record<string, unknown>;\n const context = entry.context as Record<string, unknown> | undefined;\n\n // Check meta, then context, then headers in meta\n const levelName =\n (meta[key] as string) ||\n (context?.[key] as string) ||\n ((meta.headers as Record<string, unknown>)?.[key] as string);\n\n if (levelName && typeof levelName === \"string\") {\n const upperName = levelName.toUpperCase() as LogLevelName;\n if (LogLevel[upperName]) {\n // Upgrade the entry level so it passes transport filters\n // NOTE: This modifies the entry itself, which affects all transports\n entry.level = LogLevel[upperName];\n entry.levelName = upperName;\n\n if (cleanup) {\n delete meta[key];\n if (meta.headers) {\n delete (meta.headers as Record<string, unknown>)[key];\n }\n }\n }\n }\n\n next(entry);\n };\n}\n","/**\n * @module voltlog-io\n * @description OCPP middleware — masks sensitive fields and calculates latency for OCPP messages.\n * @universal Works in all environments.\n *\n * @example\n * ```ts\n * import { createLogger, consoleTransport, ocppMiddleware } from 'voltlog-io';\n * import type { OcppExchangeMeta } from 'voltlog-io';\n *\n * const logger = createLogger<OcppExchangeMeta>({\n * transports: [consoleTransport()],\n * middleware: [ocppMiddleware()],\n * });\n *\n * // The middleware auto-computes payloadSize and adds correlationId to the entry\n * logger.info('Message received', {\n * action: 'BootNotification',\n * messageType: 'CALL',\n * direction: 'IN',\n * });\n * ```\n */\n\nimport type {\n LogEntry,\n LogMiddleware,\n OcppExchangeMeta,\n} from \"../core/types.js\";\n\nexport interface OcppMiddlewareOptions {\n /**\n * Automatically compute payload size from meta if not set.\n * Default: true\n */\n autoPayloadSize?: boolean;\n /**\n * Propagate correlationId from meta to entry.correlationId.\n * Default: true\n */\n propagateCorrelationId?: boolean;\n}\n\n/**\n * Create an OCPP enrichment middleware.\n * Enriches log entries with computed OCPP metadata.\n */\nexport function ocppMiddleware(\n options: OcppMiddlewareOptions = {},\n): LogMiddleware<OcppExchangeMeta> {\n const autoPayloadSize = options.autoPayloadSize ?? true;\n const propagateCorrelationId = options.propagateCorrelationId ?? true;\n\n return (entry: LogEntry<OcppExchangeMeta>, next) => {\n const enriched = { ...entry, meta: { ...entry.meta } };\n\n // Auto-compute payload size\n if (\n autoPayloadSize &&\n enriched.meta.payloadSize === undefined &&\n enriched.meta.action\n ) {\n try {\n enriched.meta.payloadSize = JSON.stringify(enriched.meta).length;\n } catch {\n // Failed to compute — leave undefined\n }\n }\n\n // Propagate correlation ID from OCPP meta to entry-level\n if (\n propagateCorrelationId &&\n enriched.meta.correlationId &&\n !enriched.correlationId\n ) {\n enriched.correlationId = enriched.meta.correlationId;\n }\n\n next(enriched);\n };\n}\n","/**\n * @module voltlog-io\n * @description OpenTelemetry middleware — automatically picks up the active trace context.\n * Works like Winston's @opentelemetry/instrumentation-winston but as a simple middleware.\n *\n * @server-only Requires `@opentelemetry/api` as an optional peer dependency.\n *\n * When the OpenTelemetry SDK is active (e.g., with SigNoz, Jaeger, Grafana Tempo),\n * this middleware automatically injects traceId, spanId, and traceFlags into every log entry.\n *\n * @example\n * ```ts\n * import { createLogger, otelTraceMiddleware, otelTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * middleware: [otelTraceMiddleware()],\n * transports: [\n * otelTransport({\n * endpoint: 'http://localhost:4318',\n * serviceName: 'my-app',\n * }),\n * ],\n * });\n *\n * // If an OTel span is active, every log automatically gets:\n * // { traceId: \"abc123...\", spanId: \"def456...\", traceFlags: 1 }\n * logger.info(\"Processing request\");\n * ```\n */\n\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface OtelTraceMiddlewareOptions {\n /**\n * Custom reference to `@opentelemetry/api` trace module.\n * If not provided, we attempt `require('@opentelemetry/api')` or dynamic import.\n * This allows the middleware to work without a hard dependency.\n */\n traceApi?: {\n trace: {\n getActiveSpan: () => unknown;\n };\n };\n}\n\n/**\n * Middleware that automatically injects OpenTelemetry trace context into log entries.\n *\n * How it works:\n * 1. On each log call, checks for an active OTel span\n * 2. If found, extracts traceId, spanId, and traceFlags\n * 3. Injects them into the log entry's metadata\n *\n * This is the equivalent of Winston's @opentelemetry/instrumentation-winston\n * but implemented as a zero-dependency middleware.\n */\nexport function otelTraceMiddleware<TMeta = Record<string, unknown>>(\n options: OtelTraceMiddlewareOptions = {},\n): LogMiddleware<TMeta> {\n // Try to resolve @opentelemetry/api at middleware creation time\n let traceApi: any = options.traceApi ?? null;\n let resolved = !!traceApi;\n\n if (!resolved) {\n try {\n // Use createRequire to prevent tsup from bundling the optional dep\n // Same pattern as ocpp-ws-io otel plugin\n const { createRequire } = require(\"node:module\");\n const dynamicRequire = createRequire(__filename);\n const api = dynamicRequire(\"@opentelemetry/api\");\n traceApi = api;\n resolved = true;\n } catch {\n // @opentelemetry/api not installed — middleware becomes a no-op\n }\n }\n\n return (entry, next) => {\n if (resolved && traceApi?.trace) {\n try {\n const activeSpan = traceApi.trace.getActiveSpan?.();\n if (activeSpan) {\n const spanContext = activeSpan.spanContext?.();\n if (spanContext) {\n // Inject trace context into metadata\n const meta = entry.meta as Record<string, unknown>;\n meta.traceId = spanContext.traceId;\n meta.spanId = spanContext.spanId;\n meta.traceFlags = spanContext.traceFlags;\n\n // Also set correlationId to traceId for cross-referencing\n if (!entry.correlationId) {\n entry.correlationId = spanContext.traceId;\n }\n }\n }\n } catch {\n // OTel API call failed — skip silently\n }\n }\n\n next(entry);\n };\n}\n","/**\n * @module voltlog-io\n * @description Redaction middleware — masks sensitive data in log entries.\n * @universal Works in all environments.\n *\n * @example\n * ```ts\n * import { createLogger, consoleTransport, redactionMiddleware } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [consoleTransport()],\n * middleware: [redactionMiddleware({ paths: ['password', 'idToken', 'authorization'] })],\n * });\n *\n * logger.info('Auth attempt', { password: 's3cret', user: 'admin' });\n * // → { password: '[REDACTED]', user: 'admin' }\n * ```\n */\n\nimport type { LogEntry, LogMiddleware } from \"../core/types.js\";\n\nconst DEFAULT_REDACT_VALUE = \"[REDACTED]\";\n\nexport interface RedactionOptions {\n /** Field paths to redact (case-insensitive matching) */\n paths: string[];\n /** Replacement value (default: '[REDACTED]') */\n replacement?: string;\n /** Also redact matching keys in nested objects (default: true) */\n deep?: boolean;\n}\n\n/**\n * Create a redaction middleware that replaces sensitive field values.\n */\nexport function redactionMiddleware<TMeta = Record<string, unknown>>(\n options: RedactionOptions,\n): LogMiddleware<TMeta> {\n const paths = new Set(options.paths.map((p) => p.toLowerCase()));\n const replacement = options.replacement ?? DEFAULT_REDACT_VALUE;\n const deep = options.deep ?? true;\n\n function redactObject(obj: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (paths.has(key.toLowerCase())) {\n result[key] = replacement;\n } else if (\n deep &&\n value !== null &&\n typeof value === \"object\" &&\n !Array.isArray(value)\n ) {\n result[key] = redactObject(value as Record<string, unknown>);\n } else {\n result[key] = value;\n }\n }\n return result;\n }\n\n return (entry: LogEntry<TMeta>, next) => {\n const redacted = { ...entry };\n\n if (entry.meta && typeof entry.meta === \"object\") {\n redacted.meta = redactObject(\n entry.meta as Record<string, unknown>,\n ) as TMeta;\n }\n\n if (entry.context && typeof entry.context === \"object\") {\n redacted.context = redactObject(entry.context);\n }\n\n next(redacted);\n };\n}\n","/**\n * @module voltlog-io\n * @description Sampling middleware — rate limits or probabilistically samples logs.\n * @universal Works in all environments.\n * @example\n * ```ts\n * import { createLogger, consoleTransport, samplingMiddleware } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [consoleTransport()],\n * middleware: [\n * samplingMiddleware({\n * keyFn: (entry) => `${entry.meta.action}:${entry.meta.chargePointId}`,\n * maxPerWindow: 10,\n * windowMs: 60_000, // 10 logs per minute per action+CP combo\n * }),\n * ],\n * });\n * ```\n */\n\nimport type { LogEntry, LogMiddleware } from \"../core/types.js\";\n\nexport interface SamplingOptions<TMeta = Record<string, unknown>> {\n /**\n * Function to extract a sampling key from a log entry.\n * Entries with the same key share a rate limit.\n * Default: uses `entry.message`\n */\n keyFn?: (entry: LogEntry<TMeta>) => string;\n /** Maximum entries allowed per key per window (default: 100) */\n maxPerWindow?: number;\n /** Time window in ms (default: 60000 = 1 minute) */\n windowMs?: number;\n /** Probability to keep a log (0 to 1). Default: 1 (keep all) */\n sampleRate?: number;\n /** Logs at this level or higher always pass. Default: 40 (WARN) */\n priorityLevel?: number;\n}\n\ninterface BucketEntry {\n count: number;\n windowStart: number;\n}\n\n/**\n * Create a sampling middleware that drops logs exceeding the rate limit.\n */\nexport function samplingMiddleware<TMeta = Record<string, unknown>>(\n options: SamplingOptions<TMeta> = {},\n): LogMiddleware<TMeta> {\n const keyFn = options.keyFn ?? ((entry: LogEntry<TMeta>) => entry.message);\n const maxPerWindow = options.maxPerWindow ?? 100;\n const windowMs = options.windowMs ?? 60_000;\n const sampleRate = options.sampleRate ?? 1;\n // Default priority to WARN (40) so INFO (30) gets sampled/limited\n const priorityLevel = options.priorityLevel ?? 40;\n\n const buckets = new Map<string, BucketEntry>();\n\n return (entry: LogEntry<TMeta>, next) => {\n // 1. High priority logs always pass\n if (entry.level >= priorityLevel) {\n return next(entry);\n }\n\n // 2. Probabilistic Sampling\n if (sampleRate < 1 && Math.random() > sampleRate) {\n return; // Dropped\n }\n\n // 3. Rate Limiting\n const key = keyFn(entry);\n const now = entry.timestamp;\n\n let bucket = buckets.get(key);\n if (!bucket || now - bucket.windowStart >= windowMs) {\n bucket = { count: 0, windowStart: now };\n buckets.set(key, bucket);\n }\n\n if (bucket.count < maxPerWindow) {\n bucket.count++;\n next(entry);\n }\n // else: Dropped (rate limited)\n\n // Periodic cleanup (simple heuristic)\n if (buckets.size > 2000) {\n // ... cleanup logic ...\n const expireBefore = now - windowMs * 2;\n for (const [k, b] of buckets) {\n if (b.windowStart < expireBefore) {\n buckets.delete(k);\n }\n }\n }\n };\n}\n","/**\n * @module voltlog-io\n * @description User Agent middleware — parses UA strings into browser/os/device info.\n * @universal Works in all environments (Server/Browser).\n * Lightweight regex-based parsing to avoid heavy dependencies.\n */\n\nimport type { LogMiddleware } from \"../core/types.js\";\n\nexport interface UserAgentOptions {\n /**\n * Field to look for user agent string (source).\n * Default checks `entry.meta.userAgent`, `entry.meta['user-agent']`, or context.\n */\n sourceField?: string;\n\n /**\n * Field to store the parsed info (target).\n * Default: 'client'\n */\n targetField?: string;\n}\n\n/**\n * Parses user-agent string into structured data (browser, os).\n */\nexport function userAgentMiddleware<TMeta = Record<string, unknown>>(\n options: UserAgentOptions = {},\n): LogMiddleware<TMeta> {\n const sourceField = options.sourceField;\n const targetField = options.targetField ?? \"client\";\n\n return (entry, next) => {\n const meta = entry.meta as Record<string, unknown>;\n const context = entry.context as Record<string, unknown> | undefined;\n\n // Find UA string\n const ua =\n (sourceField ? (meta[sourceField] as string) : undefined) ||\n (meta.userAgent as string) ||\n (meta[\"user-agent\"] as string) ||\n (context?.userAgent as string) ||\n (context?.[\"user-agent\"] as string);\n\n if (ua) {\n const info = parseUserAgent(ua);\n entry.meta = {\n ...entry.meta,\n [targetField]: info,\n };\n }\n\n next(entry);\n };\n}\n\n// Simple lightweight parser to avoid deps\nfunction parseUserAgent(ua: string) {\n const browser =\n /(opera|chrome|safari|firefox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i.exec(ua) ||\n [];\n let name = browser[1] ? browser[1].toLowerCase() : \"unknown\";\n let version = browser[2] || \"unknown\";\n\n if (/trident/i.test(name)) {\n name = \"ie\";\n } else if (name === \"chrome\") {\n const edge = /edg(e)?\\/(\\d+)/i.exec(ua);\n if (edge) {\n name = \"edge\";\n version = edge[2];\n }\n }\n\n const osResult =\n /(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.exec(\n ua,\n );\n const os = osResult ? osResult[0].toLowerCase() : \"desktop\";\n\n return { browser: name, version, os };\n}\n","/**\n * @module voltlog-io\n * @description Batch transformer — wraps another transformer and buffers logs.\n * @universal Works in all environments.\n *\n * @example\n * ```ts\n * import { createLogger, batchTransport, consoleTransport } from 'voltlog-io';\n *\n * // Batch console output: flush every 100 entries or every 2 seconds\n * const logger = createLogger({\n * transports: [\n * batchTransport(consoleTransport(), { batchSize: 100, flushIntervalMs: 2000 }),\n * ],\n * });\n * ```\n */\n\nimport type { LogEntry, Transport } from \"../core/types.js\";\n\nexport interface BatchTransportOptions {\n /** Number of entries to buffer before flushing (default: 100) */\n batchSize?: number;\n /** Max time in ms to wait before flushing a partial batch (default: 5000) */\n flushIntervalMs?: number;\n}\n\n/**\n * Wrap any transport with batching. Entries are buffered and flushed\n * either when the batch is full or the timer fires.\n */\nexport function batchTransport(\n inner: Transport,\n options: BatchTransportOptions = {},\n): Transport {\n const batchSize = options.batchSize ?? 100;\n const flushIntervalMs = options.flushIntervalMs ?? 5000;\n\n let buffer: LogEntry[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleFlush(): void {\n if (flushTimer) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n doFlush();\n }, flushIntervalMs);\n }\n\n function doFlush(): void {\n if (buffer.length === 0) return;\n const batch = buffer;\n buffer = [];\n for (const entry of batch) {\n try {\n const result = inner.write(entry);\n if (result && typeof (result as Promise<void>).catch === \"function\") {\n (result as Promise<void>).catch(() => {});\n }\n } catch {\n /* Swallowed */\n }\n }\n }\n\n return {\n name: `batch(${inner.name})`,\n level: inner.level,\n write(entry: LogEntry): void {\n buffer.push(entry);\n if (buffer.length >= batchSize) {\n doFlush();\n } else {\n scheduleFlush();\n }\n },\n async flush(): Promise<void> {\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n doFlush();\n await inner.flush?.();\n },\n async close(): Promise<void> {\n await this.flush?.();\n await inner.close?.();\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Browser stream transformer — writes newline-delimited JSON to a WHATWG WritableStream.\n * @browser-only Depends on WHATWG Streams API (typical in Browsers/Edge).\n * Useful for streaming logs in browser environments (e.g. to `fetch` streams or ServiceWorkers).\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface BrowserJsonStreamTransportOptions {\n /**\n * Writable stream to output to.\n * Must be a standard WHATWG WritableStream (available in modern browsers).\n */\n stream: WritableStream<string>;\n /** Per-transport level filter */\n level?: LogLevelName;\n /**\n * Custom serializer. Return the string to write.\n * Default: JSON.stringify(entry) + '\\n'\n */\n serializer?: (entry: LogEntry) => string;\n}\n\n/**\n * Create a JSON stream transport for browsers that writes to a WritableStream.\n */\nexport function browserJsonStreamTransport(\n options: BrowserJsonStreamTransportOptions,\n): Transport {\n const stream = options.stream;\n const writer = stream.getWriter();\n\n const serialize =\n options.serializer ?? ((entry: LogEntry) => `${JSON.stringify(entry)}\\n`);\n\n return {\n name: \"browser-stream\",\n level: options.level,\n async write(entry: LogEntry): Promise<void> {\n try {\n const data = serialize(entry);\n await writer.ready;\n await writer.write(data);\n } catch (err) {\n console.error(\"[voltlog] Failed to write to browser stream\", err);\n }\n },\n async close(): Promise<void> {\n try {\n await writer.close();\n } catch (_err) {\n // Ignore close errors\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Console transformer — writes to `console.log`, `console.error`, etc.\n * @universal Works in all environments. Works in Node.js, browsers, and all runtimes.\n *\n * @example\n * ```ts\n * import { createLogger, consoleTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [consoleTransport()],\n * });\n * ```\n */\n\nimport {\n type LogEntry,\n LogLevel,\n type LogLevelName,\n type Transport,\n} from \"../core/types.js\";\n\nexport interface ConsoleTransportOptions {\n /** Per-transport level filter */\n level?: LogLevelName;\n /**\n * Use appropriate console method per level (console.warn, console.error, etc.).\n * Default: true\n */\n useConsoleLevels?: boolean;\n /**\n * Format the entry before outputting. Return any value that console.log can handle.\n * Default: serializes to JSON string.\n */\n formatter?: (entry: LogEntry) => unknown;\n}\n\n/**\n * Create a console transport. Outputs structured JSON to the console.\n */\nexport function consoleTransport(\n options: ConsoleTransportOptions = {},\n): Transport {\n const useConsoleLevels = options.useConsoleLevels ?? true;\n const formatter =\n options.formatter ?? ((entry: LogEntry) => JSON.stringify(entry));\n\n return {\n name: \"console\",\n level: options.level,\n write(entry: LogEntry): void {\n const output = formatter(entry);\n\n if (!useConsoleLevels) {\n console.log(output);\n return;\n }\n\n if (entry.level >= LogLevel.FATAL) {\n console.error(output);\n } else if (entry.level >= LogLevel.ERROR) {\n console.error(output);\n } else if (entry.level >= LogLevel.WARN) {\n console.warn(output);\n } else if (entry.level >= LogLevel.INFO) {\n console.info(output);\n } else if (entry.level >= LogLevel.DEBUG) {\n console.debug(output);\n } else {\n console.log(output);\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Helper to create custom transports easily.\n * @universal Works in all environments.\n */\n\nimport type { LogEntry, Transport } from \"../core/types.js\";\n\n/**\n * Helper to build a transport without manually defining the object structure.\n *\n * @example\n * const myTransport = createTransport('my-api', async (entry) => {\n * await fetch('https://api.example.com/logs', { body: JSON.stringify(entry) });\n * });\n *\n * @param name - Unique name for the transport\n * @param write - Function to process log entries\n * @param options - Optional overrides (level, flush, close)\n */\nexport function createTransport(\n name: string,\n write: (entry: LogEntry) => void | Promise<void>,\n options: Partial<Omit<Transport, \"name\" | \"write\">> = {},\n): Transport {\n return {\n name,\n write,\n ...options,\n };\n}\n","/**\n * @module voltlog-io\n * @description Datadog transformer — sends logs directly to Datadog Intake API.\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Using this in the browser will expose your API Key.\n * > Recommended for server-side use only.\n */\n\nimport type { LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface DatadogTransportOptions {\n /** Datadog API Key */\n apiKey: string;\n /** Datadog Site (default: datadoghq.com) */\n site?: string; // e.g., datadoghq.eu\n /** Service name tag */\n service?: string;\n /** Source tag (default: nodejs) */\n ddSource?: string;\n /** Hostname override */\n hostname?: string;\n /** Tags (comma separated: env:prod,version:1.0) */\n tags?: string;\n /** Transport level filter */\n level?: LogLevelName;\n}\n\n/**\n * Sends logs to Datadog via HTTP POST.\n * Uses built-in batching (not implemented here for brevity, typically would use batchTransport wrapper).\n * This implementation sends immediately for simplicity or relies on `batchTransport` composition.\n */\nexport function datadogTransport(options: DatadogTransportOptions): Transport {\n const {\n apiKey,\n site = \"datadoghq.com\",\n service,\n ddSource = \"nodejs\",\n hostname,\n tags,\n level,\n } = options;\n const url = `https://http-intake.logs.${site}/api/v2/logs`;\n\n return {\n name: \"datadog\",\n level,\n async write(entry) {\n const payload = {\n ddsource: ddSource,\n ddtags: tags,\n hostname,\n service,\n message: entry.message,\n status: entry.levelName.toLowerCase(), // Datadog uses lowercase status\n ...entry.meta,\n timestamp: entry.timestamp, // Datadog auto-parses this\n };\n\n try {\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"DD-API-KEY\": apiKey,\n },\n body: JSON.stringify(payload),\n });\n } catch (err) {\n console.error(\"[voltlog] Datadog push failed\", err);\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Discord transformer — sends logs to Discord via Webhook.\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Using this in the browser will expose your Webhook URL.\n * > Recommended for server-side use only.\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface DiscordTransportOptions {\n /** Discord Webhook URL */\n webhookUrl: string;\n /** Minimum log level (default: ERROR) */\n level?: LogLevelName;\n /** Username override */\n username?: string;\n /** Avatar URL override */\n avatarUrl?: string;\n}\n\n/**\n * Sends formatted embeds to Discord.\n * Best used for Alerts/Errors.\n */\nexport function discordTransport(options: DiscordTransportOptions): Transport {\n const { webhookUrl, username, avatarUrl, level = \"ERROR\" } = options;\n\n return {\n name: \"discord\",\n level,\n async write(entry) {\n try {\n await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(\n formatDiscordPayload(entry, username, avatarUrl),\n ),\n });\n } catch (_err) {\n // swallow\n }\n },\n };\n}\n\nfunction formatDiscordPayload(\n entry: LogEntry,\n username?: string,\n avatar_url?: string,\n): Record<string, unknown> {\n const color = getLevelColor(entry.level);\n\n return {\n username: username || \"VoltLog\",\n avatar_url,\n embeds: [\n {\n title: `${entry.levelName} - ${entry.message}`,\n color,\n timestamp: new Date(entry.timestamp).toISOString(),\n fields: [\n {\n name: \"Meta\",\n value: `\\`\\`\\`json\\n${JSON.stringify(entry.meta, null, 2).slice(\n 0,\n 1000,\n )}\\n\\`\\`\\``,\n },\n entry.error?.stack\n ? {\n name: \"Stack\",\n value: `\\`\\`\\`js\\n${entry.error.stack.slice(0, 1000)}\\n\\`\\`\\``,\n }\n : null,\n ].filter(Boolean),\n },\n ],\n };\n}\n\nfunction getLevelColor(level: number): number {\n if (level >= 50) return 15158332; // Red (Error)\n if (level >= 40) return 16776960; // Yellow (Warn)\n if (level >= 30) return 3447003; // Blue (Info)\n return 9807270; // Grey/Green\n}\n","/**\n * @module voltlog-io\n * @description File transformer — writes logs to disk with daily rotation and optional size-based rotation.\n * @server-only\n * This transport relies on the `node:fs` module to write files to the local filesystem.\n * Browsers do not have direct access to the filesystem for security reasons.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface FileTransportOptions {\n /** Directory to store logs. Created if missing. */\n dir: string;\n /**\n * Filename pattern. Use `%DATE%` for YYYY-MM-DD.\n * Default: `app-%DATE%.log`\n */\n filename?: string;\n /** Per-transport level filter */\n level?: LogLevelName;\n /**\n * Maximum file size in bytes before rotating to a new file.\n * When exceeded, the current file is renamed with a numeric suffix.\n * Default: no size limit (daily rotation only).\n */\n maxSize?: number;\n}\n\n/**\n * Creates a file transport that writes newline-delimited JSON.\n * Rotates files daily based on the `%DATE%` pattern and optionally by size.\n */\nexport function fileTransport(options: FileTransportOptions): Transport {\n const { dir, level, maxSize } = options;\n const filenamePattern = options.filename ?? \"app-%DATE%.log\";\n\n let currentStream: fs.WriteStream | null = null;\n let currentPath = \"\";\n let currentSize = 0;\n let rotationIndex = 0;\n\n // Date caching — avoid new Date() allocation on every write\n let cachedDate = \"\";\n let cacheExpiry = 0;\n\n function getCachedDate(): string {\n const now = Date.now();\n if (now >= cacheExpiry) {\n cachedDate = new Date(now).toISOString().split(\"T\")[0] as string;\n cacheExpiry = now + 1000; // recompute every second\n }\n return cachedDate;\n }\n\n // Ensure directory exists synchronously on startup\n try {\n fs.mkdirSync(dir, { recursive: true });\n } catch (err) {\n console.error(`[voltlog] Failed to create log directory: ${dir}`, err);\n }\n\n function getPath(sizeRotation = false): string {\n const dateStr = getCachedDate();\n const filename = filenamePattern.replace(\"%DATE%\", dateStr);\n if (sizeRotation && rotationIndex > 0) {\n const ext = path.extname(filename);\n const base = filename.slice(0, -ext.length || undefined);\n return path.join(dir, `${base}.${rotationIndex}${ext}`);\n }\n return path.join(dir, filename);\n }\n\n function openStream(filePath: string): void {\n if (currentStream) {\n currentStream.end();\n }\n currentPath = filePath;\n currentSize = 0;\n currentStream = fs.createWriteStream(filePath, { flags: \"a\" });\n\n // Try to get existing file size for append mode\n try {\n const stat = fs.statSync(filePath);\n currentSize = stat.size;\n } catch {\n // File doesn't exist yet — size stays 0\n }\n\n currentStream.on(\"error\", (err) => {\n console.error(`[voltlog] File write error to ${filePath}:`, err);\n });\n }\n\n function rotate(): void {\n const newPath = getPath();\n if (newPath !== currentPath) {\n rotationIndex = 0;\n openStream(newPath);\n }\n }\n\n // Initialize\n rotate();\n\n return {\n name: \"file\",\n level,\n write(entry: LogEntry): void {\n // Check date-based rotation (uses cached date — very cheap)\n rotate();\n\n const line = `${JSON.stringify(entry)}\\n`;\n\n // Check size-based rotation\n if (maxSize && currentSize + line.length > maxSize) {\n rotationIndex++;\n openStream(getPath(true));\n }\n\n if (currentStream && !currentStream.writableEnded) {\n currentStream.write(line);\n currentSize += line.length;\n }\n },\n async flush(): Promise<void> {\n // No-op for standard fs streams — they handle backpressure internally\n },\n async close(): Promise<void> {\n if (currentStream) {\n return new Promise((resolve) => {\n currentStream?.end(() => resolve());\n });\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description JSON stream transformer — writes newline-delimited JSON to any writable stream.\n * @server-only\n * This transport relies on Node.js-style `Writable` streams (e.g. `process.stdout`, `fs.createWriteStream`).\n * Browser streams (WHATWG Streams API) use a different interface.\n *\n * Useful for file logging, piping to external tools, etc.\n *\n * @example Write to file\n * ```ts\n * import { createWriteStream } from 'node:fs';\n * import { createLogger, jsonStreamTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [\n * jsonStreamTransport({ stream: createWriteStream('./app.log', { flags: 'a' }) }),\n * ],\n * });\n * ```\n *\n * @example Write to stdout\n * ```ts\n * const logger = createLogger({\n * transports: [jsonStreamTransport({ stream: process.stdout })],\n * });\n * ```\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface JsonStreamTransportOptions {\n /** Writable stream to output to (e.g. fs.createWriteStream, process.stdout) */\n stream: NodeJS.WritableStream;\n /** Per-transport level filter */\n level?: LogLevelName;\n /**\n * Custom serializer. Return the string to write.\n * Default: JSON.stringify(entry) + '\\n'\n */\n serializer?: (entry: LogEntry) => string;\n}\n\n/**\n * Create a JSON stream transport that writes newline-delimited JSON.\n */\nexport function jsonStreamTransport(\n options: JsonStreamTransportOptions,\n): Transport {\n const stream = options.stream;\n const serialize =\n options.serializer ?? ((entry: LogEntry) => `${JSON.stringify(entry)}\\n`);\n\n return {\n name: \"json-stream\",\n level: options.level,\n write(entry: LogEntry): void {\n const data = serialize(entry);\n stream.write(data);\n },\n close(): Promise<void> {\n return new Promise((resolve) => {\n if (\"end\" in stream && typeof stream.end === \"function\") {\n stream.end(() => resolve());\n } else {\n resolve();\n }\n });\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Loki transformer — pushes logs to Grafana Loki.\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Using this in the browser exposes auth details and may face CORS issues.\n * > Recommended for server-side use.\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface LokiTransportOptions {\n /** Loki URL (e.g. http://localhost:3100) */\n host: string;\n /** Basic Auth username */\n basicAuthUser?: string;\n /** Basic Auth password/token */\n basicAuthPassword?: string;\n /** Tenant ID (header X-Scope-OrgID) */\n tenantId?: string;\n /** Static labels to attach to every stream (e.g. { app: 'volt-server' }) */\n labels?: Record<string, string>;\n /**\n * Extract dynamic labels from each log entry.\n * These are merged with static labels and used as Loki stream labels.\n * Keep cardinality low — use only a few well-known values.\n * @example (entry) => ({ level: entry.levelName, service: entry.context?.service })\n */\n dynamicLabels?: (entry: LogEntry) => Record<string, string | undefined>;\n /**\n * Include context, error, and correlationId in the Loki log payload.\n * Default: true\n */\n includeMetadata?: boolean;\n /** Transport level filter */\n level?: LogLevelName;\n /** Batch size (default: 10) */\n batchSize?: number;\n /** Batch interval ms (default: 5000) */\n interval?: number;\n /** Enable retry on push failure (default: false) */\n retry?: boolean;\n /** Maximum retries (default: 3) */\n maxRetries?: number;\n}\n\n/**\n * Pushes logs to Grafana Loki via HTTP API.\n * Uses batching to improve performance.\n */\nexport function lokiTransport(options: LokiTransportOptions): Transport {\n const { host, level } = options;\n const staticLabels = options.labels ?? { app: \"voltlog\" };\n const batchSize = options.batchSize ?? 10;\n const interval = options.interval ?? 5000;\n const includeMetadata = options.includeMetadata !== false;\n const retryEnabled = options.retry ?? false;\n const maxRetries = options.maxRetries ?? 3;\n\n const url = `${host.replace(/\\/$/, \"\")}/loki/api/v1/push`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (options.basicAuthUser && options.basicAuthPassword) {\n const creds = btoa(`${options.basicAuthUser}:${options.basicAuthPassword}`);\n headers.Authorization = `Basic ${creds}`;\n }\n\n if (options.tenantId) {\n headers[\"X-Scope-OrgID\"] = options.tenantId;\n }\n\n let buffer: LogEntry[] = [];\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function buildLogLine(entry: LogEntry): string {\n const payload: Record<string, unknown> = {\n level: entry.levelName,\n message: entry.message,\n ...(entry.meta as Record<string, unknown>),\n };\n\n if (includeMetadata) {\n if (entry.correlationId) {\n payload.correlationId = entry.correlationId;\n }\n if (entry.context) {\n payload.context = entry.context;\n }\n if (entry.error) {\n payload.error = entry.error;\n }\n }\n\n return JSON.stringify(payload);\n }\n\n function buildStreams(batch: LogEntry[]): unknown[] {\n if (!options.dynamicLabels) {\n // Simple path — all entries go to one stream\n return [\n {\n stream: staticLabels,\n values: batch.map((e) => [\n String(e.timestamp * 1000000), // Loki wants nanoseconds\n buildLogLine(e),\n ]),\n },\n ];\n }\n\n // Group entries by their label set\n const grouped = new Map<\n string,\n { labels: Record<string, string>; values: [string, string][] }\n >();\n\n for (const entry of batch) {\n const dynamic = options.dynamicLabels(entry);\n const merged: Record<string, string> = { ...staticLabels };\n for (const [k, v] of Object.entries(dynamic)) {\n if (v !== undefined) merged[k] = v;\n }\n\n const key = JSON.stringify(merged);\n let group = grouped.get(key);\n if (!group) {\n group = { labels: merged, values: [] };\n grouped.set(key, group);\n }\n group.values.push([\n String(entry.timestamp * 1000000),\n buildLogLine(entry),\n ]);\n }\n\n return Array.from(grouped.values()).map((g) => ({\n stream: g.labels,\n values: g.values,\n }));\n }\n\n async function pushWithRetry(batch: LogEntry[]): Promise<void> {\n const body = JSON.stringify({ streams: buildStreams(batch) });\n let lastError: unknown;\n\n const attempts = retryEnabled ? maxRetries : 1;\n for (let attempt = 0; attempt < attempts; attempt++) {\n try {\n const response = await fetch(url, { method: \"POST\", headers, body });\n if (response.ok) return;\n\n // Don't retry 4xx (client errors)\n if (response.status >= 400 && response.status < 500) {\n console.error(`[voltlog] Loki push failed: ${response.status}`);\n return;\n }\n lastError = new Error(`Loki HTTP ${response.status}`);\n } catch (err) {\n lastError = err;\n }\n\n // Exponential backoff: 100ms, 200ms, 400ms...\n if (attempt < attempts - 1) {\n await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));\n }\n }\n\n console.error(\"[voltlog] Loki push failed after retries\", lastError);\n }\n\n const doFlush = async () => {\n if (buffer.length === 0) return;\n const batch = buffer;\n buffer = [];\n await pushWithRetry(batch);\n };\n\n const schedule = () => {\n if (!timer) {\n timer = setTimeout(() => {\n timer = null;\n doFlush();\n }, interval);\n }\n };\n\n return {\n name: \"loki\",\n level,\n write(entry) {\n buffer.push(entry);\n if (buffer.length >= batchSize) {\n if (timer) clearTimeout(timer);\n timer = null;\n doFlush();\n } else {\n schedule();\n }\n },\n async flush() {\n if (timer) clearTimeout(timer);\n await doFlush();\n },\n async close() {\n await this.flush?.();\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description OpenTelemetry OTLP transport — sends logs via OTLP HTTP/JSON protocol.\n * @server-only Uses `fetch()` to send logs to an OTLP-compatible collector.\n *\n * Compatible with: SigNoz, Jaeger, Grafana Alloy, OpenTelemetry Collector, and any OTLP endpoint.\n *\n * @example\n * ```ts\n * import { createLogger, otelTraceMiddleware, otelTransport } from 'voltlog-io';\n *\n * // SigNoz Cloud\n * const logger = createLogger({\n * middleware: [otelTraceMiddleware()],\n * transports: [\n * otelTransport({\n * endpoint: 'https://ingest.signoz.io',\n * headers: { 'signoz-access-token': 'YOUR_TOKEN' },\n * serviceName: 'my-app',\n * }),\n * ],\n * });\n *\n * // Self-hosted OTel Collector\n * const logger2 = createLogger({\n * transports: [\n * otelTransport({\n * endpoint: 'http://localhost:4318', // OTLP HTTP port\n * serviceName: 'volt-csms',\n * resource: {\n * 'deployment.environment': 'production',\n * 'service.version': '2.0.0',\n * },\n * }),\n * ],\n * });\n *\n * logger.info('Request processed', { userId: 'u-42' });\n * // → Sent to SigNoz with traceId, spanId, severity, resource attributes\n * ```\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface OtelTransportOptions {\n /**\n * OTLP HTTP endpoint (e.g. http://localhost:4318 or https://ingest.signoz.io).\n * The `/v1/logs` path is appended automatically.\n */\n endpoint: string;\n /** Service name reported to the collector */\n serviceName: string;\n /** Additional resource attributes (e.g. deployment.environment, service.version) */\n resource?: Record<string, string>;\n /** Extra headers (e.g. signoz-access-token, Authorization) */\n headers?: Record<string, string>;\n /** Transport level filter */\n level?: LogLevelName;\n /** Batch size before flush (default: 20) */\n batchSize?: number;\n /** Batch interval in ms (default: 5000) */\n interval?: number;\n}\n\n// Map voltlog-io levels to OTel severity numbers\n// https://opentelemetry.io/docs/specs/otel/logs/data-model/#severity-fields\nconst OTEL_SEVERITY_MAP: Record<string, { number: number; text: string }> = {\n TRACE: { number: 1, text: \"TRACE\" },\n DEBUG: { number: 5, text: \"DEBUG\" },\n INFO: { number: 9, text: \"INFO\" },\n WARN: { number: 13, text: \"WARN\" },\n ERROR: { number: 17, text: \"ERROR\" },\n FATAL: { number: 21, text: \"FATAL\" },\n};\n\n/**\n * Creates an OTLP HTTP/JSON transport for OpenTelemetry-compatible backends.\n *\n * Sends logs using the OTLP Logs protocol:\n * https://opentelemetry.io/docs/specs/otlp/#otlphttp-request\n */\nexport function otelTransport(options: OtelTransportOptions): Transport {\n const { endpoint, serviceName, level, resource = {} } = options;\n const batchSize = options.batchSize ?? 20;\n const interval = options.interval ?? 5000;\n\n const url = `${endpoint.replace(/\\/$/, \"\")}/v1/logs`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n };\n\n // Build resource attributes once\n const resourceAttributes = [\n { key: \"service.name\", value: { stringValue: serviceName } },\n ...Object.entries(resource).map(([key, val]) => ({\n key,\n value: { stringValue: val },\n })),\n ];\n\n let buffer: LogEntry[] = [];\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function toOtlpLogRecord(entry: LogEntry): Record<string, unknown> {\n const severity =\n OTEL_SEVERITY_MAP[entry.levelName] ?? OTEL_SEVERITY_MAP.INFO;\n\n // Build attributes from metadata\n const attributes: Array<{ key: string; value: Record<string, unknown> }> =\n [];\n\n if (entry.meta && typeof entry.meta === \"object\") {\n for (const [key, val] of Object.entries(entry.meta)) {\n if (key === \"traceId\" || key === \"spanId\" || key === \"traceFlags\")\n continue;\n if (val !== undefined && val !== null) {\n attributes.push({\n key,\n value:\n typeof val === \"number\"\n ? { intValue: val }\n : { stringValue: String(val) },\n });\n }\n }\n }\n\n // Add context as attributes\n if (entry.context) {\n for (const [key, val] of Object.entries(entry.context)) {\n if (val !== undefined && val !== null) {\n attributes.push({\n key: `context.${key}`,\n value: { stringValue: String(val) },\n });\n }\n }\n }\n\n // Add error info\n if (entry.error) {\n attributes.push({\n key: \"error.message\",\n value: { stringValue: entry.error.message },\n });\n if (entry.error.name) {\n attributes.push({\n key: \"error.type\",\n value: { stringValue: entry.error.name },\n });\n }\n if (entry.error.stack) {\n attributes.push({\n key: \"error.stack\",\n value: { stringValue: entry.error.stack },\n });\n }\n }\n\n const record: Record<string, unknown> = {\n timeUnixNano: String(entry.timestamp * 1_000_000), // ms → ns\n severityNumber: severity?.number,\n severityText: severity?.text,\n body: { stringValue: entry.message },\n attributes,\n };\n\n // Attach trace context if available (from otelTraceMiddleware)\n const meta = entry.meta as Record<string, unknown> | undefined;\n if (meta?.traceId) {\n record.traceId = meta.traceId;\n }\n if (meta?.spanId) {\n record.spanId = meta.spanId;\n }\n if (meta?.traceFlags !== undefined) {\n record.flags = meta.traceFlags;\n }\n\n return record;\n }\n\n async function sendBatch(batch: LogEntry[]): Promise<void> {\n const payload = {\n resourceLogs: [\n {\n resource: { attributes: resourceAttributes },\n scopeLogs: [\n {\n scope: { name: \"voltlog-io\" },\n logRecords: batch.map(toOtlpLogRecord),\n },\n ],\n },\n ],\n };\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n console.error(\n `[voltlog] OTLP push failed: ${response.status} ${response.statusText}`,\n );\n }\n } catch (err) {\n console.error(\"[voltlog] OTLP push failed\", err);\n }\n }\n\n function flush(): void {\n if (buffer.length === 0) return;\n const batch = buffer;\n buffer = [];\n sendBatch(batch).catch(() => {});\n }\n\n function schedule(): void {\n if (!timer) {\n timer = setTimeout(() => {\n timer = null;\n flush();\n }, interval);\n }\n }\n\n return {\n name: \"otel\",\n level,\n write(entry: LogEntry): void {\n buffer.push(entry);\n if (buffer.length >= batchSize) {\n if (timer) clearTimeout(timer);\n timer = null;\n flush();\n } else {\n schedule();\n }\n },\n async flush(): Promise<void> {\n if (timer) clearTimeout(timer);\n timer = null;\n if (buffer.length === 0) return;\n const batch = buffer;\n buffer = [];\n await sendBatch(batch);\n },\n async close(): Promise<void> {\n await this.flush?.();\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Pretty transformer — human-readable colored output with OCPP exchange formatting.\n * @universal Works in all environments (uses ANSI codes, supported by many browser consoles or stripped).\n *\n * @example Dev mode\n * ```ts\n * import { createLogger, prettyTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [prettyTransport()],\n * });\n *\n * logger.info('Server started', { port: 9000 });\n * // → ℹ 2024-01-15T10:30:00.000Z INFO Server started { port: 9000 }\n * ```\n *\n * @example OCPP exchange logs\n * ```ts\n * logger.info('OCPP exchange', {\n * chargePointId: 'CP-101',\n * action: 'BootNotification',\n * messageType: 'CALL',\n * direction: 'IN',\n * latencyMs: 34,\n * status: 'Accepted',\n * });\n * // → ⚡ CP-101 → BootNotification [IN] CALL\n * // → ✔ Accepted (34ms)\n * ```\n */\n\nimport {\n type LogEntry,\n LogLevel,\n type LogLevelName,\n type OcppExchangeMeta,\n type Transport,\n} from \"../core/types.js\";\n\nexport interface PrettyTransportOptions {\n /** Per-transport level filter */\n level?: LogLevelName;\n /** Show timestamps (default: true) */\n timestamps?: boolean;\n /** Use colors in output (default: true) */\n colors?: boolean;\n /** Hide meta object in output (default: false) */\n hideMeta?: boolean;\n /** Pretty-print meta object with indentation (default: false) */\n prettyMeta?: boolean;\n}\n\n// ─── ANSI Color Codes ────────────────────────────────────────────\n\nconst RESET = \"\\x1b[0m\";\nconst DIM = \"\\x1b[2m\";\nconst BOLD = \"\\x1b[1m\";\n\nconst COLORS: Record<string, string> = {\n TRACE: \"\\x1b[90m\", // gray\n DEBUG: \"\\x1b[36m\", // cyan\n INFO: \"\\x1b[32m\", // green\n WARN: \"\\x1b[33m\", // yellow\n ERROR: \"\\x1b[31m\", // red\n FATAL: \"\\x1b[35;1m\", // bold magenta\n};\n\nconst ICONS: Record<string, string> = {\n TRACE: \"🔍\",\n DEBUG: \"🐛\",\n INFO: \"ℹ\",\n WARN: \"⚠\",\n ERROR: \"✖\",\n FATAL: \"💀\",\n};\n\nconst EXCHANGE_ICONS: Record<string, string> = {\n CALL: \"⚡\",\n CALLRESULT: \"✔\",\n CALLERROR: \"🚨\",\n};\n\nconst DIRECTION_ARROWS: Record<string, string> = {\n IN: \"→\",\n OUT: \"←\",\n};\n\n/**\n * Create a pretty-print transport for dev/debug use.\n * Includes OCPP exchange log prettification.\n */\nexport function prettyTransport(\n options: PrettyTransportOptions = {},\n): Transport {\n const showTimestamps = options.timestamps ?? true;\n const useColors = options.colors ?? true;\n const hideMeta = options.hideMeta ?? false;\n const prettyMeta = options.prettyMeta ?? false;\n\n function colorize(text: string, color: string): string {\n return useColors ? `${color}${text}${RESET}` : text;\n }\n\n function formatExchange(entry: LogEntry): string | null {\n const meta = entry.meta as unknown as OcppExchangeMeta;\n if (!meta || !meta.action || !meta.messageType) return null;\n\n const icon = EXCHANGE_ICONS[meta.messageType] ?? \"•\";\n const arrow = DIRECTION_ARROWS[meta.direction ?? \"IN\"] ?? \"→\";\n const cpId = meta.chargePointId ?? \"unknown\";\n const action = meta.action;\n const msgType = meta.messageType;\n const dir = meta.direction ?? \"\";\n\n let line = `${icon} ${colorize(cpId, BOLD)} ${arrow} ${colorize(\n action,\n BOLD,\n )} [${dir}] ${colorize(msgType, DIM)}`;\n\n // Second line for response info\n if (meta.status || meta.latencyMs !== undefined) {\n const statusIcon = meta.messageType === \"CALLERROR\" ? \"❌\" : \"✔\";\n const status = meta.status ?? \"\";\n const latency =\n meta.latencyMs !== undefined ? `(${meta.latencyMs}ms)` : \"\";\n line += `\\n${statusIcon} ${status} ${colorize(latency, DIM)}`;\n }\n\n return line;\n }\n\n function formatStandard(entry: LogEntry): string {\n const icon = ICONS[entry.levelName] ?? \"•\";\n const levelColor = COLORS[entry.levelName] ?? \"\";\n const level = colorize(entry.levelName.padEnd(5), levelColor);\n const ts = showTimestamps\n ? `${colorize(new Date(entry.timestamp).toISOString(), DIM)} `\n : \"\";\n\n let line = `${icon} ${ts}${level} ${entry.message}`;\n\n // Add context\n if (entry.context && Object.keys(entry.context).length > 0) {\n const kvStr = Object.entries(entry.context)\n .map(\n ([k, v]) => `${k}:${typeof v === \"string\" ? v : JSON.stringify(v)}`,\n )\n .join(\" \");\n line += ` ${colorize(kvStr, DIM)}`;\n }\n\n // Add meta (only non-OCPP fields or all if no exchange)\n if (\n !hideMeta &&\n entry.meta &&\n Object.keys(entry.meta as Record<string, unknown>).length > 0\n ) {\n if (prettyMeta) {\n const metaEntries = Object.entries(\n entry.meta as Record<string, unknown>,\n );\n const parts = metaEntries.map(([key, value]) => {\n const valStr =\n typeof value === \"string\" ? value : JSON.stringify(value);\n return `${colorize(`${key}:`, DIM)}${valStr}`;\n });\n line += ` ${parts.join(\" \")}`;\n } else {\n const kvStr = Object.entries(entry.meta as Record<string, unknown>)\n .map(\n ([k, v]) => `${k}:${typeof v === \"string\" ? v : JSON.stringify(v)}`,\n )\n .join(\" \");\n line += ` ${colorize(kvStr, DIM)}`;\n }\n }\n\n // Add error\n if (entry.error) {\n line += `\\n ${colorize(\n `${entry.error.name ?? \"Error\"}: ${entry.error.message}`,\n COLORS.ERROR ?? \"\",\n )}`;\n if (entry.error.stack) {\n line += `\\n${colorize(entry.error.stack, DIM)}`;\n }\n }\n\n return line;\n }\n\n return {\n name: \"pretty\",\n level: options.level,\n write(entry: LogEntry): void {\n // Try exchange format first\n const exchangeOutput = formatExchange(entry);\n if (exchangeOutput) {\n console.log(exchangeOutput);\n return;\n }\n\n // Fall back to standard pretty format\n const output = formatStandard(entry);\n\n if (entry.level >= LogLevel.ERROR) {\n console.error(output);\n } else if (entry.level >= LogLevel.WARN) {\n console.warn(output);\n } else {\n console.log(output);\n }\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Redis Streams transformer — publishes log entries to a Redis Stream.\n * @server-only\n * Designed for standard Redis clients (like `ioredis`) which use TCP sockets (unavailable in browsers).\n * For HTTP-based Redis (e.g. Upstash), use a custom transformer or `webhookTransport`.\n *\n * Users can then subscribe (XREAD/XREADGROUP) for real-time dashboards, monitoring, etc.\n *\n * Requires `ioredis` — user brings their own client instance (no hard dep).\n *\n * @example Basic\n * ```ts\n * import Redis from 'ioredis';\n * import { createLogger, redisTransport } from 'voltlog-io';\n *\n * const redis = new Redis();\n * const logger = createLogger({\n * transports: [\n * redisTransport({ client: redis, streamKey: 'logs:ocpp' }),\n * ],\n * });\n *\n * logger.info('CP connected', { chargePointId: 'CP-101' });\n * // → XADD logs:ocpp * level INFO message \"CP connected\" ...\n * ```\n *\n * @example With TTL and max stream length\n * ```ts\n * redisTransport({\n * client: redis,\n * streamKey: 'logs:ocpp',\n * maxLen: 10_000, // keep last 10k entries (approximate trim)\n * })\n * ```\n *\n * @example Subscribing (consumer side)\n * ```ts\n * // In your dashboard / monitoring service:\n * const redis = new Redis();\n *\n * // Read new entries from the stream\n * const entries = await redis.xread('BLOCK', 0, 'STREAMS', 'logs:ocpp', '$');\n *\n * // Or use consumer groups for at-least-once delivery:\n * await redis.xgroup('CREATE', 'logs:ocpp', 'dashboard', '$', 'MKSTREAM');\n * const entries = await redis.xreadgroup(\n * 'GROUP', 'dashboard', 'worker-1',\n * 'BLOCK', 0, 'STREAMS', 'logs:ocpp', '>'\n * );\n * ```\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\n/**\n * Minimal interface for the Redis client methods we need.\n * Compatible with `ioredis` — user provides their own instance.\n */\nexport interface RedisClient {\n xadd(...args: (string | number)[]): Promise<string | null>;\n quit?(): Promise<void>;\n}\n\nexport interface RedisTransportOptions {\n /** Redis client instance (e.g. `new Redis()` from ioredis) */\n client: RedisClient;\n /** Redis Stream key to publish to (default: 'logs') */\n streamKey?: string;\n /**\n * Max approximate stream length — older entries are trimmed.\n * Uses MAXLEN ~ (approximate) to keep the stream bounded.\n * Default: no limit.\n */\n maxLen?: number;\n /** Per-transport level filter */\n level?: LogLevelName;\n /**\n * Custom field mapper — convert LogEntry to flat key-value pairs for Redis.\n * Default: serializes the entire entry as JSON under a single 'data' field.\n */\n fieldMapper?: (entry: LogEntry) => Record<string, string>;\n}\n\n/**\n * Create a Redis Streams transport.\n *\n * Publishes each log entry via XADD to a Redis Stream.\n * Fire-and-forget — errors are silently swallowed to avoid blocking.\n */\nexport function redisTransport(options: RedisTransportOptions): Transport {\n const { client, streamKey = \"logs\", maxLen, level } = options;\n\n const fieldMapper = options.fieldMapper ?? defaultFieldMapper;\n\n function defaultFieldMapper(entry: LogEntry): Record<string, string> {\n return {\n id: entry.id,\n level: String(entry.level),\n levelName: entry.levelName,\n message: entry.message,\n timestamp: String(entry.timestamp),\n data: JSON.stringify({\n meta: entry.meta,\n context: entry.context,\n correlationId: entry.correlationId,\n error: entry.error,\n }),\n };\n }\n\n return {\n name: \"redis\",\n level,\n write(entry: LogEntry): void {\n const fields = fieldMapper(entry);\n const args: (string | number)[] = [streamKey];\n\n // Approximate trimming to bound stream length\n if (maxLen) {\n args.push(\"MAXLEN\", \"~\", maxLen);\n }\n\n args.push(\"*\"); // auto-generate stream entry ID\n\n // Flatten fields into [key, value, key, value, ...]\n for (const [key, value] of Object.entries(fields)) {\n args.push(key, value);\n }\n\n // Fire-and-forget\n client.xadd(...args).catch(() => {\n /* Swallowed — never crash the app */\n });\n },\n async close(): Promise<void> {\n // Don't close the user's Redis client — they own it\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Ring buffer transport — stores last N log entries in memory for on-demand retrieval.\n * @universal Works in all environments.\n *\n * Useful for debugging, in-app log viewers, and diagnostic endpoints.\n *\n * @example\n * ```ts\n * import { createLogger, ringBufferTransport } from 'voltlog-io';\n *\n * const ring = ringBufferTransport({ maxSize: 500 });\n * const logger = createLogger({ transports: [ring] });\n *\n * logger.info('Hello');\n * logger.error('Oops', new Error('fail'));\n *\n * // Retrieve buffered entries\n * const entries = ring.getEntries();\n * const errors = ring.getEntries({ level: 'ERROR' });\n * ```\n */\n\nimport { resolveLevel } from \"../core/levels.js\";\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface RingBufferTransportOptions {\n /** Maximum number of entries to keep (default: 1000) */\n maxSize?: number;\n /** Per-transport level filter */\n level?: LogLevelName;\n}\n\nexport interface RingBufferQueryOptions {\n /** Filter by minimum level */\n level?: LogLevelName;\n /** Return only entries after this timestamp */\n since?: number;\n /** Maximum entries to return (default: all) */\n limit?: number;\n}\n\nexport interface RingBufferTransport extends Transport {\n /** Retrieve buffered log entries with optional filtering */\n getEntries(query?: RingBufferQueryOptions): LogEntry[];\n /** Clear all buffered entries */\n clear(): void;\n /** Get current buffer size */\n size: number;\n}\n\n/**\n * Creates a ring buffer (circular buffer) transport.\n * Stores the most recent `maxSize` entries in memory.\n * Oldest entries are evicted when the buffer is full.\n */\nexport function ringBufferTransport(\n options: RingBufferTransportOptions = {},\n): RingBufferTransport {\n const maxSize = options.maxSize ?? 1000;\n const buffer: LogEntry[] = [];\n let head = 0;\n let count = 0;\n\n return {\n name: \"ring-buffer\",\n level: options.level,\n\n write(entry: LogEntry): void {\n if (count < maxSize) {\n buffer.push(entry);\n count++;\n } else {\n buffer[head] = entry;\n }\n head = (head + 1) % maxSize;\n },\n\n getEntries(query?: RingBufferQueryOptions): LogEntry[] {\n // Reconstruct ordered array (oldest → newest)\n let entries: LogEntry[];\n if (count < maxSize) {\n entries = buffer.slice();\n } else {\n entries = [...buffer.slice(head), ...buffer.slice(0, head)];\n }\n\n // Apply filters\n if (query?.level) {\n const minLevel = resolveLevel(query.level);\n entries = entries.filter((e) => e.level >= minLevel);\n }\n if (query?.since) {\n const since = query.since;\n entries = entries.filter((e) => e.timestamp >= since);\n }\n if (query?.limit) {\n entries = entries.slice(-query.limit);\n }\n\n return entries;\n },\n\n clear(): void {\n buffer.length = 0;\n head = 0;\n count = 0;\n },\n\n get size() {\n return count;\n },\n };\n}\n","/**\n * @module voltlog-io\n * @description Sentry transformer — pushes errors to Sentry.\n * @universal Works in both Server and Browser (depends on provided Sentry instance).\n */\n\nimport type { LogLevelName, Transport } from \"../core/types.js\";\n\n/**\n * Minimal Sentry client interface to avoid hard dependency on @sentry/* packages.\n * Users pass their Sentry instance (e.g. `import * as Sentry from '@sentry/node'`).\n */\nexport interface SentryInstance {\n captureException(exception: unknown, hint?: unknown): string;\n captureMessage(message: string, level?: unknown): string;\n addBreadcrumb(breadcrumb: unknown): void;\n Severity?: unknown; // To check severity enum if present\n}\n\nexport interface SentryTransportOptions {\n /** Configured Sentry instance/hub */\n sentry: SentryInstance;\n /** Minimum level to trigger captureException (default: ERROR) */\n errorLevel?: LogLevelName;\n /** Minimum level to send breadcrumbs (default: INFO) */\n breadcrumbLevel?: LogLevelName;\n}\n\nimport { resolveLevel } from \"../core/levels.js\";\n\n/**\n * Integrates with an existing Sentry instance.\n * - Logs >= errorLevel -> sent as Exceptions\n * - Logs >= breadcrumbLevel -> sent as Breadcrumbs\n */\nexport function sentryTransport(options: SentryTransportOptions): Transport {\n const { sentry } = options;\n const errorLevelValue = resolveLevel(options.errorLevel ?? \"ERROR\");\n const breadcrumbLevelValue = resolveLevel(options.breadcrumbLevel ?? \"INFO\");\n\n return {\n name: \"sentry\",\n write(entry) {\n // 1. Send as Exception if >= errorLevel\n if (entry.level >= errorLevelValue) {\n if (entry.error) {\n sentry.captureException(entry.error, {\n extra: {\n ...entry.meta,\n context: entry.context,\n },\n level: \"error\",\n });\n } else {\n sentry.captureMessage(entry.message, \"error\");\n }\n }\n\n // 2. Always add breadcrumb for context (if >= breadcrumbLevel)\n if (entry.level >= breadcrumbLevelValue) {\n sentry.addBreadcrumb({\n category: \"log\",\n message: entry.message,\n level: mapLevelToSentry(entry.level),\n data: { ...entry.meta, ...entry.context },\n timestamp: entry.timestamp / 1000,\n });\n }\n },\n };\n}\n\nfunction mapLevelToSentry(level: number): string {\n if (level >= 60) return \"fatal\";\n if (level >= 50) return \"error\";\n if (level >= 40) return \"warning\";\n if (level >= 30) return \"info\";\n return \"debug\";\n}\n","/**\n * @module voltlog-io\n * @description Slack transformer — sends rich notifications to Slack via Incoming Webhook.\n * @universal Works in all environments (uses `fetch`).\n *\n * > **Security Note**: Using this in the browser will expose your Webhook URL.\n * > Recommended for server-side use only.\n * Best used with a filter (e.g. ERROR only) or alert middleware.\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface SlackTransportOptions {\n /** Slack Incoming Webhook URL */\n webhookUrl: string;\n /** Filter level (default: ERROR) */\n level?: LogLevelName;\n /** Custom username (overrides webhook default) */\n username?: string;\n /** Custom icon emoji (overrides webhook default) */\n iconEmoji?: string;\n}\n\n/**\n * Sends logs to Slack with formatting.\n * Best suited for ERROR/FATAL logs or specific alerts.\n */\nexport function slackTransport(options: SlackTransportOptions): Transport {\n const { webhookUrl, username, iconEmoji, level } = options;\n\n return {\n name: \"slack\",\n level: level ?? \"ERROR\", // Default to ERROR to prevent spamming\n async write(entry: LogEntry): Promise<void> {\n try {\n const payload = formatSlackMessage(entry, username, iconEmoji);\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n // We don't want to throw and crash the logger, just console.error\n // console.error(`[voltlog] Slack transport failed: ${response.statusText}`);\n }\n } catch (_err) {\n // console.error(`[voltlog] Slack transport error`, err);\n }\n },\n };\n}\n\nfunction formatSlackMessage(\n entry: LogEntry,\n username?: string,\n icon_emoji?: string,\n): Record<string, unknown> {\n const levelEmoji = getLevelEmoji(entry.level);\n const color = getLevelColor(entry.level);\n\n const blocks = [\n {\n type: \"header\",\n text: {\n type: \"plain_text\",\n text: `${levelEmoji} ${entry.levelName}: ${entry.message}`,\n emoji: true,\n },\n },\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: `*Time:* ${new Date(entry.timestamp).toISOString()}`,\n },\n {\n type: \"mrkdwn\",\n text: `*ID:* \\`${entry.id}\\``,\n },\n ],\n },\n ];\n\n // Add correlation ID if present\n if (entry.correlationId) {\n (blocks[1].elements as any[]).push({\n type: \"mrkdwn\",\n text: `*Trace:* \\`${entry.correlationId}\\``,\n });\n }\n\n // Meta section\n if (Object.keys(entry.meta).length > 0) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*Metadata:*\\n\\`\\`\\`${JSON.stringify(entry.meta, null, 2)}\\`\\`\\``,\n emoji: true,\n },\n });\n }\n\n // Error stack\n if (entry.error?.stack) {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*Error Stack:*\\n\\`\\`\\`${entry.error.stack}\\`\\`\\``,\n emoji: true,\n },\n });\n }\n\n return {\n username,\n icon_emoji,\n attachments: [\n {\n color,\n blocks,\n },\n ],\n };\n}\n\nfunction getLevelEmoji(level: number): string {\n if (level >= 60) return \"🔥\"; // FATAL\n if (level >= 50) return \"🚨\"; // ERROR\n if (level >= 40) return \"⚠️\"; // WARN\n if (level >= 30) return \"ℹ️\"; // INFO\n if (level >= 20) return \"🐛\"; // DEBUG\n return \"🔍\"; // TRACE\n}\n\nfunction getLevelColor(level: number): string {\n if (level >= 60) return \"#ff0000\"; // FATAL (Red)\n if (level >= 50) return \"#ff4444\"; // ERROR (Light Red)\n if (level >= 40) return \"#ffbb33\"; // WARN (Orange)\n if (level >= 30) return \"#33b5e5\"; // INFO (Blue)\n if (level >= 20) return \"#99cc00\"; // DEBUG (Green)\n return \"#aa66cc\"; // TRACE (Purple)\n}\n","/**\n * @module voltlog-io\n * @description Webhook transformer — sends logs via HTTP POST.\n * @universal Works in all environments (uses `fetch`).\n * Supports batching for performance.\n *\n * @example\n * ```ts\n * import { createLogger, webhookTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [\n * webhookTransport({\n * url: 'https://my-api.com/logs',\n * headers: { Authorization: 'Bearer token123' },\n * batchSize: 50,\n * flushIntervalMs: 5000,\n * }),\n * ],\n * });\n * ```\n */\n\nimport type { LogEntry, LogLevelName, Transport } from \"../core/types.js\";\n\nexport interface WebhookTransportOptions {\n /** Target URL to POST log entries to */\n url: string;\n /** HTTP method (default: POST) */\n method?: string;\n /** Additional HTTP headers */\n headers?: Record<string, string>;\n /** Number of entries to batch before sending (default: 1 = no batching) */\n batchSize?: number;\n /** Max time in ms to wait before flushing a partial batch (default: 5000) */\n flushIntervalMs?: number;\n /** Per-transport level filter */\n level?: LogLevelName;\n /** Custom body serializer. Default: JSON.stringify({ entries: [...] }) */\n serializer?: (entries: LogEntry[]) => string;\n /** Retry failed requests (default: false) */\n retry?: boolean;\n /** Max retries (default: 3) */\n maxRetries?: number;\n}\n\n/**\n * Create a webhook transport that POSTs log entries to an HTTP endpoint.\n */\nexport function webhookTransport(options: WebhookTransportOptions): Transport {\n const {\n url,\n method = \"POST\",\n headers = {},\n batchSize = 1,\n flushIntervalMs = 5000,\n retry = false,\n maxRetries = 3,\n } = options;\n\n const serialize =\n options.serializer ??\n ((entries: LogEntry[]) =>\n JSON.stringify({\n entries,\n count: entries.length,\n timestamp: Date.now(),\n }));\n\n let buffer: LogEntry[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n async function sendBatch(entries: LogEntry[], attempt = 0): Promise<void> {\n try {\n const response = await fetch(url, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n body: serialize(entries),\n });\n\n if (!response.ok && retry && attempt < maxRetries) {\n // Exponential backoff retry\n const delay = Math.min(1000 * 2 ** attempt, 30000);\n await new Promise((r) => setTimeout(r, delay));\n return sendBatch(entries, attempt + 1);\n }\n } catch {\n if (retry && attempt < maxRetries) {\n const delay = Math.min(1000 * 2 ** attempt, 30000);\n await new Promise((r) => setTimeout(r, delay));\n return sendBatch(entries, attempt + 1);\n }\n /* Final failure — swallowed to avoid crashing the app */\n }\n }\n\n function scheduleFlush(): void {\n if (flushTimer) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n doFlush();\n }, flushIntervalMs);\n }\n\n function doFlush(): void {\n if (buffer.length === 0) return;\n const batch = buffer;\n buffer = [];\n // Fire-and-forget\n sendBatch(batch).catch(() => {});\n }\n\n return {\n name: \"webhook\",\n level: options.level,\n write(entry: LogEntry): void {\n buffer.push(entry);\n\n if (buffer.length >= batchSize) {\n doFlush();\n } else {\n scheduleFlush();\n }\n },\n async flush(): Promise<void> {\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n if (buffer.length > 0) {\n const batch = buffer;\n buffer = [];\n await sendBatch(batch);\n }\n },\n async close(): Promise<void> {\n await this.flush?.();\n },\n };\n}\n"]}