@tracelog/lib 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1613,6 +1613,58 @@ declare class EventManager extends StateManager {
1613
1613
  * @internal
1614
1614
  */
1615
1615
  private cleanupExpiredSessionCounts;
1616
+ /**
1617
+ * Returns the referrer if it's external, or 'Direct' if internal/empty.
1618
+ *
1619
+ * **Purpose**: Filter out internal referrers (same domain) to ensure
1620
+ * accurate traffic source attribution. Internal referrers occur when:
1621
+ * - Session expires and user navigates within the same site
1622
+ * - User opens new tab from an internal link
1623
+ * - Page refresh after session timeout
1624
+ *
1625
+ * **Logic**:
1626
+ * - Empty referrer → 'Direct'
1627
+ * - Referrer from same domain or subdomain → 'Direct' (internal navigation)
1628
+ * - External referrer → Returns original referrer
1629
+ *
1630
+ * **Subdomain Detection**:
1631
+ * - `www.example.com` → `example.com` ✓ (internal)
1632
+ * - `blog.example.com` → `example.com` ✓ (internal)
1633
+ * - `example.com` → `www.example.com` ✓ (internal)
1634
+ *
1635
+ * @returns External referrer URL or 'Direct'
1636
+ *
1637
+ * @internal
1638
+ */
1639
+ private getExternalReferrer;
1640
+ /**
1641
+ * Checks if two hostnames belong to the same domain (including subdomains).
1642
+ * Extracts root domain and compares to handle cross-subdomain navigation.
1643
+ *
1644
+ * @example
1645
+ * isSameDomain('www.example.com', 'example.com') // true
1646
+ * isSameDomain('app.example.com', 'www.example.com') // true
1647
+ * isSameDomain('example.co.uk', 'app.example.co.uk') // true
1648
+ *
1649
+ * @param hostname1 - First hostname (e.g., 'www.example.com')
1650
+ * @param hostname2 - Second hostname (e.g., 'app.example.com')
1651
+ * @returns true if same root domain
1652
+ *
1653
+ * @internal
1654
+ */
1655
+ private isSameDomain;
1656
+ /**
1657
+ * Extracts the root (registrable) domain from a hostname.
1658
+ * Handles both standard TLDs (.com, .org) and compound TLDs (.co.uk, .com.br).
1659
+ *
1660
+ * @example
1661
+ * getRootDomain('www.example.com') // 'example.com'
1662
+ * getRootDomain('app.blog.example.com') // 'example.com'
1663
+ * getRootDomain('shop.example.co.uk') // 'example.co.uk'
1664
+ *
1665
+ * @internal
1666
+ */
1667
+ private getRootDomain;
1616
1668
  /**
1617
1669
  * Persists current session event counts to localStorage (debounced).
1618
1670
  *
@@ -1613,6 +1613,58 @@ declare class EventManager extends StateManager {
1613
1613
  * @internal
1614
1614
  */
1615
1615
  private cleanupExpiredSessionCounts;
1616
+ /**
1617
+ * Returns the referrer if it's external, or 'Direct' if internal/empty.
1618
+ *
1619
+ * **Purpose**: Filter out internal referrers (same domain) to ensure
1620
+ * accurate traffic source attribution. Internal referrers occur when:
1621
+ * - Session expires and user navigates within the same site
1622
+ * - User opens new tab from an internal link
1623
+ * - Page refresh after session timeout
1624
+ *
1625
+ * **Logic**:
1626
+ * - Empty referrer → 'Direct'
1627
+ * - Referrer from same domain or subdomain → 'Direct' (internal navigation)
1628
+ * - External referrer → Returns original referrer
1629
+ *
1630
+ * **Subdomain Detection**:
1631
+ * - `www.example.com` → `example.com` ✓ (internal)
1632
+ * - `blog.example.com` → `example.com` ✓ (internal)
1633
+ * - `example.com` → `www.example.com` ✓ (internal)
1634
+ *
1635
+ * @returns External referrer URL or 'Direct'
1636
+ *
1637
+ * @internal
1638
+ */
1639
+ private getExternalReferrer;
1640
+ /**
1641
+ * Checks if two hostnames belong to the same domain (including subdomains).
1642
+ * Extracts root domain and compares to handle cross-subdomain navigation.
1643
+ *
1644
+ * @example
1645
+ * isSameDomain('www.example.com', 'example.com') // true
1646
+ * isSameDomain('app.example.com', 'www.example.com') // true
1647
+ * isSameDomain('example.co.uk', 'app.example.co.uk') // true
1648
+ *
1649
+ * @param hostname1 - First hostname (e.g., 'www.example.com')
1650
+ * @param hostname2 - Second hostname (e.g., 'app.example.com')
1651
+ * @returns true if same root domain
1652
+ *
1653
+ * @internal
1654
+ */
1655
+ private isSameDomain;
1656
+ /**
1657
+ * Extracts the root (registrable) domain from a hostname.
1658
+ * Handles both standard TLDs (.com, .org) and compound TLDs (.co.uk, .com.br).
1659
+ *
1660
+ * @example
1661
+ * getRootDomain('www.example.com') // 'example.com'
1662
+ * getRootDomain('app.blog.example.com') // 'example.com'
1663
+ * getRootDomain('shop.example.co.uk') // 'example.co.uk'
1664
+ *
1665
+ * @internal
1666
+ */
1667
+ private getRootDomain;
1616
1668
  /**
1617
1669
  * Persists current session event counts to localStorage (debounced).
1618
1670
  *
@@ -718,7 +718,7 @@ var init_performance_constants = __esm({
718
718
  var version;
719
719
  var init_package = __esm({
720
720
  "package.json"() {
721
- version = "2.0.3";
721
+ version = "2.1.0";
722
722
  }
723
723
  });
724
724
 
@@ -3518,7 +3518,7 @@ var init_event_manager = __esm({
3518
3518
  type: data.type,
3519
3519
  page_url: currentPageUrl,
3520
3520
  timestamp,
3521
- ...isSessionStart && { referrer: document.referrer || "Direct" },
3521
+ ...isSessionStart && { referrer: this.getExternalReferrer() },
3522
3522
  ...data.from_page_url && { from_page_url: data.from_page_url },
3523
3523
  ...data.scroll_data && { scroll_data: data.scroll_data },
3524
3524
  ...data.click_data && { click_data: data.click_data },
@@ -3880,6 +3880,102 @@ var init_event_manager = __esm({
3880
3880
  log("warn", "Failed to cleanup expired session counts", { error });
3881
3881
  }
3882
3882
  }
3883
+ /**
3884
+ * Returns the referrer if it's external, or 'Direct' if internal/empty.
3885
+ *
3886
+ * **Purpose**: Filter out internal referrers (same domain) to ensure
3887
+ * accurate traffic source attribution. Internal referrers occur when:
3888
+ * - Session expires and user navigates within the same site
3889
+ * - User opens new tab from an internal link
3890
+ * - Page refresh after session timeout
3891
+ *
3892
+ * **Logic**:
3893
+ * - Empty referrer → 'Direct'
3894
+ * - Referrer from same domain or subdomain → 'Direct' (internal navigation)
3895
+ * - External referrer → Returns original referrer
3896
+ *
3897
+ * **Subdomain Detection**:
3898
+ * - `www.example.com` → `example.com` ✓ (internal)
3899
+ * - `blog.example.com` → `example.com` ✓ (internal)
3900
+ * - `example.com` → `www.example.com` ✓ (internal)
3901
+ *
3902
+ * @returns External referrer URL or 'Direct'
3903
+ *
3904
+ * @internal
3905
+ */
3906
+ getExternalReferrer() {
3907
+ const referrer = document.referrer;
3908
+ if (!referrer) {
3909
+ return "Direct";
3910
+ }
3911
+ try {
3912
+ const referrerHostname = new URL(referrer).hostname.toLowerCase();
3913
+ const currentHostname = window.location.hostname.toLowerCase();
3914
+ if (this.isSameDomain(referrerHostname, currentHostname)) {
3915
+ return "Direct";
3916
+ }
3917
+ return referrer;
3918
+ } catch (error) {
3919
+ log("debug", "Failed to parse referrer URL, using raw value", { error, data: { referrer } });
3920
+ return referrer;
3921
+ }
3922
+ }
3923
+ /**
3924
+ * Checks if two hostnames belong to the same domain (including subdomains).
3925
+ * Extracts root domain and compares to handle cross-subdomain navigation.
3926
+ *
3927
+ * @example
3928
+ * isSameDomain('www.example.com', 'example.com') // true
3929
+ * isSameDomain('app.example.com', 'www.example.com') // true
3930
+ * isSameDomain('example.co.uk', 'app.example.co.uk') // true
3931
+ *
3932
+ * @param hostname1 - First hostname (e.g., 'www.example.com')
3933
+ * @param hostname2 - Second hostname (e.g., 'app.example.com')
3934
+ * @returns true if same root domain
3935
+ *
3936
+ * @internal
3937
+ */
3938
+ isSameDomain(hostname1, hostname2) {
3939
+ if (hostname1 === hostname2) {
3940
+ return true;
3941
+ }
3942
+ return this.getRootDomain(hostname1) === this.getRootDomain(hostname2);
3943
+ }
3944
+ /**
3945
+ * Extracts the root (registrable) domain from a hostname.
3946
+ * Handles both standard TLDs (.com, .org) and compound TLDs (.co.uk, .com.br).
3947
+ *
3948
+ * @example
3949
+ * getRootDomain('www.example.com') // 'example.com'
3950
+ * getRootDomain('app.blog.example.com') // 'example.com'
3951
+ * getRootDomain('shop.example.co.uk') // 'example.co.uk'
3952
+ *
3953
+ * @internal
3954
+ */
3955
+ getRootDomain(hostname) {
3956
+ const parts = hostname.toLowerCase().split(".");
3957
+ if (parts.length <= 2) {
3958
+ return hostname.toLowerCase();
3959
+ }
3960
+ const compoundTlds = [
3961
+ "co.uk",
3962
+ "org.uk",
3963
+ "com.au",
3964
+ "net.au",
3965
+ "com.br",
3966
+ "co.nz",
3967
+ "co.jp",
3968
+ "com.mx",
3969
+ "co.in",
3970
+ "com.cn",
3971
+ "co.za"
3972
+ ];
3973
+ const lastTwo = parts.slice(-2).join(".");
3974
+ if (compoundTlds.includes(lastTwo)) {
3975
+ return parts.slice(-3).join(".");
3976
+ }
3977
+ return parts.slice(-2).join(".");
3978
+ }
3883
3979
  /**
3884
3980
  * Persists current session event counts to localStorage (debounced).
3885
3981
  *