voltlog-io 1.0.0

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.

Potentially problematic release.


This version of voltlog-io might be problematic. Click here for more details.

@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core/logger.ts","../src/core/types.ts","../src/core/levels.ts","../src/core/pipeline.ts","../src/middleware/redaction.ts","../src/middleware/sampling.ts","../src/middleware/ocpp.ts","../src/middleware/alert.ts","../src/middleware/create-middleware.ts","../src/transformers/console.ts","../src/transformers/pretty.ts","../src/transformers/json-stream.ts","../src/transformers/webhook.ts","../src/transformers/batch.ts","../src/transformers/redis.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 * @example Standalone\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 With ocpp-ws-io\n * ```ts\n * import { createLogger, prettyTransport } from 'ocpp-ws-io/logger';\n * ```\n *\n * @example OCPP exchange logging\n * ```ts\n * import { createLogger, prettyTransport, ocppMiddleware } from 'voltlog-io';\n * import type { OcppExchangeMeta } from 'voltlog-io';\n *\n * const logger = createLogger<OcppExchangeMeta>({\n * transports: [prettyTransport()],\n * middleware: [ocppMiddleware()],\n * });\n *\n * const cpLog = logger.child({ chargePointId: 'CP-101' });\n * cpLog.info('Message received', {\n * action: 'BootNotification',\n * messageType: 'CALL',\n * direction: 'IN',\n * });\n * // ⚡ CP-101 → BootNotification [IN] CALL\n * ```\n *\n * @example Alerting\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: (e) => e.level >= 50,\n * threshold: 10,\n * windowMs: 60_000,\n * onAlert: (entries) => sendEmail({ subject: `${entries.length} errors` }),\n * },\n * ]),\n * ],\n * });\n * ```\n *\n * @example Webhook for AI/automation\n * ```ts\n * import { createLogger, webhookTransport } from 'voltlog-io';\n *\n * const logger = createLogger({\n * transports: [\n * webhookTransport({\n * url: 'https://api.example.com/logs',\n * batchSize: 50,\n * flushIntervalMs: 5000,\n * }),\n * ],\n * });\n * ```\n *\n * @example Custom transformer (save to database)\n * ```ts\n * import { createLogger } from 'voltlog-io';\n * import type { Transformer } from 'voltlog-io';\n *\n * const dbTransformer: Transformer = {\n * name: 'postgres',\n * async transform(entry) {\n * await db.insert('logs', entry);\n * },\n * };\n *\n * const logger = createLogger({ transports: [dbTransformer] });\n * ```\n */\n\n// ─── Core ────────────────────────────────────────────────────────\nexport { createLogger } from \"./core/logger.js\";\n\n// ─── Types ───────────────────────────────────────────────────────\nexport {\n LogLevel,\n LogLevelNameMap,\n LogLevelValueMap,\n type LogLevelName,\n type LogLevelValue,\n type LogEntry,\n type LogError,\n type OcppExchangeMeta,\n type Transformer,\n type LogMiddleware,\n type AlertRule,\n type LoggerOptions,\n type Logger,\n} from \"./core/types.js\";\n\n// ─── Level Utilities ─────────────────────────────────────────────\nexport { resolveLevel, shouldLog, shouldIncludeStack } from \"./core/levels.js\";\n\n// ─── Middleware ──────────────────────────────────────────────────\nexport {\n redactionMiddleware,\n type RedactionOptions,\n} from \"./middleware/redaction.js\";\nexport {\n samplingMiddleware,\n type SamplingOptions,\n} from \"./middleware/sampling.js\";\nexport {\n ocppMiddleware,\n type OcppMiddlewareOptions,\n} from \"./middleware/ocpp.js\";\nexport { alertMiddleware } from \"./middleware/alert.js\";\nexport { createMiddleware } from \"./middleware/create-middleware.js\";\n\n// ─── Transformers ────────────────────────────────────────────────\nexport {\n consoleTransport,\n type ConsoleTransportOptions,\n} from \"./transformers/console.js\";\nexport {\n prettyTransport,\n type PrettyTransportOptions,\n} from \"./transformers/pretty.js\";\nexport {\n jsonStreamTransport,\n type JsonStreamTransportOptions,\n} from \"./transformers/json-stream.js\";\nexport {\n webhookTransport,\n type WebhookTransportOptions,\n} from \"./transformers/webhook.js\";\nexport {\n batchTransport,\n type BatchTransportOptions,\n} from \"./transformers/batch.js\";\nexport {\n redisTransport,\n type RedisTransportOptions,\n type RedisClient,\n} from \"./transformers/redis.js\";\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, shouldLog, shouldIncludeStack } from \"./levels.js\";\nimport { composeMiddleware, fanOutToTransformers } from \"./pipeline.js\";\nimport type {\n LogEntry,\n LogError,\n Logger,\n LoggerOptions,\n LogLevelName,\n LogMiddleware,\n Transformer,\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: Transformer<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 addTransformer(transformer: Transformer<TMeta>): void {\n this._transports.push(transformer);\n }\n\n removeTransformer(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 fanOutToTransformers(entry, this._transports, this._level);\n });\n }\n}\n\n// ─── Child Logger ────────────────────────────────────────────────\n\nclass ChildLoggerImpl<\n TMeta = Record<string, unknown>,\n> implements Logger<TMeta> {\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 addTransformer(transformer: Transformer<TMeta>): void {\n this._parent.addTransformer(transformer);\n }\n removeTransformer(name: string): void {\n this._parent.removeTransformer(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 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// ─── Transformer ─────────────────────────────────────────────────\n\n/**\n * A Transformer receives formatted log entries and delivers them\n * to a destination (console, file, webhook, database, etc.).\n *\n * Transformers are async-safe — `transform()` can return a Promise.\n */\nexport interface Transformer<TMeta = Record<string, unknown>> {\n /** Unique name for this transformer */\n name: string;\n /** Optional per-transformer level filter */\n level?: LogLevelName;\n /** Process a log entry */\n transform(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 transformers.\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 /** Transformers for log output */\n transports?: Transformer<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 transformer at runtime */\n addTransformer(transformer: Transformer<TMeta>): void;\n /** Remove a transformer by name */\n removeTransformer(name: string): void;\n /** Add middleware at runtime */\n addMiddleware(middleware: LogMiddleware<TMeta>): void;\n\n /** Flush all transformers */\n flush(): Promise<void>;\n /** Close all transformers gracefully */\n close(): Promise<void>;\n}\n","/**\n * @module voltlog-io\n * @description Log level utilities — filtering, comparison, resolution.\n */\n\nimport { LogLevel, LogLevelNameMap, type LogLevelName } 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 Pipeline — middleware composition and transformer fan-out.\n */\n\nimport { resolveLevel, shouldLog } from \"./levels.js\";\nimport type { LogEntry, LogMiddleware, Transformer } 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 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 transformers, respecting per-transformer level filters.\n * All transformers run concurrently and fire-and-forget for non-blocking logging.\n */\nexport function fanOutToTransformers<TMeta = Record<string, unknown>>(\n entry: LogEntry<TMeta>,\n transformers: Transformer<TMeta>[],\n loggerLevel: number,\n): void {\n for (const t of transformers) {\n const tLevel = t.level ? resolveLevel(t.level) : loggerLevel;\n\n if (!shouldLog(entry.level, tLevel)) continue;\n\n try {\n const result = t.transform(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 Redaction middleware — auto-redacts sensitive fields from log meta and context.\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 { LogMiddleware, LogEntry } 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 logs per key to avoid flooding.\n *\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 { LogMiddleware, LogEntry } 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 OCPP enrichment middleware — auto-enriches log entries with OCPP metadata.\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 LogMiddleware,\n LogEntry,\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 Alert middleware — evaluates configurable rules and fires callbacks.\n *\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 { LogMiddleware, LogEntry, AlertRule } 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 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","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 Console transformer — outputs structured JSON logs to console.\n * 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 LogLevel,\n type LogEntry,\n type LogLevelName,\n type Transformer,\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 transformer. Outputs structured JSON to the console.\n */\nexport function consoleTransport(\n options: ConsoleTransportOptions = {},\n): Transformer {\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 transform(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 Pretty transformer — human-readable colored output with OCPP exchange formatting.\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 LogLevel,\n type LogEntry,\n type LogLevelName,\n type OcppExchangeMeta,\n type Transformer,\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 transformer for dev/debug use.\n * Includes OCPP exchange log prettification.\n */\nexport function prettyTransport(\n options: PrettyTransportOptions = {},\n): Transformer {\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(action, BOLD)} [${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(`${entry.error.name ?? \"Error\"}: ${entry.error.message}`, COLORS[\"ERROR\"] ?? \"\")}`;\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 transform(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 JSON stream transformer — writes newline-delimited JSON to any writable stream.\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, Transformer } 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 transformer that writes newline-delimited JSON.\n */\nexport function jsonStreamTransport(\n options: JsonStreamTransportOptions,\n): Transformer {\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 transform(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 Webhook transformer — sends log entries to external HTTP endpoints.\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, Transformer } 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 transformer that POSTs log entries to an HTTP endpoint.\n */\nexport function webhookTransport(\n options: WebhookTransportOptions,\n): Transformer {\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 * Math.pow(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 * Math.pow(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 transform(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","/**\n * @module voltlog-io\n * @description Batch wrapper — wraps any transformer with buffering and periodic flushing.\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, Transformer } 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 transformer with batching. Entries are buffered and flushed\n * either when the batch is full or the timer fires.\n */\nexport function batchTransport(\n inner: Transformer,\n options: BatchTransportOptions = {},\n): Transformer {\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.transform(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 transform(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 Redis Streams transformer — publishes log entries to a Redis Stream.\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, Transformer } 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 transformer.\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): Transformer {\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 transform(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"],"mappings":"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","names":[]}