@datalyr/web 1.0.7 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @datalyr/web v1.0.7
2
+ * @datalyr/web v1.1.0
3
3
  * Datalyr Web SDK - Modern attribution tracking for web applications
4
4
  * (c) 2025 Datalyr Inc.
5
5
  * Released under the MIT License
@@ -1623,24 +1623,40 @@ class FingerprintCollector {
1623
1623
  }
1624
1624
  /**
1625
1625
  * Collect minimal fingerprint data (privacy-friendly)
1626
+ * Matches browser tag minimal fingerprinting approach
1626
1627
  */
1627
1628
  collectMinimal() {
1628
- return {
1629
+ const fp = {
1629
1630
  timezone: this.getTimezone(),
1630
1631
  language: navigator.language || null,
1631
- platform: navigator.platform || null,
1632
- canvasEnabled: false,
1633
- localStorageAvailable: this.testStorage('localStorage'),
1634
- sessionStorageAvailable: this.testStorage('sessionStorage')
1632
+ screen_bucket: this.getScreenBucket(),
1633
+ dnt: (navigator.doNotTrack === '1' || window.globalPrivacyControl === true) || null,
1634
+ userAgent: navigator.userAgent || null
1635
1635
  };
1636
+ // User-Agent Client Hints (modern browsers)
1637
+ if ('userAgentData' in navigator) {
1638
+ const uaData = navigator.userAgentData;
1639
+ fp.userAgentData = {
1640
+ brands: uaData.brands || [],
1641
+ mobile: uaData.mobile || false,
1642
+ platform: uaData.platform || null
1643
+ };
1644
+ }
1645
+ return fp;
1636
1646
  }
1637
1647
  /**
1638
1648
  * Collect standard fingerprint data
1649
+ * PRIVACY: Minimal data collection matching browser tag approach
1650
+ * Only collects data necessary for basic analytics and attribution
1639
1651
  */
1640
1652
  collectStandard() {
1641
1653
  const fingerprint = {};
1642
1654
  try {
1643
- // Basic browser data
1655
+ // PRIVACY: Only collect minimal data needed for attribution
1656
+ fingerprint.timezone = this.getTimezone();
1657
+ fingerprint.language = navigator.language || null;
1658
+ fingerprint.screen_bucket = this.getScreenBucket();
1659
+ fingerprint.dnt = (navigator.doNotTrack === '1' || window.globalPrivacyControl === true) || null;
1644
1660
  fingerprint.userAgent = navigator.userAgent || null;
1645
1661
  // User-Agent Client Hints (modern browsers)
1646
1662
  if ('userAgentData' in navigator) {
@@ -1651,125 +1667,14 @@ class FingerprintCollector {
1651
1667
  platform: uaData.platform || null
1652
1668
  };
1653
1669
  }
1654
- // Language settings
1655
- fingerprint.language = navigator.language || null;
1656
- fingerprint.languages = navigator.languages ?
1657
- navigator.languages.slice(0, 2) : null; // Limit to 2 for privacy
1658
- // Platform and browser features
1659
- fingerprint.platform = navigator.platform || null;
1660
- fingerprint.cookieEnabled = navigator.cookieEnabled || null;
1661
- fingerprint.doNotTrack = navigator.doNotTrack || null;
1662
- // Hardware (coarsened for privacy)
1663
- fingerprint.hardwareConcurrency = this.coarsenHardwareConcurrency();
1664
- fingerprint.deviceMemory = this.coarsenDeviceMemory();
1665
- fingerprint.maxTouchPoints = navigator.maxTouchPoints > 0 ? 'touch' : 'no-touch';
1666
- // Screen (coarsened)
1667
- fingerprint.screenResolution = this.getScreenResolution();
1668
- fingerprint.colorDepth = screen.colorDepth || null;
1669
- fingerprint.pixelRatio = this.coarsenPixelRatio();
1670
- // Timezone
1671
- fingerprint.timezone = this.getTimezone();
1672
- fingerprint.timezoneOffset = this.coarsenTimezoneOffset();
1673
- // Canvas fingerprinting disabled for privacy
1674
- fingerprint.canvasEnabled = false;
1675
- // Plugins count only (not details for privacy)
1676
- fingerprint.pluginsCount = navigator.plugins ? navigator.plugins.length : null;
1677
- // Storage availability
1678
- fingerprint.localStorageAvailable = this.testStorage('localStorage');
1679
- fingerprint.sessionStorageAvailable = this.testStorage('sessionStorage');
1680
- fingerprint.indexedDBAvailable = this.testIndexedDB();
1681
- // Add cached heavy fingerprint data if available
1682
- if (this.heavyFingerprintDone) {
1683
- Object.assign(fingerprint, this.fingerprintCache);
1684
- }
1685
1670
  }
1686
1671
  catch (e) {
1687
1672
  console.warn('[Datalyr] Error collecting fingerprint:', e);
1688
1673
  }
1689
1674
  return fingerprint;
1690
1675
  }
1691
- /**
1692
- * Collect heavy fingerprint data (WebGL, Audio)
1693
- * Called lazily on first event to improve page load performance
1694
- */
1695
- collectHeavyFingerprint() {
1696
- return __awaiter(this, void 0, void 0, function* () {
1697
- if (this.heavyFingerprintDone || this.privacyMode === 'strict') {
1698
- return;
1699
- }
1700
- try {
1701
- // WebGL fingerprinting
1702
- const webglData = this.getWebGLFingerprint();
1703
- if (webglData) {
1704
- this.fingerprintCache.webglVendor = webglData.vendor;
1705
- this.fingerprintCache.webglRenderer = webglData.renderer;
1706
- }
1707
- // Audio fingerprinting
1708
- const audioData = yield this.getAudioFingerprint();
1709
- if (audioData) {
1710
- this.fingerprintCache.audioSampleRate = audioData.sampleRate;
1711
- this.fingerprintCache.audioState = audioData.state;
1712
- this.fingerprintCache.audioMaxChannels = audioData.maxChannels;
1713
- }
1714
- this.heavyFingerprintDone = true;
1715
- }
1716
- catch (e) {
1717
- console.warn('[Datalyr] Heavy fingerprinting failed:', e);
1718
- }
1719
- });
1720
- }
1721
- /**
1722
- * Get WebGL fingerprint
1723
- */
1724
- getWebGLFingerprint() {
1725
- try {
1726
- const canvas = document.createElement('canvas');
1727
- const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
1728
- if (!gl)
1729
- return null;
1730
- const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
1731
- if (!debugInfo) {
1732
- return {
1733
- vendor: gl.getParameter(gl.VENDOR) || 'unknown',
1734
- renderer: gl.getParameter(gl.RENDERER) || 'unknown'
1735
- };
1736
- }
1737
- return {
1738
- vendor: gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) || 'unknown',
1739
- renderer: gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || 'unknown'
1740
- };
1741
- }
1742
- catch (_a) {
1743
- return null;
1744
- }
1745
- }
1746
- /**
1747
- * Get audio fingerprint
1748
- */
1749
- getAudioFingerprint() {
1750
- return __awaiter(this, void 0, void 0, function* () {
1751
- var _a;
1752
- try {
1753
- const AudioContext = window.AudioContext || window.webkitAudioContext;
1754
- if (!AudioContext)
1755
- return null;
1756
- const audioCtx = new AudioContext();
1757
- const result = {
1758
- sampleRate: audioCtx.sampleRate || null,
1759
- state: audioCtx.state || null,
1760
- maxChannels: ((_a = audioCtx.destination) === null || _a === void 0 ? void 0 : _a.maxChannelCount) || null
1761
- };
1762
- // Close context to free resources
1763
- if (audioCtx.close) {
1764
- yield audioCtx.close();
1765
- }
1766
- return result;
1767
- }
1768
- catch (_b) {
1769
- return null;
1770
- }
1771
- });
1772
- }
1676
+ // PRIVACY: Heavy fingerprinting (WebGL, Audio) removed
1677
+ // Minimal fingerprinting only for privacy compliance
1773
1678
  /**
1774
1679
  * Get timezone
1775
1680
  */
