@opensourcekd/ng-common-libs 1.1.8

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 ADDED
@@ -0,0 +1,1255 @@
1
+ import { Subject, throwError, timer, Observable } from 'rxjs';
2
+ import { filter, map, retry, catchError, tap } from 'rxjs/operators';
3
+ import { Injectable, inject } from '@angular/core';
4
+ import { Router } from '@angular/router';
5
+ import { HttpResponse } from '@angular/common/http';
6
+
7
+ /**
8
+ * EventBus - A centralized event bus for application-wide communication
9
+ * Framework-agnostic implementation using only RxJS
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Create an instance
14
+ * const eventBus = new EventBus();
15
+ *
16
+ * // Emit an event
17
+ * eventBus.emit('user:login', { userId: '123', username: 'john' });
18
+ *
19
+ * // Subscribe to an event
20
+ * eventBus.on('user:login').subscribe(data => {
21
+ * console.log('User logged in:', data);
22
+ * });
23
+ * ```
24
+ */
25
+ class EventBus {
26
+ eventSubject = new Subject();
27
+ /**
28
+ * Emit an event with optional data
29
+ * @param eventType - The type/name of the event
30
+ * @param data - Optional data to send with the event
31
+ */
32
+ emit(eventType, data) {
33
+ this.eventSubject.next({
34
+ type: eventType,
35
+ data,
36
+ timestamp: Date.now()
37
+ });
38
+ }
39
+ /**
40
+ * Subscribe to a specific event type
41
+ * @param eventType - The type/name of the event to listen for
42
+ * @returns Observable that emits the event data
43
+ */
44
+ on(eventType) {
45
+ return this.eventSubject.asObservable().pipe(filter(event => event.type === eventType), map(event => event.data));
46
+ }
47
+ /**
48
+ * Subscribe to multiple event types
49
+ * @param eventTypes - Array of event types to listen for
50
+ * @returns Observable that emits the full event payload
51
+ */
52
+ onMultiple(eventTypes) {
53
+ return this.eventSubject.asObservable().pipe(filter(event => eventTypes.includes(event.type)));
54
+ }
55
+ /**
56
+ * Subscribe to all events
57
+ * @returns Observable that emits all event payloads
58
+ */
59
+ onAll() {
60
+ return this.eventSubject.asObservable();
61
+ }
62
+ }
63
+
64
+ /**
65
+ * TokenManager - Manages authentication tokens
66
+ * Framework-agnostic implementation using browser storage APIs
67
+ * Handles storage, retrieval, and validation of JWT tokens
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const tokenManager = new TokenManager();
72
+ *
73
+ * // Store a token
74
+ * tokenManager.setToken('your-jwt-token');
75
+ *
76
+ * // Check if authenticated
77
+ * if (tokenManager.isAuthenticated()) {
78
+ * const userData = tokenManager.getUserFromToken();
79
+ * }
80
+ * ```
81
+ */
82
+ class TokenManager {
83
+ TOKEN_KEY = 'auth_token';
84
+ REFRESH_TOKEN_KEY = 'refresh_token';
85
+ useSessionStorage = false;
86
+ /**
87
+ * Configure token manager
88
+ */
89
+ configure(config) {
90
+ if (config.tokenKey) {
91
+ this.TOKEN_KEY = config.tokenKey;
92
+ }
93
+ if (config.refreshTokenKey) {
94
+ this.REFRESH_TOKEN_KEY = config.refreshTokenKey;
95
+ }
96
+ if (config.useSessionStorage !== undefined) {
97
+ this.useSessionStorage = config.useSessionStorage;
98
+ }
99
+ }
100
+ /**
101
+ * Get the storage mechanism based on configuration
102
+ */
103
+ getStorage() {
104
+ return this.useSessionStorage ? sessionStorage : localStorage;
105
+ }
106
+ /**
107
+ * Set authentication token
108
+ */
109
+ setToken(token) {
110
+ this.getStorage().setItem(this.TOKEN_KEY, token);
111
+ }
112
+ /**
113
+ * Get authentication token
114
+ */
115
+ getToken() {
116
+ return this.getStorage().getItem(this.TOKEN_KEY);
117
+ }
118
+ /**
119
+ * Remove authentication token
120
+ */
121
+ removeToken() {
122
+ this.getStorage().removeItem(this.TOKEN_KEY);
123
+ }
124
+ /**
125
+ * Set refresh token
126
+ */
127
+ setRefreshToken(token) {
128
+ this.getStorage().setItem(this.REFRESH_TOKEN_KEY, token);
129
+ }
130
+ /**
131
+ * Get refresh token
132
+ */
133
+ getRefreshToken() {
134
+ return this.getStorage().getItem(this.REFRESH_TOKEN_KEY);
135
+ }
136
+ /**
137
+ * Remove refresh token
138
+ */
139
+ removeRefreshToken() {
140
+ this.getStorage().removeItem(this.REFRESH_TOKEN_KEY);
141
+ }
142
+ /**
143
+ * Clear all tokens
144
+ */
145
+ clearTokens() {
146
+ this.removeToken();
147
+ this.removeRefreshToken();
148
+ }
149
+ /**
150
+ * Check if token exists
151
+ */
152
+ hasToken() {
153
+ return !!this.getToken();
154
+ }
155
+ /**
156
+ * Decode JWT token (without verification)
157
+ * @param token - JWT token to decode
158
+ * @returns Decoded token payload or null if invalid
159
+ */
160
+ decodeToken(token) {
161
+ const tokenToDecode = token || this.getToken();
162
+ if (!tokenToDecode)
163
+ return null;
164
+ try {
165
+ const parts = tokenToDecode.split('.');
166
+ if (parts.length !== 3)
167
+ return null;
168
+ const payload = parts[1];
169
+ const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
170
+ return decoded;
171
+ }
172
+ catch (error) {
173
+ console.error('Error decoding token:', error);
174
+ return null;
175
+ }
176
+ }
177
+ /**
178
+ * Check if token is expired
179
+ * @param token - Optional token to check (defaults to stored token)
180
+ * @returns true if token is expired or invalid
181
+ */
182
+ isTokenExpired(token) {
183
+ const decoded = this.decodeToken(token);
184
+ if (!decoded || !decoded.exp)
185
+ return true;
186
+ const expirationDate = new Date(decoded.exp * 1000);
187
+ return expirationDate <= new Date();
188
+ }
189
+ /**
190
+ * Check if user is authenticated (has valid, non-expired token)
191
+ */
192
+ isAuthenticated() {
193
+ return this.hasToken() && !this.isTokenExpired();
194
+ }
195
+ /**
196
+ * Get token expiration date
197
+ */
198
+ getTokenExpirationDate(token) {
199
+ const decoded = this.decodeToken(token);
200
+ if (!decoded || !decoded.exp)
201
+ return null;
202
+ return new Date(decoded.exp * 1000);
203
+ }
204
+ /**
205
+ * Get user data from token
206
+ */
207
+ getUserFromToken(token) {
208
+ return this.decodeToken(token);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * StorageManager - Type-safe wrapper for browser storage
214
+ * Framework-agnostic implementation using browser storage APIs
215
+ * Provides JSON serialization/deserialization and error handling
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * const storage = new StorageManager();
220
+ *
221
+ * // Store data
222
+ * storage.setLocal('user-prefs', { theme: 'dark', lang: 'en' });
223
+ *
224
+ * // Retrieve data
225
+ * const prefs = storage.getLocal('user-prefs');
226
+ *
227
+ * // With expiration
228
+ * storage.setWithExpiration('temp-data', someData, 3600000); // 1 hour
229
+ * ```
230
+ */
231
+ class StorageManager {
232
+ /**
233
+ * Set item in localStorage with JSON serialization
234
+ */
235
+ setLocal(key, value) {
236
+ try {
237
+ const serialized = JSON.stringify(value);
238
+ localStorage.setItem(key, serialized);
239
+ return true;
240
+ }
241
+ catch (error) {
242
+ console.error('Error setting localStorage item:', error);
243
+ return false;
244
+ }
245
+ }
246
+ /**
247
+ * Get item from localStorage with JSON deserialization
248
+ */
249
+ getLocal(key, defaultValue) {
250
+ try {
251
+ const item = localStorage.getItem(key);
252
+ if (item === null) {
253
+ return defaultValue !== undefined ? defaultValue : null;
254
+ }
255
+ return JSON.parse(item);
256
+ }
257
+ catch (error) {
258
+ console.error('Error getting localStorage item:', error);
259
+ return defaultValue !== undefined ? defaultValue : null;
260
+ }
261
+ }
262
+ /**
263
+ * Remove item from localStorage
264
+ */
265
+ removeLocal(key) {
266
+ try {
267
+ localStorage.removeItem(key);
268
+ }
269
+ catch (error) {
270
+ console.error('Error removing localStorage item:', error);
271
+ }
272
+ }
273
+ /**
274
+ * Clear all localStorage items
275
+ */
276
+ clearLocal() {
277
+ try {
278
+ localStorage.clear();
279
+ }
280
+ catch (error) {
281
+ console.error('Error clearing localStorage:', error);
282
+ }
283
+ }
284
+ /**
285
+ * Check if key exists in localStorage
286
+ */
287
+ hasLocal(key) {
288
+ try {
289
+ return localStorage.getItem(key) !== null;
290
+ }
291
+ catch (error) {
292
+ console.error('Error checking localStorage key:', error);
293
+ return false;
294
+ }
295
+ }
296
+ /**
297
+ * Get all localStorage keys
298
+ */
299
+ getLocalKeys() {
300
+ try {
301
+ return Object.keys(localStorage);
302
+ }
303
+ catch (error) {
304
+ console.error('Error getting localStorage keys:', error);
305
+ return [];
306
+ }
307
+ }
308
+ /**
309
+ * Set item in sessionStorage with JSON serialization
310
+ */
311
+ setSession(key, value) {
312
+ try {
313
+ const serialized = JSON.stringify(value);
314
+ sessionStorage.setItem(key, serialized);
315
+ return true;
316
+ }
317
+ catch (error) {
318
+ console.error('Error setting sessionStorage item:', error);
319
+ return false;
320
+ }
321
+ }
322
+ /**
323
+ * Get item from sessionStorage with JSON deserialization
324
+ */
325
+ getSession(key, defaultValue) {
326
+ try {
327
+ const item = sessionStorage.getItem(key);
328
+ if (item === null) {
329
+ return defaultValue !== undefined ? defaultValue : null;
330
+ }
331
+ return JSON.parse(item);
332
+ }
333
+ catch (error) {
334
+ console.error('Error getting sessionStorage item:', error);
335
+ return defaultValue !== undefined ? defaultValue : null;
336
+ }
337
+ }
338
+ /**
339
+ * Remove item from sessionStorage
340
+ */
341
+ removeSession(key) {
342
+ try {
343
+ sessionStorage.removeItem(key);
344
+ }
345
+ catch (error) {
346
+ console.error('Error removing sessionStorage item:', error);
347
+ }
348
+ }
349
+ /**
350
+ * Clear all sessionStorage items
351
+ */
352
+ clearSession() {
353
+ try {
354
+ sessionStorage.clear();
355
+ }
356
+ catch (error) {
357
+ console.error('Error clearing sessionStorage:', error);
358
+ }
359
+ }
360
+ /**
361
+ * Check if key exists in sessionStorage
362
+ */
363
+ hasSession(key) {
364
+ try {
365
+ return sessionStorage.getItem(key) !== null;
366
+ }
367
+ catch (error) {
368
+ console.error('Error checking sessionStorage key:', error);
369
+ return false;
370
+ }
371
+ }
372
+ /**
373
+ * Get all sessionStorage keys
374
+ */
375
+ getSessionKeys() {
376
+ try {
377
+ return Object.keys(sessionStorage);
378
+ }
379
+ catch (error) {
380
+ console.error('Error getting sessionStorage keys:', error);
381
+ return [];
382
+ }
383
+ }
384
+ /**
385
+ * Set item with expiration time
386
+ * @param key - Storage key
387
+ * @param value - Value to store
388
+ * @param expirationMs - Expiration time in milliseconds
389
+ * @param useSession - Use sessionStorage instead of localStorage
390
+ */
391
+ setWithExpiration(key, value, expirationMs, useSession = false) {
392
+ const item = {
393
+ value,
394
+ expiration: Date.now() + expirationMs
395
+ };
396
+ return useSession
397
+ ? this.setSession(key, item)
398
+ : this.setLocal(key, item);
399
+ }
400
+ /**
401
+ * Get item with expiration check
402
+ * Returns null if item is expired
403
+ */
404
+ getWithExpiration(key, useSession = false) {
405
+ const item = useSession
406
+ ? this.getSession(key)
407
+ : this.getLocal(key);
408
+ if (!item)
409
+ return null;
410
+ if (Date.now() > item.expiration) {
411
+ // Item expired, remove it
412
+ if (useSession) {
413
+ this.removeSession(key);
414
+ }
415
+ else {
416
+ this.removeLocal(key);
417
+ }
418
+ return null;
419
+ }
420
+ return item.value;
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Log level enum
426
+ */
427
+ var LogLevel;
428
+ (function (LogLevel) {
429
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
430
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
431
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
432
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
433
+ LogLevel[LogLevel["NONE"] = 4] = "NONE";
434
+ })(LogLevel || (LogLevel = {}));
435
+ /**
436
+ * Logger - Centralized logging utility
437
+ * Framework-agnostic implementation using browser console APIs
438
+ * Supports different log levels and can be configured globally
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * const logger = new Logger();
443
+ *
444
+ * // Configure
445
+ * logger.configure({
446
+ * level: LogLevel.DEBUG,
447
+ * enableTimestamp: true,
448
+ * prefix: 'MyApp'
449
+ * });
450
+ *
451
+ * // Use
452
+ * logger.info('User logged in', { userId: '123' });
453
+ * logger.error('Failed to load data', error);
454
+ * ```
455
+ */
456
+ class Logger {
457
+ config = {
458
+ level: LogLevel.INFO,
459
+ enableTimestamp: true,
460
+ enableStackTrace: false,
461
+ prefix: ''
462
+ };
463
+ /**
464
+ * Configure the logger
465
+ */
466
+ configure(config) {
467
+ this.config = { ...this.config, ...config };
468
+ }
469
+ /**
470
+ * Set log level
471
+ */
472
+ setLevel(level) {
473
+ this.config.level = level;
474
+ }
475
+ /**
476
+ * Get current log level
477
+ */
478
+ getLevel() {
479
+ return this.config.level;
480
+ }
481
+ /**
482
+ * Format log message with timestamp and prefix
483
+ */
484
+ formatMessage(message, level) {
485
+ const parts = [];
486
+ if (this.config.enableTimestamp) {
487
+ parts.push(`[${new Date().toISOString()}]`);
488
+ }
489
+ if (this.config.prefix) {
490
+ parts.push(`[${this.config.prefix}]`);
491
+ }
492
+ parts.push(`[${level}]`);
493
+ parts.push(message);
494
+ return parts.join(' ');
495
+ }
496
+ /**
497
+ * Check if log level should be logged
498
+ */
499
+ shouldLog(level) {
500
+ return level >= this.config.level;
501
+ }
502
+ /**
503
+ * Log debug message
504
+ */
505
+ debug(message, ...args) {
506
+ if (this.shouldLog(LogLevel.DEBUG)) {
507
+ console.debug(this.formatMessage(message, 'DEBUG'), ...args);
508
+ }
509
+ }
510
+ /**
511
+ * Log info message
512
+ */
513
+ info(message, ...args) {
514
+ if (this.shouldLog(LogLevel.INFO)) {
515
+ console.info(this.formatMessage(message, 'INFO'), ...args);
516
+ }
517
+ }
518
+ /**
519
+ * Log warning message
520
+ */
521
+ warn(message, ...args) {
522
+ if (this.shouldLog(LogLevel.WARN)) {
523
+ console.warn(this.formatMessage(message, 'WARN'), ...args);
524
+ }
525
+ }
526
+ /**
527
+ * Log error message
528
+ */
529
+ error(message, error, ...args) {
530
+ if (this.shouldLog(LogLevel.ERROR)) {
531
+ console.error(this.formatMessage(message, 'ERROR'), ...args);
532
+ if (error) {
533
+ console.error(error);
534
+ if (this.config.enableStackTrace && error?.stack) {
535
+ console.error('Stack trace:', error.stack);
536
+ }
537
+ }
538
+ }
539
+ }
540
+ /**
541
+ * Log a group of messages
542
+ */
543
+ group(label, callback) {
544
+ if (this.shouldLog(LogLevel.INFO)) {
545
+ console.group(this.formatMessage(label, 'GROUP'));
546
+ callback();
547
+ console.groupEnd();
548
+ }
549
+ }
550
+ /**
551
+ * Log a collapsed group of messages
552
+ */
553
+ groupCollapsed(label, callback) {
554
+ if (this.shouldLog(LogLevel.INFO)) {
555
+ console.groupCollapsed(this.formatMessage(label, 'GROUP'));
556
+ callback();
557
+ console.groupEnd();
558
+ }
559
+ }
560
+ /**
561
+ * Log a table (useful for arrays of objects)
562
+ */
563
+ table(data, columns) {
564
+ if (this.shouldLog(LogLevel.INFO)) {
565
+ console.table(data, columns);
566
+ }
567
+ }
568
+ /**
569
+ * Log execution time of a function
570
+ */
571
+ async time(label, fn) {
572
+ const start = performance.now();
573
+ try {
574
+ const result = await fn();
575
+ const end = performance.now();
576
+ const duration = (end - start).toFixed(2);
577
+ this.info(`${label} completed in ${duration}ms`);
578
+ return result;
579
+ }
580
+ catch (error) {
581
+ const end = performance.now();
582
+ const duration = (end - start).toFixed(2);
583
+ this.error(`${label} failed after ${duration}ms`, error);
584
+ throw error;
585
+ }
586
+ }
587
+ }
588
+
589
+ /******************************************************************************
590
+ Copyright (c) Microsoft Corporation.
591
+
592
+ Permission to use, copy, modify, and/or distribute this software for any
593
+ purpose with or without fee is hereby granted.
594
+
595
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
596
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
597
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
598
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
599
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
600
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
601
+ PERFORMANCE OF THIS SOFTWARE.
602
+ ***************************************************************************** */
603
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
604
+
605
+
606
+ function __decorate(decorators, target, key, desc) {
607
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
608
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
609
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
610
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
611
+ }
612
+
613
+ function __metadata(metadataKey, metadataValue) {
614
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
615
+ }
616
+
617
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
618
+ var e = new Error(message);
619
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
620
+ };
621
+
622
+ /**
623
+ * NgEventEmitter - Angular service wrapper for EventBus
624
+ * Provides Angular dependency injection support for the framework-agnostic EventBus
625
+ *
626
+ * @example
627
+ * ```typescript
628
+ * import { Component, inject } from '@angular/core';
629
+ * import { NgEventEmitter } from 'common-libs';
630
+ *
631
+ * @Component({
632
+ * selector: 'app-example',
633
+ * template: '...'
634
+ * })
635
+ * export class ExampleComponent {
636
+ * private eventEmitter = inject(NgEventEmitter);
637
+ *
638
+ * ngOnInit() {
639
+ * this.eventEmitter.on('user:login').subscribe(data => {
640
+ * console.log('User logged in:', data);
641
+ * });
642
+ * }
643
+ *
644
+ * login() {
645
+ * this.eventEmitter.emit('user:login', { userId: '123' });
646
+ * }
647
+ * }
648
+ * ```
649
+ */
650
+ let NgEventEmitter = class NgEventEmitter extends EventBus {
651
+ constructor() {
652
+ super();
653
+ }
654
+ };
655
+ NgEventEmitter = __decorate([
656
+ Injectable({
657
+ providedIn: 'root'
658
+ }),
659
+ __metadata("design:paramtypes", [])
660
+ ], NgEventEmitter);
661
+
662
+ /**
663
+ * TokenService - Angular service wrapper for TokenManager
664
+ * Provides Angular dependency injection support for the framework-agnostic TokenManager
665
+ *
666
+ * @example
667
+ * ```typescript
668
+ * import { Component, inject } from '@angular/core';
669
+ * import { TokenService } from 'common-libs';
670
+ *
671
+ * export class AuthService {
672
+ * private tokenService = inject(TokenService);
673
+ *
674
+ * login(token: string) {
675
+ * this.tokenService.setToken(token);
676
+ * }
677
+ *
678
+ * isAuthenticated(): boolean {
679
+ * return this.tokenService.isAuthenticated();
680
+ * }
681
+ * }
682
+ * ```
683
+ */
684
+ let TokenService = class TokenService extends TokenManager {
685
+ constructor() {
686
+ console.log("Hey Jude in TokenService of common-libs");
687
+ super();
688
+ }
689
+ };
690
+ TokenService = __decorate([
691
+ Injectable({
692
+ providedIn: 'root'
693
+ }),
694
+ __metadata("design:paramtypes", [])
695
+ ], TokenService);
696
+
697
+ /**
698
+ * StorageService - Angular service wrapper for StorageManager
699
+ * Provides Angular dependency injection support for the framework-agnostic StorageManager
700
+ *
701
+ * @example
702
+ * ```typescript
703
+ * import { Component, inject } from '@angular/core';
704
+ * import { StorageService } from 'common-libs';
705
+ *
706
+ * export class UserPreferencesService {
707
+ * private storage = inject(StorageService);
708
+ *
709
+ * savePreferences(prefs: any) {
710
+ * this.storage.setLocal('user-prefs', prefs);
711
+ * }
712
+ *
713
+ * getPreferences() {
714
+ * return this.storage.getLocal('user-prefs');
715
+ * }
716
+ * }
717
+ * ```
718
+ */
719
+ let StorageService = class StorageService extends StorageManager {
720
+ constructor() {
721
+ super();
722
+ }
723
+ };
724
+ StorageService = __decorate([
725
+ Injectable({
726
+ providedIn: 'root'
727
+ }),
728
+ __metadata("design:paramtypes", [])
729
+ ], StorageService);
730
+
731
+ /**
732
+ * LoggerService - Angular service wrapper for Logger
733
+ * Provides Angular dependency injection support for the framework-agnostic Logger
734
+ *
735
+ * @example
736
+ * ```typescript
737
+ * import { Component, inject } from '@angular/core';
738
+ * import { LoggerService, LogLevel } from 'common-libs';
739
+ *
740
+ * export class MyService {
741
+ * private logger = inject(LoggerService);
742
+ *
743
+ * constructor() {
744
+ * this.logger.configure({
745
+ * level: LogLevel.DEBUG,
746
+ * prefix: 'MyApp'
747
+ * });
748
+ * }
749
+ *
750
+ * doSomething() {
751
+ * this.logger.info('Doing something');
752
+ * }
753
+ * }
754
+ * ```
755
+ */
756
+ let LoggerService = class LoggerService extends Logger {
757
+ constructor() {
758
+ super();
759
+ }
760
+ };
761
+ LoggerService = __decorate([
762
+ Injectable({
763
+ providedIn: 'root'
764
+ }),
765
+ __metadata("design:paramtypes", [])
766
+ ], LoggerService);
767
+
768
+ /**
769
+ * PermissionService - Manages user permissions and roles
770
+ */
771
+ let PermissionService = class PermissionService {
772
+ tokenService = inject(TokenService);
773
+ /**
774
+ * Check if user has a specific permission
775
+ */
776
+ hasPermission(permission) {
777
+ const user = this.tokenService.getUserFromToken();
778
+ const permissions = user?.permissions || [];
779
+ return permissions.includes(permission);
780
+ }
781
+ /**
782
+ * Check if user has any of the specified permissions
783
+ */
784
+ hasAnyPermission(permissions) {
785
+ return permissions.some(permission => this.hasPermission(permission));
786
+ }
787
+ /**
788
+ * Check if user has all of the specified permissions
789
+ */
790
+ hasAllPermissions(permissions) {
791
+ return permissions.every(permission => this.hasPermission(permission));
792
+ }
793
+ /**
794
+ * Check if user has a specific role
795
+ */
796
+ hasRole(role) {
797
+ const user = this.tokenService.getUserFromToken();
798
+ const roles = user?.roles || [];
799
+ return roles.includes(role);
800
+ }
801
+ /**
802
+ * Check if user has any of the specified roles
803
+ */
804
+ hasAnyRole(roles) {
805
+ return roles.some(role => this.hasRole(role));
806
+ }
807
+ /**
808
+ * Check if user has all of the specified roles
809
+ */
810
+ hasAllRoles(roles) {
811
+ return roles.every(role => this.hasRole(role));
812
+ }
813
+ /**
814
+ * Get all user permissions
815
+ */
816
+ getPermissions() {
817
+ const user = this.tokenService.getUserFromToken();
818
+ return user?.permissions || [];
819
+ }
820
+ /**
821
+ * Get all user roles
822
+ */
823
+ getRoles() {
824
+ const user = this.tokenService.getUserFromToken();
825
+ return user?.roles || [];
826
+ }
827
+ /**
828
+ * Get user ID from token
829
+ */
830
+ getUserId() {
831
+ const user = this.tokenService.getUserFromToken();
832
+ return user?.sub || user?.id || user?.userId || null;
833
+ }
834
+ /**
835
+ * Get username from token
836
+ */
837
+ getUsername() {
838
+ const user = this.tokenService.getUserFromToken();
839
+ return user?.username || user?.name || user?.email || null;
840
+ }
841
+ /**
842
+ * Get user email from token
843
+ */
844
+ getUserEmail() {
845
+ const user = this.tokenService.getUserFromToken();
846
+ return user?.email || null;
847
+ }
848
+ };
849
+ PermissionService = __decorate([
850
+ Injectable({
851
+ providedIn: 'root'
852
+ })
853
+ ], PermissionService);
854
+
855
+ /**
856
+ * Factory function to create an auth guard with configuration
857
+ *
858
+ * @example
859
+ * ```typescript
860
+ * // In routes
861
+ * {
862
+ * path: 'dashboard',
863
+ * component: DashboardComponent,
864
+ * canActivate: [createAuthGuard({ redirectUrl: '/login' })]
865
+ * }
866
+ * ```
867
+ */
868
+ function createAuthGuard(config = {}) {
869
+ return (route, state) => {
870
+ const tokenService = inject(TokenService);
871
+ const router = inject(Router);
872
+ const redirectUrl = config.redirectUrl || '/login';
873
+ const checkExpiration = config.checkExpiration !== false;
874
+ const hasToken = tokenService.hasToken();
875
+ const isExpired = checkExpiration ? tokenService.isTokenExpired() : false;
876
+ if (hasToken && !isExpired) {
877
+ return true;
878
+ }
879
+ // Store the attempted URL for redirecting after login
880
+ const returnUrl = state.url;
881
+ router.navigate([redirectUrl], {
882
+ queryParams: { returnUrl },
883
+ queryParamsHandling: 'merge'
884
+ });
885
+ return false;
886
+ };
887
+ }
888
+ /**
889
+ * Default auth guard - redirects to '/login' if not authenticated
890
+ */
891
+ const authGuard = createAuthGuard();
892
+ /**
893
+ * Permission-based guard factory
894
+ * Checks if user has required permissions from token
895
+ *
896
+ * @example
897
+ * ```typescript
898
+ * {
899
+ * path: 'admin',
900
+ * component: AdminComponent,
901
+ * canActivate: [createPermissionGuard(['admin', 'editor'])]
902
+ * }
903
+ * ```
904
+ */
905
+ function createPermissionGuard(requiredPermissions, config = {}) {
906
+ return (route, state) => {
907
+ const tokenService = inject(TokenService);
908
+ const router = inject(Router);
909
+ const redirectUrl = config.redirectUrl || '/unauthorized';
910
+ // First check authentication
911
+ if (!tokenService.isAuthenticated()) {
912
+ router.navigate(['/login'], {
913
+ queryParams: { returnUrl: state.url }
914
+ });
915
+ return false;
916
+ }
917
+ // Check permissions
918
+ const user = tokenService.getUserFromToken();
919
+ const userPermissions = user?.permissions || user?.roles || [];
920
+ const hasPermission = requiredPermissions.some(permission => userPermissions.includes(permission));
921
+ if (!hasPermission) {
922
+ router.navigate([redirectUrl]);
923
+ return false;
924
+ }
925
+ return true;
926
+ };
927
+ }
928
+ /**
929
+ * Role-based guard factory
930
+ * Checks if user has required role from token
931
+ *
932
+ * @example
933
+ * ```typescript
934
+ * {
935
+ * path: 'admin',
936
+ * component: AdminComponent,
937
+ * canActivate: [createRoleGuard(['admin'])]
938
+ * }
939
+ * ```
940
+ */
941
+ function createRoleGuard(requiredRoles, config = {}) {
942
+ return createPermissionGuard(requiredRoles, config);
943
+ }
944
+
945
+ const defaultConfig$1 = {
946
+ headerName: 'Authorization',
947
+ tokenPrefix: 'Bearer',
948
+ excludedUrls: []
949
+ };
950
+ let interceptorConfig = { ...defaultConfig$1 };
951
+ /**
952
+ * Configure the auth interceptor
953
+ */
954
+ function configureAuthInterceptor(config) {
955
+ interceptorConfig = { ...defaultConfig$1, ...config };
956
+ }
957
+ /**
958
+ * Auth Interceptor - Automatically adds authentication token to HTTP requests
959
+ *
960
+ * @example
961
+ * ```typescript
962
+ * // In app.config.ts
963
+ * export const appConfig: ApplicationConfig = {
964
+ * providers: [
965
+ * provideHttpClient(
966
+ * withInterceptors([authInterceptor])
967
+ * )
968
+ * ]
969
+ * };
970
+ * ```
971
+ */
972
+ const authInterceptor = (req, next) => {
973
+ const tokenService = inject(TokenService);
974
+ const config = interceptorConfig;
975
+ // Check if URL should be excluded
976
+ const isExcluded = config.excludedUrls?.some(url => req.url.includes(url));
977
+ if (isExcluded) {
978
+ return next(req);
979
+ }
980
+ // Get token and add to request if available
981
+ const token = tokenService.getToken();
982
+ if (token) {
983
+ const authReq = req.clone({
984
+ setHeaders: {
985
+ [config.headerName]: `${config.tokenPrefix} ${token}`
986
+ }
987
+ });
988
+ return next(authReq);
989
+ }
990
+ return next(req);
991
+ };
992
+
993
+ const defaultConfig = {
994
+ enableLogging: true,
995
+ retryAttempts: 0,
996
+ retryDelay: 1000,
997
+ retryStatusCodes: [408, 429, 500, 502, 503, 504],
998
+ excludedUrls: []
999
+ };
1000
+ let errorConfig = { ...defaultConfig };
1001
+ /**
1002
+ * Configure the error handling interceptor
1003
+ */
1004
+ function configureErrorHandling(config) {
1005
+ errorConfig = { ...defaultConfig, ...config };
1006
+ }
1007
+ /**
1008
+ * Error handling interceptor - Handles HTTP errors and retries
1009
+ *
1010
+ * @example
1011
+ * ```typescript
1012
+ * // In app.config.ts
1013
+ * export const appConfig: ApplicationConfig = {
1014
+ * providers: [
1015
+ * provideHttpClient(
1016
+ * withInterceptors([errorHandlingInterceptor])
1017
+ * )
1018
+ * ]
1019
+ * };
1020
+ *
1021
+ * // Configure retry behavior
1022
+ * configureErrorHandling({
1023
+ * retryAttempts: 3,
1024
+ * retryDelay: 2000,
1025
+ * retryStatusCodes: [500, 502, 503]
1026
+ * });
1027
+ * ```
1028
+ */
1029
+ const errorHandlingInterceptor = (req, next) => {
1030
+ const logger = inject(LoggerService);
1031
+ const config = errorConfig;
1032
+ // Check if URL should be excluded
1033
+ const isExcluded = config.excludedUrls?.some(url => req.url.includes(url));
1034
+ if (isExcluded) {
1035
+ return next(req);
1036
+ }
1037
+ return next(req).pipe(
1038
+ // Retry logic with exponential backoff
1039
+ retry({
1040
+ count: config.retryAttempts,
1041
+ delay: (error, retryCount) => {
1042
+ // Only retry for specific status codes
1043
+ if (!config.retryStatusCodes?.includes(error.status)) {
1044
+ return throwError(() => error);
1045
+ }
1046
+ const delay = config.retryDelay * Math.pow(2, retryCount - 1);
1047
+ if (config.enableLogging) {
1048
+ logger.warn(`Retrying request (attempt ${retryCount}) after ${delay}ms`, { url: req.url, status: error.status });
1049
+ }
1050
+ return timer(delay);
1051
+ }
1052
+ }),
1053
+ // Error handling
1054
+ catchError((error) => {
1055
+ if (config.enableLogging) {
1056
+ logger.error('HTTP request failed', error, {
1057
+ url: req.url,
1058
+ status: error.status,
1059
+ message: error.message
1060
+ });
1061
+ }
1062
+ return throwError(() => error);
1063
+ }));
1064
+ };
1065
+ /**
1066
+ * HTTP Error class with additional context
1067
+ */
1068
+ class HttpError extends Error {
1069
+ status;
1070
+ statusText;
1071
+ url;
1072
+ originalError;
1073
+ constructor(status, statusText, url, originalError) {
1074
+ super(`HTTP ${status} ${statusText}: ${url}`);
1075
+ this.status = status;
1076
+ this.statusText = statusText;
1077
+ this.url = url;
1078
+ this.originalError = originalError;
1079
+ this.name = 'HttpError';
1080
+ }
1081
+ }
1082
+ /**
1083
+ * Parse HTTP error and return user-friendly message
1084
+ */
1085
+ function parseHttpError(error) {
1086
+ if (error.error instanceof ErrorEvent) {
1087
+ // Client-side error
1088
+ return `Network error: ${error.error.message}`;
1089
+ }
1090
+ else {
1091
+ // Server-side error
1092
+ const errorMessage = error.error?.message || error.message || 'Unknown error';
1093
+ return `Server error (${error.status}): ${errorMessage}`;
1094
+ }
1095
+ }
1096
+ /**
1097
+ * Check if error is a network error
1098
+ */
1099
+ function isNetworkError(error) {
1100
+ return error.error instanceof ErrorEvent || error.status === 0;
1101
+ }
1102
+ /**
1103
+ * Check if error is a server error (5xx)
1104
+ */
1105
+ function isServerError(error) {
1106
+ return error.status >= 500 && error.status < 600;
1107
+ }
1108
+ /**
1109
+ * Check if error is a client error (4xx)
1110
+ */
1111
+ function isClientError(error) {
1112
+ return error.status >= 400 && error.status < 500;
1113
+ }
1114
+
1115
+ const defaultCacheConfig = {
1116
+ enabled: true,
1117
+ maxAge: 60000, // 1 minute
1118
+ excludedUrls: [],
1119
+ cacheableUrls: [],
1120
+ cacheMethods: ['GET']
1121
+ };
1122
+ let cacheConfig = { ...defaultCacheConfig };
1123
+ const cache = new Map();
1124
+ const MAX_CACHE_SIZE = 100; // Maximum number of cached entries
1125
+ /**
1126
+ * Configure the caching interceptor
1127
+ */
1128
+ function configureCaching(config) {
1129
+ cacheConfig = { ...defaultCacheConfig, ...config };
1130
+ }
1131
+ /**
1132
+ * Clean up expired cache entries
1133
+ */
1134
+ function cleanupCache() {
1135
+ const now = Date.now();
1136
+ const keysToDelete = [];
1137
+ cache.forEach((entry, key) => {
1138
+ const age = now - entry.timestamp;
1139
+ if (age >= cacheConfig.maxAge) {
1140
+ keysToDelete.push(key);
1141
+ }
1142
+ });
1143
+ keysToDelete.forEach(key => cache.delete(key));
1144
+ }
1145
+ /**
1146
+ * Evict oldest cache entry if cache is full
1147
+ */
1148
+ function evictOldestIfFull() {
1149
+ if (cache.size >= MAX_CACHE_SIZE) {
1150
+ let oldestKey = null;
1151
+ let oldestTimestamp = Infinity;
1152
+ cache.forEach((entry, key) => {
1153
+ if (entry.timestamp < oldestTimestamp) {
1154
+ oldestTimestamp = entry.timestamp;
1155
+ oldestKey = key;
1156
+ }
1157
+ });
1158
+ if (oldestKey) {
1159
+ cache.delete(oldestKey);
1160
+ }
1161
+ }
1162
+ }
1163
+ /**
1164
+ * Clear all cached entries
1165
+ */
1166
+ function clearCache() {
1167
+ cache.clear();
1168
+ }
1169
+ /**
1170
+ * Clear cache entry for specific URL
1171
+ */
1172
+ function clearCacheEntry(url) {
1173
+ cache.delete(url);
1174
+ }
1175
+ /**
1176
+ * Caching interceptor - Caches HTTP GET requests
1177
+ *
1178
+ * @example
1179
+ * ```typescript
1180
+ * // In app.config.ts
1181
+ * export const appConfig: ApplicationConfig = {
1182
+ * providers: [
1183
+ * provideHttpClient(
1184
+ * withInterceptors([cachingInterceptor])
1185
+ * )
1186
+ * ]
1187
+ * };
1188
+ *
1189
+ * // Configure caching
1190
+ * configureCaching({
1191
+ * enabled: true,
1192
+ * maxAge: 300000, // 5 minutes
1193
+ * cacheableUrls: ['/api/users', '/api/products']
1194
+ * });
1195
+ * ```
1196
+ */
1197
+ const cachingInterceptor = (req, next) => {
1198
+ const logger = inject(LoggerService);
1199
+ const config = cacheConfig;
1200
+ // Only cache if enabled
1201
+ if (!config.enabled) {
1202
+ return next(req);
1203
+ }
1204
+ // Only cache specific methods (default: GET)
1205
+ if (!config.cacheMethods?.includes(req.method)) {
1206
+ return next(req);
1207
+ }
1208
+ // Check if URL should be excluded
1209
+ const isExcluded = config.excludedUrls?.some(url => req.url.includes(url));
1210
+ if (isExcluded) {
1211
+ return next(req);
1212
+ }
1213
+ // Check if URL is explicitly cacheable (if list is provided)
1214
+ if (config.cacheableUrls && config.cacheableUrls.length > 0) {
1215
+ const isCacheable = config.cacheableUrls.some(url => req.url.includes(url));
1216
+ if (!isCacheable) {
1217
+ return next(req);
1218
+ }
1219
+ }
1220
+ const cacheKey = req.urlWithParams;
1221
+ // Check cache
1222
+ const cached = cache.get(cacheKey);
1223
+ if (cached) {
1224
+ const age = Date.now() - cached.timestamp;
1225
+ if (age < config.maxAge) {
1226
+ logger.debug(`Cache hit for ${cacheKey}`, { age });
1227
+ return new Observable(observer => {
1228
+ observer.next(cached.response.clone());
1229
+ observer.complete();
1230
+ });
1231
+ }
1232
+ else {
1233
+ // Cache expired
1234
+ cache.delete(cacheKey);
1235
+ }
1236
+ }
1237
+ // Clean up expired entries periodically
1238
+ if (Math.random() < 0.1) { // 10% chance on each request
1239
+ cleanupCache();
1240
+ }
1241
+ // Make request and cache response
1242
+ return next(req).pipe(tap(event => {
1243
+ if (event instanceof HttpResponse) {
1244
+ evictOldestIfFull();
1245
+ cache.set(cacheKey, {
1246
+ response: event.clone(),
1247
+ timestamp: Date.now()
1248
+ });
1249
+ logger.debug(`Cached response for ${cacheKey}`);
1250
+ }
1251
+ }));
1252
+ };
1253
+
1254
+ export { EventBus, HttpError, LogLevel, Logger, LoggerService, NgEventEmitter, PermissionService, StorageManager, StorageService, TokenManager, TokenService, authGuard, authInterceptor, cachingInterceptor, clearCache, clearCacheEntry, configureAuthInterceptor, configureCaching, configureErrorHandling, createAuthGuard, createPermissionGuard, createRoleGuard, errorHandlingInterceptor, isClientError, isNetworkError, isServerError, parseHttpError };
1255
+ //# sourceMappingURL=index.mjs.map