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