@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.
- package/dist/datalyr.cjs.js +33 -219
- package/dist/datalyr.cjs.js.map +1 -1
- package/dist/datalyr.esm.js +33 -219
- package/dist/datalyr.esm.js.map +1 -1
- package/dist/datalyr.esm.min.js +1 -1
- package/dist/datalyr.esm.min.js.map +1 -1
- package/dist/datalyr.js +33 -219
- package/dist/datalyr.js.map +1 -1
- package/dist/datalyr.min.js +1 -1
- package/dist/datalyr.min.js.map +1 -1
- package/dist/fingerprint.d.ts +6 -39
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +11 -27
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/datalyr.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @datalyr/web v1.0
|
|
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
|
-
|
|
1629
|
+
const fp = {
|
|
1629
1630
|
timezone: this.getTimezone(),
|
|
1630
1631
|
language: navigator.language || null,
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
1690
|
+
* Get coarse screen bucket for basic device classification
|
|
1691
|
+
* Rounds to nearest 100px for privacy
|
|
1786
1692
|
*/
|
|
1787
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|