@@ -1782,10 +1687,13 @@ class FingerprintCollector {
1782
1687
  }
1783
1688
  }
1784
1689
  /**
1785
- * Get screen resolution (coarsened)
1690
+ * Get coarse screen bucket for basic device classification
1691
+ * Rounds to nearest 100px for privacy
1786
1692
  */
1787
- getScreenResolution() {
1693
+ getScreenBucket() {
1788
1694
  try {
1695
+ if (!window.screen)
1696
+ return null;
1789
1697
  const width = Math.round(screen.width / 100) * 100;
1790
1698
  const height = Math.round(screen.height / 100) * 100;
1791
1699
  return `${width}x${height}`;
@@ -1794,96 +1702,8 @@ class FingerprintCollector {
1794
1702
  return null;
1795
1703
  }
1796
1704
  }
1797
- /**
1798
- * Coarsen hardware concurrency for privacy
1799
- */
1800
- coarsenHardwareConcurrency() {
1801
- try {
1802
- const cores = navigator.hardwareConcurrency;
1803
- if (!cores)
1804
- return null;
1805
- return cores > 8 ? '8+' : cores;
1806
- }
1807
- catch (_a) {
1808
- return null;
1809
- }
1810
- }
1811
- /**
1812
- * Coarsen device memory for privacy
1813
- */
1814
- coarsenDeviceMemory() {
1815
- try {
1816
- const memory = navigator.deviceMemory;
1817
- if (!memory)
1818
- return null;
1819
- return memory > 4 ? '4+' : memory;
1820
- }
1821
- catch (_a) {
1822
- return null;
1823
- }
1824
- }
1825
- /**
1826
- * Coarsen pixel ratio for privacy
1827
- */
1828
- coarsenPixelRatio() {
1829
- try {
1830
- const ratio = window.devicePixelRatio;
1831
- if (!ratio)
1832
- return null;
1833
- // Round to common values
1834
- if (ratio <= 1)
1835
- return '1';
1836
- if (ratio <= 1.5)
1837
- return '1.5';
1838
- if (ratio <= 2)
1839
- return '2';
1840
- if (ratio <= 3)
1841
- return '3';
1842
- return '3+';
1843
- }
1844
- catch (_a) {
1845
- return null;
1846
- }
1847
- }
1848
- /**
1849
- * Coarsen timezone offset for privacy
1850
- */
1851
- coarsenTimezoneOffset() {
1852
- try {
1853
- const offset = new Date().getTimezoneOffset();
1854
- // Round to nearest 30 minutes
1855
- return Math.round(offset / 30) * 30;
1856
- }
1857
- catch (_a) {
1858
- return null;
1859
- }
1860
- }
1861
- /**
1862
- * Test if storage is available
1863
- */
1864
- testStorage(type) {
1865
- try {
1866
- const storage = window[type];
1867
- const testKey = '__dl_test__';
1868
- storage.setItem(testKey, '1');
1869
- storage.removeItem(testKey);
1870
- return true;
1871
- }
1872
- catch (_a) {
1873
- return false;
1874
- }
1875
- }
1876
- /**
1877
- * Test if IndexedDB is available
1878
- */
1879
- testIndexedDB() {
1880
- try {
1881
- return !!window.indexedDB;
1882
- }
1883
- catch (_a) {
1884
- return false;
1885
- }
1886
- }
1705
+ // PRIVACY: Unused helper methods removed (coarsen functions, storage tests)
1706
+ // Minimal fingerprinting only
1887
1707
  /**
1888
1708
  * Generate fingerprint hash
1889
1709
  */
@@ -2523,13 +2343,7 @@ class Datalyr {
2523
2343
  if (!this.shouldTrack())
2524
2344
  return;
2525
2345
  try {
2526
- // Collect heavy fingerprint on first event (lazy loading)
2527
- if (!this.heavyFingerprintCollected && this.config.enableFingerprinting) {
2528
- this.heavyFingerprintCollected = true;
2529
- this.fingerprint.collectHeavyFingerprint().catch(err => {
2530
- this.log('Heavy fingerprint collection failed:', err);
2531
- });
2532
- }
2346
+ // PRIVACY: Heavy fingerprinting removed - using minimal fingerprinting only
2533
2347
  // Update session activity
2534
2348
  this.session.updateActivity(eventName);
2535
2349
  // Create event payload