@versini/ui-fingerprint 1.1.4 → 1.3.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/index.d.ts +25 -77
- package/dist/index.js +625 -336
- package/package.json +9 -9
package/dist/index.d.ts
CHANGED
|
@@ -1,77 +1,25 @@
|
|
|
1
|
-
type
|
|
2
|
-
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
type AudioFP = {
|
|
28
|
-
audio: {
|
|
29
|
-
sampleHash: string;
|
|
30
|
-
oscillator: string;
|
|
31
|
-
maxChannels: number;
|
|
32
|
-
channelCountMode: string;
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
type SystemFP = {
|
|
37
|
-
system: {
|
|
38
|
-
platform: string;
|
|
39
|
-
cookieEnabled: boolean;
|
|
40
|
-
productSub: string;
|
|
41
|
-
product: string;
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
type LocalesFP = {
|
|
46
|
-
locales: {
|
|
47
|
-
languages: string;
|
|
48
|
-
timezone: string;
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
type ScreenFP = {
|
|
53
|
-
screen: {
|
|
54
|
-
colorDepth: number;
|
|
55
|
-
pixelDepth: number;
|
|
56
|
-
isTouchScreen: boolean;
|
|
57
|
-
maxTouchPoints: number;
|
|
58
|
-
mediaMatches: string[];
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
type FontsFP = string[];
|
|
63
|
-
|
|
64
|
-
type FingerprintData = [
|
|
65
|
-
AudioFP,
|
|
66
|
-
BrowserFP,
|
|
67
|
-
CanvasFP,
|
|
68
|
-
FontsFP,
|
|
69
|
-
HardwareFP,
|
|
70
|
-
LocalesFP,
|
|
71
|
-
ScreenFP,
|
|
72
|
-
SystemFP
|
|
73
|
-
];
|
|
74
|
-
declare const getFingerprintData: (debug?: boolean) => Promise<FingerprintData>;
|
|
75
|
-
declare const getFingerprintHash: (debug?: boolean) => Promise<string>;
|
|
76
|
-
|
|
77
|
-
export { getFingerprintData, getFingerprintHash };
|
|
1
|
+
import type { AudioFP } from '../common/types';
|
|
2
|
+
import type { BrowserFP } from '../common/types';
|
|
3
|
+
import type { CanvasFP } from '../common/types';
|
|
4
|
+
import type { FontsFP } from '../common/types';
|
|
5
|
+
import type { HardwareFP } from '../common/types';
|
|
6
|
+
import type { LocalesFP } from '../common/types';
|
|
7
|
+
import type { ScreenFP } from '../common/types';
|
|
8
|
+
import type { SystemFP } from '../common/types';
|
|
9
|
+
|
|
10
|
+
declare type FingerprintData = [
|
|
11
|
+
AudioFP,
|
|
12
|
+
BrowserFP,
|
|
13
|
+
CanvasFP,
|
|
14
|
+
FontsFP,
|
|
15
|
+
HardwareFP,
|
|
16
|
+
LocalesFP,
|
|
17
|
+
ScreenFP,
|
|
18
|
+
SystemFP
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export declare const getFingerprintData: (debug?: boolean) => Promise<FingerprintData>;
|
|
22
|
+
|
|
23
|
+
export declare const getFingerprintHash: (debug?: boolean) => Promise<string>;
|
|
24
|
+
|
|
25
|
+
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,359 +1,648 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-fingerprint v1.
|
|
2
|
+
@versini/ui-fingerprint v1.3.0
|
|
3
3
|
© 2025 gizmette.com
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
} catch {
|
|
13
|
-
}
|
|
14
|
-
const D = (t) => Array.from(t).map((e) => e.toString(16).padStart(2, "0")).join(""), w = async (t) => {
|
|
15
|
-
if (t === "")
|
|
16
|
-
return "";
|
|
17
|
-
const e = new TextEncoder().encode(t), o = await crypto.subtle.digest("SHA-256", e);
|
|
18
|
-
return Array.from(new Uint8Array(o)).map((s) => s.toString(16).padStart(2, "0")).join("");
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
;// CONCATENATED MODULE: ./src/common/utilities.ts
|
|
8
|
+
const hashFromFloat32Array = (array)=>{
|
|
9
|
+
const hashArray = Array.from(array).map((b)=>b.toString(16).padStart(2, "0")).join("");
|
|
10
|
+
return hashArray;
|
|
19
11
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
await
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}, p = (f) => {
|
|
34
|
-
l = !0, s(f);
|
|
35
|
-
};
|
|
36
|
-
r.onload = m, r.onerror = p;
|
|
37
|
-
const { style: u } = r;
|
|
38
|
-
u.setProperty("display", "block", "important"), u.position = "absolute", u.top = "0", u.left = "0", u.visibility = "hidden", r.src = "about:blank", o.body.appendChild(r);
|
|
39
|
-
const g = () => {
|
|
40
|
-
l || (r.contentWindow?.document?.readyState === "complete" ? m() : setTimeout(g, 10));
|
|
41
|
-
};
|
|
42
|
-
g();
|
|
43
|
-
}); !r.contentWindow?.document?.body; )
|
|
44
|
-
await y(e);
|
|
45
|
-
return await t(r, r.contentWindow);
|
|
46
|
-
} finally {
|
|
47
|
-
r.parentNode?.removeChild(r);
|
|
48
|
-
}
|
|
12
|
+
const hashFromString = async (input)=>{
|
|
13
|
+
if (input === "") {
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
const encoder = new TextEncoder();
|
|
17
|
+
const data = encoder.encode(input);
|
|
18
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
19
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
20
|
+
const hashHex = hashArray.map((b)=>b.toString(16).padStart(2, "0")).join("");
|
|
21
|
+
return hashHex;
|
|
22
|
+
};
|
|
23
|
+
function wait(durationMs, resolveWith) {
|
|
24
|
+
return new Promise((resolve)=>setTimeout(resolve, durationMs, resolveWith));
|
|
49
25
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Creates and keeps an invisible iframe while the given function runs.
|
|
28
|
+
* The given function is called when the iframe is loaded and has a body.
|
|
29
|
+
* The iframe allows to measure DOM sizes inside itself.
|
|
30
|
+
*
|
|
31
|
+
* Notice: passing an initial HTML code doesn't work in IE.
|
|
32
|
+
*
|
|
33
|
+
* Warning for package users:
|
|
34
|
+
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
|
|
35
|
+
*/ async function withIframe(action, initialHtml, domPollInterval = 50) {
|
|
36
|
+
const d = document;
|
|
37
|
+
// document.body can be null while the page is loading
|
|
38
|
+
while(!d.body){
|
|
39
|
+
await wait(domPollInterval);
|
|
40
|
+
}
|
|
41
|
+
const iframe = d.createElement("iframe");
|
|
42
|
+
try {
|
|
43
|
+
await new Promise((_resolve, _reject)=>{
|
|
44
|
+
let isComplete = false;
|
|
45
|
+
const resolve = ()=>{
|
|
46
|
+
isComplete = true;
|
|
47
|
+
_resolve();
|
|
48
|
+
};
|
|
49
|
+
const reject = (error)=>{
|
|
50
|
+
isComplete = true;
|
|
51
|
+
_reject(error);
|
|
52
|
+
};
|
|
53
|
+
iframe.onload = resolve;
|
|
54
|
+
iframe.onerror = reject;
|
|
55
|
+
const { style } = iframe;
|
|
56
|
+
// Required for browsers to calculate the layout
|
|
57
|
+
style.setProperty("display", "block", "important");
|
|
58
|
+
style.position = "absolute";
|
|
59
|
+
style.top = "0";
|
|
60
|
+
style.left = "0";
|
|
61
|
+
style.visibility = "hidden";
|
|
62
|
+
if (initialHtml && "srcdoc" in iframe) {
|
|
63
|
+
iframe.srcdoc = initialHtml;
|
|
64
|
+
} else {
|
|
65
|
+
iframe.src = "about:blank";
|
|
66
|
+
}
|
|
67
|
+
d.body.appendChild(iframe);
|
|
68
|
+
// WebKit in WeChat doesn't fire the iframe's `onload` for some reason.
|
|
69
|
+
// This code checks for the loading state manually.
|
|
70
|
+
// See https://github.com/fingerprintjs/fingerprintjs/issues/645
|
|
71
|
+
const checkReadyState = ()=>{
|
|
72
|
+
// The ready state may never become 'complete' in Firefox despite the 'load' event being fired.
|
|
73
|
+
// So an infinite setTimeout loop can happen without this check.
|
|
74
|
+
// See https://github.com/fingerprintjs/fingerprintjs/pull/716#issuecomment-986898796
|
|
75
|
+
if (isComplete) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Make sure iframe.contentWindow and iframe.contentWindow.document are both loaded
|
|
79
|
+
// The contentWindow.document can miss in JSDOM (https://github.com/jsdom/jsdom).
|
|
80
|
+
if (iframe.contentWindow?.document?.readyState === "complete") {
|
|
81
|
+
resolve();
|
|
82
|
+
} else {
|
|
83
|
+
setTimeout(checkReadyState, 10);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
checkReadyState();
|
|
87
|
+
});
|
|
88
|
+
while(!iframe.contentWindow?.document?.body){
|
|
89
|
+
await wait(domPollInterval);
|
|
70
90
|
}
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
return await action(iframe, iframe.contentWindow);
|
|
92
|
+
} finally{
|
|
93
|
+
iframe.parentNode?.removeChild(iframe);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
;// CONCATENATED MODULE: ./src/components/audio.ts
|
|
98
|
+
|
|
99
|
+
const emptyAudio = {
|
|
100
|
+
audio: {
|
|
76
101
|
sampleHash: "",
|
|
77
102
|
oscillator: "",
|
|
78
103
|
maxChannels: 0,
|
|
79
104
|
channelCountMode: ""
|
|
80
|
-
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const getAudio = async (debug)=>{
|
|
108
|
+
return new Promise((resolve)=>{
|
|
109
|
+
try {
|
|
110
|
+
const audioContext = new window.OfflineAudioContext(1, 5000, 44100);
|
|
111
|
+
const audioBuffer = audioContext.createBufferSource();
|
|
112
|
+
const oscillator = audioContext.createOscillator();
|
|
113
|
+
oscillator.frequency.value = 1000;
|
|
114
|
+
const compressor = audioContext.createDynamicsCompressor();
|
|
115
|
+
compressor.threshold.value = -50;
|
|
116
|
+
compressor.knee.value = 40;
|
|
117
|
+
compressor.ratio.value = 12;
|
|
118
|
+
compressor.attack.value = 0;
|
|
119
|
+
compressor.release.value = 0.2;
|
|
120
|
+
oscillator.connect(compressor);
|
|
121
|
+
compressor.connect(audioContext.destination);
|
|
122
|
+
oscillator.start();
|
|
123
|
+
audioContext.startRendering();
|
|
124
|
+
audioContext.oncomplete = (event)=>{
|
|
125
|
+
const samples = event.renderedBuffer.getChannelData(0);
|
|
126
|
+
oscillator.disconnect();
|
|
127
|
+
compressor.disconnect();
|
|
128
|
+
resolve({
|
|
129
|
+
audio: {
|
|
130
|
+
sampleHash: hashFromFloat32Array(samples),
|
|
131
|
+
oscillator: oscillator.type,
|
|
132
|
+
maxChannels: audioContext.destination.maxChannelCount,
|
|
133
|
+
channelCountMode: audioBuffer.channelCountMode
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (debug) {
|
|
139
|
+
console.error("Error creating audio fingerprint:", error);
|
|
140
|
+
}
|
|
141
|
+
resolve({
|
|
142
|
+
audio: {
|
|
143
|
+
sampleHash: "",
|
|
144
|
+
oscillator: "",
|
|
145
|
+
maxChannels: 0,
|
|
146
|
+
channelCountMode: ""
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
81
150
|
});
|
|
82
|
-
}
|
|
83
|
-
}), v = { browser: "" }, H = async (t) => typeof navigator > "u" ? v : { browser: navigator.userAgent }, E = {
|
|
84
|
-
canvas: {
|
|
85
|
-
data: ""
|
|
86
|
-
}
|
|
87
|
-
}, R = async (t) => {
|
|
88
|
-
try {
|
|
89
|
-
const o = Array.from(
|
|
90
|
-
{ length: 3 },
|
|
91
|
-
() => _(300, 30)
|
|
92
|
-
), r = N(o, 300, 30);
|
|
93
|
-
return {
|
|
94
|
-
canvas: {
|
|
95
|
-
data: (await w(r.data.toString())).toString()
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
} catch (o) {
|
|
99
|
-
return t && (console.error("Error creating canvas fingerprint"), console.info(o)), E;
|
|
100
|
-
}
|
|
101
|
-
}, _ = (t, n) => {
|
|
102
|
-
const e = document.createElement("canvas"), o = e.getContext("2d");
|
|
103
|
-
if (!o)
|
|
104
|
-
return new ImageData(1, 1);
|
|
105
|
-
e.width = t, e.height = n;
|
|
106
|
-
const r = o.createLinearGradient(0, 0, e.width, e.height);
|
|
107
|
-
r.addColorStop(0, "red"), r.addColorStop(1 / 6, "orange"), r.addColorStop(2 / 6, "yellow"), r.addColorStop(3 / 6, "green"), r.addColorStop(4 / 6, "blue"), r.addColorStop(5 / 6, "indigo"), r.addColorStop(1, "violet"), o.fillStyle = r, o.fillRect(0, 0, e.width, e.height);
|
|
108
|
-
const a = "mmMwWLliI0O&1 - Les sanglots longs des violons de l'automne blessent mon coeur d'une langueur monotone";
|
|
109
|
-
return o.font = "26.321px Arial", o.fillStyle = "black", o.fillText(a, -5, 15), o.fillStyle = "rgba(0, 0, 255, 0.5)", o.fillText(a, -3.3, 17.7), o.beginPath(), o.moveTo(0, 0), o.lineTo(e.width * 2 / 7, e.height), o.strokeStyle = "white", o.lineWidth = 2, o.stroke(), o.getImageData(0, 0, e.width, e.height);
|
|
110
|
-
}, F = (t) => {
|
|
111
|
-
if (t.length === 0)
|
|
112
|
-
return 0;
|
|
113
|
-
const n = {};
|
|
114
|
-
for (const o of t)
|
|
115
|
-
n[o] = (n[o] || 0) + 1;
|
|
116
|
-
let e = t[0];
|
|
117
|
-
for (const o in n)
|
|
118
|
-
n[o] > n[e] && (e = parseInt(o, 10));
|
|
119
|
-
return e;
|
|
120
|
-
}, N = (t, n, e) => {
|
|
121
|
-
const o = [];
|
|
122
|
-
for (let s = 0; s < t[0].data.length; s++) {
|
|
123
|
-
const l = [];
|
|
124
|
-
for (let m = 0; m < t.length; m++)
|
|
125
|
-
l.push(t[m].data[s]);
|
|
126
|
-
o.push(F(l));
|
|
127
|
-
}
|
|
128
|
-
const r = o, a = new Uint8ClampedArray(r);
|
|
129
|
-
return new ImageData(a, n, e);
|
|
130
|
-
}, G = [], L = "mmMwWLliI0O&1", k = "48px", h = ["monospace", "sans-serif", "serif"], S = [
|
|
131
|
-
"sans-serif-thin",
|
|
132
|
-
"ARNO PRO",
|
|
133
|
-
"Agency FB",
|
|
134
|
-
"Arabic Typesetting",
|
|
135
|
-
"Arial Unicode MS",
|
|
136
|
-
"AvantGarde Bk BT",
|
|
137
|
-
"BankGothic Md BT",
|
|
138
|
-
"Bitstream Vera Sans Mono",
|
|
139
|
-
"Calibri",
|
|
140
|
-
"Century",
|
|
141
|
-
"Century Gothic",
|
|
142
|
-
"Clarendon",
|
|
143
|
-
"EUROSTILE",
|
|
144
|
-
"Franklin Gothic",
|
|
145
|
-
"GOTHAM",
|
|
146
|
-
"Gill Sans",
|
|
147
|
-
"Helvetica Neue",
|
|
148
|
-
"Letter Gothic",
|
|
149
|
-
"Menlo",
|
|
150
|
-
"MS Outlook",
|
|
151
|
-
"MS Reference Specialty",
|
|
152
|
-
"MS UI Gothic",
|
|
153
|
-
"MT Extra",
|
|
154
|
-
"MYRIAD PRO",
|
|
155
|
-
"Marlett",
|
|
156
|
-
"Microsoft Uighur",
|
|
157
|
-
"Minion Pro",
|
|
158
|
-
"Monotype Corsiva",
|
|
159
|
-
"PMingLiU",
|
|
160
|
-
"Pristina",
|
|
161
|
-
"SCRIPTINA",
|
|
162
|
-
"SimHei",
|
|
163
|
-
"Small Fonts",
|
|
164
|
-
"Staccato222 BT",
|
|
165
|
-
"TRAJAN PRO",
|
|
166
|
-
"Univers CE 55 Medium",
|
|
167
|
-
"ZWAdobeF"
|
|
168
|
-
], O = async (t) => T(async (n, { document: e }) => {
|
|
169
|
-
const o = e.body;
|
|
170
|
-
o.style.fontSize = k;
|
|
171
|
-
const r = e.createElement("div");
|
|
172
|
-
r.style.setProperty("visibility", "hidden", "important");
|
|
173
|
-
const a = {}, s = {}, l = (i) => {
|
|
174
|
-
const c = e.createElement("span"), { style: d } = c;
|
|
175
|
-
return d.position = "absolute", d.top = "0", d.left = "0", d.fontFamily = i, c.textContent = L, r.appendChild(c), c;
|
|
176
|
-
}, m = (i, c) => l(`'${i}',${c}`), p = () => h.map(l), u = () => {
|
|
177
|
-
const i = {};
|
|
178
|
-
for (const c of S)
|
|
179
|
-
i[c] = h.map(
|
|
180
|
-
(d) => m(c, d)
|
|
181
|
-
);
|
|
182
|
-
return i;
|
|
183
|
-
}, g = (i) => h.some(
|
|
184
|
-
(c, d) => i[d].offsetWidth !== a[c] || i[d].offsetHeight !== s[c]
|
|
185
|
-
), f = p(), I = u();
|
|
186
|
-
o.appendChild(r);
|
|
187
|
-
for (let i = 0; i < h.length; i++)
|
|
188
|
-
a[h[i]] = f[i].offsetWidth, s[h[i]] = f[i].offsetHeight;
|
|
189
|
-
return S.filter((i) => g(I[i]));
|
|
190
|
-
}), C = {
|
|
191
|
-
vendor: "",
|
|
192
|
-
vendorUnmasked: "",
|
|
193
|
-
renderer: "",
|
|
194
|
-
rendererUnmasked: "",
|
|
195
|
-
version: "",
|
|
196
|
-
shadingLanguageVersion: ""
|
|
197
|
-
}, M = {
|
|
198
|
-
hardware: {
|
|
199
|
-
videocard: C,
|
|
200
|
-
architecture: 0,
|
|
201
|
-
deviceMemory: "undefined",
|
|
202
|
-
jsHeapSizeLimit: 0
|
|
203
|
-
}
|
|
204
151
|
};
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
rendererUnmasked: e ? (n.getParameter(e.UNMASKED_RENDERER_WEBGL) || "").toString() : "",
|
|
214
|
-
version: (n.getParameter(n.VERSION) || "").toString(),
|
|
215
|
-
shadingLanguageVersion: (n.getParameter(n.SHADING_LANGUAGE_VERSION) || "").toString()
|
|
152
|
+
|
|
153
|
+
;// CONCATENATED MODULE: ./src/components/browser.ts
|
|
154
|
+
const emptyBrowser = {
|
|
155
|
+
browser: ""
|
|
156
|
+
};
|
|
157
|
+
const getBrowser = async (_debug)=>{
|
|
158
|
+
return typeof navigator === "undefined" ? emptyBrowser : {
|
|
159
|
+
browser: navigator.userAgent
|
|
216
160
|
};
|
|
217
|
-
|
|
218
|
-
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
;// CONCATENATED MODULE: ./src/components/canvas.ts
|
|
164
|
+
|
|
165
|
+
const emptyCanvas = {
|
|
166
|
+
canvas: {
|
|
167
|
+
data: ""
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const getCanvas = async (debug)=>{
|
|
171
|
+
const WIDTH = 300;
|
|
172
|
+
const HEIGHT = 30;
|
|
173
|
+
try {
|
|
174
|
+
/**
|
|
175
|
+
* Creating 3 identical image data with same text and same colors,
|
|
176
|
+
* and extracting the common pixels from them. This is a fingerprint
|
|
177
|
+
* busting technique to defeat canvas counter-fingerprinting.
|
|
178
|
+
*/ const imageData = Array.from({
|
|
179
|
+
length: 3
|
|
180
|
+
}, ()=>generateImageData(WIDTH, HEIGHT));
|
|
181
|
+
const commonImageData = getCommonPixels(imageData, WIDTH, HEIGHT);
|
|
182
|
+
const hashedData = (await hashFromString(commonImageData.data.toString())).toString();
|
|
183
|
+
return {
|
|
184
|
+
canvas: {
|
|
185
|
+
data: hashedData
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (debug) {
|
|
190
|
+
console.error("Error creating canvas fingerprint");
|
|
191
|
+
console.info(error);
|
|
192
|
+
}
|
|
193
|
+
return emptyCanvas;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
const generateImageData = (width, height)=>{
|
|
197
|
+
const canvas = document.createElement("canvas");
|
|
198
|
+
const ctx = canvas.getContext("2d");
|
|
199
|
+
if (!ctx) {
|
|
200
|
+
return new ImageData(1, 1);
|
|
201
|
+
}
|
|
202
|
+
canvas.width = width;
|
|
203
|
+
canvas.height = height;
|
|
204
|
+
// Create rainbow gradient for the background rectangle
|
|
205
|
+
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
|
|
206
|
+
gradient.addColorStop(0, "red");
|
|
207
|
+
gradient.addColorStop(1 / 6, "orange");
|
|
208
|
+
gradient.addColorStop(2 / 6, "yellow");
|
|
209
|
+
gradient.addColorStop(3 / 6, "green");
|
|
210
|
+
gradient.addColorStop(4 / 6, "blue");
|
|
211
|
+
gradient.addColorStop(5 / 6, "indigo");
|
|
212
|
+
gradient.addColorStop(1, "violet");
|
|
213
|
+
// Draw background rectangle with the rainbow gradient
|
|
214
|
+
ctx.fillStyle = gradient;
|
|
215
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
216
|
+
// Draw some random text
|
|
217
|
+
// We use m or w because these two characters take up the maximum width.
|
|
218
|
+
// And we use a LLi so that the same matching fonts can get separated.
|
|
219
|
+
const randomText = "mmMwWLliI0O&1 - Les sanglots longs des violons de l'automne blessent mon coeur d'une langueur monotone";
|
|
220
|
+
ctx.font = "26.321px Arial";
|
|
221
|
+
ctx.fillStyle = "black";
|
|
222
|
+
ctx.fillText(randomText, -5, 15);
|
|
223
|
+
// Draw the same text with an offset, different color, and slight transparency
|
|
224
|
+
ctx.fillStyle = "rgba(0, 0, 255, 0.5)";
|
|
225
|
+
ctx.fillText(randomText, -3.3, 17.7);
|
|
226
|
+
// Draw a line crossing the image at an arbitrary angle
|
|
227
|
+
ctx.beginPath();
|
|
228
|
+
ctx.moveTo(0, 0);
|
|
229
|
+
ctx.lineTo(canvas.width * 2 / 7, canvas.height);
|
|
230
|
+
ctx.strokeStyle = "white";
|
|
231
|
+
ctx.lineWidth = 2;
|
|
232
|
+
ctx.stroke();
|
|
233
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
234
|
+
return imageData;
|
|
235
|
+
};
|
|
236
|
+
const getMostFrequent = (arr)=>{
|
|
237
|
+
if (arr.length === 0) {
|
|
238
|
+
return 0;
|
|
239
|
+
}
|
|
240
|
+
const frequencyMap = {};
|
|
241
|
+
for (const num of arr){
|
|
242
|
+
frequencyMap[num] = (frequencyMap[num] || 0) + 1;
|
|
243
|
+
}
|
|
244
|
+
let mostFrequent = arr[0];
|
|
245
|
+
// Find the number with the highest frequency
|
|
246
|
+
for(const num in frequencyMap){
|
|
247
|
+
if (frequencyMap[num] > frequencyMap[mostFrequent]) {
|
|
248
|
+
mostFrequent = parseInt(num, 10);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return mostFrequent;
|
|
252
|
+
};
|
|
253
|
+
const getCommonPixels = (images, width, height)=>{
|
|
254
|
+
const finalData = [];
|
|
255
|
+
for(let i = 0; i < images[0].data.length; i++){
|
|
256
|
+
const indice = [];
|
|
257
|
+
for(let u = 0; u < images.length; u++){
|
|
258
|
+
indice.push(images[u].data[i]);
|
|
259
|
+
}
|
|
260
|
+
finalData.push(getMostFrequent(indice));
|
|
261
|
+
}
|
|
262
|
+
const pixelData = finalData;
|
|
263
|
+
const pixelArray = new Uint8ClampedArray(pixelData);
|
|
264
|
+
return new ImageData(pixelArray, width, height);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
;// CONCATENATED MODULE: ./src/components/fonts.ts
|
|
268
|
+
|
|
269
|
+
const emptyFonts = [];
|
|
270
|
+
// We use m or w because these two characters take up the maximum width.
|
|
271
|
+
// And we use a LLi so that the same matching fonts can get separated.
|
|
272
|
+
const testString = "mmMwWLliI0O&1";
|
|
273
|
+
// We test using 48px font size, we may use any size. I guess larger the better.
|
|
274
|
+
const textSize = "48px";
|
|
275
|
+
// A font will be compared against all the three default fonts.
|
|
276
|
+
// And if for any default fonts it doesn't match, then that font is available.
|
|
277
|
+
const baseFonts = [
|
|
278
|
+
"monospace",
|
|
279
|
+
"sans-serif",
|
|
280
|
+
"serif"
|
|
281
|
+
];
|
|
282
|
+
const fontList = [
|
|
283
|
+
"sans-serif-thin",
|
|
284
|
+
"ARNO PRO",
|
|
285
|
+
"Agency FB",
|
|
286
|
+
"Arabic Typesetting",
|
|
287
|
+
"Arial Unicode MS",
|
|
288
|
+
"AvantGarde Bk BT",
|
|
289
|
+
"BankGothic Md BT",
|
|
290
|
+
"Bitstream Vera Sans Mono",
|
|
291
|
+
"Calibri",
|
|
292
|
+
"Century",
|
|
293
|
+
"Century Gothic",
|
|
294
|
+
"Clarendon",
|
|
295
|
+
"EUROSTILE",
|
|
296
|
+
"Franklin Gothic",
|
|
297
|
+
"GOTHAM",
|
|
298
|
+
"Gill Sans",
|
|
299
|
+
"Helvetica Neue",
|
|
300
|
+
"Letter Gothic",
|
|
301
|
+
"Menlo",
|
|
302
|
+
"MS Outlook",
|
|
303
|
+
"MS Reference Specialty",
|
|
304
|
+
"MS UI Gothic",
|
|
305
|
+
"MT Extra",
|
|
306
|
+
"MYRIAD PRO",
|
|
307
|
+
"Marlett",
|
|
308
|
+
"Microsoft Uighur",
|
|
309
|
+
"Minion Pro",
|
|
310
|
+
"Monotype Corsiva",
|
|
311
|
+
"PMingLiU",
|
|
312
|
+
"Pristina",
|
|
313
|
+
"SCRIPTINA",
|
|
314
|
+
"SimHei",
|
|
315
|
+
"Small Fonts",
|
|
316
|
+
"Staccato222 BT",
|
|
317
|
+
"TRAJAN PRO",
|
|
318
|
+
"Univers CE 55 Medium",
|
|
319
|
+
"ZWAdobeF"
|
|
320
|
+
];
|
|
321
|
+
const getFonts = async (_debug)=>{
|
|
322
|
+
return withIframe(async (_, { document })=>{
|
|
323
|
+
const holder = document.body;
|
|
324
|
+
holder.style.fontSize = textSize;
|
|
325
|
+
// div to load spans for the default fonts and the fonts to detect
|
|
326
|
+
const spansContainer = document.createElement("div");
|
|
327
|
+
spansContainer.style.setProperty("visibility", "hidden", "important");
|
|
328
|
+
const defaultWidth = {};
|
|
329
|
+
const defaultHeight = {};
|
|
330
|
+
// creates a span where the fonts will be loaded
|
|
331
|
+
const createSpan = (fontFamily)=>{
|
|
332
|
+
const span = document.createElement("span");
|
|
333
|
+
const { style } = span;
|
|
334
|
+
style.position = "absolute";
|
|
335
|
+
style.top = "0";
|
|
336
|
+
style.left = "0";
|
|
337
|
+
style.fontFamily = fontFamily;
|
|
338
|
+
span.textContent = testString;
|
|
339
|
+
spansContainer.appendChild(span);
|
|
340
|
+
return span;
|
|
341
|
+
};
|
|
342
|
+
// creates a span and load the font to detect and a base font for fallback
|
|
343
|
+
const createSpanWithFonts = (fontToDetect, baseFont)=>{
|
|
344
|
+
return createSpan(`'${fontToDetect}',${baseFont}`);
|
|
345
|
+
};
|
|
346
|
+
// creates spans for the base fonts and adds them to baseFontsDiv
|
|
347
|
+
const initializeBaseFontsSpans = ()=>{
|
|
348
|
+
return baseFonts.map(createSpan);
|
|
349
|
+
};
|
|
350
|
+
// creates spans for the fonts to detect and adds them to fontsDiv
|
|
351
|
+
const initializeFontsSpans = ()=>{
|
|
352
|
+
// Stores {fontName : [spans for that font]}
|
|
353
|
+
const spans = {};
|
|
354
|
+
for (const font of fontList){
|
|
355
|
+
spans[font] = baseFonts.map((baseFont)=>createSpanWithFonts(font, baseFont));
|
|
356
|
+
}
|
|
357
|
+
return spans;
|
|
358
|
+
};
|
|
359
|
+
// checks if a font is available
|
|
360
|
+
const isFontAvailable = (fontSpans)=>{
|
|
361
|
+
return baseFonts.some((baseFont, baseFontIndex)=>fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] || fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont]);
|
|
362
|
+
};
|
|
363
|
+
// create spans for base fonts
|
|
364
|
+
const baseFontsSpans = initializeBaseFontsSpans();
|
|
365
|
+
// create spans for fonts to detect
|
|
366
|
+
const fontsSpans = initializeFontsSpans();
|
|
367
|
+
// add all the spans to the DOM
|
|
368
|
+
holder.appendChild(spansContainer);
|
|
369
|
+
// get the default width for the three base fonts
|
|
370
|
+
for(let index = 0; index < baseFonts.length; index++){
|
|
371
|
+
defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font
|
|
372
|
+
defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font
|
|
373
|
+
}
|
|
374
|
+
// check available fonts
|
|
375
|
+
return fontList.filter((font)=>isFontAvailable(fontsSpans[font]));
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
;// CONCATENATED MODULE: ./src/components/hardware.ts
|
|
380
|
+
const emptyVideoCard = {
|
|
381
|
+
vendor: "",
|
|
382
|
+
vendorUnmasked: "",
|
|
383
|
+
renderer: "",
|
|
384
|
+
rendererUnmasked: "",
|
|
385
|
+
version: "",
|
|
386
|
+
shadingLanguageVersion: ""
|
|
387
|
+
};
|
|
388
|
+
const emptyHardware = {
|
|
389
|
+
hardware: {
|
|
390
|
+
videocard: emptyVideoCard,
|
|
391
|
+
architecture: 0,
|
|
392
|
+
deviceMemory: "undefined",
|
|
393
|
+
jsHeapSizeLimit: 0
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
function getVideoCard() {
|
|
397
|
+
const canvas = document.createElement("canvas");
|
|
398
|
+
const gl = canvas.getContext("webgl") ?? canvas.getContext("experimental-webgl");
|
|
399
|
+
if (gl && "getParameter" in gl) {
|
|
400
|
+
const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
|
401
|
+
return {
|
|
402
|
+
vendor: (gl.getParameter(gl.VENDOR) || "").toString(),
|
|
403
|
+
vendorUnmasked: debugInfo ? (gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) || "").toString() : "",
|
|
404
|
+
renderer: (gl.getParameter(gl.RENDERER) || "").toString(),
|
|
405
|
+
rendererUnmasked: debugInfo ? (gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || "").toString() : "",
|
|
406
|
+
version: (gl.getParameter(gl.VERSION) || "").toString(),
|
|
407
|
+
shadingLanguageVersion: (gl.getParameter(gl.SHADING_LANGUAGE_VERSION) || "").toString()
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return emptyVideoCard;
|
|
219
411
|
}
|
|
220
|
-
function
|
|
221
|
-
|
|
222
|
-
|
|
412
|
+
function getArchitecture() {
|
|
413
|
+
const f = new Float32Array(1);
|
|
414
|
+
const u8 = new Uint8Array(f.buffer);
|
|
415
|
+
f[0] = Infinity;
|
|
416
|
+
f[0] = f[0] - f[0];
|
|
417
|
+
return u8[3];
|
|
223
418
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
419
|
+
// @ts-ignore
|
|
420
|
+
const getDeviceMemory = ()=>navigator.deviceMemory || 0;
|
|
421
|
+
const getMemoryInfo = ()=>window.performance && window.performance.memory || {
|
|
422
|
+
jsHeapSizeLimit: 0
|
|
423
|
+
};
|
|
424
|
+
const getHardware = async (debug)=>{
|
|
425
|
+
return new Promise((resolve)=>{
|
|
426
|
+
try {
|
|
427
|
+
const deviceMemory = getDeviceMemory();
|
|
428
|
+
const memoryInfo = getMemoryInfo();
|
|
429
|
+
resolve({
|
|
430
|
+
hardware: {
|
|
431
|
+
videocard: getVideoCard(),
|
|
432
|
+
architecture: getArchitecture(),
|
|
433
|
+
deviceMemory: deviceMemory.toString() || "undefined",
|
|
434
|
+
jsHeapSizeLimit: memoryInfo.jsHeapSizeLimit || 0
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
} catch (error) {
|
|
438
|
+
if (debug) {
|
|
439
|
+
console.error("Error getting hardware data");
|
|
440
|
+
console.info(error);
|
|
441
|
+
}
|
|
442
|
+
resolve(emptyHardware);
|
|
443
|
+
}
|
|
236
444
|
});
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
locales: {
|
|
242
|
-
languages: "",
|
|
243
|
-
timezone: ""
|
|
244
|
-
}
|
|
245
|
-
}, $ = async (t) => new Promise((n) => {
|
|
246
|
-
n({
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
;// CONCATENATED MODULE: ./src/components/locale.ts
|
|
448
|
+
const emptyLocales = {
|
|
247
449
|
locales: {
|
|
248
|
-
|
|
249
|
-
|
|
450
|
+
languages: "",
|
|
451
|
+
timezone: ""
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
const getLocales = async (_debug)=>{
|
|
455
|
+
return new Promise((resolve)=>{
|
|
456
|
+
resolve({
|
|
457
|
+
locales: {
|
|
458
|
+
languages: navigator.language,
|
|
459
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
;// CONCATENATED MODULE: ./src/components/screen.ts
|
|
466
|
+
const emptyScreen = {
|
|
467
|
+
screen: {
|
|
468
|
+
colorDepth: 0,
|
|
469
|
+
pixelDepth: 0,
|
|
470
|
+
isTouchScreen: false,
|
|
471
|
+
maxTouchPoints: 0,
|
|
472
|
+
mediaMatches: []
|
|
250
473
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
474
|
+
};
|
|
475
|
+
const getScreen = async (debug)=>{
|
|
476
|
+
return new Promise((resolve)=>{
|
|
477
|
+
try {
|
|
478
|
+
const screen = window.screen;
|
|
479
|
+
const screenData = {
|
|
480
|
+
screen: {
|
|
481
|
+
colorDepth: screen.colorDepth,
|
|
482
|
+
pixelDepth: screen.pixelDepth,
|
|
483
|
+
isTouchScreen: navigator.maxTouchPoints > 0,
|
|
484
|
+
maxTouchPoints: navigator.maxTouchPoints,
|
|
485
|
+
mediaMatches: mediaMatches()
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
resolve(screenData);
|
|
489
|
+
} catch (error) {
|
|
490
|
+
if (debug) {
|
|
491
|
+
console.error("Error creating screen fingerprint");
|
|
492
|
+
console.info(error);
|
|
493
|
+
}
|
|
494
|
+
resolve(emptyScreen);
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
};
|
|
498
|
+
function mediaMatches() {
|
|
499
|
+
const results = [];
|
|
500
|
+
const mediaQueries = {
|
|
501
|
+
"prefers-contrast": [
|
|
502
|
+
"high",
|
|
503
|
+
"more",
|
|
504
|
+
"low",
|
|
505
|
+
"less",
|
|
506
|
+
"forced",
|
|
507
|
+
"no-preference"
|
|
508
|
+
],
|
|
509
|
+
"any-hover": [
|
|
510
|
+
"hover",
|
|
511
|
+
"none"
|
|
512
|
+
],
|
|
513
|
+
"any-pointer": [
|
|
514
|
+
"none",
|
|
515
|
+
"coarse",
|
|
516
|
+
"fine"
|
|
517
|
+
],
|
|
518
|
+
pointer: [
|
|
519
|
+
"none",
|
|
520
|
+
"coarse",
|
|
521
|
+
"fine"
|
|
522
|
+
],
|
|
523
|
+
hover: [
|
|
524
|
+
"hover",
|
|
525
|
+
"none"
|
|
526
|
+
],
|
|
527
|
+
update: [
|
|
528
|
+
"fast",
|
|
529
|
+
"slow"
|
|
530
|
+
],
|
|
531
|
+
"inverted-colors": [
|
|
532
|
+
"inverted",
|
|
533
|
+
"none"
|
|
534
|
+
],
|
|
535
|
+
"prefers-reduced-motion": [
|
|
536
|
+
"reduce",
|
|
537
|
+
"no-preference"
|
|
538
|
+
],
|
|
539
|
+
"prefers-reduced-transparency": [
|
|
540
|
+
"reduce",
|
|
541
|
+
"no-preference"
|
|
542
|
+
],
|
|
543
|
+
scripting: [
|
|
544
|
+
"none",
|
|
545
|
+
"initial-only",
|
|
546
|
+
"enabled"
|
|
547
|
+
],
|
|
548
|
+
"forced-colors": [
|
|
549
|
+
"active",
|
|
550
|
+
"none"
|
|
551
|
+
],
|
|
552
|
+
"color-gamut": [
|
|
553
|
+
"srgb",
|
|
554
|
+
"p3",
|
|
555
|
+
"rec2020"
|
|
556
|
+
]
|
|
270
557
|
};
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const t = [], n = {
|
|
278
|
-
"prefers-contrast": [
|
|
279
|
-
"high",
|
|
280
|
-
"more",
|
|
281
|
-
"low",
|
|
282
|
-
"less",
|
|
283
|
-
"forced",
|
|
284
|
-
"no-preference"
|
|
285
|
-
],
|
|
286
|
-
"any-hover": ["hover", "none"],
|
|
287
|
-
"any-pointer": ["none", "coarse", "fine"],
|
|
288
|
-
pointer: ["none", "coarse", "fine"],
|
|
289
|
-
hover: ["hover", "none"],
|
|
290
|
-
update: ["fast", "slow"],
|
|
291
|
-
"inverted-colors": ["inverted", "none"],
|
|
292
|
-
"prefers-reduced-motion": ["reduce", "no-preference"],
|
|
293
|
-
"prefers-reduced-transparency": ["reduce", "no-preference"],
|
|
294
|
-
scripting: ["none", "initial-only", "enabled"],
|
|
295
|
-
"forced-colors": ["active", "none"],
|
|
296
|
-
"color-gamut": ["srgb", "p3", "rec2020"]
|
|
297
|
-
};
|
|
298
|
-
return Object.keys(n).forEach((e) => {
|
|
299
|
-
n[e].forEach((o) => {
|
|
300
|
-
matchMedia(`(${e}: ${o})`).matches && t.push(`${e}: ${o}`);
|
|
558
|
+
Object.keys(mediaQueries).forEach((key)=>{
|
|
559
|
+
mediaQueries[key].forEach((value)=>{
|
|
560
|
+
if (matchMedia(`(${key}: ${value})`).matches) {
|
|
561
|
+
results.push(`${key}: ${value}`);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
301
564
|
});
|
|
302
|
-
|
|
565
|
+
return results;
|
|
303
566
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
z(t),
|
|
332
|
-
$(),
|
|
333
|
-
q(t),
|
|
334
|
-
K(t)
|
|
335
|
-
]);
|
|
336
|
-
} catch {
|
|
337
|
-
return [
|
|
338
|
-
x,
|
|
339
|
-
v,
|
|
340
|
-
E,
|
|
341
|
-
G,
|
|
342
|
-
M,
|
|
343
|
-
j,
|
|
344
|
-
b,
|
|
345
|
-
A
|
|
346
|
-
];
|
|
347
|
-
}
|
|
348
|
-
}, Q = async (t) => {
|
|
349
|
-
try {
|
|
350
|
-
const n = await Z(t);
|
|
351
|
-
return await w(JSON.stringify(n));
|
|
352
|
-
} catch (n) {
|
|
353
|
-
return t && (console.error("Error getting fingerprint hash"), console.info(n)), "";
|
|
354
|
-
}
|
|
567
|
+
|
|
568
|
+
;// CONCATENATED MODULE: ./src/components/system.ts
|
|
569
|
+
const getSystem = async (debug)=>{
|
|
570
|
+
try {
|
|
571
|
+
return {
|
|
572
|
+
system: {
|
|
573
|
+
platform: navigator.platform,
|
|
574
|
+
cookieEnabled: navigator.cookieEnabled,
|
|
575
|
+
productSub: navigator.productSub,
|
|
576
|
+
product: navigator.product
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
} catch (error) {
|
|
580
|
+
if (debug) {
|
|
581
|
+
console.error("Error getting system data");
|
|
582
|
+
console.info(error);
|
|
583
|
+
}
|
|
584
|
+
return emptySystem;
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
const emptySystem = {
|
|
588
|
+
system: {
|
|
589
|
+
platform: "",
|
|
590
|
+
cookieEnabled: false,
|
|
591
|
+
productSub: "",
|
|
592
|
+
product: ""
|
|
593
|
+
}
|
|
355
594
|
};
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
595
|
+
|
|
596
|
+
;// CONCATENATED MODULE: ./src/common/fingerprint.ts
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
const getFingerprintData = async (debug)=>{
|
|
607
|
+
try {
|
|
608
|
+
return Promise.all([
|
|
609
|
+
getAudio(debug),
|
|
610
|
+
getBrowser(debug),
|
|
611
|
+
getCanvas(debug),
|
|
612
|
+
getFonts(debug),
|
|
613
|
+
getHardware(debug),
|
|
614
|
+
getLocales(debug),
|
|
615
|
+
getScreen(debug),
|
|
616
|
+
getSystem(debug)
|
|
617
|
+
]);
|
|
618
|
+
} catch (_error) {
|
|
619
|
+
return [
|
|
620
|
+
emptyAudio,
|
|
621
|
+
emptyBrowser,
|
|
622
|
+
emptyCanvas,
|
|
623
|
+
emptyFonts,
|
|
624
|
+
emptyHardware,
|
|
625
|
+
emptyLocales,
|
|
626
|
+
emptyScreen,
|
|
627
|
+
emptySystem
|
|
628
|
+
];
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
const getFingerprintHash = async (debug)=>{
|
|
632
|
+
try {
|
|
633
|
+
const data = await getFingerprintData(debug);
|
|
634
|
+
return await hashFromString(JSON.stringify(data));
|
|
635
|
+
} catch (_error) {
|
|
636
|
+
if (debug) {
|
|
637
|
+
console.error("Error getting fingerprint hash");
|
|
638
|
+
console.info(_error);
|
|
639
|
+
}
|
|
640
|
+
return "";
|
|
641
|
+
}
|
|
359
642
|
};
|
|
643
|
+
|
|
644
|
+
;// CONCATENATED MODULE: ./src/components/index.ts
|
|
645
|
+
// force new release
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
export { getFingerprintData, getFingerprintHash };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versini/ui-fingerprint",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Arno Versini",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
9
|
-
"homepage": "https://
|
|
9
|
+
"homepage": "https://www.npmjs.com/package/@versini/ui-fingerprint",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "git@github.com:aversini/ui-components.git"
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build:check": "tsc",
|
|
23
|
-
"build:js": "
|
|
24
|
-
"build:types": "
|
|
25
|
-
"build": "npm-run-all --serial clean build:check build:js
|
|
23
|
+
"build:js": "rslib build",
|
|
24
|
+
"build:types": "echo 'Types now built with rslib'",
|
|
25
|
+
"build": "npm-run-all --serial clean build:check build:js",
|
|
26
26
|
"clean": "rimraf dist tmp",
|
|
27
|
-
"dev:js": "
|
|
28
|
-
"dev:types": "
|
|
29
|
-
"dev": "
|
|
27
|
+
"dev:js": "rslib build --watch",
|
|
28
|
+
"dev:types": "echo 'Types now watched with rslib'",
|
|
29
|
+
"dev": "rslib build --watch",
|
|
30
30
|
"lint": "biome lint src",
|
|
31
31
|
"lint:fix": "biome check src --write --no-errors-on-unmatched",
|
|
32
32
|
"prettier": "biome check --write --no-errors-on-unmatched",
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"test:watch": "vitest",
|
|
35
35
|
"test": "vitest run"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "b2ee2e328ecadfbedcb26d14afa896a30f0ab54b"
|
|
38
38
|
}
|