@abdokouta/react-logger 1.0.0 → 1.0.1

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.mjs CHANGED
@@ -87,7 +87,7 @@ var LOGGER_CONFIG = /* @__PURE__ */ Symbol("LOGGER_CONFIG");
87
87
  var LoggerService = class {
88
88
  /**
89
89
  * Create a new logger service
90
- *
90
+ *
91
91
  * @param config - Logger configuration
92
92
  */
93
93
  constructor(config) {
@@ -101,20 +101,20 @@ var LoggerService = class {
101
101
  }
102
102
  /**
103
103
  * Get a logger channel instance
104
- *
104
+ *
105
105
  * Returns the specified channel, or the default channel if no name is provided.
106
106
  * Channels are lazily initialized and cached.
107
- *
107
+ *
108
108
  * @param name - Channel name (uses default if not specified)
109
109
  * @returns Logger instance
110
110
  * @throws Error if channel is not configured
111
- *
111
+ *
112
112
  * @example
113
113
  * ```typescript
114
114
  * // Use default channel
115
115
  * const logger = loggerService.channel();
116
116
  * logger.info('Default message');
117
- *
117
+ *
118
118
  * // Use specific channel
119
119
  * const errorLogger = loggerService.channel('errors');
120
120
  * errorLogger.error('Critical error');
@@ -131,10 +131,10 @@ var LoggerService = class {
131
131
  }
132
132
  /**
133
133
  * Log a message at the debug level (default channel)
134
- *
134
+ *
135
135
  * @param message - The log message
136
136
  * @param context - Optional contextual data
137
- *
137
+ *
138
138
  * @example
139
139
  * ```typescript
140
140
  * loggerService.debug('Cache hit', { key: 'user:123' });
@@ -145,10 +145,10 @@ var LoggerService = class {
145
145
  }
146
146
  /**
147
147
  * Log a message at the info level (default channel)
148
- *
148
+ *
149
149
  * @param message - The log message
150
150
  * @param context - Optional contextual data
151
- *
151
+ *
152
152
  * @example
153
153
  * ```typescript
154
154
  * loggerService.info('User logged in', { userId: '123' });
@@ -159,10 +159,10 @@ var LoggerService = class {
159
159
  }
160
160
  /**
161
161
  * Log a message at the warn level (default channel)
162
- *
162
+ *
163
163
  * @param message - The log message
164
164
  * @param context - Optional contextual data
165
- *
165
+ *
166
166
  * @example
167
167
  * ```typescript
168
168
  * loggerService.warn('API rate limit approaching', { remaining: 10 });
@@ -173,10 +173,10 @@ var LoggerService = class {
173
173
  }
174
174
  /**
175
175
  * Log a message at the error level (default channel)
176
- *
176
+ *
177
177
  * @param message - The log message
178
178
  * @param context - Optional contextual data
179
- *
179
+ *
180
180
  * @example
181
181
  * ```typescript
182
182
  * loggerService.error('Database connection failed', { error });
@@ -187,10 +187,10 @@ var LoggerService = class {
187
187
  }
188
188
  /**
189
189
  * Log a message at the fatal level (default channel)
190
- *
190
+ *
191
191
  * @param message - The log message
192
192
  * @param context - Optional contextual data
193
- *
193
+ *
194
194
  * @example
195
195
  * ```typescript
196
196
  * loggerService.fatal('Application crashed', { error, stack });
@@ -201,10 +201,10 @@ var LoggerService = class {
201
201
  }
202
202
  /**
203
203
  * Add persistent context to the default channel
204
- *
204
+ *
205
205
  * @param context - Key-value pairs to add to the shared context
206
206
  * @returns The logger service instance for fluent chaining
207
- *
207
+ *
208
208
  * @example
209
209
  * ```typescript
210
210
  * loggerService
@@ -219,15 +219,15 @@ var LoggerService = class {
219
219
  }
220
220
  /**
221
221
  * Remove keys from the default channel's shared context
222
- *
222
+ *
223
223
  * @param keys - Optional array of context keys to remove
224
224
  * @returns The logger service instance for fluent chaining
225
- *
225
+ *
226
226
  * @example
227
227
  * ```typescript
228
228
  * // Remove specific keys
229
229
  * loggerService.withoutContext(['userId', 'requestId']);
230
- *
230
+ *
231
231
  * // Clear all context
232
232
  * loggerService.withoutContext();
233
233
  * ```
@@ -238,7 +238,7 @@ var LoggerService = class {
238
238
  }
239
239
  /**
240
240
  * Get all transporters from the default channel
241
- *
241
+ *
242
242
  * @returns Array of transporter instances
243
243
  */
244
244
  getTransporters() {
@@ -246,7 +246,7 @@ var LoggerService = class {
246
246
  }
247
247
  /**
248
248
  * Get the default channel name
249
- *
249
+ *
250
250
  * @returns Default channel name
251
251
  */
252
252
  getDefaultChannelName() {
@@ -254,7 +254,7 @@ var LoggerService = class {
254
254
  }
255
255
  /**
256
256
  * Get all configured channel names
257
- *
257
+ *
258
258
  * @returns Array of channel names
259
259
  */
260
260
  getChannelNames() {
@@ -262,7 +262,7 @@ var LoggerService = class {
262
262
  }
263
263
  /**
264
264
  * Check if a channel is configured
265
- *
265
+ *
266
266
  * @param name - Channel name
267
267
  * @returns True if the channel is configured
268
268
  */
@@ -271,7 +271,7 @@ var LoggerService = class {
271
271
  }
272
272
  /**
273
273
  * Check if a channel is currently active (cached)
274
- *
274
+ *
275
275
  * @param name - Channel name (uses default if not specified)
276
276
  * @returns True if the channel is active
277
277
  */
@@ -281,7 +281,7 @@ var LoggerService = class {
281
281
  }
282
282
  /**
283
283
  * Get the default channel instance
284
- *
284
+ *
285
285
  * @returns Logger instance
286
286
  * @throws Error if default channel cannot be resolved
287
287
  * @private
@@ -301,9 +301,9 @@ var LoggerService = class {
301
301
  }
302
302
  /**
303
303
  * Resolve a channel by name
304
- *
304
+ *
305
305
  * Creates a new logger instance based on channel configuration.
306
- *
306
+ *
307
307
  * @param name - Channel name
308
308
  * @returns Logger instance
309
309
  * @throws Error if channel is not configured
@@ -651,10 +651,10 @@ var ConsoleTransporter = class {
651
651
  var LoggerModule = class {
652
652
  /**
653
653
  * Configure the logger module
654
- *
654
+ *
655
655
  * @param config - Logger configuration (can be passed directly without defineConfig)
656
656
  * @returns Dynamic module
657
- *
657
+ *
658
658
  * @example
659
659
  * ```typescript
660
660
  * LoggerModule.forRoot({
@@ -682,7 +682,7 @@ var LoggerModule = class {
682
682
  }
683
683
  /**
684
684
  * Process configuration to add default transporters if needed
685
- *
685
+ *
686
686
  * @param config - Raw configuration
687
687
  * @returns Processed configuration with defaults
688
688
  * @private
@@ -720,7 +720,7 @@ LoggerModule = __decorateClass([
720
720
  Module({})
721
721
  ], LoggerModule);
722
722
 
723
- // src/config/logger.config.ts
723
+ // src/utils/define-config.util.ts
724
724
  function defineConfig(config) {
725
725
  return config;
726
726
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/enums/log-level.enum.ts","../src/services/logger.service.ts","../src/logger.ts","../src/constants/tokens.constant.ts","../src/logger.module.ts","../src/formatters/pretty.formatter.ts","../src/formatters/json.formatter.ts","../src/formatters/simple.formatter.ts","../src/transporters/silent.transporter.ts","../src/transporters/console.transporter.ts","../src/config/logger.config.ts","../src/transporters/storage.transporter.ts","../src/hooks/use-logger/use-logger.hook.ts","../src/hooks/use-logger-context/use-logger-context.hook.ts"],"sourcesContent":["/**\n * Log Level Enum\n *\n * Defines the severity levels for log messages, ordered from\n * least severe (debug) to most severe (fatal). Inspired by\n * PSR-3 / Laravel logging levels, adapted for client-side use.\n *\n * Each level has a numeric value that enables level-based filtering.\n * A transporter configured with a minimum level will only process\n * messages at or above that severity.\n *\n * @module enums/log-level\n */\nexport enum LogLevel {\n /**\n * Detailed debug information.\n * Use for development-time diagnostics that should not appear in production.\n */\n Debug = 0,\n\n /**\n * Interesting events.\n * Example: User logs in, feature flag evaluated, route navigated.\n */\n Info = 1,\n\n /**\n * Exceptional occurrences that are not errors.\n * Example: Use of deprecated APIs, poor use of an API, recoverable issues.\n */\n Warn = 2,\n\n /**\n * Runtime errors that do not require immediate action but should\n * typically be logged and monitored.\n */\n Error = 3,\n\n /**\n * System is unusable or a critical failure has occurred.\n * Example: Unrecoverable state, data corruption detected.\n */\n Fatal = 4,\n}\n","/**\n * Logger Service\n * \n * Main logger service that handles channels internally (NO separate manager).\n * Provides high-level logging operations with support for multiple channels.\n * \n * **Architecture:**\n * - Manages channels internally (no separate LoggerManager)\n * - Provides unified logging API\n * - Supports multiple channels with different transporters\n * - Lazy channel initialization\n * \n * @module services/logger\n */\n\nimport { Injectable, Inject } from '@abdokouta/react-di';\nimport { Logger } from '../logger';\nimport type { LoggerInterface } from '../interfaces/logger.interface';\nimport type { LoggerServiceInterface } from '../interfaces/logger-service.interface';\nimport { LOGGER_CONFIG } from '../constants/tokens.constant';\nimport type { LoggerModuleOptions } from '../config/logger.config';\n\n/**\n * Logger service implementation\n * \n * Provides a unified logging API with support for multiple channels.\n * Handles channel management internally without a separate manager class.\n * \n * @example\n * ```typescript\n * @Injectable()\n * class UserService {\n * constructor(\n * @Inject(LoggerService) private logger: LoggerService\n * ) {}\n * \n * async createUser(data: UserData) {\n * // Use default channel\n * this.logger.info('Creating user', { email: data.email });\n * \n * try {\n * const user = await this.db.users.create(data);\n * this.logger.info('User created', { userId: user.id });\n * return user;\n * } catch (error) {\n * this.logger.error('Failed to create user', { error });\n * throw error;\n * }\n * }\n * \n * async auditAction(action: string) {\n * // Use specific channel\n * const auditLogger = this.logger.channel('audit');\n * auditLogger.info('User action', { action });\n * }\n * }\n * ```\n */\n@Injectable()\nexport class LoggerService implements LoggerServiceInterface {\n /** Logger configuration */\n private readonly config: LoggerModuleOptions;\n \n /** Cached channel instances */\n private channels: Map<string, LoggerInterface> = new Map();\n \n /** Default channel instance */\n private defaultChannel?: LoggerInterface;\n\n /**\n * Create a new logger service\n * \n * @param config - Logger configuration\n */\n constructor(\n @Inject(LOGGER_CONFIG)\n config: LoggerModuleOptions\n ) {\n this.config = config;\n }\n\n /**\n * Get a logger channel instance\n * \n * Returns the specified channel, or the default channel if no name is provided.\n * Channels are lazily initialized and cached.\n * \n * @param name - Channel name (uses default if not specified)\n * @returns Logger instance\n * @throws Error if channel is not configured\n * \n * @example\n * ```typescript\n * // Use default channel\n * const logger = loggerService.channel();\n * logger.info('Default message');\n * \n * // Use specific channel\n * const errorLogger = loggerService.channel('errors');\n * errorLogger.error('Critical error');\n * ```\n */\n channel(name?: string): LoggerInterface {\n const channelName = name ?? this.config.default;\n \n // Return cached channel if exists\n if (this.channels.has(channelName)) {\n return this.channels.get(channelName) as LoggerInterface;\n }\n \n // Resolve and cache new channel\n const channel = this.resolve(channelName);\n this.channels.set(channelName, channel);\n \n return channel;\n }\n\n /**\n * Log a message at the debug level (default channel)\n * \n * @param message - The log message\n * @param context - Optional contextual data\n * \n * @example\n * ```typescript\n * loggerService.debug('Cache hit', { key: 'user:123' });\n * ```\n */\n debug(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().debug(message, context);\n }\n\n /**\n * Log a message at the info level (default channel)\n * \n * @param message - The log message\n * @param context - Optional contextual data\n * \n * @example\n * ```typescript\n * loggerService.info('User logged in', { userId: '123' });\n * ```\n */\n info(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().info(message, context);\n }\n\n /**\n * Log a message at the warn level (default channel)\n * \n * @param message - The log message\n * @param context - Optional contextual data\n * \n * @example\n * ```typescript\n * loggerService.warn('API rate limit approaching', { remaining: 10 });\n * ```\n */\n warn(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().warn(message, context);\n }\n\n /**\n * Log a message at the error level (default channel)\n * \n * @param message - The log message\n * @param context - Optional contextual data\n * \n * @example\n * ```typescript\n * loggerService.error('Database connection failed', { error });\n * ```\n */\n error(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().error(message, context);\n }\n\n /**\n * Log a message at the fatal level (default channel)\n * \n * @param message - The log message\n * @param context - Optional contextual data\n * \n * @example\n * ```typescript\n * loggerService.fatal('Application crashed', { error, stack });\n * ```\n */\n fatal(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().fatal(message, context);\n }\n\n /**\n * Add persistent context to the default channel\n * \n * @param context - Key-value pairs to add to the shared context\n * @returns The logger service instance for fluent chaining\n * \n * @example\n * ```typescript\n * loggerService\n * .withContext({ requestId: 'abc-123' })\n * .withContext({ userId: 42 })\n * .info('Processing request');\n * ```\n */\n withContext(context: Record<string, unknown>): this {\n this.getDefaultChannel().withContext(context);\n return this;\n }\n\n /**\n * Remove keys from the default channel's shared context\n * \n * @param keys - Optional array of context keys to remove\n * @returns The logger service instance for fluent chaining\n * \n * @example\n * ```typescript\n * // Remove specific keys\n * loggerService.withoutContext(['userId', 'requestId']);\n * \n * // Clear all context\n * loggerService.withoutContext();\n * ```\n */\n withoutContext(keys?: string[]): this {\n this.getDefaultChannel().withoutContext(keys);\n return this;\n }\n\n /**\n * Get all transporters from the default channel\n * \n * @returns Array of transporter instances\n */\n getTransporters() {\n return this.getDefaultChannel().getTransporters();\n }\n\n /**\n * Get the default channel name\n * \n * @returns Default channel name\n */\n getDefaultChannelName(): string {\n return this.config.default;\n }\n\n /**\n * Get all configured channel names\n * \n * @returns Array of channel names\n */\n getChannelNames(): string[] {\n return Object.keys(this.config.channels);\n }\n\n /**\n * Check if a channel is configured\n * \n * @param name - Channel name\n * @returns True if the channel is configured\n */\n hasChannel(name: string): boolean {\n return name in this.config.channels;\n }\n\n /**\n * Check if a channel is currently active (cached)\n * \n * @param name - Channel name (uses default if not specified)\n * @returns True if the channel is active\n */\n isChannelActive(name?: string): boolean {\n const channelName = name ?? this.config.default;\n return this.channels.has(channelName);\n }\n\n /**\n * Get the default channel instance\n * \n * @returns Logger instance\n * @throws Error if default channel cannot be resolved\n * @private\n */\n private getDefaultChannel(): LoggerInterface {\n if (this.defaultChannel) {\n return this.defaultChannel;\n }\n \n // Lazy initialize default channel\n const channelName = this.config.default;\n \n if (this.channels.has(channelName)) {\n this.defaultChannel = this.channels.get(channelName) as LoggerInterface;\n return this.defaultChannel;\n }\n \n this.defaultChannel = this.resolve(channelName);\n this.channels.set(channelName, this.defaultChannel);\n \n return this.defaultChannel;\n }\n\n /**\n * Resolve a channel by name\n * \n * Creates a new logger instance based on channel configuration.\n * \n * @param name - Channel name\n * @returns Logger instance\n * @throws Error if channel is not configured\n * @private\n */\n private resolve(name: string): LoggerInterface {\n const channelConfig = this.config.channels[name];\n \n if (!channelConfig) {\n throw new Error(\n `Logger channel [${name}] is not configured. ` +\n `Available channels: ${Object.keys(this.config.channels).join(', ')}`\n );\n }\n \n // Create logger with channel configuration\n return new Logger(channelConfig);\n }\n}\n","/**\n * Logger Implementation\n * \n * Core logger class that implements the LoggerInterface.\n * Handles log entry creation, context management, and transporter dispatch.\n * \n * @module logger\n */\n\nimport type { LoggerInterface } from './interfaces/logger.interface';\nimport type { LoggerConfig } from './interfaces/logger-config.interface';\nimport type { TransporterInterface } from './interfaces/transporter.interface';\nimport type { LogEntry } from './interfaces/log-entry.interface';\nimport { LogLevel } from './enums/log-level.enum';\n\n/**\n * Logger class\n * \n * Implements the LoggerInterface with support for multiple transporters,\n * contextual logging, and log level filtering.\n */\nexport class Logger implements LoggerInterface {\n private readonly config: LoggerConfig;\n private sharedContext: Record<string, unknown> = {};\n\n constructor(config: LoggerConfig) {\n this.config = config;\n if (config.context) {\n this.sharedContext = { ...config.context };\n }\n }\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.DEBUG, message, context);\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.INFO, message, context);\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.WARN, message, context);\n }\n\n error(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.ERROR, message, context);\n }\n\n fatal(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.FATAL, message, context);\n }\n\n withContext(context: Record<string, unknown>): this {\n this.sharedContext = { ...this.sharedContext, ...context };\n return this;\n }\n\n withoutContext(keys?: string[]): this {\n if (!keys) {\n this.sharedContext = {};\n } else {\n for (const key of keys) {\n delete this.sharedContext[key];\n }\n }\n return this;\n }\n\n getTransporters(): TransporterInterface[] {\n return this.config.transporters;\n }\n\n private log(level: LogLevel, message: string, context?: Record<string, unknown>): void {\n const entry: LogEntry = {\n level,\n message,\n context: { ...this.sharedContext, ...context },\n timestamp: new Date(),\n };\n\n for (const transporter of this.config.transporters) {\n transporter.log(entry);\n }\n }\n}\n","/**\n * Dependency Injection Tokens\n * \n * Defines DI tokens for the logger package.\n * Used with @abdokouta/react-di for dependency injection.\n * \n * @module constants/tokens\n */\n\n/**\n * Logger configuration token\n * \n * Used to inject the logger configuration into the LoggerService.\n * \n * @example\n * ```typescript\n * @Injectable()\n * class LoggerService {\n * constructor(\n * @Inject(LOGGER_CONFIG) private config: LoggerModuleOptions\n * ) {}\n * }\n * ```\n */\nexport const LOGGER_CONFIG = Symbol('LOGGER_CONFIG');\n\n/**\n * Logger service token\n * \n * Used to inject the logger service into other services.\n * \n * @example\n * ```typescript\n * @Injectable()\n * class UserService {\n * constructor(\n * @Inject(LOGGER_SERVICE) private logger: LoggerService\n * ) {}\n * }\n * ```\n */\nexport const LOGGER_SERVICE = Symbol('LOGGER_SERVICE');\n","/**\n * Logger Module\n * \n * Configures the logger system for dependency injection.\n * Provides LoggerService (NO manager) to the application.\n * \n * @module logger.module\n */\n\nimport { Module, forRoot, type DynamicModule } from '@abdokouta/react-di';\n\nimport { LoggerService } from './services/logger.service';\nimport { LOGGER_CONFIG } from './constants/tokens.constant';\nimport type { LoggerModuleOptions } from './config/logger.config';\nimport { SilentTransporter } from './transporters/silent.transporter';\nimport { ConsoleTransporter } from './transporters/console.transporter';\nimport type { LoggerConfig } from './interfaces/logger-config.interface';\n\n/**\n * Logger module\n * \n * Provides LoggerService to the application via dependency injection.\n * The service handles channels internally (NO separate manager).\n * \n * @example\n * ```typescript\n * import { Module } from '@abdokouta/react-di';\n * import { LoggerModule, defineConfig } from '@abdokouta/logger';\n * import { ConsoleTransporter, StorageTransporter } from '@abdokouta/logger';\n * import { LogLevel } from '@abdokouta/logger';\n * \n * @Module({\n * imports: [\n * LoggerModule.forRoot(\n * defineConfig({\n * default: 'console',\n * channels: {\n * console: {\n * transporters: [new ConsoleTransporter()],\n * context: { app: 'my-app' },\n * },\n * storage: {\n * transporters: [new StorageTransporter({ maxEntries: 500 })],\n * },\n * errors: {\n * transporters: [\n * new ConsoleTransporter({ level: LogLevel.Error }),\n * new StorageTransporter({ key: 'error-logs' }),\n * ],\n * },\n * },\n * })\n * ),\n * ],\n * })\n * export class AppModule {}\n * ```\n * \n * @example\n * ```typescript\n * // Using logger in a service\n * import { Injectable, Inject } from '@abdokouta/react-di';\n * import { LoggerService } from '@abdokouta/logger';\n * \n * @Injectable()\n * export class UserService {\n * constructor(\n * @Inject(LoggerService) private logger: LoggerService\n * ) {}\n * \n * async createUser(data: UserData) {\n * this.logger.info('Creating user', { email: data.email });\n * \n * try {\n * const user = await this.db.users.create(data);\n * this.logger.info('User created', { userId: user.id });\n * return user;\n * } catch (error) {\n * this.logger.error('Failed to create user', { error });\n * throw error;\n * }\n * }\n * \n * async auditAction(action: string) {\n * // Use specific channel\n * const auditLogger = this.logger.channel('audit');\n * auditLogger.info('User action', { action });\n * }\n * }\n * ```\n */\n@Module({})\n// biome-ignore lint/complexity/noStaticOnlyClass: Module pattern requires static methods\nexport class LoggerModule {\n /**\n * Configure the logger module\n * \n * @param config - Logger configuration (can be passed directly without defineConfig)\n * @returns Dynamic module\n * \n * @example\n * ```typescript\n * LoggerModule.forRoot({\n * default: 'console',\n * channels: {\n * console: {\n * transporters: [new ConsoleTransporter()],\n * },\n * },\n * })\n * ```\n */\n static forRoot(config: LoggerModuleOptions): DynamicModule {\n // Ensure default channel has transporters\n const processedConfig = LoggerModule.processConfig(config);\n\n return forRoot(LoggerModule, {\n providers: [\n {\n provide: LOGGER_CONFIG,\n useValue: processedConfig,\n },\n LoggerService,\n ],\n exports: [LoggerService, LOGGER_CONFIG],\n });\n }\n\n /**\n * Process configuration to add default transporters if needed\n * \n * @param config - Raw configuration\n * @returns Processed configuration with defaults\n * @private\n */\n private static processConfig(config: LoggerModuleOptions): LoggerModuleOptions {\n const processedChannels: Record<string, LoggerConfig> = {};\n\n // Use for...in loop for better compatibility\n for (const name in config.channels) {\n if (Object.prototype.hasOwnProperty.call(config.channels, name)) {\n const channelConfig = config.channels[name];\n \n // Skip if channelConfig is undefined\n if (!channelConfig) continue;\n \n // If no transporters specified, add default based on channel name\n if (!channelConfig.transporters || channelConfig.transporters.length === 0) {\n if (name === 'silent') {\n processedChannels[name] = {\n ...channelConfig,\n transporters: [new SilentTransporter()],\n };\n } else {\n processedChannels[name] = {\n ...channelConfig,\n transporters: [new ConsoleTransporter()],\n };\n }\n } else {\n processedChannels[name] = channelConfig;\n }\n }\n }\n\n return {\n ...config,\n channels: processedChannels,\n };\n }\n}\n","/**\n * Pretty Formatter\n *\n * A visually rich formatter that produces colorful, emoji-prefixed\n * log output designed for the browser console. Each log level is\n * assigned a distinct emoji and CSS color to make scanning logs\n * effortless during development.\n *\n * This is the default formatter used by the ConsoleTransporter.\n *\n * @module formatters/pretty\n *\n * @example\n * ```ts\n * const formatter = new PrettyFormatter();\n * const output = formatter.format(entry);\n * // => \"🐛 [DEBUG] [14:30:00.000] Hello world {userId: 42}\"\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport type { FormatterInterface, LogEntry } from '@/interfaces';\n\n/**\n * Mapping of log levels to their emoji prefix.\n * Provides instant visual identification of severity in console output.\n */\nconst LEVEL_EMOJI: Record<LogLevel, string> = {\n [LogLevel.Debug]: '🐛',\n [LogLevel.Info]: 'ℹ️',\n [LogLevel.Warn]: '⚠️',\n [LogLevel.Error]: '❌',\n [LogLevel.Fatal]: '💀',\n};\n\n/**\n * Mapping of log levels to their display label.\n * Used in the formatted output string between brackets.\n */\nconst LEVEL_LABEL: Record<LogLevel, string> = {\n [LogLevel.Debug]: 'DEBUG',\n [LogLevel.Info]: 'INFO',\n [LogLevel.Warn]: 'WARN',\n [LogLevel.Error]: 'ERROR',\n [LogLevel.Fatal]: 'FATAL',\n};\n\n/**\n * Mapping of log levels to CSS color strings for browser console styling.\n * These colors are applied via `%c` formatting in `console.log`.\n */\nexport const LEVEL_COLORS: Record<LogLevel, string> = {\n [LogLevel.Debug]: 'color: #8B8B8B',\n [LogLevel.Info]: 'color: #2196F3',\n [LogLevel.Warn]: 'color: #FF9800',\n [LogLevel.Error]: 'color: #F44336',\n [LogLevel.Fatal]:\n 'color: #FFFFFF; background: #F44336; font-weight: bold; padding: 1px 4px; border-radius: 2px',\n};\n\nexport class PrettyFormatter implements FormatterInterface {\n /**\n * Format a log entry into a pretty, human-readable string with\n * emoji prefix, level badge, timestamp, message, and context.\n *\n * The returned string includes `%c` placeholders for CSS styling\n * in the browser console. The ConsoleTransporter is responsible\n * for passing the corresponding style strings.\n *\n * @param entry - The log entry to format.\n * @returns The formatted string with CSS style placeholders.\n */\n format(entry: LogEntry): string {\n const emoji = LEVEL_EMOJI[entry.level];\n const label = LEVEL_LABEL[entry.level];\n const time = this.formatTimestamp(entry.timestamp);\n const contextStr = this.formatContext(entry.context);\n\n return `${emoji} %c[${label}]%c [${time}] ${entry.message}${contextStr}`;\n }\n\n /**\n * Extract a short time string (HH:mm:ss.SSS) from an ISO timestamp.\n *\n * @param timestamp - ISO 8601 timestamp string.\n * @returns A short time-only representation.\n */\n private formatTimestamp(timestamp: string): string {\n try {\n const date = new Date(timestamp);\n const hours = String(date.getHours()).padStart(2, '0');\n const minutes = String(date.getMinutes()).padStart(2, '0');\n const seconds = String(date.getSeconds()).padStart(2, '0');\n const ms = String(date.getMilliseconds()).padStart(3, '0');\n \n return `${hours}:${minutes}:${seconds}.${ms}`;\n } catch {\n return timestamp;\n }\n }\n\n /**\n * Serialize context data into a compact, readable string.\n * Returns an empty string when context is empty to keep output clean.\n *\n * @param context - The context record to serialize.\n * @returns A formatted context string or empty string.\n */\n private formatContext(context: Record<string, unknown>): string {\n const keys = Object.keys(context);\n\n if (keys.length === 0) {\n return '';\n }\n\n try {\n return ` ${JSON.stringify(context)}`;\n } catch {\n return ` [context serialization failed]`;\n }\n }\n}\n","/**\n * JSON Formatter\n *\n * Serializes log entries into compact JSON strings. Ideal for\n * structured logging scenarios where logs need to be parsed\n * programmatically — for example, when sending logs to a remote\n * endpoint or storing them in localStorage for later analysis.\n *\n * @module formatters/json\n *\n * @example\n * ```ts\n * const formatter = new JsonFormatter();\n * const output = formatter.format(entry);\n * // => '{\"level\":\"info\",\"message\":\"User logged in\",\"timestamp\":\"...\",\"context\":{\"userId\":42}}'\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport type { FormatterInterface, LogEntry } from '@/interfaces';\n\n/**\n * Mapping of log levels to their lowercase string representation\n * for JSON output.\n */\nconst LEVEL_STRING: Record<LogLevel, string> = {\n [LogLevel.Debug]: 'debug',\n [LogLevel.Info]: 'info',\n [LogLevel.Warn]: 'warn',\n [LogLevel.Error]: 'error',\n [LogLevel.Fatal]: 'fatal',\n};\n\nexport class JsonFormatter implements FormatterInterface {\n /**\n * Format a log entry as a JSON string.\n *\n * Produces a flat JSON object with level (as string), message,\n * timestamp, and context fields. Context is omitted when empty\n * to keep output compact.\n *\n * @param entry - The log entry to format.\n * @returns A JSON-serialized string representation of the entry.\n */\n format(entry: LogEntry): string {\n const payload: Record<string, unknown> = {\n level: LEVEL_STRING[entry.level],\n message: entry.message,\n timestamp: entry.timestamp,\n };\n\n // Only include context when it has keys to keep JSON compact.\n if (Object.keys(entry.context).length > 0) {\n payload.context = entry.context;\n }\n\n try {\n return JSON.stringify(payload);\n } catch {\n return JSON.stringify({\n level: LEVEL_STRING[entry.level],\n message: entry.message,\n timestamp: entry.timestamp,\n error: 'context serialization failed',\n });\n }\n }\n}\n","/**\n * Simple Formatter\n *\n * A minimal, no-frills formatter that produces plain text log lines\n * without colors, emojis, or CSS styling. Suitable for environments\n * where styled output is not supported or when logs need to be\n * stored as plain text.\n *\n * @module formatters/simple\n *\n * @example\n * ```ts\n * const formatter = new SimpleFormatter();\n * const output = formatter.format(entry);\n * // => \"[DEBUG] [2026-03-28T14:30:00.000Z] Hello world {userId: 42}\"\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport type { FormatterInterface, LogEntry } from '@/interfaces';\n\n/**\n * Mapping of log levels to their uppercase string label.\n */\nconst LEVEL_LABEL: Record<LogLevel, string> = {\n [LogLevel.Debug]: 'DEBUG',\n [LogLevel.Info]: 'INFO',\n [LogLevel.Warn]: 'WARN',\n [LogLevel.Error]: 'ERROR',\n [LogLevel.Fatal]: 'FATAL',\n};\n\nexport class SimpleFormatter implements FormatterInterface {\n /**\n * Format a log entry into a plain text line.\n *\n * Output pattern: `[LEVEL] [timestamp] message {context}`\n *\n * @param entry - The log entry to format.\n * @returns A plain text string with no styling.\n */\n format(entry: LogEntry): string {\n const label = LEVEL_LABEL[entry.level];\n const contextStr = this.formatContext(entry.context);\n\n return `[${label}] [${entry.timestamp}] ${entry.message}${contextStr}`;\n }\n\n /**\n * Serialize context data into a compact string.\n * Returns an empty string when context is empty.\n *\n * @param context - The context record to serialize.\n * @returns A formatted context string or empty string.\n */\n private formatContext(context: Record<string, unknown>): string {\n const keys = Object.keys(context);\n\n if (keys.length === 0) {\n return '';\n }\n\n try {\n return ` ${JSON.stringify(context)}`;\n } catch {\n return ' [context serialization failed]';\n }\n }\n}\n","/**\n * Silent Transporter\n *\n * A no-op transporter that silently discards all log entries.\n * Useful for production environments where logging should be\n * disabled, or for testing scenarios where log output would\n * pollute test results.\n *\n * Equivalent to Laravel's \"null\" log driver.\n *\n * @module transporters/silent\n *\n * @example\n * ```ts\n * const logger = new Logger({\n * transporters: [new SilentTransporter()],\n * });\n * logger.info('This will be silently discarded');\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport { SimpleFormatter } from '@/formatters';\nimport type { FormatterInterface, LogEntry, TransporterInterface } from '@/interfaces';\n\nexport class SilentTransporter implements TransporterInterface {\n /**\nThe formatter instance (maintained for interface compliance). */\n private _formatter: FormatterInterface;\n\n /**\nThe minimum log level (maintained for interface compliance). */\n private _level: LogLevel;\n\n /**\n * Create a new SilentTransporter instance.\n * All parameters are optional and exist only for interface compliance.\n */\n constructor() {\n this._formatter = new SimpleFormatter();\n this._level = LogLevel.Debug;\n }\n\n /**\n * No-op transport method. Silently discards the entry.\n *\n * @param _entry - The log entry (ignored).\n */\n transport(_entry: LogEntry): void {\n // Intentionally empty — this transporter discards all entries.\n }\n\n /**\n * Replace the current formatter.\n *\n * @param formatter - The new formatter instance.\n */\n setFormatter(formatter: FormatterInterface): void {\n this._formatter = formatter;\n }\n\n /**\n * Retrieve the currently assigned formatter.\n *\n * @returns The active formatter instance.\n */\n getFormatter(): FormatterInterface {\n return this._formatter;\n }\n\n /**\n * Get the minimum log level.\n *\n * @returns The current minimum LogLevel threshold.\n */\n getLevel(): LogLevel {\n return this._level;\n }\n\n /**\n * Set the minimum log level.\n *\n * @param level - The new minimum LogLevel threshold.\n */\n setLevel(level: LogLevel): void {\n this._level = level;\n }\n}\n","/**\n * Console Transporter\n *\n * Delivers log entries to the browser's developer console using the\n * appropriate `console.*` method for each severity level. When paired\n * with the PrettyFormatter (the default), output includes CSS-styled\n * level badges with colors and emoji prefixes.\n *\n * This is the primary transporter for development environments.\n *\n * @module transporters/console\n *\n * @example\n * ```ts\n * const transporter = new ConsoleTransporter();\n * transporter.transport(entry); // outputs to console with colors\n *\n * // With custom minimum level:\n * const warnOnly = new ConsoleTransporter({ level: LogLevel.Warn });\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport { LEVEL_COLORS, PrettyFormatter } from '@/formatters';\nimport type { FormatterInterface, LogEntry, TransporterInterface } from '@/interfaces';\n\n/**\n * Configuration options for the ConsoleTransporter.\n */\nexport interface ConsoleTransporterOptions {\n /**\n * The formatter to use for log entries.\n * Defaults to PrettyFormatter if not provided.\n */\n formatter?: FormatterInterface;\n\n /**\n * The minimum log level to transport.\n * Entries below this level are silently ignored.\n *\n * @default LogLevel.Debug\n */\n level?: LogLevel;\n}\n\nexport class ConsoleTransporter implements TransporterInterface {\n /**\nThe formatter used to convert log entries into output strings. */\n private _formatter: FormatterInterface;\n\n /**\nThe minimum log level threshold for this transporter. */\n private _level: LogLevel;\n\n /**\n * Create a new ConsoleTransporter instance.\n *\n * @param options - Optional configuration for formatter and minimum level.\n */\n constructor(options: ConsoleTransporterOptions = {}) {\n this._formatter = options.formatter ?? new PrettyFormatter();\n this._level = options.level ?? LogLevel.Debug;\n }\n\n /**\n * Deliver a log entry to the browser console.\n *\n * Routes the entry to the appropriate `console.*` method based on\n * the log level. When using the PrettyFormatter, CSS styles are\n * applied via `%c` placeholders for colored output.\n *\n * Entries below the configured minimum level are silently skipped.\n *\n * @param entry - The log entry to output.\n */\n transport(entry: LogEntry): void {\n // Skip entries below the minimum level threshold.\n if (entry.level < this._level) {\n return;\n }\n\n const formatted = this._formatter.format(entry);\n const method = this.resolveConsoleMethod(entry.level);\n\n // When using PrettyFormatter, apply CSS styles via %c placeholders.\n if (this._formatter instanceof PrettyFormatter) {\n const levelStyle = LEVEL_COLORS[entry.level];\n const resetStyle = 'color: inherit';\n\n method(formatted, levelStyle, resetStyle);\n } else {\n method(formatted);\n }\n }\n\n /**\n * Replace the current formatter.\n *\n * @param formatter - The new formatter instance to use.\n */\n setFormatter(formatter: FormatterInterface): void {\n this._formatter = formatter;\n }\n\n /**\n * Retrieve the currently assigned formatter.\n *\n * @returns The active formatter instance.\n */\n getFormatter(): FormatterInterface {\n return this._formatter;\n }\n\n /**\n * Get the minimum log level this transporter handles.\n *\n * @returns The current minimum LogLevel threshold.\n */\n getLevel(): LogLevel {\n return this._level;\n }\n\n /**\n * Set the minimum log level this transporter handles.\n *\n * @param level - The new minimum LogLevel threshold.\n */\n setLevel(level: LogLevel): void {\n this._level = level;\n }\n\n /**\n * Resolve the appropriate `console.*` method for a given log level.\n *\n * Maps logger severity levels to the closest browser console method\n * to ensure proper DevTools filtering and visual distinction.\n *\n * @param level - The log level to resolve.\n * @returns The bound console method function.\n */\n private resolveConsoleMethod(level: LogLevel): (...args: unknown[]) => void {\n switch (level) {\n case LogLevel.Debug:\n return console.debug.bind(console);\n case LogLevel.Info:\n return console.info.bind(console);\n case LogLevel.Warn:\n return console.warn.bind(console);\n case LogLevel.Error:\n return console.error.bind(console);\n case LogLevel.Fatal:\n return console.error.bind(console);\n default:\n return console.log.bind(console);\n }\n }\n}\n","/**\n * Logger Module Configuration\n * \n * Defines the configuration interface for the logger module.\n * Supports multiple channels with different transporters and settings.\n * \n * @module config/logger\n */\n\nimport type { LoggerConfig } from '../interfaces/logger-config.interface';\n\n/**\n * Logger module options\n * \n * Configuration for the logger module with support for multiple channels.\n * Each channel can have its own transporters, context, and settings.\n * \n * @example\n * ```typescript\n * const config: LoggerModuleOptions = {\n * default: 'console',\n * channels: {\n * console: {\n * transporters: [new ConsoleTransporter()],\n * context: { app: 'my-app' },\n * },\n * storage: {\n * transporters: [new StorageTransporter()],\n * },\n * errors: {\n * transporters: [\n * new ConsoleTransporter({ level: LogLevel.ERROR }),\n * new StorageTransporter({ key: 'error-logs' }),\n * ],\n * },\n * },\n * };\n * ```\n */\nexport interface LoggerModuleOptions {\n /**\n * Default channel name\n * \n * The channel to use when no specific channel is requested.\n * Must match one of the keys in the channels object.\n */\n default: string;\n\n /**\n * Channel configurations\n * \n * Map of channel names to their configurations.\n * Each channel can have its own transporters and settings.\n */\n channels: Record<string, LoggerConfig>;\n}\n\n/**\n * Helper function to define logger configuration with type safety\n * \n * @param config - Logger module options\n * @returns The same config with proper typing\n * \n * @example\n * ```typescript\n * import { defineConfig } from '@abdokouta/logger';\n * \n * const config = defineConfig({\n * default: 'console',\n * channels: {\n * console: {\n * transporters: [new ConsoleTransporter()],\n * },\n * },\n * });\n * ```\n */\nexport function defineConfig(config: LoggerModuleOptions): LoggerModuleOptions {\n return config;\n}\n","/**\n * Storage Transporter\n *\n * Persists log entries to the browser's localStorage as JSON strings.\n * Useful for capturing logs that survive page reloads, enabling\n * post-mortem debugging or user-submitted bug reports that include\n * recent log history.\n *\n * Entries are stored as a JSON array under a configurable storage key.\n * A maximum entry limit prevents unbounded storage growth.\n *\n * @module transporters/storage\n *\n * @example\n * ```ts\n * const transporter = new StorageTransporter({\n * key: 'app-logs',\n * maxEntries: 200,\n * });\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport { JsonFormatter } from '@/formatters';\nimport type { FormatterInterface, LogEntry, TransporterInterface } from '@/interfaces';\n\n/**\n * Configuration options for the StorageTransporter.\n */\nexport interface StorageTransporterOptions {\n /**\n * The localStorage key under which log entries are stored.\n *\n * @default \"logger:entries\"\n */\n key?: string;\n\n /**\n * Maximum number of entries to retain in storage.\n * When the limit is exceeded, the oldest entries are discarded (FIFO).\n *\n * @default 100\n */\n maxEntries?: number;\n\n /**\n * The formatter to use for serializing log entries.\n * Defaults to JsonFormatter if not provided.\n */\n formatter?: FormatterInterface;\n\n /**\n * The minimum log level to transport.\n * Entries below this level are silently ignored.\n *\n * @default LogLevel.Debug\n */\n level?: LogLevel;\n}\n\n/**\nDefault localStorage key for log storage. */\nconst DEFAULT_STORAGE_KEY = 'logger:entries';\n\n/**\nDefault maximum number of retained entries. */\nconst DEFAULT_MAX_ENTRIES = 100;\n\nexport class StorageTransporter implements TransporterInterface {\n /**\nThe formatter used to convert log entries into storable strings. */\n private _formatter: FormatterInterface;\n\n /**\nThe minimum log level threshold for this transporter. */\n private _level: LogLevel;\n\n /**\nThe localStorage key for persisting entries. */\n private readonly _key: string;\n\n /**\nMaximum number of entries to retain. */\n private readonly _maxEntries: number;\n\n /**\n * Create a new StorageTransporter instance.\n *\n * @param options - Optional configuration for storage key, limits, and formatter.\n */\n constructor(options: StorageTransporterOptions = {}) {\n this._formatter = options.formatter ?? new JsonFormatter();\n this._level = options.level ?? LogLevel.Debug;\n this._key = options.key ?? DEFAULT_STORAGE_KEY;\n this._maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n }\n\n /**\n * Persist a log entry to localStorage.\n *\n * The entry is formatted, appended to the existing entries array,\n * and trimmed to the maximum entry limit. If localStorage is\n * unavailable or full, the error is silently swallowed to avoid\n * crashing the application.\n *\n * @param entry - The log entry to persist.\n */\n transport(entry: LogEntry): void {\n // Skip entries below the minimum level threshold.\n if (entry.level < this._level) {\n return;\n }\n\n try {\n const formatted = this._formatter.format(entry);\n const entries = this.readEntries();\n\n entries.push(formatted);\n\n // Trim oldest entries when the limit is exceeded.\n while (entries.length > this._maxEntries) {\n entries.shift();\n }\n\n localStorage.setItem(this._key, JSON.stringify(entries));\n } catch {\n // Silently swallow storage errors (quota exceeded, unavailable, etc.)\n // to prevent logging from crashing the application.\n }\n }\n\n /**\n * Replace the current formatter.\n *\n * @param formatter - The new formatter instance.\n */\n setFormatter(formatter: FormatterInterface): void {\n this._formatter = formatter;\n }\n\n /**\n * Retrieve the currently assigned formatter.\n *\n * @returns The active formatter instance.\n */\n getFormatter(): FormatterInterface {\n return this._formatter;\n }\n\n /**\n * Get the minimum log level this transporter handles.\n *\n * @returns The current minimum LogLevel threshold.\n */\n getLevel(): LogLevel {\n return this._level;\n }\n\n /**\n * Set the minimum log level this transporter handles.\n *\n * @param level - The new minimum LogLevel threshold.\n */\n setLevel(level: LogLevel): void {\n this._level = level;\n }\n\n /**\n * Clear all stored log entries from localStorage.\n * Useful for manual cleanup or when resetting application state.\n */\n clear(): void {\n try {\n localStorage.removeItem(this._key);\n } catch {\n // Silently swallow if localStorage is unavailable.\n }\n }\n\n /**\n * Retrieve all stored log entries from localStorage.\n *\n * @returns An array of formatted log entry strings.\n */\n getEntries(): string[] {\n return this.readEntries();\n }\n\n /**\n * Read the current entries array from localStorage.\n * Returns an empty array if the key does not exist or parsing fails.\n *\n * @returns The parsed entries array.\n */\n private readEntries(): string[] {\n try {\n const raw = localStorage.getItem(this._key);\n\n if (!raw) {\n return [];\n }\n\n const parsed = JSON.parse(raw);\n\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n }\n}\n","/**\n * useLogger Hook\n * \n * React hook for accessing the logger service in components.\n * Provides access to the default channel or a specific channel.\n * \n * @module hooks/use-logger\n */\n\nimport { useInject } from '@abdokouta/react-di';\nimport { LoggerService } from '@/services/logger.service';\nimport type { LoggerInterface } from '@/interfaces/logger.interface';\n\n/**\n * Access the logger service in React components\n * \n * Returns the logger service instance or a specific channel.\n * \n * @param channelName - Optional channel name (uses default if not specified)\n * @returns Logger instance\n * \n * @example\n * ```typescript\n * import { useLogger } from '@abdokouta/logger';\n * \n * function MyComponent() {\n * const logger = useLogger();\n * \n * const handleClick = () => {\n * logger.info('Button clicked', { timestamp: Date.now() });\n * };\n * \n * return <button onClick={handleClick}>Click me</button>;\n * }\n * ```\n * \n * @example\n * ```typescript\n * // Use specific channel\n * function ErrorBoundary({ error }: { error: Error }) {\n * const errorLogger = useLogger('errors');\n * \n * useEffect(() => {\n * errorLogger.error('Component error', {\n * message: error.message,\n * stack: error.stack,\n * });\n * }, [error]);\n * \n * return <div>Something went wrong</div>;\n * }\n * ```\n */\nexport function useLogger(channelName?: string): LoggerInterface {\n const loggerService = useInject(LoggerService);\n \n if (channelName) {\n return loggerService.channel(channelName);\n }\n \n // Return the service itself which implements LoggerInterface\n return loggerService as LoggerInterface;\n}\n","/**\n * useLoggerContext Hook\n * \n * React hook for managing logger context in components.\n * Automatically adds and removes context based on component lifecycle.\n * \n * @module hooks/use-logger-context\n */\n\nimport { useEffect } from 'react';\nimport { useLogger } from '../use-logger';\n\n/**\n * Manage logger context in React components\n * \n * Automatically adds context when the component mounts and removes it when unmounted.\n * Useful for adding component-specific context to all logs.\n * \n * @param context - Context to add to the logger\n * @param channelName - Optional channel name (uses default if not specified)\n * \n * @example\n * ```typescript\n * import { useLoggerContext } from '@abdokouta/logger';\n * \n * function UserProfile({ userId }: { userId: string }) {\n * // Add userId to all logs in this component\n * useLoggerContext({ userId, component: 'UserProfile' });\n * \n * const logger = useLogger();\n * \n * const handleUpdate = () => {\n * // This log will include { userId, component: 'UserProfile' }\n * logger.info('Updating profile');\n * };\n * \n * return <button onClick={handleUpdate}>Update</button>;\n * }\n * ```\n * \n * @example\n * ```typescript\n * // With specific channel\n * function PaymentForm({ orderId }: { orderId: string }) {\n * useLoggerContext({ orderId, form: 'payment' }, 'payment');\n * \n * const logger = useLogger('payment');\n * \n * const handleSubmit = () => {\n * logger.info('Payment submitted');\n * };\n * \n * return <form onSubmit={handleSubmit}>...</form>;\n * }\n * ```\n */\nexport function useLoggerContext(\n context: Record<string, unknown>,\n channelName?: string\n): void {\n const logger = useLogger(channelName);\n\n useEffect(() => {\n // Add context on mount\n logger.withContext(context);\n\n // Remove context on unmount\n return () => {\n logger.withoutContext(Object.keys(context));\n };\n }, [logger, context]);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAaO,IAAK,WAAL,kBAAKA,cAAL;AAKL,EAAAA,oBAAA,WAAQ,KAAR;AAMA,EAAAA,oBAAA,UAAO,KAAP;AAMA,EAAAA,oBAAA,UAAO,KAAP;AAMA,EAAAA,oBAAA,WAAQ,KAAR;AAMA,EAAAA,oBAAA,WAAQ,KAAR;AA7BU,SAAAA;AAAA,GAAA;;;ACEZ,SAAS,YAAY,cAAc;;;ACM5B,IAAM,SAAN,MAAwC;AAAA,EAI7C,YAAY,QAAsB;AAHlC,wBAAiB;AACjB,wBAAQ,iBAAyC,CAAC;AAGhD,SAAK,SAAS;AACd,QAAI,OAAO,SAAS;AAClB,WAAK,gBAAgB,EAAE,GAAG,OAAO,QAAQ;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,MAAM,SAAiB,SAAyC;AAC9D,SAAK,IAAI,SAAS,OAAO,SAAS,OAAO;AAAA,EAC3C;AAAA,EAEA,KAAK,SAAiB,SAAyC;AAC7D,SAAK,IAAI,SAAS,MAAM,SAAS,OAAO;AAAA,EAC1C;AAAA,EAEA,KAAK,SAAiB,SAAyC;AAC7D,SAAK,IAAI,SAAS,MAAM,SAAS,OAAO;AAAA,EAC1C;AAAA,EAEA,MAAM,SAAiB,SAAyC;AAC9D,SAAK,IAAI,SAAS,OAAO,SAAS,OAAO;AAAA,EAC3C;AAAA,EAEA,MAAM,SAAiB,SAAyC;AAC9D,SAAK,IAAI,SAAS,OAAO,SAAS,OAAO;AAAA,EAC3C;AAAA,EAEA,YAAY,SAAwC;AAClD,SAAK,gBAAgB,EAAE,GAAG,KAAK,eAAe,GAAG,QAAQ;AACzD,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAuB;AACpC,QAAI,CAAC,MAAM;AACT,WAAK,gBAAgB,CAAC;AAAA,IACxB,OAAO;AACL,iBAAW,OAAO,MAAM;AACtB,eAAO,KAAK,cAAc,GAAG;AAAA,MAC/B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,kBAA0C;AACxC,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEQ,IAAI,OAAiB,SAAiB,SAAyC;AACrF,UAAM,QAAkB;AAAA,MACtB;AAAA,MACA;AAAA,MACA,SAAS,EAAE,GAAG,KAAK,eAAe,GAAG,QAAQ;AAAA,MAC7C,WAAW,oBAAI,KAAK;AAAA,IACtB;AAEA,eAAW,eAAe,KAAK,OAAO,cAAc;AAClD,kBAAY,IAAI,KAAK;AAAA,IACvB;AAAA,EACF;AACF;;;AC5DO,IAAM,gBAAgB,uBAAO,eAAe;;;AFmC5C,IAAM,gBAAN,MAAsD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAe3D,YAEE,QACA;AAhBF;AAAA,wBAAiB;AAGjB;AAAA,wBAAQ,YAAyC,oBAAI,IAAI;AAGzD;AAAA,wBAAQ;AAWN,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,QAAQ,MAAgC;AACtC,UAAM,cAAc,QAAQ,KAAK,OAAO;AAGxC,QAAI,KAAK,SAAS,IAAI,WAAW,GAAG;AAClC,aAAO,KAAK,SAAS,IAAI,WAAW;AAAA,IACtC;AAGA,UAAM,UAAU,KAAK,QAAQ,WAAW;AACxC,SAAK,SAAS,IAAI,aAAa,OAAO;AAEtC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,SAAiB,UAAmC,CAAC,GAAS;AAClE,SAAK,kBAAkB,EAAE,MAAM,SAAS,OAAO;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,KAAK,SAAiB,UAAmC,CAAC,GAAS;AACjE,SAAK,kBAAkB,EAAE,KAAK,SAAS,OAAO;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,KAAK,SAAiB,UAAmC,CAAC,GAAS;AACjE,SAAK,kBAAkB,EAAE,KAAK,SAAS,OAAO;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,SAAiB,UAAmC,CAAC,GAAS;AAClE,SAAK,kBAAkB,EAAE,MAAM,SAAS,OAAO;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,SAAiB,UAAmC,CAAC,GAAS;AAClE,SAAK,kBAAkB,EAAE,MAAM,SAAS,OAAO;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,YAAY,SAAwC;AAClD,SAAK,kBAAkB,EAAE,YAAY,OAAO;AAC5C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,eAAe,MAAuB;AACpC,SAAK,kBAAkB,EAAE,eAAe,IAAI;AAC5C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB;AAChB,WAAO,KAAK,kBAAkB,EAAE,gBAAgB;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,wBAAgC;AAC9B,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAA4B;AAC1B,WAAO,OAAO,KAAK,KAAK,OAAO,QAAQ;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,WAAW,MAAuB;AAChC,WAAO,QAAQ,KAAK,OAAO;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,gBAAgB,MAAwB;AACtC,UAAM,cAAc,QAAQ,KAAK,OAAO;AACxC,WAAO,KAAK,SAAS,IAAI,WAAW;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,oBAAqC;AAC3C,QAAI,KAAK,gBAAgB;AACvB,aAAO,KAAK;AAAA,IACd;AAGA,UAAM,cAAc,KAAK,OAAO;AAEhC,QAAI,KAAK,SAAS,IAAI,WAAW,GAAG;AAClC,WAAK,iBAAiB,KAAK,SAAS,IAAI,WAAW;AACnD,aAAO,KAAK;AAAA,IACd;AAEA,SAAK,iBAAiB,KAAK,QAAQ,WAAW;AAC9C,SAAK,SAAS,IAAI,aAAa,KAAK,cAAc;AAElD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,QAAQ,MAA+B;AAC7C,UAAM,gBAAgB,KAAK,OAAO,SAAS,IAAI;AAE/C,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI;AAAA,QACR,mBAAmB,IAAI,4CACA,OAAO,KAAK,KAAK,OAAO,QAAQ,EAAE,KAAK,IAAI,CAAC;AAAA,MACrE;AAAA,IACF;AAGA,WAAO,IAAI,OAAO,aAAa;AAAA,EACjC;AACF;AA7Qa,gBAAN;AAAA,EADN,WAAW;AAAA,EAiBP,0BAAO,aAAa;AAAA,GAhBZ;;;AGlDb,SAAS,QAAQ,eAAmC;;;ACiBpD,IAAM,cAAwC;AAAA,EAC5C,cAAe,GAAG;AAAA,EAClB,aAAc,GAAG;AAAA,EACjB,aAAc,GAAG;AAAA,EACjB,cAAe,GAAG;AAAA,EAClB,cAAe,GAAG;AACpB;AAMA,IAAM,cAAwC;AAAA,EAC5C,cAAe,GAAG;AAAA,EAClB,aAAc,GAAG;AAAA,EACjB,aAAc,GAAG;AAAA,EACjB,cAAe,GAAG;AAAA,EAClB,cAAe,GAAG;AACpB;AAMO,IAAM,eAAyC;AAAA,EACpD,cAAe,GAAG;AAAA,EAClB,aAAc,GAAG;AAAA,EACjB,aAAc,GAAG;AAAA,EACjB,cAAe,GAAG;AAAA,EAClB,cAAe,GACb;AACJ;AAEO,IAAM,kBAAN,MAAoD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYzD,OAAO,OAAyB;AAC9B,UAAM,QAAQ,YAAY,MAAM,KAAK;AACrC,UAAM,QAAQ,YAAY,MAAM,KAAK;AACrC,UAAM,OAAO,KAAK,gBAAgB,MAAM,SAAS;AACjD,UAAM,aAAa,KAAK,cAAc,MAAM,OAAO;AAEnD,WAAO,GAAG,KAAK,OAAO,KAAK,QAAQ,IAAI,KAAK,MAAM,OAAO,GAAG,UAAU;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,WAA2B;AACjD,QAAI;AACF,YAAM,OAAO,IAAI,KAAK,SAAS;AAC/B,YAAM,QAAQ,OAAO,KAAK,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG;AACrD,YAAM,UAAU,OAAO,KAAK,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;AACzD,YAAM,UAAU,OAAO,KAAK,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;AACzD,YAAM,KAAK,OAAO,KAAK,gBAAgB,CAAC,EAAE,SAAS,GAAG,GAAG;AAEzD,aAAO,GAAG,KAAK,IAAI,OAAO,IAAI,OAAO,IAAI,EAAE;AAAA,IAC7C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,cAAc,SAA0C;AAC9D,UAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,QAAI,KAAK,WAAW,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,aAAO,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,IACpC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AChGA,IAAM,eAAyC;AAAA,EAC7C,cAAe,GAAG;AAAA,EAClB,aAAc,GAAG;AAAA,EACjB,aAAc,GAAG;AAAA,EACjB,cAAe,GAAG;AAAA,EAClB,cAAe,GAAG;AACpB;AAEO,IAAM,gBAAN,MAAkD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWvD,OAAO,OAAyB;AAC9B,UAAM,UAAmC;AAAA,MACvC,OAAO,aAAa,MAAM,KAAK;AAAA,MAC/B,SAAS,MAAM;AAAA,MACf,WAAW,MAAM;AAAA,IACnB;AAGA,QAAI,OAAO,KAAK,MAAM,OAAO,EAAE,SAAS,GAAG;AACzC,cAAQ,UAAU,MAAM;AAAA,IAC1B;AAEA,QAAI;AACF,aAAO,KAAK,UAAU,OAAO;AAAA,IAC/B,QAAQ;AACN,aAAO,KAAK,UAAU;AAAA,QACpB,OAAO,aAAa,MAAM,KAAK;AAAA,QAC/B,SAAS,MAAM;AAAA,QACf,WAAW,MAAM;AAAA,QACjB,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AC3CA,IAAMC,eAAwC;AAAA,EAC5C,cAAe,GAAG;AAAA,EAClB,aAAc,GAAG;AAAA,EACjB,aAAc,GAAG;AAAA,EACjB,cAAe,GAAG;AAAA,EAClB,cAAe,GAAG;AACpB;AAEO,IAAM,kBAAN,MAAoD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASzD,OAAO,OAAyB;AAC9B,UAAM,QAAQA,aAAY,MAAM,KAAK;AACrC,UAAM,aAAa,KAAK,cAAc,MAAM,OAAO;AAEnD,WAAO,IAAI,KAAK,MAAM,MAAM,SAAS,KAAK,MAAM,OAAO,GAAG,UAAU;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,cAAc,SAA0C;AAC9D,UAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,QAAI,KAAK,WAAW,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,aAAO,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,IACpC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC3CO,IAAM,oBAAN,MAAwD;AAAA;AAAA;AAAA;AAAA;AAAA,EAa7D,cAAc;AAVd;AAAA;AAAA,wBAAQ;AAIR;AAAA;AAAA,wBAAQ;AAON,SAAK,aAAa,IAAI,gBAAgB;AACtC,SAAK;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,QAAwB;AAAA,EAElC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,WAAqC;AAChD,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,OAAuB;AAC9B,SAAK,SAAS;AAAA,EAChB;AACF;;;AC1CO,IAAM,qBAAN,MAAyD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAc9D,YAAY,UAAqC,CAAC,GAAG;AAXrD;AAAA;AAAA,wBAAQ;AAIR;AAAA;AAAA,wBAAQ;AAQN,SAAK,aAAa,QAAQ,aAAa,IAAI,gBAAgB;AAC3D,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,UAAU,OAAuB;AAE/B,QAAI,MAAM,QAAQ,KAAK,QAAQ;AAC7B;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,WAAW,OAAO,KAAK;AAC9C,UAAM,SAAS,KAAK,qBAAqB,MAAM,KAAK;AAGpD,QAAI,KAAK,sBAAsB,iBAAiB;AAC9C,YAAM,aAAa,aAAa,MAAM,KAAK;AAC3C,YAAM,aAAa;AAEnB,aAAO,WAAW,YAAY,UAAU;AAAA,IAC1C,OAAO;AACL,aAAO,SAAS;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,WAAqC;AAChD,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,OAAuB;AAC9B,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,qBAAqB,OAA+C;AAC1E,YAAQ,OAAO;AAAA,MACb;AACE,eAAO,QAAQ,MAAM,KAAK,OAAO;AAAA,MACnC;AACE,eAAO,QAAQ,KAAK,KAAK,OAAO;AAAA,MAClC;AACE,eAAO,QAAQ,KAAK,KAAK,OAAO;AAAA,MAClC;AACE,eAAO,QAAQ,MAAM,KAAK,OAAO;AAAA,MACnC;AACE,eAAO,QAAQ,MAAM,KAAK,OAAO;AAAA,MACnC;AACE,eAAO,QAAQ,IAAI,KAAK,OAAO;AAAA,IACnC;AAAA,EACF;AACF;;;AL9DO,IAAM,eAAN,MAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBxB,OAAO,QAAQ,QAA4C;AAEzD,UAAM,kBAAkB,aAAa,cAAc,MAAM;AAEzD,WAAO,QAAQ,cAAc;AAAA,MAC3B,WAAW;AAAA,QACT;AAAA,UACE,SAAS;AAAA,UACT,UAAU;AAAA,QACZ;AAAA,QACA;AAAA,MACF;AAAA,MACA,SAAS,CAAC,eAAe,aAAa;AAAA,IACxC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAe,cAAc,QAAkD;AAC7E,UAAM,oBAAkD,CAAC;AAGzD,eAAW,QAAQ,OAAO,UAAU;AAClC,UAAI,OAAO,UAAU,eAAe,KAAK,OAAO,UAAU,IAAI,GAAG;AAC/D,cAAM,gBAAgB,OAAO,SAAS,IAAI;AAG1C,YAAI,CAAC,cAAe;AAGpB,YAAI,CAAC,cAAc,gBAAgB,cAAc,aAAa,WAAW,GAAG;AAC1E,cAAI,SAAS,UAAU;AACrB,8BAAkB,IAAI,IAAI;AAAA,cACxB,GAAG;AAAA,cACH,cAAc,CAAC,IAAI,kBAAkB,CAAC;AAAA,YACxC;AAAA,UACF,OAAO;AACL,8BAAkB,IAAI,IAAI;AAAA,cACxB,GAAG;AAAA,cACH,cAAc,CAAC,IAAI,mBAAmB,CAAC;AAAA,YACzC;AAAA,UACF;AAAA,QACF,OAAO;AACL,4BAAkB,IAAI,IAAI;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,UAAU;AAAA,IACZ;AAAA,EACF;AACF;AA7Ea,eAAN;AAAA,EAFN,OAAO,CAAC,CAAC;AAAA,GAEG;;;AMhBN,SAAS,aAAa,QAAkD;AAC7E,SAAO;AACT;;;AClBA,IAAM,sBAAsB;AAI5B,IAAM,sBAAsB;AAErB,IAAM,qBAAN,MAAyD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsB9D,YAAY,UAAqC,CAAC,GAAG;AAnBrD;AAAA;AAAA,wBAAQ;AAIR;AAAA;AAAA,wBAAQ;AAIR;AAAA;AAAA,wBAAiB;AAIjB;AAAA;AAAA,wBAAiB;AAQf,SAAK,aAAa,QAAQ,aAAa,IAAI,cAAc;AACzD,SAAK,SAAS,QAAQ;AACtB,SAAK,OAAO,QAAQ,OAAO;AAC3B,SAAK,cAAc,QAAQ,cAAc;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,UAAU,OAAuB;AAE/B,QAAI,MAAM,QAAQ,KAAK,QAAQ;AAC7B;AAAA,IACF;AAEA,QAAI;AACF,YAAM,YAAY,KAAK,WAAW,OAAO,KAAK;AAC9C,YAAM,UAAU,KAAK,YAAY;AAEjC,cAAQ,KAAK,SAAS;AAGtB,aAAO,QAAQ,SAAS,KAAK,aAAa;AACxC,gBAAQ,MAAM;AAAA,MAChB;AAEA,mBAAa,QAAQ,KAAK,MAAM,KAAK,UAAU,OAAO,CAAC;AAAA,IACzD,QAAQ;AAAA,IAGR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,WAAqC;AAChD,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,OAAuB;AAC9B,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAc;AACZ,QAAI;AACF,mBAAa,WAAW,KAAK,IAAI;AAAA,IACnC,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAuB;AACrB,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,cAAwB;AAC9B,QAAI;AACF,YAAM,MAAM,aAAa,QAAQ,KAAK,IAAI;AAE1C,UAAI,CAAC,KAAK;AACR,eAAO,CAAC;AAAA,MACV;AAEA,YAAM,SAAS,KAAK,MAAM,GAAG;AAE7B,aAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC;AAAA,IAC3C,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AACF;;;ACvMA,SAAS,iBAAiB;AA4CnB,SAAS,UAAU,aAAuC;AAC/D,QAAM,gBAAgB,UAAU,aAAa;AAE7C,MAAI,aAAa;AACf,WAAO,cAAc,QAAQ,WAAW;AAAA,EAC1C;AAGA,SAAO;AACT;;;ACrDA,SAAS,iBAAiB;AA+CnB,SAAS,iBACd,SACA,aACM;AACN,QAAM,SAAS,UAAU,WAAW;AAEpC,YAAU,MAAM;AAEd,WAAO,YAAY,OAAO;AAG1B,WAAO,MAAM;AACX,aAAO,eAAe,OAAO,KAAK,OAAO,CAAC;AAAA,IAC5C;AAAA,EACF,GAAG,CAAC,QAAQ,OAAO,CAAC;AACtB;","names":["LogLevel","LEVEL_LABEL"]}
1
+ {"version":3,"sources":["../src/enums/log-level.enum.ts","../src/services/logger.service.ts","../src/logger.ts","../src/constants/tokens.constant.ts","../src/logger.module.ts","../src/formatters/pretty.formatter.ts","../src/formatters/json.formatter.ts","../src/formatters/simple.formatter.ts","../src/transporters/silent.transporter.ts","../src/transporters/console.transporter.ts","../src/utils/define-config.util.ts","../src/transporters/storage.transporter.ts","../src/hooks/use-logger/use-logger.hook.ts","../src/hooks/use-logger-context/use-logger-context.hook.ts"],"sourcesContent":["/**\n * Log Level Enum\n *\n * Defines the severity levels for log messages, ordered from\n * least severe (debug) to most severe (fatal). Inspired by\n * PSR-3 / Laravel logging levels, adapted for client-side use.\n *\n * Each level has a numeric value that enables level-based filtering.\n * A transporter configured with a minimum level will only process\n * messages at or above that severity.\n *\n * @module enums/log-level\n */\nexport enum LogLevel {\n /**\n * Detailed debug information.\n * Use for development-time diagnostics that should not appear in production.\n */\n Debug = 0,\n\n /**\n * Interesting events.\n * Example: User logs in, feature flag evaluated, route navigated.\n */\n Info = 1,\n\n /**\n * Exceptional occurrences that are not errors.\n * Example: Use of deprecated APIs, poor use of an API, recoverable issues.\n */\n Warn = 2,\n\n /**\n * Runtime errors that do not require immediate action but should\n * typically be logged and monitored.\n */\n Error = 3,\n\n /**\n * System is unusable or a critical failure has occurred.\n * Example: Unrecoverable state, data corruption detected.\n */\n Fatal = 4,\n}\n","/**\n * Logger Service\n *\n * Main logger service that handles channels internally (NO separate manager).\n * Provides high-level logging operations with support for multiple channels.\n *\n * **Architecture:**\n * - Manages channels internally (no separate LoggerManager)\n * - Provides unified logging API\n * - Supports multiple channels with different transporters\n * - Lazy channel initialization\n *\n * @module services/logger\n */\n\nimport { Injectable, Inject } from '@abdokouta/react-di';\nimport { Logger } from '../logger';\nimport type { LoggerInterface } from '../interfaces/logger.interface';\nimport type { LoggerServiceInterface } from '../interfaces/logger-service.interface';\nimport { LOGGER_CONFIG } from '../constants/tokens.constant';\nimport type { LoggerModuleOptions } from '../config/logger.config';\n\n/**\n * Logger service implementation\n *\n * Provides a unified logging API with support for multiple channels.\n * Handles channel management internally without a separate manager class.\n *\n * @example\n * ```typescript\n * @Injectable()\n * class UserService {\n * constructor(\n * @Inject(LoggerService) private logger: LoggerService\n * ) {}\n *\n * async createUser(data: UserData) {\n * // Use default channel\n * this.logger.info('Creating user', { email: data.email });\n *\n * try {\n * const user = await this.db.users.create(data);\n * this.logger.info('User created', { userId: user.id });\n * return user;\n * } catch (error) {\n * this.logger.error('Failed to create user', { error });\n * throw error;\n * }\n * }\n *\n * async auditAction(action: string) {\n * // Use specific channel\n * const auditLogger = this.logger.channel('audit');\n * auditLogger.info('User action', { action });\n * }\n * }\n * ```\n */\n@Injectable()\nexport class LoggerService implements LoggerServiceInterface {\n /** Logger configuration */\n private readonly config: LoggerModuleOptions;\n\n /** Cached channel instances */\n private channels: Map<string, LoggerInterface> = new Map();\n\n /** Default channel instance */\n private defaultChannel?: LoggerInterface;\n\n /**\n * Create a new logger service\n *\n * @param config - Logger configuration\n */\n constructor(\n @Inject(LOGGER_CONFIG)\n config: LoggerModuleOptions\n ) {\n this.config = config;\n }\n\n /**\n * Get a logger channel instance\n *\n * Returns the specified channel, or the default channel if no name is provided.\n * Channels are lazily initialized and cached.\n *\n * @param name - Channel name (uses default if not specified)\n * @returns Logger instance\n * @throws Error if channel is not configured\n *\n * @example\n * ```typescript\n * // Use default channel\n * const logger = loggerService.channel();\n * logger.info('Default message');\n *\n * // Use specific channel\n * const errorLogger = loggerService.channel('errors');\n * errorLogger.error('Critical error');\n * ```\n */\n channel(name?: string): LoggerInterface {\n const channelName = name ?? this.config.default;\n\n // Return cached channel if exists\n if (this.channels.has(channelName)) {\n return this.channels.get(channelName) as LoggerInterface;\n }\n\n // Resolve and cache new channel\n const channel = this.resolve(channelName);\n this.channels.set(channelName, channel);\n\n return channel;\n }\n\n /**\n * Log a message at the debug level (default channel)\n *\n * @param message - The log message\n * @param context - Optional contextual data\n *\n * @example\n * ```typescript\n * loggerService.debug('Cache hit', { key: 'user:123' });\n * ```\n */\n debug(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().debug(message, context);\n }\n\n /**\n * Log a message at the info level (default channel)\n *\n * @param message - The log message\n * @param context - Optional contextual data\n *\n * @example\n * ```typescript\n * loggerService.info('User logged in', { userId: '123' });\n * ```\n */\n info(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().info(message, context);\n }\n\n /**\n * Log a message at the warn level (default channel)\n *\n * @param message - The log message\n * @param context - Optional contextual data\n *\n * @example\n * ```typescript\n * loggerService.warn('API rate limit approaching', { remaining: 10 });\n * ```\n */\n warn(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().warn(message, context);\n }\n\n /**\n * Log a message at the error level (default channel)\n *\n * @param message - The log message\n * @param context - Optional contextual data\n *\n * @example\n * ```typescript\n * loggerService.error('Database connection failed', { error });\n * ```\n */\n error(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().error(message, context);\n }\n\n /**\n * Log a message at the fatal level (default channel)\n *\n * @param message - The log message\n * @param context - Optional contextual data\n *\n * @example\n * ```typescript\n * loggerService.fatal('Application crashed', { error, stack });\n * ```\n */\n fatal(message: string, context: Record<string, unknown> = {}): void {\n this.getDefaultChannel().fatal(message, context);\n }\n\n /**\n * Add persistent context to the default channel\n *\n * @param context - Key-value pairs to add to the shared context\n * @returns The logger service instance for fluent chaining\n *\n * @example\n * ```typescript\n * loggerService\n * .withContext({ requestId: 'abc-123' })\n * .withContext({ userId: 42 })\n * .info('Processing request');\n * ```\n */\n withContext(context: Record<string, unknown>): this {\n this.getDefaultChannel().withContext(context);\n return this;\n }\n\n /**\n * Remove keys from the default channel's shared context\n *\n * @param keys - Optional array of context keys to remove\n * @returns The logger service instance for fluent chaining\n *\n * @example\n * ```typescript\n * // Remove specific keys\n * loggerService.withoutContext(['userId', 'requestId']);\n *\n * // Clear all context\n * loggerService.withoutContext();\n * ```\n */\n withoutContext(keys?: string[]): this {\n this.getDefaultChannel().withoutContext(keys);\n return this;\n }\n\n /**\n * Get all transporters from the default channel\n *\n * @returns Array of transporter instances\n */\n getTransporters() {\n return this.getDefaultChannel().getTransporters();\n }\n\n /**\n * Get the default channel name\n *\n * @returns Default channel name\n */\n getDefaultChannelName(): string {\n return this.config.default;\n }\n\n /**\n * Get all configured channel names\n *\n * @returns Array of channel names\n */\n getChannelNames(): string[] {\n return Object.keys(this.config.channels);\n }\n\n /**\n * Check if a channel is configured\n *\n * @param name - Channel name\n * @returns True if the channel is configured\n */\n hasChannel(name: string): boolean {\n return name in this.config.channels;\n }\n\n /**\n * Check if a channel is currently active (cached)\n *\n * @param name - Channel name (uses default if not specified)\n * @returns True if the channel is active\n */\n isChannelActive(name?: string): boolean {\n const channelName = name ?? this.config.default;\n return this.channels.has(channelName);\n }\n\n /**\n * Get the default channel instance\n *\n * @returns Logger instance\n * @throws Error if default channel cannot be resolved\n * @private\n */\n private getDefaultChannel(): LoggerInterface {\n if (this.defaultChannel) {\n return this.defaultChannel;\n }\n\n // Lazy initialize default channel\n const channelName = this.config.default;\n\n if (this.channels.has(channelName)) {\n this.defaultChannel = this.channels.get(channelName) as LoggerInterface;\n return this.defaultChannel;\n }\n\n this.defaultChannel = this.resolve(channelName);\n this.channels.set(channelName, this.defaultChannel);\n\n return this.defaultChannel;\n }\n\n /**\n * Resolve a channel by name\n *\n * Creates a new logger instance based on channel configuration.\n *\n * @param name - Channel name\n * @returns Logger instance\n * @throws Error if channel is not configured\n * @private\n */\n private resolve(name: string): LoggerInterface {\n const channelConfig = this.config.channels[name];\n\n if (!channelConfig) {\n throw new Error(\n `Logger channel [${name}] is not configured. ` +\n `Available channels: ${Object.keys(this.config.channels).join(', ')}`\n );\n }\n\n // Create logger with channel configuration\n return new Logger(channelConfig);\n }\n}\n","/**\n * Logger Implementation\n *\n * Core logger class that implements the LoggerInterface.\n * Handles log entry creation, context management, and transporter dispatch.\n *\n * @module logger\n */\n\nimport type { LoggerInterface } from './interfaces/logger.interface';\nimport type { LoggerConfig } from './interfaces/logger-config.interface';\nimport type { TransporterInterface } from './interfaces/transporter.interface';\nimport type { LogEntry } from './interfaces/log-entry.interface';\nimport { LogLevel } from './enums/log-level.enum';\n\n/**\n * Logger class\n *\n * Implements the LoggerInterface with support for multiple transporters,\n * contextual logging, and log level filtering.\n */\nexport class Logger implements LoggerInterface {\n private readonly config: LoggerConfig;\n private sharedContext: Record<string, unknown> = {};\n\n constructor(config: LoggerConfig) {\n this.config = config;\n if (config.context) {\n this.sharedContext = { ...config.context };\n }\n }\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.DEBUG, message, context);\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.INFO, message, context);\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.WARN, message, context);\n }\n\n error(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.ERROR, message, context);\n }\n\n fatal(message: string, context?: Record<string, unknown>): void {\n this.log(LogLevel.FATAL, message, context);\n }\n\n withContext(context: Record<string, unknown>): this {\n this.sharedContext = { ...this.sharedContext, ...context };\n return this;\n }\n\n withoutContext(keys?: string[]): this {\n if (!keys) {\n this.sharedContext = {};\n } else {\n for (const key of keys) {\n delete this.sharedContext[key];\n }\n }\n return this;\n }\n\n getTransporters(): TransporterInterface[] {\n return this.config.transporters;\n }\n\n private log(level: LogLevel, message: string, context?: Record<string, unknown>): void {\n const entry: LogEntry = {\n level,\n message,\n context: { ...this.sharedContext, ...context },\n timestamp: new Date(),\n };\n\n for (const transporter of this.config.transporters) {\n transporter.log(entry);\n }\n }\n}\n","/**\n * Dependency Injection Tokens\n *\n * Defines DI tokens for the logger package.\n * Used with @abdokouta/react-di for dependency injection.\n *\n * @module constants/tokens\n */\n\n/**\n * Logger configuration token\n *\n * Used to inject the logger configuration into the LoggerService.\n *\n * @example\n * ```typescript\n * @Injectable()\n * class LoggerService {\n * constructor(\n * @Inject(LOGGER_CONFIG) private config: LoggerModuleOptions\n * ) {}\n * }\n * ```\n */\nexport const LOGGER_CONFIG = Symbol('LOGGER_CONFIG');\n\n/**\n * Logger service token\n *\n * Used to inject the logger service into other services.\n *\n * @example\n * ```typescript\n * @Injectable()\n * class UserService {\n * constructor(\n * @Inject(LOGGER_SERVICE) private logger: LoggerService\n * ) {}\n * }\n * ```\n */\nexport const LOGGER_SERVICE = Symbol('LOGGER_SERVICE');\n","/**\n * Logger Module\n *\n * Configures the logger system for dependency injection.\n * Provides LoggerService (NO manager) to the application.\n *\n * @module logger.module\n */\n\nimport { Module, forRoot, type DynamicModule } from '@abdokouta/react-di';\n\nimport { LoggerService } from './services/logger.service';\nimport { LOGGER_CONFIG } from './constants/tokens.constant';\nimport type { LoggerModuleOptions } from './config/logger.config';\nimport { SilentTransporter } from './transporters/silent.transporter';\nimport { ConsoleTransporter } from './transporters/console.transporter';\nimport type { LoggerConfig } from './interfaces/logger-config.interface';\n\n/**\n * Logger module\n *\n * Provides LoggerService to the application via dependency injection.\n * The service handles channels internally (NO separate manager).\n *\n * @example\n * ```typescript\n * import { Module } from '@abdokouta/react-di';\n * import { LoggerModule, defineConfig } from '@abdokouta/logger';\n * import { ConsoleTransporter, StorageTransporter } from '@abdokouta/logger';\n * import { LogLevel } from '@abdokouta/logger';\n *\n * @Module({\n * imports: [\n * LoggerModule.forRoot(\n * defineConfig({\n * default: 'console',\n * channels: {\n * console: {\n * transporters: [new ConsoleTransporter()],\n * context: { app: 'my-app' },\n * },\n * storage: {\n * transporters: [new StorageTransporter({ maxEntries: 500 })],\n * },\n * errors: {\n * transporters: [\n * new ConsoleTransporter({ level: LogLevel.Error }),\n * new StorageTransporter({ key: 'error-logs' }),\n * ],\n * },\n * },\n * })\n * ),\n * ],\n * })\n * export class AppModule {}\n * ```\n *\n * @example\n * ```typescript\n * // Using logger in a service\n * import { Injectable, Inject } from '@abdokouta/react-di';\n * import { LoggerService } from '@abdokouta/logger';\n *\n * @Injectable()\n * export class UserService {\n * constructor(\n * @Inject(LoggerService) private logger: LoggerService\n * ) {}\n *\n * async createUser(data: UserData) {\n * this.logger.info('Creating user', { email: data.email });\n *\n * try {\n * const user = await this.db.users.create(data);\n * this.logger.info('User created', { userId: user.id });\n * return user;\n * } catch (error) {\n * this.logger.error('Failed to create user', { error });\n * throw error;\n * }\n * }\n *\n * async auditAction(action: string) {\n * // Use specific channel\n * const auditLogger = this.logger.channel('audit');\n * auditLogger.info('User action', { action });\n * }\n * }\n * ```\n */\n@Module({})\n// biome-ignore lint/complexity/noStaticOnlyClass: Module pattern requires static methods\nexport class LoggerModule {\n /**\n * Configure the logger module\n *\n * @param config - Logger configuration (can be passed directly without defineConfig)\n * @returns Dynamic module\n *\n * @example\n * ```typescript\n * LoggerModule.forRoot({\n * default: 'console',\n * channels: {\n * console: {\n * transporters: [new ConsoleTransporter()],\n * },\n * },\n * })\n * ```\n */\n static forRoot(config: LoggerModuleOptions): DynamicModule {\n // Ensure default channel has transporters\n const processedConfig = LoggerModule.processConfig(config);\n\n return forRoot(LoggerModule, {\n providers: [\n {\n provide: LOGGER_CONFIG,\n useValue: processedConfig,\n },\n LoggerService,\n ],\n exports: [LoggerService, LOGGER_CONFIG],\n });\n }\n\n /**\n * Process configuration to add default transporters if needed\n *\n * @param config - Raw configuration\n * @returns Processed configuration with defaults\n * @private\n */\n private static processConfig(config: LoggerModuleOptions): LoggerModuleOptions {\n const processedChannels: Record<string, LoggerConfig> = {};\n\n // Use for...in loop for better compatibility\n for (const name in config.channels) {\n if (Object.prototype.hasOwnProperty.call(config.channels, name)) {\n const channelConfig = config.channels[name];\n\n // Skip if channelConfig is undefined\n if (!channelConfig) continue;\n\n // If no transporters specified, add default based on channel name\n if (!channelConfig.transporters || channelConfig.transporters.length === 0) {\n if (name === 'silent') {\n processedChannels[name] = {\n ...channelConfig,\n transporters: [new SilentTransporter()],\n };\n } else {\n processedChannels[name] = {\n ...channelConfig,\n transporters: [new ConsoleTransporter()],\n };\n }\n } else {\n processedChannels[name] = channelConfig;\n }\n }\n }\n\n return {\n ...config,\n channels: processedChannels,\n };\n }\n}\n","/**\n * Pretty Formatter\n *\n * A visually rich formatter that produces colorful, emoji-prefixed\n * log output designed for the browser console. Each log level is\n * assigned a distinct emoji and CSS color to make scanning logs\n * effortless during development.\n *\n * This is the default formatter used by the ConsoleTransporter.\n *\n * @module formatters/pretty\n *\n * @example\n * ```ts\n * const formatter = new PrettyFormatter();\n * const output = formatter.format(entry);\n * // => \"🐛 [DEBUG] [14:30:00.000] Hello world {userId: 42}\"\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport type { FormatterInterface, LogEntry } from '@/interfaces';\n\n/**\n * Mapping of log levels to their emoji prefix.\n * Provides instant visual identification of severity in console output.\n */\nconst LEVEL_EMOJI: Record<LogLevel, string> = {\n [LogLevel.Debug]: '🐛',\n [LogLevel.Info]: 'ℹ️',\n [LogLevel.Warn]: '⚠️',\n [LogLevel.Error]: '❌',\n [LogLevel.Fatal]: '💀',\n};\n\n/**\n * Mapping of log levels to their display label.\n * Used in the formatted output string between brackets.\n */\nconst LEVEL_LABEL: Record<LogLevel, string> = {\n [LogLevel.Debug]: 'DEBUG',\n [LogLevel.Info]: 'INFO',\n [LogLevel.Warn]: 'WARN',\n [LogLevel.Error]: 'ERROR',\n [LogLevel.Fatal]: 'FATAL',\n};\n\n/**\n * Mapping of log levels to CSS color strings for browser console styling.\n * These colors are applied via `%c` formatting in `console.log`.\n */\nexport const LEVEL_COLORS: Record<LogLevel, string> = {\n [LogLevel.Debug]: 'color: #8B8B8B',\n [LogLevel.Info]: 'color: #2196F3',\n [LogLevel.Warn]: 'color: #FF9800',\n [LogLevel.Error]: 'color: #F44336',\n [LogLevel.Fatal]:\n 'color: #FFFFFF; background: #F44336; font-weight: bold; padding: 1px 4px; border-radius: 2px',\n};\n\nexport class PrettyFormatter implements FormatterInterface {\n /**\n * Format a log entry into a pretty, human-readable string with\n * emoji prefix, level badge, timestamp, message, and context.\n *\n * The returned string includes `%c` placeholders for CSS styling\n * in the browser console. The ConsoleTransporter is responsible\n * for passing the corresponding style strings.\n *\n * @param entry - The log entry to format.\n * @returns The formatted string with CSS style placeholders.\n */\n format(entry: LogEntry): string {\n const emoji = LEVEL_EMOJI[entry.level];\n const label = LEVEL_LABEL[entry.level];\n const time = this.formatTimestamp(entry.timestamp);\n const contextStr = this.formatContext(entry.context);\n\n return `${emoji} %c[${label}]%c [${time}] ${entry.message}${contextStr}`;\n }\n\n /**\n * Extract a short time string (HH:mm:ss.SSS) from an ISO timestamp.\n *\n * @param timestamp - ISO 8601 timestamp string.\n * @returns A short time-only representation.\n */\n private formatTimestamp(timestamp: string): string {\n try {\n const date = new Date(timestamp);\n const hours = String(date.getHours()).padStart(2, '0');\n const minutes = String(date.getMinutes()).padStart(2, '0');\n const seconds = String(date.getSeconds()).padStart(2, '0');\n const ms = String(date.getMilliseconds()).padStart(3, '0');\n\n return `${hours}:${minutes}:${seconds}.${ms}`;\n } catch {\n return timestamp;\n }\n }\n\n /**\n * Serialize context data into a compact, readable string.\n * Returns an empty string when context is empty to keep output clean.\n *\n * @param context - The context record to serialize.\n * @returns A formatted context string or empty string.\n */\n private formatContext(context: Record<string, unknown>): string {\n const keys = Object.keys(context);\n\n if (keys.length === 0) {\n return '';\n }\n\n try {\n return ` ${JSON.stringify(context)}`;\n } catch {\n return ` [context serialization failed]`;\n }\n }\n}\n","/**\n * JSON Formatter\n *\n * Serializes log entries into compact JSON strings. Ideal for\n * structured logging scenarios where logs need to be parsed\n * programmatically — for example, when sending logs to a remote\n * endpoint or storing them in localStorage for later analysis.\n *\n * @module formatters/json\n *\n * @example\n * ```ts\n * const formatter = new JsonFormatter();\n * const output = formatter.format(entry);\n * // => '{\"level\":\"info\",\"message\":\"User logged in\",\"timestamp\":\"...\",\"context\":{\"userId\":42}}'\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport type { FormatterInterface, LogEntry } from '@/interfaces';\n\n/**\n * Mapping of log levels to their lowercase string representation\n * for JSON output.\n */\nconst LEVEL_STRING: Record<LogLevel, string> = {\n [LogLevel.Debug]: 'debug',\n [LogLevel.Info]: 'info',\n [LogLevel.Warn]: 'warn',\n [LogLevel.Error]: 'error',\n [LogLevel.Fatal]: 'fatal',\n};\n\nexport class JsonFormatter implements FormatterInterface {\n /**\n * Format a log entry as a JSON string.\n *\n * Produces a flat JSON object with level (as string), message,\n * timestamp, and context fields. Context is omitted when empty\n * to keep output compact.\n *\n * @param entry - The log entry to format.\n * @returns A JSON-serialized string representation of the entry.\n */\n format(entry: LogEntry): string {\n const payload: Record<string, unknown> = {\n level: LEVEL_STRING[entry.level],\n message: entry.message,\n timestamp: entry.timestamp,\n };\n\n // Only include context when it has keys to keep JSON compact.\n if (Object.keys(entry.context).length > 0) {\n payload.context = entry.context;\n }\n\n try {\n return JSON.stringify(payload);\n } catch {\n return JSON.stringify({\n level: LEVEL_STRING[entry.level],\n message: entry.message,\n timestamp: entry.timestamp,\n error: 'context serialization failed',\n });\n }\n }\n}\n","/**\n * Simple Formatter\n *\n * A minimal, no-frills formatter that produces plain text log lines\n * without colors, emojis, or CSS styling. Suitable for environments\n * where styled output is not supported or when logs need to be\n * stored as plain text.\n *\n * @module formatters/simple\n *\n * @example\n * ```ts\n * const formatter = new SimpleFormatter();\n * const output = formatter.format(entry);\n * // => \"[DEBUG] [2026-03-28T14:30:00.000Z] Hello world {userId: 42}\"\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport type { FormatterInterface, LogEntry } from '@/interfaces';\n\n/**\n * Mapping of log levels to their uppercase string label.\n */\nconst LEVEL_LABEL: Record<LogLevel, string> = {\n [LogLevel.Debug]: 'DEBUG',\n [LogLevel.Info]: 'INFO',\n [LogLevel.Warn]: 'WARN',\n [LogLevel.Error]: 'ERROR',\n [LogLevel.Fatal]: 'FATAL',\n};\n\nexport class SimpleFormatter implements FormatterInterface {\n /**\n * Format a log entry into a plain text line.\n *\n * Output pattern: `[LEVEL] [timestamp] message {context}`\n *\n * @param entry - The log entry to format.\n * @returns A plain text string with no styling.\n */\n format(entry: LogEntry): string {\n const label = LEVEL_LABEL[entry.level];\n const contextStr = this.formatContext(entry.context);\n\n return `[${label}] [${entry.timestamp}] ${entry.message}${contextStr}`;\n }\n\n /**\n * Serialize context data into a compact string.\n * Returns an empty string when context is empty.\n *\n * @param context - The context record to serialize.\n * @returns A formatted context string or empty string.\n */\n private formatContext(context: Record<string, unknown>): string {\n const keys = Object.keys(context);\n\n if (keys.length === 0) {\n return '';\n }\n\n try {\n return ` ${JSON.stringify(context)}`;\n } catch {\n return ' [context serialization failed]';\n }\n }\n}\n","/**\n * Silent Transporter\n *\n * A no-op transporter that silently discards all log entries.\n * Useful for production environments where logging should be\n * disabled, or for testing scenarios where log output would\n * pollute test results.\n *\n * Equivalent to Laravel's \"null\" log driver.\n *\n * @module transporters/silent\n *\n * @example\n * ```ts\n * const logger = new Logger({\n * transporters: [new SilentTransporter()],\n * });\n * logger.info('This will be silently discarded');\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport { SimpleFormatter } from '@/formatters';\nimport type { FormatterInterface, LogEntry, TransporterInterface } from '@/interfaces';\n\nexport class SilentTransporter implements TransporterInterface {\n /**\nThe formatter instance (maintained for interface compliance). */\n private _formatter: FormatterInterface;\n\n /**\nThe minimum log level (maintained for interface compliance). */\n private _level: LogLevel;\n\n /**\n * Create a new SilentTransporter instance.\n * All parameters are optional and exist only for interface compliance.\n */\n constructor() {\n this._formatter = new SimpleFormatter();\n this._level = LogLevel.Debug;\n }\n\n /**\n * No-op transport method. Silently discards the entry.\n *\n * @param _entry - The log entry (ignored).\n */\n transport(_entry: LogEntry): void {\n // Intentionally empty — this transporter discards all entries.\n }\n\n /**\n * Replace the current formatter.\n *\n * @param formatter - The new formatter instance.\n */\n setFormatter(formatter: FormatterInterface): void {\n this._formatter = formatter;\n }\n\n /**\n * Retrieve the currently assigned formatter.\n *\n * @returns The active formatter instance.\n */\n getFormatter(): FormatterInterface {\n return this._formatter;\n }\n\n /**\n * Get the minimum log level.\n *\n * @returns The current minimum LogLevel threshold.\n */\n getLevel(): LogLevel {\n return this._level;\n }\n\n /**\n * Set the minimum log level.\n *\n * @param level - The new minimum LogLevel threshold.\n */\n setLevel(level: LogLevel): void {\n this._level = level;\n }\n}\n","/**\n * Console Transporter\n *\n * Delivers log entries to the browser's developer console using the\n * appropriate `console.*` method for each severity level. When paired\n * with the PrettyFormatter (the default), output includes CSS-styled\n * level badges with colors and emoji prefixes.\n *\n * This is the primary transporter for development environments.\n *\n * @module transporters/console\n *\n * @example\n * ```ts\n * const transporter = new ConsoleTransporter();\n * transporter.transport(entry); // outputs to console with colors\n *\n * // With custom minimum level:\n * const warnOnly = new ConsoleTransporter({ level: LogLevel.Warn });\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport { LEVEL_COLORS, PrettyFormatter } from '@/formatters';\nimport type { FormatterInterface, LogEntry, TransporterInterface } from '@/interfaces';\n\n/**\n * Configuration options for the ConsoleTransporter.\n */\nexport interface ConsoleTransporterOptions {\n /**\n * The formatter to use for log entries.\n * Defaults to PrettyFormatter if not provided.\n */\n formatter?: FormatterInterface;\n\n /**\n * The minimum log level to transport.\n * Entries below this level are silently ignored.\n *\n * @default LogLevel.Debug\n */\n level?: LogLevel;\n}\n\nexport class ConsoleTransporter implements TransporterInterface {\n /**\nThe formatter used to convert log entries into output strings. */\n private _formatter: FormatterInterface;\n\n /**\nThe minimum log level threshold for this transporter. */\n private _level: LogLevel;\n\n /**\n * Create a new ConsoleTransporter instance.\n *\n * @param options - Optional configuration for formatter and minimum level.\n */\n constructor(options: ConsoleTransporterOptions = {}) {\n this._formatter = options.formatter ?? new PrettyFormatter();\n this._level = options.level ?? LogLevel.Debug;\n }\n\n /**\n * Deliver a log entry to the browser console.\n *\n * Routes the entry to the appropriate `console.*` method based on\n * the log level. When using the PrettyFormatter, CSS styles are\n * applied via `%c` placeholders for colored output.\n *\n * Entries below the configured minimum level are silently skipped.\n *\n * @param entry - The log entry to output.\n */\n transport(entry: LogEntry): void {\n // Skip entries below the minimum level threshold.\n if (entry.level < this._level) {\n return;\n }\n\n const formatted = this._formatter.format(entry);\n const method = this.resolveConsoleMethod(entry.level);\n\n // When using PrettyFormatter, apply CSS styles via %c placeholders.\n if (this._formatter instanceof PrettyFormatter) {\n const levelStyle = LEVEL_COLORS[entry.level];\n const resetStyle = 'color: inherit';\n\n method(formatted, levelStyle, resetStyle);\n } else {\n method(formatted);\n }\n }\n\n /**\n * Replace the current formatter.\n *\n * @param formatter - The new formatter instance to use.\n */\n setFormatter(formatter: FormatterInterface): void {\n this._formatter = formatter;\n }\n\n /**\n * Retrieve the currently assigned formatter.\n *\n * @returns The active formatter instance.\n */\n getFormatter(): FormatterInterface {\n return this._formatter;\n }\n\n /**\n * Get the minimum log level this transporter handles.\n *\n * @returns The current minimum LogLevel threshold.\n */\n getLevel(): LogLevel {\n return this._level;\n }\n\n /**\n * Set the minimum log level this transporter handles.\n *\n * @param level - The new minimum LogLevel threshold.\n */\n setLevel(level: LogLevel): void {\n this._level = level;\n }\n\n /**\n * Resolve the appropriate `console.*` method for a given log level.\n *\n * Maps logger severity levels to the closest browser console method\n * to ensure proper DevTools filtering and visual distinction.\n *\n * @param level - The log level to resolve.\n * @returns The bound console method function.\n */\n private resolveConsoleMethod(level: LogLevel): (...args: unknown[]) => void {\n switch (level) {\n case LogLevel.Debug:\n return console.debug.bind(console);\n case LogLevel.Info:\n return console.info.bind(console);\n case LogLevel.Warn:\n return console.warn.bind(console);\n case LogLevel.Error:\n return console.error.bind(console);\n case LogLevel.Fatal:\n return console.error.bind(console);\n default:\n return console.log.bind(console);\n }\n }\n}\n","/**\n * Define Config Utility\n *\n * Helper function to define logger configuration with type safety.\n *\n * @module @abdokouta/logger\n */\n\nimport type { LoggerModuleOptions } from '../config/logger.config';\n\n/**\n * Helper function to define logger configuration with type safety\n *\n * Provides IDE autocomplete and type checking for configuration objects.\n * This pattern is consistent with modern tooling (Vite, Vitest, etc.).\n *\n * @param config - The logger configuration object\n * @returns The same configuration object with proper typing\n *\n * @example\n * ```typescript\n * // logger.config.ts\n * import { defineConfig, ConsoleTransporter, LogLevel } from '@abdokouta/logger';\n *\n * export default defineConfig({\n * default: 'console',\n * channels: {\n * console: {\n * transporters: [new ConsoleTransporter({ level: LogLevel.Debug })],\n * context: { app: 'my-app' },\n * },\n * },\n * });\n * ```\n */\nexport function defineConfig(config: LoggerModuleOptions): LoggerModuleOptions {\n return config;\n}\n","/**\n * Storage Transporter\n *\n * Persists log entries to the browser's localStorage as JSON strings.\n * Useful for capturing logs that survive page reloads, enabling\n * post-mortem debugging or user-submitted bug reports that include\n * recent log history.\n *\n * Entries are stored as a JSON array under a configurable storage key.\n * A maximum entry limit prevents unbounded storage growth.\n *\n * @module transporters/storage\n *\n * @example\n * ```ts\n * const transporter = new StorageTransporter({\n * key: 'app-logs',\n * maxEntries: 200,\n * });\n * ```\n */\nimport { LogLevel } from '@/enums';\nimport { JsonFormatter } from '@/formatters';\nimport type { FormatterInterface, LogEntry, TransporterInterface } from '@/interfaces';\n\n/**\n * Configuration options for the StorageTransporter.\n */\nexport interface StorageTransporterOptions {\n /**\n * The localStorage key under which log entries are stored.\n *\n * @default \"logger:entries\"\n */\n key?: string;\n\n /**\n * Maximum number of entries to retain in storage.\n * When the limit is exceeded, the oldest entries are discarded (FIFO).\n *\n * @default 100\n */\n maxEntries?: number;\n\n /**\n * The formatter to use for serializing log entries.\n * Defaults to JsonFormatter if not provided.\n */\n formatter?: FormatterInterface;\n\n /**\n * The minimum log level to transport.\n * Entries below this level are silently ignored.\n *\n * @default LogLevel.Debug\n */\n level?: LogLevel;\n}\n\n/**\nDefault localStorage key for log storage. */\nconst DEFAULT_STORAGE_KEY = 'logger:entries';\n\n/**\nDefault maximum number of retained entries. */\nconst DEFAULT_MAX_ENTRIES = 100;\n\nexport class StorageTransporter implements TransporterInterface {\n /**\nThe formatter used to convert log entries into storable strings. */\n private _formatter: FormatterInterface;\n\n /**\nThe minimum log level threshold for this transporter. */\n private _level: LogLevel;\n\n /**\nThe localStorage key for persisting entries. */\n private readonly _key: string;\n\n /**\nMaximum number of entries to retain. */\n private readonly _maxEntries: number;\n\n /**\n * Create a new StorageTransporter instance.\n *\n * @param options - Optional configuration for storage key, limits, and formatter.\n */\n constructor(options: StorageTransporterOptions = {}) {\n this._formatter = options.formatter ?? new JsonFormatter();\n this._level = options.level ?? LogLevel.Debug;\n this._key = options.key ?? DEFAULT_STORAGE_KEY;\n this._maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n }\n\n /**\n * Persist a log entry to localStorage.\n *\n * The entry is formatted, appended to the existing entries array,\n * and trimmed to the maximum entry limit. If localStorage is\n * unavailable or full, the error is silently swallowed to avoid\n * crashing the application.\n *\n * @param entry - The log entry to persist.\n */\n transport(entry: LogEntry): void {\n // Skip entries below the minimum level threshold.\n if (entry.level < this._level) {\n return;\n }\n\n try {\n const formatted = this._formatter.format(entry);\n const entries = this.readEntries();\n\n entries.push(formatted);\n\n // Trim oldest entries when the limit is exceeded.\n while (entries.length > this._maxEntries) {\n entries.shift();\n }\n\n localStorage.setItem(this._key, JSON.stringify(entries));\n } catch {\n // Silently swallow storage errors (quota exceeded, unavailable, etc.)\n // to prevent logging from crashing the application.\n }\n }\n\n /**\n * Replace the current formatter.\n *\n * @param formatter - The new formatter instance.\n */\n setFormatter(formatter: FormatterInterface): void {\n this._formatter = formatter;\n }\n\n /**\n * Retrieve the currently assigned formatter.\n *\n * @returns The active formatter instance.\n */\n getFormatter(): FormatterInterface {\n return this._formatter;\n }\n\n /**\n * Get the minimum log level this transporter handles.\n *\n * @returns The current minimum LogLevel threshold.\n */\n getLevel(): LogLevel {\n return this._level;\n }\n\n /**\n * Set the minimum log level this transporter handles.\n *\n * @param level - The new minimum LogLevel threshold.\n */\n setLevel(level: LogLevel): void {\n this._level = level;\n }\n\n /**\n * Clear all stored log entries from localStorage.\n * Useful for manual cleanup or when resetting application state.\n */\n clear(): void {\n try {\n localStorage.removeItem(this._key);\n } catch {\n // Silently swallow if localStorage is unavailable.\n }\n }\n\n /**\n * Retrieve all stored log entries from localStorage.\n *\n * @returns An array of formatted log entry strings.\n */\n getEntries(): string[] {\n return this.readEntries();\n }\n\n /**\n * Read the current entries array from localStorage.\n * Returns an empty array if the key does not exist or parsing fails.\n *\n * @returns The parsed entries array.\n */\n private readEntries(): string[] {\n try {\n const raw = localStorage.getItem(this._key);\n\n if (!raw) {\n return [];\n }\n\n const parsed = JSON.parse(raw);\n\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n }\n}\n","/**\n * useLogger Hook\n *\n * React hook for accessing the logger service in components.\n * Provides access to the default channel or a specific channel.\n *\n * @module hooks/use-logger\n */\n\nimport { useInject } from '@abdokouta/react-di';\nimport { LoggerService } from '@/services/logger.service';\nimport type { LoggerInterface } from '@/interfaces/logger.interface';\n\n/**\n * Access the logger service in React components\n *\n * Returns the logger service instance or a specific channel.\n *\n * @param channelName - Optional channel name (uses default if not specified)\n * @returns Logger instance\n *\n * @example\n * ```typescript\n * import { useLogger } from '@abdokouta/logger';\n *\n * function MyComponent() {\n * const logger = useLogger();\n *\n * const handleClick = () => {\n * logger.info('Button clicked', { timestamp: Date.now() });\n * };\n *\n * return <button onClick={handleClick}>Click me</button>;\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Use specific channel\n * function ErrorBoundary({ error }: { error: Error }) {\n * const errorLogger = useLogger('errors');\n *\n * useEffect(() => {\n * errorLogger.error('Component error', {\n * message: error.message,\n * stack: error.stack,\n * });\n * }, [error]);\n *\n * return <div>Something went wrong</div>;\n * }\n * ```\n */\nexport function useLogger(channelName?: string): LoggerInterface {\n const loggerService = useInject(LoggerService);\n\n if (channelName) {\n return loggerService.channel(channelName);\n }\n\n // Return the service itself which implements LoggerInterface\n return loggerService as LoggerInterface;\n}\n","/**\n * useLoggerContext Hook\n *\n * React hook for managing logger context in components.\n * Automatically adds and removes context based on component lifecycle.\n *\n * @module hooks/use-logger-context\n */\n\nimport { useEffect } from 'react';\nimport { useLogger } from '../use-logger';\n\n/**\n * Manage logger context in React components\n *\n * Automatically adds context when the component mounts and removes it when unmounted.\n * Useful for adding component-specific context to all logs.\n *\n * @param context - Context to add to the logger\n * @param channelName - Optional channel name (uses default if not specified)\n *\n * @example\n * ```typescript\n * import { useLoggerContext } from '@abdokouta/logger';\n *\n * function UserProfile({ userId }: { userId: string }) {\n * // Add userId to all logs in this component\n * useLoggerContext({ userId, component: 'UserProfile' });\n *\n * const logger = useLogger();\n *\n * const handleUpdate = () => {\n * // This log will include { userId, component: 'UserProfile' }\n * logger.info('Updating profile');\n * };\n *\n * return <button onClick={handleUpdate}>Update</button>;\n * }\n * ```\n *\n * @example\n * ```typescript\n * // With specific channel\n * function PaymentForm({ orderId }: { orderId: string }) {\n * useLoggerContext({ orderId, form: 'payment' }, 'payment');\n *\n * const logger = useLogger('payment');\n *\n * const handleSubmit = () => {\n * logger.info('Payment submitted');\n * };\n *\n * return <form onSubmit={handleSubmit}>...</form>;\n * }\n * ```\n */\nexport function useLoggerContext(context: Record<string, unknown>, channelName?: string): void {\n const logger = useLogger(channelName);\n\n useEffect(() => {\n // Add context on mount\n logger.withContext(context);\n\n // Remove context on unmount\n return () => {\n logger.withoutContext(Object.keys(context));\n };\n }, [logger, context]);\n}\n"],"mappings":"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","names":["LogLevel","LEVEL_LABEL"]}
package/package.json CHANGED
@@ -1,26 +1,33 @@
1
1
  {
2
2
  "name": "@abdokouta/react-logger",
3
- "version": "1.0.0",
4
- "private": false,
3
+ "version": "1.0.1",
5
4
  "description": "Flexible logging system with multiple formatters and transporters for React",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "Abdelrhman Kouta",
8
+ "email": "abdelrhman@pixielity.com",
9
+ "url": "https://github.com/abdokouta"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/abdokouta/logger.git",
14
+ "directory": "packages/logger"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/abdokouta/logger/issues"
18
+ },
19
+ "homepage": "https://github.com/abdokouta/logger#readme",
6
20
  "keywords": [
7
21
  "react",
8
22
  "logger",
9
23
  "logging",
10
24
  "console",
11
25
  "transport",
12
- "formatter"
26
+ "formatter",
27
+ "dependency-injection",
28
+ "di"
13
29
  ],
14
- "repository": {
15
- "type": "git",
16
- "url": "https://github.com/pixielity-inc/react-logger.git"
17
- },
18
- "license": "MIT",
19
- "author": "Pixielity",
20
30
  "type": "module",
21
- "main": "./dist/index.js",
22
- "module": "./dist/index.mjs",
23
- "types": "./dist/index.d.ts",
24
31
  "exports": {
25
32
  ".": {
26
33
  "types": "./dist/index.d.ts",
@@ -28,42 +35,38 @@
28
35
  "require": "./dist/index.js"
29
36
  }
30
37
  },
38
+ "main": "./dist/index.js",
39
+ "module": "./dist/index.mjs",
40
+ "types": "./dist/index.d.ts",
31
41
  "files": [
32
42
  "dist",
33
- "config"
43
+ "config",
44
+ "README.md",
45
+ "LICENSE"
34
46
  ],
35
- "scripts": {
36
- "build": "tsup",
37
- "dev": "tsup --watch",
38
- "lint": "eslint . --max-warnings 0",
39
- "lint:fix": "eslint . --fix",
40
- "check-types": "tsc --noEmit",
41
- "clean": "rm -rf dist",
42
- "test": "vitest --run",
43
- "test:watch": "vitest",
44
- "test:coverage": "vitest --run --coverage",
45
- "test:ui": "vitest --ui",
46
- "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json}\""
47
- },
48
47
  "peerDependencies": {
49
- "@abdokouta/react-di": "^2.1.0",
48
+ "@abdokouta/react-di": "^2.0.0",
50
49
  "react": "^18.0.0 || ^19.0.0"
51
50
  },
52
51
  "peerDependenciesMeta": {
52
+ "@abdokouta/react-di": {
53
+ "optional": false
54
+ },
53
55
  "react": {
54
56
  "optional": true
55
57
  }
56
58
  },
57
59
  "devDependencies": {
60
+ "@abdokouta/react-di": "^2.0.0",
58
61
  "@nesvel/eslint-config": "^1.0.5",
59
62
  "@nesvel/prettier-config": "^1.0.3",
60
63
  "@nesvel/tsup-config": "^1.0.3",
61
64
  "@nesvel/typescript-config": "^1.0.4",
62
- "@abdokouta/react-di": "^2.1.0",
63
65
  "@types/node": "^25.5.0",
64
66
  "@types/react": "^19.2.14",
65
67
  "@vitest/ui": "^4.1.2",
66
68
  "eslint": "^10.1.0",
69
+ "jsdom": "^29.0.1",
67
70
  "prettier": "^3.8.1",
68
71
  "react": "^19.2.4",
69
72
  "tsup": "^8.5.1",
@@ -71,6 +74,26 @@
71
74
  "vitest": "^4.1.2"
72
75
  },
73
76
  "publishConfig": {
74
- "access": "public"
77
+ "access": "public",
78
+ "registry": "https://registry.npmjs.org/"
79
+ },
80
+ "engines": {
81
+ "node": ">=18.0.0",
82
+ "pnpm": ">=9.0.0"
83
+ },
84
+ "sideEffects": false,
85
+ "scripts": {
86
+ "build": "tsup",
87
+ "dev": "tsup --watch",
88
+ "clean": "rm -rf dist node_modules/.cache",
89
+ "typecheck": "tsc --noEmit",
90
+ "lint": "eslint . --max-warnings 0",
91
+ "lint:fix": "eslint . --fix",
92
+ "format": "prettier --write .",
93
+ "format:check": "prettier --check .",
94
+ "test": "vitest run",
95
+ "test:watch": "vitest",
96
+ "test:coverage": "vitest run --coverage",
97
+ "release": "pnpm publish --access public --no-git-checks"
75
98
  }
76
- }
99
+ }