@abdokouta/react-logger 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,906 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var __decorateClass = (decorators, target, key, kind) => {
21
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
22
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
23
+ if (decorator = decorators[i])
24
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
25
+ if (kind && result) __defProp(target, key, result);
26
+ return result;
27
+ };
28
+ var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
29
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
30
+
31
+ // src/index.ts
32
+ var index_exports = {};
33
+ __export(index_exports, {
34
+ ConsoleTransporter: () => ConsoleTransporter,
35
+ JsonFormatter: () => JsonFormatter,
36
+ LogLevel: () => LogLevel,
37
+ Logger: () => LoggerService,
38
+ LoggerModule: () => LoggerModule,
39
+ LoggerService: () => LoggerService,
40
+ PrettyFormatter: () => PrettyFormatter,
41
+ SilentTransporter: () => SilentTransporter,
42
+ SimpleFormatter: () => SimpleFormatter,
43
+ StorageTransporter: () => StorageTransporter,
44
+ defineConfig: () => defineConfig,
45
+ useLogger: () => useLogger,
46
+ useLoggerContext: () => useLoggerContext
47
+ });
48
+ module.exports = __toCommonJS(index_exports);
49
+
50
+ // src/enums/log-level.enum.ts
51
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
52
+ LogLevel2[LogLevel2["Debug"] = 0] = "Debug";
53
+ LogLevel2[LogLevel2["Info"] = 1] = "Info";
54
+ LogLevel2[LogLevel2["Warn"] = 2] = "Warn";
55
+ LogLevel2[LogLevel2["Error"] = 3] = "Error";
56
+ LogLevel2[LogLevel2["Fatal"] = 4] = "Fatal";
57
+ return LogLevel2;
58
+ })(LogLevel || {});
59
+
60
+ // src/services/logger.service.ts
61
+ var import_react_di = require("@abdokouta/react-di");
62
+
63
+ // src/logger.ts
64
+ var Logger = class {
65
+ constructor(config) {
66
+ __publicField(this, "config");
67
+ __publicField(this, "sharedContext", {});
68
+ this.config = config;
69
+ if (config.context) {
70
+ this.sharedContext = { ...config.context };
71
+ }
72
+ }
73
+ debug(message, context) {
74
+ this.log(LogLevel.DEBUG, message, context);
75
+ }
76
+ info(message, context) {
77
+ this.log(LogLevel.INFO, message, context);
78
+ }
79
+ warn(message, context) {
80
+ this.log(LogLevel.WARN, message, context);
81
+ }
82
+ error(message, context) {
83
+ this.log(LogLevel.ERROR, message, context);
84
+ }
85
+ fatal(message, context) {
86
+ this.log(LogLevel.FATAL, message, context);
87
+ }
88
+ withContext(context) {
89
+ this.sharedContext = { ...this.sharedContext, ...context };
90
+ return this;
91
+ }
92
+ withoutContext(keys) {
93
+ if (!keys) {
94
+ this.sharedContext = {};
95
+ } else {
96
+ for (const key of keys) {
97
+ delete this.sharedContext[key];
98
+ }
99
+ }
100
+ return this;
101
+ }
102
+ getTransporters() {
103
+ return this.config.transporters;
104
+ }
105
+ log(level, message, context) {
106
+ const entry = {
107
+ level,
108
+ message,
109
+ context: { ...this.sharedContext, ...context },
110
+ timestamp: /* @__PURE__ */ new Date()
111
+ };
112
+ for (const transporter of this.config.transporters) {
113
+ transporter.log(entry);
114
+ }
115
+ }
116
+ };
117
+
118
+ // src/constants/tokens.constant.ts
119
+ var LOGGER_CONFIG = /* @__PURE__ */ Symbol("LOGGER_CONFIG");
120
+
121
+ // src/services/logger.service.ts
122
+ var LoggerService = class {
123
+ /**
124
+ * Create a new logger service
125
+ *
126
+ * @param config - Logger configuration
127
+ */
128
+ constructor(config) {
129
+ /** Logger configuration */
130
+ __publicField(this, "config");
131
+ /** Cached channel instances */
132
+ __publicField(this, "channels", /* @__PURE__ */ new Map());
133
+ /** Default channel instance */
134
+ __publicField(this, "defaultChannel");
135
+ this.config = config;
136
+ }
137
+ /**
138
+ * Get a logger channel instance
139
+ *
140
+ * Returns the specified channel, or the default channel if no name is provided.
141
+ * Channels are lazily initialized and cached.
142
+ *
143
+ * @param name - Channel name (uses default if not specified)
144
+ * @returns Logger instance
145
+ * @throws Error if channel is not configured
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * // Use default channel
150
+ * const logger = loggerService.channel();
151
+ * logger.info('Default message');
152
+ *
153
+ * // Use specific channel
154
+ * const errorLogger = loggerService.channel('errors');
155
+ * errorLogger.error('Critical error');
156
+ * ```
157
+ */
158
+ channel(name) {
159
+ const channelName = name ?? this.config.default;
160
+ if (this.channels.has(channelName)) {
161
+ return this.channels.get(channelName);
162
+ }
163
+ const channel = this.resolve(channelName);
164
+ this.channels.set(channelName, channel);
165
+ return channel;
166
+ }
167
+ /**
168
+ * Log a message at the debug level (default channel)
169
+ *
170
+ * @param message - The log message
171
+ * @param context - Optional contextual data
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * loggerService.debug('Cache hit', { key: 'user:123' });
176
+ * ```
177
+ */
178
+ debug(message, context = {}) {
179
+ this.getDefaultChannel().debug(message, context);
180
+ }
181
+ /**
182
+ * Log a message at the info level (default channel)
183
+ *
184
+ * @param message - The log message
185
+ * @param context - Optional contextual data
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * loggerService.info('User logged in', { userId: '123' });
190
+ * ```
191
+ */
192
+ info(message, context = {}) {
193
+ this.getDefaultChannel().info(message, context);
194
+ }
195
+ /**
196
+ * Log a message at the warn level (default channel)
197
+ *
198
+ * @param message - The log message
199
+ * @param context - Optional contextual data
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * loggerService.warn('API rate limit approaching', { remaining: 10 });
204
+ * ```
205
+ */
206
+ warn(message, context = {}) {
207
+ this.getDefaultChannel().warn(message, context);
208
+ }
209
+ /**
210
+ * Log a message at the error level (default channel)
211
+ *
212
+ * @param message - The log message
213
+ * @param context - Optional contextual data
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * loggerService.error('Database connection failed', { error });
218
+ * ```
219
+ */
220
+ error(message, context = {}) {
221
+ this.getDefaultChannel().error(message, context);
222
+ }
223
+ /**
224
+ * Log a message at the fatal level (default channel)
225
+ *
226
+ * @param message - The log message
227
+ * @param context - Optional contextual data
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * loggerService.fatal('Application crashed', { error, stack });
232
+ * ```
233
+ */
234
+ fatal(message, context = {}) {
235
+ this.getDefaultChannel().fatal(message, context);
236
+ }
237
+ /**
238
+ * Add persistent context to the default channel
239
+ *
240
+ * @param context - Key-value pairs to add to the shared context
241
+ * @returns The logger service instance for fluent chaining
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * loggerService
246
+ * .withContext({ requestId: 'abc-123' })
247
+ * .withContext({ userId: 42 })
248
+ * .info('Processing request');
249
+ * ```
250
+ */
251
+ withContext(context) {
252
+ this.getDefaultChannel().withContext(context);
253
+ return this;
254
+ }
255
+ /**
256
+ * Remove keys from the default channel's shared context
257
+ *
258
+ * @param keys - Optional array of context keys to remove
259
+ * @returns The logger service instance for fluent chaining
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * // Remove specific keys
264
+ * loggerService.withoutContext(['userId', 'requestId']);
265
+ *
266
+ * // Clear all context
267
+ * loggerService.withoutContext();
268
+ * ```
269
+ */
270
+ withoutContext(keys) {
271
+ this.getDefaultChannel().withoutContext(keys);
272
+ return this;
273
+ }
274
+ /**
275
+ * Get all transporters from the default channel
276
+ *
277
+ * @returns Array of transporter instances
278
+ */
279
+ getTransporters() {
280
+ return this.getDefaultChannel().getTransporters();
281
+ }
282
+ /**
283
+ * Get the default channel name
284
+ *
285
+ * @returns Default channel name
286
+ */
287
+ getDefaultChannelName() {
288
+ return this.config.default;
289
+ }
290
+ /**
291
+ * Get all configured channel names
292
+ *
293
+ * @returns Array of channel names
294
+ */
295
+ getChannelNames() {
296
+ return Object.keys(this.config.channels);
297
+ }
298
+ /**
299
+ * Check if a channel is configured
300
+ *
301
+ * @param name - Channel name
302
+ * @returns True if the channel is configured
303
+ */
304
+ hasChannel(name) {
305
+ return name in this.config.channels;
306
+ }
307
+ /**
308
+ * Check if a channel is currently active (cached)
309
+ *
310
+ * @param name - Channel name (uses default if not specified)
311
+ * @returns True if the channel is active
312
+ */
313
+ isChannelActive(name) {
314
+ const channelName = name ?? this.config.default;
315
+ return this.channels.has(channelName);
316
+ }
317
+ /**
318
+ * Get the default channel instance
319
+ *
320
+ * @returns Logger instance
321
+ * @throws Error if default channel cannot be resolved
322
+ * @private
323
+ */
324
+ getDefaultChannel() {
325
+ if (this.defaultChannel) {
326
+ return this.defaultChannel;
327
+ }
328
+ const channelName = this.config.default;
329
+ if (this.channels.has(channelName)) {
330
+ this.defaultChannel = this.channels.get(channelName);
331
+ return this.defaultChannel;
332
+ }
333
+ this.defaultChannel = this.resolve(channelName);
334
+ this.channels.set(channelName, this.defaultChannel);
335
+ return this.defaultChannel;
336
+ }
337
+ /**
338
+ * Resolve a channel by name
339
+ *
340
+ * Creates a new logger instance based on channel configuration.
341
+ *
342
+ * @param name - Channel name
343
+ * @returns Logger instance
344
+ * @throws Error if channel is not configured
345
+ * @private
346
+ */
347
+ resolve(name) {
348
+ const channelConfig = this.config.channels[name];
349
+ if (!channelConfig) {
350
+ throw new Error(
351
+ `Logger channel [${name}] is not configured. Available channels: ${Object.keys(this.config.channels).join(", ")}`
352
+ );
353
+ }
354
+ return new Logger(channelConfig);
355
+ }
356
+ };
357
+ LoggerService = __decorateClass([
358
+ (0, import_react_di.Injectable)(),
359
+ __decorateParam(0, (0, import_react_di.Inject)(LOGGER_CONFIG))
360
+ ], LoggerService);
361
+
362
+ // src/logger.module.ts
363
+ var import_react_di2 = require("@abdokouta/react-di");
364
+
365
+ // src/formatters/pretty.formatter.ts
366
+ var LEVEL_EMOJI = {
367
+ [0 /* Debug */]: "\u{1F41B}",
368
+ [1 /* Info */]: "\u2139\uFE0F",
369
+ [2 /* Warn */]: "\u26A0\uFE0F",
370
+ [3 /* Error */]: "\u274C",
371
+ [4 /* Fatal */]: "\u{1F480}"
372
+ };
373
+ var LEVEL_LABEL = {
374
+ [0 /* Debug */]: "DEBUG",
375
+ [1 /* Info */]: "INFO",
376
+ [2 /* Warn */]: "WARN",
377
+ [3 /* Error */]: "ERROR",
378
+ [4 /* Fatal */]: "FATAL"
379
+ };
380
+ var LEVEL_COLORS = {
381
+ [0 /* Debug */]: "color: #8B8B8B",
382
+ [1 /* Info */]: "color: #2196F3",
383
+ [2 /* Warn */]: "color: #FF9800",
384
+ [3 /* Error */]: "color: #F44336",
385
+ [4 /* Fatal */]: "color: #FFFFFF; background: #F44336; font-weight: bold; padding: 1px 4px; border-radius: 2px"
386
+ };
387
+ var PrettyFormatter = class {
388
+ /**
389
+ * Format a log entry into a pretty, human-readable string with
390
+ * emoji prefix, level badge, timestamp, message, and context.
391
+ *
392
+ * The returned string includes `%c` placeholders for CSS styling
393
+ * in the browser console. The ConsoleTransporter is responsible
394
+ * for passing the corresponding style strings.
395
+ *
396
+ * @param entry - The log entry to format.
397
+ * @returns The formatted string with CSS style placeholders.
398
+ */
399
+ format(entry) {
400
+ const emoji = LEVEL_EMOJI[entry.level];
401
+ const label = LEVEL_LABEL[entry.level];
402
+ const time = this.formatTimestamp(entry.timestamp);
403
+ const contextStr = this.formatContext(entry.context);
404
+ return `${emoji} %c[${label}]%c [${time}] ${entry.message}${contextStr}`;
405
+ }
406
+ /**
407
+ * Extract a short time string (HH:mm:ss.SSS) from an ISO timestamp.
408
+ *
409
+ * @param timestamp - ISO 8601 timestamp string.
410
+ * @returns A short time-only representation.
411
+ */
412
+ formatTimestamp(timestamp) {
413
+ try {
414
+ const date = new Date(timestamp);
415
+ const hours = String(date.getHours()).padStart(2, "0");
416
+ const minutes = String(date.getMinutes()).padStart(2, "0");
417
+ const seconds = String(date.getSeconds()).padStart(2, "0");
418
+ const ms = String(date.getMilliseconds()).padStart(3, "0");
419
+ return `${hours}:${minutes}:${seconds}.${ms}`;
420
+ } catch {
421
+ return timestamp;
422
+ }
423
+ }
424
+ /**
425
+ * Serialize context data into a compact, readable string.
426
+ * Returns an empty string when context is empty to keep output clean.
427
+ *
428
+ * @param context - The context record to serialize.
429
+ * @returns A formatted context string or empty string.
430
+ */
431
+ formatContext(context) {
432
+ const keys = Object.keys(context);
433
+ if (keys.length === 0) {
434
+ return "";
435
+ }
436
+ try {
437
+ return ` ${JSON.stringify(context)}`;
438
+ } catch {
439
+ return ` [context serialization failed]`;
440
+ }
441
+ }
442
+ };
443
+
444
+ // src/formatters/json.formatter.ts
445
+ var LEVEL_STRING = {
446
+ [0 /* Debug */]: "debug",
447
+ [1 /* Info */]: "info",
448
+ [2 /* Warn */]: "warn",
449
+ [3 /* Error */]: "error",
450
+ [4 /* Fatal */]: "fatal"
451
+ };
452
+ var JsonFormatter = class {
453
+ /**
454
+ * Format a log entry as a JSON string.
455
+ *
456
+ * Produces a flat JSON object with level (as string), message,
457
+ * timestamp, and context fields. Context is omitted when empty
458
+ * to keep output compact.
459
+ *
460
+ * @param entry - The log entry to format.
461
+ * @returns A JSON-serialized string representation of the entry.
462
+ */
463
+ format(entry) {
464
+ const payload = {
465
+ level: LEVEL_STRING[entry.level],
466
+ message: entry.message,
467
+ timestamp: entry.timestamp
468
+ };
469
+ if (Object.keys(entry.context).length > 0) {
470
+ payload.context = entry.context;
471
+ }
472
+ try {
473
+ return JSON.stringify(payload);
474
+ } catch {
475
+ return JSON.stringify({
476
+ level: LEVEL_STRING[entry.level],
477
+ message: entry.message,
478
+ timestamp: entry.timestamp,
479
+ error: "context serialization failed"
480
+ });
481
+ }
482
+ }
483
+ };
484
+
485
+ // src/formatters/simple.formatter.ts
486
+ var LEVEL_LABEL2 = {
487
+ [0 /* Debug */]: "DEBUG",
488
+ [1 /* Info */]: "INFO",
489
+ [2 /* Warn */]: "WARN",
490
+ [3 /* Error */]: "ERROR",
491
+ [4 /* Fatal */]: "FATAL"
492
+ };
493
+ var SimpleFormatter = class {
494
+ /**
495
+ * Format a log entry into a plain text line.
496
+ *
497
+ * Output pattern: `[LEVEL] [timestamp] message {context}`
498
+ *
499
+ * @param entry - The log entry to format.
500
+ * @returns A plain text string with no styling.
501
+ */
502
+ format(entry) {
503
+ const label = LEVEL_LABEL2[entry.level];
504
+ const contextStr = this.formatContext(entry.context);
505
+ return `[${label}] [${entry.timestamp}] ${entry.message}${contextStr}`;
506
+ }
507
+ /**
508
+ * Serialize context data into a compact string.
509
+ * Returns an empty string when context is empty.
510
+ *
511
+ * @param context - The context record to serialize.
512
+ * @returns A formatted context string or empty string.
513
+ */
514
+ formatContext(context) {
515
+ const keys = Object.keys(context);
516
+ if (keys.length === 0) {
517
+ return "";
518
+ }
519
+ try {
520
+ return ` ${JSON.stringify(context)}`;
521
+ } catch {
522
+ return " [context serialization failed]";
523
+ }
524
+ }
525
+ };
526
+
527
+ // src/transporters/silent.transporter.ts
528
+ var SilentTransporter = class {
529
+ /**
530
+ * Create a new SilentTransporter instance.
531
+ * All parameters are optional and exist only for interface compliance.
532
+ */
533
+ constructor() {
534
+ /**
535
+ The formatter instance (maintained for interface compliance). */
536
+ __publicField(this, "_formatter");
537
+ /**
538
+ The minimum log level (maintained for interface compliance). */
539
+ __publicField(this, "_level");
540
+ this._formatter = new SimpleFormatter();
541
+ this._level = 0 /* Debug */;
542
+ }
543
+ /**
544
+ * No-op transport method. Silently discards the entry.
545
+ *
546
+ * @param _entry - The log entry (ignored).
547
+ */
548
+ transport(_entry) {
549
+ }
550
+ /**
551
+ * Replace the current formatter.
552
+ *
553
+ * @param formatter - The new formatter instance.
554
+ */
555
+ setFormatter(formatter) {
556
+ this._formatter = formatter;
557
+ }
558
+ /**
559
+ * Retrieve the currently assigned formatter.
560
+ *
561
+ * @returns The active formatter instance.
562
+ */
563
+ getFormatter() {
564
+ return this._formatter;
565
+ }
566
+ /**
567
+ * Get the minimum log level.
568
+ *
569
+ * @returns The current minimum LogLevel threshold.
570
+ */
571
+ getLevel() {
572
+ return this._level;
573
+ }
574
+ /**
575
+ * Set the minimum log level.
576
+ *
577
+ * @param level - The new minimum LogLevel threshold.
578
+ */
579
+ setLevel(level) {
580
+ this._level = level;
581
+ }
582
+ };
583
+
584
+ // src/transporters/console.transporter.ts
585
+ var ConsoleTransporter = class {
586
+ /**
587
+ * Create a new ConsoleTransporter instance.
588
+ *
589
+ * @param options - Optional configuration for formatter and minimum level.
590
+ */
591
+ constructor(options = {}) {
592
+ /**
593
+ The formatter used to convert log entries into output strings. */
594
+ __publicField(this, "_formatter");
595
+ /**
596
+ The minimum log level threshold for this transporter. */
597
+ __publicField(this, "_level");
598
+ this._formatter = options.formatter ?? new PrettyFormatter();
599
+ this._level = options.level ?? 0 /* Debug */;
600
+ }
601
+ /**
602
+ * Deliver a log entry to the browser console.
603
+ *
604
+ * Routes the entry to the appropriate `console.*` method based on
605
+ * the log level. When using the PrettyFormatter, CSS styles are
606
+ * applied via `%c` placeholders for colored output.
607
+ *
608
+ * Entries below the configured minimum level are silently skipped.
609
+ *
610
+ * @param entry - The log entry to output.
611
+ */
612
+ transport(entry) {
613
+ if (entry.level < this._level) {
614
+ return;
615
+ }
616
+ const formatted = this._formatter.format(entry);
617
+ const method = this.resolveConsoleMethod(entry.level);
618
+ if (this._formatter instanceof PrettyFormatter) {
619
+ const levelStyle = LEVEL_COLORS[entry.level];
620
+ const resetStyle = "color: inherit";
621
+ method(formatted, levelStyle, resetStyle);
622
+ } else {
623
+ method(formatted);
624
+ }
625
+ }
626
+ /**
627
+ * Replace the current formatter.
628
+ *
629
+ * @param formatter - The new formatter instance to use.
630
+ */
631
+ setFormatter(formatter) {
632
+ this._formatter = formatter;
633
+ }
634
+ /**
635
+ * Retrieve the currently assigned formatter.
636
+ *
637
+ * @returns The active formatter instance.
638
+ */
639
+ getFormatter() {
640
+ return this._formatter;
641
+ }
642
+ /**
643
+ * Get the minimum log level this transporter handles.
644
+ *
645
+ * @returns The current minimum LogLevel threshold.
646
+ */
647
+ getLevel() {
648
+ return this._level;
649
+ }
650
+ /**
651
+ * Set the minimum log level this transporter handles.
652
+ *
653
+ * @param level - The new minimum LogLevel threshold.
654
+ */
655
+ setLevel(level) {
656
+ this._level = level;
657
+ }
658
+ /**
659
+ * Resolve the appropriate `console.*` method for a given log level.
660
+ *
661
+ * Maps logger severity levels to the closest browser console method
662
+ * to ensure proper DevTools filtering and visual distinction.
663
+ *
664
+ * @param level - The log level to resolve.
665
+ * @returns The bound console method function.
666
+ */
667
+ resolveConsoleMethod(level) {
668
+ switch (level) {
669
+ case 0 /* Debug */:
670
+ return console.debug.bind(console);
671
+ case 1 /* Info */:
672
+ return console.info.bind(console);
673
+ case 2 /* Warn */:
674
+ return console.warn.bind(console);
675
+ case 3 /* Error */:
676
+ return console.error.bind(console);
677
+ case 4 /* Fatal */:
678
+ return console.error.bind(console);
679
+ default:
680
+ return console.log.bind(console);
681
+ }
682
+ }
683
+ };
684
+
685
+ // src/logger.module.ts
686
+ var LoggerModule = class {
687
+ /**
688
+ * Configure the logger module
689
+ *
690
+ * @param config - Logger configuration (can be passed directly without defineConfig)
691
+ * @returns Dynamic module
692
+ *
693
+ * @example
694
+ * ```typescript
695
+ * LoggerModule.forRoot({
696
+ * default: 'console',
697
+ * channels: {
698
+ * console: {
699
+ * transporters: [new ConsoleTransporter()],
700
+ * },
701
+ * },
702
+ * })
703
+ * ```
704
+ */
705
+ static forRoot(config) {
706
+ const processedConfig = LoggerModule.processConfig(config);
707
+ return (0, import_react_di2.forRoot)(LoggerModule, {
708
+ providers: [
709
+ {
710
+ provide: LOGGER_CONFIG,
711
+ useValue: processedConfig
712
+ },
713
+ LoggerService
714
+ ],
715
+ exports: [LoggerService, LOGGER_CONFIG]
716
+ });
717
+ }
718
+ /**
719
+ * Process configuration to add default transporters if needed
720
+ *
721
+ * @param config - Raw configuration
722
+ * @returns Processed configuration with defaults
723
+ * @private
724
+ */
725
+ static processConfig(config) {
726
+ const processedChannels = {};
727
+ for (const name in config.channels) {
728
+ if (Object.prototype.hasOwnProperty.call(config.channels, name)) {
729
+ const channelConfig = config.channels[name];
730
+ if (!channelConfig) continue;
731
+ if (!channelConfig.transporters || channelConfig.transporters.length === 0) {
732
+ if (name === "silent") {
733
+ processedChannels[name] = {
734
+ ...channelConfig,
735
+ transporters: [new SilentTransporter()]
736
+ };
737
+ } else {
738
+ processedChannels[name] = {
739
+ ...channelConfig,
740
+ transporters: [new ConsoleTransporter()]
741
+ };
742
+ }
743
+ } else {
744
+ processedChannels[name] = channelConfig;
745
+ }
746
+ }
747
+ }
748
+ return {
749
+ ...config,
750
+ channels: processedChannels
751
+ };
752
+ }
753
+ };
754
+ LoggerModule = __decorateClass([
755
+ (0, import_react_di2.Module)({})
756
+ ], LoggerModule);
757
+
758
+ // src/config/logger.config.ts
759
+ function defineConfig(config) {
760
+ return config;
761
+ }
762
+
763
+ // src/transporters/storage.transporter.ts
764
+ var DEFAULT_STORAGE_KEY = "logger:entries";
765
+ var DEFAULT_MAX_ENTRIES = 100;
766
+ var StorageTransporter = class {
767
+ /**
768
+ * Create a new StorageTransporter instance.
769
+ *
770
+ * @param options - Optional configuration for storage key, limits, and formatter.
771
+ */
772
+ constructor(options = {}) {
773
+ /**
774
+ The formatter used to convert log entries into storable strings. */
775
+ __publicField(this, "_formatter");
776
+ /**
777
+ The minimum log level threshold for this transporter. */
778
+ __publicField(this, "_level");
779
+ /**
780
+ The localStorage key for persisting entries. */
781
+ __publicField(this, "_key");
782
+ /**
783
+ Maximum number of entries to retain. */
784
+ __publicField(this, "_maxEntries");
785
+ this._formatter = options.formatter ?? new JsonFormatter();
786
+ this._level = options.level ?? 0 /* Debug */;
787
+ this._key = options.key ?? DEFAULT_STORAGE_KEY;
788
+ this._maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
789
+ }
790
+ /**
791
+ * Persist a log entry to localStorage.
792
+ *
793
+ * The entry is formatted, appended to the existing entries array,
794
+ * and trimmed to the maximum entry limit. If localStorage is
795
+ * unavailable or full, the error is silently swallowed to avoid
796
+ * crashing the application.
797
+ *
798
+ * @param entry - The log entry to persist.
799
+ */
800
+ transport(entry) {
801
+ if (entry.level < this._level) {
802
+ return;
803
+ }
804
+ try {
805
+ const formatted = this._formatter.format(entry);
806
+ const entries = this.readEntries();
807
+ entries.push(formatted);
808
+ while (entries.length > this._maxEntries) {
809
+ entries.shift();
810
+ }
811
+ localStorage.setItem(this._key, JSON.stringify(entries));
812
+ } catch {
813
+ }
814
+ }
815
+ /**
816
+ * Replace the current formatter.
817
+ *
818
+ * @param formatter - The new formatter instance.
819
+ */
820
+ setFormatter(formatter) {
821
+ this._formatter = formatter;
822
+ }
823
+ /**
824
+ * Retrieve the currently assigned formatter.
825
+ *
826
+ * @returns The active formatter instance.
827
+ */
828
+ getFormatter() {
829
+ return this._formatter;
830
+ }
831
+ /**
832
+ * Get the minimum log level this transporter handles.
833
+ *
834
+ * @returns The current minimum LogLevel threshold.
835
+ */
836
+ getLevel() {
837
+ return this._level;
838
+ }
839
+ /**
840
+ * Set the minimum log level this transporter handles.
841
+ *
842
+ * @param level - The new minimum LogLevel threshold.
843
+ */
844
+ setLevel(level) {
845
+ this._level = level;
846
+ }
847
+ /**
848
+ * Clear all stored log entries from localStorage.
849
+ * Useful for manual cleanup or when resetting application state.
850
+ */
851
+ clear() {
852
+ try {
853
+ localStorage.removeItem(this._key);
854
+ } catch {
855
+ }
856
+ }
857
+ /**
858
+ * Retrieve all stored log entries from localStorage.
859
+ *
860
+ * @returns An array of formatted log entry strings.
861
+ */
862
+ getEntries() {
863
+ return this.readEntries();
864
+ }
865
+ /**
866
+ * Read the current entries array from localStorage.
867
+ * Returns an empty array if the key does not exist or parsing fails.
868
+ *
869
+ * @returns The parsed entries array.
870
+ */
871
+ readEntries() {
872
+ try {
873
+ const raw = localStorage.getItem(this._key);
874
+ if (!raw) {
875
+ return [];
876
+ }
877
+ const parsed = JSON.parse(raw);
878
+ return Array.isArray(parsed) ? parsed : [];
879
+ } catch {
880
+ return [];
881
+ }
882
+ }
883
+ };
884
+
885
+ // src/hooks/use-logger/use-logger.hook.ts
886
+ var import_react_di3 = require("@abdokouta/react-di");
887
+ function useLogger(channelName) {
888
+ const loggerService = (0, import_react_di3.useInject)(LoggerService);
889
+ if (channelName) {
890
+ return loggerService.channel(channelName);
891
+ }
892
+ return loggerService;
893
+ }
894
+
895
+ // src/hooks/use-logger-context/use-logger-context.hook.ts
896
+ var import_react = require("react");
897
+ function useLoggerContext(context, channelName) {
898
+ const logger = useLogger(channelName);
899
+ (0, import_react.useEffect)(() => {
900
+ logger.withContext(context);
901
+ return () => {
902
+ logger.withoutContext(Object.keys(context));
903
+ };
904
+ }, [logger, context]);
905
+ }
906
+ //# sourceMappingURL=index.js.map