@opensourcekd/ng-common-libs 2.0.5 → 2.0.7

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/README.md CHANGED
@@ -23,6 +23,7 @@ import {
23
23
 
24
24
  - **AuthService**: Complete Auth0 authentication with token management (Pure TypeScript - no Angular dependency)
25
25
  - **EventBus**: Framework-agnostic event bus using RxJS
26
+ - **Logger**: OpenTelemetry-compliant structured logging with observable log streams
26
27
  - **Works with any framework**: Angular, React, Vue, Svelte, vanilla JS
27
28
  - **Module Federation Support**: Singleton pattern works across shell + MFEs
28
29
  - **Zero Framework Lock-in**: Core services are pure TypeScript classes
@@ -94,6 +95,7 @@ import {
94
95
  // Services
95
96
  AuthService,
96
97
  EventBus,
98
+ Logger,
97
99
 
98
100
  // Helper Functions
99
101
  configureAuth0,
@@ -121,6 +123,10 @@ import {
121
123
  AppState,
122
124
  AuthorizationParams,
123
125
  CallbackResult,
126
+ LogRecord,
127
+ LogAttributes,
128
+ LogSeverity,
129
+ LoggerOptions,
124
130
  } from '@opensourcekd/ng-common-libs';
125
131
  ```
126
132
 
package/dist/index.cjs CHANGED
@@ -12,6 +12,9 @@ var operators = require('rxjs/operators');
12
12
  * // Create an instance
13
13
  * const eventBus = new EventBus();
14
14
  *
15
+ * // Create an instance with an identifier
16
+ * const eventBus = new EventBus({ id: 'MFE' });
17
+ *
15
18
  * // Emit an event
16
19
  * eventBus.emit('user:login', { userId: '123', username: 'john' });
17
20
  *
@@ -19,10 +22,28 @@ var operators = require('rxjs/operators');
19
22
  * eventBus.on('user:login').subscribe(data => {
20
23
  * console.log('User logged in:', data);
21
24
  * });
25
+ *
26
+ * // Get the identifier
27
+ * const id = eventBus.getId(); // 'MFE' or undefined
22
28
  * ```
23
29
  */
24
30
  class EventBus {
25
31
  eventSubject = new rxjs.Subject();
32
+ id;
33
+ /**
34
+ * Create a new EventBus instance
35
+ * @param options - Optional configuration with an id to identify the instance
36
+ */
37
+ constructor(options) {
38
+ this.id = options?.id;
39
+ }
40
+ /**
41
+ * Get the identifier of this EventBus instance
42
+ * @returns The id if provided during initialization, undefined otherwise
43
+ */
44
+ getId() {
45
+ return this.id;
46
+ }
26
47
  /**
27
48
  * Emit an event with optional data
28
49
  * @param eventType - The type/name of the event
@@ -245,10 +266,16 @@ function removeStorageItem(key, storageType = 'sessionStorage') {
245
266
  * };
246
267
  * const authService = new AuthService(authConfig, eventBus);
247
268
  *
269
+ * // Or create with an identifier
270
+ * const authService = new AuthService(authConfig, eventBus, undefined, undefined, { id: 'MFE' });
271
+ *
248
272
  * // Use the service
249
273
  * await authService.login();
250
274
  * const user = authService.getUser();
251
275
  * const token = await authService.getToken();
276
+ *
277
+ * // Get the identifier
278
+ * const id = authService.getId(); // 'MFE' or undefined
252
279
  * ```
253
280
  */
254
281
  class AuthService {
@@ -267,12 +294,14 @@ class AuthService {
267
294
  storageConfig;
268
295
  storageKeys;
269
296
  eventBus;
297
+ id;
270
298
  /**
271
299
  * Create a new AuthService instance
272
300
  * @param config - Auth0 configuration
273
301
  * @param eventBus - EventBus instance for emitting auth events
274
302
  * @param storageConfig - Storage configuration (optional, defaults to sessionStorage)
275
303
  * @param storageKeys - Storage keys (optional, defaults to standard keys)
304
+ * @param options - Optional configuration with an id to identify the instance
276
305
  */
277
306
  constructor(config, eventBus, storageConfig = {
278
307
  TOKEN_STORAGE: 'sessionStorage',
@@ -280,16 +309,24 @@ class AuthService {
280
309
  }, storageKeys = {
281
310
  ACCESS_TOKEN: 'auth0_access_token',
282
311
  USER_INFO: 'auth0_user_info'
283
- }) {
312
+ }, options) {
284
313
  this.config = config;
285
314
  this.eventBus = eventBus;
286
315
  this.storageConfig = storageConfig;
287
316
  this.storageKeys = storageKeys;
317
+ this.id = options?.id;
288
318
  this.userSubject = new rxjs.BehaviorSubject(this.getUserInfoFromStorage());
289
319
  this.user$ = this.userSubject.asObservable();
290
320
  console.log("[AuthService] AuthService instance created (Auth0 client will be initialized on first use)");
291
321
  // Lazy initialization - Auth0 client will be initialized in ensureInitialized() on first use
292
322
  }
323
+ /**
324
+ * Get the identifier of this AuthService instance
325
+ * @returns The id if provided during initialization, undefined otherwise
326
+ */
327
+ getId() {
328
+ return this.id;
329
+ }
293
330
  /**
294
331
  * Initialize Auth0 client
295
332
  */
@@ -406,6 +443,8 @@ class AuthService {
406
443
  }
407
444
  const token = await this.auth0Client.getTokenSilently();
408
445
  this.setToken(token);
446
+ // Clean up OAuth callback parameters from URL
447
+ this.cleanupCallbackUrl();
409
448
  console.log("[AuthService] Authentication successful");
410
449
  this.emitAuthEvent('login_success', { user, appState: result.appState });
411
450
  return { success: true, appState: result.appState };
@@ -628,6 +667,34 @@ class AuthService {
628
667
  this.eventBus.emit(event.type, event);
629
668
  console.log('[AuthService] Auth event emitted:', event.type);
630
669
  }
670
+ /**
671
+ * Clean up OAuth callback parameters from URL after successful authentication
672
+ * Removes 'code' and 'state' parameters while preserving other query parameters
673
+ */
674
+ cleanupCallbackUrl() {
675
+ try {
676
+ const url = new URL(window.location.href);
677
+ const params = new URLSearchParams(url.search);
678
+ // Check if OAuth params exist
679
+ const hasCode = params.has('code');
680
+ const hasState = params.has('state');
681
+ if (hasCode || hasState) {
682
+ // Remove OAuth callback parameters
683
+ params.delete('code');
684
+ params.delete('state');
685
+ // Construct new URL without OAuth params
686
+ const newSearch = params.toString();
687
+ const newUrl = `${url.pathname}${newSearch ? '?' + newSearch : ''}${url.hash}`;
688
+ // Replace URL without adding to browser history
689
+ window.history.replaceState({}, '', newUrl);
690
+ console.log('[AuthService] OAuth callback parameters cleaned from URL');
691
+ }
692
+ }
693
+ catch (error) {
694
+ console.warn('[AuthService] Failed to clean up callback URL:', error);
695
+ // Don't throw - URL cleanup is not critical for auth functionality
696
+ }
697
+ }
631
698
  }
632
699
  /**
633
700
  * Create AuthService instance using AUTH0_CONFIG
@@ -667,10 +734,215 @@ function createAuthService(eventBus) {
667
734
  return new AuthService(auth0Config, eventBus, STORAGE_CONFIG, STORAGE_KEYS);
668
735
  }
669
736
 
737
+ /**
738
+ * OpenTelemetry severity levels
739
+ * Based on OpenTelemetry log data model
740
+ * @see https://opentelemetry.io/docs/specs/otel/logs/data-model/
741
+ */
742
+ exports.LogSeverity = void 0;
743
+ (function (LogSeverity) {
744
+ LogSeverity[LogSeverity["TRACE"] = 1] = "TRACE";
745
+ LogSeverity[LogSeverity["DEBUG"] = 5] = "DEBUG";
746
+ LogSeverity[LogSeverity["INFO"] = 9] = "INFO";
747
+ LogSeverity[LogSeverity["WARN"] = 13] = "WARN";
748
+ LogSeverity[LogSeverity["ERROR"] = 17] = "ERROR";
749
+ LogSeverity[LogSeverity["FATAL"] = 21] = "FATAL";
750
+ })(exports.LogSeverity || (exports.LogSeverity = {}));
751
+ /**
752
+ * Logger - OpenTelemetry-compliant structured logging
753
+ * Framework-agnostic implementation using RxJS for observability
754
+ *
755
+ * Conforms to OpenTelemetry log data model:
756
+ * - Structured logging with severity levels
757
+ * - Timestamp in milliseconds
758
+ * - Support for attributes and resource context
759
+ * - Observable log stream for integration with OTel exporters
760
+ *
761
+ * @example
762
+ * ```typescript
763
+ * // Create a logger instance
764
+ * const logger = new Logger({
765
+ * name: 'MyService',
766
+ * minSeverity: LogSeverity.INFO,
767
+ * resource: {
768
+ * 'service.name': 'my-app',
769
+ * 'deployment.environment': 'production'
770
+ * }
771
+ * });
772
+ *
773
+ * // Simple logging
774
+ * logger.info('User logged in');
775
+ * logger.error('Failed to fetch data');
776
+ *
777
+ * // Structured logging with attributes
778
+ * logger.info('User action', {
779
+ * userId: '123',
780
+ * action: 'purchase',
781
+ * amount: 99.99
782
+ * });
783
+ *
784
+ * // Subscribe to log stream (e.g., for sending to OTel collector)
785
+ * logger.getLogStream().subscribe(logRecord => {
786
+ * // Send to OTel exporter, custom backend, etc.
787
+ * console.log(JSON.stringify(logRecord));
788
+ * });
789
+ *
790
+ * // Log with trace correlation
791
+ * logger.info('Processing request',
792
+ * { requestId: 'abc-123' },
793
+ * { traceId: '1234...', spanId: '5678...' }
794
+ * );
795
+ * ```
796
+ */
797
+ class Logger {
798
+ logSubject = new rxjs.Subject();
799
+ name;
800
+ minSeverity;
801
+ resource;
802
+ consoleOutput;
803
+ /**
804
+ * Create a new Logger instance
805
+ * @param options - Optional configuration for the logger
806
+ */
807
+ constructor(options) {
808
+ this.name = options?.name;
809
+ this.minSeverity = options?.minSeverity ?? exports.LogSeverity.TRACE;
810
+ this.resource = options?.resource;
811
+ this.consoleOutput = options?.consoleOutput ?? true;
812
+ }
813
+ /**
814
+ * Get the logger name
815
+ * @returns The logger name if provided during initialization
816
+ */
817
+ getName() {
818
+ return this.name;
819
+ }
820
+ /**
821
+ * Get the observable log stream
822
+ * Subscribe to this to receive all log records emitted by this logger
823
+ * @returns Observable that emits LogRecord objects
824
+ */
825
+ getLogStream() {
826
+ return this.logSubject.asObservable();
827
+ }
828
+ /**
829
+ * Emit a log record
830
+ * @param severity - Severity level
831
+ * @param severityText - Severity level text
832
+ * @param body - Log message
833
+ * @param attributes - Optional structured attributes
834
+ * @param context - Optional trace context (traceId, spanId)
835
+ */
836
+ log(severity, severityText, body, attributes, context) {
837
+ // Check if we should log based on minimum severity
838
+ if (severity < this.minSeverity) {
839
+ return;
840
+ }
841
+ const logRecord = {
842
+ timestamp: Date.now(),
843
+ severityNumber: severity,
844
+ severityText,
845
+ body,
846
+ attributes,
847
+ traceId: context?.traceId,
848
+ spanId: context?.spanId,
849
+ resource: this.resource
850
+ };
851
+ // Emit to observable stream
852
+ this.logSubject.next(logRecord);
853
+ // Console output if enabled
854
+ if (this.consoleOutput) {
855
+ this.outputToConsole(logRecord);
856
+ }
857
+ }
858
+ /**
859
+ * Output log to console
860
+ * @param logRecord - The log record to output
861
+ */
862
+ outputToConsole(logRecord) {
863
+ const prefix = this.name ? `[${this.name}]` : '';
864
+ const message = `${prefix} ${logRecord.body}`;
865
+ const meta = logRecord.attributes;
866
+ switch (logRecord.severityNumber) {
867
+ case exports.LogSeverity.TRACE:
868
+ case exports.LogSeverity.DEBUG:
869
+ console.debug(message, meta);
870
+ break;
871
+ case exports.LogSeverity.INFO:
872
+ console.info(message, meta);
873
+ break;
874
+ case exports.LogSeverity.WARN:
875
+ console.warn(message, meta);
876
+ break;
877
+ case exports.LogSeverity.ERROR:
878
+ case exports.LogSeverity.FATAL:
879
+ console.error(message, meta);
880
+ break;
881
+ default:
882
+ console.log(message, meta);
883
+ }
884
+ }
885
+ /**
886
+ * Log a TRACE level message
887
+ * @param body - Log message
888
+ * @param attributes - Optional structured attributes
889
+ * @param context - Optional trace context
890
+ */
891
+ trace(body, attributes, context) {
892
+ this.log(exports.LogSeverity.TRACE, 'TRACE', body, attributes, context);
893
+ }
894
+ /**
895
+ * Log a DEBUG level message
896
+ * @param body - Log message
897
+ * @param attributes - Optional structured attributes
898
+ * @param context - Optional trace context
899
+ */
900
+ debug(body, attributes, context) {
901
+ this.log(exports.LogSeverity.DEBUG, 'DEBUG', body, attributes, context);
902
+ }
903
+ /**
904
+ * Log an INFO level message
905
+ * @param body - Log message
906
+ * @param attributes - Optional structured attributes
907
+ * @param context - Optional trace context
908
+ */
909
+ info(body, attributes, context) {
910
+ this.log(exports.LogSeverity.INFO, 'INFO', body, attributes, context);
911
+ }
912
+ /**
913
+ * Log a WARN level message
914
+ * @param body - Log message
915
+ * @param attributes - Optional structured attributes
916
+ * @param context - Optional trace context
917
+ */
918
+ warn(body, attributes, context) {
919
+ this.log(exports.LogSeverity.WARN, 'WARN', body, attributes, context);
920
+ }
921
+ /**
922
+ * Log an ERROR level message
923
+ * @param body - Log message
924
+ * @param attributes - Optional structured attributes
925
+ * @param context - Optional trace context
926
+ */
927
+ error(body, attributes, context) {
928
+ this.log(exports.LogSeverity.ERROR, 'ERROR', body, attributes, context);
929
+ }
930
+ /**
931
+ * Log a FATAL level message
932
+ * @param body - Log message
933
+ * @param attributes - Optional structured attributes
934
+ * @param context - Optional trace context
935
+ */
936
+ fatal(body, attributes, context) {
937
+ this.log(exports.LogSeverity.FATAL, 'FATAL', body, attributes, context);
938
+ }
939
+ }
940
+
670
941
  exports.APP_CONFIG = APP_CONFIG;
671
942
  exports.AUTH0_CONFIG = AUTH0_CONFIG;
672
943
  exports.AuthService = AuthService;
673
944
  exports.EventBus = EventBus;
945
+ exports.Logger = Logger;
674
946
  exports.STORAGE_CONFIG = STORAGE_CONFIG;
675
947
  exports.STORAGE_KEYS = STORAGE_KEYS;
676
948
  exports.configureAuth0 = configureAuth0;