@tracelog/lib 2.0.3 → 2.1.0-rc.74.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @tracelog/lib might be problematic. Click here for more details.

@@ -267,12 +267,6 @@ var init_event_types = __esm({
267
267
  }
268
268
  });
269
269
 
270
- // src/types/log.types.ts
271
- var init_log_types = __esm({
272
- "src/types/log.types.ts"() {
273
- }
274
- });
275
-
276
270
  // src/types/mode.types.ts
277
271
  exports.Mode = void 0;
278
272
  var init_mode_types = __esm({
@@ -387,7 +381,6 @@ var init_types = __esm({
387
381
  init_emitter_types();
388
382
  init_error_types();
389
383
  init_event_types();
390
- init_log_types();
391
384
  init_mode_types();
392
385
  init_queue_types();
393
386
  init_scroll_types();
@@ -400,10 +393,22 @@ var init_types = __esm({
400
393
  }
401
394
  });
402
395
 
396
+ // src/constants/app.constants.ts
397
+ var LOG_STYLE_ACTIVE, LOG_STYLE_DISABLED, LOG_STYLE_CRITICAL;
398
+ var init_app_constants = __esm({
399
+ "src/constants/app.constants.ts"() {
400
+ LOG_STYLE_ACTIVE = "background: #ff9800; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;";
401
+ LOG_STYLE_DISABLED = "background: #9e9e9e; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;";
402
+ LOG_STYLE_CRITICAL = "background: #d32f2f; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;";
403
+ }
404
+ });
405
+
403
406
  // src/utils/logging.utils.ts
404
- var formatLogMsg, log, sanitizeLogData;
407
+ var formatLogMsg, isQaModeActive, log, shouldShowLog, getEffectiveStyle, outputLog, sanitizeLogData;
405
408
  var init_logging_utils = __esm({
406
409
  "src/utils/logging.utils.ts"() {
410
+ init_storage_constants();
411
+ init_app_constants();
407
412
  formatLogMsg = (msg, error) => {
408
413
  if (error) {
409
414
  if (process.env.NODE_ENV !== "development" && error instanceof Error) {
@@ -427,27 +432,59 @@ var init_logging_utils = __esm({
427
432
  }
428
433
  return `[TraceLog] ${msg}`;
429
434
  };
435
+ isQaModeActive = () => {
436
+ if (typeof window === "undefined" || typeof sessionStorage === "undefined") {
437
+ return false;
438
+ }
439
+ try {
440
+ return sessionStorage.getItem(QA_MODE_KEY) === "true";
441
+ } catch {
442
+ return false;
443
+ }
444
+ };
430
445
  log = (type, msg, extra) => {
431
- const { error, data, showToClient = false, style } = extra ?? {};
446
+ const { error, data, showToClient = false, style, visibility } = extra ?? {};
432
447
  const formattedMsg = error ? formatLogMsg(msg, error) : `[TraceLog] ${msg}`;
433
448
  const method = type === "error" ? "error" : type === "warn" ? "warn" : "log";
434
449
  const isProduction = process.env.NODE_ENV !== "development";
435
- if (isProduction) {
436
- if (type === "debug") {
437
- return;
438
- }
439
- if (type === "info" && !showToClient) {
440
- return;
441
- }
450
+ if (!isProduction) {
451
+ outputLog(method, formattedMsg, style, data);
452
+ return;
453
+ }
454
+ const shouldShow = shouldShowLog(visibility, showToClient);
455
+ if (!shouldShow) {
456
+ return;
457
+ }
458
+ const effectiveStyle = getEffectiveStyle(visibility, style);
459
+ const sanitizedData = data !== void 0 ? sanitizeLogData(data) : void 0;
460
+ outputLog(method, formattedMsg, effectiveStyle, sanitizedData);
461
+ };
462
+ shouldShowLog = (visibility, showToClient) => {
463
+ if (visibility === "critical") {
464
+ return true;
465
+ }
466
+ if (visibility === "qa" || showToClient) {
467
+ return isQaModeActive();
442
468
  }
469
+ return false;
470
+ };
471
+ getEffectiveStyle = (visibility, providedStyle) => {
472
+ if (providedStyle !== void 0 && providedStyle !== "") {
473
+ return providedStyle;
474
+ }
475
+ if (visibility === "critical") {
476
+ return LOG_STYLE_CRITICAL;
477
+ }
478
+ return "";
479
+ };
480
+ outputLog = (method, formattedMsg, style, data) => {
443
481
  const hasStyle = style !== void 0 && style !== "";
444
482
  const styledMsg = hasStyle ? `%c${formattedMsg}` : formattedMsg;
445
483
  if (data !== void 0) {
446
- const sanitizedData = isProduction ? sanitizeLogData(data) : data;
447
484
  if (hasStyle) {
448
- console[method](styledMsg, style, sanitizedData);
485
+ console[method](styledMsg, style, data);
449
486
  } else {
450
- console[method](styledMsg, sanitizedData);
487
+ console[method](styledMsg, data);
451
488
  }
452
489
  } else {
453
490
  if (hasStyle) {
@@ -482,7 +519,7 @@ var init_logging_utils = __esm({
482
519
  });
483
520
 
484
521
  // src/utils/browser/device-detector.utils.ts
485
- var coarsePointerQuery, noHoverQuery, initMediaQueries, getDeviceType;
522
+ var coarsePointerQuery, noHoverQuery, initMediaQueries, UNKNOWN, detectOS, detectBrowser, getDeviceType, getDeviceInfo;
486
523
  var init_device_detector_utils = __esm({
487
524
  "src/utils/browser/device-detector.utils.ts"() {
488
525
  init_device_types();
@@ -493,11 +530,53 @@ var init_device_detector_utils = __esm({
493
530
  noHoverQuery = window.matchMedia("(hover: none)");
494
531
  }
495
532
  };
533
+ UNKNOWN = "Unknown";
534
+ detectOS = (nav) => {
535
+ const platform = nav.userAgentData?.platform;
536
+ if (platform != null && platform !== "") {
537
+ if (/windows/i.test(platform)) return "Windows";
538
+ if (/macos/i.test(platform)) return "macOS";
539
+ if (/android/i.test(platform)) return "Android";
540
+ if (/linux/i.test(platform)) return "Linux";
541
+ if (/chromeos/i.test(platform)) return "ChromeOS";
542
+ if (/ios/i.test(platform)) return "iOS";
543
+ }
544
+ const ua = navigator.userAgent;
545
+ if (/Windows/i.test(ua)) return "Windows";
546
+ if (/iPhone|iPad|iPod/i.test(ua)) return "iOS";
547
+ if (/Mac OS X|Macintosh/i.test(ua)) return "macOS";
548
+ if (/Android/i.test(ua)) return "Android";
549
+ if (/CrOS/i.test(ua)) return "ChromeOS";
550
+ if (/Linux/i.test(ua)) return "Linux";
551
+ return UNKNOWN;
552
+ };
553
+ detectBrowser = (nav) => {
554
+ const brands = nav.userAgentData?.brands;
555
+ if (brands != null && brands.length > 0) {
556
+ const validBrands = brands.filter((b) => !/not.?a.?brand|chromium/i.test(b.brand));
557
+ const firstBrand = validBrands[0];
558
+ if (firstBrand != null) {
559
+ const brand = firstBrand.brand;
560
+ if (/google chrome/i.test(brand)) return "Chrome";
561
+ if (/microsoft edge/i.test(brand)) return "Edge";
562
+ if (/opera/i.test(brand)) return "Opera";
563
+ return brand;
564
+ }
565
+ }
566
+ const ua = navigator.userAgent;
567
+ if (/Edg\//i.test(ua)) return "Edge";
568
+ if (/OPR\//i.test(ua)) return "Opera";
569
+ if (/Chrome/i.test(ua)) return "Chrome";
570
+ if (/Firefox/i.test(ua)) return "Firefox";
571
+ if (/Safari/i.test(ua) && !/Chrome/i.test(ua)) return "Safari";
572
+ return UNKNOWN;
573
+ };
496
574
  getDeviceType = () => {
497
575
  try {
498
576
  const nav = navigator;
499
- if (nav.userAgentData && typeof nav.userAgentData.mobile === "boolean") {
500
- if (nav.userAgentData.platform && /ipad|tablet/i.test(nav.userAgentData.platform)) {
577
+ if (nav.userAgentData != null && typeof nav.userAgentData.mobile === "boolean") {
578
+ const uaPlatform = nav.userAgentData.platform;
579
+ if (uaPlatform != null && uaPlatform !== "" && /ipad|tablet/i.test(uaPlatform)) {
501
580
  return "tablet" /* Tablet */;
502
581
  }
503
582
  const result = nav.userAgentData.mobile ? "mobile" /* Mobile */ : "desktop" /* Desktop */;
@@ -519,19 +598,27 @@ var init_device_detector_utils = __esm({
519
598
  }
520
599
  return "desktop" /* Desktop */;
521
600
  } catch (error) {
522
- log("warn", "Device detection failed, defaulting to desktop", { error });
601
+ log("debug", "Device detection failed, defaulting to desktop", { error });
523
602
  return "desktop" /* Desktop */;
524
603
  }
525
604
  };
526
- }
527
- });
528
-
529
- // src/constants/app.constants.ts
530
- var LOG_STYLE_ACTIVE, LOG_STYLE_DISABLED;
531
- var init_app_constants = __esm({
532
- "src/constants/app.constants.ts"() {
533
- LOG_STYLE_ACTIVE = "background: #ff9800; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;";
534
- LOG_STYLE_DISABLED = "background: #9e9e9e; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;";
605
+ getDeviceInfo = () => {
606
+ try {
607
+ const nav = navigator;
608
+ return {
609
+ type: getDeviceType(),
610
+ os: detectOS(nav),
611
+ browser: detectBrowser(nav)
612
+ };
613
+ } catch (error) {
614
+ log("debug", "Device info detection failed, using defaults", { error });
615
+ return {
616
+ type: "desktop" /* Desktop */,
617
+ os: UNKNOWN,
618
+ browser: UNKNOWN
619
+ };
620
+ }
621
+ };
535
622
  }
536
623
  });
537
624
 
@@ -633,7 +720,7 @@ var init_performance_constants = __esm({
633
720
  var version;
634
721
  var init_package = __esm({
635
722
  "package.json"() {
636
- version = "2.0.2";
723
+ version = "2.1.0";
637
724
  }
638
725
  });
639
726
 
@@ -658,19 +745,33 @@ var init_constants = __esm({
658
745
  }
659
746
  });
660
747
 
661
- // src/utils/browser/qa-mode.utils.ts
662
- var qa_mode_utils_exports = {};
663
- __export(qa_mode_utils_exports, {
748
+ // src/utils/browser/mode.utils.ts
749
+ var mode_utils_exports = {};
750
+ __export(mode_utils_exports, {
664
751
  detectQaMode: () => detectQaMode,
752
+ isQaModeActive: () => isQaModeActive2,
665
753
  setQaMode: () => setQaMode
666
754
  });
667
- var detectQaMode, setQaMode;
668
- var init_qa_mode_utils = __esm({
669
- "src/utils/browser/qa-mode.utils.ts"() {
755
+ var isBrowserEnvironment, cleanUrlParameter, detectQaMode, setQaMode, isQaModeActive2;
756
+ var init_mode_utils = __esm({
757
+ "src/utils/browser/mode.utils.ts"() {
670
758
  init_constants();
671
759
  init_logging_utils();
760
+ isBrowserEnvironment = () => {
761
+ return typeof window !== "undefined" && typeof sessionStorage !== "undefined";
762
+ };
763
+ cleanUrlParameter = () => {
764
+ try {
765
+ const params = new URLSearchParams(window.location.search);
766
+ params.delete(QA_MODE_URL_PARAM);
767
+ const search = params.toString();
768
+ const url = window.location.pathname + (search ? "?" + search : "") + window.location.hash;
769
+ window.history.replaceState({}, "", url);
770
+ } catch {
771
+ }
772
+ };
672
773
  detectQaMode = () => {
673
- if (typeof window === "undefined" || typeof document === "undefined") {
774
+ if (!isBrowserEnvironment()) {
674
775
  return false;
675
776
  }
676
777
  try {
@@ -682,25 +783,19 @@ var init_qa_mode_utils = __esm({
682
783
  newState = true;
683
784
  sessionStorage.setItem(QA_MODE_KEY, "true");
684
785
  log("info", "QA Mode ACTIVE", {
685
- showToClient: true,
786
+ visibility: "qa",
686
787
  style: LOG_STYLE_ACTIVE
687
788
  });
688
789
  } else if (urlParam === QA_MODE_DISABLE_VALUE) {
689
790
  newState = false;
690
791
  sessionStorage.setItem(QA_MODE_KEY, "false");
691
792
  log("info", "QA Mode DISABLED", {
692
- showToClient: true,
793
+ visibility: "qa",
693
794
  style: LOG_STYLE_DISABLED
694
795
  });
695
796
  }
696
797
  if (urlParam === QA_MODE_ENABLE_VALUE || urlParam === QA_MODE_DISABLE_VALUE) {
697
- try {
698
- params.delete(QA_MODE_URL_PARAM);
699
- const search = params.toString();
700
- const url = window.location.pathname + (search ? "?" + search : "") + window.location.hash;
701
- window.history.replaceState({}, "", url);
702
- } catch {
703
- }
798
+ cleanUrlParameter();
704
799
  }
705
800
  return newState ?? storedState === "true";
706
801
  } catch {
@@ -708,25 +803,27 @@ var init_qa_mode_utils = __esm({
708
803
  }
709
804
  };
710
805
  setQaMode = (enabled) => {
711
- if (typeof window === "undefined" || typeof document === "undefined") {
806
+ if (!isBrowserEnvironment()) {
712
807
  return;
713
808
  }
714
809
  try {
715
- if (enabled) {
716
- sessionStorage.setItem(QA_MODE_KEY, "true");
717
- log("info", "QA Mode ENABLED", {
718
- showToClient: true,
719
- style: LOG_STYLE_ACTIVE
720
- });
721
- } else {
722
- sessionStorage.setItem(QA_MODE_KEY, "false");
723
- log("info", "QA Mode DISABLED", {
724
- showToClient: true,
725
- style: LOG_STYLE_DISABLED
726
- });
727
- }
810
+ sessionStorage.setItem(QA_MODE_KEY, enabled ? "true" : "false");
811
+ log("info", enabled ? "QA Mode ACTIVE" : "QA Mode DISABLED", {
812
+ visibility: "qa",
813
+ style: enabled ? LOG_STYLE_ACTIVE : LOG_STYLE_DISABLED
814
+ });
728
815
  } catch {
729
- log("warn", "Cannot set QA mode: sessionStorage unavailable");
816
+ log("debug", "Cannot set QA mode: sessionStorage unavailable");
817
+ }
818
+ };
819
+ isQaModeActive2 = () => {
820
+ if (!isBrowserEnvironment()) {
821
+ return false;
822
+ }
823
+ try {
824
+ return sessionStorage.getItem(QA_MODE_KEY) === "true";
825
+ } catch {
826
+ return false;
730
827
  }
731
828
  };
732
829
  }
@@ -757,7 +854,7 @@ var init_utm_params_utils = __esm({
757
854
  var init_browser = __esm({
758
855
  "src/utils/browser/index.ts"() {
759
856
  init_device_detector_utils();
760
- init_qa_mode_utils();
857
+ init_mode_utils();
761
858
  init_utm_params_utils();
762
859
  }
763
860
  });
@@ -887,7 +984,7 @@ var init_url_utils = __esm({
887
984
  };
888
985
  normalizeUrl = (url, sensitiveQueryParams = []) => {
889
986
  if (!url || typeof url !== "string") {
890
- log("warn", "Invalid URL provided to normalizeUrl", { data: { url: String(url) } });
987
+ log("warn", "Invalid URL provided to normalizeUrl", { data: { type: typeof url } });
891
988
  return url || "";
892
989
  }
893
990
  try {
@@ -910,8 +1007,7 @@ var init_url_utils = __esm({
910
1007
  const result = urlObject.toString();
911
1008
  return result;
912
1009
  } catch (error) {
913
- const urlPreview = url && typeof url === "string" ? url.slice(0, 100) : String(url);
914
- log("warn", "URL normalization failed, returning original", { error, data: { url: urlPreview } });
1010
+ log("warn", "URL normalization failed, returning original", { error, data: { urlLength: url?.length } });
915
1011
  return url;
916
1012
  }
917
1013
  };
@@ -951,7 +1047,7 @@ var init_sanitize_utils = __esm({
951
1047
  log("warn", "XSS patterns detected and removed", {
952
1048
  data: {
953
1049
  patternMatches: xssPatternMatches,
954
- originalValue: value.slice(0, 100)
1050
+ valueLength: value.length
955
1051
  }
956
1052
  });
957
1053
  }
@@ -1467,7 +1563,6 @@ var init_event_validations_utils = __esm({
1467
1563
  const nameValidation = isValidEventName(eventName);
1468
1564
  if (!nameValidation.valid) {
1469
1565
  log("error", "Event name validation failed", {
1470
- showToClient: true,
1471
1566
  data: { eventName, error: nameValidation.error }
1472
1567
  });
1473
1568
  return nameValidation;
@@ -1478,7 +1573,6 @@ var init_event_validations_utils = __esm({
1478
1573
  const metadataValidation = isValidMetadata(eventName, metadata, "customEvent");
1479
1574
  if (!metadataValidation.valid) {
1480
1575
  log("error", "Event metadata validation failed", {
1481
- showToClient: true,
1482
1576
  data: {
1483
1577
  eventName,
1484
1578
  error: metadataValidation.error
@@ -1644,7 +1738,10 @@ function transformEvent(event2, transformer, context) {
1644
1738
  log("warn", `beforeSend transformer returned invalid data, using original [${context}]`);
1645
1739
  return event2;
1646
1740
  } catch (error) {
1647
- log("error", `beforeSend transformer threw error, using original event [${context}]`, { error });
1741
+ log("error", `beforeSend transformer threw error, using original event [${context}]`, {
1742
+ error,
1743
+ visibility: "critical"
1744
+ });
1648
1745
  return event2;
1649
1746
  }
1650
1747
  }
@@ -1670,7 +1767,8 @@ function transformBatch(batch, transformer, context) {
1670
1767
  } catch (error) {
1671
1768
  log("error", `beforeBatch transformer threw error, using original batch [${context}]`, {
1672
1769
  error,
1673
- data: { eventCount: batch.events.length }
1770
+ data: { eventCount: batch.events.length },
1771
+ visibility: "critical"
1674
1772
  });
1675
1773
  return batch;
1676
1774
  }
@@ -2173,7 +2271,7 @@ var init_sender_manager = __esm({
2173
2271
  return true;
2174
2272
  }
2175
2273
  if (this.apiUrl?.includes("localhost:9999" /* Fail */)) {
2176
- log("warn", `Fail mode: simulating network failure${this.integrationId ? ` [${this.integrationId}]` : ""}`, {
2274
+ log("debug", `Fail mode: simulating network failure${this.integrationId ? ` [${this.integrationId}]` : ""}`, {
2177
2275
  data: { events: transformedBody.events.length }
2178
2276
  });
2179
2277
  return false;
@@ -2400,7 +2498,7 @@ var init_sender_manager = __esm({
2400
2498
  return JSON.parse(persistedDataString);
2401
2499
  }
2402
2500
  } catch (error) {
2403
- log("warn", `Failed to parse persisted data${this.integrationId ? ` [${this.integrationId}]` : ""}`, { error });
2501
+ log("debug", `Failed to parse persisted data${this.integrationId ? ` [${this.integrationId}]` : ""}`, { error });
2404
2502
  this.clearPersistedEvents();
2405
2503
  }
2406
2504
  return null;
@@ -2479,7 +2577,7 @@ var init_sender_manager = __esm({
2479
2577
  this.storeManager.setItem(storageKey, JSON.stringify(persistedData));
2480
2578
  return !!this.storeManager.getItem(storageKey);
2481
2579
  } catch (error) {
2482
- log("warn", `Failed to persist events${this.integrationId ? ` [${this.integrationId}]` : ""}`, { error });
2580
+ log("debug", `Failed to persist events${this.integrationId ? ` [${this.integrationId}]` : ""}`, { error });
2483
2581
  return false;
2484
2582
  }
2485
2583
  }
@@ -2488,7 +2586,9 @@ var init_sender_manager = __esm({
2488
2586
  const key = this.getQueueStorageKey();
2489
2587
  this.storeManager.removeItem(key);
2490
2588
  } catch (error) {
2491
- log("warn", `Failed to clear persisted events${this.integrationId ? ` [${this.integrationId}]` : ""}`, { error });
2589
+ log("debug", `Failed to clear persisted events${this.integrationId ? ` [${this.integrationId}]` : ""}`, {
2590
+ error
2591
+ });
2492
2592
  }
2493
2593
  }
2494
2594
  shouldSkipSend() {
@@ -2563,7 +2663,7 @@ var init_time_manager = __esm({
2563
2663
  } else {
2564
2664
  this.bootTime = 0;
2565
2665
  this.bootTimestamp = Date.now();
2566
- log("warn", "performance.now() not available, falling back to Date.now()");
2666
+ log("debug", "performance.now() not available, falling back to Date.now()");
2567
2667
  }
2568
2668
  }
2569
2669
  /**
@@ -2798,7 +2898,7 @@ var init_event_manager = __esm({
2798
2898
  }
2799
2899
  },
2800
2900
  onFailure: () => {
2801
- log("warn", "Failed to recover persisted events");
2901
+ log("debug", "Failed to recover persisted events");
2802
2902
  }
2803
2903
  })
2804
2904
  );
@@ -2872,7 +2972,8 @@ var init_event_manager = __esm({
2872
2972
  custom_event,
2873
2973
  web_vitals,
2874
2974
  error_data,
2875
- viewport_data
2975
+ viewport_data,
2976
+ page_view
2876
2977
  }) {
2877
2978
  if (!type) {
2878
2979
  log("error", "Event type is required - event will be ignored");
@@ -2882,7 +2983,7 @@ var init_event_manager = __esm({
2882
2983
  if (!currentSessionId) {
2883
2984
  if (this.pendingEventsBuffer.length >= MAX_PENDING_EVENTS_BUFFER) {
2884
2985
  this.pendingEventsBuffer.shift();
2885
- log("warn", "Pending events buffer full - dropping oldest event", {
2986
+ log("debug", "Pending events buffer full - dropping oldest event", {
2886
2987
  data: { maxBufferSize: MAX_PENDING_EVENTS_BUFFER }
2887
2988
  });
2888
2989
  }
@@ -2895,7 +2996,8 @@ var init_event_manager = __esm({
2895
2996
  custom_event,
2896
2997
  web_vitals,
2897
2998
  error_data,
2898
- viewport_data
2999
+ viewport_data,
3000
+ page_view
2899
3001
  });
2900
3002
  return;
2901
3003
  }
@@ -2956,7 +3058,8 @@ var init_event_manager = __esm({
2956
3058
  custom_event,
2957
3059
  web_vitals,
2958
3060
  error_data,
2959
- viewport_data
3061
+ viewport_data,
3062
+ page_view
2960
3063
  });
2961
3064
  if (!payload) {
2962
3065
  return;
@@ -2971,7 +3074,7 @@ var init_event_manager = __esm({
2971
3074
  return;
2972
3075
  }
2973
3076
  if (this.get("hasStartSession")) {
2974
- log("warn", "Duplicate session_start detected", {
3077
+ log("debug", "Duplicate session_start detected", {
2975
3078
  data: { sessionId: currentSessionId2 }
2976
3079
  });
2977
3080
  return;
@@ -2981,16 +3084,33 @@ var init_event_manager = __esm({
2981
3084
  if (this.isDuplicateEvent(payload)) {
2982
3085
  return;
2983
3086
  }
2984
- if (this.get("mode") === "qa" /* QA */ && eventType === "custom" /* CUSTOM */ && custom_event) {
2985
- log("info", `Custom Event: ${custom_event.name}`, {
2986
- showToClient: true,
2987
- data: {
2988
- name: custom_event.name,
2989
- ...custom_event.metadata && { metadata: custom_event.metadata }
2990
- }
2991
- });
2992
- this.emitEvent(payload);
2993
- return;
3087
+ if (this.get("mode") === "qa" /* QA */) {
3088
+ if (eventType === "custom" /* CUSTOM */ && custom_event) {
3089
+ log("info", `Custom Event: ${custom_event.name}`, {
3090
+ visibility: "qa",
3091
+ data: {
3092
+ name: custom_event.name,
3093
+ ...custom_event.metadata && { metadata: custom_event.metadata }
3094
+ }
3095
+ });
3096
+ this.emitEvent(payload);
3097
+ return;
3098
+ }
3099
+ if (eventType === "viewport_visible" /* VIEWPORT_VISIBLE */ && viewport_data) {
3100
+ const displayName = viewport_data.name || viewport_data.id || viewport_data.selector;
3101
+ log("info", `Viewport Visible: ${displayName}`, {
3102
+ visibility: "qa",
3103
+ data: {
3104
+ selector: viewport_data.selector,
3105
+ ...viewport_data.name && { name: viewport_data.name },
3106
+ ...viewport_data.id && { id: viewport_data.id },
3107
+ visibilityRatio: viewport_data.visibilityRatio,
3108
+ dwellTime: viewport_data.dwellTime
3109
+ }
3110
+ });
3111
+ this.emitEvent(payload);
3112
+ return;
3113
+ }
2994
3114
  }
2995
3115
  this.addToQueue(payload);
2996
3116
  if (!isCriticalEvent) {
@@ -3242,7 +3362,7 @@ var init_event_manager = __esm({
3242
3362
  }
3243
3363
  const currentSessionId = this.get("sessionId");
3244
3364
  if (!currentSessionId) {
3245
- log("warn", "Cannot flush pending events: session not initialized - keeping in buffer", {
3365
+ log("debug", "Cannot flush pending events: session not initialized - keeping in buffer", {
3246
3366
  data: { bufferedEventCount: this.pendingEventsBuffer.length }
3247
3367
  });
3248
3368
  return;
@@ -3284,7 +3404,7 @@ var init_event_manager = __esm({
3284
3404
  this.emitEventsQueue(body);
3285
3405
  } else {
3286
3406
  this.clearSendInterval();
3287
- log("warn", "Sync flush complete failure, events kept in queue for retry", {
3407
+ log("debug", "Sync flush complete failure, events kept in queue for retry", {
3288
3408
  data: { eventCount: eventIds.length }
3289
3409
  });
3290
3410
  }
@@ -3305,7 +3425,7 @@ var init_event_manager = __esm({
3305
3425
  this.clearSendInterval();
3306
3426
  this.emitEventsQueue(body);
3307
3427
  } else {
3308
- log("warn", "Async flush complete failure, events kept in queue for retry", {
3428
+ log("debug", "Async flush complete failure, events kept in queue for retry", {
3309
3429
  data: { eventCount: eventsToSend.length }
3310
3430
  });
3311
3431
  }
@@ -3339,12 +3459,12 @@ var init_event_manager = __esm({
3339
3459
  this.emitEventsQueue(body);
3340
3460
  const failedCount = results.filter((result) => !this.isSuccessfulResult(result)).length;
3341
3461
  if (failedCount > 0) {
3342
- log("warn", "Periodic send completed with some failures, removed from queue and persisted per-integration", {
3462
+ log("debug", "Periodic send completed with some failures, removed from queue and persisted per-integration", {
3343
3463
  data: { eventCount: eventsToSend.length, failedCount }
3344
3464
  });
3345
3465
  }
3346
3466
  } else {
3347
- log("warn", "Periodic send complete failure, events kept in queue for retry", {
3467
+ log("debug", "Periodic send complete failure, events kept in queue for retry", {
3348
3468
  data: { eventCount: eventsToSend.length }
3349
3469
  });
3350
3470
  }
@@ -3400,7 +3520,7 @@ var init_event_manager = __esm({
3400
3520
  type: data.type,
3401
3521
  page_url: currentPageUrl,
3402
3522
  timestamp,
3403
- ...isSessionStart && { referrer: document.referrer || "Direct" },
3523
+ ...isSessionStart && { referrer: this.getExternalReferrer() },
3404
3524
  ...data.from_page_url && { from_page_url: data.from_page_url },
3405
3525
  ...data.scroll_data && { scroll_data: data.scroll_data },
3406
3526
  ...data.click_data && { click_data: data.click_data },
@@ -3408,6 +3528,7 @@ var init_event_manager = __esm({
3408
3528
  ...data.web_vitals && { web_vitals: data.web_vitals },
3409
3529
  ...data.error_data && { error_data: data.error_data },
3410
3530
  ...data.viewport_data && { viewport_data: data.viewport_data },
3531
+ ...data.page_view && { page_view: data.page_view },
3411
3532
  ...isSessionStart && getUTMParameters() && { utm: getUTMParameters() }
3412
3533
  };
3413
3534
  const collectApiUrls = this.get("collectApiUrls");
@@ -3441,7 +3562,7 @@ var init_event_manager = __esm({
3441
3562
  if (this.recentEventFingerprints.size > MAX_FINGERPRINTS_HARD_LIMIT) {
3442
3563
  this.recentEventFingerprints.clear();
3443
3564
  this.recentEventFingerprints.set(fingerprint, now);
3444
- log("warn", "Event fingerprint cache exceeded hard limit, cleared", {
3565
+ log("debug", "Event fingerprint cache exceeded hard limit, cleared", {
3445
3566
  data: { hardLimit: MAX_FINGERPRINTS_HARD_LIMIT }
3446
3567
  });
3447
3568
  }
@@ -3761,6 +3882,60 @@ var init_event_manager = __esm({
3761
3882
  log("warn", "Failed to cleanup expired session counts", { error });
3762
3883
  }
3763
3884
  }
3885
+ /**
3886
+ * Returns the referrer if it's external, or 'Direct' if internal/empty.
3887
+ *
3888
+ * **Purpose**: Filter out internal referrers (same domain) to ensure
3889
+ * accurate traffic source attribution. Internal referrers occur when:
3890
+ * - Session expires and user navigates within the same site
3891
+ * - User opens new tab from an internal link
3892
+ * - Page refresh after session timeout
3893
+ *
3894
+ * **Logic**:
3895
+ * - Empty referrer → 'Direct'
3896
+ * - Referrer from same domain or subdomain → 'Direct' (internal navigation)
3897
+ * - External referrer → Returns original referrer
3898
+ *
3899
+ * **Subdomain Detection**:
3900
+ * - `www.example.com` → `example.com` ✓ (internal)
3901
+ * - `blog.example.com` → `example.com` ✓ (internal)
3902
+ * - `example.com` → `www.example.com` ✓ (internal)
3903
+ *
3904
+ * @returns External referrer URL or 'Direct'
3905
+ *
3906
+ * @internal
3907
+ */
3908
+ getExternalReferrer() {
3909
+ const referrer = document.referrer;
3910
+ if (!referrer) {
3911
+ return "Direct";
3912
+ }
3913
+ try {
3914
+ const referrerHostname = new URL(referrer).hostname.toLowerCase();
3915
+ const currentHostname = window.location.hostname.toLowerCase();
3916
+ if (this.isSameDomain(referrerHostname, currentHostname)) {
3917
+ return "Direct";
3918
+ }
3919
+ return referrer;
3920
+ } catch {
3921
+ return referrer || "Direct";
3922
+ }
3923
+ }
3924
+ /**
3925
+ * Checks if two hostnames belong to the same domain (including subdomains).
3926
+ *
3927
+ * @param hostname1 - First hostname (e.g., 'www.example.com')
3928
+ * @param hostname2 - Second hostname (e.g., 'example.com')
3929
+ * @returns true if same domain or subdomain relationship exists
3930
+ *
3931
+ * @internal
3932
+ */
3933
+ isSameDomain(hostname1, hostname2) {
3934
+ if (hostname1 === hostname2) {
3935
+ return true;
3936
+ }
3937
+ return hostname1.endsWith(`.${hostname2}`) || hostname2.endsWith(`.${hostname1}`);
3938
+ }
3764
3939
  /**
3765
3940
  * Persists current session event counts to localStorage (debounced).
3766
3941
  *
@@ -3879,7 +4054,7 @@ var init_session_manager = __esm({
3879
4054
  }
3880
4055
  initCrossTabSync() {
3881
4056
  if (typeof BroadcastChannel === "undefined") {
3882
- log("warn", "BroadcastChannel not supported");
4057
+ log("debug", "BroadcastChannel not supported");
3883
4058
  return;
3884
4059
  }
3885
4060
  const projectId = this.getProjectId();
@@ -4027,7 +4202,7 @@ var init_session_manager = __esm({
4027
4202
  */
4028
4203
  startTracking() {
4029
4204
  if (this.isTracking) {
4030
- log("warn", "Session tracking already active");
4205
+ log("debug", "Session tracking already active");
4031
4206
  return;
4032
4207
  }
4033
4208
  const recoveredSessionId = this.recoverSession();
@@ -4250,7 +4425,7 @@ var init_session_handler = __esm({
4250
4425
  return;
4251
4426
  }
4252
4427
  if (this.destroyed) {
4253
- log("warn", "Cannot start tracking on destroyed handler");
4428
+ log("debug", "Cannot start tracking on destroyed handler");
4254
4429
  return;
4255
4430
  }
4256
4431
  const config = this.get("config");
@@ -4486,7 +4661,7 @@ var init_click_handler = __esm({
4486
4661
  const target = mouseEvent.target;
4487
4662
  const clickedElement = typeof HTMLElement !== "undefined" && target instanceof HTMLElement ? target : typeof HTMLElement !== "undefined" && target instanceof Node && target.parentElement instanceof HTMLElement ? target.parentElement : null;
4488
4663
  if (!clickedElement) {
4489
- log("warn", "Click target not found or not an element");
4664
+ log("debug", "Click target not found or not an element");
4490
4665
  return;
4491
4666
  }
4492
4667
  if (this.shouldIgnoreElement(clickedElement)) {
@@ -4648,7 +4823,7 @@ var init_click_handler = __esm({
4648
4823
  return parent;
4649
4824
  }
4650
4825
  } catch (error) {
4651
- log("warn", "Invalid selector in element search", { error, data: { selector } });
4826
+ log("debug", "Invalid selector in element search", { error, data: { selector } });
4652
4827
  continue;
4653
4828
  }
4654
4829
  }
@@ -5030,7 +5205,7 @@ var init_scroll_handler = __esm({
5030
5205
  return;
5031
5206
  }
5032
5207
  this.limitWarningLogged = true;
5033
- log("warn", "Max scroll events per session reached", {
5208
+ log("debug", "Max scroll events per session reached", {
5034
5209
  data: { limit: this.maxEventsPerSession }
5035
5210
  });
5036
5211
  }
@@ -5115,7 +5290,7 @@ var init_scroll_handler = __esm({
5115
5290
  } else {
5116
5291
  const element = document.querySelector(selector);
5117
5292
  if (!(element instanceof HTMLElement)) {
5118
- log("warn", `Selector "${selector}" did not match an HTMLElement`);
5293
+ log("debug", `Selector "${selector}" did not match an HTMLElement`);
5119
5294
  return;
5120
5295
  }
5121
5296
  targetElement = element;
@@ -5168,15 +5343,15 @@ var init_viewport_handler = __esm({
5168
5343
  const threshold = this.config.threshold ?? 0.5;
5169
5344
  const minDwellTime = this.config.minDwellTime ?? 1e3;
5170
5345
  if (threshold < 0 || threshold > 1) {
5171
- log("warn", "ViewportHandler: Invalid threshold, must be between 0 and 1");
5346
+ log("debug", "ViewportHandler: Invalid threshold, must be between 0 and 1");
5172
5347
  return;
5173
5348
  }
5174
5349
  if (minDwellTime < 0) {
5175
- log("warn", "ViewportHandler: Invalid minDwellTime, must be non-negative");
5350
+ log("debug", "ViewportHandler: Invalid minDwellTime, must be non-negative");
5176
5351
  return;
5177
5352
  }
5178
5353
  if (typeof IntersectionObserver === "undefined") {
5179
- log("warn", "ViewportHandler: IntersectionObserver not supported in this browser");
5354
+ log("debug", "ViewportHandler: IntersectionObserver not supported in this browser");
5180
5355
  return;
5181
5356
  }
5182
5357
  this.observer = new IntersectionObserver(this.handleIntersection, {
@@ -5220,7 +5395,7 @@ var init_viewport_handler = __esm({
5220
5395
  const elements = document.querySelectorAll(elementConfig.selector);
5221
5396
  for (const element of Array.from(elements)) {
5222
5397
  if (totalTracked >= maxTrackedElements) {
5223
- log("warn", "ViewportHandler: Maximum tracked elements reached", {
5398
+ log("debug", "ViewportHandler: Maximum tracked elements reached", {
5224
5399
  data: {
5225
5400
  limit: maxTrackedElements,
5226
5401
  selector: elementConfig.selector,
@@ -5248,7 +5423,7 @@ var init_viewport_handler = __esm({
5248
5423
  totalTracked++;
5249
5424
  }
5250
5425
  } catch (error) {
5251
- log("warn", `ViewportHandler: Invalid selector "${elementConfig.selector}"`, { error });
5426
+ log("debug", `ViewportHandler: Invalid selector "${elementConfig.selector}"`, { error });
5252
5427
  }
5253
5428
  }
5254
5429
  log("debug", "ViewportHandler: Elements tracked", {
@@ -5328,7 +5503,7 @@ var init_viewport_handler = __esm({
5328
5503
  return;
5329
5504
  }
5330
5505
  if (!document.body) {
5331
- log("warn", "ViewportHandler: document.body not available, skipping MutationObserver setup");
5506
+ log("debug", "ViewportHandler: document.body not available, skipping MutationObserver setup");
5332
5507
  return;
5333
5508
  }
5334
5509
  this.mutationObserver = new MutationObserver((mutations) => {
@@ -5403,10 +5578,10 @@ var init_storage_manager = __esm({
5403
5578
  this.storage = this.initializeStorage("localStorage");
5404
5579
  this.sessionStorageRef = this.initializeStorage("sessionStorage");
5405
5580
  if (!this.storage) {
5406
- log("warn", "localStorage not available, using memory fallback");
5581
+ log("debug", "localStorage not available, using memory fallback");
5407
5582
  }
5408
5583
  if (!this.sessionStorageRef) {
5409
- log("warn", "sessionStorage not available, using memory fallback");
5584
+ log("debug", "sessionStorage not available, using memory fallback");
5410
5585
  }
5411
5586
  }
5412
5587
  /**
@@ -5792,7 +5967,7 @@ var init_performance_handler = __esm({
5792
5967
  try {
5793
5968
  obs.disconnect();
5794
5969
  } catch (error) {
5795
- log("warn", "Failed to disconnect performance observer", { error, data: { observerIndex: index } });
5970
+ log("debug", "Failed to disconnect performance observer", { error, data: { observerIndex: index } });
5796
5971
  }
5797
5972
  });
5798
5973
  this.observers.length = 0;
@@ -5877,7 +6052,7 @@ var init_performance_handler = __esm({
5877
6052
  onTTFB(report("TTFB"), { reportAllChanges: false });
5878
6053
  onINP(report("INP"), { reportAllChanges: false });
5879
6054
  } catch (error) {
5880
- log("warn", "Failed to load web-vitals library, using fallback", { error });
6055
+ log("debug", "Failed to load web-vitals library, using fallback", { error });
5881
6056
  this.observeWebVitalsFallback();
5882
6057
  }
5883
6058
  }
@@ -5892,7 +6067,7 @@ var init_performance_handler = __esm({
5892
6067
  this.sendVital({ type: "TTFB", value: Number(ttfb.toFixed(PRECISION_TWO_DECIMALS)) });
5893
6068
  }
5894
6069
  } catch (error) {
5895
- log("warn", "Failed to report TTFB", { error });
6070
+ log("debug", "Failed to report TTFB", { error });
5896
6071
  }
5897
6072
  }
5898
6073
  observeLongTasks() {
@@ -5942,7 +6117,7 @@ var init_performance_handler = __esm({
5942
6117
  }
5943
6118
  trackWebVital(type, value) {
5944
6119
  if (!Number.isFinite(value)) {
5945
- log("warn", "Invalid web vital value", { data: { type, value } });
6120
+ log("debug", "Invalid web vital value", { data: { type, value } });
5946
6121
  return;
5947
6122
  }
5948
6123
  this.eventManager.track({
@@ -5985,7 +6160,7 @@ var init_performance_handler = __esm({
5985
6160
  const baseId = `${timestamp.toFixed(2)}_${window.location.pathname}`;
5986
6161
  return counter > 1 ? `${baseId}_${counter}` : baseId;
5987
6162
  } catch (error) {
5988
- log("warn", "Failed to get navigation ID", { error });
6163
+ log("debug", "Failed to get navigation ID", { error });
5989
6164
  return null;
5990
6165
  }
5991
6166
  }
@@ -6003,7 +6178,7 @@ var init_performance_handler = __esm({
6003
6178
  try {
6004
6179
  cb(list, observer);
6005
6180
  } catch (callbackError) {
6006
- log("warn", "Observer callback failed", {
6181
+ log("debug", "Observer callback failed", {
6007
6182
  error: callbackError,
6008
6183
  data: { type }
6009
6184
  });
@@ -6021,7 +6196,7 @@ var init_performance_handler = __esm({
6021
6196
  }
6022
6197
  return true;
6023
6198
  } catch (error) {
6024
- log("warn", "Failed to create performance observer", {
6199
+ log("debug", "Failed to create performance observer", {
6025
6200
  error,
6026
6201
  data: { type }
6027
6202
  });
@@ -6030,7 +6205,7 @@ var init_performance_handler = __esm({
6030
6205
  }
6031
6206
  shouldSendVital(type, value) {
6032
6207
  if (typeof value !== "number" || !Number.isFinite(value)) {
6033
- log("warn", "Invalid web vital value", { data: { type, value } });
6208
+ log("debug", "Invalid web vital value", { data: { type, value } });
6034
6209
  return false;
6035
6210
  }
6036
6211
  const threshold = this.vitalThresholds[type];
@@ -6102,7 +6277,7 @@ var init_error_handler = __esm({
6102
6277
  this.errorBurstCounter++;
6103
6278
  if (this.errorBurstCounter > ERROR_BURST_THRESHOLD) {
6104
6279
  this.burstBackoffUntil = now + ERROR_BURST_BACKOFF_MS;
6105
- log("warn", "Error burst detected - entering cooldown", {
6280
+ log("debug", "Error burst detected - entering cooldown", {
6106
6281
  data: {
6107
6282
  errorsInWindow: this.errorBurstCounter,
6108
6283
  cooldownMs: ERROR_BURST_BACKOFF_MS
@@ -6357,13 +6532,13 @@ var init_app = __esm({
6357
6532
  this.set("userId", userId);
6358
6533
  const collectApiUrls = getCollectApiUrls(config);
6359
6534
  this.set("collectApiUrls", collectApiUrls);
6360
- const device = getDeviceType();
6535
+ const device = getDeviceInfo();
6361
6536
  this.set("device", device);
6362
6537
  const pageUrl = normalizeUrl(window.location.href, config.sensitiveQueryParams);
6363
6538
  this.set("pageUrl", pageUrl);
6364
- const mode = detectQaMode() ? "qa" /* QA */ : void 0;
6365
- if (mode) {
6366
- this.set("mode", mode);
6539
+ const isQaMode = detectQaMode();
6540
+ if (isQaMode) {
6541
+ this.set("mode", "qa" /* QA */);
6367
6542
  }
6368
6543
  }
6369
6544
  /**
@@ -6509,7 +6684,7 @@ var init_test_bridge = __esm({
6509
6684
  "src/test-bridge.ts"() {
6510
6685
  init_app();
6511
6686
  init_api();
6512
- init_qa_mode_utils();
6687
+ init_mode_utils();
6513
6688
  TestBridge = class extends App {
6514
6689
  constructor() {
6515
6690
  super();
@@ -6986,7 +7161,7 @@ var init_api = __esm({
6986
7161
  }
6987
7162
  }).catch(() => {
6988
7163
  });
6989
- void Promise.resolve().then(() => (init_qa_mode_utils(), qa_mode_utils_exports)).then((module) => {
7164
+ void Promise.resolve().then(() => (init_mode_utils(), mode_utils_exports)).then((module) => {
6990
7165
  if (typeof module.detectQaMode === "function") {
6991
7166
  module.detectQaMode();
6992
7167
  }